From 0ee455b281810421b0cc81949a10f20430ceeaa0 Mon Sep 17 00:00:00 2001 From: gaoluyang <2820782392@qq.com> Date: 星期二, 27 五月 2025 14:40:49 +0800 Subject: [PATCH] 初始化 --- src/assets/icons/svg/component.svg | 1 src/assets/icons/svg/clipboard.svg | 1 src/assets/styles/index.scss | 180 src/views/tool/gen/createTable.vue | 46 src/assets/icons/svg/download.svg | 1 src/assets/icons/svg/eye.svg | 1 src/layout/index.vue | 112 src/assets/images/light.svg | 39 src/views/system/user/authRole.vue | 123 src/api/system/notice.js | 44 src/views/monitor/operlog/index.vue | 310 src/views/tool/build/IconsDialog.vue | 115 src/assets/icons/svg/cascader.svg | 1 src/views/tool/build/DraggableItem.vue | 68 src/views/tool/build/CodeTypeDialog.vue | 71 vite/plugins/index.js | 15 src/api/monitor/jobLog.js | 26 src/assets/icons/svg/tool.svg | 1 src/api/login.js | 60 src/assets/icons/svg/radio.svg | 1 src/views/monitor/cache/index.vue | 132 src/assets/401_images/401.gif | 0 src/views/error/404.vue | 227 src/settings.js | 49 src/utils/generator/config.js | 452 + src/utils/generator/html.js | 359 src/components/SizeSelect/index.vue | 45 bin/run-web.bat | 12 src/assets/icons/svg/user.svg | 1 src/components/Breadcrumb/index.vue | 98 .env.development | 8 src/assets/icons/svg/form.svg | 1 src/store/modules/tagsView.js | 182 src/api/tool/gen.js | 85 src/assets/icons/svg/time.svg | 1 src/components/TopNav/index.vue | 217 src/directive/permission/hasPermi.js | 27 src/assets/icons/svg/chart.svg | 1 src/assets/icons/svg/switch.svg | 1 src/assets/icons/svg/upload.svg | 1 src/assets/styles/mixin.scss | 66 src/views/system/role/index.vue | 584 + src/directive/common/copyText.js | 65 src/assets/icons/svg/log.svg | 1 src/utils/ruoyi.js | 228 src/assets/styles/variables.module.scss | 221 src/assets/icons/svg/theme.svg | 1 src/assets/icons/svg/date-range.svg | 1 src/views/monitor/server/index.vue | 187 src/assets/icons/svg/guide.svg | 1 src/views/error/401.vue | 82 src/views/system/dict/data.vue | 362 src/views/tool/gen/editTable.vue | 211 src/views/tool/gen/importTable.vue | 126 src/assets/icons/svg/moon.svg | 1 src/assets/icons/svg/job.svg | 1 src/assets/icons/svg/checkbox.svg | 1 src/views/system/dept/index.vue | 283 src/assets/icons/svg/qq.svg | 1 src/utils/generator/render.js | 156 vite/plugins/setup-extend.js | 5 src/views/system/user/profile/userInfo.vue | 67 src/components/iFrame/index.vue | 31 src/plugins/download.js | 79 src/components/RuoYi/Doc/index.vue | 13 src/assets/icons/svg/language.svg | 1 src/plugins/auth.js | 60 src/assets/icons/svg/github.svg | 1 src/assets/icons/svg/dict.svg | 1 src/assets/styles/btn.scss | 99 src/assets/icons/svg/row.svg | 1 src/assets/icons/svg/link.svg | 1 src/utils/generator/icon.json | 1 src/assets/icons/svg/wechat.svg | 1 src/assets/icons/svg/druid.svg | 1 src/views/register.vue | 220 src/layout/components/Sidebar/Logo.vue | 99 index.html | 215 bin/package.bat | 12 src/assets/icons/svg/input.svg | 1 src/assets/icons/svg/validCode.svg | 1 LICENSE | 20 src/assets/icons/svg/icon.svg | 1 src/assets/icons/svg/number.svg | 1 src/views/tool/gen/basicInfoForm.vue | 48 src/assets/icons/svg/exit-fullscreen.svg | 1 src/assets/icons/svg/rate.svg | 1 src/router/index.js | 174 src/components/Pagination/index.vue | 105 src/assets/icons/svg/money.svg | 1 src/assets/icons/svg/phone.svg | 1 src/assets/icons/svg/size.svg | 1 src/assets/icons/svg/system.svg | 2 src/components/FileUpload/index.vue | 256 src/views/system/dict/index.vue | 323 src/assets/404_images/404.png | 0 src/api/monitor/online.js | 18 src/components/Crontab/year.vue | 143 src/assets/images/dark.svg | 39 .gitignore | 23 src/api/monitor/job.js | 71 src/utils/generator/css.js | 18 src/views/monitor/druid/index.vue | 13 src/components/RuoYi/Git/index.vue | 13 src/utils/dynamicTitle.js | 14 src/views/redirect/index.vue | 14 src/plugins/tab.js | 71 src/views/system/role/selectUser.vue | 144 src/utils/errorCode.js | 6 src/components/HeaderSearch/index.vue | 252 src/assets/icons/svg/education.svg | 1 src/assets/icons/svg/international.svg | 1 src/views/monitor/job/index.vue | 502 + src/assets/icons/svg/pdf.svg | 1 src/assets/404_images/404_cloud.png | 0 src/components/SvgIcon/index.vue | 53 src/components/RightToolbar/index.vue | 157 src/views/monitor/online/index.vue | 109 src/components/Crontab/day.vue | 174 src/api/monitor/server.js | 9 src/components/IconSelect/index.vue | 111 src/views/system/user/index.vue | 538 + src/views/tool/swagger/index.vue | 9 src/views/login.vue | 229 src/views/tool/gen/genInfoForm.vue | 305 src/assets/icons/svg/search.svg | 1 src/layout/components/IframeToggle/index.vue | 25 package.json | 51 src/App.vue | 15 src/assets/images/login-background.jpg | 0 src/components/IconSelect/requireIcons.js | 8 src/components/Crontab/index.vue | 309 src/assets/icons/svg/shopping.svg | 1 src/assets/icons/svg/message.svg | 1 src/assets/icons/svg/drag.svg | 1 src/assets/icons/svg/textarea.svg | 1 src/assets/icons/svg/button.svg | 1 src/assets/icons/svg/peoples.svg | 1 src/store/modules/settings.js | 48 src/views/monitor/logininfor/index.vue | 233 src/assets/icons/svg/tab.svg | 1 src/assets/icons/svg/sunny.svg | 1 src/assets/styles/ruoyi.scss | 290 html/ie.html | 46 src/assets/icons/svg/list.svg | 1 src/assets/icons/svg/online.svg | 1 src/views/monitor/job/log.vue | 283 vite/plugins/svg-icon.js | 10 src/assets/icons/svg/eye-open.svg | 1 src/components/ImageUpload/index.vue | 258 src/api/system/menu.js | 60 src/assets/icons/svg/404.svg | 1 src/store/modules/user.js | 77 src/store/index.js | 3 src/api/system/dict/data.js | 52 src/utils/generator/drawingDefalut.js | 29 src/assets/icons/svg/tree-table.svg | 1 src/assets/icons/svg/documentation.svg | 1 src/utils/theme.js | 49 src/layout/components/Sidebar/Link.vue | 40 src/components/Hamburger/index.vue | 42 src/views/tool/gen/index.vue | 308 src/assets/icons/svg/more-up.svg | 1 src/components/ParentView/index.vue | 3 src/plugins/index.js | 18 src/utils/index.js | 390 src/api/monitor/cache.js | 57 src/assets/icons/svg/password.svg | 1 src/components/ImagePreview/index.vue | 92 src/assets/icons/svg/question.svg | 1 src/assets/icons/svg/zip.svg | 1 src/assets/icons/svg/people.svg | 1 src/components/Crontab/hour.vue | 133 src/views/monitor/cache/list.vue | 246 src/components/Crontab/result.vue | 540 + src/assets/icons/svg/edit.svg | 1 src/components/Editor/index.vue | 276 src/api/monitor/logininfor.js | 34 src/assets/icons/svg/skill.svg | 1 src/components/Crontab/month.vue | 141 src/views/system/menu/index.vue | 452 + src/directive/index.js | 9 src/assets/icons/svg/server.svg | 1 src/layout/components/Sidebar/index.vue | 104 src/store/modules/app.js | 46 src/assets/icons/svg/excel.svg | 1 src/api/system/user.js | 136 src/assets/icons/svg/table.svg | 1 src/assets/styles/sidebar.scss | 236 src/assets/images/profile.jpg | 0 .github/FUNDING.yml | 1 src/assets/styles/element-ui.scss | 96 src/layout/components/TagsView/index.vue | 365 src/assets/icons/svg/redis-list.svg | 2 src/utils/request.js | 152 src/assets/icons/svg/star.svg | 1 src/assets/icons/svg/code.svg | 1 src/views/system/user/profile/resetPwd.vue | 59 src/layout/components/AppMain.vue | 83 src/assets/icons/svg/post.svg | 1 vite/plugins/compression.js | 28 src/components/Crontab/second.vue | 128 src/views/index.vue | 1095 ++ src/assets/icons/svg/fullscreen.svg | 1 src/assets/icons/svg/build.svg | 1 src/api/system/config.js | 60 src/assets/icons/svg/dashboard.svg | 1 src/views/system/user/profile/userAvatar.vue | 180 src/components/Screenfull/index.vue | 22 src/assets/icons/svg/date.svg | 1 src/utils/auth.js | 15 src/api/system/dept.js | 52 src/assets/icons/svg/enter.svg | 1 src/layout/components/InnerLink/index.vue | 35 src/views/system/role/authUser.vue | 179 src/api/system/dict/type.js | 60 public/favicon.ico | 0 vite.config.js | 79 src/assets/icons/svg/nested.svg | 1 src/views/tool/build/index.vue | 653 + src/permission.js | 69 src/assets/icons/svg/monitor.svg | 2 src/utils/scroll-to.js | 58 src/views/system/post/index.vue | 287 src/assets/images/pay.png | 0 src/assets/icons/svg/example.svg | 1 src/components/SvgIcon/svgicon.js | 10 src/views/system/user/profile/index.vue | 87 src/assets/styles/transition.scss | 49 src/utils/generator/js.js | 370 src/assets/icons/svg/time-range.svg | 1 src/plugins/modal.js | 82 src/assets/icons/svg/logininfor.svg | 1 vite/plugins/auto-import.js | 12 src/main.js | 82 src/layout/components/TagsView/ScrollPane.vue | 107 .env.production | 11 src/layout/components/index.js | 4 src/views/system/notice/index.vue | 292 src/layout/components/Settings/index.vue | 205 bin/build.bat | 12 src/assets/icons/svg/lock.svg | 1 src/views/system/config/index.vue | 316 src/api/system/role.js | 119 src/views/tool/build/RightPanel.vue | 906 ++ src/layout/components/Navbar.vue | 224 src/utils/jsencrypt.js | 30 src/components/Crontab/min.vue | 126 .env.staging | 11 src/assets/icons/svg/bug.svg | 1 src/views/tool/build/TreeNodeDialog.vue | 93 src/api/monitor/operlog.js | 26 src/assets/icons/svg/email.svg | 1 src/layout/components/Sidebar/SidebarItem.vue | 100 src/assets/icons/svg/redis.svg | 1 src/utils/dict.js | 24 src/assets/icons/svg/color.svg | 1 src/components/DictTag/index.vue | 82 src/assets/icons/svg/swagger.svg | 1 src/store/modules/permission.js | 127 src/assets/icons/svg/slider.svg | 1 src/assets/logo/logo.png | 0 src/api/menu.js | 9 src/utils/validate.js | 114 src/utils/permission.js | 51 src/plugins/cache.js | 79 src/store/modules/dict.js | 57 src/api/system/post.js | 44 src/assets/icons/svg/tree.svg | 1 src/components/Crontab/week.vue | 197 src/directive/permission/hasRole.js | 27 src/assets/icons/svg/select.svg | 1 272 files changed, 23,617 insertions(+), 0 deletions(-) diff --git a/.env.development b/.env.development new file mode 100644 index 0000000..af9ba00 --- /dev/null +++ b/.env.development @@ -0,0 +1,8 @@ +# 椤甸潰鏍囬 +VITE_APP_TITLE = 鑻ヤ緷绠$悊绯荤粺 + +# 寮�鍙戠幆澧冮厤缃� +VITE_APP_ENV = 'development' + +# 鑻ヤ緷绠$悊绯荤粺/寮�鍙戠幆澧� +VITE_APP_BASE_API = '/dev-api' diff --git a/.env.production b/.env.production new file mode 100644 index 0000000..cbabf89 --- /dev/null +++ b/.env.production @@ -0,0 +1,11 @@ +# 椤甸潰鏍囬 +VITE_APP_TITLE = 鑻ヤ緷绠$悊绯荤粺 + +# 鐢熶骇鐜閰嶇疆 +VITE_APP_ENV = 'production' + +# 鑻ヤ緷绠$悊绯荤粺/鐢熶骇鐜 +VITE_APP_BASE_API = '/prod-api' + +# 鏄惁鍦ㄦ墦鍖呮椂寮�鍚帇缂╋紝鏀寔 gzip 鍜� brotli +VITE_BUILD_COMPRESS = gzip \ No newline at end of file diff --git a/.env.staging b/.env.staging new file mode 100644 index 0000000..b11336d --- /dev/null +++ b/.env.staging @@ -0,0 +1,11 @@ +# 椤甸潰鏍囬 +VITE_APP_TITLE = 鑻ヤ緷绠$悊绯荤粺 + +# 鐢熶骇鐜閰嶇疆 +VITE_APP_ENV = 'staging' + +# 鑻ヤ緷绠$悊绯荤粺/鐢熶骇鐜 +VITE_APP_BASE_API = '/stage-api' + +# 鏄惁鍦ㄦ墦鍖呮椂寮�鍚帇缂╋紝鏀寔 gzip 鍜� brotli +VITE_BUILD_COMPRESS = gzip \ No newline at end of file diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 0000000..fbcab77 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1 @@ +custom: http://doc.ruoyi.vip/ruoyi-vue/other/donate.html diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..78a752d --- /dev/null +++ b/.gitignore @@ -0,0 +1,23 @@ +.DS_Store +node_modules/ +dist/ +npm-debug.log* +yarn-debug.log* +yarn-error.log* +**/*.log + +tests/**/coverage/ +tests/e2e/reports +selenium-debug.log + +# Editor directories and files +.idea +.vscode +*.suo +*.ntvs* +*.njsproj +*.sln +*.local + +package-lock.json +yarn.lock diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..8564f29 --- /dev/null +++ b/LICENSE @@ -0,0 +1,20 @@ +The MIT License (MIT) + +Copyright (c) 2018 RuoYi + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/bin/build.bat b/bin/build.bat new file mode 100644 index 0000000..ecbb454 --- /dev/null +++ b/bin/build.bat @@ -0,0 +1,12 @@ +@echo off +echo. +echo [信息] 打包Web工程,生成dist文件。 +echo. + +%~d0 +cd %~dp0 + +cd .. +yarn build:prod + +pause \ No newline at end of file diff --git a/bin/package.bat b/bin/package.bat new file mode 100644 index 0000000..f5b24e0 --- /dev/null +++ b/bin/package.bat @@ -0,0 +1,12 @@ +@echo off +echo. +echo [信息] 安装Web工程,生成node_modules文件。 +echo. + +%~d0 +cd %~dp0 + +cd .. +yarn --registry=https://registry.npmmirror.com + +pause \ No newline at end of file diff --git a/bin/run-web.bat b/bin/run-web.bat new file mode 100644 index 0000000..d2fe397 --- /dev/null +++ b/bin/run-web.bat @@ -0,0 +1,12 @@ +@echo off +echo. +echo [信息] 使用 Vite 命令运行 Web 工程。 +echo. + +%~d0 +cd %~dp0 + +cd .. +yarn dev + +pause \ No newline at end of file diff --git a/html/ie.html b/html/ie.html new file mode 100644 index 0000000..052ffcd --- /dev/null +++ b/html/ie.html @@ -0,0 +1,46 @@ + +<!DOCTYPE html> +<html lang="zh-CN"> +<head> + <meta charset="UTF-8" /> + <title>璇峰崌绾ф偍鐨勬祻瑙堝櫒</title> + <meta http-equiv="X-UA-Compatible" content="IE=Edge,chrome=1" > + <meta name="renderer" content="webkit"> + <base target="_blank" /> + <style type="text/css"> + html,body,div,span,applet,object,iframe,h1,h2,h3,h4,h5,h6,p,blockquote,pre,a,abbr,acronym,address,big,cite,code,del,dfn,em,img,ins,kbd,q,s,samp,small,strike,strong,sub,sup,tt,var,b,u,i,center,dl,dt,dd,ol,ul,li,fieldset,form,label,legend,table,caption,tbody,tfoot,thead,tr,th,td,article,aside,canvas,details,embed,figure,figcaption,footer,header,hgroup,menu,nav,output,ruby,section,summary,time,mark,audio,video{border:0;font-size:100%;font:inherit;vertical-align:baseline;margin:0;padding:0}article,aside,details,figcaption,figure,footer,header,hgroup,menu,nav,section{display:block}body{line-height:1}ol,ul{list-style:none}blockquote,q{quotes:none}blockquote:before,blockquote:after,q:before,q:after{content:none}table{border-collapse:collapse;border-spacing:0} + a{text-decoration:none;color:#0072c6;}a:hover{text-decoration:none;color:#004d8c;} + body{width:960px;margin:0 auto;padding:10px;font-size:14px;line-height:24px;color:#454545;font-family:'Microsoft YaHei UI','Microsoft YaHei',DengXian,SimSun,'Segoe UI',Tahoma,Helvetica,sans-serif;overflow-y:scroll} + h1{font-size:40px;line-height:80px;font-weight:100;margin-bottom:10px;} + h2{font-size:20px;line-height:25px;font-weight:100;margin:10px 0;} + em{color:red} + p{margin-bottom:10px;} + hr{margin:20px 0;border:0;border-top:1px solid #dadada} + span{display:block;font-size:12px;line-height:12px;} + .clean{clear:both;} + .browser{padding:10px 10px;} + .browser li{width:auto;padding:0 80px;margin-top:30px;height:34px;line-height:22px;float:left;list-style:none;background:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACIAAADMCAYAAAAWCXEwAAAACXBIWXMAAAsTAAALEwEAmpwYAAAKTWlDQ1BQaG90b3Nob3AgSUNDIHByb2ZpbGUAAHjanVN3WJP3Fj7f92UPVkLY8LGXbIEAIiOsCMgQWaIQkgBhhBASQMWFiApWFBURnEhVxILVCkidiOKgKLhnQYqIWotVXDjuH9yntX167+3t+9f7vOec5/zOec8PgBESJpHmomoAOVKFPDrYH49PSMTJvYACFUjgBCAQ5svCZwXFAADwA3l4fnSwP/wBr28AAgBw1S4kEsfh/4O6UCZXACCRAOAiEucLAZBSAMguVMgUAMgYALBTs2QKAJQAAGx5fEIiAKoNAOz0ST4FANipk9wXANiiHKkIAI0BAJkoRyQCQLsAYFWBUiwCwMIAoKxAIi4EwK4BgFm2MkcCgL0FAHaOWJAPQGAAgJlCLMwAIDgCAEMeE80DIEwDoDDSv+CpX3CFuEgBAMDLlc2XS9IzFLiV0Bp38vDg4iHiwmyxQmEXKRBmCeQinJebIxNI5wNMzgwAABr50cH+OD+Q5+bk4eZm52zv9MWi/mvwbyI+IfHf/ryMAgQAEE7P79pf5eXWA3DHAbB1v2upWwDaVgBo3/ldM9sJoFoK0Hr5i3k4/EAenqFQyDwdHAoLC+0lYqG9MOOLPv8z4W/gi372/EAe/tt68ABxmkCZrcCjg/1xYW52rlKO58sEQjFu9+cj/seFf/2OKdHiNLFcLBWK8ViJuFAiTcd5uVKRRCHJleIS6X8y8R+W/QmTdw0ArIZPwE62B7XLbMB+7gECiw5Y0nYAQH7zLYwaC5EAEGc0Mnn3AACTv/mPQCsBAM2XpOMAALzoGFyolBdMxggAAESggSqwQQcMwRSswA6cwR28wBcCYQZEQAwkwDwQQgbkgBwKoRiWQRlUwDrYBLWwAxqgEZrhELTBMTgN5+ASXIHrcBcGYBiewhi8hgkEQcgIE2EhOogRYo7YIs4IF5mOBCJhSDSSgKQg6YgUUSLFyHKkAqlCapFdSCPyLXIUOY1cQPqQ28ggMor8irxHMZSBslED1AJ1QLmoHxqKxqBz0XQ0D12AlqJr0Rq0Hj2AtqKn0UvodXQAfYqOY4DRMQ5mjNlhXIyHRWCJWBomxxZj5Vg1Vo81Yx1YN3YVG8CeYe8IJAKLgBPsCF6EEMJsgpCQR1hMWEOoJewjtBK6CFcJg4Qxwicik6hPtCV6EvnEeGI6sZBYRqwm7iEeIZ4lXicOE1+TSCQOyZLkTgohJZAySQtJa0jbSC2kU6Q+0hBpnEwm65Btyd7kCLKArCCXkbeQD5BPkvvJw+S3FDrFiOJMCaIkUqSUEko1ZT/lBKWfMkKZoKpRzame1AiqiDqfWkltoHZQL1OHqRM0dZolzZsWQ8ukLaPV0JppZ2n3aC/pdLoJ3YMeRZfQl9Jr6Afp5+mD9HcMDYYNg8dIYigZaxl7GacYtxkvmUymBdOXmchUMNcyG5lnmA+Yb1VYKvYqfBWRyhKVOpVWlX6V56pUVXNVP9V5qgtUq1UPq15WfaZGVbNQ46kJ1Bar1akdVbupNq7OUndSj1DPUV+jvl/9gvpjDbKGhUaghkijVGO3xhmNIRbGMmXxWELWclYD6yxrmE1iW7L57Ex2Bfsbdi97TFNDc6pmrGaRZp3mcc0BDsax4PA52ZxKziHODc57LQMtPy2x1mqtZq1+rTfaetq+2mLtcu0W7eva73VwnUCdLJ31Om0693UJuja6UbqFutt1z+o+02PreekJ9cr1Dund0Uf1bfSj9Rfq79bv0R83MDQINpAZbDE4Y/DMkGPoa5hpuNHwhOGoEctoupHEaKPRSaMnuCbuh2fjNXgXPmasbxxirDTeZdxrPGFiaTLbpMSkxeS+Kc2Ua5pmutG003TMzMgs3KzYrMnsjjnVnGueYb7ZvNv8jYWlRZzFSos2i8eW2pZ8ywWWTZb3rJhWPlZ5VvVW16xJ1lzrLOtt1ldsUBtXmwybOpvLtqitm63Edptt3xTiFI8p0in1U27aMez87ArsmuwG7Tn2YfYl9m32zx3MHBId1jt0O3xydHXMdmxwvOuk4TTDqcSpw+lXZxtnoXOd8zUXpkuQyxKXdpcXU22niqdun3rLleUa7rrStdP1o5u7m9yt2W3U3cw9xX2r+00umxvJXcM970H08PdY4nHM452nm6fC85DnL152Xlle+70eT7OcJp7WMG3I28Rb4L3Le2A6Pj1l+s7pAz7GPgKfep+Hvqa+It89viN+1n6Zfgf8nvs7+sv9j/i/4XnyFvFOBWABwQHlAb2BGoGzA2sDHwSZBKUHNQWNBbsGLww+FUIMCQ1ZH3KTb8AX8hv5YzPcZyya0RXKCJ0VWhv6MMwmTB7WEY6GzwjfEH5vpvlM6cy2CIjgR2yIuB9pGZkX+X0UKSoyqi7qUbRTdHF09yzWrORZ+2e9jvGPqYy5O9tqtnJ2Z6xqbFJsY+ybuIC4qriBeIf4RfGXEnQTJAntieTE2MQ9ieNzAudsmjOc5JpUlnRjruXcorkX5unOy553PFk1WZB8OIWYEpeyP+WDIEJQLxhP5aduTR0T8oSbhU9FvqKNolGxt7hKPJLmnVaV9jjdO31D+miGT0Z1xjMJT1IreZEZkrkj801WRNberM/ZcdktOZSclJyjUg1plrQr1zC3KLdPZisrkw3keeZtyhuTh8r35CP5c/PbFWyFTNGjtFKuUA4WTC+oK3hbGFt4uEi9SFrUM99m/ur5IwuCFny9kLBQuLCz2Lh4WfHgIr9FuxYji1MXdy4xXVK6ZHhp8NJ9y2jLspb9UOJYUlXyannc8o5Sg9KlpUMrglc0lamUycturvRauWMVYZVkVe9ql9VbVn8qF5VfrHCsqK74sEa45uJXTl/VfPV5bdra3kq3yu3rSOuk626s91m/r0q9akHV0IbwDa0b8Y3lG19tSt50oXpq9Y7NtM3KzQM1YTXtW8y2rNvyoTaj9nqdf13LVv2tq7e+2Sba1r/dd3vzDoMdFTve75TsvLUreFdrvUV99W7S7oLdjxpiG7q/5n7duEd3T8Wej3ulewf2Re/ranRvbNyvv7+yCW1SNo0eSDpw5ZuAb9qb7Zp3tXBaKg7CQeXBJ9+mfHvjUOihzsPcw83fmX+39QjrSHkr0jq/dawto22gPaG97+iMo50dXh1Hvrf/fu8x42N1xzWPV56gnSg98fnkgpPjp2Snnp1OPz3Umdx590z8mWtdUV29Z0PPnj8XdO5Mt1/3yfPe549d8Lxw9CL3Ytslt0utPa49R35w/eFIr1tv62X3y+1XPK509E3rO9Hv03/6asDVc9f41y5dn3m978bsG7duJt0cuCW69fh29u0XdwruTNxdeo94r/y+2v3qB/oP6n+0/rFlwG3g+GDAYM/DWQ/vDgmHnv6U/9OH4dJHzEfVI0YjjY+dHx8bDRq98mTOk+GnsqcTz8p+Vv9563Or59/94vtLz1j82PAL+YvPv655qfNy76uprzrHI8cfvM55PfGm/K3O233vuO+638e9H5ko/ED+UPPR+mPHp9BP9z7nfP78L/eE8/sl0p8zAAAAIGNIUk0AAHolAACAgwAA+f8AAIDpAAB1MAAA6mAAADqYAAAXb5JfxUYAAC7ESURBVHja5Lx5dFRV1rBfgHwYRQQVtB26ZWhtabtfeUGxGxFbUGZF8RMHGkVbRkekVYiKisicVhE0gEwBokgDAhEMMSSQkAECwcxkrlRSqVTqJqnxzs/vj5t7qUyAvr9e37fWV2vtleSm6p6n9t5nn733OVU2RaUaEP5PiqJSbeMXPBTA5/Xhzk9Vnd9vo3HFx21E2LYJX9IRgh6npvyCe9uaqS4K4C3IpXHFx9S99CTuJ8Z0KLVjRlA7ZgTuJ8ZgXxmJL+kIlwAkXBQk6HFq9pWRVA8fSvXwodYgdS892a6EA1UNvouqwXdR99KTeAtyfz2IL+kI1cOHYh9wqwVwKWJqpXbMCOv19gG3Imzb1JF2OgZxfr/NukH4jcNVfyEAE8IU+4BbKet1PfaVke3BtA/i/H6b8aIBt7a4mWmaC0nr55vmqRp8F5V33Mm5LhHtwbQF8SUdsSDCb1I1+K42g1xIWgOYYh9wK+e6RCBs29QxSIWus37aJM51iWjx4so77mwD1d5AHQ1eecedlN9yuyVlva6nrNf14Q7cEmRn4W7u3T2E9ME3UX7L7W1uZg5Weced1s3sA2613ql5LXzQjuRclwjcT4wxTXQeRHC7GLdnHPeensiCVwa3e0PznZk3EbZtwluQa0kofz8NcVNxr++Ce30XnNuv61Bcu7viXt8Fvyu7JYipjfGHxzD+8Bh2j+7fAiZcC+Y0zPDIbCyD6DyV6DyVeDcIQR2C39J4oieNJ3oSOnkVcnZ35Ozu6MVdDHF0N6S4C43OqJYg/0ydzb27hzDx0FjuPT2R+asfa6OVsl7X40s6QoWus/CQk6fWZPHChhxe3lbMCxtyrN9TyxSQSwidvMoC0XK6tRGybPjSRmOuNUKVo4Zxe8YxIu4+Jh4ay/jDY7j39MQWWjnXJYLGFR9Toes8tSaLiavTrIHDxfxfapkCwW8hy9YuhCmhk1fR1FRnaCS1NM4yy8RDYy2tjIkZRXq/HtYsCnqc2sJDTkYsTrU00J6YkEJQR7M/eEGY0MmrcOenqjZA2JmyzTJLuJiOe65LBHUvPUmGR2bE4lQmrk7jqTVZHcrE1WkMWpRIdJ4KnpUXBCHLRl3e16EWIOEaMU00/vAY9na/gsYVH/NdgYe+8w9bMBeSQYsSWXjICcFvL2ga+dhlFwcJ10rjio/ZklprgbSWiavTWvzdd/5hXt5W/OtATC201sq9u4eQ+PVijmSW0nf+YQYtSmTQosR2gUYsTmXQokT6zj9saeRCpmkJ0hxD2gOZeGgsI+Lu45+ps7FXlFmDmDDtSd/5h+k7/zCpZQpa9cwOQciyIR+77LyzFhXlMyZmFOP2jLP8orVWRsTdR2ppHFtSa+k6ZZM1WHvSdcomwyxySceayO4OWTY88TdirygzUkWf18eL2//RQiutYcwYE/Q4tagDOUQ8uo6uUzbRZ3qMJV2nbCLi0XU8tSbrolNXzu6OfOyylgEN4NOkaO5acw/j9ozr0ET37h5imehIZimPL91rAfSZHsOQBfuISS7E7vaTETeX0MmrOoQInbwK+dhlNKWsahni0zPSuGvNPW1M1BrI1NrOwt0WkCn2ijJSS+MYt2ccuQk3oxd36RCi8URPY+HLT1VbgGiSzPsx71laCddMe2Yygf6ZOtuScXvG0XfJn/n8YL+LQnjibyQ34WZ8Xl/bfKSoKL+FVi4EYwKZcu/uIQzaPoExMaPQcrq1ADFX33AI1+6u1OV9HVI6ShU/TYqm75I/dwjTHtDEQ2MZt2ccg7ZPaGGScIDWEBlxc42UoSMQ00StYdoDCgcbtH0Cbx+8p40ZTIBwiFM7RmB3+y+exZvT2YRpDdR6ZoVrw1xRWwN44m/Euf06A6Ki7NLrmnDNmH7TEdSg7RP4/GA/yLK1GdwEKNzSk1M7RlDlqPl1JefOlG2MXTGmXaAxMaMsB/XE34h4tH+7ANlrB7T2iV8OAlDlqOH9mPcsIBPKlF3R16Ad7GwlxoVberYAKCrKv1ghfmkg5sPldLIzZVsLqLErxpC9doAlp3aMICNurlGyVpRdSAu/HqS1Q58rd1JUlI87P1UtKsrHXlGG3e1HCOoov+x2wiX3RxT+o49L1IgutXxVUCfDIxNfLraQDI+M3e3/NdCXbhohqBNfLrIsVzZqmoT6dmXG0SBLTrmJLxd/CVRLECXcDGFaSC1TmHE0yKg4B0P2uxiy38WoOAePHaptAfHYoVqG7HcxcGc5o+IcfFfgsbQUPoYoSa213BbE78oGucTSwpJTbobFFjNgbQHdvi6g8/Z6Om+vZ8h+VxsQE7T/97UMWFvA+Og0UvIryfDIZBQ4CeXvt8a5IAhAY/RImlJWUaHrPHaolhuXFXHN+8e58qNcbomq5P6t3xG973WePLzPgnnsUG0LiP7f1zJwZzk3LisyctfSOFxOJ4lfLzYToQubxu/KpmpWBFWzInguOokrP8ql7/zDRMxLpFfUabasHwlZNnITbmbgznI6b6+3Bu7/fa2lrW5fF9Ar6jQD1hYwLLaYx5fupdi+EiGok748koa4qa010xKkKWUV2UM7kd6vB7tH9yfpnUFkLzQiZOGWnmgHO9N4oie9ok5bA4YPbkqvqNNc8/5xIuYl8tSaLOLLRXambENXF+PxNJD0ziAanVHhYaEliH1lJD/1iqD0qSsIzu2M/N550TZ3QjvYmS3rR1qDtwdhgpgwnabGMj46zRjQsxJdXYw7P1X1pY0GuaRjkMKxPah5qxuV8y6nct7l1LzVDfdyo6miHexM+ou9mblwKfdv/Y77t37HNe8fbwMQDhIxL5FOU2PZklqLJjUYdU7wWxBuN+ricBAF0KQG6pcNovZpw0fCQao/MEBcu7tSOLYHjnu7EZzbmeDczqyfNokrP8ptMXi4XDnzAJ0n72TIgn1oUoMB4VlpgIjj24I0payi9KkrqHj+Ssth2wM5c38f8p68D2nbHKRtc3h86d42A/eZHsOVMw9Y0nXKJmxDvyS1NA70z8Gz0qh5hNvbzpr6ZYMofzyiBUwLkOVdjfR/eVcao0dSl/d1aHx0GhHzEi0TXDnzAJ2mxtJpaixdp2yypM/0GLrcs5D3Y94ztNDsK7qjuxmzDBBz2rYGqZoVQc1b3dr4yfppk+g8eWeLd91aAxGPrqPbyKV0G7mUiEfXMWdz+nmQ0Jsgn1AbT/SkMXrkeZC6vK9DpU9d0S5I5bzLqf6gq6UV7WBn5q9+zDJBuEQ8us4SE6LLPQvpcs9CjmSW4ndlo1XPNBxWLiE34WbSX+wNapEBEsrfT/njERSO7WGBmDA1b3Wj9KkrSO/Xg1WjBjJl/CT+8sQ8a0BT/eGDhwN0uWchXe94ia07YkE+oSLc3gxyQt2yfiSrRg0E+YRqgRSO7UHh2B4UT7ragqmcdznFk67mp14ROO7txpTxk7AN/bLFgN1GLsU29EvrejiACdG59xQjKgu3GzVP9UwIvcmCVwYb102NmBHVBDFNVDUrgjP39yF98E0E5xox5Dcj5lsDhwOYQObg4dK59xR2RV8D4njEo/0NIEd3dkVfgy9t9HkfMTWSO6pXG63kjupF8aSrqXj+SoJzO1M573KmjJ/Eb0bM5y9PzGPBK4Mp3GKUEFvWj+Q3I+a3AOjcewp/eWKesUQ0T1mz2att7oSU9+F5EE2SqXvpSbKHdrIGNmHCoapmRVgh33LezZ3QNncyloGDnVnwyuA2IFvWj0Q+dplREzu6Wy0r9/KubVvg9pWRpPfrwZn7+1haMSHCxdSM/J4RWWufjiC9Xw/m9PgtN9w0uo1JbrhpNI0njAXTrAIbT/TEvb4LjdEj2641vqQjpPfrQfrgm1qYKHxKlz51BbmjerFj4G2WtAYwtWDKglcGG2ZoXrldu43AWDUrAmnbnLaRVZMayHvyPn7qZThoa38pfeoKap+OIDi3M6tGDeSGm0a3GTT82g03jeaGm0bj3H4d8rHLrN0I93LDpDsG3kb68si2a425hfZTrwjSB9/UBiZcM+YM6ghoyvhJpL/Ym+yFhknc67tYQVF+z3gjc3r8Fuf32zpOFTMeHXpRGDNfMYF2j+7PqlEDWTVqIOkv9rZ8SNvcCff6LlTOu9yK1Okv9mZOj9+S8ehQNKmBDhs17vxU9adeES1gwoHKH49oFyhcwhfKynmXWzOu4vkryR7aieyhnQjl7+84QzNNJGzbxN7uV1gw7WmntYZaLw2mmNdrn44ge2gnztzfx9od7zBnDa9t0pdHtgsTDhRustaaCndwEyLj0aG481PVS9r3FSUJj6eBrConMZHvnodpntrh2gkHCgcLl/TBN7G3+xXGLMlIo0LXjU7ixeoaUZIQ3C7OlTtJya8kJvJddgy8DctvWgGFaylcHPd2Y2/3K5jT47esGjWQrTtiyapy4nI6jUrvUmpfUytFRfmkZ6SxdUcs66dNYsfA2ywNtQBrJeb/dgy8jZjId/kx4YgF4fP6Ln1L3uyhhWvnSGYpOw6lEBP5LuunTWLDAw+x4YGHrAi74YGHWD9tEuunTSIm8l227ohtAyBK0i8/pNDagTVJxuf1YXf7OVfuJKvKMF16RhrpGWkcySwlJb+SrCqn1awRgjqaJP9nO0b/Zxo1v+ahS0ZqKJ9QCX5rJMyhN42aRj6h/udB5BKjiAp+i64uNrJ2M0Vs3rUiy4aU92G42X49iCYZDZjUMoX4ctFIcILfGgVU6E0LwEyCxKP98aWNxpc2GvFof+RjlyHlfdjxWnOxh93tJya5kIWHnDx2qJbnopP4NCmaYvtKC0LL6WYkQps70RA3laaUVbjzU1V7RRn2ijK8BbkWUJsM7VIAog7k8MyuPKtD1AJA/9zQQpYN9/oubFk/kpkLl7J4a0KbtrdZa/vSRrfMWS8GcSSzlGd25TH5VIjptTpR9T5SS+OMsrHZD3RHd7SDnTm1YwSzY2KsTtL46DSei07iSGZpm/tKeR8a5gnf0+vI8zfE5zAstpjptTrvifBJeeZ5LTQDkGXDtbsr0fte59mjDmaWaUyv1ZlZpvH3XJlRcQ6Grj5OTHJhy/t7VhrpwMVAog7kMCrOwcs+nZWaccak2L7S0oLpC6d2jGDJiUyWN8E6FVZqsLwJ5ruwYO5O9jFoUSIb4nPOT+/gtxf3kZjkQobFFreAaHRGGZoQbm+hhWd25fHsUQevHilgbo7bAmoNM2S/i6Grj3Mks9Tolcgn1Hb39MzHuXInw9edZrJd4z3xPISuLrYgCrf0ZOuOWKLzVFLLFDIKmlfr5EJmHMxhfoWvDczkUyELxl5RduFUUZNkIvdm8+BpkZd9eocQPyYc6XDnocpRQ+TebObmuFmptdTK5FMhBqwt4K1vMi4cWTMKnIyKczDZrvFJeWaHEBdrbVc5aphxMIflTR1rJaPA2TFI1IEc7k72tZwdYRCLtyZc6h4MMcmF7WrlwRSRAWsLiNyb3T6Iz+vjmV15jIpztIHwxN/I7JgY4svFS47CHk9DG62Y5hm4s5zx0Wntb0CnlikMiy3m06ToFpFSO9iZnSnbeGZXHkcyS8kocF6SHMksZc7m9AuaJyW/si3IltRaZsfEGNM09KZVs2bEzWV5EyzLlXn1SEG7MuNgTruy5JS73dlzd7IvPMi1BIlJLmRnyjbLJFawar7ZHi5NdrSS9jRyd7KPXlGnzQDXyjSlcYY2mk1SuKUnS05kslI7f9M9/HKgdaoh74nn/cR02NV7M9t2A9A/t/qf2uZOvB/zHvNdxk3Mm0bV+36VzK8wxHTWVutPmEbkE6q1hjQ3/yefCvGeeB7k1SPGlLsUeeubDOtnezJnczpvfZPBuXJnGEjzAqSri9FyulG4pSf3b/3OCvErNQNmxsEczpU70ST5kuWXJc9yiZXemQ3du5N9TK/VedmnW1qZm+M+v3r+gpTS42nA42nA5XRa4vE0hFd8zSDBb63cInvtAAYtSuTuZB+T7ZoFYy7tz+zK6+igQZtHRoGTyL3ZLab4M7vyGB+dxpAF+1i8NaEliLmWyNndsa+MZPi60/T/vpaJhTKT7ZqllZWaoZW3vsnA42m4IMS5cifPRScxN8fNeyK87NOZXqszsdDITa55/3i4dgVb0OPUTG2IR/vjzk9Vt6Qau5R3J/uYWCi3MJEJM2dzOkcyS80Q3WKrPia50IIIX2cmnwrxYIpIr6jTPBed1Mo0apFgpv0NcVMR3C5ESWLO5nS6fV3Ag6fFdmHmV/iYcTCHyL3ZRB3IsSRybzbP7MpjfoWvXYj+39cyZME+c7aEgTQ36smy0RA31dostrv9DF193IIJ9xcTxgSam+O2xAQwg9fMMo2JhTIPnjYgBi1KbC+RPq8REyR8iT9X7rRgWptpvssYLBwqHGB6rc7fc2ULYsh+F4MWJbLjUErH09c8ytcaxNTMCxtyGLC2oIUDT6/VO5TJdkMLJsTAneUMWpTYNotvE0eaj3rKxy6zun2t69mdKdt4fOley4lN35ls11pIOIC51D8XnWQu9xcGUQCteibyscuM5n31TKNqD5fm1H9DfA7PRScxdPVxhsUWMyy22Dq4MGS/i2GxxQxfd9oC2HEopb1WVcdtCU2Sqcv7OmTWpGbRLOV9SCh/P0GPUwvPvDIKnMQkFxK5N5s5m9N5LjqJ56KTeOubDFbvzSQlv7LN1P5FxzZ8Xp918v8SWk5WsWStLbr0a5oLHRdY/+GjPP8vtq7+0yCiJOHz+hDcLlxOJ2bzxeV0Irhdlk/9x0B8Xh9VjhoEt6s5rZTaFU1qQHC7qHLU/PpZ05EGqhw1uJxO0CVESSIlv5KoAznM2ZxufTJgzuZ0og7kkJJfaR1mcjmdVDlqflkc6ahSs1eUWdMzJrmQQYsSrYMJNy4raiHmYQWzD2IC2SvKLpa/dAzi8/qsc6cZBU6GLNjHlTMPcEtUJVMSdd45qRGdp7KxDOvDPu+c1JhxNMgtUZVcOfMAQxbss0K7vaLsQqbq+GCtCbEhPodOU2O58qNcZhwNsrMK4t0Xlp1VMONokCs/yqXT1FgrE7sATPvbJK0hblxWxDsnNWugvc7zcqFry3JlbomqbANzSdskpk9kFDjpOmWTpQnzne6sMgbbWWWYY8kpN0tOuYnOU1v8z9TcOyc1blxWRNcpmwwz6dLFjxr7vD5rY+eO13YSMS+Rh/co1iAby4wBluXKLDnl5rsCD1lVxk7FdwUelpxysyxXbvHcjWUwYb9CxLxE7nhtp7X10spELUHMMiHqQA6dJ+9k8KYaJh1u6ZRLTrnZklrb+hS3lURtSa1lySm39fyNZTAlUWfwpho6T95p1rqtS5LzICapJsmWNkbEBpiSqLMs1/gY3DsntfAuT4tDlkrYtci92bxzUmNjmaG9KYk6I2IDbbTStsBqjhma1EBKfiVdp2xiwNoCHt6jMOmwxjsnNev46KWUkaIksfCQk2W5Mu+c1Jh0WGPCfoUBawvoOmWT1d4Miy3nQczIuXpvJp2mxjJ4Uw0T9hsg09KM6fhcdBIxyYWXJM9FJzHjaJBpaTDpsAEzeFMNnabGGhVec+RtA1LlqAFd4vGley0Q8wZTEnWmpWGdWX3sUC3PHnW0K+b/n0qoZ1oaTEszfCQc5PGle0GXwv0k7PxI87S9EMjMMo35rvMdILPDbErrzlA4iOmw4SBh0/iXgUxLg8mnQvw9V2Zmmdau/D1XtpoxpiYe3qPw8B6FW6IqreOCvwpkWhqMinMwaFEi46PTfrFMXG38HLr6OHe8ttPykXZNYzrr4q0JdJoay4C1BS2cdfCmGuZsTrd6Hv/T5ozZJ7no9L1xWZE1fU0bD193unXx3GESFZNcyIb4nDazaUN8Dh6PkTy1O307CmgT9itM2K9YWnkuOumi26wTV6dZR43NXOXKj3LpPHknEY+us0DaDWiWnwCr92bSdcomBm+q4eE9ShsThTXh2jRn5mxOZ/CmmjYzZkRsgE5TY40Q33bhu/iiF66VcJjh604TuTfbUnnk3myGrzttQZgzZtJhzQrvfabHWGNccNELnz2tfSUcJjxADVhbwIC1BdYsMyOp+fyH9yhWGnAks/TS0gDTV4qK8q2NxU5TY7klqrIFTDhQ6+gZ/hwzdoSbpKgo/9LPj5hnR8yUwEwVw810MRkRG7BSRXPpLyrKv/RUsT2YI5mlLZLnEbEBK1q2lhGxASt5vuO1nZY5ioryL5TJX7icENwuioryjV1rr4+oAzkMWbDvouXEkAX7iDqQg8/rQ5MaLgZxaQWWJslWSWkWWBkFzl9UYP2PvgjFPNrj8/osM/2YcIQfE46QnpFmfL7K7SLocWpBj1Mz6+D0jLQWzzPb3b/6aI8SVnCbvXTTVOZxno6kqCjfKlPNUH4pIP9XPGz/N319UFnrf2iKLGi6LmggqCBoIOi6JuiqIqCrgqIrgqyrgoYu6JpiiK4LKgigCpquCCEdQdVVAU0VdP2iMGW29tplmtbcQNQ1QEXXNDQdQGsWHZBbvdQsKkTQfaiaBJrc/PyLPpQ2zqqbL9U10GV0TUbTZUCyQAoaJPaVinx5RmbVKZnVWRpf56r8WKlQFww2Q4bf8VdMXwsEtfkdGb97xSAb8yRG7df4zYYQ3deEsK2WsK1UsK1U6LIqxJWfKQzcEODVw0GS7KbG1F8Pout6C7WuL5Dpv1PBtlLEFgWXfyHTY61Ery91rvkiwLWfB7h6jcxV/5LoskLF9gl0+tjLI7FesuuxzKnrHeqneQdL143Bjacj6wqg4ZFUph8JYvusCdsXIldvhGvXi/T+SuS6dQrXrZO4fp3Ib76UuH5NiD6fi1z/mcgNnwa5epWMbbHG1StEvsoSjbeoq2i60h6MYNN1XTAhNF1vdlBoVFSG7/Nh+1Ti2o1Brl8v03uDyDVfN3DDVz5u+FKh15cKvdbp9FoHvT5X6PW5wjVr4LrPda6NkugTJdL1EwXbIpkVx5sdGaXZ8S9gGgNIJ6ipPHgghO3TED23h+ixTafXZpmb1ofos0ml+9dw1VcaV3wapMvKIF1WSVz+qULPzxV6faZw9Wc613yq0Xt1iN9Ehei+WMG2QObz03JHDtxsGk07P2XRmZ/hx7ZG5rqtMjdubqTHFonrNov8doPMZRvA9pmPqz8X+MNWhb/tkrg/VuGWaJXLPmmk85Imen6m0+sz6BMlcsNqP9etVujysU63jwIcrwy1N6UFm6Zrgma4KKBxrE7lyq999PnaT58dcMNWjV5bFa7d6sP2lcj/+szP6/FNHK2SqQtpSKqIKItUN2psyJH52yYXtkV+uq9UuP5fMj1XqVy9WuWGFSE6LQgxbHMQv6kVXW92B12wKZouSEjGNNMVJvwgYdugcGOsym+2q/TZqnD9dh3bVz5u3h4guVJtnpJa808zkJlBMMS7SQG6vB/gimUKvVdK9Fmu0nu5zLXLZGzvaWzLDhggmoysqwYIKoKqG+rKqVO5douP62JUfvutxg2xCn1iZTpv0rgpRuF0XQAIgRJElSUURUWWZWRZRpFlgrIKeIEg7yaC7X2FXkslei+XDVkmY1sQ4pFNDaA3hwcdNF0XbGjNZwNQWXZaxrZV5XexMjftFLnpW4ne34rYNvjZUywBQUJqEEkMoEk6oqIgySqipCCKEt6Qis8fRNEaAB+TtijYInV6Lwtx7VKRPstkIj5S6PGBRGFtwFCgApquCDYFTQANXZeZkiARsVPnlu9kfhcr0/cbiYivA4w94DM0oet4VQVJUQiJGiFRIiTKBEMSAX+QhoBIvU/C1SQCfpIKGrl8kZerFitcu0Tkuk9ErlsiYXtDYuMpYyobE0gVbIouC6DiDsgMiwtx406Z/rs0+u6WGPCNSI8tIZbnSoCCEvITkBRkWSMUkAgEJbz+EE2+IA3eAPUNjTR6fNTWSni9PuoFN/d8KtBpkcg1n3jp82GQ3h/6sc33seAHb/P6pYOmCTY0VQCNEkHhrgMhfrdL5k/fafT/XqT/boU+sRI/2r0AhESFYFDCF1TwBSWa/CE8TQHcjQFcDX6cdQGq63w43PWU1AoEmup4emMjtvl+enzop/d7Aa57N4Btvo/Z37jCHBzBpuqaAHDOHWDo/iD99in8+XuZO/er/H6fxsB/h0irDgGqoYGAhOAL0eALUd/oo87TRK2nCUddI3anQKXTTUl1DUVVNXga6nh2mwvb6066L3Bz3btOekU2YXtd5MVNDmuVVtEFm6brAmiUu4OMPODnjv0idx+UGHpQ4q6DEnf928+h0iCg0egN0OgXqW8MUCd4cXm81LgbqHIJlDs9lFd5KK90U1hWQ3GlgLOqlrs/rsQ2q45rFjq57q0yukc6sL3iYc62akBDR0fRNcGmq5oAQYSAyuQEibsPhnjgkMYD8T4ePOTnv/ZrfJrtBTWE4A3ibvRTJ3hx1jdRXddApbOeMoebEruL3Ao3p8vqOVVSR1JuDZkFtbywvgDb0zl0eqmanm+Wct2bFdhmlvP2Po/hH6qIrmiCTdNUAVVElTVeyfTz10My435UGHNE5JGfJIYf1ZiV4kFo8uILBKirD+LwBHC43Dhq6ymurqfAUU9ORS05RSU0NHmQVRW/JCMqOho6354U6DEri04z8+nxWim2fxSx8ZgLEAlJCqoiCzZZUwVZVECDjUVNDD8s8sRRlSmJOs8mwbPHZJ466iO2yI8aDNJU56a8tpGqaicOZx2FVfWcLa8lq7CMBn8IHfAGZQKSik/SQAoBOjEZtdiezqTTS/l0fzmPrFIBNB9CUCcoSYJN0TTBKymgS5TXBXn8pwCTj8lMT1WZmarx+nGR2Rk680+GOFleh9/bgMtZR3V1HYWVLvJKajiTV0pVjRsV8IVEgrLaLApeERSCAAx5/xS2+48zZvlZAmKIQFMTHq+PppAi2DRdFQIyyKIfRImoMz6ePO7lpUyJl08r/PN0iMjTEh9kS6zNEUgp92GvaaDAXstZh4DLG0JoChAMyviDMn5Jxi/K+EISIVnFr0h4JWPZ33a8mNteSCI6vhpZbMDhaqChyYfHHxRsmhYURBECoRDoMvkukVfTFN7IlHk7W+aDXIlVOSHW5ob4qhi2F4v8WNLIiSov5wLgkVVERSUYMqa2LyTjF1UCkkpQ1vGLImJAxCsai2SdKFJQ6aG0ooqK+gBuVxOCTxBsuq4IkqQSFCVCkgyqzg8lXt5J9/H+WViVJ7G+KMSOEoVdJSp77DJxdRrH3Rq5goLDJyMERRqCIt6QbPiHqBAQFSRJJSCrhGSZJklDUs/nIefsNRRXe3DWefE0NjUf21BURFEiGDRWVH9I5Nu8Rt7Pk/lXocbWIpFvKzT2VSr8YJdIcEqk1Svke2TsPhV3SMYTEmkISngDCr6QTFBSCUkqQUnFL2kEJUNLflFF1aGuyUepow6HuxG34DdyVkVRkCQFUVLxBWR0ScEfFPmuuIG1hTIxpSr/rpA46FBIqJHJdGmcqVPJa1Co9MrUBiTcQQlPQKYhoNAUUvCJCn5JJSApBCTZEr8oEVJU/IpKiaOOmnov9Q1+QyOqqiErGqKiIYk6/mAATQ4QalRItPvZU+EnvkrmxxqJRJdIVp1KTr1GQaNChVei2idTE9BwBRTqAzKeoEyjKNMkKvglhaCkNAMZogAeX4DS6npcDQE8jYHmM0aajqLqyLJOSNbwSTJev0woEKCxyU9OdZCEkgAJ1UGSBYWsBo3cRihq0qj0KVT5ZBwBjdqQRn1IRhBVGiWVRlklqOiIikZQ1hAV4ytjJE2n0ummqt6LU/AjNAYEm64jaBqoqo6iaEiKhiirBESVhkAQr9eH0ChSUu3nVGkdGY4mUmt8ZLoC5DWoFDUplHpVKnw6VT6ZWn+IuqCEJ6TQEFINzUgSflXFJ8nUe304XALVdQ3UNwaob/TT5A0ZILoO4TCyrBKSZHxBGcEfxNPgpdETwO32U+ZoIKesnrPlHrLtbn6urCfPXk+B3U2R3cO5qgbOVTVQUilwrkKgtEqguLKe4sp6yhwNlNg9VLkEhKYgjd4QTX6RYFA+X2Dpuo6maaiqiqqqKIqGKKn4QwrekERjIIC70YenMUBjk0S9EKK23our3ovb48Xj8SI0BfD4ROq9IdyNQeoa/Lg8AZxuPzV1PuobRASfguAN0egP4Q1KBEMykqwKNkAxMnpDNM1oSxhQGrKiI6oqTapIkyTiDYUIiDLBkEwoICOGjHghKxqKqqCoEooqEVJFgkqIkBIiKIsEpBB+MYA/FMAXkgiICiHRmK2KoilWo6bZRIKu61bjRdd1QdEQVBVBkzRBlVRBFhVBVTRBUXRBknVB1hAUECQQNF0XUHVB13RB0XRBVDRBUjRBUTVBUlRBlBRBlGQhJGuCJOuCouiCpuqCqqpl/7Eemqor5HnS2Ja/hPezpvCP1PuYlfo3vvo5EnfA0baH9qs+CKZpBIIh7DUuyuw1lNprqHDU4mnwoqoamq5xyn2YVTkv8cKJO3n+TH+eTB7Ao/H9eSr+TnbmrfyfgdiddZzKKaK0yklhuYN6oWVfvabay+6Tu3gzaSJPpPZm9E9XMmnvH1n60wKSanZypuEg35WuZlrCMLb9vPSXgzicdWTkFLX7vya5Dq/spk62s8v1AW+cu53ns29kSd6z/Fi9mZ/L8tpqVFfZeHYxBe7MSwdJy85v8Xd1oJwDFRtZlTeTD88+wcKsMSzMGsv8rL8wNbMnc7LuJN6xg6AcsF6TW1xBkzfQct9P8pDrSkfT1QuDKKrKz8UV1t+V3kKi89/m1YyhvHlyMPOz/ouFZ4fwYe59fJAzjLfO3s66wuep8p7jbF0iUTkzOe76/rzZ6jxUVteGtch06gL2C4PIikJFtcv6e3/ZeuamDOHNU//NivwxfFY8jnXlE/iyYiKflz/Eh4WD2Gv/CL/YQIJjI2+dvJvXTt7FtJS+LPt5OvVBY383KEoUlFaGzSz5wqb5ubC0WSsyG3PfZUbKnXzw8wOsKX6EdWUT+NI+nq8cY1nrGMnikjuJd0Xhld1sr3iTt37+IyuLHmZN0WMszxnPzLSBvJnxMMWNPxv7vUITLrdw8VlzMswnNud+xD+O3cGy3LF8ce5R1pZN4IuKsXzlGM0X1SP4uPJ2jgpraJAcfFb+CJHnbuOz8pF8UT6OL0om8nnRJFblPcrLaXfxxolROHzGd2idq7xIHBEavTQFQwAcLNvMP5Lu5JOcsawpmsRnJROIKnuYtVWjWVP9Vz6q7McRz0pUTSa2Zh6LSgeytOJPfGa/j3UVY1lTMoFPz01kdcEjLM95hNmp/8UHmU+j6MYnlrJyz3UMknHW0IbDW8rLyfexIGs4nxU8zqqi8Xx07gGiKkfyheN+ltnvJEFYGdYOFWlUqjniWcGK8iFElQ1jTek4Pi2awOqCR1iZ9wgfnx3Hs4l9+aHc+BqH2voGRFFqC+JpaEKSjOR2Y84iZqX8majcx1ieN57Xc+/hvXPD+aziAZaX30VGY0yH0/1s00E+KR7KquL7+ezceFbnT2BFzkSW5Uzg7VP38UbKQ3hCdc1aKWoLktHsG06/nbnJ9/H+6VGsyJnIC9l38kreMNaUPsKSkkHsdy26aABMcK3lw4L/5l9FY1mdP56lOeP55Ox4Psh+mOeT7+BAyUZj17O8qiWIKMkUlNoBOFQaw4zkQSw+M5bZp+7in7mPsKnkFVade4DPSsfTJNVeFCSk+lhbPIVl+Q+wMnccS8+OY/GZsXxwZjTTj9/OkqwXACi3O/H5A+dBKhy1lFQac33t2bf5R/KdvJnxFxadnkSyYzuf5j3BssIR/Kt4DBvLp/NF2dOsqXiSNRVPsKbyCeNnxZN8XjaFz4ufJrr4Bf5V8Agr8h5iWc5YPs4ey4enR/P+6YeYnfZn3kh9CAUfqgz2Gtd5kLOFpZTYjUMHH516jmlJA3jjxHCO2XexteBtFpwZyqqC0awo+huLCv7Eu4W38V7x73mvtD/vl/Xl/bJ+vFfye94tuo2F+X/g3dw/szT/b6zIHcMnZ0fz0ZmHWXT6ISKzRvJq5mBeSh5MSeNZyzyyrBggWTlFlNsNssiMKYz9oQe7i/9FmmM/r6bezZKfx7Is5yGW5f+NFYUjWHXuflaXDmN12V+JKhtGVNkwVpX9lZXFw1lRNILl+Q/ySc6DfHRmFIuyRhF5ciRvZ/6NNzPvZ3baIJ5N+AM/1xsfXcg9V47XH2wLMidpFE/9eAcVQg7Lsp7j9fShfHTmIT4+M4rIrKG8ljGAeSf78eaZfszP7sc/z/bln9n9mH+mH29m9eO1jP7MPfF7ZibfxvSE3zP1UD+eiruVxw/cxIT9fRj+764Mje3M6bqjAOQVl+MPhgyQvHPllFQapnkhfgRf5y7haNV3PJvwe945+QDvnnyAf2bcQ0zR22S7fySzbj+Z7n2cdO/jZP1eTtbvI9O9j8y6fWS49pHm3Edq9T6OV+0luXIPRyt2k1C+i/jybzhYupUfSrfjV40wX1zhQNN0A8RR66bEbjjr5p+Xc9IRz9snJvJ88h94O/N+3s64j1dS7mJLXuT/v0e/vT6qa93nnVXXdXLOlRtJi6qSWLmL8Yd682rGvcxLG8qbJ4byRuoQXj56L+UNuRcdoDHk5kDJNvaXbuZA2Rb2l21hX9nX7C3byNaCKJKr4pqnbw3+QLBlQDttxn4dPsh4hseP3sjcjP/m5dRBvJYymNdTBjMtvh8rT865KMja0wsZvqsr4/f3ZNyBnjx88CpGxV3BiAM2bt5iY8PPKwz/KKlsG1lDooTgCRJAYPKR/jyb2pcZaQOZdfyPzDn+J145/l/MSfojU364lW05yzuE2F30FU/80JcZSQN5+fifmH38Tmam3MGM1Dt4LOE6pv90DyHFCGLZ+SXtL3pn88rJCR5hbPy1TEq6jqnJv2XGsduZdfwPzD52By8n/5FZSX9g8sGbeDflGU7VHMUTqKMhVM/Z2hMsSZ/JY3G38I/E25l77I/MOv4HZhy/nRkptzE1+Rbu+beNhMrvjLEKSi+cj0T+8AaPZfTi2eQ/8Gj89fz96C3MSB7AjOTfMzPpNmYn3c7MowN4/IdrmXKoPy8l3MtLP/2Fpw7fxiMHr+HFxH7MTrqNmUm/56XkAbyY3I/pyb/jr/tsRJ542hqnOGydaRdkxv6J/DXBxvflX/Fd0Rru2W3jmYTrmZnUnxlJ/ZhxtB+zjg5g1tH+vJBwM1Pjr+fZ+Ot5PuFmZiX2Y9ZR43kvJfXlpeR+PJ90M3/da2Nm4gME5MZ2c5F2QV5OeYA/7rZxrOYgANE/f8S933ViTFxXZiX1ZfbRvsxK7MusxFuZnXgrs8JkZuKtzEi8lZlHf8espL48Gd+Lu3fbeDVpLA1BY+kvc7T7ZTktQUQlyLQjg/nzv20cyo+zrsdX7OKR/bcybLeNp368hpd+uok5ib9lbuKtzfI75ib+jtmJv2PGT7fwfMJveOj7zty/O4JPs+YjKsYUdTc04Wloav/YRusLz/04lAeTIsgsPENewfnc0is1EH32Qx47MICH913F+O//F+O/t/H4wW7877gIHtnfhXHfd2Hs91cyZl9v3k19lgLPaev15TV1NDR6Oz4/0vrC26ceYVhcL45X/GB4d2Eljf7Q+cJI9pHqiGPVqVeZd+wRZicOZ0bCvbyS9DAfpD3PnnNfUuO3ny9NVI2T+eVI8oVPGrUB2ZsfzX1HehJTtMK6FgyJZOYW0+gXf1EIz8wro9LhvKTn2lrugkMoFOS5n/7C0APXYK8tb3GepMrh5HB8Cmknz5JbXEpBSQVlFbVU2N0UlVWRW1RK1s95/JCQzMkzPyPLMpqm4ff7CQQChEIhJElCURQ0TcPsVOm6fn6tCT+oUOkq4bGE27n/qzv4KeMIwVCQQCBAbV0ttXW1VFRWkJ19lrS0DJKSj5F4NInk5OOcPHmK/Px8amtrcbvd1NTU4HQ6cbvdNDU1WTCyLKOqaguYDmvfgNzE4bIYdpWv4UT5EezuMkQl9B877PT/DQC7cLwx8LR3hQAAAABJRU5ErkJggg==) no-repeat;padding-left:40px} + .browser .browser-firefox{background-position:0 -34px} + .browser .browser-ie{background-position:0 -68px;margin-left:0px} + .browser .browser-360{background-position:0 -170px;margin-left: -27px} + </style> +</head> +<body style="margin-top:50px"> +<h1>璇峰崌绾ф偍鐨勬祻瑙堝櫒锛屼互渚挎垜浠洿濂界殑涓烘偍鎻愪緵鏈嶅姟锛�</h1> +<p>鎮ㄦ鍦ㄤ娇鐢� Internet Explorer 鐨勬棭鏈熺増鏈紙IE11浠ヤ笅鐗堟湰鎴栦娇鐢ㄨ鍐呮牳鐨勬祻瑙堝櫒锛夈�傝繖鎰忓懗鐫�鍦ㄥ崌绾ф祻瑙堝櫒鍓嶏紝鎮ㄥ皢鏃犳硶璁块棶姝ょ綉绔欍��</p> +<hr> +<h2>璇锋敞鎰忥細寰蒋鍏徃瀵筗indows XP 鍙� Internet Explorer 鏃╂湡鐗堟湰鐨勬敮鎸佸凡缁忕粨鏉�</h2> +<p>鑷� 2016 骞� 1 鏈� 12 鏃ヨ捣锛孧icrosoft 涓嶅啀涓� IE 11 浠ヤ笅鐗堟湰鎻愪緵鐩稿簲鏀寔鍜屾洿鏂般�傛病鏈夊叧閿殑娴忚鍣ㄥ畨鍏ㄦ洿鏂帮紝鎮ㄧ殑鐢佃剳鍙兘鏄撳彈鏈夊鐥呮瘨銆侀棿璋嶈蒋浠跺拰鍏朵粬鎭舵剰杞欢鐨勬敾鍑伙紝瀹冧滑鍙互绐冨彇鎴栨崯瀹虫偍鐨勪笟鍔℃暟鎹拰淇℃伅銆傝鍙傞槄 <a href="https://www.microsoft.com/zh-cn/WindowsForBusiness/End-of-IE-support">寰蒋瀵� Internet Explorer 鏃╂湡鐗堟湰鐨勬敮鎸佸皢浜� 2016 骞� 1 鏈� 12 鏃ョ粨鏉熺殑璇存槑</a> 銆�</p> +<hr> +<h2>鎮ㄥ彲浠ラ�夋嫨鏇村厛杩涚殑娴忚鍣�</h2> +<p>鎺ㄨ崘浣跨敤浠ヤ笅娴忚鍣ㄧ殑鏈�鏂扮増鏈�傚鏋滄偍鐨勭數鑴戝凡鏈変互涓嬫祻瑙堝櫒鐨勬渶鏂扮増鏈垯鐩存帴浣跨敤璇ユ祻瑙堝櫒璁块棶鍗冲彲銆�</p> +<ul class="browser"> + <li class="browser-chrome"><a href="https://www.google.cn/chrome/browser/desktop/index.html?hl=zh-CN&standalone=1"> 璋锋瓕娴忚鍣�<span>Google Chrome</span></a></li> + <li class="browser-firefox"><a href="https://www.mozilla.org/zh-CN/firefox/new/"> 鐏嫄娴忚鍣�<span>Mozilla Firefox</span></a></li> + <li class="browser-ie"><a href="https://windows.microsoft.com/zh-cn/internet-explorer/download-ie"> IE 11 娴忚鍣�<span>Internet Explorer</span></a></li> + <li class="browser-360"><a href="http://se.360.cn/"> 360瀹夊叏娴忚鍣�<span>360 Chrome</span></a></li> + <div class="clean"></div> +</ul> +<hr> +</body> +</html> \ No newline at end of file diff --git a/index.html b/index.html new file mode 100644 index 0000000..d20d02e --- /dev/null +++ b/index.html @@ -0,0 +1,215 @@ +<!DOCTYPE html> +<html> + +<head> + <meta charset="utf-8"> + <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"> + <meta name="renderer" content="webkit"> + <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no"> + <link rel="icon" href="/favicon.ico"> + <title>鑻ヤ緷绠$悊绯荤粺</title> + <!--[if lt IE 11]><script>window.location.href='/html/ie.html';</script><![endif]--> + <style> + html, + body, + #app { + height: 100%; + margin: 0px; + padding: 0px; + } + + .chromeframe { + margin: 0.2em 0; + background: #ccc; + color: #000; + padding: 0.2em 0; + } + + #loader-wrapper { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + z-index: 999999; + } + + #loader { + display: block; + position: relative; + left: 50%; + top: 50%; + width: 150px; + height: 150px; + margin: -75px 0 0 -75px; + border-radius: 50%; + border: 3px solid transparent; + border-top-color: #FFF; + -webkit-animation: spin 2s linear infinite; + -ms-animation: spin 2s linear infinite; + -moz-animation: spin 2s linear infinite; + -o-animation: spin 2s linear infinite; + animation: spin 2s linear infinite; + z-index: 1001; + } + + #loader:before { + content: ""; + position: absolute; + top: 5px; + left: 5px; + right: 5px; + bottom: 5px; + border-radius: 50%; + border: 3px solid transparent; + border-top-color: #FFF; + -webkit-animation: spin 3s linear infinite; + -moz-animation: spin 3s linear infinite; + -o-animation: spin 3s linear infinite; + -ms-animation: spin 3s linear infinite; + animation: spin 3s linear infinite; + } + + #loader:after { + content: ""; + position: absolute; + top: 15px; + left: 15px; + right: 15px; + bottom: 15px; + border-radius: 50%; + border: 3px solid transparent; + border-top-color: #FFF; + -moz-animation: spin 1.5s linear infinite; + -o-animation: spin 1.5s linear infinite; + -ms-animation: spin 1.5s linear infinite; + -webkit-animation: spin 1.5s linear infinite; + animation: spin 1.5s linear infinite; + } + + + @-webkit-keyframes spin { + 0% { + -webkit-transform: rotate(0deg); + -ms-transform: rotate(0deg); + transform: rotate(0deg); + } + + 100% { + -webkit-transform: rotate(360deg); + -ms-transform: rotate(360deg); + transform: rotate(360deg); + } + } + + @keyframes spin { + 0% { + -webkit-transform: rotate(0deg); + -ms-transform: rotate(0deg); + transform: rotate(0deg); + } + + 100% { + -webkit-transform: rotate(360deg); + -ms-transform: rotate(360deg); + transform: rotate(360deg); + } + } + + + #loader-wrapper .loader-section { + position: fixed; + top: 0; + width: 51%; + height: 100%; + background: #7171C6; + z-index: 1000; + -webkit-transform: translateX(0); + -ms-transform: translateX(0); + transform: translateX(0); + } + + #loader-wrapper .loader-section.section-left { + left: 0; + } + + #loader-wrapper .loader-section.section-right { + right: 0; + } + + + .loaded #loader-wrapper .loader-section.section-left { + -webkit-transform: translateX(-100%); + -ms-transform: translateX(-100%); + transform: translateX(-100%); + -webkit-transition: all 0.7s 0.3s cubic-bezier(0.645, 0.045, 0.355, 1.000); + transition: all 0.7s 0.3s cubic-bezier(0.645, 0.045, 0.355, 1.000); + } + + .loaded #loader-wrapper .loader-section.section-right { + -webkit-transform: translateX(100%); + -ms-transform: translateX(100%); + transform: translateX(100%); + -webkit-transition: all 0.7s 0.3s cubic-bezier(0.645, 0.045, 0.355, 1.000); + transition: all 0.7s 0.3s cubic-bezier(0.645, 0.045, 0.355, 1.000); + } + + .loaded #loader { + opacity: 0; + -webkit-transition: all 0.3s ease-out; + transition: all 0.3s ease-out; + } + + .loaded #loader-wrapper { + visibility: hidden; + -webkit-transform: translateY(-100%); + -ms-transform: translateY(-100%); + transform: translateY(-100%); + -webkit-transition: all 0.3s 1s ease-out; + transition: all 0.3s 1s ease-out; + } + + .no-js #loader-wrapper { + display: none; + } + + .no-js h1 { + color: #222222; + } + + #loader-wrapper .load_title { + font-family: 'Open Sans'; + color: #FFF; + font-size: 19px; + width: 100%; + text-align: center; + z-index: 9999999999999; + position: absolute; + top: 60%; + opacity: 1; + line-height: 30px; + } + + #loader-wrapper .load_title span { + font-weight: normal; + font-style: italic; + font-size: 13px; + color: #FFF; + opacity: 0.5; + } + </style> +</head> + +<body> + <div id="app"> + <div id="loader-wrapper"> + <div id="loader"></div> + <div class="loader-section section-left"></div> + <div class="loader-section section-right"></div> + <div class="load_title">姝e湪鍔犺浇绯荤粺璧勬簮锛岃鑰愬績绛夊緟</div> + </div> + </div> + <script type="module" src="/src/main.js"></script> +</body> + +</html> \ No newline at end of file diff --git a/package.json b/package.json new file mode 100644 index 0000000..9e749e3 --- /dev/null +++ b/package.json @@ -0,0 +1,51 @@ +{ + "name": "ruoyi", + "version": "3.8.9", + "description": "鑻ヤ緷绠$悊绯荤粺", + "author": "鑻ヤ緷", + "license": "MIT", + "type": "module", + "scripts": { + "dev": "vite", + "build:prod": "vite build", + "build:stage": "vite build --mode staging", + "preview": "vite preview" + }, + "repository": { + "type": "git", + "url": "https://gitee.com/y_project/RuoYi-Vue.git" + }, + "dependencies": { + "@element-plus/icons-vue": "2.3.1", + "@vueup/vue-quill": "1.2.0", + "@vueuse/core": "10.11.0", + "axios": "0.28.1", + "clipboard": "2.0.11", + "echarts": "5.5.1", + "element-plus": "2.7.6", + "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", + "splitpanes": "3.1.5", + "vue": "3.4.31", + "vue-cropper": "1.1.1", + "vue-router": "4.4.0", + "vuedraggable": "4.1.0" + }, + "devDependencies": { + "@vitejs/plugin-vue": "5.0.5", + "sass": "1.77.5", + "unplugin-auto-import": "0.17.6", + "unplugin-vue-setup-extend-plus": "1.0.1", + "vite": "5.3.2", + "vite-plugin-compression": "0.5.1", + "vite-plugin-svg-icons": "2.0.1" + }, + "overrides": { + "quill": "2.0.2" + } +} diff --git a/public/favicon.ico b/public/favicon.ico new file mode 100644 index 0000000..e263760 --- /dev/null +++ b/public/favicon.ico Binary files differ diff --git a/src/App.vue b/src/App.vue new file mode 100644 index 0000000..31839f2 --- /dev/null +++ b/src/App.vue @@ -0,0 +1,15 @@ +<template> + <router-view /> +</template> + +<script setup> +import useSettingsStore from '@/store/modules/settings' +import { handleThemeStyle } from '@/utils/theme' + +onMounted(() => { + nextTick(() => { + // 鍒濆鍖栦富棰樻牱寮� + handleThemeStyle(useSettingsStore().theme) + }) +}) +</script> diff --git a/src/api/login.js b/src/api/login.js new file mode 100644 index 0000000..7b7388f --- /dev/null +++ b/src/api/login.js @@ -0,0 +1,60 @@ +import request from '@/utils/request' + +// 鐧诲綍鏂规硶 +export function login(username, password, code, uuid) { + const data = { + username, + password, + code, + uuid + } + return request({ + url: '/login', + headers: { + isToken: false, + repeatSubmit: false + }, + method: 'post', + data: data + }) +} + +// 娉ㄥ唽鏂规硶 +export function register(data) { + return request({ + url: '/register', + headers: { + isToken: false + }, + method: 'post', + data: data + }) +} + +// 鑾峰彇鐢ㄦ埛璇︾粏淇℃伅 +export function getInfo() { + return request({ + url: '/getInfo', + method: 'get' + }) +} + +// 閫�鍑烘柟娉� +export function logout() { + return request({ + url: '/logout', + method: 'post' + }) +} + +// 鑾峰彇楠岃瘉鐮� +export function getCodeImg() { + return request({ + url: '/captchaImage', + headers: { + isToken: false + }, + method: 'get', + timeout: 20000 + }) +} \ No newline at end of file diff --git a/src/api/menu.js b/src/api/menu.js new file mode 100644 index 0000000..faef101 --- /dev/null +++ b/src/api/menu.js @@ -0,0 +1,9 @@ +import request from '@/utils/request' + +// 鑾峰彇璺敱 +export const getRouters = () => { + return request({ + url: '/getRouters', + method: 'get' + }) +} \ No newline at end of file diff --git a/src/api/monitor/cache.js b/src/api/monitor/cache.js new file mode 100644 index 0000000..72c5f6a --- /dev/null +++ b/src/api/monitor/cache.js @@ -0,0 +1,57 @@ +import request from '@/utils/request' + +// 鏌ヨ缂撳瓨璇︾粏 +export function getCache() { + return request({ + url: '/monitor/cache', + method: 'get' + }) +} + +// 鏌ヨ缂撳瓨鍚嶇О鍒楄〃 +export function listCacheName() { + return request({ + url: '/monitor/cache/getNames', + method: 'get' + }) +} + +// 鏌ヨ缂撳瓨閿悕鍒楄〃 +export function listCacheKey(cacheName) { + return request({ + url: '/monitor/cache/getKeys/' + cacheName, + method: 'get' + }) +} + +// 鏌ヨ缂撳瓨鍐呭 +export function getCacheValue(cacheName, cacheKey) { + return request({ + url: '/monitor/cache/getValue/' + cacheName + '/' + cacheKey, + method: 'get' + }) +} + +// 娓呯悊鎸囧畾鍚嶇О缂撳瓨 +export function clearCacheName(cacheName) { + return request({ + url: '/monitor/cache/clearCacheName/' + cacheName, + method: 'delete' + }) +} + +// 娓呯悊鎸囧畾閿悕缂撳瓨 +export function clearCacheKey(cacheKey) { + return request({ + url: '/monitor/cache/clearCacheKey/' + cacheKey, + method: 'delete' + }) +} + +// 娓呯悊鍏ㄩ儴缂撳瓨 +export function clearCacheAll() { + return request({ + url: '/monitor/cache/clearCacheAll', + method: 'delete' + }) +} diff --git a/src/api/monitor/job.js b/src/api/monitor/job.js new file mode 100644 index 0000000..3815569 --- /dev/null +++ b/src/api/monitor/job.js @@ -0,0 +1,71 @@ +import request from '@/utils/request' + +// 鏌ヨ瀹氭椂浠诲姟璋冨害鍒楄〃 +export function listJob(query) { + return request({ + url: '/monitor/job/list', + method: 'get', + params: query + }) +} + +// 鏌ヨ瀹氭椂浠诲姟璋冨害璇︾粏 +export function getJob(jobId) { + return request({ + url: '/monitor/job/' + jobId, + method: 'get' + }) +} + +// 鏂板瀹氭椂浠诲姟璋冨害 +export function addJob(data) { + return request({ + url: '/monitor/job', + method: 'post', + data: data + }) +} + +// 淇敼瀹氭椂浠诲姟璋冨害 +export function updateJob(data) { + return request({ + url: '/monitor/job', + method: 'put', + data: data + }) +} + +// 鍒犻櫎瀹氭椂浠诲姟璋冨害 +export function delJob(jobId) { + return request({ + url: '/monitor/job/' + jobId, + method: 'delete' + }) +} + +// 浠诲姟鐘舵�佷慨鏀� +export function changeJobStatus(jobId, status) { + const data = { + jobId, + status + } + return request({ + url: '/monitor/job/changeStatus', + method: 'put', + data: data + }) +} + + +// 瀹氭椂浠诲姟绔嬪嵆鎵ц涓�娆� +export function runJob(jobId, jobGroup) { + const data = { + jobId, + jobGroup + } + return request({ + url: '/monitor/job/run', + method: 'put', + data: data + }) +} \ No newline at end of file diff --git a/src/api/monitor/jobLog.js b/src/api/monitor/jobLog.js new file mode 100644 index 0000000..6e0be61 --- /dev/null +++ b/src/api/monitor/jobLog.js @@ -0,0 +1,26 @@ +import request from '@/utils/request' + +// 鏌ヨ璋冨害鏃ュ織鍒楄〃 +export function listJobLog(query) { + return request({ + url: '/monitor/jobLog/list', + method: 'get', + params: query + }) +} + +// 鍒犻櫎璋冨害鏃ュ織 +export function delJobLog(jobLogId) { + return request({ + url: '/monitor/jobLog/' + jobLogId, + method: 'delete' + }) +} + +// 娓呯┖璋冨害鏃ュ織 +export function cleanJobLog() { + return request({ + url: '/monitor/jobLog/clean', + method: 'delete' + }) +} diff --git a/src/api/monitor/logininfor.js b/src/api/monitor/logininfor.js new file mode 100644 index 0000000..4d112b7 --- /dev/null +++ b/src/api/monitor/logininfor.js @@ -0,0 +1,34 @@ +import request from '@/utils/request' + +// 鏌ヨ鐧诲綍鏃ュ織鍒楄〃 +export function list(query) { + return request({ + url: '/monitor/logininfor/list', + method: 'get', + params: query + }) +} + +// 鍒犻櫎鐧诲綍鏃ュ織 +export function delLogininfor(infoId) { + return request({ + url: '/monitor/logininfor/' + infoId, + method: 'delete' + }) +} + +// 瑙i攣鐢ㄦ埛鐧诲綍鐘舵�� +export function unlockLogininfor(userName) { + return request({ + url: '/monitor/logininfor/unlock/' + userName, + method: 'get' + }) +} + +// 娓呯┖鐧诲綍鏃ュ織 +export function cleanLogininfor() { + return request({ + url: '/monitor/logininfor/clean', + method: 'delete' + }) +} diff --git a/src/api/monitor/online.js b/src/api/monitor/online.js new file mode 100644 index 0000000..bd22137 --- /dev/null +++ b/src/api/monitor/online.js @@ -0,0 +1,18 @@ +import request from '@/utils/request' + +// 鏌ヨ鍦ㄧ嚎鐢ㄦ埛鍒楄〃 +export function list(query) { + return request({ + url: '/monitor/online/list', + method: 'get', + params: query + }) +} + +// 寮洪��鐢ㄦ埛 +export function forceLogout(tokenId) { + return request({ + url: '/monitor/online/' + tokenId, + method: 'delete' + }) +} diff --git a/src/api/monitor/operlog.js b/src/api/monitor/operlog.js new file mode 100644 index 0000000..a04bca8 --- /dev/null +++ b/src/api/monitor/operlog.js @@ -0,0 +1,26 @@ +import request from '@/utils/request' + +// 鏌ヨ鎿嶄綔鏃ュ織鍒楄〃 +export function list(query) { + return request({ + url: '/monitor/operlog/list', + method: 'get', + params: query + }) +} + +// 鍒犻櫎鎿嶄綔鏃ュ織 +export function delOperlog(operId) { + return request({ + url: '/monitor/operlog/' + operId, + method: 'delete' + }) +} + +// 娓呯┖鎿嶄綔鏃ュ織 +export function cleanOperlog() { + return request({ + url: '/monitor/operlog/clean', + method: 'delete' + }) +} diff --git a/src/api/monitor/server.js b/src/api/monitor/server.js new file mode 100644 index 0000000..e1f9ca2 --- /dev/null +++ b/src/api/monitor/server.js @@ -0,0 +1,9 @@ +import request from '@/utils/request' + +// 鑾峰彇鏈嶅姟淇℃伅 +export function getServer() { + return request({ + url: '/monitor/server', + method: 'get' + }) +} \ No newline at end of file diff --git a/src/api/system/config.js b/src/api/system/config.js new file mode 100644 index 0000000..a404d82 --- /dev/null +++ b/src/api/system/config.js @@ -0,0 +1,60 @@ +import request from '@/utils/request' + +// 鏌ヨ鍙傛暟鍒楄〃 +export function listConfig(query) { + return request({ + url: '/system/config/list', + method: 'get', + params: query + }) +} + +// 鏌ヨ鍙傛暟璇︾粏 +export function getConfig(configId) { + return request({ + url: '/system/config/' + configId, + method: 'get' + }) +} + +// 鏍规嵁鍙傛暟閿悕鏌ヨ鍙傛暟鍊� +export function getConfigKey(configKey) { + return request({ + url: '/system/config/configKey/' + configKey, + method: 'get' + }) +} + +// 鏂板鍙傛暟閰嶇疆 +export function addConfig(data) { + return request({ + url: '/system/config', + method: 'post', + data: data + }) +} + +// 淇敼鍙傛暟閰嶇疆 +export function updateConfig(data) { + return request({ + url: '/system/config', + method: 'put', + data: data + }) +} + +// 鍒犻櫎鍙傛暟閰嶇疆 +export function delConfig(configId) { + return request({ + url: '/system/config/' + configId, + method: 'delete' + }) +} + +// 鍒锋柊鍙傛暟缂撳瓨 +export function refreshCache() { + return request({ + url: '/system/config/refreshCache', + method: 'delete' + }) +} diff --git a/src/api/system/dept.js b/src/api/system/dept.js new file mode 100644 index 0000000..fc943cd --- /dev/null +++ b/src/api/system/dept.js @@ -0,0 +1,52 @@ +import request from '@/utils/request' + +// 鏌ヨ閮ㄩ棬鍒楄〃 +export function listDept(query) { + return request({ + url: '/system/dept/list', + method: 'get', + params: query + }) +} + +// 鏌ヨ閮ㄩ棬鍒楄〃锛堟帓闄よ妭鐐癸級 +export function listDeptExcludeChild(deptId) { + return request({ + url: '/system/dept/list/exclude/' + deptId, + method: 'get' + }) +} + +// 鏌ヨ閮ㄩ棬璇︾粏 +export function getDept(deptId) { + return request({ + url: '/system/dept/' + deptId, + method: 'get' + }) +} + +// 鏂板閮ㄩ棬 +export function addDept(data) { + return request({ + url: '/system/dept', + method: 'post', + data: data + }) +} + +// 淇敼閮ㄩ棬 +export function updateDept(data) { + return request({ + url: '/system/dept', + method: 'put', + data: data + }) +} + +// 鍒犻櫎閮ㄩ棬 +export function delDept(deptId) { + return request({ + url: '/system/dept/' + deptId, + method: 'delete' + }) +} \ No newline at end of file diff --git a/src/api/system/dict/data.js b/src/api/system/dict/data.js new file mode 100644 index 0000000..6c9eb79 --- /dev/null +++ b/src/api/system/dict/data.js @@ -0,0 +1,52 @@ +import request from '@/utils/request' + +// 鏌ヨ瀛楀吀鏁版嵁鍒楄〃 +export function listData(query) { + return request({ + url: '/system/dict/data/list', + method: 'get', + params: query + }) +} + +// 鏌ヨ瀛楀吀鏁版嵁璇︾粏 +export function getData(dictCode) { + return request({ + url: '/system/dict/data/' + dictCode, + method: 'get' + }) +} + +// 鏍规嵁瀛楀吀绫诲瀷鏌ヨ瀛楀吀鏁版嵁淇℃伅 +export function getDicts(dictType) { + return request({ + url: '/system/dict/data/type/' + dictType, + method: 'get' + }) +} + +// 鏂板瀛楀吀鏁版嵁 +export function addData(data) { + return request({ + url: '/system/dict/data', + method: 'post', + data: data + }) +} + +// 淇敼瀛楀吀鏁版嵁 +export function updateData(data) { + return request({ + url: '/system/dict/data', + method: 'put', + data: data + }) +} + +// 鍒犻櫎瀛楀吀鏁版嵁 +export function delData(dictCode) { + return request({ + url: '/system/dict/data/' + dictCode, + method: 'delete' + }) +} diff --git a/src/api/system/dict/type.js b/src/api/system/dict/type.js new file mode 100644 index 0000000..a0254ba --- /dev/null +++ b/src/api/system/dict/type.js @@ -0,0 +1,60 @@ +import request from '@/utils/request' + +// 鏌ヨ瀛楀吀绫诲瀷鍒楄〃 +export function listType(query) { + return request({ + url: '/system/dict/type/list', + method: 'get', + params: query + }) +} + +// 鏌ヨ瀛楀吀绫诲瀷璇︾粏 +export function getType(dictId) { + return request({ + url: '/system/dict/type/' + dictId, + method: 'get' + }) +} + +// 鏂板瀛楀吀绫诲瀷 +export function addType(data) { + return request({ + url: '/system/dict/type', + method: 'post', + data: data + }) +} + +// 淇敼瀛楀吀绫诲瀷 +export function updateType(data) { + return request({ + url: '/system/dict/type', + method: 'put', + data: data + }) +} + +// 鍒犻櫎瀛楀吀绫诲瀷 +export function delType(dictId) { + return request({ + url: '/system/dict/type/' + dictId, + method: 'delete' + }) +} + +// 鍒锋柊瀛楀吀缂撳瓨 +export function refreshCache() { + return request({ + url: '/system/dict/type/refreshCache', + method: 'delete' + }) +} + +// 鑾峰彇瀛楀吀閫夋嫨妗嗗垪琛� +export function optionselect() { + return request({ + url: '/system/dict/type/optionselect', + method: 'get' + }) +} diff --git a/src/api/system/menu.js b/src/api/system/menu.js new file mode 100644 index 0000000..f6415c6 --- /dev/null +++ b/src/api/system/menu.js @@ -0,0 +1,60 @@ +import request from '@/utils/request' + +// 鏌ヨ鑿滃崟鍒楄〃 +export function listMenu(query) { + return request({ + url: '/system/menu/list', + method: 'get', + params: query + }) +} + +// 鏌ヨ鑿滃崟璇︾粏 +export function getMenu(menuId) { + return request({ + url: '/system/menu/' + menuId, + method: 'get' + }) +} + +// 鏌ヨ鑿滃崟涓嬫媺鏍戠粨鏋� +export function treeselect() { + return request({ + url: '/system/menu/treeselect', + method: 'get' + }) +} + +// 鏍规嵁瑙掕壊ID鏌ヨ鑿滃崟涓嬫媺鏍戠粨鏋� +export function roleMenuTreeselect(roleId) { + return request({ + url: '/system/menu/roleMenuTreeselect/' + roleId, + method: 'get' + }) +} + +// 鏂板鑿滃崟 +export function addMenu(data) { + return request({ + url: '/system/menu', + method: 'post', + data: data + }) +} + +// 淇敼鑿滃崟 +export function updateMenu(data) { + return request({ + url: '/system/menu', + method: 'put', + data: data + }) +} + +// 鍒犻櫎鑿滃崟 +export function delMenu(menuId) { + return request({ + url: '/system/menu/' + menuId, + method: 'delete' + }) +} \ No newline at end of file diff --git a/src/api/system/notice.js b/src/api/system/notice.js new file mode 100644 index 0000000..c274ea5 --- /dev/null +++ b/src/api/system/notice.js @@ -0,0 +1,44 @@ +import request from '@/utils/request' + +// 鏌ヨ鍏憡鍒楄〃 +export function listNotice(query) { + return request({ + url: '/system/notice/list', + method: 'get', + params: query + }) +} + +// 鏌ヨ鍏憡璇︾粏 +export function getNotice(noticeId) { + return request({ + url: '/system/notice/' + noticeId, + method: 'get' + }) +} + +// 鏂板鍏憡 +export function addNotice(data) { + return request({ + url: '/system/notice', + method: 'post', + data: data + }) +} + +// 淇敼鍏憡 +export function updateNotice(data) { + return request({ + url: '/system/notice', + method: 'put', + data: data + }) +} + +// 鍒犻櫎鍏憡 +export function delNotice(noticeId) { + return request({ + url: '/system/notice/' + noticeId, + method: 'delete' + }) +} \ No newline at end of file diff --git a/src/api/system/post.js b/src/api/system/post.js new file mode 100644 index 0000000..1a8e9ca --- /dev/null +++ b/src/api/system/post.js @@ -0,0 +1,44 @@ +import request from '@/utils/request' + +// 鏌ヨ宀椾綅鍒楄〃 +export function listPost(query) { + return request({ + url: '/system/post/list', + method: 'get', + params: query + }) +} + +// 鏌ヨ宀椾綅璇︾粏 +export function getPost(postId) { + return request({ + url: '/system/post/' + postId, + method: 'get' + }) +} + +// 鏂板宀椾綅 +export function addPost(data) { + return request({ + url: '/system/post', + method: 'post', + data: data + }) +} + +// 淇敼宀椾綅 +export function updatePost(data) { + return request({ + url: '/system/post', + method: 'put', + data: data + }) +} + +// 鍒犻櫎宀椾綅 +export function delPost(postId) { + return request({ + url: '/system/post/' + postId, + method: 'delete' + }) +} diff --git a/src/api/system/role.js b/src/api/system/role.js new file mode 100644 index 0000000..f13e6f4 --- /dev/null +++ b/src/api/system/role.js @@ -0,0 +1,119 @@ +import request from '@/utils/request' + +// 鏌ヨ瑙掕壊鍒楄〃 +export function listRole(query) { + return request({ + url: '/system/role/list', + method: 'get', + params: query + }) +} + +// 鏌ヨ瑙掕壊璇︾粏 +export function getRole(roleId) { + return request({ + url: '/system/role/' + roleId, + method: 'get' + }) +} + +// 鏂板瑙掕壊 +export function addRole(data) { + return request({ + url: '/system/role', + method: 'post', + data: data + }) +} + +// 淇敼瑙掕壊 +export function updateRole(data) { + return request({ + url: '/system/role', + method: 'put', + data: data + }) +} + +// 瑙掕壊鏁版嵁鏉冮檺 +export function dataScope(data) { + return request({ + url: '/system/role/dataScope', + method: 'put', + data: data + }) +} + +// 瑙掕壊鐘舵�佷慨鏀� +export function changeRoleStatus(roleId, status) { + const data = { + roleId, + status + } + return request({ + url: '/system/role/changeStatus', + method: 'put', + data: data + }) +} + +// 鍒犻櫎瑙掕壊 +export function delRole(roleId) { + return request({ + url: '/system/role/' + roleId, + method: 'delete' + }) +} + +// 鏌ヨ瑙掕壊宸叉巿鏉冪敤鎴峰垪琛� +export function allocatedUserList(query) { + return request({ + url: '/system/role/authUser/allocatedList', + method: 'get', + params: query + }) +} + +// 鏌ヨ瑙掕壊鏈巿鏉冪敤鎴峰垪琛� +export function unallocatedUserList(query) { + return request({ + url: '/system/role/authUser/unallocatedList', + method: 'get', + params: query + }) +} + +// 鍙栨秷鐢ㄦ埛鎺堟潈瑙掕壊 +export function authUserCancel(data) { + return request({ + url: '/system/role/authUser/cancel', + method: 'put', + data: data + }) +} + +// 鎵归噺鍙栨秷鐢ㄦ埛鎺堟潈瑙掕壊 +export function authUserCancelAll(data) { + return request({ + url: '/system/role/authUser/cancelAll', + method: 'put', + params: data + }) +} + +// 鎺堟潈鐢ㄦ埛閫夋嫨 +export function authUserSelectAll(data) { + return request({ + url: '/system/role/authUser/selectAll', + method: 'put', + params: data + }) +} + +// 鏍规嵁瑙掕壊ID鏌ヨ閮ㄩ棬鏍戠粨鏋� +export function deptTreeSelect(roleId) { + return request({ + url: '/system/role/deptTree/' + roleId, + method: 'get' + }) +} diff --git a/src/api/system/user.js b/src/api/system/user.js new file mode 100644 index 0000000..b5e3edd --- /dev/null +++ b/src/api/system/user.js @@ -0,0 +1,136 @@ +import request from '@/utils/request' +import { parseStrEmpty } from "@/utils/ruoyi"; + +// 鏌ヨ鐢ㄦ埛鍒楄〃 +export function listUser(query) { + return request({ + url: '/system/user/list', + method: 'get', + params: query + }) +} + +// 鏌ヨ鐢ㄦ埛璇︾粏 +export function getUser(userId) { + return request({ + url: '/system/user/' + parseStrEmpty(userId), + method: 'get' + }) +} + +// 鏂板鐢ㄦ埛 +export function addUser(data) { + return request({ + url: '/system/user', + method: 'post', + data: data + }) +} + +// 淇敼鐢ㄦ埛 +export function updateUser(data) { + return request({ + url: '/system/user', + method: 'put', + data: data + }) +} + +// 鍒犻櫎鐢ㄦ埛 +export function delUser(userId) { + return request({ + url: '/system/user/' + userId, + method: 'delete' + }) +} + +// 鐢ㄦ埛瀵嗙爜閲嶇疆 +export function resetUserPwd(userId, password) { + const data = { + userId, + password + } + return request({ + url: '/system/user/resetPwd', + method: 'put', + data: data + }) +} + +// 鐢ㄦ埛鐘舵�佷慨鏀� +export function changeUserStatus(userId, status) { + const data = { + userId, + status + } + return request({ + url: '/system/user/changeStatus', + method: 'put', + data: data + }) +} + +// 鏌ヨ鐢ㄦ埛涓汉淇℃伅 +export function getUserProfile() { + return request({ + url: '/system/user/profile', + method: 'get' + }) +} + +// 淇敼鐢ㄦ埛涓汉淇℃伅 +export function updateUserProfile(data) { + return request({ + url: '/system/user/profile', + method: 'put', + data: data + }) +} + +// 鐢ㄦ埛瀵嗙爜閲嶇疆 +export function updateUserPwd(oldPassword, newPassword) { + const data = { + oldPassword, + newPassword + } + return request({ + url: '/system/user/profile/updatePwd', + method: 'put', + data: data + }) +} + +// 鐢ㄦ埛澶村儚涓婁紶 +export function uploadAvatar(data) { + return request({ + url: '/system/user/profile/avatar', + method: 'post', + headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, + data: data + }) +} + +// 鏌ヨ鎺堟潈瑙掕壊 +export function getAuthRole(userId) { + return request({ + url: '/system/user/authRole/' + userId, + method: 'get' + }) +} + +// 淇濆瓨鎺堟潈瑙掕壊 +export function updateAuthRole(data) { + return request({ + url: '/system/user/authRole', + method: 'put', + params: data + }) +} + +// 鏌ヨ閮ㄩ棬涓嬫媺鏍戠粨鏋� +export function deptTreeSelect() { + return request({ + url: '/system/user/deptTree', + method: 'get' + }) +} diff --git a/src/api/tool/gen.js b/src/api/tool/gen.js new file mode 100644 index 0000000..2075677 --- /dev/null +++ b/src/api/tool/gen.js @@ -0,0 +1,85 @@ +import request from '@/utils/request' + +// 鏌ヨ鐢熸垚琛ㄦ暟鎹� +export function listTable(query) { + return request({ + url: '/tool/gen/list', + method: 'get', + params: query + }) +} +// 鏌ヨdb鏁版嵁搴撳垪琛� +export function listDbTable(query) { + return request({ + url: '/tool/gen/db/list', + method: 'get', + params: query + }) +} + +// 鏌ヨ琛ㄨ缁嗕俊鎭� +export function getGenTable(tableId) { + return request({ + url: '/tool/gen/' + tableId, + method: 'get' + }) +} + +// 淇敼浠g爜鐢熸垚淇℃伅 +export function updateGenTable(data) { + return request({ + url: '/tool/gen', + method: 'put', + data: data + }) +} + +// 瀵煎叆琛� +export function importTable(data) { + return request({ + url: '/tool/gen/importTable', + method: 'post', + params: data + }) +} + +// 鍒涘缓琛� +export function createTable(data) { + return request({ + url: '/tool/gen/createTable', + method: 'post', + params: data + }) +} + +// 棰勮鐢熸垚浠g爜 +export function previewTable(tableId) { + return request({ + url: '/tool/gen/preview/' + tableId, + method: 'get' + }) +} + +// 鍒犻櫎琛ㄦ暟鎹� +export function delTable(tableId) { + return request({ + url: '/tool/gen/' + tableId, + method: 'delete' + }) +} + +// 鐢熸垚浠g爜锛堣嚜瀹氫箟璺緞锛� +export function genCode(tableName) { + return request({ + url: '/tool/gen/genCode/' + tableName, + method: 'get' + }) +} + +// 鍚屾鏁版嵁搴� +export function synchDb(tableName) { + return request({ + url: '/tool/gen/synchDb/' + tableName, + method: 'get' + }) +} diff --git a/src/assets/401_images/401.gif b/src/assets/401_images/401.gif new file mode 100644 index 0000000..cd6e0d9 --- /dev/null +++ b/src/assets/401_images/401.gif Binary files differ diff --git a/src/assets/404_images/404.png b/src/assets/404_images/404.png new file mode 100644 index 0000000..3d8e230 --- /dev/null +++ b/src/assets/404_images/404.png Binary files differ diff --git a/src/assets/404_images/404_cloud.png b/src/assets/404_images/404_cloud.png new file mode 100644 index 0000000..c6281d0 --- /dev/null +++ b/src/assets/404_images/404_cloud.png Binary files differ diff --git a/src/assets/icons/svg/404.svg b/src/assets/icons/svg/404.svg new file mode 100644 index 0000000..6df5019 --- /dev/null +++ b/src/assets/icons/svg/404.svg @@ -0,0 +1 @@ +<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M121.718 73.272v9.953c3.957-7.584 6.199-16.05 6.199-24.995C127.917 26.079 99.273 0 63.958 0 28.644 0 0 26.079 0 58.23c0 .403.028.806.028 1.21l22.97-25.953h13.34l-19.76 27.187h6.42V53.77l13.728-19.477v49.361H22.998V73.272H2.158c5.951 20.284 23.608 36.208 45.998 41.399-1.44 3.3-5.618 11.263-12.565 12.674-8.607 1.764 23.358.428 46.163-13.178 17.519-4.611 31.938-15.849 39.77-30.513h-13.506V73.272H85.02V59.464l22.998-25.977h13.008l-19.429 27.187h6.421v-7.433l13.727-19.402v39.433h-.027zm-78.24 2.822a10.516 10.516 0 0 1-.996-4.535V44.548c0-1.613.332-3.124.996-4.535a11.66 11.66 0 0 1 2.713-3.68c1.134-1.032 2.49-1.864 4.04-2.468 1.55-.605 3.21-.908 4.982-.908h11.292c1.77 0 3.431.303 4.981.908 1.522.604 2.85 1.41 3.986 2.418l-12.26 16.303v-2.898a1.96 1.96 0 0 0-.665-1.512c-.443-.403-.996-.604-1.66-.604-.665 0-1.218.201-1.661.604a1.96 1.96 0 0 0-.664 1.512v9.071L44.364 77.606a10.556 10.556 0 0 1-.886-1.512zm35.73-4.535c0 1.613-.332 3.124-.997 4.535a11.66 11.66 0 0 1-2.712 3.68c-1.134 1.032-2.49 1.864-4.04 2.469-1.55.604-3.21.907-4.982.907H55.185c-1.77 0-3.431-.303-4.981-.907-1.55-.605-2.906-1.437-4.041-2.47a12.49 12.49 0 0 1-1.384-1.512l13.727-18.217v6.375c0 .605.222 1.109.665 1.512.442.403.996.604 1.66.604.664 0 1.218-.201 1.66-.604a1.96 1.96 0 0 0 .665-1.512V53.87L75.97 36.838c.913.932 1.66 1.99 2.214 3.175.664 1.41.996 2.922.996 4.535v27.011h.028z"/></svg> \ No newline at end of file diff --git a/src/assets/icons/svg/bug.svg b/src/assets/icons/svg/bug.svg new file mode 100644 index 0000000..05a150d --- /dev/null +++ b/src/assets/icons/svg/bug.svg @@ -0,0 +1 @@ +<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M127.88 73.143c0 1.412-.506 2.635-1.518 3.669-1.011 1.033-2.209 1.55-3.592 1.55h-17.887c0 9.296-1.783 17.178-5.35 23.645l16.609 17.044c1.011 1.034 1.517 2.257 1.517 3.67 0 1.412-.506 2.635-1.517 3.668-.958 1.033-2.155 1.55-3.593 1.55-1.438 0-2.635-.517-3.593-1.55l-15.811-16.063a15.49 15.49 0 0 1-1.196 1.06c-.532.434-1.65 1.208-3.353 2.322a50.104 50.104 0 0 1-5.192 2.974c-1.758.87-3.94 1.658-6.546 2.364-2.607.706-5.189 1.06-7.748 1.06V47.044H58.89v73.062c-2.716 0-5.417-.367-8.106-1.102-2.688-.734-5.003-1.631-6.945-2.692a66.769 66.769 0 0 1-5.268-3.179c-1.571-1.057-2.73-1.94-3.476-2.65L33.9 109.34l-14.611 16.877c-1.066 1.14-2.344 1.711-3.833 1.711-1.277 0-2.422-.434-3.434-1.304-1.012-.978-1.557-2.187-1.635-3.627-.079-1.44.333-2.705 1.236-3.794l16.129-18.51c-3.087-6.197-4.63-13.644-4.63-22.342H5.235c-1.383 0-2.58-.517-3.592-1.55S.125 74.545.125 73.132c0-1.412.506-2.635 1.518-3.668 1.012-1.034 2.21-1.55 3.592-1.55h17.887V43.939L9.308 29.833c-1.012-1.033-1.517-2.256-1.517-3.669 0-1.412.505-2.635 1.517-3.668 1.012-1.034 2.21-1.55 3.593-1.55s2.58.516 3.593 1.55l13.813 14.106h67.396l13.814-14.106c1.012-1.034 2.21-1.55 3.592-1.55 1.384 0 2.581.516 3.593 1.55 1.012 1.033 1.518 2.256 1.518 3.668 0 1.413-.506 2.636-1.518 3.67l-13.814 14.105v23.975h17.887c1.383 0 2.58.516 3.593 1.55 1.011 1.033 1.517 2.256 1.517 3.668l-.005.01zM89.552 26.175H38.448c0-7.23 2.489-13.386 7.466-18.469C50.892 2.623 56.92.082 64 .082c7.08 0 13.108 2.541 18.086 7.624 4.977 5.083 7.466 11.24 7.466 18.469z"/></svg> \ No newline at end of file diff --git a/src/assets/icons/svg/build.svg b/src/assets/icons/svg/build.svg new file mode 100644 index 0000000..97c4688 --- /dev/null +++ b/src/assets/icons/svg/build.svg @@ -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="1568899741379" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2054" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M960 591.424V368.96c0-0.288 0.16-0.512 0.16-0.768S960 367.68 960 367.424V192a32 32 0 0 0-32-32H96a32 32 0 0 0-32 32v175.424c0 0.288-0.16 0.512-0.16 0.768s0.16 0.48 0.16 0.768v222.464c0 0.288-0.16 0.512-0.16 0.768s0.16 0.48 0.16 0.768V864a32 32 0 0 0 32 32h832a32 32 0 0 0 32-32v-271.04c0-0.288 0.16-0.512 0.16-0.768S960 591.68 960 591.424z m-560-31.232v-160H608v160h-208z m208 64V832h-208v-207.808H608z m-480-224h208v160H128v-160z m544 0h224v160h-224v-160zM896 224v112.192H128V224h768zM128 624.192h208V832H128v-207.808zM672 832v-207.808h224V832h-224z" p-id="2055"></path></svg> \ No newline at end of file diff --git a/src/assets/icons/svg/button.svg b/src/assets/icons/svg/button.svg new file mode 100644 index 0000000..904fddc --- /dev/null +++ b/src/assets/icons/svg/button.svg @@ -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="1588670460195" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1314" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M230.4 307.712c13.824 0 25.088-11.264 25.088-25.088 0-100.352 81.92-182.272 182.272-182.272s182.272 81.408 182.272 182.272c0 13.824 11.264 25.088 25.088 25.088s25.088-11.264 24.576-25.088c0-127.488-103.936-231.936-231.936-231.936S205.824 154.624 205.824 282.624c-0.512 14.336 10.752 25.088 24.576 25.088z m564.736 234.496c-11.264 0-21.504 2.048-31.232 6.144 0-44.544-40.448-81.92-88.064-81.92-14.848 0-28.16 3.584-39.936 10.24-13.824-28.16-44.544-48.128-78.848-48.128-12.288 0-24.576 2.56-35.328 7.68V284.16c0-45.568-37.888-81.92-84.48-81.92s-84.48 36.864-84.48 81.92v348.672l-69.12-112.64c-18.432-28.16-58.368-36.864-91.136-19.968-26.624 14.336-46.592 47.104-30.208 88.064 3.072 8.192 76.8 205.312 171.52 311.296 0 0 28.16 24.576 43.008 58.88 4.096 9.728 13.312 15.36 22.528 15.36 3.072 0 6.656-0.512 9.728-2.048 12.288-5.12 18.432-19.968 12.8-32.256-19.456-44.544-53.76-74.752-53.76-74.752C281.6 768 209.408 573.44 208.384 570.88c-5.12-12.8-2.56-20.992 7.168-26.112 9.216-4.608 21.504-4.608 26.112 2.56l113.152 184.32c4.096 8.704 12.8 14.336 22.528 14.336 13.824 0 25.088-10.752 25.088-25.088V284.16c0-17.92 15.36-32.256 34.816-32.256s34.816 14.336 34.816 32.256v284.16c0 13.824 10.24 25.088 24.576 25.088 13.824 0 25.088-11.264 25.088-25.088v-57.344c0-17.92 15.36-32.768 34.816-32.768 19.968 0 37.376 15.36 37.376 32.768v95.232c0 7.168 3.072 13.312 7.68 17.92 4.608 4.608 10.752 7.168 17.92 7.168 13.824 0 24.576-11.264 24.576-25.088V547.84c0-18.432 13.824-32.256 32.256-32.256 20.48 0 38.912 15.36 38.912 32.256v95.232c0 13.824 11.264 25.088 25.088 25.088s24.576-11.264 25.088-25.088v-18.944c0-18.944 12.8-32.256 30.72-32.256 18.432 0 22.528 18.944 22.528 31.744 0 1.024-11.776 99.84-50.688 173.056-30.72 58.368-45.056 112.128-51.2 146.944-2.56 13.312 6.656 26.112 19.968 28.672 1.536 0 3.072 0.512 4.608 0.512 11.776 0 22.016-8.192 24.064-20.48 5.632-31.232 18.432-79.36 46.08-132.608 43.52-81.92 55.808-186.88 56.32-193.536-0.512-50.688-29.696-83.968-72.704-83.968z"></path></path></svg> \ No newline at end of file diff --git a/src/assets/icons/svg/cascader.svg b/src/assets/icons/svg/cascader.svg new file mode 100644 index 0000000..e256024 --- /dev/null +++ b/src/assets/icons/svg/cascader.svg @@ -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="1576153230908" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="971" xmlns:xlink="http://www.w3.org/1999/xlink" width="81" height="81"><defs><style type="text/css"></style></defs><path d="M772.87036133 734.06115723c-43.34106445 0-80.00793458 27.93273926-93.76831055 66.57714843H475.90991211c-56.60705567 0-102.66723633-46.06018067-102.66723633-102.66723633V600.82446289h305.859375c13.76037598 38.64440918 50.42724609 66.57714844 93.76831055 66.57714844 55.12390137 0 99.94812012-44.82421875 99.94812012-99.94812012S827.9942627 467.50537109 772.87036133 467.50537109c-43.34106445 0-80.00793458 27.93273926-93.76831055 66.57714844H373.24267578V401.01062011h321.92687989c55.12390137 0 99.94812012-44.82421875 99.94812011-99.94812011V190.07312011C795.11767578 134.94921875 750.29345703 90.125 695.16955567 90.125H251.12963867C196.0057373 90.125 151.18151855 134.94921875 151.18151855 190.07312011V301.0625c0 55.12390137 44.82421875 99.94812012 99.94812012 99.94812012h55.53588867v296.96044921c0 93.35632325 75.97045898 169.32678223 169.32678224 169.32678223h203.19213866c13.76037598 38.64440918 50.42724609 66.57714844 93.76831055 66.57714844 55.12390137 0 99.94812012-44.82421875 99.94812012-99.94812012s-44.90661622-99.86572266-100.03051758-99.86572265z m0-199.89624024c18.37463379 0 33.28857422 14.91394043 33.28857422 33.28857423s-14.91394043 33.28857422-33.28857422 33.28857421-33.28857422-14.91394043-33.28857422-33.28857421 14.91394043-33.28857422 33.28857422-33.28857422zM217.75866699 301.0625V190.07312011c0-18.37463379 14.91394043-33.28857422 33.28857423-33.28857421h444.03991698c18.37463379 0 33.28857422 14.91394043 33.28857422 33.28857422V301.0625c0 18.37463379-14.91394043 33.28857422-33.28857422 33.28857422H251.12963867c-18.37463379 0-33.37097168-14.91394043-33.37097168-33.28857422z m555.11169434 566.23535156c-18.37463379 0-33.28857422-14.91394043-33.28857422-33.28857422 0-18.37463379 14.91394043-33.28857422 33.28857422-33.28857422s33.28857422 14.91394043 33.28857422 33.28857422c0.08239747 18.29223633-14.91394043 33.28857422-33.28857422 33.28857422z" p-id="972"></path></svg> \ No newline at end of file diff --git a/src/assets/icons/svg/chart.svg b/src/assets/icons/svg/chart.svg new file mode 100644 index 0000000..27728fb --- /dev/null +++ b/src/assets/icons/svg/chart.svg @@ -0,0 +1 @@ +<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M0 54.857h36.571V128H0V54.857zM91.429 27.43H128V128H91.429V27.429zM45.714 0h36.572v128H45.714V0z"/></svg> \ No newline at end of file diff --git a/src/assets/icons/svg/checkbox.svg b/src/assets/icons/svg/checkbox.svg new file mode 100644 index 0000000..013fd3a --- /dev/null +++ b/src/assets/icons/svg/checkbox.svg @@ -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="1575982282951" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="902" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M828.40625 90.125H195.59375C137.375 90.125 90.125 137.375 90.125 195.59375v632.8125c0 58.21875 47.25 105.46875 105.46875 105.46875h632.8125c58.21875 0 105.46875-47.25 105.46875-105.46875V195.59375c0-58.21875-47.25-105.46875-105.46875-105.46875z m52.734375 738.28125c0 29.16-23.57015625 52.734375-52.734375 52.734375H195.59375c-29.109375 0-52.734375-23.574375-52.734375-52.734375V195.59375c0-29.109375 23.625-52.734375 52.734375-52.734375h632.8125c29.16 0 52.734375 23.625 52.734375 52.734375v632.8125z" p-id="903"></path><path d="M421.52890625 709.55984375a36.28125 36.28125 0 0 1-27.55265625-12.66890625L205.17453125 476.613125a36.28546875 36.28546875 0 0 1 55.10109375-47.22890625l164.986875 192.4846875 342.16171875-298.48078125a36.2896875 36.2896875 0 0 1 47.70984375 54.68765625L445.3859375 700.6203125a36.3234375 36.3234375 0 0 1-23.85703125 8.93953125z" p-id="904"></path></svg> \ No newline at end of file diff --git a/src/assets/icons/svg/clipboard.svg b/src/assets/icons/svg/clipboard.svg new file mode 100644 index 0000000..90923ff --- /dev/null +++ b/src/assets/icons/svg/clipboard.svg @@ -0,0 +1 @@ +<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M54.857 118.857h64V73.143H89.143c-1.902 0-3.52-.668-4.855-2.002-1.335-1.335-2.002-2.954-2.002-4.855V36.57H54.857v82.286zM73.143 16v-4.571a2.2 2.2 0 0 0-.677-1.61 2.198 2.198 0 0 0-1.609-.676H20.571c-.621 0-1.158.225-1.609.676a2.198 2.198 0 0 0-.676 1.61V16a2.2 2.2 0 0 0 .676 1.61c.451.45.988.676 1.61.676h50.285c.622 0 1.158-.226 1.61-.677.45-.45.676-.987.676-1.609zm18.286 48h21.357L91.43 42.642V64zM128 73.143v48c0 1.902-.667 3.52-2.002 4.855-1.335 1.335-2.953 2.002-4.855 2.002H52.57c-1.901 0-3.52-.667-4.854-2.002-1.335-1.335-2.003-2.953-2.003-4.855v-11.429H6.857c-1.902 0-3.52-.667-4.855-2.002C.667 106.377 0 104.759 0 102.857v-96c0-1.902.667-3.52 2.002-4.855C3.337.667 4.955 0 6.857 0h77.714c1.902 0 3.52.667 4.855 2.002 1.335 1.335 2.003 2.953 2.003 4.855V30.29c1 .622 1.856 1.29 2.569 2.003l29.147 29.147c1.335 1.335 2.478 3.145 3.429 5.43.95 2.287 1.426 4.383 1.426 6.291v-.018z"/></svg> \ No newline at end of file diff --git a/src/assets/icons/svg/code.svg b/src/assets/icons/svg/code.svg new file mode 100644 index 0000000..5f9c5ab --- /dev/null +++ b/src/assets/icons/svg/code.svg @@ -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="1546567861908" class="icon" style="" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2422" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M318.577778 819.2L17.066667 512l301.511111-307.2 45.511111 45.511111L96.711111 512l267.377778 261.688889zM705.422222 819.2l-45.511111-45.511111L927.288889 512l-267.377778-261.688889 45.511111-45.511111L1006.933333 512zM540.785778 221.866667l55.751111 11.150222L483.157333 802.133333l-55.751111-11.093333z" p-id="2423"></path></svg> \ No newline at end of file diff --git a/src/assets/icons/svg/color.svg b/src/assets/icons/svg/color.svg new file mode 100644 index 0000000..44a81aa --- /dev/null +++ b/src/assets/icons/svg/color.svg @@ -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="1577252187056" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2508" xmlns:xlink="http://www.w3.org/1999/xlink" width="81" height="81"><defs><style type="text/css"></style></defs><path d="M747.59340925 691.12859384c11.51396329 0.25305413 22.43746719-0.21087818 40.74171707-1.51832482 29.35428085-2.10878421 35.84933734-2.36183835 46.47761114-0.8856895 24.71495444 3.37405491 41.12129828 21.76265671 32.47528161 47.95376084-85.57447632 258.19957947-442.00123984 249.76444099-628.67084683 50.73735554-153.47733892-159.33976008-153.09775772-414.41833795 0.92786545-573.42069196 159.71934128-162.67163983 424.03439521-166.59397897 565.78689185 0.63263534 80.38686649 94.81095318 108.34934958 169.16669549 89.11723508 230.57450162-15.01454608 47.99593598-50.61082928 77.68762207-119.77896259 114.63352789-4.89237973 2.65706845-29.35428085 15.52065436-35.84933652 19.02123633-46.94154346 25.30541465-63.51659033 41.20565021-62.20914449 58.45550757 2.95229856 39.13904114 24.16667102 52.7196135 70.98168823 53.81618115z m44.41100207 50.10472101c-19.82257471 1.43397372-32.05352527 1.940082-45.63409763 1.6448519-70.34905207-1.60267593-115.98314969-30.91478165-121.38163769-101.64341492-3.45840683-46.05585397 24.7571304-73.13264758 89.24376132-107.96976837 6.7902866-3.66928501 31.37871396-16.57504688 36.06021551-19.06341229 57.69634516-30.83042972 85.15271997-53.73183005 94.76877722-84.47790866 12.77923398-40.78389304-9.10994898-98.94417051-79.24812286-181.6507002-121.17075953-142.97559219-350.14258521-139.60153647-489.2380134 2.06660824-134.49827774 138.84237405-134.79350784 362.12048163-0.42175717 501.637667 158.53842169 168.99799328 451.9968783 181.18676788 534.57688175-11.80919339-4.68150156 0.2952301-10.71262573 0.67481131-18.72600705 1.26527069z" p-id="2509"></path><path d="M346.03865637 637.18588562a78.82636652 78.82636652 0 0 0 78.32025825-79.29029883c0-43.69401562-35.005823-79.29029883-78.32025825-79.29029882a78.82636652 78.82636652 0 0 0-78.36243338 79.29029882c0 43.69401562 35.005823 79.29029883 78.36243338 79.29029883z m0-51.7495729a27.07679361 27.07679361 0 0 1-26.5706845-27.54072593c0-15.30977536 11.97789643-27.54072593 26.5706845-27.54072592 14.55061295 0 26.57068533 12.23095057 26.57068533 27.54072592a27.07679361 27.07679361 0 0 1-26.57068533 27.54072593zM475.7289063 807.11174353a78.82636652 78.82636652 0 0 0 78.3624334-79.29029882c0-43.69401562-34.96364785-79.29029883-78.32025825-79.29029883a78.82636652 78.82636652 0 0 0-78.32025742 79.29029883c0 43.69401562 34.96364785 79.29029883 78.32025742 79.29029882z m0-51.74957208a27.07679361 27.07679361 0 0 1-26.57068532-27.54072674c0-15.30977536 12.06224753-27.54072593 26.57068532-27.54072593 14.59278892 0 26.57068533 12.23095057 26.57068453 27.54072593a27.07679361 27.07679361 0 0 1-26.57068453 27.54072674zM601.24376214 377.21492718a78.82636652 78.82636652 0 0 0 78.32025742-79.29029883c0-43.69401562-34.96364785-79.29029883-78.32025742-79.29029882a78.82636652 78.82636652 0 0 0-78.32025823 79.29029883c0 43.69401562 34.96364785 79.29029883 78.32025824 79.29029883z m1e-8-51.74957208a27.07679361 27.07679361 0 0 1-26.57068534-27.54072675c0-15.30977536 11.97789643-27.54072593 26.57068534-27.54072591 14.55061295 0 26.57068533 12.23095057 26.57068451 27.54072592a27.07679361 27.07679361 0 0 1-26.57068451 27.54072674zM378.80916809 433.85687983a78.82636652 78.82636652 0 0 0 78.32025824-79.29029883c0-43.69401562-34.96364785-79.29029883-78.32025824-79.29029802a78.82636652 78.82636652 0 0 0-78.32025742 79.29029802c0 43.69401562 34.96364785 79.29029883 78.32025742 79.29029883z m0-51.74957209a27.07679361 27.07679361 0 0 1-26.57068451-27.54072674c0-15.30977536 11.97789643-27.54072593 26.57068451-27.54072593 14.55061295 0 26.57068533 12.23095057 26.57068533 27.54072593a27.07679361 27.07679361 0 0 1-26.57068533 27.54072674z" p-id="2510"></path></svg> \ No newline at end of file diff --git a/src/assets/icons/svg/component.svg b/src/assets/icons/svg/component.svg new file mode 100644 index 0000000..29c3458 --- /dev/null +++ b/src/assets/icons/svg/component.svg @@ -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="1575804206892" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3145" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M826.56 470.016c-32.896 0-64.384 12.288-89.984 35.52l0-104.96c0-62.208-50.496-112.832-112.64-113.088L623.936 287.04 519.552 287.104C541.824 262.72 554.56 230.72 554.56 197.12c0-73.536-59.904-133.44-133.504-133.44-73.472 0-133.376 59.904-133.376 133.44 0 32.896 12.224 64.256 35.52 89.984L175.232 287.104l0 0.576C113.728 288.704 64 338.88 64 400.576l0.32 0 0.32 116.48C60.864 544.896 70.592 577.728 100.8 588.48c12.736 4.608 37.632 7.488 60.864-25.28 12.992-18.368 34.24-29.248 56.64-29.248 38.336 0 69.504 31.104 69.504 69.312 0 38.4-31.168 69.504-69.504 69.504-22.656 0-44.032-11.264-57.344-30.4C138.688 610.112 112.576 615.36 102.464 619.136c-29.824 10.752-39.104 43.776-38.144 67.392l0 160.384L64 846.912C64 909.248 114.752 960 177.216 960l446.272 0c62.4 0 113.152-50.752 113.152-113.152l0-145.024c24.384 22.272 56.384 35.008 89.984 35.008 73.536 0 133.44-59.904 133.44-133.504C960 529.92 900.096 470.016 826.56 470.016zM826.56 672.896c-22.72 0-44.032-11.264-57.344-30.4-22.272-32.384-48.448-27.136-58.56-23.36-29.824 10.752-39.04 43.776-38.08 67.392l0 160.384c0 27.136-22.016 49.152-49.152 49.152L177.216 896.064C150.08 896 128 873.984 128 846.848l0.32 0 0-145.024c24.384 22.272 56.384 35.008 89.984 35.008 73.6 0 133.504-59.904 133.504-133.504 0-73.472-59.904-133.376-133.504-133.376-32.896 0-64.32 12.288-89.984 35.52l0-104.96L128 400.512c0-27.072 22.08-49.152 49.216-49.152L177.216 351.04 334.656 350.72c3.776 0.512 7.616 0.832 11.52 0.832 24.896 0 50.752-10.816 60.032-37.056 4.544-12.736 7.424-37.568-25.344-60.736C362.624 240.768 351.68 219.52 351.68 197.12c0-38.272 31.104-69.44 69.376-69.44 38.336 0 69.504 31.168 69.504 69.44 0 22.72-11.264 44.032-30.528 57.472C427.968 276.736 433.088 302.784 436.8 313.024c10.752 29.888 43.072 39.232 67.392 38.08l119.232 0 0 0.384c27.136 0 49.152 22.08 49.152 49.152l0.256 116.48c-3.776 27.84 6.016 60.736 36.224 71.488 12.736 4.608 37.632 7.488 60.8-25.28 13.056-18.368 34.24-29.248 56.704-29.248C864.832 534.016 896 565.12 896 603.392 896 641.728 864.832 672.896 826.56 672.896z" p-id="3146"></path></svg> \ No newline at end of file diff --git a/src/assets/icons/svg/dashboard.svg b/src/assets/icons/svg/dashboard.svg new file mode 100644 index 0000000..5317d37 --- /dev/null +++ b/src/assets/icons/svg/dashboard.svg @@ -0,0 +1 @@ +<svg width="128" height="100" xmlns="http://www.w3.org/2000/svg"><path d="M27.429 63.638c0-2.508-.893-4.65-2.679-6.424-1.786-1.775-3.94-2.662-6.464-2.662-2.524 0-4.679.887-6.465 2.662-1.785 1.774-2.678 3.916-2.678 6.424 0 2.508.893 4.65 2.678 6.424 1.786 1.775 3.94 2.662 6.465 2.662 2.524 0 4.678-.887 6.464-2.662 1.786-1.775 2.679-3.916 2.679-6.424zm13.714-31.801c0-2.508-.893-4.65-2.679-6.424-1.785-1.775-3.94-2.662-6.464-2.662-2.524 0-4.679.887-6.464 2.662-1.786 1.774-2.679 3.916-2.679 6.424 0 2.508.893 4.65 2.679 6.424 1.785 1.774 3.94 2.662 6.464 2.662 2.524 0 4.679-.888 6.464-2.662 1.786-1.775 2.679-3.916 2.679-6.424zM71.714 65.98l7.215-27.116c.285-1.23.107-2.378-.536-3.443-.643-1.064-1.56-1.762-2.75-2.094-1.19-.33-2.333-.177-3.429.462-1.095.639-1.81 1.573-2.143 2.804l-7.214 27.116c-2.857.237-5.405 1.266-7.643 3.088-2.238 1.822-3.738 4.152-4.5 6.992-.952 3.644-.476 7.098 1.429 10.364 1.905 3.265 4.69 5.37 8.357 6.317 3.667.947 7.143.474 10.429-1.42 3.285-1.892 5.404-4.66 6.357-8.305.762-2.84.619-5.607-.429-8.305-1.047-2.697-2.762-4.85-5.143-6.46zm47.143-2.342c0-2.508-.893-4.65-2.678-6.424-1.786-1.775-3.94-2.662-6.465-2.662-2.524 0-4.678.887-6.464 2.662-1.786 1.774-2.679 3.916-2.679 6.424 0 2.508.893 4.65 2.679 6.424 1.786 1.775 3.94 2.662 6.464 2.662 2.524 0 4.679-.887 6.465-2.662 1.785-1.775 2.678-3.916 2.678-6.424zm-45.714-45.43c0-2.509-.893-4.65-2.679-6.425C68.68 10.01 66.524 9.122 64 9.122c-2.524 0-4.679.887-6.464 2.661-1.786 1.775-2.679 3.916-2.679 6.425 0 2.508.893 4.65 2.679 6.424 1.785 1.774 3.94 2.662 6.464 2.662 2.524 0 4.679-.888 6.464-2.662 1.786-1.775 2.679-3.916 2.679-6.424zm32 13.629c0-2.508-.893-4.65-2.679-6.424-1.785-1.775-3.94-2.662-6.464-2.662-2.524 0-4.679.887-6.464 2.662-1.786 1.774-2.679 3.916-2.679 6.424 0 2.508.893 4.65 2.679 6.424 1.785 1.774 3.94 2.662 6.464 2.662 2.524 0 4.679-.888 6.464-2.662 1.786-1.775 2.679-3.916 2.679-6.424zM128 63.638c0 12.351-3.357 23.78-10.071 34.286-.905 1.372-2.19 2.058-3.858 2.058H13.93c-1.667 0-2.953-.686-3.858-2.058C3.357 87.465 0 76.037 0 63.638c0-8.613 1.69-16.847 5.071-24.703C8.452 31.08 13 24.312 18.714 18.634c5.715-5.68 12.524-10.199 20.429-13.559C47.048 1.715 55.333.035 64 .035c8.667 0 16.952 1.68 24.857 5.04 7.905 3.36 14.714 7.88 20.429 13.559 5.714 5.678 10.262 12.446 13.643 20.301 3.38 7.856 5.071 16.09 5.071 24.703z"/></svg> \ No newline at end of file diff --git a/src/assets/icons/svg/date-range.svg b/src/assets/icons/svg/date-range.svg new file mode 100644 index 0000000..fda571e --- /dev/null +++ b/src/assets/icons/svg/date-range.svg @@ -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="1579774833889" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1376" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M887.466667 192.853333h-100.693334V119.466667c0-10.24-6.826667-17.066667-17.066666-17.066667s-17.066667 6.826667-17.066667 17.066667v73.386666H303.786667V119.466667c0-10.24-6.826667-17.066667-17.066667-17.066667s-17.066667 6.826667-17.066667 17.066667v73.386666H168.96c-46.08 0-85.333333 37.546667-85.333333 85.333334V836.266667c0 46.08 37.546667 85.333333 85.333333 85.333333H887.466667c46.08 0 85.333333-37.546667 85.333333-85.333333V278.186667c0-47.786667-37.546667-85.333333-85.333333-85.333334z m-718.506667 34.133334h100.693333v66.56c0 10.24 6.826667 17.066667 17.066667 17.066666s17.066667-6.826667 17.066667-17.066666v-66.56h450.56v66.56c0 10.24 6.826667 17.066667 17.066666 17.066666s17.066667-6.826667 17.066667-17.066666v-66.56H887.466667c27.306667 0 51.2 22.186667 51.2 51.2v88.746666H117.76v-88.746666c0-29.013333 22.186667-51.2 51.2-51.2zM887.466667 887.466667H168.96c-27.306667 0-51.2-22.186667-51.2-51.2V401.066667H938.666667V836.266667c0 27.306667-22.186667 51.2-51.2 51.2z" p-id="1377"></path><path d="M858.453333 493.226667H327.68c-10.24 0-17.066667 6.826667-17.066667 17.066666v114.346667h-116.053333c-10.24 0-17.066667 6.826667-17.066667 17.066667v133.12c0 10.24 6.826667 17.066667 17.066667 17.066666H460.8c10.24 0 17.066667-6.826667 17.066667-17.066666v-114.346667h380.586666c10.24 0 17.066667-6.826667 17.066667-17.066667v-133.12c0-10.24-6.826667-17.066667-17.066667-17.066666z m-413.013333 34.133333v97.28h-98.986667v-97.28h98.986667z m-230.4 131.413333h98.986667v98.986667h-98.986667v-98.986667z m131.413333 97.28v-97.28h98.986667v97.28h-98.986667z m133.12-228.693333h97.28v98.986667h-97.28v-98.986667z m131.413334 0h98.986666v98.986667h-98.986666v-98.986667z m230.4 97.28h-98.986667v-98.986667h98.986667v98.986667z" p-id="1378"></path></svg> \ No newline at end of file diff --git a/src/assets/icons/svg/date.svg b/src/assets/icons/svg/date.svg new file mode 100644 index 0000000..52dc73e --- /dev/null +++ b/src/assets/icons/svg/date.svg @@ -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="1577186573535" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1068" xmlns:xlink="http://www.w3.org/1999/xlink" width="81" height="81"><defs><style type="text/css"></style></defs><path d="M479.85714249 608.42857168h64.28571502c19.28571417 0 32.14285751-12.85714249 32.14285664-32.14285751s-12.85714249-32.14285751-32.14285664-32.14285664h-64.28571504c-19.28571417 0-32.14285751 12.85714249-32.14285664 32.14285662s12.85714249 32.14285751 32.14285664 32.14285753z m-2e-8 122.14285665h64.28571504c19.28571417 0 32.14285751-12.85714249 32.14285664-32.14285665s-12.85714249-32.14285751-32.14285664-32.14285751h-64.28571504c-19.28571417 0-32.14285751 12.85714249-32.14285664 32.14285751s12.85714249 32.14285751 32.14285664 32.14285664z m353.57142921-559.28571416h-128.57142921v-32.14285664c0-19.28571417-12.85714249-32.14285751-32.14285664-32.14285753s-32.14285751 12.85714249-32.14285751 32.14285753v32.14285664h-257.14285665v-32.14285664c0-19.28571417-12.85714249-32.14285751-32.14285752-32.14285753s-32.14285751 12.85714249-32.14285664 32.14285753v32.14285664h-128.57142919c-70.71428585 0-128.57142832 57.85714249-128.57142832 122.14285751v501.42857081c0 70.71428585 57.85714249 128.57142832 128.57142832 122.14285751h642.85714335c70.71428585 0 128.57142832-57.85714249 128.57142833-122.14285751v-501.42857081c0-70.71428585-57.85714249-122.14285753-128.57142833-122.14285751z m64.28571415 623.57142832c0 32.14285751-32.14285751 64.28571415-64.28571416 64.28571504h-642.85714335c-32.14285751 0-64.28571415-25.71428583-64.28571417-64.28571504v-372.85714249h771.42857168v372.85714249z m0-437.14285664h-771.42857168v-64.28571417c0-32.14285751 32.14285751-64.28571415 64.28571417-64.28571415h128.57142919v32.14285664c0 19.28571417 12.85714249 32.14285751 32.14285664 32.14285751s32.14285751-12.85714249 32.14285753-32.14285751v-32.14285664h257.14285665v32.14285664c0 19.28571417 12.85714249 32.14285751 32.1428575 32.14285751s32.14285751-12.85714249 32.14285664-32.14285751v-32.14285664h128.57142921c32.14285751 0 64.28571415 25.71428583 64.28571415 64.28571415v64.28571417z m-610.71428583 372.85714247h64.28571415c19.28571417 0 32.14285751-12.85714249 32.14285753-32.14285664s-12.85714249-32.14285751-32.14285753-32.14285751h-64.28571415c-19.28571417 0-32.14285751 12.85714249-32.14285751 32.14285751s12.85714249 32.14285751 32.14285751 32.14285665z m385.71428583-122.14285664h64.28571417c19.28571417 0 32.14285751-12.85714249 32.14285751-32.14285751s-12.85714249-32.14285751-32.14285751-32.14285664h-64.28571415c-19.28571417 0-32.14285751 12.85714249-32.14285753 32.14285664s12.85714249 32.14285751 32.14285753 32.14285751z m-385.71428583 0h64.28571415c19.28571417 0 32.14285751-12.85714249 32.14285753-32.14285751s-12.85714249-32.14285751-32.14285753-32.14285664h-64.28571415c-19.28571417 0-32.14285751 12.85714249-32.14285751 32.14285664s12.85714249 32.14285751 32.14285751 32.14285751z m385.71428583 122.14285665h64.28571417c19.28571417 0 32.14285751-12.85714249 32.14285751-32.14285665s-12.85714249-32.14285751-32.14285751-32.14285751h-64.28571415c-19.28571417 0-32.14285751 12.85714249-32.14285753 32.14285751s12.85714249 32.14285751 32.14285753 32.14285665z" p-id="1069"></path></svg> \ No newline at end of file diff --git a/src/assets/icons/svg/dict.svg b/src/assets/icons/svg/dict.svg new file mode 100644 index 0000000..4849377 --- /dev/null +++ b/src/assets/icons/svg/dict.svg @@ -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="1566035680909" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3601" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M1002.0848 744.672l-33.568 10.368c0.96 7.264 2.144 14.304 2.144 21.76 0 7.328-1.184 14.432-2.368 21.568l33.792 10.56c7.936 2.24 14.496 7.616 18.336 14.752 3.84 7.328 4.672 15.808 1.952 23.552-5.376 16-23.168 24.672-39.936 19.68l-34.176-10.624c-7.136 12.8-15.776 24.672-26.208 35.2l20.8 27.488a28.96 28.96 0 0 1 5.824 22.816 29.696 29.696 0 0 1-12.704 19.616 32.544 32.544 0 0 1-44.416-6.752l-20.8-27.552c-13.696 6.56-28.192 11.2-43.008 13.888v33.632c0 16.736-14.112 30.432-31.648 30.432-17.6 0-31.872-13.696-31.872-30.432v-33.632a167.616 167.616 0 0 1-42.88-13.888l-20.928 27.552c-10.72 13.76-30.08 16.64-44.288 6.752a29.632 29.632 0 0 1-12.704-19.616 29.28 29.28 0 0 1 5.696-22.816l20.896-27.808a166.72 166.72 0 0 1-27.008-34.688l-33.376 10.432c-16.8 5.184-34.56-3.552-39.936-19.616a29.824 29.824 0 0 1 20.224-38.24l33.472-10.432c-0.8-7.264-2.016-14.304-2.016-21.824 0-7.36 1.184-14.496 2.304-21.632l-33.792-10.368c-16.672-5.376-25.632-22.496-20.224-38.432 5.376-16 23.136-24.672 39.936-19.68l34.016 10.752c7.328-12.672 15.84-24.8 26.336-35.328l-20.8-27.552a29.44 29.44 0 0 1 6.944-42.432 32.704 32.704 0 0 1 44.384 6.752l20.832 27.616c13.696-6.432 28.224-11.2 43.104-13.952v-33.568c0-16.736 14.048-30.432 31.648-30.432 17.536 0 31.808 13.568 31.808 30.432v33.504c15.072 2.688 29.344 7.808 42.848 14.016l20.992-27.616a32.48 32.48 0 0 1 44.224-6.752 29.568 29.568 0 0 1 7.136 42.432l-21.024 27.808c10.432 10.432 19.872 21.888 27.04 34.752l33.376-10.432c16.768-5.12 34.56 3.68 39.936 19.68 5.536 15.936-3.712 33.056-20.32 38.304z m-206.016-74.432c-61.344 0-111.136 47.808-111.136 106.56 0 58.88 49.792 106.496 111.136 106.496 61.312 0 111.104-47.616 111.104-106.496 0-58.752-49.792-106.56-111.104-106.56z" p-id="3602"></path><path d="M802.7888 57.152h-76.448c0-22.08-21.024-38.24-42.848-38.24H39.3968a39.68 39.68 0 0 0-39.36 40.032v795.616s41.888 120.192 110.752 120.192H673.2848a227.488 227.488 0 0 1-107.04-97.44H117.6368s-40.608-13.696-40.608-41.248l470.304-0.256 1.664 3.36a227.68 227.68 0 0 1-12.64-73.632c0-60.576 24-118.624 66.88-161.44a228.352 228.352 0 0 1 123.552-63.392l-3.2 0.288 2.144-424.672h38.208l0.576 421.024c27.04 0 52.672 4.8 76.64 13.344V101.536c0.032 0-6.304-44.384-38.368-44.384zM149.7648 514.336H72.3888v-77.408H149.7648v77.408z m0-144.32H72.3888v-77.44H149.7648v77.44z m0-137.248H72.3888v-77.44H149.7648v77.44z m501.856 281.568H206.0848v-77.408h445.536v77.408z m0-144.32H206.0848v-77.44h445.536v77.44z m0-137.248H206.0848v-77.44h445.536v77.44z" p-id="3603"></path></svg> \ No newline at end of file diff --git a/src/assets/icons/svg/documentation.svg b/src/assets/icons/svg/documentation.svg new file mode 100644 index 0000000..7043122 --- /dev/null +++ b/src/assets/icons/svg/documentation.svg @@ -0,0 +1 @@ +<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M71.984 44.815H115.9L71.984 9.642v35.173zM16.094.05h63.875l47.906 38.37v76.74c0 3.392-1.682 6.645-4.677 9.044-2.995 2.399-7.056 3.746-11.292 3.746H16.094c-4.236 0-8.297-1.347-11.292-3.746-2.995-2.399-4.677-5.652-4.677-9.044V12.84C.125 5.742 7.23.05 16.094.05zm71.86 102.32V89.58h-71.86v12.79h71.86zm23.952-25.58V64H16.094v12.79h95.812z"/></svg> \ No newline at end of file diff --git a/src/assets/icons/svg/download.svg b/src/assets/icons/svg/download.svg new file mode 100644 index 0000000..c896951 --- /dev/null +++ b/src/assets/icons/svg/download.svg @@ -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="1569915748289" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3062" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M768.35456 416a256 256 0 1 0-512 0 192 192 0 1 0 0 384v64a256 256 0 0 1-58.88-505.216 320.128 320.128 0 0 1 629.76 0A256.128 256.128 0 0 1 768.35456 864v-64a192 192 0 0 0 0-384z m-512 384h64v64H256.35456v-64z m448 0h64v64h-64v-64z" fill="#333333" p-id="3063"></path><path d="M539.04256 845.248V512.192a32.448 32.448 0 0 0-32-32.192c-17.664 0-32 14.912-32 32.192v333.056l-36.096-36.096a32.192 32.192 0 0 0-45.056 0.192 31.616 31.616 0 0 0-0.192 45.056l90.88 90.944a31.36 31.36 0 0 0 22.528 9.088 30.08 30.08 0 0 0 22.4-9.088l90.88-90.88a32.192 32.192 0 0 0-0.192-45.12 31.616 31.616 0 0 0-45.056-0.192l-36.096 36.096z" fill="#333333" p-id="3064"></path></svg> \ No newline at end of file diff --git a/src/assets/icons/svg/drag.svg b/src/assets/icons/svg/drag.svg new file mode 100644 index 0000000..4185d3c --- /dev/null +++ b/src/assets/icons/svg/drag.svg @@ -0,0 +1 @@ +<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M73.137 29.08h-9.209 29.7L63.886.093 34.373 29.08h20.49v27.035H27.238v17.948h27.625v27.133h18.274V74.063h27.41V56.115h-27.41V29.08zm-9.245 98.827l27.518-26.711H36.59l27.302 26.71zM.042 64.982l27.196 27.029V38.167L.042 64.982zm100.505-26.815V92.01l27.41-27.029-27.41-26.815z"/></svg> \ No newline at end of file diff --git a/src/assets/icons/svg/druid.svg b/src/assets/icons/svg/druid.svg new file mode 100644 index 0000000..a2b4b4e --- /dev/null +++ b/src/assets/icons/svg/druid.svg @@ -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="1566036347051" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="5853" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M832 128H192a64.19 64.19 0 0 0-64 64v640a64.19 64.19 0 0 0 64 64h640a64.19 64.19 0 0 0 64-64V192a64.19 64.19 0 0 0-64-64z m0 703.89l-0.11 0.11H192.11l-0.11-0.11V768h640zM832 544H720L605.6 696.54 442.18 435.07 333.25 544H192v-64h114.75l147.07-147.07L610.4 583.46 688 480h144z m0-288H192v-63.89l0.11-0.11h639.78l0.11 0.11z" p-id="5854"></path></svg> \ No newline at end of file diff --git a/src/assets/icons/svg/edit.svg b/src/assets/icons/svg/edit.svg new file mode 100644 index 0000000..d26101f --- /dev/null +++ b/src/assets/icons/svg/edit.svg @@ -0,0 +1 @@ +<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M106.133 67.2a4.797 4.797 0 0 0-4.8 4.8c0 .187.014.36.027.533h-.027V118.4H9.6V26.667h50.133c2.654 0 4.8-2.147 4.8-4.8 0-2.654-2.146-4.8-4.8-4.8H9.6a9.594 9.594 0 0 0-9.6 9.6V118.4c0 5.307 4.293 9.6 9.6 9.6h91.733c5.307 0 9.6-4.293 9.6-9.6V72.533h-.026c.013-.173.026-.346.026-.533 0-2.653-2.146-4.8-4.8-4.8z"/><path d="M125.16 13.373L114.587 2.8c-3.747-3.747-9.854-3.72-13.6.027l-52.96 52.96a4.264 4.264 0 0 0-.907 1.36L33.813 88.533c-.746 1.76-.226 3.534.907 4.68 1.133 1.147 2.92 1.667 4.693.92l31.4-13.293c.507-.213.96-.52 1.36-.907l52.96-52.96c3.747-3.746 3.774-9.853.027-13.6zM66.107 72.4l-18.32 7.76 7.76-18.32L92.72 24.667l10.56 10.56L66.107 72.4zm52.226-52.227l-8.266 8.267-10.56-10.56 8.266-8.267.027-.026 10.56 10.56-.027.026z"/></svg> \ No newline at end of file diff --git a/src/assets/icons/svg/education.svg b/src/assets/icons/svg/education.svg new file mode 100644 index 0000000..7bfb01d --- /dev/null +++ b/src/assets/icons/svg/education.svg @@ -0,0 +1 @@ +<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M88.883 119.565c-7.284 0-19.434 2.495-21.333 8.25v.127c-4.232.13-5.222 0-7.108 0-1.895-5.76-14.045-8.256-21.333-8.256H0V0h42.523c9.179 0 17.109 5.47 21.47 13.551C68.352 5.475 76.295 0 85.478 0H128v119.57l-39.113-.005h-.004zM60.442 24.763c0-9.651-8.978-16.507-17.777-16.507H7.108V111.43H39.11c7.054-.14 18.177.082 21.333 6.12v-4.628c-.134-5.722-.004-13.522 0-13.832V27.413l.004-2.655-.004.005zm60.442-16.517h-35.55c-8.802 0-17.78 6.856-17.78 16.493v74.259c.004.32.138 8.115 0 13.813v4.627c3.155-6.022 14.279-6.26 21.333-6.114h32V8.25l-.003-.005z"/></svg> \ No newline at end of file diff --git a/src/assets/icons/svg/email.svg b/src/assets/icons/svg/email.svg new file mode 100644 index 0000000..74d25e2 --- /dev/null +++ b/src/assets/icons/svg/email.svg @@ -0,0 +1 @@ +<svg width="128" height="96" xmlns="http://www.w3.org/2000/svg"><path d="M64.125 56.975L120.188.912A12.476 12.476 0 0 0 115.5 0h-103c-1.588 0-3.113.3-4.513.838l56.138 56.137z"/><path d="M64.125 68.287l-62.3-62.3A12.42 12.42 0 0 0 0 12.5v71C0 90.4 5.6 96 12.5 96h103c6.9 0 12.5-5.6 12.5-12.5v-71a12.47 12.47 0 0 0-1.737-6.35L64.125 68.287z"/></svg> \ No newline at end of file diff --git a/src/assets/icons/svg/enter.svg b/src/assets/icons/svg/enter.svg new file mode 100644 index 0000000..f7cabf2 --- /dev/null +++ b/src/assets/icons/svg/enter.svg @@ -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> \ No newline at end of file diff --git a/src/assets/icons/svg/example.svg b/src/assets/icons/svg/example.svg new file mode 100644 index 0000000..46f42b5 --- /dev/null +++ b/src/assets/icons/svg/example.svg @@ -0,0 +1 @@ +<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M96.258 57.462h31.421C124.794 27.323 100.426 2.956 70.287.07v31.422a32.856 32.856 0 0 1 25.971 25.97zm-38.796-25.97V.07C27.323 2.956 2.956 27.323.07 57.462h31.422a32.856 32.856 0 0 1 25.97-25.97zm12.825 64.766v31.421c30.46-2.885 54.507-27.253 57.713-57.712H96.579c-2.886 13.466-13.146 23.726-26.292 26.291zM31.492 70.287H.07c2.886 30.46 27.253 54.507 57.713 57.713V96.579c-13.466-2.886-23.726-13.146-26.291-26.292z"/></svg> \ No newline at end of file diff --git a/src/assets/icons/svg/excel.svg b/src/assets/icons/svg/excel.svg new file mode 100644 index 0000000..74d97b8 --- /dev/null +++ b/src/assets/icons/svg/excel.svg @@ -0,0 +1 @@ +<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M78.208 16.576v8.384h38.72v5.376h-38.72v8.704h38.72v5.376h-38.72v8.576h38.72v5.376h-38.72v8.576h38.72v5.376h-38.72v8.576h38.72v5.376h-38.72v8.512h38.72v5.376h-38.72v11.136H128v-94.72H78.208zM0 114.368L72.128 128V0L0 13.632v100.736z"/><path d="M28.672 82.56h-11.2l14.784-23.488-14.08-22.592h11.52l8.192 14.976 8.448-14.976h11.136l-14.08 22.208L58.368 82.56H46.656l-8.768-15.68z"/></svg> \ No newline at end of file diff --git a/src/assets/icons/svg/exit-fullscreen.svg b/src/assets/icons/svg/exit-fullscreen.svg new file mode 100644 index 0000000..485c128 --- /dev/null +++ b/src/assets/icons/svg/exit-fullscreen.svg @@ -0,0 +1 @@ +<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M49.217 41.329l-.136-35.24c-.06-2.715-2.302-4.345-5.022-4.405h-3.65c-2.712-.06-4.866 2.303-4.806 5.016l.152 19.164-24.151-23.79a6.698 6.698 0 0 0-9.499 0 6.76 6.76 0 0 0 0 9.526l23.93 23.713-18.345.074c-2.712-.069-5.228 1.813-5.64 5.02v3.462c.069 2.721 2.31 4.97 5.022 5.03l35.028-.207c.052.005.087.025.133.025l2.457.054a4.626 4.626 0 0 0 3.436-1.38c.88-.874 1.205-2.096 1.169-3.462l-.262-2.465c0-.048.182-.081.182-.136h.002zm52.523 51.212l18.32-.073c2.713.06 5.224-1.609 5.64-4.815v-3.462c-.068-2.722-2.317-4.97-5.021-5.04l-34.58.21c-.053 0-.086-.021-.138-.021l-2.451-.06a4.64 4.64 0 0 0-3.445 1.381c-.885.868-1.201 2.094-1.174 3.46l.27 2.46c.005.06-.177.095-.177.141l.141 34.697c.069 2.713 2.31 4.338 5.022 4.397l3.45.006c2.705.062 4.867-2.31 4.8-5.026l-.153-18.752 24.151 23.946a6.69 6.69 0 0 0 9.494 0 6.747 6.747 0 0 0 0-9.523L101.74 92.54v.001zM48.125 80.662a4.636 4.636 0 0 0-3.437-1.382l-2.457.06c-.05 0-.082.022-.137.022l-35.025-.21c-2.712.07-4.957 2.318-5.022 5.04v3.462c.409 3.206 2.925 4.874 5.633 4.814l18.554.06-24.132 23.928c-2.62 2.626-2.62 6.89 0 9.524a6.694 6.694 0 0 0 9.496 0l24.155-23.79-.155 18.866c-.06 2.722 2.094 5.093 4.801 5.025h3.65c2.72-.069 4.962-1.685 5.022-4.406l.141-34.956c0-.05-.182-.082-.182-.136l.262-2.46c.03-1.366-.286-2.592-1.166-3.46h-.001zM80.08 47.397a4.62 4.62 0 0 0 3.443 1.374l2.45-.054c.055 0 .088-.02.143-.028l35.08.21c2.712-.062 4.953-2.312 5.021-5.033l.009-3.463c-.417-3.211-2.937-5.084-5.64-5.025l-18.615-.073 23.917-23.715c2.63-2.623 2.63-6.879.008-9.513a6.691 6.691 0 0 0-9.494 0L92.251 26.016l.155-19.312c.065-2.713-2.097-5.085-4.802-5.025h-3.45c-2.713.069-4.954 1.693-5.022 4.406l-.139 35.247c0 .054.18.088.18.136l-.267 2.465c-.028 1.366.288 2.588 1.174 3.463v.001z"/></svg> \ No newline at end of file diff --git a/src/assets/icons/svg/eye-open.svg b/src/assets/icons/svg/eye-open.svg new file mode 100644 index 0000000..88dcc98 --- /dev/null +++ b/src/assets/icons/svg/eye-open.svg @@ -0,0 +1 @@ +<svg class="icon" viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg" width="128" height="128"><defs><style/></defs><path d="M512 128q69.675 0 135.51 21.163t115.498 54.997 93.483 74.837 73.685 82.006 51.67 74.837 32.17 54.827L1024 512q-2.347 4.992-6.315 13.483T998.87 560.17t-31.658 51.669-44.331 59.99-56.832 64.34-69.504 60.16-82.347 51.5-94.848 34.687T512 896q-69.675 0-135.51-21.163t-115.498-54.826-93.483-74.326-73.685-81.493-51.67-74.496-32.17-54.997L0 513.707q2.347-4.992 6.315-13.483t18.816-34.816 31.658-51.84 44.331-60.33 56.832-64.683 69.504-60.331 82.347-51.84 94.848-34.816T512 128.085zm0 85.333q-46.677 0-91.648 12.331t-81.152 31.83-70.656 47.146-59.648 54.485-48.853 57.686-37.675 52.821-26.325 43.99q12.33 21.674 26.325 43.52t37.675 52.351 48.853 57.003 59.648 53.845T339.2 767.02t81.152 31.488T512 810.667t91.648-12.331 81.152-31.659 70.656-46.848 59.648-54.186 48.853-57.344 37.675-52.651T927.957 512q-12.33-21.675-26.325-43.648t-37.675-52.65-48.853-57.345-59.648-54.186-70.656-46.848-81.152-31.659T512 213.334zm0 128q70.656 0 120.661 50.006T682.667 512 632.66 632.661 512 682.667 391.339 632.66 341.333 512t50.006-120.661T512 341.333zm0 85.334q-35.328 0-60.33 25.002T426.666 512t25.002 60.33T512 597.334t60.33-25.002T597.334 512t-25.002-60.33T512 426.666z"/></svg> \ No newline at end of file diff --git a/src/assets/icons/svg/eye.svg b/src/assets/icons/svg/eye.svg new file mode 100644 index 0000000..16ed2d8 --- /dev/null +++ b/src/assets/icons/svg/eye.svg @@ -0,0 +1 @@ +<svg width="128" height="64" xmlns="http://www.w3.org/2000/svg"><path d="M127.072 7.994c1.37-2.208.914-5.152-.914-6.87-2.056-1.717-4.797-1.226-6.396.982-.229.245-25.586 32.382-55.74 32.382-29.24 0-55.74-32.382-55.968-32.627-1.6-1.963-4.57-2.208-6.397-.49C-.17 3.086-.399 6.275 1.2 8.238c.457.736 5.94 7.36 14.62 14.72L4.17 35.96c-1.828 1.963-1.6 5.152.228 6.87.457.98 1.6 1.471 2.742 1.471s2.284-.49 3.198-1.472l12.564-13.983c5.94 4.416 13.021 8.587 20.788 11.53l-4.797 17.418c-.685 2.699.686 5.397 3.198 6.133h1.37c2.057 0 3.884-1.472 4.341-3.68L52.6 42.83c3.655.736 7.538 1.227 11.422 1.227 3.883 0 7.767-.49 11.422-1.227l4.797 17.173c.457 2.208 2.513 3.68 4.34 3.68.457 0 .914 0 1.143-.246 2.513-.736 3.883-3.434 3.198-6.133l-4.797-17.172c7.767-2.944 14.848-7.114 20.788-11.53l12.336 13.738c.913.981 2.056 1.472 3.198 1.472s2.284-.49 3.198-1.472c1.828-1.963 1.828-4.906.228-6.87l-11.65-13.001c9.366-7.36 14.849-14.474 14.849-14.474z"/></svg> \ No newline at end of file diff --git a/src/assets/icons/svg/form.svg b/src/assets/icons/svg/form.svg new file mode 100644 index 0000000..dcbaa18 --- /dev/null +++ b/src/assets/icons/svg/form.svg @@ -0,0 +1 @@ +<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M84.068 23.784c-1.02 0-1.877-.32-2.572-.96a8.588 8.588 0 0 1-1.738-2.237 11.524 11.524 0 0 1-1.042-2.621c-.232-.895-.348-1.641-.348-2.238V0h.278c.834 0 1.622.085 2.363.256.742.17 1.645.575 2.711 1.214 1.066.64 2.363 1.535 3.892 2.686 1.53 1.15 3.453 2.664 5.77 4.54 2.502 2.045 4.494 3.771 5.977 5.178 1.483 1.406 2.618 2.6 3.406 3.58.787.98 1.274 1.812 1.46 2.494.185.682.277 1.278.277 1.79v2.046H84.068zM127.3 84.01c.278.682.464 1.535.556 2.558.093 1.023-.37 2.003-1.39 2.94-.463.427-.88.832-1.25 1.215-.372.384-.696.704-.974.96a6.69 6.69 0 0 1-.973.767l-11.816-10.741a44.331 44.331 0 0 0 1.877-1.535 31.028 31.028 0 0 1 1.737-1.406c1.112-.938 2.317-1.343 3.615-1.215 1.297.128 2.363.405 3.197.83.927.427 1.923 1.173 2.989 2.239 1.065 1.065 1.876 2.195 2.432 3.388zM78.23 95.902c2.038 0 3.752-.511 5.143-1.534l-26.969 25.83H18.037c-1.761 0-3.684-.47-5.77-1.407a24.549 24.549 0 0 1-5.838-3.709 21.373 21.373 0 0 1-4.518-5.306c-1.204-2.003-1.807-4.07-1.807-6.202V16.495c0-1.79.44-3.665 1.32-5.626A18.41 18.41 0 0 1 5.04 5.562a21.798 21.798 0 0 1 5.213-3.964C12.198.533 14.237 0 16.37 0h53.24v15.984c0 1.62.278 3.367.834 5.242a16.704 16.704 0 0 0 2.572 5.179c1.159 1.577 2.665 2.898 4.518 3.964 1.853 1.066 4.078 1.598 6.673 1.598h20.295v42.325L85.458 92.45c1.02-1.364 1.529-2.856 1.529-4.476 0-2.216-.857-4.113-2.572-5.69-1.714-1.577-3.776-2.366-6.186-2.366H26.1c-2.409 0-4.448.789-6.116 2.366-1.668 1.577-2.502 3.474-2.502 5.69 0 2.217.834 4.092 2.502 5.626 1.668 1.535 3.707 2.302 6.117 2.302h52.13zM26.1 47.951c-2.41 0-4.449.789-6.117 2.366-1.668 1.577-2.502 3.473-2.502 5.69 0 2.216.834 4.092 2.502 5.626 1.668 1.534 3.707 2.302 6.117 2.302h52.13c2.409 0 4.47-.768 6.185-2.302 1.715-1.534 2.572-3.41 2.572-5.626 0-2.217-.857-4.113-2.572-5.69-1.714-1.577-3.776-2.366-6.186-2.366H26.1zm52.407 64.063l1.807-1.663 3.476-3.196a479.75 479.75 0 0 0 4.587-4.284 500.757 500.757 0 0 1 5.004-4.667c3.985-3.666 8.48-7.758 13.485-12.276l11.677 10.741-13.485 12.404-5.004 4.603-4.587 4.22a179.46 179.46 0 0 0-3.267 3.068c-.88.853-1.367 1.322-1.46 1.407-.463.341-.973.703-1.529 1.087-.556.383-1.112.703-1.668.959-.556.256-1.413.575-2.572.959a83.5 83.5 0 0 1-3.545 1.087 72.2 72.2 0 0 1-3.475.895c-1.112.256-1.946.426-2.502.511-1.112.17-1.854.043-2.224-.383-.371-.426-.464-1.151-.278-2.174.092-.511.278-1.279.556-2.302.278-1.023.602-2.067.973-3.132l1.042-3.005c.325-.938.58-1.577.765-1.918a10.157 10.157 0 0 1 2.224-2.941z"/></svg> \ No newline at end of file diff --git a/src/assets/icons/svg/fullscreen.svg b/src/assets/icons/svg/fullscreen.svg new file mode 100644 index 0000000..0e86b6f --- /dev/null +++ b/src/assets/icons/svg/fullscreen.svg @@ -0,0 +1 @@ +<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M38.47 52L52 38.462l-23.648-23.67L43.209 0H.035L0 43.137l14.757-14.865L38.47 52zm74.773 47.726L89.526 76 76 89.536l23.648 23.672L84.795 128h43.174L128 84.863l-14.757 14.863zM89.538 52l23.668-23.648L128 43.207V.038L84.866 0 99.73 14.76 76 38.472 89.538 52zM38.46 76L14.792 99.651 0 84.794v43.173l43.137.033-14.865-14.757L52 89.53 38.46 76z"/></svg> \ No newline at end of file diff --git a/src/assets/icons/svg/github.svg b/src/assets/icons/svg/github.svg new file mode 100644 index 0000000..db0a0d4 --- /dev/null +++ b/src/assets/icons/svg/github.svg @@ -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="1581238998885" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4187" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M511.542857 14.057143C228.914286 13.942857 0 242.742857 0 525.142857 0 748.457143 143.2 938.285714 342.628571 1008c26.857143 6.742857 22.742857-12.342857 22.742858-25.371429v-88.571428c-155.085714 18.171429-161.371429-84.457143-171.771429-101.6C172.571429 756.571429 122.857143 747.428571 137.714286 730.285714c35.314286-18.171429 71.314286 4.571429 113.028571 66.171429 30.171429 44.685714 89.028571 37.142857 118.857143 29.714286 6.514286-26.857143 20.457143-50.857143 39.657143-69.485715-160.685714-28.8-227.657143-126.857143-227.657143-243.428571 0-56.571429 18.628571-108.571429 55.2-150.514286-23.314286-69.142857 2.171429-128.342857 5.6-137.142857 66.4-5.942857 135.428571 47.542857 140.8 51.771429 37.714286-10.171429 80.8-15.542857 129.028571-15.542858 48.457143 0 91.657143 5.6 129.714286 15.885715 12.914286-9.828571 76.914286-55.771429 138.628572-50.171429 3.314286 8.8 28.228571 66.628571 6.285714 134.857143 37.028571 42.057143 55.885714 94.514286 55.885714 151.2 0 116.8-67.428571 214.971429-228.571428 243.314286a145.714286 145.714286 0 0 1 43.542857 104v128.571428c0.914286 10.285714 0 20.457143 17.142857 20.457143 202.4-68.228571 348.114286-259.428571 348.114286-484.685714 0-282.514286-229.028571-511.2-511.428572-511.2z" p-id="4188"></path></svg> \ No newline at end of file diff --git a/src/assets/icons/svg/guide.svg b/src/assets/icons/svg/guide.svg new file mode 100644 index 0000000..b271001 --- /dev/null +++ b/src/assets/icons/svg/guide.svg @@ -0,0 +1 @@ +<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M1.482 70.131l36.204 16.18 69.932-65.485-61.38 70.594 46.435 18.735c1.119.425 2.397-.17 2.797-1.363v-.085L127.998.047 1.322 65.874c-1.12.597-1.519 1.959-1.04 3.151.32.511.72.937 1.2 1.107zm44.676 57.821L64.22 107.26l-18.062-7.834v28.527z"/></svg> \ No newline at end of file diff --git a/src/assets/icons/svg/icon.svg b/src/assets/icons/svg/icon.svg new file mode 100644 index 0000000..82be8ee --- /dev/null +++ b/src/assets/icons/svg/icon.svg @@ -0,0 +1 @@ +<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M115.147.062a13 13 0 0 1 4.94.945c1.55.63 2.907 1.526 4.069 2.688a13.148 13.148 0 0 1 2.761 4.069c.678 1.55 1.017 3.245 1.017 5.086v102.3c0 3.681-1.187 6.733-3.56 9.155-2.373 2.422-5.352 3.633-8.937 3.633H12.992c-3.875 0-7-1.26-9.373-3.779-2.373-2.518-3.56-5.667-3.56-9.445V12.704c0-3.39 1.163-6.345 3.488-8.863C5.872 1.32 8.972.062 12.847.062h102.3zM81.434 109.047c1.744 0 3.003-.412 3.778-1.235.775-.824 1.163-1.914 1.163-3.27 0-1.26-.388-2.325-1.163-3.197-.775-.872-2.034-1.307-3.778-1.307H72.57c.097-.194.145-.485.145-.872V27.09h9.01c1.743 0 2.954-.436 3.633-1.308.678-.872 1.017-1.938 1.017-3.197 0-1.26-.34-2.325-1.017-3.197-.679-.872-1.89-1.308-3.633-1.308H46.268c-1.743 0-2.954.436-3.632 1.308-.678.872-1.018 1.938-1.018 3.197 0 1.26.34 2.325 1.018 3.197.678.872 1.889 1.308 3.632 1.308h8.138v72.075c0 .193.024.339.073.436.048.096.072.242.072.436H46.56c-1.744 0-3.003.435-3.778 1.307-.775.872-1.163 1.938-1.163 3.197 0 1.356.388 2.446 1.163 3.27.775.823 2.034 1.235 3.778 1.235h34.875z"/></svg> \ No newline at end of file diff --git a/src/assets/icons/svg/input.svg b/src/assets/icons/svg/input.svg new file mode 100644 index 0000000..ab91381 --- /dev/null +++ b/src/assets/icons/svg/input.svg @@ -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="1575802859706" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3102" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M896 224H128c-35.2 0-64 28.8-64 64v448c0 35.2 28.8 64 64 64h768c35.2 0 64-28.8 64-64V288c0-35.2-28.8-64-64-64z m0 480c0 19.2-12.8 32-32 32H160c-19.2 0-32-12.8-32-32V320c0-19.2 12.8-32 32-32h704c19.2 0 32 12.8 32 32v384z" p-id="3103"></path><path d="M224 352c-19.2 0-32 12.8-32 32v256c0 16 12.8 32 32 32s32-12.8 32-32V384c0-16-12.8-32-32-32z" p-id="3104"></path></svg> \ No newline at end of file diff --git a/src/assets/icons/svg/international.svg b/src/assets/icons/svg/international.svg new file mode 100644 index 0000000..e9b56ee --- /dev/null +++ b/src/assets/icons/svg/international.svg @@ -0,0 +1 @@ +<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M83.287 103.01c-1.57-3.84-6.778-10.414-15.447-19.548-2.327-2.444-2.182-4.306-1.338-9.862v-.64c.553-3.81 1.513-6.05 14.313-8.087 6.516-1.018 8.203 1.57 10.589 5.178l.785 1.193a12.625 12.625 0 0 0 6.43 5.207c1.134.524 2.53 1.164 4.421 2.24 4.596 2.53 4.596 5.41 4.596 11.753v.727a26.91 26.91 0 0 1-5.178 17.454 59.055 59.055 0 0 1-19.025 11.026c3.49-6.546.814-14.313 0-16.553l-.146-.087zM64 5.12a58.502 58.502 0 0 1 25.484 5.818 54.313 54.313 0 0 0-12.859 10.327c-.93 1.28-1.716 2.473-2.472 3.579-2.444 3.694-3.637 5.352-5.818 5.614a25.105 25.105 0 0 1-4.219 0c-4.276-.29-10.094-.64-11.956 4.422-1.193 3.23-1.396 11.956 2.444 16.495.66 1.077.778 2.4.32 3.578a7.01 7.01 0 0 1-2.066 3.229 18.938 18.938 0 0 1-2.909-2.91 18.91 18.91 0 0 0-8.32-6.603c-1.25-.349-2.647-.64-3.985-.93-3.782-.786-8.03-1.688-9.019-3.812a14.895 14.895 0 0 1-.727-5.818 21.935 21.935 0 0 0-1.396-9.25 8.873 8.873 0 0 0-5.557-4.946A58.705 58.705 0 0 1 64 5.12zM0 64c0 35.346 28.654 64 64 64 35.346 0 64-28.654 64-64 0-35.346-28.654-64-64-64C28.654 0 0 28.654 0 64z"/></svg> \ No newline at end of file diff --git a/src/assets/icons/svg/job.svg b/src/assets/icons/svg/job.svg new file mode 100644 index 0000000..2a93a25 --- /dev/null +++ b/src/assets/icons/svg/job.svg @@ -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="1566036191400" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="5472" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M934.912 1016.832H192c-14.336 0-25.6-11.264-25.6-25.6v-189.44c0-14.336 11.264-25.6 25.6-25.6s25.6 11.264 25.6 25.6v163.84h691.712V64H217.6v148.48c0 14.336-11.264 25.6-25.6 25.6s-25.6-11.264-25.6-25.6v-174.08c0-14.336 11.264-25.6 25.6-25.6h742.912c14.336 0 25.6 11.264 25.6 25.6v952.832c0 14.336-11.264 25.6-25.6 25.6z" p-id="5473"></path><path d="M232.96 371.2h-117.76c-14.336 0-25.6-11.264-25.6-25.6s11.264-25.6 25.6-25.6h117.76c14.336 0 25.6 11.264 25.6 25.6s-11.264 25.6-25.6 25.6zM232.96 540.16h-117.76c-14.336 0-25.6-11.264-25.6-25.6s11.264-25.6 25.6-25.6h117.76c14.336 0 25.6 11.264 25.6 25.6s-11.264 25.6-25.6 25.6zM232.96 698.88h-117.76c-14.336 0-25.6-11.264-25.6-25.6s11.264-25.6 25.6-25.6h117.76c14.336 0 25.6 11.264 25.6 25.6s-11.264 25.6-25.6 25.6zM574.464 762.88c-134.144 0-243.2-109.056-243.2-243.2S440.32 276.48 574.464 276.48s243.2 109.056 243.2 243.2-109.056 243.2-243.2 243.2z m0-435.2c-105.984 0-192 86.016-192 192S468.48 711.68 574.464 711.68s192-86.016 192-192S680.448 327.68 574.464 327.68z" p-id="5474"></path><path d="M663.04 545.28h-87.04c-14.336 0-25.6-11.264-25.6-25.6s11.264-25.6 25.6-25.6h87.04c14.336 0 25.6 11.264 25.6 25.6s-11.264 25.6-25.6 25.6z" p-id="5475"></path><path d="M576 545.28c-14.336 0-25.6-11.264-25.6-25.6v-87.04c0-14.336 11.264-25.6 25.6-25.6s25.6 11.264 25.6 25.6v87.04c0 14.336-11.264 25.6-25.6 25.6z" p-id="5476"></path></svg> \ No newline at end of file diff --git a/src/assets/icons/svg/language.svg b/src/assets/icons/svg/language.svg new file mode 100644 index 0000000..0082b57 --- /dev/null +++ b/src/assets/icons/svg/language.svg @@ -0,0 +1 @@ +<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M84.742 36.8c2.398 7.2 5.595 12.8 11.19 18.4 4.795-4.8 7.992-11.2 10.39-18.4h-21.58zm-52.748 40h20.78l-10.39-28-10.39 28z"/><path d="M111.916 0H16.009C7.218 0 .025 7.2.025 16v96c0 8.8 7.193 16 15.984 16h95.907c8.791 0 15.984-7.2 15.984-16V16c0-8.8-6.394-16-15.984-16zM72.754 103.2c-1.598 1.6-3.197 1.6-4.795 1.6-.8 0-2.398 0-3.197-.8-.8-.8-1.599 0-1.599-.8s-.799-1.6-1.598-3.2c-.8-1.6-.8-2.4-1.599-4l-3.196-8.8H28.797L25.6 96c-1.598 3.2-2.398 5.6-3.197 7.2-.8 1.6-2.398 1.6-4.795 1.6-1.599 0-3.197-.8-4.796-1.6-1.598-1.6-2.397-2.4-2.397-4 0-.8 0-1.6.799-3.2.8-1.6.8-2.4 1.598-4l17.583-44.8c.8-1.6.8-3.2 1.599-4.8.799-1.6 1.598-3.2 2.397-4 .8-.8 1.599-2.4 3.197-3.2 1.599-.8 3.197-.8 4.796-.8 1.598 0 3.196 0 4.795.8 1.598.8 2.398 1.6 3.197 3.2.799.8 1.598 2.4 2.397 4 .8 1.6 1.599 3.2 2.398 5.6l17.583 44c1.598 3.2 2.398 5.6 2.398 7.2-.8.8-1.599 2.4-2.398 4zM116.711 72c-8.791-3.2-15.185-7.2-20.78-12-5.594 5.6-12.787 9.6-21.579 12l-2.397-4c8.791-2.4 15.984-5.6 21.579-11.2C87.939 51.2 83.144 44 81.545 36h-7.992v-3.2h21.58c-1.6-2.4-3.198-5.6-4.796-8l2.397-.8c1.599 2.4 3.997 5.6 5.595 8.8h19.98v4h-7.992c-2.397 8-6.393 15.2-11.189 20 5.595 4.8 11.988 8.8 20.78 11.2l-3.197 4z"/></svg> \ No newline at end of file diff --git a/src/assets/icons/svg/link.svg b/src/assets/icons/svg/link.svg new file mode 100644 index 0000000..48197ba --- /dev/null +++ b/src/assets/icons/svg/link.svg @@ -0,0 +1 @@ +<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M115.625 127.937H.063V12.375h57.781v12.374H12.438v90.813h90.813V70.156h12.374z"/><path d="M116.426 2.821l8.753 8.753-56.734 56.734-8.753-8.745z"/><path d="M127.893 37.982h-12.375V12.375H88.706V0h39.187z"/></svg> \ No newline at end of file diff --git a/src/assets/icons/svg/list.svg b/src/assets/icons/svg/list.svg new file mode 100644 index 0000000..20259ed --- /dev/null +++ b/src/assets/icons/svg/list.svg @@ -0,0 +1 @@ +<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M1.585 12.087c0 6.616 3.974 11.98 8.877 11.98 4.902 0 8.877-5.364 8.877-11.98 0-6.616-3.975-11.98-8.877-11.98-4.903 0-8.877 5.364-8.877 11.98zM125.86.107H35.613c-1.268 0-2.114 1.426-2.114 2.852v18.255c0 1.712 1.057 2.853 2.114 2.853h90.247c1.268 0 2.114-1.426 2.114-2.853V2.96c0-1.711-1.057-2.852-2.114-2.852zM.106 62.86c0 6.615 3.974 11.979 8.876 11.979 4.903 0 8.877-5.364 8.877-11.98 0-6.616-3.974-11.98-8.877-11.98-4.902 0-8.876 5.364-8.876 11.98zM124.17 50.88H33.921c-1.268 0-2.114 1.425-2.114 2.851v18.256c0 1.711 1.057 2.852 2.114 2.852h90.247c1.268 0 2.114-1.426 2.114-2.852V53.73c0-1.426-.846-2.852-2.114-2.852zM.106 115.913c0 6.616 3.974 11.98 8.876 11.98 4.903 0 8.877-5.364 8.877-11.98 0-6.616-3.974-11.98-8.877-11.98-4.902 0-8.876 5.364-8.876 11.98zm124.064-11.98H33.921c-1.268 0-2.114 1.426-2.114 2.853v18.255c0 1.711 1.057 2.852 2.114 2.852h90.247c1.268 0 2.114-1.426 2.114-2.852v-18.255c0-1.427-.846-2.853-2.114-2.853z"/></svg> \ No newline at end of file diff --git a/src/assets/icons/svg/lock.svg b/src/assets/icons/svg/lock.svg new file mode 100644 index 0000000..74fee54 --- /dev/null +++ b/src/assets/icons/svg/lock.svg @@ -0,0 +1 @@ +<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M119.88 49.674h-7.987V39.52C111.893 17.738 90.45.08 63.996.08 37.543.08 16.1 17.738 16.1 39.52v10.154H8.113c-4.408 0-7.987 2.94-7.987 6.577v65.13c0 3.637 3.57 6.577 7.987 6.577H119.88c4.407 0 7.987-2.94 7.987-6.577v-65.13c-.008-3.636-3.58-6.577-7.987-6.577zm-23.953 0H32.065V39.52c0-14.524 14.301-26.295 31.931-26.295 17.63 0 31.932 11.777 31.932 26.295v10.153z"/></svg> \ No newline at end of file diff --git a/src/assets/icons/svg/log.svg b/src/assets/icons/svg/log.svg new file mode 100644 index 0000000..d879d33 --- /dev/null +++ b/src/assets/icons/svg/log.svg @@ -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="1566035943711" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4805" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M208.736 566.336H64.384v59.328h144.352v-59.328z m0-336.096H165.44V74.592c0-7.968 4.896-14.848 10.464-14.848h502.016V0.448H175.936c-38.72 1.248-69.248 34.368-68.192 74.144v155.648H64.384V289.6h144.352V230.24z m0 168.096H64.384v59.328h144.352v-59.328z m714.656 76.576h-57.76v474.496c0 7.936-4.896 14.848-10.464 14.848H175.936c-5.568 0-10.464-6.912-10.464-14.848v-155.68h43.296v-59.296H64.384v59.296h43.328v155.68c-1.024 39.776 29.472 72.896 68.192 74.144h679.232c38.72-1.184 69.248-34.368 68.256-74.144V474.912z m14.944-290.336l-83.072-85.312a71.264 71.264 0 0 0-52.544-21.728 71.52 71.52 0 0 0-51.616 23.872L386.528 507.264a30.496 30.496 0 0 0-6.176 10.72L308.16 740.512a30.016 30.016 0 0 0 6.976 30.24c7.712 7.968 19.2 10.752 29.568 7.2l216.544-74.112a28.736 28.736 0 0 0 12.128-7.936L940.448 287.456a75.552 75.552 0 0 0-2.112-102.88z m-557.12 518.272l39.104-120.64 78.336 80.416-117.44 40.224z m170.048-70.016l-103.552-106.016 200.16-222.4 103.52 106.304-200.128 222.112zM897.952 247.072l-0.256 0.224-107.136 119.168-103.52-106.528 106.432-118.624a14.144 14.144 0 0 1 10.304-4.736 13.44 13.44 0 0 1 10.464 4.288l83.264 85.696c5.472 5.6 5.664 14.72 0.448 20.512z" p-id="4806"></path></svg> \ No newline at end of file diff --git a/src/assets/icons/svg/logininfor.svg b/src/assets/icons/svg/logininfor.svg new file mode 100644 index 0000000..267f844 --- /dev/null +++ b/src/assets/icons/svg/logininfor.svg @@ -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="1566036016814" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="5261" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M896 128h-85.333333a42.666667 42.666667 0 0 0 0 85.333333h42.666666v640H170.666667V213.333333h42.666666a42.666667 42.666667 0 0 0 0-85.333333H128a42.666667 42.666667 0 0 0-42.666667 42.666667v725.333333a42.666667 42.666667 0 0 0 42.666667 42.666667h768a42.666667 42.666667 0 0 0 42.666667-42.666667V170.666667a42.666667 42.666667 0 0 0-42.666667-42.666667z" p-id="5262"></path><path d="M341.333333 298.666667a42.666667 42.666667 0 0 0 42.666667-42.666667V128a42.666667 42.666667 0 0 0-85.333333 0v128a42.666667 42.666667 0 0 0 42.666666 42.666667zM512 298.666667a42.666667 42.666667 0 0 0 42.666667-42.666667V128a42.666667 42.666667 0 0 0-85.333334 0v128a42.666667 42.666667 0 0 0 42.666667 42.666667zM682.666667 298.666667a42.666667 42.666667 0 0 0 42.666666-42.666667V128a42.666667 42.666667 0 0 0-85.333333 0v128a42.666667 42.666667 0 0 0 42.666667 42.666667zM341.333333 768a42.666667 42.666667 0 0 0 42.666667-42.666667 128 128 0 0 1 256 0 42.666667 42.666667 0 0 0 85.333333 0 213.333333 213.333333 0 0 0-107.52-184.32A128 128 0 0 0 640 469.333333a128 128 0 0 0-256 0 128 128 0 0 0 22.186667 71.68A213.333333 213.333333 0 0 0 298.666667 725.333333a42.666667 42.666667 0 0 0 42.666666 42.666667z m128-298.666667a42.666667 42.666667 0 1 1 42.666667 42.666667 42.666667 42.666667 0 0 1-42.666667-42.666667z" p-id="5263"></path></svg> \ No newline at end of file diff --git a/src/assets/icons/svg/message.svg b/src/assets/icons/svg/message.svg new file mode 100644 index 0000000..14ca817 --- /dev/null +++ b/src/assets/icons/svg/message.svg @@ -0,0 +1 @@ +<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M0 20.967v59.59c0 11.59 8.537 20.966 19.075 20.966h28.613l1 26.477L76.8 101.523h32.125c10.538 0 19.075-9.377 19.075-20.966v-59.59C128 9.377 119.463 0 108.925 0h-89.85C8.538 0 0 9.377 0 20.967zm82.325 33.1c0-5.524 4.013-9.935 9.037-9.935 5.026 0 9.038 4.41 9.038 9.934 0 5.524-4.025 9.934-9.038 9.934-5.024 0-9.037-4.41-9.037-9.934zm-27.613 0c0-5.524 4.013-9.935 9.038-9.935s9.037 4.41 9.037 9.934c0 5.524-4.025 9.934-9.037 9.934-5.025 0-9.038-4.41-9.038-9.934zm-27.1 0c0-5.524 4.013-9.935 9.038-9.935s9.038 4.41 9.038 9.934c0 5.524-4.026 9.934-9.05 9.934-5.013 0-9.025-4.41-9.025-9.934z"/></svg> \ No newline at end of file diff --git a/src/assets/icons/svg/money.svg b/src/assets/icons/svg/money.svg new file mode 100644 index 0000000..c1580de --- /dev/null +++ b/src/assets/icons/svg/money.svg @@ -0,0 +1 @@ +<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M54.122 127.892v-28.68H7.513V87.274h46.609v-12.4H7.513v-12.86h38.003L.099 0h22.6l32.556 45.07c3.617 5.144 6.44 9.611 8.487 13.385 1.788-3.05 4.89-7.779 9.301-14.186L103.93 0h24.01L82.385 62.013h38.34v12.862h-46.41v12.4h46.41v11.937h-46.41v28.68H54.123z"/></svg> \ No newline at end of file diff --git a/src/assets/icons/svg/monitor.svg b/src/assets/icons/svg/monitor.svg new file mode 100644 index 0000000..bc308cb --- /dev/null +++ b/src/assets/icons/svg/monitor.svg @@ -0,0 +1,2 @@ +<?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="1543827393750" class="icon" style="" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4695" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css">@font-face { font-family: rbicon; src: url("chrome-extension://dipiagiiohfljcicegpgffpbnjmgjcnf/fonts/rbicon.woff2") format("woff2"); font-weight: normal; font-style: normal; } +</style></defs><path d="M64 64V640H896V64H64zM0 0h960v704H0V0z" p-id="4696"></path><path d="M192 896H768v64H192zM448 640H512v256h-64z" p-id="4697"></path><path d="M479.232 561.604267l309.9904-348.330667-47.803733-42.5472-259.566934 291.669333L303.957333 240.008533 163.208533 438.6048l52.224 37.009067 91.6224-129.28z" p-id="4698"></path></svg> \ No newline at end of file diff --git a/src/assets/icons/svg/moon.svg b/src/assets/icons/svg/moon.svg new file mode 100644 index 0000000..ec72d77 --- /dev/null +++ b/src/assets/icons/svg/moon.svg @@ -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> \ No newline at end of file diff --git a/src/assets/icons/svg/more-up.svg b/src/assets/icons/svg/more-up.svg new file mode 100644 index 0000000..d30ac11 --- /dev/null +++ b/src/assets/icons/svg/more-up.svg @@ -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> \ No newline at end of file diff --git a/src/assets/icons/svg/nested.svg b/src/assets/icons/svg/nested.svg new file mode 100644 index 0000000..06713a8 --- /dev/null +++ b/src/assets/icons/svg/nested.svg @@ -0,0 +1 @@ +<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M.002 9.2c0 5.044 3.58 9.133 7.998 9.133 4.417 0 7.997-4.089 7.997-9.133 0-5.043-3.58-9.132-7.997-9.132S.002 4.157.002 9.2zM31.997.066h95.981V18.33H31.997V.066zm0 45.669c0 5.044 3.58 9.132 7.998 9.132 4.417 0 7.997-4.088 7.997-9.132 0-3.263-1.524-6.278-3.998-7.91-2.475-1.63-5.524-1.63-7.998 0-2.475 1.632-4 4.647-4 7.91zM63.992 36.6h63.986v18.265H63.992V36.6zm-31.995 82.2c0 5.043 3.58 9.132 7.998 9.132 4.417 0 7.997-4.089 7.997-9.132 0-5.044-3.58-9.133-7.997-9.133s-7.998 4.089-7.998 9.133zm31.995-9.131h63.986v18.265H63.992V109.67zm0-27.404c0 5.044 3.58 9.133 7.998 9.133 4.417 0 7.997-4.089 7.997-9.133 0-3.263-1.524-6.277-3.998-7.909-2.475-1.631-5.524-1.631-7.998 0-2.475 1.632-4 4.646-4 7.91zm31.995-9.13h31.991V91.4H95.987V73.135z"/></svg> \ No newline at end of file diff --git a/src/assets/icons/svg/number.svg b/src/assets/icons/svg/number.svg new file mode 100644 index 0000000..ad5ce9a --- /dev/null +++ b/src/assets/icons/svg/number.svg @@ -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="1575802851180" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2867" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M279.272727 791.272727h512a46.545455 46.545455 0 0 1 0 93.090909H279.272727a46.545455 46.545455 0 0 1 0-93.090909z m33.838546-617.984V651.636364H193.722182V395.170909c0-37.003636-0.884364-59.298909-2.653091-66.746182a24.948364 24.948364 0 0 0-14.615273-16.989091c-8.005818-3.863273-25.786182-5.771636-53.341091-5.771636h-11.822545v-55.854545c57.716364-12.381091 101.562182-37.888 131.490909-76.520728h70.283636z m303.709091 396.8V651.636364H354.164364v-68.235637c77.777455-127.255273 124.043636-206.010182 138.705454-236.218182 14.661818-30.254545 22.016-53.853091 22.016-70.74909 0-13.032727-2.234182-22.714182-6.656-29.137455-4.421818-6.376727-11.170909-9.588364-20.247273-9.588364a22.248727 22.248727 0 0 0-20.200727 10.612364c-4.468364 7.121455-6.656 21.178182-6.656 42.263273v45.521454H354.164364v-17.454545c0-26.763636 1.396364-47.941818 4.142545-63.348364 2.746182-15.499636 9.541818-30.72 20.386909-45.661091 10.798545-14.987636 24.901818-26.298182 42.216727-33.978182 17.361455-7.68 38.167273-11.543273 62.37091-11.543272 47.476364 0 83.316364 11.776 107.706181 35.328 24.296727 23.552 36.445091 53.341091 36.445091 89.367272 0 27.368727-6.842182 56.32-20.48 86.853819-13.730909 30.533818-54.039273 95.325091-121.018182 194.420363h130.885819z m270.615272-189.393454c18.152727 6.097455 31.650909 16.104727 40.494546 29.975272 8.843636 13.917091 13.312 46.452364 13.312 97.652364 0 38.027636-4.328727 67.490909-13.032727 88.529455-8.657455 20.945455-23.598545 36.910545-44.869819 47.848727-21.271273 10.938182-48.593455 16.384-81.873454 16.384-37.794909 0-67.490909-6.330182-89.088-19.083636-21.550545-12.660364-35.746909-28.253091-42.542546-46.638546-6.795636-18.432-10.193455-50.362182-10.193454-95.883636v-37.841455h119.389091v77.730909c0 20.666182 1.210182 33.838545 3.723636 39.424 2.420364 5.585455 7.912727 8.424727 16.337455 8.424728 9.309091 0 15.36-3.537455 18.338909-10.612364 2.932364-7.121455 4.421818-25.6 4.421818-55.575273v-33.047273c0-18.338909-2.048-31.744-6.190546-40.215272a30.72 30.72 0 0 0-18.338909-16.709818c-8.052364-2.653091-23.738182-4.189091-46.964363-4.561455V357.050182c28.392727 0 45.893818-1.070545 52.596363-3.258182a22.946909 22.946909 0 0 0 14.475637-14.149818c2.932364-7.307636 4.421818-18.711273 4.421818-34.257455v-26.624c0-16.756364-1.722182-27.741091-5.12-33.047272-3.490909-5.352727-8.843636-8.005818-16.151273-8.005819-8.285091 0-13.963636 2.792727-16.989091 8.378182-3.025455 5.632-4.561455 17.640727-4.561454 35.933091v39.284364h-119.389091v-40.773818c0-45.661091 10.472727-76.567273 31.325091-92.625455 20.898909-16.058182 54.085818-24.064 99.607272-24.064 56.878545 0 95.511273 11.170909 115.805091 33.373091 20.293818 22.248727 30.394182 53.201455 30.394182 92.765091 0 26.810182-3.630545 46.173091-10.891636 58.088727-7.307636 11.915636-20.107636 22.807273-38.446546 32.628364z" p-id="2868"></path></svg> \ No newline at end of file diff --git a/src/assets/icons/svg/online.svg b/src/assets/icons/svg/online.svg new file mode 100644 index 0000000..330a202 --- /dev/null +++ b/src/assets/icons/svg/online.svg @@ -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="1568899557259" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="535" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M356.246145 681.56286c-68.156286-41.949414-107.246583-103.84102-107.246583-169.805384 0-65.966411 39.090297-127.860063 107.246583-169.809477 12.046361-7.414877 15.800871-23.190165 8.385994-35.236526-7.413853-12.046361-23.191188-15.801894-35.236526-8.387018-39.640836 24.399713-72.539106 56.044434-95.137801 91.515297-23.86657 37.461193-36.481889 79.620385-36.481889 121.917724 0 42.297338 12.615319 84.454484 36.481889 121.914654 22.598694 35.469839 55.496965 67.11456 95.137801 91.51325 4.185322 2.576685 8.821923 3.804652 13.400195 3.804652 8.598842 0 16.998139-4.329609 21.836331-12.190647C372.047016 704.752002 368.291482 688.976714 356.246145 681.56286zM263.943926 754.580874c-92.603071-61.111846-145.713686-149.623739-145.713686-242.840794 0-93.195565 53.094242-181.682899 145.667637-242.774279 11.805884-7.79043 15.061021-23.677259 7.269567-35.483142-7.79043-11.805884-23.677259-15.062044-35.483142-7.269567C128.487861 296.954249 67.006602 401.024489 67.006602 511.74008c0 110.73708 61.496609 214.830857 168.721703 285.593504 4.343935 2.867304 9.240455 4.238534 14.08274 4.238534 8.317433 0 16.476253-4.046153 21.400403-11.507078C279.003923 778.258133 275.748786 762.372328 263.943926 754.580874zM788.660552 226.213092c-11.80486-7.791453-27.692712-4.536316-35.483142 7.269567-7.79043 11.805884-4.536316 27.692712 7.269567 35.483142 92.575442 61.092403 145.670707 149.579737 145.670707 242.774279 0 93.216032-53.111638 181.727924-145.715733 242.840794-11.805884 7.79043-15.059997 23.678282-7.269567 35.484166 4.925173 7.461949 13.081946 11.507078 21.400403 11.507078 4.841262 0 9.739828-1.37123 14.083763-4.238534 107.22714-70.761624 168.724773-174.857447 168.724773-285.593504C957.341323 401.025513 895.860063 296.955272 788.660552 226.213092zM790.090111 633.67213c23.865547-37.459147 36.480866-79.617315 36.480866-121.914654 0-42.298362-12.615319-84.45653-36.480866-121.917724-22.598694-35.470863-55.496965-67.115584-95.139847-91.515297-12.047384-7.413853-27.821649-3.659343-35.236526 8.387018-7.414877 12.045337-3.659343 27.821649 8.385994 35.236526 68.156286 41.949414 107.247606 103.842043 107.247606 169.809477 0 65.964364-39.090297 127.85597-107.247606 169.804361-12.045337 7.414877-15.800871 23.190165-8.385994 35.237549 4.838192 7.861038 13.236466 12.190647 21.835308 12.190647 4.579295 0 9.215896-1.227967 13.400195-3.804652C734.591099 700.786691 767.490394 669.142993 790.090111 633.67213zM567.129086 518.274914c24.12342-17.150612 39.887452-45.305859 39.887452-77.07133 0-52.128241-42.452881-94.538143-94.634334-94.538143-52.18043 0-94.633311 42.408879-94.633311 94.538143 0 31.695886 15.696494 59.797921 39.730886 76.958766-49.875944 21.128203-84.917018 70.234621-84.917018 127.301338 0 2.366907 0.061398 4.762467 0.182149 7.119141l1.249457 24.296359 276.373515 0 1.238201-24.308639c0.119727-2.358721 0.181125-4.750187 0.181125-7.106862C651.786185 588.497255 616.865861 539.465538 567.129086 518.274914zM512.381182 397.889079c23.937179 0 43.411719 19.430538 43.411719 43.314505 0 23.882943-19.47454 43.313481-43.411719 43.313481-23.936155 0-43.409672-19.430538-43.409672-43.313481C468.971509 417.320641 488.445026 397.889079 512.381182 397.889079zM426.08884 625.656573c9.119705-38.542828 44.254923-67.337641 86.085634-67.337641s76.966952 28.794813 86.085634 67.337641L426.08884 625.656573z" p-id="536"></path></svg> \ No newline at end of file diff --git a/src/assets/icons/svg/password.svg b/src/assets/icons/svg/password.svg new file mode 100644 index 0000000..6c64def --- /dev/null +++ b/src/assets/icons/svg/password.svg @@ -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="1575802846045" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2750" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M868.593046 403.832442c-30.081109-28.844955-70.037123-44.753273-112.624057-44.753273L265.949606 359.079168c-42.554188 0-82.510202 15.908318-112.469538 44.690852-30.236652 28.782533-46.857191 67.222007-46.857191 108.198258l0 294.079782c0 40.977273 16.619516 79.414701 46.702672 108.136859 29.959336 28.844955 70.069869 44.814672 112.624057 44.814672l490.019383 0c42.585911 0 82.696444-15.969717 112.624057-44.814672 30.082132-28.844955 46.579875-67.222007 46.579875-108.136859L915.172921 511.968278C915.171897 471.053426 898.675178 432.677397 868.593046 403.832442zM841.821309 806.049083c0 22.098297-8.882298 42.772152-25.099654 58.306964-16.154935 15.661701-37.81935 24.203238-60.752666 24.203238L265.949606 888.559285c-22.934339 0-44.567032-8.54256-60.877509-24.264637-16.186657-15.474436-25.067932-36.148291-25.067932-58.246589L180.004165 511.968278c0-22.035876 8.881274-42.772152 25.192775-58.307987 16.186657-15.536858 37.81935-24.139793 60.753689-24.139793l490.019383 0c22.933315 0 44.597731 8.602935 60.752666 24.139793 16.21838 15.535835 25.099654 36.272112 25.099654 58.307987L841.822332 806.049083zM510.974136 135.440715c114.914216 0 208.318536 89.75214 208.318536 200.055338l73.350588 0c0-149.113109-126.366036-270.496667-281.669124-270.496667-155.333788 0-281.699824 121.383558-281.699824 270.496667l73.350588 0C302.623877 225.193879 396.059919 135.440715 510.974136 135.440715zM474.299865 747.244792l73.350588 0L547.650453 629.576859l-73.350588 0L474.299865 747.244792z" p-id="2751"></path></svg> \ No newline at end of file diff --git a/src/assets/icons/svg/pdf.svg b/src/assets/icons/svg/pdf.svg new file mode 100644 index 0000000..957aa0c --- /dev/null +++ b/src/assets/icons/svg/pdf.svg @@ -0,0 +1 @@ +<svg viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg" width="128" height="128"><path d="M869.073 277.307H657.111V65.344l211.962 211.963zm-238.232 26.27V65.344l-476.498-.054v416.957h714.73v-178.67H630.841zm-335.836 360.57c-5.07-3.064-10.944-5.133-17.61-6.201-6.67-1.064-13.603-1.6-20.81-1.6h-48.821v85.641h48.822c7.206 0 14.14-.532 20.81-1.6 6.665-1.065 12.54-3.133 17.609-6.202 5.064-3.063 9.134-7.406 12.208-13.007 3.065-5.602 4.6-12.937 4.6-22.011 0-9.07-1.535-16.408-4.6-22.01-3.074-5.603-7.144-9.94-12.208-13.01zM35.82 541.805v416.904h952.358V541.805H35.821zm331.421 191.179c-3.6 11.071-9.343 20.879-17.209 29.413-7.874 8.542-18.078 15.408-30.617 20.61-12.544 5.206-27.747 7.807-45.621 7.807h-66.036v102.45h-62.831V607.517h128.867c17.874 0 33.077 2.6 45.62 7.802 12.541 5.207 22.745 12.076 30.618 20.615 7.866 8.538 13.604 18.277 17.21 29.212 3.6 10.943 5.401 22.278 5.401 34.018 0 11.477-1.8 22.752-5.402 33.819zM644.9 806.417c-5.343 17.61-13.408 32.818-24.212 45.627-10.807 12.803-24.283 22.879-40.423 30.213-16.146 7.343-35.155 11.007-57.03 11.007h-123.26V607.518h123.26c18.41 0 35.552 2.941 51.428 8.808 15.873 5.869 29.618 14.671 41.22 26.412 11.608 11.744 20.674 26.411 27.217 44.02 6.535 17.61 9.803 38.288 9.803 62.035 0 20.81-2.67 40.02-8.003 57.624zm245.362-146.07h-138.07v66.03h119.66v48.829h-119.66v118.058h-62.83V607.518h200.9v52.829h-.001zm-318.2 25.611c-6.402-8.266-14.877-14.604-25.412-19.01-10.544-4.402-23.551-6.602-39.019-6.602h-44.825v180.088h56.029c9.07 0 17.872-1.463 26.415-4.401 8.535-2.932 16.14-7.802 22.812-14.609 6.665-6.8 12.007-15.667 16.007-26.61 4.003-10.94 6.003-24.275 6.003-40.021 0-14.408-1.4-27.416-4.202-39.019-2.8-11.607-7.406-21.542-13.808-29.816zm0 0"/></svg> \ No newline at end of file diff --git a/src/assets/icons/svg/people.svg b/src/assets/icons/svg/people.svg new file mode 100644 index 0000000..2bd54ae --- /dev/null +++ b/src/assets/icons/svg/people.svg @@ -0,0 +1 @@ +<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M104.185 95.254c8.161 7.574 13.145 17.441 13.145 28.28 0 1.508-.098 2.998-.285 4.466h-10.784c.238-1.465.403-2.948.403-4.465 0-8.983-4.36-17.115-11.419-23.216C86 104.66 75.355 107.162 64 107.162c-11.344 0-21.98-2.495-31.22-6.83-7.064 6.099-11.444 14.218-11.444 23.203 0 1.517.165 3 .403 4.465H10.955a35.444 35.444 0 0 1-.285-4.465c0-10.838 4.974-20.713 13.127-28.291C9.294 85.42.003 70.417.003 53.58.003 23.99 28.656.001 64 .001s63.997 23.988 63.997 53.58c0 16.842-9.299 31.85-23.812 41.673zM64 36.867c-29.454 0-53.33-10.077-53.33 15.342 0 25.418 23.876 46.023 53.33 46.023 29.454 0 53.33-20.605 53.33-46.023 0-25.419-23.876-15.342-53.33-15.342zm24.888 25.644c-3.927 0-7.111-2.665-7.111-5.953 0-3.288 3.184-5.954 7.11-5.954 3.928 0 7.111 2.666 7.111 5.954s-3.183 5.953-7.11 5.953zm-3.556 16.372c0 4.11-9.55 7.442-21.332 7.442-11.781 0-21.332-3.332-21.332-7.442 0-1.06.656-2.064 1.8-2.976 3.295 2.626 10.79 4.465 19.532 4.465 8.743 0 16.237-1.84 19.531-4.465 1.145.912 1.801 1.916 1.801 2.976zm-46.22-16.372c-3.927 0-7.11-2.665-7.11-5.953 0-3.288 3.183-5.954 7.11-5.954 3.927 0 7.111 2.666 7.111 5.954s-3.184 5.953-7.11 5.953z"/></svg> \ No newline at end of file diff --git a/src/assets/icons/svg/peoples.svg b/src/assets/icons/svg/peoples.svg new file mode 100644 index 0000000..aab852e --- /dev/null +++ b/src/assets/icons/svg/peoples.svg @@ -0,0 +1 @@ +<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M95.648 118.762c0 5.035-3.563 9.121-7.979 9.121H7.98c-4.416 0-7.979-4.086-7.979-9.121C0 100.519 15.408 83.47 31.152 76.75c-9.099-6.43-15.216-17.863-15.216-30.987v-9.128c0-20.16 14.293-36.518 31.893-36.518s31.894 16.358 31.894 36.518v9.122c0 13.137-6.123 24.556-15.216 30.993 15.738 6.726 31.141 23.769 31.141 42.012z"/><path d="M106.032 118.252h15.867c3.376 0 6.101-3.125 6.101-6.972 0-13.957-11.787-26.984-23.819-32.123 6.955-4.919 11.638-13.66 11.638-23.704v-6.985c0-15.416-10.928-27.926-24.39-27.926-1.674 0-3.306.193-4.89.561 1.936 4.713 3.018 9.974 3.018 15.526v9.121c0 13.137-3.056 23.111-11.066 30.993 14.842 4.41 27.312 23.42 27.541 41.509z"/></svg> \ No newline at end of file diff --git a/src/assets/icons/svg/phone.svg b/src/assets/icons/svg/phone.svg new file mode 100644 index 0000000..ab8e8c4 --- /dev/null +++ b/src/assets/icons/svg/phone.svg @@ -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="1567417214476" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2266" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M761.503029 2.90619 242.121921 2.90619c-32.405037 0-58.932204 26.060539-58.932204 58.527998l0 902.302287c0 32.156374 26.217105 58.216913 58.932204 58.216913l519.381108 0c32.344662 0 58.591443-26.060539 58.591443-58.216913L820.094472 61.123103C820.094472 28.966729 793.847691 2.90619 761.503029 2.90619M452.878996 61.123103l98.147344 0c6.780427 0 12.31549 5.536087 12.31549 12.253068 0 6.748704-5.535063 12.253068-12.31549 12.253068l-98.147344 0c-6.779404 0-12.345166-5.504364-12.345166-12.253068C440.532807 66.659189 446.099592 61.123103 452.878996 61.123103M501.641583 980.593398c-29.636994 0-53.987588-23.946388-53.987588-53.677527 0-29.356608 24.039509-53.614082 53.987588-53.614082 29.91738 0 53.987588 23.883967 53.987588 53.614082C555.629171 956.647009 531.559986 980.593398 501.641583 980.593398M766.35657 803.142893c0 16.23373-13.186324 29.107945-29.233811 29.107945l-470.618521 0c-16.35755 0-29.325909-13.186324-29.325909-29.107945L237.178329 163.500794c0-16.232706 13.279445-29.138644 29.325909-29.138644l470.246037 0c16.420995 0 29.357632 13.1853 29.357632 29.138644l0 639.642099L766.35657 803.142893zM766.35657 803.142893" p-id="2267"></path></svg> \ No newline at end of file diff --git a/src/assets/icons/svg/post.svg b/src/assets/icons/svg/post.svg new file mode 100644 index 0000000..2922c61 --- /dev/null +++ b/src/assets/icons/svg/post.svg @@ -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="1566035724641" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3998" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M136.4 434.3h77.7c21.5 0 38.9-17.4 38.9-38.9s-17.4-38.9-38.9-38.9h-77.7c-21.5 0-38.9 17.4-38.9 38.9s17.4 38.9 38.9 38.9zM252.9 628.6c0-21.5-17.4-38.9-38.9-38.9h-77.7c-21.5 0-38.9 17.4-38.9 38.9s17.4 38.9 38.9 38.9H214c21.5-0.1 38.9-17.5 38.9-38.9z" p-id="3999"></path><path d="M874.7 97.5H227c-28.6 0-51.8 23.2-51.8 51.8v194.3h38.9c28.6 0 51.8 23.2 51.8 51.8 0 28.6-23.2 51.8-51.8 51.8h-38.9v129.5h38.9c28.6 0 51.8 23.2 51.8 51.8 0 28.6-23.2 51.8-51.8 51.8h-38.9v194.3c0 28.6 23.2 51.8 51.8 51.8h647.7c28.6 0 51.8-23.2 51.8-51.8V149.3c0-28.6-23.2-51.8-51.8-51.8z m-311.3 723c-15.6 0-146.7-71.6-146.7-91 0-19.4 102-368.6 102-368.6l-83.6-104s-12.3-23.1 24.6-23.1h208.9c36.9 0 18.4 23.1 18.4 23.1l-79 104s102 351.3 102 368.6c0.1 17.3-131 91-146.6 91z m169.2-253.6l-27.9 40.2-74.5-240 103.4 171.7c4.6 7.9 4.2 20.6-1 28.1z" p-id="4000"></path></svg> \ No newline at end of file diff --git a/src/assets/icons/svg/qq.svg b/src/assets/icons/svg/qq.svg new file mode 100644 index 0000000..ee13d4e --- /dev/null +++ b/src/assets/icons/svg/qq.svg @@ -0,0 +1 @@ +<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M18.448 57.545l-.244-.744-.198-.968-.132-.53v-2.181l.236-.859.24-.908.317-.953.428-1.06.561-1.103.794-1.104v-.773l.077-.724.123-.984.34-1.106.313-1.194.25-.548.289-.511.371-.569.405-.423v-2.73l.234-1.407.236-1.633.42-1.955.577-2.035.43-1.118.426-1.217.468-1.135.559-1.216.57-1.332.655-1.247.737-1.331.929-1.33.43-.762.457-.624.995-1.406 1.025-1.403 1.163-1.444 1.246-1.405 1.352-1.384 1.41-1.423 1.708-1.536 1.083-.934 1.322-1.008 1.34-.89 1.448-.855 1.392-.76 1.57-.63 1.667-.775 1.657-.532 1.653-.552 1.787-.548 1.785-.417 1.876-.347L59.128.68l1.879-.245 1.876-.252 2.002-.106h5.912l1.97.243 1.981.231 2.019.207 1.874.441 1.979.413 1.857.475 2.035.53 1.862.646 1.782.738 1.904.78 1.736.853 1.689.95 1.655 1.044 1.425.971.662.548.693.401 1.323 1.1 1.115 1.064 1.112 1.1 1.083 1.214.894 1.178 1.064 1.217.74 1.306.752 1.162.798 1.352.661 1.175 1.113 2.489.546 1.286.428 1.192.428 1.294.384 1.217.267 1.047.347 1.231.607 2.198.388 1.924.253 1.861.217 1.497.342 2.28.077.362.274.41.737 1.18.473.8.42.832.534.892.472 1.07.307 1.093.334 1.2.252 1.232.115.605.106.746v.648l-.106.643v.8l-.192.774-.35 1.5-.403.76-.299.852v.213l.142.264.4.623 1.746 2.53 1.377 1.9.66 1.267.889 1.389.774 1.52.893 1.627.894 1.828 1.006 2.069.567 1.268.518 1.239.447 1.307.44 1.175.336 1.235.342 1.16.432 2.261.343 2.31.235 2.05v2.891l-.158 1.025-.226 1.768-.308 1.59-.48 1.44-.18.588-.336.707-.28.493-.375.607-.33.383-.42.494-.375.4-.401.34-.48.207-.432.207-.355.114h-.543l-.346-.114-.66-.32-.302-.212-.317-.223-.347-.304-.35-.342-.579-.63-.684-.89-.539-.917-.538-.734-.526-.855-.741-1.517-.833-1.579-.098-.055h-.138l-.338.247-.196.415-.326.516-.567 1.533-.856 2.182-1.096 2.626-.824 1.308-.864 1.366-1.027 1.536-1.09 1.503-.557.68-.676.743-1.555 1.497.136.135.21.214.777.446 3.235 1.524 1.41.779 1.347.756 1.332.953 1.187.982.574.443.432.511.445.593.367.643.198.533.242.64.105.554.115.647-.115.433v.44l-.105.454-.242.415-.092.325-.22.394-.587.784-.543.627-.42.47-.35.348-.893.638-1.01.556-1.077.532-1.155.511-1.287.495-.693.207-.608.167-1.496.342-1.545.325-1.552.323-1.689.27-1.74.072-1.785.21h-5.539l-1.998-.114-1.86-.168-2.005-.27-1.99-.209-2.095-.286-2.03-.495-1.981-.374-1.968-.552-2.019-.707-1.98-.585-1.044-.342-.927-.323-.586-.223-.582-.12h-1.647l-1.904-.131-.962-.096-1.24-.135-.795.705-1.085.665-1.471.701-1.628.875-.99.475-1.033.376-2.281.914-1.24.305-1.3.343-1.803.344-1.13.086-1.193.1-1.246.135-1.45.053h-5.926l-3.346-.053-3.25-.321-1.644-.23-1.589-.23-1.546-.227-1.547-.305-1.442-.456-1.434-.325-1.294-.51-1.223-.474-1.142-.533-.99-.583-.984-.71-.336-.343-.44-.415-.334-.362-.3-.417-.278-.415-.215-.42-.311-.89-.109-.46-.138-.51v-.473l.138-.533v-.53l.109-.53v-1.069l.052-.564.259-.647.215-.646.39-.779.286-.3.236-.348.615-.738.49-.38.464-.266.428-.338.676-.21.543-.324.676-.341.77-.227.775-.231.897-.192.85-.11 1.008-.13 1.093-.081.284-.092h.063l.137-.115v-.13l-.2-.266-.58-.27-1.45-1.231-.975-.761-1.127-.967-1.136-1.082-1.181-1.382-1.36-1.558-.508-.843-.672-.87-.58-1.007-.522-1.1-.704-1.047-.459-1.194-.547-1.192-.546-1.33-.397-1.273-.378-1.575-.112-.057h-.115l-.059-.113h-.14l-.23.113-.114.057-.158.264-.057.321-.119.286-.206.477-.664 1.157-.345.701-.546.612-.58.736-.641.816-.677.724-.795.701-.734.658-.814.524-.89.546-.855.325-1.008.247-.99.095h-.233l-.228-.095-.18-.384-.29-.188-.38-.912-.237-.493-.255-.707-.21-.734-.113-.724-.313-1.648-.12-.972v-3.185l.12-2.379.196-1.214.23-1.252.21-1.347.374-1.254.42-1.443.431-1.407.578-1.448.545-1.38.754-1.4.699-1.52.855-1.425 1.006-1.538 1.023-1.382 1.069-1.538.891-1.071 1.142-1.227 1.202-1.237.56-.59.678-.662.985-.836 1.012-.853 1.647-1.446 1.242-.889z"/></svg> \ No newline at end of file diff --git a/src/assets/icons/svg/question.svg b/src/assets/icons/svg/question.svg new file mode 100644 index 0000000..cf75bd4 --- /dev/null +++ b/src/assets/icons/svg/question.svg @@ -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="1581238842264" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1409" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M512 0C229.233778 0 0 229.233778 0 512s229.233778 512 512 512 512-229.233778 512-512A512 512 0 0 0 512 0z m0 938.666667C276.366222 938.666667 85.333333 747.633778 85.333333 512 85.333333 276.366222 276.366222 85.333333 512 85.333333c235.633778 0 426.666667 191.032889 426.666667 426.666667a426.666667 426.666667 0 0 1-426.666667 426.666667z m0-717.653334a170.666667 170.666667 0 0 0-170.666667 170.666667 42.666667 42.666667 0 0 0 85.333334 0 85.333333 85.333333 0 1 1 85.333333 85.333333 42.666667 42.666667 0 0 0-42.666667 42.666667v111.36a42.666667 42.666667 0 0 0 85.333334 0v-74.24A170.666667 170.666667 0 0 0 512 221.013333z m-42.666667 542.293334a42.666667 42.666667 0 1 0 85.333334 0 42.666667 42.666667 0 0 0-85.333334 0z" p-id="1410"></path></svg> \ No newline at end of file diff --git a/src/assets/icons/svg/radio.svg b/src/assets/icons/svg/radio.svg new file mode 100644 index 0000000..0cde345 --- /dev/null +++ b/src/assets/icons/svg/radio.svg @@ -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="1575966775973" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="879" xmlns:xlink="http://www.w3.org/1999/xlink" width="81" height="81"><defs><style type="text/css"></style></defs><path d="M507.39346659 71.84873358c241.53533667 0 437.39770766 195.85422109 437.39770767 437.37442191 0 241.53766571-195.86237099 437.38955776-437.39770767 437.38955776-241.50040803 0-437.34997219-195.85189205-437.34997219-437.38955776C70.0434944 267.70295467 265.89189347 71.84873358 507.39346659 71.84873358L507.39346659 71.84873358zM507.39346659 282.81899805c-125.00686734 0-226.37039389 101.38914133-226.37039388 226.41813048 0 125.01268821 101.36352768 226.39717262 226.37039388 226.39717262 125.04295993 0 226.42395136-101.38448441 226.42395136-226.39717262C733.81625401 384.20813938 632.43642653 282.81899805 507.39346659 282.81899805L507.39346659 282.81899805zM507.39346659 120.78172615c-214.46664192 0-388.42047261 173.95150279-388.4204726 388.44026539 0 214.51204949 173.95499463 388.46122325 388.4204726 388.46122325 214.52369237 0 388.46005817-173.94800981 388.46005818-388.46122325C895.85236082 294.73322894 721.91715897 120.78172615 507.39346659 120.78172615z" p-id="880"></path></svg> \ No newline at end of file diff --git a/src/assets/icons/svg/rate.svg b/src/assets/icons/svg/rate.svg new file mode 100644 index 0000000..aa3b14d --- /dev/null +++ b/src/assets/icons/svg/rate.svg @@ -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="1577246781606" class="icon" viewBox="0 0 1069 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1098" xmlns:xlink="http://www.w3.org/1999/xlink" width="84.5595703125" height="81"><defs><style type="text/css"></style></defs><path d="M633.72929961 378.02038203l9.49872568 18.68789795 20.78025469 2.79745225 206.61592412 27.33248408a11.46496817 11.46496817 0 0 1 6.6095543 19.47324902l-147.2675168 147.35350284-14.89299345 14.89299345 3.8006376 20.68280244 37.84585956 204.89044571a11.46496817 11.46496817 0 0 1-16.4808914 12.2961788L554.68980898 751.84713388l-18.68789794-9.49299345-18.48726123 9.99171915-183.23885392 99.34968163a11.46496817 11.46496817 0 0 1-16.78471347-11.8662416l32.5433127-205.79617881 3.29617793-20.78598692-15.19108243-14.49172002-151.03375839-143.48407587a11.46496817 11.46496817 0 0 1 6.09936328-19.63949062l205.79617881-32.63503185 20.78598691-3.2961788L428.87898125 380.72038203 518.59235674 192.64331182a11.46496817 11.46496817 0 0 1 20.56815264-0.26369385l94.56879023 185.63503183zM496.64840732 85.52038203l-121.75796162 254.98089229L95.76433145 384.76178369A34.3949045 34.3949045 0 0 0 77.46050938 443.66879023l204.87324901 194.66369385-44.16879023 279.1146498a34.3949045 34.3949045 0 0 0 50.36560489 35.61592325l248.4-134.67898038 251.84522285 128.27579591a34.3949045 34.3949045 0 0 0 49.43694287-36.89426777l-51.30573223-277.85350284 199.73120977-199.90891758a34.3949045 34.3949045 0 0 0-19.82866201-58.40827998l-280.11783428-37.03184736L558.32993633 84.71210205a34.3949045 34.3949045 0 0 0-61.68152901 0.80254775z" p-id="1099"></path></svg> \ No newline at end of file diff --git a/src/assets/icons/svg/redis-list.svg b/src/assets/icons/svg/redis-list.svg new file mode 100644 index 0000000..98a15b2 --- /dev/null +++ b/src/assets/icons/svg/redis-list.svg @@ -0,0 +1,2 @@ +<?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="1656035183065" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3395" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css">@font-face { font-family: feedback-iconfont; src: url("//at.alicdn.com/t/font_1031158_u69w8yhxdu.woff2?t=1630033759944") format("woff2"), url("//at.alicdn.com/t/font_1031158_u69w8yhxdu.woff?t=1630033759944") format("woff"), url("//at.alicdn.com/t/font_1031158_u69w8yhxdu.ttf?t=1630033759944") format("truetype"); } +</style></defs><path d="M958.88 730.06H65.12c-18.28 0-33.12-14.82-33.12-33.12V68.91c0-18.29 14.83-33.12 33.12-33.12h893.77c18.28 0 33.12 14.82 33.12 33.12v628.03c-0.01 18.3-14.84 33.12-33.13 33.12zM98.23 663.83h827.53v-561.8H98.23v561.8z" p-id="3396"></path><path d="M512 954.55c-18.28 0-33.12-14.82-33.12-33.12V733.92c0-18.29 14.83-33.12 33.12-33.12s33.12 14.82 33.12 33.12v187.51c0 18.3-14.84 33.12-33.12 33.12z" p-id="3397"></path><path d="M762.01 988.21H261.99c-18.28 0-33.12-14.82-33.12-33.12 0-18.29 14.83-33.12 33.12-33.12h500.03c18.28 0 33.12 14.82 33.12 33.12-0.01 18.29-14.84 33.12-33.13 33.12zM514.74 578.55c-21.63 0-43.31-3.87-64.21-11.65-45.95-17.13-82.49-51.13-102.86-95.74-5.07-11.08-0.19-24.19 10.89-29.26 11.08-5.09 24.19-0.18 29.26 10.91 15.5 33.88 43.25 59.7 78.14 72.71 34.93 12.99 72.79 11.64 106.66-3.85 33.22-15.17 58.8-42.26 72.03-76.3 4.42-11.37 17.21-17.01 28.57-12.58 11.36 4.42 16.99 17.22 12.57 28.58-17.42 44.82-51.1 80.5-94.82 100.47-24.34 11.12-50.25 16.71-76.23 16.71z" p-id="3398"></path><path d="M325.27 528.78c-1.66 0-3.34-0.18-5.02-0.57-11.88-2.77-19.28-14.63-16.49-26.51l18.84-81c1.34-5.82 5-10.84 10.13-13.92 5.09-3.09 11.3-3.96 17.03-2.41l80.51 21.43c11.79 3.14 18.8 15.23 15.67 27.02-3.15 11.79-15.42 18.75-27.02 15.65l-58.49-15.57-13.69 58.81c-2.37 10.2-11.45 17.07-21.47 17.07zM360.8 351.01c-2.65 0-5.37-0.49-8-1.51-11.36-4.41-16.99-17.21-12.59-28.57 17.4-44.79 51.06-80.47 94.8-100.48 92.15-42.06 201.25-1.39 243.31 90.68 5.07 11.08 0.19 24.19-10.89 29.26-11.13 5.07-24.19 0.17-29.26-10.91-31.97-69.91-114.9-100.82-184.79-68.86-33.22 15.19-58.8 42.28-71.99 76.29-3.41 8.74-11.75 14.1-20.59 14.1z" p-id="3399"></path><path d="M684.68 376.74c-1.47 0-2.95-0.15-4.42-0.44l-81.61-16.68c-11.94-2.45-19.64-14.11-17.21-26.06 2.44-11.96 14.1-19.64 26.04-17.22l59.29 12.12 10.23-59.5c2.05-12 13.52-20.19 25.48-18.01 12.03 2.06 20.09 13.48 18.02 25.5l-14.08 81.96a22.089 22.089 0 0 1-9.29 14.49c-3.7 2.51-8.03 3.84-12.45 3.84z" p-id="3400"></path></svg> \ No newline at end of file diff --git a/src/assets/icons/svg/redis.svg b/src/assets/icons/svg/redis.svg new file mode 100644 index 0000000..2f1d62d --- /dev/null +++ b/src/assets/icons/svg/redis.svg @@ -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="1605865043777" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="856" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M1023.786667 611.84c-0.426667 9.770667-13.354667 20.693333-39.893334 34.56-54.613333 28.458667-337.749333 144.896-397.994666 176.298667-60.288 31.402667-93.738667 31.104-141.354667 8.32-47.616-22.741333-348.842667-144.469333-403.114667-170.368-27.093333-12.970667-40.917333-23.893333-41.386666-34.218667v103.509333c0 10.325333 14.250667 21.290667 41.386666 34.261334 54.272 25.941333 355.541333 147.626667 403.114667 170.368 47.616 22.784 81.066667 23.082667 141.354667-8.362667 60.245333-31.402667 343.338667-147.797333 397.994666-176.298667 27.776-14.464 40.106667-25.728 40.106667-35.925333v-102.058667l-0.213333-0.085333z m0-168.746667c-0.512 9.770667-13.397333 20.650667-39.893334 34.517334-54.613333 28.458667-337.749333 144.896-397.994666 176.298666-60.288 31.402667-93.738667 31.104-141.354667 8.362667-47.616-22.741333-348.842667-144.469333-403.114667-170.410667-27.093333-12.928-40.917333-23.893333-41.386666-34.176v103.509334c0 10.325333 14.250667 21.248 41.386666 34.218666 54.272 25.941333 355.498667 147.626667 403.114667 170.368 47.616 22.784 81.066667 23.082667 141.354667-8.32 60.245333-31.402667 343.338667-147.84 397.994666-176.298666 27.776-14.506667 40.106667-25.770667 40.106667-35.968v-102.058667l-0.256-0.042667z m0-175.018666c0.469333-10.410667-13.141333-19.541333-40.533334-29.610667-53.248-19.498667-334.634667-131.498667-388.522666-151.253333-53.888-19.712-75.818667-18.901333-139.093334 3.84C392.234667 113.706667 92.629333 231.253333 39.338667 252.074667c-26.666667 10.496-39.68 20.181333-39.253334 30.506666V386.133333c0 10.325333 14.250667 21.248 41.386667 34.218667 54.272 25.941333 355.498667 147.669333 403.114667 170.410667 47.616 22.741333 81.066667 23.04 141.354666-8.362667 60.245333-31.402667 343.338667-147.84 397.994667-176.298667 27.776-14.506667 40.106667-25.770667 40.106667-35.968V268.074667h-0.341334zM366.677333 366.08l237.269334-36.437333-71.68 105.088-165.546667-68.650667z m524.8-94.634667l-140.330666 55.466667-15.232 5.973333-140.245334-55.466666 155.392-61.44 140.373334 55.466666z m-411.989333-101.674666l-22.954667-42.325334 71.594667 27.989334 67.498667-22.101334-18.261334 43.733334 68.778667 25.770666-88.704 9.216-19.882667 47.786667-32.085333-53.290667-102.4-9.216 76.416-27.562666z m-176.768 59.733333c70.058667 0 126.805333 21.973333 126.805333 49.109333s-56.746667 49.152-126.805333 49.152-126.848-22.058667-126.848-49.152c0-27.136 56.789333-49.152 126.848-49.152z" p-id="857"></path></svg> \ No newline at end of file diff --git a/src/assets/icons/svg/row.svg b/src/assets/icons/svg/row.svg new file mode 100644 index 0000000..0780992 --- /dev/null +++ b/src/assets/icons/svg/row.svg @@ -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="1579339929870" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1182" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M152 854.856875h325.7146875V237.715625H134.856875v600q0 6.99375 5.0746875 12.0684375T152 854.856875z m737.143125-17.1421875v-600H546.284375v617.1421875H872q6.99375 0 12.0684375-5.07375t5.0746875-12.0684375z m68.5715625-651.429375V837.715625q0 35.3821875-25.16625 60.5484375T872 923.4284375H152q-35.383125 0-60.5484375-25.1653125T66.284375 837.7146875V186.284375q0-35.3821875 25.16625-60.5484375T152 100.5715625h720q35.383125 0 60.5484375 25.1653125t25.16625 60.5484375z" p-id="1183"></path></svg> \ No newline at end of file diff --git a/src/assets/icons/svg/search.svg b/src/assets/icons/svg/search.svg new file mode 100644 index 0000000..84233dd --- /dev/null +++ b/src/assets/icons/svg/search.svg @@ -0,0 +1 @@ +<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M124.884 109.812L94.256 79.166c-.357-.357-.757-.629-1.129-.914a50.366 50.366 0 0 0 8.186-27.59C101.327 22.689 78.656 0 50.67 0 22.685 0 0 22.688 0 50.663c0 27.989 22.685 50.663 50.656 50.663 10.186 0 19.643-3.03 27.6-8.201.286.385.557.771.9 1.114l30.628 30.632a10.633 10.633 0 0 0 7.543 3.129c2.728 0 5.457-1.043 7.543-3.115 4.171-4.157 4.171-10.915.014-15.073M50.671 85.338C31.557 85.338 16 69.78 16 50.663c0-19.102 15.557-34.661 34.67-34.661 19.115 0 34.657 15.559 34.657 34.675 0 19.102-15.557 34.661-34.656 34.661"/></svg> \ No newline at end of file diff --git a/src/assets/icons/svg/select.svg b/src/assets/icons/svg/select.svg new file mode 100644 index 0000000..d628382 --- /dev/null +++ b/src/assets/icons/svg/select.svg @@ -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="1575803481213" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="804" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M62 511.97954521C62 263.86590869 263.90681826 62 511.97954521 62s449.97954521 201.825 449.97954521 449.97954521c0 248.19545479-201.90681826 449.97954521-449.97954521 449.97954521C263.90681826 962 62 760.175 62 511.97954521M901.98636348 511.97954521c0-215.24318174-175.00909131-390.41590869-390.00681827-390.41590869-215.03863652 0-389.96590869 175.17272695-389.96590868 390.41590869 0 215.28409131 175.00909131 390.45681826 389.96590868 390.45681826C727.01818174 902.47727305 901.98636348 727.30454521 901.98636348 511.97954521M264.17272695 430.28409131c0-5.76818174 2.12727305-11.51590869 6.64772696-15.87272696 8.71363652-8.75454521 22.88863652-8.75454521 31.725 0l209.4340913 208.22727305L721.45454521 414.53409131c8.75454521-8.71363652 22.97045479-8.71363652 31.90909132 0 8.71363652 8.75454521 8.71363652 22.88863652 0 31.60227304L511.97954521 685.74090869 270.71818174 446.01363653C266.27954521 441.77954521 264.17272695 436.05227305 264.17272695 430.28409131" p-id="805"></path></svg> \ No newline at end of file diff --git a/src/assets/icons/svg/server.svg b/src/assets/icons/svg/server.svg new file mode 100644 index 0000000..eb287e3 --- /dev/null +++ b/src/assets/icons/svg/server.svg @@ -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="1547360688278" class="icon" style="" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="6717" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M890 120H134a70 70 0 0 0-70 70v500a70 70 0 0 0 70 70h756a70 70 0 0 0 70-70V190a70 70 0 0 0-70-70z m-10 520a40 40 0 0 1-40 40H712V448a40 40 0 0 0-80 0v232h-80V368a40 40 0 0 0-80 0v312h-80V512a40 40 0 0 0-80 0v168H184a40 40 0 0 1-40-40V240a40 40 0 0 1 40-40h656a40 40 0 0 1 40 40zM696 824H328a40 40 0 0 0 0 80h368a40 40 0 0 0 0-80z" p-id="6718"></path></svg> \ No newline at end of file diff --git a/src/assets/icons/svg/shopping.svg b/src/assets/icons/svg/shopping.svg new file mode 100644 index 0000000..87513e7 --- /dev/null +++ b/src/assets/icons/svg/shopping.svg @@ -0,0 +1 @@ +<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M42.913 101.36c1.642 0 3.198.332 4.667.996a12.28 12.28 0 0 1 3.89 2.772c1.123 1.184 1.987 2.582 2.592 4.193.605 1.612.908 3.318.908 5.118 0 1.8-.303 3.507-.908 5.118-.605 1.611-1.469 3.01-2.593 4.194a13.3 13.3 0 0 1-3.889 2.843 10.582 10.582 0 0 1-4.667 1.066c-1.729 0-3.306-.355-4.732-1.066a13.604 13.604 0 0 1-3.825-2.843c-1.123-1.185-1.988-2.583-2.593-4.194a14.437 14.437 0 0 1-.907-5.118c0-1.8.302-3.506.907-5.118.605-1.61 1.47-3.009 2.593-4.193a12.515 12.515 0 0 1 3.825-2.772c1.426-.664 3.003-.996 4.732-.996zm53.932.285c1.643 0 3.22.331 4.733.995a11.386 11.386 0 0 1 3.889 2.772c1.08 1.185 1.945 2.583 2.593 4.194.648 1.61.972 3.317.972 5.118 0 1.8-.324 3.506-.972 5.117-.648 1.611-1.513 3.01-2.593 4.194a12.253 12.253 0 0 1-3.89 2.843 11 11 0 0 1-4.732 1.066 10.58 10.58 0 0 1-4.667-1.066 12.478 12.478 0 0 1-3.824-2.843c-1.08-1.185-1.945-2.583-2.593-4.194a13.581 13.581 0 0 1-.973-5.117c0-1.801.325-3.507.973-5.118.648-1.611 1.512-3.01 2.593-4.194a11.559 11.559 0 0 1 3.824-2.772 11.212 11.212 0 0 1 4.667-.995zm21.781-80.747c2.42 0 4.3.355 5.64 1.066 1.34.71 2.29 1.587 2.852 2.63a6.427 6.427 0 0 1 .778 3.34c-.044 1.185-.195 2.204-.454 3.057-.26.853-.8 2.606-1.62 5.26a589.268 589.268 0 0 1-2.788 8.743 1236.373 1236.373 0 0 0-3.047 9.453c-.994 3.128-1.75 5.592-2.269 7.393-1.123 3.79-2.55 6.42-4.278 7.89-1.728 1.469-3.846 2.203-6.352 2.203H39.023l1.945 12.795h65.342c4.148 0 6.223 1.943 6.223 5.828 0 1.896-.41 3.53-1.232 4.905-.821 1.374-2.442 2.061-4.862 2.061H38.505c-1.729 0-3.176-.426-4.343-1.28-1.167-.852-2.14-1.966-2.917-3.34a21.277 21.277 0 0 1-1.88-4.478 44.128 44.128 0 0 1-1.102-4.55c-.087-.568-.324-1.942-.713-4.122-.39-2.18-.865-4.904-1.426-8.174l-1.88-10.947c-.692-4.027-1.383-8.079-2.075-12.154-1.642-9.572-3.5-20.234-5.574-31.986H6.87c-1.296 0-2.377-.356-3.24-1.067a9.024 9.024 0 0 1-2.14-2.558 10.416 10.416 0 0 1-1.167-3.2C.108 8.53 0 7.488 0 6.54c0-1.896.583-3.46 1.75-4.69C2.917.615 4.494 0 6.482 0h13.095c1.728 0 3.111.284 4.148.853 1.037.569 1.858 1.28 2.463 2.132a8.548 8.548 0 0 1 1.297 2.701c.26.948.475 1.754.648 2.417.173.758.346 1.825.519 3.199.173 1.374.345 2.772.518 4.193.26 1.706.519 3.507.778 5.403h88.678z"/></svg> \ No newline at end of file diff --git a/src/assets/icons/svg/size.svg b/src/assets/icons/svg/size.svg new file mode 100644 index 0000000..ddb25b8 --- /dev/null +++ b/src/assets/icons/svg/size.svg @@ -0,0 +1 @@ +<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M0 54.857h54.796v18.286H36.531V128H18.265V73.143H0V54.857zm127.857-36.571H91.935V128H72.456V18.286H36.534V0h91.326l-.003 18.286z"/></svg> \ No newline at end of file diff --git a/src/assets/icons/svg/skill.svg b/src/assets/icons/svg/skill.svg new file mode 100644 index 0000000..a3b7312 --- /dev/null +++ b/src/assets/icons/svg/skill.svg @@ -0,0 +1 @@ +<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M31.652 93.206h33.401c1.44 2.418 3.077 4.663 4.93 6.692h-38.33v-6.692zm0-10.586h28.914a44.8 44.8 0 0 1-1.264-6.688h-27.65v6.688zm0-17.27H59.39c.288-2.286.714-4.532 1.34-6.687H31.65v6.687h.003zm53.913 44.84v5.85c0 2.798-2.095 5.075-4.667 5.075h-70.07c-2.576 0-4.663-2.277-4.663-5.075V31.26l23.22-20.96v22.25H17.16v6.688h18.39V6.688h45.348c2.576 0 4.667 2.277 4.667 5.066v20.009c1.987-.675 4.053-1.128 6.17-1.445v-18.56C91.738 5.28 86.874 0 80.902 0H31.15L0 28.118v87.917c0 6.48 4.859 11.759 10.832 11.759h70.07c5.974 0 10.837-5.27 10.837-11.759v-4.41c-2.117-.312-4.183-.765-6.17-1.435h-.004zM23.279 58.667h-7.96v6.688h7.96v-6.688zm-7.956 41.23h7.96v-6.691h-7.96v6.692zm7.956-23.96h-7.96v6.687h7.96v-6.688zm89.718-15.042l-4.896-4.07-12.447 17.613-11.19-9.305-3.762 5.311 16.091 13.38 16.204-22.929zM128 70.978c0-18.632-13.97-33.782-31.147-33.782-17.168 0-31.135 15.155-31.135 33.782 0 18.628 13.97 33.783 31.135 33.783 17.172 0 31.143-15.15 31.143-33.783H128zm-6.17 0c0 14.933-11.203 27.1-24.981 27.1-13.77 0-24.987-12.158-24.987-27.1 0-14.941 11.195-27.099 24.987-27.099 13.778 0 24.982 12.158 24.982 27.1z"/></svg> \ No newline at end of file diff --git a/src/assets/icons/svg/slider.svg b/src/assets/icons/svg/slider.svg new file mode 100644 index 0000000..fbe4f39 --- /dev/null +++ b/src/assets/icons/svg/slider.svg @@ -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="1577185310368" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1238" xmlns:xlink="http://www.w3.org/1999/xlink" width="81" height="81"><defs><style type="text/css"></style></defs><path d="M951.453125 476.84375H523.671875a131.8359375 131.8359375 0 0 0-254.1796875 0H72.546875v70.3125h196.9453125a131.8359375 131.8359375 0 0 0 254.1796875 0H951.453125z" p-id="1239"></path></svg> \ No newline at end of file diff --git a/src/assets/icons/svg/star.svg b/src/assets/icons/svg/star.svg new file mode 100644 index 0000000..6cf86e6 --- /dev/null +++ b/src/assets/icons/svg/star.svg @@ -0,0 +1 @@ +<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M70.66 4.328l14.01 29.693c1.088 2.29 3.177 3.882 5.603 4.25l31.347 4.76c6.087.926 8.528 8.756 4.117 13.247L103.05 79.395c-1.75 1.78-2.544 4.352-2.132 6.867l5.352 32.641c1.043 6.337-5.33 11.182-10.778 8.19l-28.039-15.409a7.13 7.13 0 0 0-6.91 0l-28.039 15.41c-5.448 2.99-11.821-1.854-10.777-8.19l5.352-32.642c.415-2.515-.387-5.088-2.136-6.867L2.264 56.278C-2.146 51.787.286 43.957 6.38 43.031l31.343-4.76c2.419-.368 4.51-1.96 5.595-4.25L57.334 4.328c2.728-5.77 10.605-5.77 13.325 0z"/></svg> \ No newline at end of file diff --git a/src/assets/icons/svg/sunny.svg b/src/assets/icons/svg/sunny.svg new file mode 100644 index 0000000..cc628bf --- /dev/null +++ b/src/assets/icons/svg/sunny.svg @@ -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> \ No newline at end of file diff --git a/src/assets/icons/svg/swagger.svg b/src/assets/icons/svg/swagger.svg new file mode 100644 index 0000000..05d4e7b --- /dev/null +++ b/src/assets/icons/svg/swagger.svg @@ -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="1566036776944" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="6463" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M64 223.995345h168.001164v47.997673c0 26.428509 18.878836 47.997673 41.984 47.997673h140.036654c23.095855 0 41.984-21.569164 41.984-47.997673v-47.997673h504.003491a32.004655 32.004655 0 0 0 0-64.009309H455.996509V111.988364c0-26.428509-18.878836-47.997673-41.984-47.997673H273.985164c-23.095855 0-41.984 21.569164-41.984 47.997673v47.997672H64a32.004655 32.004655 0 0 0 0 64.009309zM288.004655 128h111.997672V256H288.004655V128zM960 479.995345H791.998836v-47.997672c0-26.372655-18.878836-47.997673-41.984-47.997673H609.978182c-23.095855 0-41.984 21.634327-41.984 47.997673v47.997672H64a32.004655 32.004655 0 0 0 0 64.00931h504.003491v47.997672c0 26.363345 18.878836 47.997673 41.984 47.997673h140.036654c23.095855 0 41.984-21.634327 41.984-47.997673v-47.997672h168.001164a32.004655 32.004655 0 1 0-0.009309-64.00931zM735.995345 576H623.997673v-128h111.997672v128zM960 800.293236v-0.288581H455.996509v-47.997673c0-26.363345-18.878836-47.997673-41.984-47.997673H274.050327c-23.105164 0-41.984 21.634327-41.984 47.997673v47.997673H64v0.288581a32.004655 32.004655 0 0 0 0 64.009309c0.986764 0 1.917673-0.195491 2.885818-0.288581h165.115346v47.997672c0 26.363345 18.878836 47.997673 41.984 47.997673h140.036654c23.095855 0 41.984-21.634327 41.984-47.997673v-47.997672h501.108364c0.968145 0.093091 1.899055 0.288582 2.895127 0.288581a32.004655 32.004655 0 1 0-0.009309-64.009309zM400.002327 896H288.004655V768h111.997672v128z" fill="" p-id="6464"></path></svg> \ No newline at end of file diff --git a/src/assets/icons/svg/switch.svg b/src/assets/icons/svg/switch.svg new file mode 100644 index 0000000..0ba61e3 --- /dev/null +++ b/src/assets/icons/svg/switch.svg @@ -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="1576042673958" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1110" xmlns:xlink="http://www.w3.org/1999/xlink" width="81" height="81"><defs><style type="text/css"></style></defs><path d="M692 792H332c-150 0-270-120-270-270s120-270 270-270h360c150 0 270 120 270 270 0 147-120 270-270 270zM332 312c-117 0-210 93-210 210s93 210 210 210h360c117 0 210-93 210-210s-93-210-210-210H332z" p-id="1111"></path><path d="M341 522m-150 0a150 150 0 1 0 300 0 150 150 0 1 0-300 0Z" p-id="1112"></path></svg> \ No newline at end of file diff --git a/src/assets/icons/svg/system.svg b/src/assets/icons/svg/system.svg new file mode 100644 index 0000000..5992593 --- /dev/null +++ b/src/assets/icons/svg/system.svg @@ -0,0 +1,2 @@ +<?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="1543827724451" class="icon" style="" viewBox="0 0 1084 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="10233" xmlns:xlink="http://www.w3.org/1999/xlink" width="211.71875" height="200"><defs><style type="text/css">@font-face { font-family: rbicon; src: url("chrome-extension://dipiagiiohfljcicegpgffpbnjmgjcnf/fonts/rbicon.woff2") format("woff2"); font-weight: normal; font-style: normal; } +</style></defs><path d="M1080.09609 434.500756c-4.216302-23.731757-26.9241-47.945376-50.595623-53.185637l-17.648235-4.095836a175.940257 175.940257 0 0 1-101.612877-80.832531 177.807476 177.807476 0 0 1-18.732427-129.801867l5.541425-16.684509c7.10748-23.129428-2.108151-54.992624-20.599646-70.833873 0 0-16.624276-14.094495-63.244529-41.199293-46.800951-26.984332-66.858502-34.513443-66.858502-34.513443-22.76803-8.372371-54.631227-0.361397-71.255503 17.407304l-12.287509 13.251234a173.470708 173.470708 0 0 1-120.465769 48.065842A174.13327 174.13327 0 0 1 421.329029 33.590675L409.583617 20.761071C393.140039 2.99237 361.096144-4.898138 338.267881 3.353767c0 0-20.358715 7.529111-67.099434 34.513443-46.800951 27.34573-63.244529 41.440225-63.244529 41.440225-18.431263 15.66055-27.646894 47.222582-20.539413 70.592941l5.059562 16.865207a178.048407 178.048407 0 0 1-18.672194 129.621169 174.916297 174.916297 0 0 1-102.275439 81.073463l-17.045906 3.854904c-23.310126 5.42096-46.258856 29.333415-50.595623 53.185637 0 0-3.854905 21.382674-3.854905 75.712737 0 54.330062 3.854905 75.712736 3.854905 75.712736 4.216302 23.972688 26.9241 47.945376 50.595623 53.185637l16.624276 3.854905a174.253736 174.253736 0 0 1 102.395904 81.314394c23.310126 40.837896 28.911785 87.337683 18.732427 129.801867l-4.81863 16.443578c-7.10748 23.129428 2.108151 54.992624 20.599646 70.833872 0 0 16.624276 14.094495 63.244529 41.199293 46.800951 27.104798 66.918735 34.513443 66.918735 34.513443 22.707798 8.372371 54.631227 0.361397 71.255503-17.407303l11.624947-12.588673a175.096996 175.096996 0 0 1 242.256662 0.120465l11.624947 12.648906c16.383345 17.708468 48.427239 25.598976 71.255503 17.347071 0 0 20.358715-7.529111 67.159666-34.513443 46.740719-27.104798 63.124063-41.199293 63.124064-41.199293 18.491496-15.600317 27.707127-47.463513 20.599646-70.833873l-5.059562-17.106139a176.723284 176.723284 0 0 1 18.672194-129.139305 176.060722 176.060722 0 0 1 102.395904-81.314394l16.68451-3.854905c23.310126-5.42096 46.258856-29.333415 50.595623-53.185637 0 0 3.854905-21.382674 3.854904-75.712737-0.240932-54.330062-4.095836-75.833202-4.095836-75.833202z m-537.819428 293.334149c-119.261112 0-216.175824-97.336342-216.175824-217.621412a216.657687 216.657687 0 0 1 216.236057-217.320249c119.200879 0 216.115591 97.276109 216.11559 217.56118-0.240932 120.044139-96.974945 217.320248-216.175823 217.320249z" p-id="10234"></path></svg> \ No newline at end of file diff --git a/src/assets/icons/svg/tab.svg b/src/assets/icons/svg/tab.svg new file mode 100644 index 0000000..b4b48e4 --- /dev/null +++ b/src/assets/icons/svg/tab.svg @@ -0,0 +1 @@ +<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M78.921.052H49.08c-1.865 0-3.198 1.599-3.198 3.464v6.661c0 1.865 1.6 3.464 3.198 3.464h29.84c1.865 0 3.198-1.599 3.198-3.464V3.516C82.385 1.65 80.786.052 78.92.052zm45.563 0H94.642c-1.865 0-3.464 1.599-3.464 3.464v6.661c0 1.865 1.599 3.464 3.464 3.464h29.842c1.865-.266 3.464-1.599 3.464-3.464V3.516c0-1.865-1.599-3.464-3.464-3.464zm0 22.382H40.02c-1.866 0-3.464-1.599-3.464-3.464V3.516c0-1.865-1.599-3.464-3.464-3.464H3.516C1.65.052.052 1.651.052 3.516V124.75c0 1.598 1.599 3.197 3.464 3.197h120.968c1.865 0 3.464-1.599 3.464-3.464V25.898c0-1.865-1.599-3.464-3.464-3.464z"/></svg> \ No newline at end of file diff --git a/src/assets/icons/svg/table.svg b/src/assets/icons/svg/table.svg new file mode 100644 index 0000000..0e3dc9d --- /dev/null +++ b/src/assets/icons/svg/table.svg @@ -0,0 +1 @@ +<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M.006.064h127.988v31.104H.006V.064zm0 38.016h38.396v41.472H.006V38.08zm0 48.384h38.396v41.472H.006V86.464zM44.802 38.08h38.396v41.472H44.802V38.08zm0 48.384h38.396v41.472H44.802V86.464zM89.598 38.08h38.396v41.472H89.598zm0 48.384h38.396v41.472H89.598z"/><path d="M.006.064h127.988v31.104H.006V.064zm0 38.016h38.396v41.472H.006V38.08zm0 48.384h38.396v41.472H.006V86.464zM44.802 38.08h38.396v41.472H44.802V38.08zm0 48.384h38.396v41.472H44.802V86.464zM89.598 38.08h38.396v41.472H89.598zm0 48.384h38.396v41.472H89.598z"/></svg> \ No newline at end of file diff --git a/src/assets/icons/svg/textarea.svg b/src/assets/icons/svg/textarea.svg new file mode 100644 index 0000000..2709f29 --- /dev/null +++ b/src/assets/icons/svg/textarea.svg @@ -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="1575802855098" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2984" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M896 160H128c-35.2 0-64 28.8-64 64v576c0 35.2 28.8 64 64 64h768c35.2 0 64-28.8 64-64V224c0-35.2-28.8-64-64-64z m0 608c0 16-12.8 32-32 32H160c-19.2 0-32-12.8-32-32V256c0-16 12.8-32 32-32h704c19.2 0 32 12.8 32 32v512z" p-id="2985"></path><path d="M224 288c-19.2 0-32 12.8-32 32v256c0 16 12.8 32 32 32s32-12.8 32-32V320c0-16-12.8-32-32-32z m608 480c19.2 0 32-12.8 32-32V608L704 768h128z" p-id="2986"></path></svg> \ No newline at end of file diff --git a/src/assets/icons/svg/theme.svg b/src/assets/icons/svg/theme.svg new file mode 100644 index 0000000..5982a2f --- /dev/null +++ b/src/assets/icons/svg/theme.svg @@ -0,0 +1 @@ +<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M125.5 36.984L95.336 2.83C93.735 1.018 91.565 0 89.3 0c-2.263 0-4.433 1.018-6.033 2.83l-3.786 4.286c-1.6 1.812-3.77 2.83-6.032 2.831H54.553c-2.263 0-4.434-1.018-6.033-2.83L44.734 2.83C43.134 1.018 40.964 0 38.701 0c-2.263 0-4.434 1.018-6.034 2.83L2.5 36.984C.9 38.796 0 41.254 0 43.815c0 2.562.899 5.02 2.5 6.831L14.565 64.31c2.178 2.468 5.367 3.403 8.33 2.444 1.35-.435 2.709.592 2.709 2.18v49.407c0 5.313 3.84 9.66 8.532 9.66h59.726c4.693 0 8.532-4.347 8.532-9.66V68.934c0-1.59 1.36-2.616 2.71-2.181 2.962.96 6.15.024 8.329-2.444L125.5 50.646c1.6-1.811 2.499-4.269 2.499-6.83 0-2.563-.899-5.02-2.5-6.832z"/></svg> \ No newline at end of file diff --git a/src/assets/icons/svg/time-range.svg b/src/assets/icons/svg/time-range.svg new file mode 100644 index 0000000..13c1202 --- /dev/null +++ b/src/assets/icons/svg/time-range.svg @@ -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="1579774825624" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1248" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M498.595712 482.290351 345.420077 482.290351l0 57.307194 210.477712 0L555.897789 274.196942l-57.301054 0L498.596735 482.290351zM498.595712 482.290351" p-id="1249"></path><path d="M577.685002 644.98478l379.879913 0 0 57.302077L577.685002 702.286858 577.685002 644.98478 577.685002 644.98478zM577.685002 644.98478" p-id="1250"></path><path d="M577.685002 773.764795l379.879913 0 0 57.307194L577.685002 831.071989 577.685002 773.764795 577.685002 773.764795zM577.685002 773.764795" p-id="1251"></path><path d="M577.685002 902.549927l379.879913 0 0 57.307194L577.685002 959.857121 577.685002 902.549927 577.685002 902.549927zM577.685002 902.549927" p-id="1252"></path><path d="M102.523001 382.290823c4.450359 2.615571 9.470699 3.954055 14.530948 3.954055 2.969635 0 5.952572-0.461511 8.836249-1.394766l190.809767-61.886489c15.052834-4.882194 23.297612-21.040199 18.415418-36.08894-4.882194-15.052834-21.040199-23.297612-36.093033-18.415418L175.676092 308.458257c15.994276-26.115797 35.170011-50.537 57.370639-72.743768 73.767074-73.767074 171.845857-114.388237 276.16783-114.388237 104.32095 0 202.39564 40.622186 276.16169 114.388237s114.393353 171.845857 114.393353 276.16783c0 26.427906-2.615571 52.449559-7.709589 77.780481l58.302871 0c4.464685-25.499767 6.708795-51.470255 6.708795-77.780481 0-60.449767-11.845793-119.102608-35.204803-174.336584-22.559808-53.334719-54.850236-101.226472-95.968725-142.349055-41.122583-41.122583-89.017406-73.408917-142.348032-95.968725C628.317169 75.866898 569.659211 64.021106 509.215584 64.021106c-60.448744 0-119.106702 11.845793-174.336584 35.207873-53.334719 22.559808-101.230566 54.846142-142.349055 95.968725-23.980157 23.980157-44.934398 50.278103-62.727647 78.601172l-20.738323-105.655342c-3.043313-15.527648-18.105357-25.642007-33.631982-22.599717-15.527648 3.048429-25.64303 18.105357-22.599717 33.637098l36.102243 183.932126C90.51348 371.153158 95.460142 378.13313 102.523001 382.290823L102.523001 382.290823zM102.523001 382.290823" p-id="1253"></path><path d="M126.020158 587.9416 67.768453 587.9416c5.759167 33.679054 15.368012 66.544579 28.789697 98.278327 22.559808 53.333696 54.850236 101.225449 95.971795 142.348032 41.122583 41.122583 89.014336 73.408917 142.349055 95.968725 54.112432 22.88829 111.517863 34.71157 170.668031 35.18229L505.547031 902.395408c-102.94972-0.941442-199.594851-41.445948-272.499277-114.349351C177.545672 732.543975 140.810003 663.275355 126.020158 587.9416L126.020158 587.9416zM126.020158 587.9416" p-id="1254"></path></svg> \ No newline at end of file diff --git a/src/assets/icons/svg/time.svg b/src/assets/icons/svg/time.svg new file mode 100644 index 0000000..b376e32 --- /dev/null +++ b/src/assets/icons/svg/time.svg @@ -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="1577099827399" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1008" xmlns:xlink="http://www.w3.org/1999/xlink" width="81" height="81"><defs><style type="text/css"></style></defs><path d="M520 559h204c17.673 0 32 14.327 32 32 0 17.673-14.327 32-32 32H488c-17.673 0-32-14.327-32-32 0-0.167 0.001-0.334 0.004-0.5a32.65 32.65 0 0 1-0.004-0.5V277c0-17.673 14.327-32 32-32 17.673 0 32 14.327 32 32v282z m-8 401C264.576 960 64 759.424 64 512S264.576 64 512 64s448 200.576 448 448-200.576 448-448 448z m0-64c212.077 0 384-171.923 384-384S724.077 128 512 128 128 299.923 128 512s171.923 384 384 384z" p-id="1009"></path></svg> \ No newline at end of file diff --git a/src/assets/icons/svg/tool.svg b/src/assets/icons/svg/tool.svg new file mode 100644 index 0000000..48e0e35 --- /dev/null +++ b/src/assets/icons/svg/tool.svg @@ -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="1553828490559" class="icon" style="" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1684" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M898.831744 900.517641 103.816972 900.517641c-36.002982 0-65.363683-29.286-65.363683-65.313541l0-554.949184c0-36.041868 29.361725-65.326844 65.363683-65.326844l795.015795 0c36.002982 0 65.198931 29.284977 65.198931 65.326844l0 554.949184C964.030675 871.231641 934.834726 900.517641 898.831744 900.517641L898.831744 900.517641zM103.816972 255.593236c-13.576203 0-24.711821 11.085476-24.711821 24.662703l0 554.949184c0 13.576203 11.136641 24.662703 24.711821 24.662703l795.015795 0c13.577227 0 24.547069-11.086499 24.547069-24.662703l0-554.949184c0-13.577227-10.970866-24.662703-24.547069-24.662703L103.816972 255.593236 103.816972 255.593236zM664.346245 251.774257c-11.161201 0-20.332071-9.080819-20.332071-20.332071l0-101.278661c0-13.576203-11.047614-24.623817-24.699542-24.623817L383.181611 105.539708c-13.576203 0-24.712845 11.04659-24.712845 24.623817l0 101.278661c0 11.252275-9.041934 20.332071-20.332071 20.332071-11.20111 0-20.319791-9.080819-20.319791-20.332071l0-101.278661c0-35.989679 29.323862-65.275679 65.364707-65.275679l236.133022 0c36.06745 0 65.402569 29.284977 65.402569 65.275679l0 101.278661C684.717202 242.694461 675.636383 251.774257 664.346245 251.774257L664.346245 251.774257zM413.233044 521.725502 75.694471 521.725502c-11.163247 0-20.333094-9.117658-20.333094-20.35663 0-11.252275 9.169847-20.332071 20.333094-20.332071l337.538573 0c11.277858 0 20.319791 9.080819 20.319791 20.332071C433.552835 512.607844 424.510902 521.725502 413.233044 521.725502L413.233044 521.725502zM912.894018 521.725502 575.367725 521.725502c-11.213389 0-20.332071-9.117658-20.332071-20.35663 0-11.252275 9.118682-20.332071 20.332071-20.332071l337.526293 0c11.290137 0 20.332071 9.080819 20.332071 20.332071C933.226089 512.607844 924.184155 521.725502 912.894018 521.725502L912.894018 521.725502zM557.56322 634.217552 445.085496 634.217552c-11.213389 0-20.332071-9.079796-20.332071-20.331048l0-168.763658c0-11.251252 9.118682-20.332071 20.332071-20.332071l112.478747 0c11.290137 0 20.370956 9.080819 20.370956 20.332071l0 168.763658C577.934177 625.137757 568.853357 634.217552 557.56322 634.217552L557.56322 634.217552zM465.417567 593.514525l71.827909 0L537.245476 465.454918l-71.827909 0L465.417567 593.514525 465.417567 593.514525z" p-id="1685"></path></svg> \ No newline at end of file diff --git a/src/assets/icons/svg/tree-table.svg b/src/assets/icons/svg/tree-table.svg new file mode 100644 index 0000000..8aafdb8 --- /dev/null +++ b/src/assets/icons/svg/tree-table.svg @@ -0,0 +1 @@ +<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M44.8 0h79.543C126.78 0 128 1.422 128 4.267v23.466c0 2.845-1.219 4.267-3.657 4.267H44.8c-2.438 0-3.657-1.422-3.657-4.267V4.267C41.143 1.422 42.362 0 44.8 0zm22.857 48h56.686c2.438 0 3.657 1.422 3.657 4.267v23.466c0 2.845-1.219 4.267-3.657 4.267H67.657C65.22 80 64 78.578 64 75.733V52.267C64 49.422 65.219 48 67.657 48zm0 48h56.686c2.438 0 3.657 1.422 3.657 4.267v23.466c0 2.845-1.219 4.267-3.657 4.267H67.657C65.22 128 64 126.578 64 123.733v-23.466C64 97.422 65.219 96 67.657 96zM50.286 68.267c2.02 0 3.657-1.91 3.657-4.267 0-2.356-1.638-4.267-3.657-4.267H17.37V32h6.4c2.02 0 3.658-1.91 3.658-4.267V4.267C27.429 1.91 25.79 0 23.77 0H3.657C1.637 0 0 1.91 0 4.267v23.466C0 30.09 1.637 32 3.657 32h6.4v80c0 2.356 1.638 4.267 3.657 4.267h36.572c2.02 0 3.657-1.91 3.657-4.267 0-2.356-1.638-4.267-3.657-4.267H17.37V68.267h32.915z"/></svg> \ No newline at end of file diff --git a/src/assets/icons/svg/tree.svg b/src/assets/icons/svg/tree.svg new file mode 100644 index 0000000..dd4b7dd --- /dev/null +++ b/src/assets/icons/svg/tree.svg @@ -0,0 +1 @@ +<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M126.713 90.023c.858.985 1.287 2.134 1.287 3.447v29.553c0 1.423-.429 2.6-1.287 3.53-.858.93-1.907 1.395-3.146 1.395H97.824c-1.145 0-2.146-.465-3.004-1.395-.858-.93-1.287-2.107-1.287-3.53V93.47c0-.875.19-1.696.572-2.462.382-.766.906-1.368 1.573-1.806a3.84 3.84 0 0 1 2.146-.657h9.725V69.007a3.84 3.84 0 0 0-.43-1.806 3.569 3.569 0 0 0-1.143-1.313 2.714 2.714 0 0 0-1.573-.492h-36.47v23.149h9.725c1.144 0 2.145.492 3.004 1.478.858.985 1.287 2.134 1.287 3.447v29.553c0 .876-.191 1.696-.573 2.463-.38.766-.905 1.368-1.573 1.806a3.84 3.84 0 0 1-2.145.656H51.915a3.84 3.84 0 0 1-2.145-.656c-.668-.438-1.216-1.04-1.645-1.806a4.96 4.96 0 0 1-.644-2.463V93.47c0-1.313.43-2.462 1.288-3.447.858-.986 1.907-1.478 3.146-1.478h9.582v-23.15h-37.9c-.953 0-1.74.356-2.359 1.068-.62.711-.93 1.56-.93 2.544v19.538h9.726c1.239 0 2.264.492 3.074 1.478.81.985 1.216 2.134 1.216 3.447v29.553c0 1.423-.405 2.6-1.216 3.53-.81.93-1.835 1.395-3.074 1.395H4.29c-.476 0-.93-.082-1.358-.246a4.1 4.1 0 0 1-1.144-.657 4.658 4.658 0 0 1-.93-1.067 5.186 5.186 0 0 1-.643-1.395 5.566 5.566 0 0 1-.215-1.56V93.47c0-.437.048-.875.143-1.313a3.95 3.95 0 0 1 .429-1.15c.19-.328.429-.656.715-.984.286-.329.572-.602.858-.821.286-.22.62-.383 1.001-.493.382-.11.763-.164 1.144-.164h9.726V61.619c0-.985.31-1.833.93-2.544.619-.712 1.358-1.068 2.216-1.068h44.335V39.62h-9.582c-1.24 0-2.288-.492-3.146-1.477a5.09 5.09 0 0 1-1.287-3.448V5.14c0-1.423.429-2.627 1.287-3.612.858-.985 1.907-1.477 3.146-1.477h25.743c.763 0 1.478.246 2.145.739a5.17 5.17 0 0 1 1.573 1.888c.382.766.573 1.587.573 2.462v29.553c0 1.313-.43 2.463-1.287 3.448-.859.985-1.86 1.477-3.004 1.477h-9.725v18.389h42.762c.954 0 1.74.355 2.36 1.067.62.711.93 1.56.93 2.545v26.925h9.582c1.239 0 2.288.492 3.146 1.478z"/></svg> \ No newline at end of file diff --git a/src/assets/icons/svg/upload.svg b/src/assets/icons/svg/upload.svg new file mode 100644 index 0000000..bae49c0 --- /dev/null +++ b/src/assets/icons/svg/upload.svg @@ -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="1577540289643" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="7922" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M530.944 458.24l4.8 3.456 122.176 106.816a32 32 0 0 1-37.44 51.584l-4.672-3.392L546.56 556.16v280.704a32 32 0 0 1-26.24 31.488l-5.76 0.512a32 32 0 0 1-31.424-26.24l-0.512-5.76-0.064-280.704-69.12 60.48a32 32 0 0 1-40.96 0.896l-4.16-3.968a32 32 0 0 1-0.96-40.96l4.032-4.16 122.176-106.816a32 32 0 0 1 37.312-3.456zM497.92 128c128.128 0 239.168 82.304 275.52 199.04 123.968 11.264 221.312 113.088 221.312 237.44 0 128.128-103.68 232.96-234.88 238.272h-5.888l-35.52 0.192a32 32 0 0 1-0.192-64l35.264-0.128 4.672-0.064c96.384-3.84 172.544-80.896 172.544-174.272 0-96.128-80.512-174.464-179.584-174.464h-1.984a32 32 0 0 1-32-25.28C695.872 264.96 604.736 192 497.92 192 381.824 192 285.44 277.76 274.816 388.48a32 32 0 0 1-28.352 28.8c-83.968 9.152-147.84 78.208-147.84 159.552l0.192 7.936c3.84 85.76 77.056 154.112 166.592 154.112h45.632a32 32 0 0 1 0 64h-45.632C142.016 802.944 40.32 708.032 34.88 586.88l-0.192-9.28c0-106.88 76.352-197.184 179.968-219.904C239.488 226.112 357.76 128 497.856 128z" p-id="7923"></path></svg> \ No newline at end of file diff --git a/src/assets/icons/svg/user.svg b/src/assets/icons/svg/user.svg new file mode 100644 index 0000000..0ba0716 --- /dev/null +++ b/src/assets/icons/svg/user.svg @@ -0,0 +1 @@ +<svg width="130" height="130" xmlns="http://www.w3.org/2000/svg"><path d="M63.444 64.996c20.633 0 37.359-14.308 37.359-31.953 0-17.649-16.726-31.952-37.359-31.952-20.631 0-37.36 14.303-37.358 31.952 0 17.645 16.727 31.953 37.359 31.953zM80.57 75.65H49.434c-26.652 0-48.26 18.477-48.26 41.27v2.664c0 9.316 21.608 9.325 48.26 9.325H80.57c26.649 0 48.256-.344 48.256-9.325v-2.663c0-22.794-21.605-41.271-48.256-41.271z" stroke="#979797"/></svg> \ No newline at end of file diff --git a/src/assets/icons/svg/validCode.svg b/src/assets/icons/svg/validCode.svg new file mode 100644 index 0000000..cfb1021 --- /dev/null +++ b/src/assets/icons/svg/validCode.svg @@ -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="1569580729849" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1939" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M513.3 958.5c-142.2 0-397.9-222.1-401.6-440.5V268c1.7-39.6 31.7-72.3 71.1-77.3 49-4.6 97.1-16.5 142.7-35.3 47.8-14 91.9-38.3 129.4-71.1 30.3-24.4 72.9-26.3 105.3-4.6 39.9 30.7 83.8 55.9 130.5 74.6 48.6 14.7 98.2 25.9 148.4 33.7 38.5 7.6 67.1 40.3 69.5 79.5 3.3 84.9 2.5 169.9-2.6 254.7-33.7 281.6-253.7 436.4-392.7 436.3z m-0.1-813.7c-7.2-0.2-14.3 2-20 6.4-39.7 35.2-86.8 61.1-137.7 75.7-46.8 19.2-96.2 31-146.6 35.2-11 3.2-18.8 13-19.5 24.4v230.1c3.5 180.3 223.3 361 323.9 361s287.3-120.2 317.6-360.5c7.3-142.7 0-228.6 0-229.6-1.3-13.3-11-24.3-24-27.3-49.6-7.7-98.6-19-146.5-33.7-46.3-19.5-89.7-45.3-129-76.7-5.8-3.8-12.7-5.5-19.5-4.9l1.3-0.1z" fill="#C6CCDA" p-id="1940"></path><path d="M750.1 428L490.7 673.2c-11.7 11.1-29.5 12.9-43.1 4.2l-6.8-5.8-141.2-149.4c-9.3-9.3-12.7-22.9-9-35.5 3.8-12.6 14.1-22.1 27-24.8 12.9-2.7 26.1 1.9 34.6 11.9L469 597.5l233.7-221c14.6-12.8 36.8-11.6 49.9 2.7 13.2 14.2 11.5 35.3-2.5 48.8" fill="#C6CCDA" p-id="1941"></path></svg> \ No newline at end of file diff --git a/src/assets/icons/svg/wechat.svg b/src/assets/icons/svg/wechat.svg new file mode 100644 index 0000000..c586e55 --- /dev/null +++ b/src/assets/icons/svg/wechat.svg @@ -0,0 +1 @@ +<svg width="128" height="110" xmlns="http://www.w3.org/2000/svg"><path d="M86.635 33.334c1.467 0 2.917.113 4.358.283C87.078 14.392 67.58.111 45.321.111 20.44.111.055 17.987.055 40.687c0 13.104 6.781 23.863 18.115 32.209l-4.527 14.352 15.82-8.364c5.666 1.182 10.207 2.395 15.858 2.395 1.42 0 2.829-.073 4.227-.189-.886-3.19-1.398-6.53-1.398-9.996 0-20.845 16.98-37.76 38.485-37.76zm-24.34-12.936c3.407 0 5.665 2.363 5.665 5.954 0 3.576-2.258 5.97-5.666 5.97-3.392 0-6.795-2.395-6.795-5.97 0-3.591 3.403-5.954 6.795-5.954zM30.616 32.323c-3.393 0-6.818-2.395-6.818-5.971 0-3.591 3.425-5.954 6.818-5.954 3.392 0 5.65 2.363 5.65 5.954 0 3.576-2.258 5.97-5.65 5.97z"/><path d="M127.945 70.52c0-19.075-18.108-34.623-38.448-34.623-21.537 0-38.5 15.548-38.5 34.623 0 19.108 16.963 34.622 38.5 34.622 4.508 0 9.058-1.2 13.584-2.395l12.414 7.167-3.404-11.923c9.087-7.184 15.854-16.712 15.854-27.471zm-50.928-5.97c-2.254 0-4.53-2.362-4.53-4.773 0-2.378 2.276-4.771 4.53-4.771 3.422 0 5.665 2.393 5.665 4.771 0 2.41-2.243 4.773-5.665 4.773zm24.897 0c-2.24 0-4.498-2.362-4.498-4.773 0-2.378 2.258-4.771 4.498-4.771 3.392 0 5.665 2.393 5.665 4.771 0 2.41-2.273 4.773-5.665 4.773z"/></svg> \ No newline at end of file diff --git a/src/assets/icons/svg/zip.svg b/src/assets/icons/svg/zip.svg new file mode 100644 index 0000000..f806fc4 --- /dev/null +++ b/src/assets/icons/svg/zip.svg @@ -0,0 +1 @@ +<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M78.527 116.793c.178.008.348.024.527.024h40.233c4.711-.005 8.53-3.677 8.534-8.21V18.895c-.004-4.532-3.823-8.204-8.534-8.209H79.054c-.179 0-.353.016-.527.024V0L0 10.082v107.406l78.527 10.342v-11.037zm0-101.362c.174-.024.348-.052.527-.052h40.233c2.018 0 3.659 1.578 3.659 3.52v89.713c-.003 1.942-1.64 3.517-3.659 3.519H79.054c-.179 0-.353-.028-.527-.052V15.431zM30.262 75.757l-18.721-.46V72.37l11.3-16.673v-.148l-10.266.164v-4.51l17.504-.44v3.264L18.696 70.76v.144l11.566.176v4.678zm9.419.231l-5.823-.144V50.671l5.823-.144v25.461zm22.255-11.632c-2.168 1.922-5.353 2.76-9.02 2.736-.702.004-1.402-.04-2.097-.131v9.303l-5.997-.148V50.743c1.852-.352 4.473-.647 8.218-.743 3.838-.096 6.608.539 8.48 1.913 1.807 1.306 3.032 3.5 3.032 6.112s-.926 4.833-2.612 6.331h-.004zM53.36 54.45c-.856-.01-1.71.083-2.541.275v7.682c.523.116 1.167.152 2.06.152 3.301-.004 5.36-1.614 5.36-4.314 0-2.425-1.772-3.843-4.875-3.791l-.004-.004zm39.847-37.066h9.564v3.795h-9.564v-3.795zm-9.568 5.68h9.564v3.8h-9.564v-3.8zm9.568 6.216h9.564v3.799h-9.564V29.28zm0 12h9.564v3.794h-9.564V41.28zm-9.568-6.096h9.564v3.795h-9.564v-3.795zm9.472 47.064c2.512 0 4.921-.96 6.697-2.67 1.776-1.708 2.773-4.026 2.772-6.442l-1.748-15.263c0-5.033-2.492-9.112-7.725-9.112-5.232 0-7.72 4.079-7.72 9.112l-1.752 15.263c-.001 2.417.996 4.735 2.773 6.444 1.777 1.71 4.187 2.669 6.7 2.668h.003zm-3.135-16.75h6.27v12.743h-6.27V65.5z"/></svg> \ No newline at end of file diff --git a/src/assets/images/dark.svg b/src/assets/images/dark.svg new file mode 100644 index 0000000..f646bd7 --- /dev/null +++ b/src/assets/images/dark.svg @@ -0,0 +1,39 @@ +<?xml version="1.0" encoding="UTF-8"?> +<svg width="52px" height="45px" viewBox="0 0 52 45" version="1.1" + xmlns="http://www.w3.org/2000/svg" + xmlns:xlink="http://www.w3.org/1999/xlink"> + <defs> + <filter x="-9.4%" y="-6.2%" width="118.8%" height="122.5%" filterUnits="objectBoundingBox" id="filter-1"> + <feOffset dx="0" dy="1" in="SourceAlpha" result="shadowOffsetOuter1"></feOffset> + <feGaussianBlur stdDeviation="1" in="shadowOffsetOuter1" result="shadowBlurOuter1"></feGaussianBlur> + <feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.15 0" type="matrix" in="shadowBlurOuter1" result="shadowMatrixOuter1"></feColorMatrix> + <feMerge> + <feMergeNode in="shadowMatrixOuter1"></feMergeNode> + <feMergeNode in="SourceGraphic"></feMergeNode> + </feMerge> + </filter> + <rect id="path-2" x="0" y="0" width="48" height="40" rx="4"></rect> + <filter x="-4.2%" y="-2.5%" width="108.3%" height="110.0%" filterUnits="objectBoundingBox" id="filter-4"> + <feOffset dx="0" dy="1" in="SourceAlpha" result="shadowOffsetOuter1"></feOffset> + <feGaussianBlur stdDeviation="0.5" in="shadowOffsetOuter1" result="shadowBlurOuter1"></feGaussianBlur> + <feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.1 0" type="matrix" in="shadowBlurOuter1"></feColorMatrix> + </filter> + </defs> + <g id="閰嶇疆闈㈡澘" width="48" height="40" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd"> + <g id="setting-copy-2" width="48" height="40" transform="translate(-1190.000000, -136.000000)"> + <g id="Group-8" width="48" height="40" transform="translate(1167.000000, 0.000000)"> + <g id="Group-5-Copy-5" filter="url(#filter-1)" transform="translate(25.000000, 137.000000)"> + <mask id="mask-3" fill="white"> + <use xlink:href="#path-2"></use> + </mask> + <g id="Rectangle-18"> + <use fill="black" fill-opacity="1" filter="url(#filter-4)" xlink:href="#path-2"></use> + <use fill="#F0F2F5" fill-rule="evenodd" xlink:href="#path-2"></use> + </g> + <rect id="Rectangle-11" fill="#FFFFFF" mask="url(#mask-3)" x="0" y="0" width="48" height="10"></rect> + <rect id="Rectangle-18" fill="#303648" mask="url(#mask-3)" x="0" y="0" width="16" height="40"></rect> + </g> + </g> + </g> + </g> +</svg> \ No newline at end of file diff --git a/src/assets/images/light.svg b/src/assets/images/light.svg new file mode 100644 index 0000000..ab7cc08 --- /dev/null +++ b/src/assets/images/light.svg @@ -0,0 +1,39 @@ +<?xml version="1.0" encoding="UTF-8"?> +<svg width="52px" height="45px" viewBox="0 0 52 45" version="1.1" + xmlns="http://www.w3.org/2000/svg" + xmlns:xlink="http://www.w3.org/1999/xlink"> + <defs> + <filter x="-9.4%" y="-6.2%" width="118.8%" height="122.5%" filterUnits="objectBoundingBox" id="filter-1"> + <feOffset dx="0" dy="1" in="SourceAlpha" result="shadowOffsetOuter1"></feOffset> + <feGaussianBlur stdDeviation="1" in="shadowOffsetOuter1" result="shadowBlurOuter1"></feGaussianBlur> + <feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.15 0" type="matrix" in="shadowBlurOuter1" result="shadowMatrixOuter1"></feColorMatrix> + <feMerge> + <feMergeNode in="shadowMatrixOuter1"></feMergeNode> + <feMergeNode in="SourceGraphic"></feMergeNode> + </feMerge> + </filter> + <rect id="path-2" x="0" y="0" width="48" height="40" rx="4"></rect> + <filter x="-4.2%" y="-2.5%" width="108.3%" height="110.0%" filterUnits="objectBoundingBox" id="filter-4"> + <feOffset dx="0" dy="1" in="SourceAlpha" result="shadowOffsetOuter1"></feOffset> + <feGaussianBlur stdDeviation="0.5" in="shadowOffsetOuter1" result="shadowBlurOuter1"></feGaussianBlur> + <feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.1 0" type="matrix" in="shadowBlurOuter1"></feColorMatrix> + </filter> + </defs> + <g id="閰嶇疆闈㈡澘" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd"> + <g id="setting-copy-2" transform="translate(-1254.000000, -136.000000)"> + <g id="Group-8" transform="translate(1167.000000, 0.000000)"> + <g id="Group-5" filter="url(#filter-1)" transform="translate(89.000000, 137.000000)"> + <mask id="mask-3" fill="white"> + <use xlink:href="#path-2"></use> + </mask> + <g id="Rectangle-18"> + <use fill="black" fill-opacity="1" filter="url(#filter-4)" xlink:href="#path-2"></use> + <use fill="#F0F2F5" fill-rule="evenodd" xlink:href="#path-2"></use> + </g> + <rect id="Rectangle-18" fill="#FFFFFF" mask="url(#mask-3)" x="0" y="0" width="16" height="40"></rect> + <rect id="Rectangle-11" fill="#FFFFFF" mask="url(#mask-3)" x="0" y="0" width="48" height="10"></rect> + </g> + </g> + </g> + </g> +</svg> \ No newline at end of file diff --git a/src/assets/images/login-background.jpg b/src/assets/images/login-background.jpg new file mode 100644 index 0000000..8a89eb8 --- /dev/null +++ b/src/assets/images/login-background.jpg Binary files differ diff --git a/src/assets/images/pay.png b/src/assets/images/pay.png new file mode 100644 index 0000000..bb8b967 --- /dev/null +++ b/src/assets/images/pay.png Binary files differ diff --git a/src/assets/images/profile.jpg b/src/assets/images/profile.jpg new file mode 100644 index 0000000..b3a940b --- /dev/null +++ b/src/assets/images/profile.jpg Binary files differ diff --git a/src/assets/logo/logo.png b/src/assets/logo/logo.png new file mode 100644 index 0000000..e263760 --- /dev/null +++ b/src/assets/logo/logo.png Binary files differ diff --git a/src/assets/styles/btn.scss b/src/assets/styles/btn.scss new file mode 100644 index 0000000..3590d8d --- /dev/null +++ b/src/assets/styles/btn.scss @@ -0,0 +1,99 @@ +@import './variables.module.scss'; + +@mixin colorBtn($color) { + background: $color; + + &:hover { + color: $color; + + &:before, + &:after { + background: $color; + } + } +} + +.blue-btn { + @include colorBtn($blue) +} + +.light-blue-btn { + @include colorBtn($light-blue) +} + +.red-btn { + @include colorBtn($red) +} + +.pink-btn { + @include colorBtn($pink) +} + +.green-btn { + @include colorBtn($green) +} + +.tiffany-btn { + @include colorBtn($tiffany) +} + +.yellow-btn { + @include colorBtn($yellow) +} + +.pan-btn { + font-size: 14px; + color: #fff; + padding: 14px 36px; + border-radius: 8px; + border: none; + outline: none; + transition: 600ms ease all; + position: relative; + display: inline-block; + + &:hover { + background: #fff; + + &:before, + &:after { + width: 100%; + transition: 600ms ease all; + } + } + + &:before, + &:after { + content: ''; + position: absolute; + top: 0; + right: 0; + height: 2px; + width: 0; + transition: 400ms ease all; + } + + &::after { + right: inherit; + top: inherit; + left: 0; + bottom: 0; + } +} + +.custom-button { + display: inline-block; + line-height: 1; + white-space: nowrap; + cursor: pointer; + background: #fff; + color: #fff; + -webkit-appearance: none; + text-align: center; + box-sizing: border-box; + outline: 0; + margin: 0; + padding: 10px 15px; + font-size: 14px; + border-radius: 4px; +} diff --git a/src/assets/styles/element-ui.scss b/src/assets/styles/element-ui.scss new file mode 100644 index 0000000..0f175f2 --- /dev/null +++ b/src/assets/styles/element-ui.scss @@ -0,0 +1,96 @@ +// cover some element-ui styles + +.el-breadcrumb__inner, +.el-breadcrumb__inner a { + font-weight: 400 !important; +} + +.el-upload { + input[type="file"] { + display: none !important; + } +} + +.el-upload__input { + display: none; +} + +.cell { + .el-tag { + margin-right: 0px; + } +} + +.small-padding { + .cell { + padding-left: 5px; + padding-right: 5px; + } +} + +.fixed-width { + .el-button--mini { + padding: 7px 10px; + width: 60px; + } +} + +.status-col { + .cell { + padding: 0 10px; + text-align: center; + + .el-tag { + margin-right: 0px; + } + } +} + +// to fixed https://github.com/ElemeFE/element/issues/2461 +.el-dialog { + transform: none; + left: 0; + position: relative; + margin: 0 auto; +} + +// refine element ui upload +.upload-container { + .el-upload { + width: 100%; + + .el-upload-dragger { + width: 100%; + height: 200px; + } + } +} + +// dropdown +.el-dropdown-menu { + a { + display: block + } +} + +// fix date-picker ui bug in filter-item +.el-range-editor.el-input__inner { + display: inline-flex !important; +} + +// to fix el-date-picker css style +.el-range-separator { + box-sizing: content-box; +} + +.el-menu--collapse + > div + > .el-submenu + > .el-submenu__title + .el-submenu__icon-arrow { + display: none; +} + +.el-dropdown .el-dropdown-link{ + color: var(--el-color-primary) !important; +} \ No newline at end of file diff --git a/src/assets/styles/index.scss b/src/assets/styles/index.scss new file mode 100644 index 0000000..efc1ddd --- /dev/null +++ b/src/assets/styles/index.scss @@ -0,0 +1,180 @@ +@import './variables.module.scss'; +@import './mixin.scss'; +@import './transition.scss'; +@import './element-ui.scss'; +@import './sidebar.scss'; +@import './btn.scss'; +@import './ruoyi.scss'; + +body { + height: 100%; + margin: 0; + -moz-osx-font-smoothing: grayscale; + -webkit-font-smoothing: antialiased; + text-rendering: optimizeLegibility; + font-family: Helvetica Neue, Helvetica, PingFang SC, Hiragino Sans GB, Microsoft YaHei, Arial, sans-serif; +} + +label { + font-weight: 700; +} + +html { + height: 100%; + box-sizing: border-box; +} + +#app { + height: 100%; +} + +*, +*:before, +*:after { + box-sizing: inherit; +} + +.no-padding { + padding: 0px !important; +} + +.padding-content { + padding: 4px 0; +} + +a:focus, +a:active { + outline: none; +} + +a, +a:focus, +a:hover { + cursor: pointer; + color: inherit; + text-decoration: none; +} + +div:focus { + outline: none; +} + +.fr { + float: right; +} + +.fl { + float: left; +} + +.pr-5 { + padding-right: 5px; +} + +.pl-5 { + padding-left: 5px; +} + +.block { + display: block; +} + +.pointer { + cursor: pointer; +} + +.inlineBlock { + display: block; +} + +.clearfix { + &:after { + visibility: hidden; + display: block; + font-size: 0; + content: " "; + clear: both; + height: 0; + } +} + +aside { + background: #eef1f6; + padding: 8px 24px; + margin-bottom: 20px; + border-radius: 2px; + display: block; + line-height: 32px; + font-size: 16px; + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif; + color: #2c3e50; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + + a { + color: #337ab7; + cursor: pointer; + + &:hover { + color: rgb(32, 160, 255); + } + } +} + +//main-container鍏ㄥ眬鏍峰紡 +.app-container { + padding: 20px; +} + +.components-container { + margin: 30px 50px; + position: relative; +} + +.text-center { + text-align: center +} + +.sub-navbar { + height: 50px; + line-height: 50px; + position: relative; + width: 100%; + text-align: right; + padding-right: 20px; + transition: 600ms ease position; + background: linear-gradient(90deg, rgba(32, 182, 249, 1) 0%, rgba(32, 182, 249, 1) 0%, rgba(33, 120, 241, 1) 100%, rgba(33, 120, 241, 1) 100%); + + .subtitle { + font-size: 20px; + color: #fff; + } + + &.draft { + background: #d0d0d0; + } + + &.deleted { + background: #d0d0d0; + } +} + +.link-type, +.link-type:focus { + color: #337ab7; + cursor: pointer; + + &:hover { + color: rgb(32, 160, 255); + } +} + +.filter-container { + padding-bottom: 10px; + + .filter-item { + display: inline-block; + vertical-align: middle; + margin-bottom: 10px; + } +} diff --git a/src/assets/styles/mixin.scss b/src/assets/styles/mixin.scss new file mode 100644 index 0000000..06fa061 --- /dev/null +++ b/src/assets/styles/mixin.scss @@ -0,0 +1,66 @@ +@mixin clearfix { + &:after { + content: ""; + display: table; + clear: both; + } +} + +@mixin scrollBar { + &::-webkit-scrollbar-track-piece { + background: #d3dce6; + } + + &::-webkit-scrollbar { + width: 6px; + } + + &::-webkit-scrollbar-thumb { + background: #99a9bf; + border-radius: 20px; + } +} + +@mixin relative { + position: relative; + width: 100%; + height: 100%; +} + +@mixin pct($pct) { + width: #{$pct}; + position: relative; + margin: 0 auto; +} + +@mixin triangle($width, $height, $color, $direction) { + $width: $width/2; + $color-border-style: $height solid $color; + $transparent-border-style: $width solid transparent; + height: 0; + width: 0; + + @if $direction==up { + border-bottom: $color-border-style; + border-left: $transparent-border-style; + border-right: $transparent-border-style; + } + + @else if $direction==right { + border-left: $color-border-style; + border-top: $transparent-border-style; + border-bottom: $transparent-border-style; + } + + @else if $direction==down { + border-top: $color-border-style; + border-left: $transparent-border-style; + border-right: $transparent-border-style; + } + + @else if $direction==left { + border-right: $color-border-style; + border-top: $transparent-border-style; + border-bottom: $transparent-border-style; + } +} diff --git a/src/assets/styles/ruoyi.scss b/src/assets/styles/ruoyi.scss new file mode 100644 index 0000000..b3240c3 --- /dev/null +++ b/src/assets/styles/ruoyi.scss @@ -0,0 +1,290 @@ +/** + * 閫氱敤css鏍峰紡甯冨眬澶勭悊 + * Copyright (c) 2019 ruoyi + */ + +/** 鍩虹閫氱敤 **/ +.pt5 { + padding-top: 5px; +} +.pr5 { + padding-right: 5px; +} +.pb5 { + padding-bottom: 5px; +} +.mt5 { + margin-top: 5px; +} +.mr5 { + margin-right: 5px; +} +.mb5 { + margin-bottom: 5px; +} +.mb8 { + margin-bottom: 8px; +} +.ml5 { + margin-left: 5px; +} +.mt10 { + margin-top: 10px; +} +.mr10 { + margin-right: 10px; +} +.mb10 { + margin-bottom: 10px; +} +.ml10 { + margin-left: 10px; +} +.mt20 { + margin-top: 20px; +} +.mr20 { + margin-right: 20px; +} +.mb20 { + margin-bottom: 20px; +} +.ml20 { + 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; +} + +.el-form .el-form-item__label { + font-weight: 700; +} +.el-dialog:not(.is-fullscreen) { + margin-top: 6vh !important; +} + +.el-dialog.scrollbar .el-dialog__body { + 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; + } + } +} + +/** 琛ㄥ崟甯冨眬 **/ +.form-header { + font-size:15px; + color:#6379bb; + border-bottom:1px solid #ddd; + margin:8px 10px 25px 10px; + padding-bottom:5px +} + +/** 琛ㄦ牸甯冨眬 **/ +.pagination-container { + display: flex; + justify-content: flex-end; + margin-top: 20px; + background-color: transparent !important; +} + +/* 寮圭獥涓殑鍒嗛〉鍣� */ +.el-dialog .pagination-container { + 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 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; +} + +/** 琛ㄦ牸鏇村鎿嶄綔涓嬫媺鏍峰紡 */ +.el-table .el-dropdown-link { + cursor: pointer; + color: #409EFF; + margin-left: 10px; +} + +.el-table .el-dropdown, .el-icon-arrow-down { + font-size: 12px; +} + +.el-tree-node__content > .el-checkbox { + margin-right: 8px; +} + +.list-group-striped > .list-group-item { + border-left: 0; + border-right: 0; + border-radius: 0; + padding-left: 0; + padding-right: 0; +} + +.list-group { + 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; +} + +.pull-right { + float: right !important; +} + +.el-card__header { + padding: 14px 15px 7px !important; + min-height: 40px; +} + +.el-card__body { + padding: 15px 20px 20px 20px !important; +} + +.card-box { + margin-bottom: 10px; +} + +/* button color */ +.el-button--cyan.is-active, +.el-button--cyan:active { + background: #20B2AA; + border-color: #20B2AA; + color: #FFFFFF; +} + +.el-button--cyan:focus, +.el-button--cyan:hover { + background: #48D1CC; + border-color: #48D1CC; + color: #FFFFFF; +} + +.el-button--cyan { + background-color: #20B2AA; + border-color: #20B2AA; + color: #FFFFFF; +} + +/* text color */ +.text-navy { + color: #1ab394; +} + +.text-primary { + color: inherit; +} + +.text-success { + color: #1c84c6; +} + +.text-info { + color: #23c6c8; +} + +.text-warning { + color: #f8ac59; +} + +.text-danger { + color: #ed5565; +} + +.text-muted { + color: #888888; +} + +/* image */ +.img-circle { + border-radius: 50%; +} + +.img-lg { + 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; +} + +/* 鎷栨嫿鍒楁牱寮� */ +.sortable-ghost{ + opacity: .8; + color: #fff!important; + background: #42b983!important; +} + +/* 琛ㄦ牸鍙充晶宸ュ叿鏍忔牱寮� */ +.top-right-btn { + margin-left: auto; +} + +/* 鍒嗗壊闈㈡澘鏍峰紡 */ +.splitpanes.default-theme .splitpanes__pane { + background-color: var(--splitpanes-default-bg) !important; +} diff --git a/src/assets/styles/sidebar.scss b/src/assets/styles/sidebar.scss new file mode 100644 index 0000000..9a4c487 --- /dev/null +++ b/src/assets/styles/sidebar.scss @@ -0,0 +1,236 @@ +#app { + + .main-container { + min-height: 100%; + transition: margin-left .28s; + margin-left: $base-sidebar-width; + position: relative; + } + + .sidebarHide { + margin-left: 0!important; + } + + .sidebar-container { + transition: width 0.28s; + width: $base-sidebar-width !important; + height: 100%; + position: fixed; + font-size: 0px; + top: 0; + bottom: 0; + left: 0; + z-index: 1001; + overflow: hidden; + -webkit-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 { + transition: 0s width ease-in-out, 0s padding-left ease-in-out, 0s padding-right ease-in-out; + } + + .scrollbar-wrapper { + overflow-x: hidden !important; + } + + .el-scrollbar__bar.is-vertical { + right: 0px; + } + + .el-scrollbar { + height: 100%; + } + + &.has-logo { + .el-scrollbar { + height: calc(100% - 50px); + } + } + + .is-horizontal { + display: none; + } + + a { + display: inline-block; + width: 100%; + overflow: hidden; + } + + .svg-icon { + margin-right: 16px; + } + + .el-menu { + border: none; + height: 100%; + width: 100% !important; + } + + .el-menu-item, .menu-title { + overflow: hidden !important; + text-overflow: ellipsis !important; + white-space: nowrap !important; + } + + .el-menu-item .el-menu-tooltip__trigger { + display: inline-block !important; + } + + // menu hover + .sub-menu-title-noDropdown, + .el-sub-menu__title { + &:hover { + background-color: rgba(0, 0, 0, 0.06) !important; + } + } + + & .theme-dark .is-active > .el-sub-menu__title { + color: $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; + + &:hover { + background-color: rgba(0, 0, 0, 0.06) !important; + } + } + + & .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; + + &:hover { + background-color: $base-sub-menu-hover !important; + } + } + } + + .hideSidebar { + .sidebar-container { + width: 54px !important; + } + + .main-container { + margin-left: 54px; + } + + .sub-menu-title-noDropdown { + padding: 0 !important; + position: relative; + + .el-tooltip { + padding: 0 !important; + + .svg-icon { + margin-left: 20px; + } + } + } + + .el-sub-menu { + overflow: hidden; + + &>.el-sub-menu__title { + padding: 0 !important; + + .svg-icon { + margin-left: 20px; + } + + } + } + + .el-menu--collapse { + .el-sub-menu { + &>.el-sub-menu__title { + &>span { + height: 0; + width: 0; + overflow: hidden; + visibility: hidden; + display: inline-block; + } + &>i { + height: 0; + width: 0; + overflow: hidden; + visibility: hidden; + display: inline-block; + } + } + } + } + } + + .el-menu--collapse .el-menu .el-sub-menu { + min-width: $base-sidebar-width !important; + } + + // mobile responsive + .mobile { + .main-container { + margin-left: 0px; + } + + .sidebar-container { + transition: transform .28s; + width: $base-sidebar-width !important; + } + + &.hideSidebar { + .sidebar-container { + pointer-events: none; + transition-duration: 0.3s; + transform: translate3d(-$base-sidebar-width, 0, 0); + } + } + } + + .withoutAnimation { + + .main-container, + .sidebar-container { + transition: none; + } + } +} + +// when menu collapsed +.el-menu--vertical { + &>.el-menu { + .svg-icon { + margin-right: 16px; + } + } + + .nest-menu .el-sub-menu>.el-sub-menu__title, + .el-menu-item { + &:hover { + // you can use $sub-menuHover + background-color: rgba(0, 0, 0, 0.06) !important; + } + } + + // the scroll bar appears when the sub-menu is too long + >.el-menu--popup { + max-height: 100vh; + overflow-y: auto; + + &::-webkit-scrollbar-track-piece { + background: #d3dce6; + } + + &::-webkit-scrollbar { + width: 6px; + } + + &::-webkit-scrollbar-thumb { + background: #99a9bf; + border-radius: 20px; + } + } +} diff --git a/src/assets/styles/transition.scss b/src/assets/styles/transition.scss new file mode 100644 index 0000000..073f8c6 --- /dev/null +++ b/src/assets/styles/transition.scss @@ -0,0 +1,49 @@ +// global transition css + +/* fade */ +.fade-enter-active, +.fade-leave-active { + transition: opacity 0.28s; +} + +.fade-enter, +.fade-leave-active { + opacity: 0; +} + +/* fade-transform */ +.fade-transform--move, +.fade-transform-leave-active, +.fade-transform-enter-active { + transition: all .5s; +} + +.fade-transform-enter { + opacity: 0; + transform: translateX(-30px); +} + +.fade-transform-leave-to { + opacity: 0; + transform: translateX(30px); +} + +/* breadcrumb transition */ +.breadcrumb-enter-active, +.breadcrumb-leave-active { + transition: all .5s; +} + +.breadcrumb-enter, +.breadcrumb-leave-active { + opacity: 0; + transform: translateX(20px); +} + +.breadcrumb-move { + transition: all .5s; +} + +.breadcrumb-leave-active { + position: absolute; +} diff --git a/src/assets/styles/variables.module.scss b/src/assets/styles/variables.module.scss new file mode 100644 index 0000000..8764e13 --- /dev/null +++ b/src/assets/styles/variables.module.scss @@ -0,0 +1,221 @@ +// base color +$blue: #324157; +$light-blue: #333c46; +$red: #C03639; +$pink: #E65D6E; +$green: #30B08F; +$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-sub-menu-background: #1f2d3d; +$base-sub-menu-hover: #001528; + +// 缁勪欢鍙橀噺 +$--color-primary: #409EFF; +$--color-success: #67C23A; +$--color-warning: #E6A23C; +$--color-danger: #F56C6C; +$--color-info: #909399; + +:export { + 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); + } + +} + diff --git a/src/components/Breadcrumb/index.vue b/src/components/Breadcrumb/index.vue new file mode 100644 index 0000000..c13e5e9 --- /dev/null +++ b/src/components/Breadcrumb/index.vue @@ -0,0 +1,98 @@ +<template> + <el-breadcrumb class="app-breadcrumb" separator="/"> + <transition-group name="breadcrumb"> + <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> + </transition-group> + </el-breadcrumb> +</template> + +<script setup> +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 = [] + 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) { + return false + } + return name.trim() === 'Index' +} +function handleLink(item) { + const { redirect, path } = item + if (redirect) { + router.push(redirect) + return + } + router.push(path) +} + +watchEffect(() => { + // if you go to the redirect page, do not update the breadcrumbs + if (route.path.startsWith('/redirect/')) { + return + } + getBreadcrumb() +}) +getBreadcrumb() +</script> + +<style lang='scss' scoped> +.app-breadcrumb.el-breadcrumb { + display: inline-block; + font-size: 14px; + line-height: 50px; + margin-left: 8px; + + .no-redirect { + color: #97a8be; + cursor: text; + } +} +</style> \ No newline at end of file diff --git a/src/components/Crontab/day.vue b/src/components/Crontab/day.vue new file mode 100644 index 0000000..39263f5 --- /dev/null +++ b/src/components/Crontab/day.vue @@ -0,0 +1,174 @@ +<template> + <el-form> + <el-form-item> + <el-radio v-model='radioValue' :value="1"> + 鏃ワ紝鍏佽鐨勯�氶厤绗, - * ? / L W] + </el-radio> + </el-form-item> + + <el-form-item> + <el-radio v-model='radioValue' :value="2"> + 涓嶆寚瀹� + </el-radio> + </el-form-item> + + <el-form-item> + <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" /> 鏃� + </el-radio> + </el-form-item> + + <el-form-item> + <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" /> 鏃ユ墽琛屼竴娆� + </el-radio> + </el-form-item> + + <el-form-item> + <el-radio v-model='radioValue' :value="5"> + 姣忔湀 + <el-input-number v-model='workday' :min="1" :max="31" /> 鍙锋渶杩戠殑閭d釜宸ヤ綔鏃� + </el-radio> + </el-form-item> + + <el-form-item> + <el-radio v-model='radioValue' :value="6"> + 鏈湀鏈�鍚庝竴澶� + </el-radio> + </el-form-item> + + <el-form-item> + <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" /> + </el-select> + </el-radio> + </el-form-item> + </el-form> +</template> +<script setup> +const emit = defineEmits(['update']) +const props = defineProps({ + cron: { + type: Object, + default: { + second: "*", + min: "*", + hour: "*", + day: "*", + month: "*", + week: "?", + year: "", + } + }, + check: { + type: Function, + default: () => { + } + } +}) +const radioValue = ref(1) +const cycle01 = ref(1) +const cycle02 = ref(2) +const average01 = ref(1) +const average02 = ref(1) +const workday = ref(1) +const checkboxList = ref([]) +const checkCopy = ref([1]) +const cycleTotal = computed(() => { + cycle01.value = props.check(cycle01.value, 1, 30) + cycle02.value = props.check(cycle02.value, cycle01.value + 1, 31) + return cycle01.value + '-' + cycle02.value +}) +const averageTotal = computed(() => { + average01.value = props.check(average01.value, 1, 30) + average02.value = props.check(average02.value, 1, 31 - average01.value) + return average01.value + '/' + average02.value +}) +const workdayTotal = computed(() => { + workday.value = props.check(workday.value, 1, 31) + return workday.value + 'W' +}) +const checkboxString = computed(() => { + return checkboxList.value.join(',') +}) +watch(() => props.cron.day, value => changeRadioValue(value)) +watch([radioValue, cycleTotal, averageTotal, workdayTotal, checkboxString], () => onRadioChange()) +function changeRadioValue(value) { + if (value === "*") { + radioValue.value = 1 + } else if (value === "?") { + radioValue.value = 2 + } else if (value.indexOf("-") > -1) { + const indexArr = value.split('-') + cycle01.value = Number(indexArr[0]) + cycle02.value = Number(indexArr[1]) + radioValue.value = 3 + } else if (value.indexOf("/") > -1) { + const indexArr = value.split('/') + average01.value = Number(indexArr[0]) + average02.value = Number(indexArr[1]) + radioValue.value = 4 + } else if (value.indexOf("W") > -1) { + const indexArr = value.split("W") + workday.value = Number(indexArr[0]) + radioValue.value = 5 + } else if (value === "L") { + radioValue.value = 6 + } else { + checkboxList.value = [...new Set(value.split(',').map(item => Number(item)))] + radioValue.value = 7 + } +} +// 鍗曢�夋寜閽�煎彉鍖栨椂 +function onRadioChange() { + if (radioValue.value === 2 && props.cron.week === '?') { + emit('update', 'week', '*', 'day') + } + if (radioValue.value !== 2 && props.cron.week !== '?') { + emit('update', 'week', '?', 'day') + } + switch (radioValue.value) { + case 1: + emit('update', 'day', '*', 'day') + break + case 2: + emit('update', 'day', '?', 'day') + break + case 3: + emit('update', 'day', cycleTotal.value, 'day') + break + case 4: + emit('update', 'day', averageTotal.value, 'day') + break + case 5: + emit('update', 'day', workdayTotal.value, 'day') + break + case 6: + emit('update', 'day', 'L', 'day') + break + case 7: + if (checkboxList.value.length === 0) { + checkboxList.value.push(checkCopy.value[0]) + } else { + checkCopy.value = checkboxList.value + } + emit('update', 'day', checkboxString.value, 'day') + break + } +} +</script> + +<style lang="scss" scoped> +.el-input-number--small, .el-select, .el-select--small { + margin: 0 0.2rem; +} +.el-select, .el-select--small { + width: 18.8rem; +} +</style> \ No newline at end of file diff --git a/src/components/Crontab/hour.vue b/src/components/Crontab/hour.vue new file mode 100644 index 0000000..db77835 --- /dev/null +++ b/src/components/Crontab/hour.vue @@ -0,0 +1,133 @@ +<template> + <el-form> + <el-form-item> + <el-radio v-model='radioValue' :value="1"> + 灏忔椂锛屽厑璁哥殑閫氶厤绗, - * /] + </el-radio> + </el-form-item> + + <el-form-item> + <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" /> 鏃� + </el-radio> + </el-form-item> + + <el-form-item> + <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" /> 灏忔椂鎵ц涓�娆� + </el-radio> + </el-form-item> + + <el-form-item> + <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" /> + </el-select> + </el-radio> + </el-form-item> + </el-form> +</template> + +<script setup> +const emit = defineEmits(['update']) +const props = defineProps({ + cron: { + type: Object, + default: { + second: "*", + min: "*", + hour: "*", + day: "*", + month: "*", + week: "?", + year: "", + } + }, + check: { + type: Function, + default: () => { + } + } +}) +const radioValue = ref(1) +const cycle01 = ref(0) +const cycle02 = ref(1) +const average01 = ref(0) +const average02 = ref(1) +const checkboxList = ref([]) +const checkCopy = ref([0]) +const cycleTotal = computed(() => { + cycle01.value = props.check(cycle01.value, 0, 22) + cycle02.value = props.check(cycle02.value, cycle01.value + 1, 23) + return cycle01.value + '-' + cycle02.value +}) +const averageTotal = computed(() => { + average01.value = props.check(average01.value, 0, 22) + average02.value = props.check(average02.value, 1, 23 - average01.value) + return average01.value + '/' + average02.value +}) +const checkboxString = computed(() => { + return checkboxList.value.join(',') +}) +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) { + const indexArr = value.split('-') + cycle01.value = Number(indexArr[0]) + cycle02.value = Number(indexArr[1]) + radioValue.value = 2 + } else if (value.indexOf('/') > -1) { + const indexArr = value.split('/') + average01.value = Number(indexArr[0]) + average02.value = Number(indexArr[1]) + radioValue.value = 3 + } else { + checkboxList.value = [...new Set(value.split(',').map(item => Number(item)))] + radioValue.value = 4 + } +} +function onRadioChange() { + switch (radioValue.value) { + case 1: + emit('update', 'hour', '*', 'hour') + break + case 2: + emit('update', 'hour', cycleTotal.value, 'hour') + break + case 3: + emit('update', 'hour', averageTotal.value, 'hour') + break + case 4: + if (checkboxList.value.length === 0) { + checkboxList.value.push(checkCopy.value[0]) + } else { + checkCopy.value = checkboxList.value + } + emit('update', 'hour', checkboxString.value, 'hour') + break + } +} +</script> + +<style lang="scss" scoped> +.el-input-number--small, .el-select, .el-select--small { + margin: 0 0.2rem; +} +.el-select, .el-select--small { + width: 18.8rem; +} +</style> \ No newline at end of file diff --git a/src/components/Crontab/index.vue b/src/components/Crontab/index.vue new file mode 100644 index 0000000..103cf4c --- /dev/null +++ b/src/components/Crontab/index.vue @@ -0,0 +1,309 @@ +<template> + <div> + <el-tabs type="border-card"> + <el-tab-pane label="绉�" v-if="shouldHide('second')"> + <CrontabSecond + @update="updateCrontabValue" + :check="checkNumber" + :cron="crontabValueObj" + ref="cronsecond" + /> + </el-tab-pane> + + <el-tab-pane label="鍒嗛挓" v-if="shouldHide('min')"> + <CrontabMin + @update="updateCrontabValue" + :check="checkNumber" + :cron="crontabValueObj" + ref="cronmin" + /> + </el-tab-pane> + + <el-tab-pane label="灏忔椂" v-if="shouldHide('hour')"> + <CrontabHour + @update="updateCrontabValue" + :check="checkNumber" + :cron="crontabValueObj" + ref="cronhour" + /> + </el-tab-pane> + + <el-tab-pane label="鏃�" v-if="shouldHide('day')"> + <CrontabDay + @update="updateCrontabValue" + :check="checkNumber" + :cron="crontabValueObj" + ref="cronday" + /> + </el-tab-pane> + + <el-tab-pane label="鏈�" v-if="shouldHide('month')"> + <CrontabMonth + @update="updateCrontabValue" + :check="checkNumber" + :cron="crontabValueObj" + ref="cronmonth" + /> + </el-tab-pane> + + <el-tab-pane label="鍛�" v-if="shouldHide('week')"> + <CrontabWeek + @update="updateCrontabValue" + :check="checkNumber" + :cron="crontabValueObj" + ref="cronweek" + /> + </el-tab-pane> + + <el-tab-pane label="骞�" v-if="shouldHide('year')"> + <CrontabYear + @update="updateCrontabValue" + :check="checkNumber" + :cron="crontabValueObj" + ref="cronyear" + /> + </el-tab-pane> + </el-tabs> + + <div class="popup-main"> + <div class="popup-result"> + <p class="title">鏃堕棿琛ㄨ揪寮�</p> + <table> + <thead> + <th v-for="item of tabTitles" :key="item">{{item}}</th> + <th>Cron 琛ㄨ揪寮�</th> + </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> + </tbody> + </table> + </div> + <CrontabResult :ex="crontabValueString"></CrontabResult> + + <div class="pop_btn"> + <el-button type="primary" @click="submitFill">纭畾</el-button> + <el-button type="warning" @click="clearCron">閲嶇疆</el-button> + <el-button @click="hidePopup">鍙栨秷</el-button> + </div> + </div> + </div> +</template> + +<script setup> +import CrontabSecond from "./second.vue" +import CrontabMin from "./min.vue" +import CrontabHour from "./hour.vue" +import CrontabDay from "./day.vue" +import CrontabMonth from "./month.vue" +import CrontabWeek from "./week.vue" +import CrontabYear from "./year.vue" +import CrontabResult from "./result.vue" +const { proxy } = getCurrentInstance() +const emit = defineEmits(['hide', 'fill']) +const props = defineProps({ + hideComponent: { + type: Array, + default: () => [], + }, + expression: { + type: String, + default: "" + } +}) +const tabTitles = ref(["绉�", "鍒嗛挓", "灏忔椂", "鏃�", "鏈�", "鍛�", "骞�"]) +const tabActive = ref(0) +const hideComponent = ref([]) +const expression = ref('') +const crontabValueObj = ref({ + second: "*", + min: "*", + hour: "*", + day: "*", + month: "*", + week: "?", + year: "", +}) +const crontabValueString = computed(() => { + const obj = crontabValueObj.value + return obj.second + + " " + + obj.min + + " " + + obj.hour + + " " + + obj.day + + " " + + obj.month + + " " + + obj.week + + (obj.year === "" ? "" : " " + obj.year) +}) +watch(expression, () => resolveExp()) +function shouldHide(key) { + return !(hideComponent.value && hideComponent.value.includes(key)) +} +function resolveExp() { + // 鍙嶈В鏋� 琛ㄨ揪寮� + if (expression.value) { + const arr = expression.value.split(/\s+/) + if (arr.length >= 6) { + //6 浣嶄互涓婃槸鍚堟硶琛ㄨ揪寮� + let obj = { + second: arr[0], + min: arr[1], + hour: arr[2], + day: arr[3], + month: arr[4], + week: arr[5], + year: arr[6] ? arr[6] : "" + } + crontabValueObj.value = { + ...obj, + } + } + } else { + // 娌℃湁浼犲叆鐨勮〃杈惧紡 鍒欒繕鍘� + clearCron() + } +} +// tab鍒囨崲鍊� +function tabCheck(index) { + tabActive.value = index +} +// 鐢卞瓙缁勪欢瑙﹀彂锛屾洿鏀硅〃杈惧紡缁勬垚鐨勫瓧娈靛�� +function updateCrontabValue(name, value, from) { + crontabValueObj.value[name] = value +} +// 琛ㄥ崟閫夐」鐨勫瓙缁勪欢鏍¢獙鏁板瓧鏍煎紡锛堥�氳繃-props浼犻�掞級 +function checkNumber(value, minLimit, maxLimit) { + // 妫�鏌ュ繀椤讳负鏁存暟 + value = Math.floor(value) + if (value < minLimit) { + value = minLimit + } else if (value > maxLimit) { + value = maxLimit + } + return value +} +// 闅愯棌寮圭獥 +function hidePopup() { + emit("hide") +} +// 濉厖琛ㄨ揪寮� +function submitFill() { + emit("fill", crontabValueString.value) + hidePopup() +} +function clearCron() { + // 杩樺師閫夋嫨椤� + crontabValueObj.value = { + second: "*", + min: "*", + hour: "*", + day: "*", + month: "*", + week: "?", + year: "", + } +} +onMounted(() => { + expression.value = props.expression + hideComponent.value = props.hideComponent +}) +</script> + +<style lang="scss" scoped> +.pop_btn { + text-align: center; + margin-top: 20px; +} +.popup-main { + position: relative; + margin: 10px auto; + border-radius: 5px; + font-size: 12px; + overflow: hidden; +} +.popup-title { + overflow: hidden; + line-height: 34px; + padding-top: 6px; + background: #f2f2f2; +} +.popup-result { + box-sizing: border-box; + line-height: 24px; + margin: 25px auto; + padding: 15px 10px 10px; + border: 1px solid #ccc; + position: relative; +} +.popup-result .title { + position: absolute; + top: -28px; + left: 50%; + width: 140px; + font-size: 14px; + margin-left: -70px; + text-align: center; + line-height: 30px; + background: #fff; +} +.popup-result table { + text-align: center; + width: 100%; + margin: 0 auto; +} +.popup-result table td:not(.result) { + width: 3.5rem; + min-width: 3.5rem; + max-width: 3.5rem; +} +.popup-result table span { + display: block; + width: 100%; + font-family: arial; + line-height: 30px; + height: 30px; + white-space: nowrap; + overflow: hidden; + border: 1px solid #e8e8e8; +} +.popup-result-scroll { + font-size: 12px; + line-height: 24px; + height: 10em; + overflow-y: auto; +} +</style> \ No newline at end of file diff --git a/src/components/Crontab/min.vue b/src/components/Crontab/min.vue new file mode 100644 index 0000000..65f0946 --- /dev/null +++ b/src/components/Crontab/min.vue @@ -0,0 +1,126 @@ +<template> + <el-form> + <el-form-item> + <el-radio v-model='radioValue' :value="1"> + 鍒嗛挓锛屽厑璁哥殑閫氶厤绗, - * /] + </el-radio> + </el-form-item> + + <el-form-item> + <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" /> 鍒嗛挓 + </el-radio> + </el-form-item> + + <el-form-item> + <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" /> 鍒嗛挓鎵ц涓�娆� + </el-radio> + </el-form-item> + + <el-form-item> + <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" /> + </el-select> + </el-radio> + </el-form-item> + </el-form> +</template> +<script setup> +const emit = defineEmits(['update']) +const props = defineProps({ + cron: { + type: Object, + default: { + second: "*", + min: "*", + hour: "*", + day: "*", + month: "*", + week: "?", + year: "", + } + }, + check: { + type: Function, + default: () => { + } + } +}) +const radioValue = ref(1) +const cycle01 = ref(0) +const cycle02 = ref(1) +const average01 = ref(0) +const average02 = ref(1) +const checkboxList = ref([]) +const checkCopy = ref([0]) +const cycleTotal = computed(() => { + cycle01.value = props.check(cycle01.value, 0, 58) + cycle02.value = props.check(cycle02.value, cycle01.value + 1, 59) + return cycle01.value + '-' + cycle02.value +}) +const averageTotal = computed(() => { + average01.value = props.check(average01.value, 0, 58) + average02.value = props.check(average02.value, 1, 59 - average01.value) + return average01.value + '/' + average02.value +}) +const checkboxString = computed(() => { + return checkboxList.value.join(',') +}) +watch(() => props.cron.min, value => changeRadioValue(value)) +watch([radioValue, cycleTotal, averageTotal, checkboxString], () => onRadioChange()) +function changeRadioValue(value) { + if (value === '*') { + radioValue.value = 1 + } else if (value.indexOf('-') > -1) { + const indexArr = value.split('-') + cycle01.value = Number(indexArr[0]) + cycle02.value = Number(indexArr[1]) + radioValue.value = 2 + } else if (value.indexOf('/') > -1) { + const indexArr = value.split('/') + average01.value = Number(indexArr[0]) + average02.value = Number(indexArr[1]) + radioValue.value = 3 + } else { + checkboxList.value = [...new Set(value.split(',').map(item => Number(item)))] + radioValue.value = 4 + } +} +function onRadioChange() { + switch (radioValue.value) { + case 1: + emit('update', 'min', '*', 'min') + break + case 2: + emit('update', 'min', cycleTotal.value, 'min') + break + case 3: + emit('update', 'min', averageTotal.value, 'min') + break + case 4: + if (checkboxList.value.length === 0) { + checkboxList.value.push(checkCopy.value[0]) + } else { + checkCopy.value = checkboxList.value + } + emit('update', 'min', checkboxString.value, 'min') + break + } +} +</script> + +<style lang="scss" scoped> +.el-input-number--small, .el-select, .el-select--small { + margin: 0 0.2rem; +} +.el-select, .el-select--small { + width: 19.8rem; +} +</style> \ No newline at end of file diff --git a/src/components/Crontab/month.vue b/src/components/Crontab/month.vue new file mode 100644 index 0000000..e561ba4 --- /dev/null +++ b/src/components/Crontab/month.vue @@ -0,0 +1,141 @@ +<template> + <el-form> + <el-form-item> + <el-radio v-model='radioValue' :value="1"> + 鏈堬紝鍏佽鐨勯�氶厤绗, - * /] + </el-radio> + </el-form-item> + + <el-form-item> + <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" /> 鏈� + </el-radio> + </el-form-item> + + <el-form-item> + <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" /> 鏈堟湀鎵ц涓�娆� + </el-radio> + </el-form-item> + + <el-form-item> + <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" /> + </el-select> + </el-radio> + </el-form-item> + </el-form> +</template> + +<script setup> +const emit = defineEmits(['update']) +const props = defineProps({ + cron: { + type: Object, + default: { + second: "*", + min: "*", + hour: "*", + day: "*", + month: "*", + week: "?", + year: "", + } + }, + check: { + type: Function, + default: () => { + } + } +}) +const radioValue = ref(1) +const cycle01 = ref(1) +const cycle02 = ref(2) +const average01 = ref(1) +const average02 = ref(1) +const checkboxList = ref([]) +const checkCopy = ref([1]) +const monthList = ref([ + {key: 1, value: '涓�鏈�'}, + {key: 2, value: '浜屾湀'}, + {key: 3, value: '涓夋湀'}, + {key: 4, value: '鍥涙湀'}, + {key: 5, value: '浜旀湀'}, + {key: 6, value: '鍏湀'}, + {key: 7, value: '涓冩湀'}, + {key: 8, value: '鍏湀'}, + {key: 9, value: '涔濇湀'}, + {key: 10, value: '鍗佹湀'}, + {key: 11, value: '鍗佷竴鏈�'}, + {key: 12, value: '鍗佷簩鏈�'} +]) +const cycleTotal = computed(() => { + cycle01.value = props.check(cycle01.value, 1, 11) + cycle02.value = props.check(cycle02.value, cycle01.value + 1, 12) + return cycle01.value + '-' + cycle02.value +}) +const averageTotal = computed(() => { + average01.value = props.check(average01.value, 1, 11) + average02.value = props.check(average02.value, 1, 12 - average01.value) + return average01.value + '/' + average02.value +}) +const checkboxString = computed(() => { + return checkboxList.value.join(',') +}) +watch(() => props.cron.month, value => changeRadioValue(value)) +watch([radioValue, cycleTotal, averageTotal, checkboxString], () => onRadioChange()) +function changeRadioValue(value) { + if (value === '*') { + radioValue.value = 1 + } else if (value.indexOf('-') > -1) { + const indexArr = value.split('-') + cycle01.value = Number(indexArr[0]) + cycle02.value = Number(indexArr[1]) + radioValue.value = 2 + } else if (value.indexOf('/') > -1) { + const indexArr = value.split('/') + average01.value = Number(indexArr[0]) + average02.value = Number(indexArr[1]) + radioValue.value = 3 + } else { + checkboxList.value = [...new Set(value.split(',').map(item => Number(item)))] + radioValue.value = 4 + } +} +function onRadioChange() { + switch (radioValue.value) { + case 1: + emit('update', 'month', '*', 'month') + break + case 2: + emit('update', 'month', cycleTotal.value, 'month') + break + case 3: + emit('update', 'month', averageTotal.value, 'month') + break + case 4: + if (checkboxList.value.length === 0) { + checkboxList.value.push(checkCopy.value[0]) + } else { + checkCopy.value = checkboxList.value + } + emit('update', 'month', checkboxString.value, 'month') + break + } +} +</script> + +<style lang="scss" scoped> +.el-input-number--small, .el-select, .el-select--small { + margin: 0 0.2rem; +} +.el-select, .el-select--small { + width: 18.8rem; +} +</style> \ No newline at end of file diff --git a/src/components/Crontab/result.vue b/src/components/Crontab/result.vue new file mode 100644 index 0000000..cbb49ba --- /dev/null +++ b/src/components/Crontab/result.vue @@ -0,0 +1,540 @@ +<template> + <div class="popup-result"> + <p class="title">鏈�杩�5娆¤繍琛屾椂闂�</p> + <ul class="popup-result-scroll"> + <template v-if='isShow'> + <li v-for='item in resultList' :key="item">{{item}}</li> + </template> + <li v-else>璁$畻缁撴灉涓�...</li> + </ul> + </div> +</template> + +<script setup> +const props = defineProps({ + ex: { + type: String, + default: '' + } +}) +const dayRule = ref('') +const dayRuleSup = ref('') +const dateArr = ref([]) +const resultList = ref([]) +const isShow = ref(false) +watch(() => props.ex, () => expressionChange()) +// 琛ㄨ揪寮忓�煎彉鍖栨椂锛屽紑濮嬪幓璁$畻缁撴灉 +function expressionChange() { + // 璁$畻寮�濮�-闅愯棌缁撴灉 + isShow.value = false + // 鑾峰彇瑙勫垯鏁扮粍[0绉掋��1鍒嗐��2鏃躲��3鏃ャ��4鏈堛��5鏄熸湡銆�6骞碷 + let ruleArr = props.ex.split(' ') + // 鐢ㄤ簬璁板綍杩涘叆寰幆鐨勬鏁� + let nums = 0 + // 鐢ㄤ簬鏆傛椂瀛樼鍙锋椂闂磋鍒欑粨鏋滅殑鏁扮粍 + 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() + // 鏍规嵁瑙勫垯鑾峰彇鍒拌繎100骞村彲鑳藉勾鏁扮粍銆佹湀鏁扮粍绛夌瓑 + 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 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 + nSecond = sDate[sIdx] + } + const resetMin = function () { + mIdx = 0 + nMin = mDate[mIdx] + resetSecond() + } + const resetHour = function () { + hIdx = 0 + nHour = hDate[hIdx] + resetMin() + } + const resetDay = function () { + DIdx = 0 + nDay = DDate[DIdx] + resetHour() + } + const resetMonth = function () { + MIdx = 0 + nMonth = MDate[MIdx] + resetDay() + } + // 濡傛灉褰撳墠骞翠唤涓嶄负鏁扮粍涓綋鍓嶅�� + if (nYear !== YDate[YIdx]) { + resetMonth() + } + // 濡傛灉褰撳墠鏈堜唤涓嶄负鏁扮粍涓綋鍓嶅�� + if (nMonth !== MDate[MIdx]) { + resetDay() + } + // 濡傛灉褰撳墠鈥滄棩鈥濅笉涓烘暟缁勪腑褰撳墠鍊� + if (nDay !== DDate[DIdx]) { + resetHour() + } + // 濡傛灉褰撳墠鈥滄椂鈥濅笉涓烘暟缁勪腑褰撳墠鍊� + if (nHour !== hDate[hIdx]) { + resetMin() + } + // 濡傛灉褰撳墠鈥滃垎鈥濅笉涓烘暟缁勪腑褰撳墠鍊� + if (nMin !== mDate[mIdx]) { + resetSecond() + } + // 寰幆骞翠唤鏁扮粍 + goYear: for (let Yi = YIdx; Yi < YDate.length; Yi++) { + let YY = YDate[Yi] + // 濡傛灉鍒拌揪鏈�澶у�兼椂 + if (nMonth > MDate[MDate.length - 1]) { + resetMonth() + continue + } + // 寰幆鏈堜唤鏁扮粍 + goMonth: for (let Mi = MIdx; Mi < MDate.length; Mi++) { + // 璧嬪�笺�佹柟渚垮悗闈㈣繍绠� + let MM = MDate[Mi]; + MM = MM < 10 ? '0' + MM : MM + // 濡傛灉鍒拌揪鏈�澶у�兼椂 + if (nDay > DDate[DDate.length - 1]) { + resetDay() + if (Mi === MDate.length - 1) { + resetMonth() + continue goYear + } + continue + } + // 寰幆鏃ユ湡鏁扮粍 + goDay: for (let Di = DIdx; Di < DDate.length; Di++) { + // 璧嬪�笺�佹柟渚垮悗闈㈣繍绠� + let DD = DDate[Di] + let thisDD = DD < 10 ? '0' + DD : DD + // 濡傛灉鍒拌揪鏈�澶у�兼椂 + if (nHour > hDate[hDate.length - 1]) { + resetHour() + if (Di === DDate.length - 1) { + resetDay() + if (Mi === MDate.length - 1) { + resetMonth() + continue goYear + } + continue goMonth + } + continue + } + // 鍒ゆ柇鏃ユ湡鐨勫悎娉曟�э紝涓嶅悎娉曠殑璇濅篃鏄烦鍑哄綋鍓嶅惊鐜� + if (checkDate(YY + '-' + MM + '-' + thisDD + ' 00:00:00') !== true && dayRule.value !== 'workDay' && dayRule.value !== 'lastWeek' && dayRule.value !== 'lastDay') { + 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 + } + } + } 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 + } + } + // 鑾峰彇杈惧埌鏉′欢鐨勬棩鏈熸槸鏄熸湡X + let thisWeek = formatDate(new Date(YY + '-' + MM + '-' + thisDD + ' 00:00:00'), 'week') + // 褰撴槦鏈熸棩鏃� + if (thisWeek === 1) { + // 鍏堟壘涓嬩竴涓棩锛屽苟鍒ゆ柇鏄惁涓烘湀搴� + DD++ + thisDD = DD < 10 ? '0' + DD : DD + // 鍒ゆ柇涓嬩竴鏃ュ凡缁忎笉鏄悎娉曟棩鏈� + if (checkDate(YY + '-' + MM + '-' + thisDD + ' 00:00:00') !== true) { + DD -= 3 + } + } else if (thisWeek === 7) { + // 褰撴槦鏈�6鏃跺彧闇�鍒ゆ柇涓嶆槸1鍙峰氨鍙繘琛屾搷浣� + if (dayRuleSup.value !== 1) { + DD-- + } else { + DD += 2 + } + } + } else if (dayRule.value === 'weekDay') { + // 濡傛灉鎸囧畾浜嗘槸鏄熸湡鍑� + // 鑾峰彇褰撳墠鏃ユ湡鏄睘浜庢槦鏈熷嚑 + let thisWeek = formatDate(new Date(YY + '-' + MM + '-' + DD + ' 00:00:00'), 'week') + // 鏍¢獙褰撳墠鏄熸湡鏄惁鍦ㄦ槦鏈熸睜锛坉ayRuleSup锛変腑 + if (dayRuleSup.value.indexOf(thisWeek) < 0) { + // 濡傛灉鍒拌揪鏈�澶у�兼椂 + if (Di === DDate.length - 1) { + resetDay() + if (Mi === MDate.length - 1) { + resetMonth() + continue goYear + } + continue goMonth + } + continue + } + } else if (dayRule.value === 'assWeek') { + // 濡傛灉鎸囧畾浜嗘槸绗嚑鍛ㄧ殑鏄熸湡鍑� + // 鑾峰彇姣忔湀1鍙锋槸灞炰簬鏄熸湡鍑� + 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 + } else { + 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 + } + } + // 鑾峰彇鏈堟湯鏈�鍚庝竴澶╂槸鏄熸湡鍑� + let thisWeek = formatDate(new Date(YY + '-' + MM + '-' + thisDD + ' 00:00:00'), 'week') + // 鎵惧埌瑕佹眰涓渶杩戠殑閭d釜鏄熸湡鍑� + if (dayRuleSup.value < thisWeek) { + DD -= thisWeek - dayRuleSup.value + } else if (dayRuleSup.value > thisWeek) { + DD -= 7 - (dayRuleSup.value - thisWeek) + } + } + // 鍒ゆ柇鏃堕棿鍊兼槸鍚﹀皬浜�10缃崲鎴愨��05鈥濊繖绉嶆牸寮� + 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() + if (hi === hDate.length - 1) { + resetHour() + if (Di === DDate.length - 1) { + resetDay() + if (Mi === MDate.length - 1) { + resetMonth() + continue goYear + } + continue goMonth + } + continue goDay + } + continue + } + // 寰幆"鍒�"鏁扮粍 + goMin: for (let mi = mIdx; mi < mDate.length; mi++) { + let mm = mDate[mi] < 10 ? '0' + mDate[mi] : mDate[mi] + // 濡傛灉鍒拌揪鏈�澶у�兼椂 + if (nSecond > sDate[sDate.length - 1]) { + resetSecond() + if (mi === mDate.length - 1) { + resetMin() + if (hi === hDate.length - 1) { + resetHour() + if (Di === DDate.length - 1) { + resetDay() + if (Mi === MDate.length - 1) { + resetMonth() + continue goYear + } + continue goMonth + } + continue goDay + } + continue goHour + } + continue + } + // 寰幆"绉�"鏁扮粍 + goSecond: for (let si = sIdx; si <= sDate.length - 1; 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++ + } + // 濡傛灉鏉℃暟婊′簡灏遍��鍑哄惊鐜� + if (nums === 5) break goYear + // 濡傛灉鍒拌揪鏈�澶у�兼椂 + if (si === sDate.length - 1) { + resetSecond() + if (mi === mDate.length - 1) { + resetMin() + if (hi === hDate.length - 1) { + resetHour() + if (Di === DDate.length - 1) { + resetDay() + if (Mi === MDate.length - 1) { + resetMonth() + continue goYear + } + continue goMonth + } + continue goDay + } + continue goHour + } + continue goMin + } + } //goSecond + } //goMin + }//goHour + }//goDay + }//goMonth + } + // 鍒ゆ柇100骞村唴鐨勭粨鏋滄潯鏁� + if (resultArr.length === 0) { + resultList.value = ['娌℃湁杈惧埌鏉′欢鐨勭粨鏋滐紒'] + } else { + resultList.value = resultArr + if (resultArr.length !== 5) { + resultList.value.push('鏈�杩�100骞村唴鍙湁涓婇潰' + resultArr.length + '鏉$粨鏋滐紒') + } + } + // 璁$畻瀹屾垚-鏄剧ず缁撴灉 + isShow.value = true +} +// 鐢ㄤ簬璁$畻鏌愪綅鏁板瓧鍦ㄦ暟缁勪腑鐨勭储寮� +function getIndex(arr, value) { + if (value <= arr[0] || value > arr[arr.length - 1]) { + return 0 + } else { + for (let i = 0; i < arr.length - 1; i++) { + if (value > arr[i] && value <= arr[i + 1]) { + return i + 1 + } + } + } +} +// 鑾峰彇"骞�"鏁扮粍 +function getYearArr(rule, year) { + dateArr.value[5] = getOrderArr(year, year + 100) + if (rule !== undefined) { + if (rule.indexOf('-') >= 0) { + dateArr.value[5] = getCycleArr(rule, year + 100, false) + } else if (rule.indexOf('/') >= 0) { + dateArr.value[5] = getAverageArr(rule, year + 100) + } else if (rule !== '*') { + dateArr.value[5] = getAssignArr(rule) + } + } +} +// 鑾峰彇"鏈�"鏁扮粍 +function getMonthArr(rule) { + dateArr.value[4] = getOrderArr(1, 12) + if (rule.indexOf('-') >= 0) { + dateArr.value[4] = getCycleArr(rule, 12, false) + } else if (rule.indexOf('/') >= 0) { + dateArr.value[4] = getAverageArr(rule, 12) + } else if (rule !== '*') { + dateArr.value[4] = getAssignArr(rule) + } +} +// 鑾峰彇"鏃�"鏁扮粍-涓昏涓烘棩鏈熻鍒� +function getWeekArr(rule) { + // 鍙湁褰撴棩鏈熻鍒欑殑涓や釜鍊煎潎涓衡�溾�濇椂鍒欒〃杈炬棩鏈熸槸鏈夐�夐」鐨� + if (dayRule.value === '' && dayRuleSup.value === '') { + if (rule.indexOf('-') >= 0) { + 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] + if (dayRuleSup.value[1] === 7) { + 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] + if (dayRuleSup.value === 7) { + dayRuleSup.value = 0 + } + } else if (rule !== '*' && rule !== '?') { + dayRule.value = 'weekDay' + dayRuleSup.value = getAssignArr(rule) + } + } +} +// 鑾峰彇"鏃�"鏁扮粍-灏戦噺涓烘棩鏈熻鍒� +function getDayArr(rule) { + dateArr.value[3] = getOrderArr(1, 31) + dayRule.value = '' + dayRuleSup.value = '' + if (rule.indexOf('-') >= 0) { + dateArr.value[3] = getCycleArr(rule, 31, false) + dayRuleSup.value = 'null' + } else if (rule.indexOf('/') >= 0) { + dateArr.value[3] = getAverageArr(rule, 31) + 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] + } else if (rule.indexOf('L') >= 0) { + dayRule.value = 'lastDay' + dayRuleSup.value = 'null' + dateArr.value[3] = [31] + } else if (rule !== '*' && rule !== '?') { + dateArr.value[3] = getAssignArr(rule) + dayRuleSup.value = 'null' + } else if (rule === '*') { + dayRuleSup.value = 'null' + } +} +// 鑾峰彇"鏃�"鏁扮粍 +function getHourArr(rule) { + dateArr.value[2] = getOrderArr(0, 23) + if (rule.indexOf('-') >= 0) { + dateArr.value[2] = getCycleArr(rule, 24, true) + } else if (rule.indexOf('/') >= 0) { + dateArr.value[2] = getAverageArr(rule, 23) + } else if (rule !== '*') { + dateArr.value[2] = getAssignArr(rule) + } +} +// 鑾峰彇"鍒�"鏁扮粍 +function getMinArr(rule) { + dateArr.value[1] = getOrderArr(0, 59) + if (rule.indexOf('-') >= 0) { + dateArr.value[1] = getCycleArr(rule, 60, true) + } else if (rule.indexOf('/') >= 0) { + dateArr.value[1] = getAverageArr(rule, 59) + } else if (rule !== '*') { + dateArr.value[1] = getAssignArr(rule) + } +} +// 鑾峰彇"绉�"鏁扮粍 +function getSecondArr(rule) { + dateArr.value[0] = getOrderArr(0, 59) + if (rule.indexOf('-') >= 0) { + dateArr.value[0] = getCycleArr(rule, 60, true) + } else if (rule.indexOf('/') >= 0) { + dateArr.value[0] = getAverageArr(rule, 59) + } else if (rule !== '*') { + dateArr.value[0] = getAssignArr(rule) + } +} +// 鏍规嵁浼犺繘鏉ョ殑min-max杩斿洖涓�涓『搴忕殑鏁扮粍 +function getOrderArr(min, max) { + let arr = [] + for (let i = min; i <= max; i++) { + arr.push(i) + } + return arr +} +// 鏍规嵁瑙勫垯涓寚瀹氱殑闆舵暎鍊艰繑鍥炰竴涓暟缁� +function getAssignArr(rule) { + let arr = [] + let assiginArr = rule.split(',') + for (let i = 0; i < assiginArr.length; i++) { + arr[i] = Number(assiginArr[i]) + } + arr.sort(compare) + return arr +} +// 鏍规嵁涓�瀹氱畻鏈鍒欒绠楄繑鍥炰竴涓暟缁� +function getAverageArr(rule, limit) { + let arr = [] + let agArr = rule.split('/') + let min = Number(agArr[0]) + let step = Number(agArr[1]) + while (min <= limit) { + arr.push(min) + min += step + } + 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]) + if (min > max) { + max += limit + } + for (let i = min; i <= max; i++) { + let add = 0 + if (status === false && i % limit === 0) { + add = limit + } + arr.push(Math.round(i % limit + add)) + } + arr.sort(compare) + return arr +} +// 姣旇緝鏁板瓧澶у皬锛堢敤浜嶢rray.sort锛� +function compare(value1, value2) { + if (value2 - value1 > 0) { + return -1 + } else { + 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() + // 濡傛灉浼犻�掍簡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) + } else if (type === 'week') { + // 鍦╭uartz涓� 1涓烘槦鏈熸棩 + return week + 1 + } +} +// 妫�鏌ユ棩鏈熸槸鍚﹀瓨鍦� +function checkDate(value) { + let time = new Date(value) + let format = formatDate(time) + return value === format +} +onMounted(() => { + expressionChange() +}) +</script> \ No newline at end of file diff --git a/src/components/Crontab/second.vue b/src/components/Crontab/second.vue new file mode 100644 index 0000000..15e47a0 --- /dev/null +++ b/src/components/Crontab/second.vue @@ -0,0 +1,128 @@ +<template> + <el-form> + <el-form-item> + <el-radio v-model='radioValue' :value="1"> + 绉掞紝鍏佽鐨勯�氶厤绗, - * /] + </el-radio> + </el-form-item> + + <el-form-item> + <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" /> 绉� + </el-radio> + </el-form-item> + + <el-form-item> + <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" /> 绉掓墽琛屼竴娆� + </el-radio> + </el-form-item> + + <el-form-item> + <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" /> + </el-select> + </el-radio> + </el-form-item> + </el-form> +</template> + +<script setup> +const emit = defineEmits(['update']) +const props = defineProps({ + cron: { + type: Object, + default: { + second: "*", + min: "*", + hour: "*", + day: "*", + month: "*", + week: "?", + year: "", + } + }, + check: { + type: Function, + default: () => { + } + } +}) +const radioValue = ref(1) +const cycle01 = ref(0) +const cycle02 = ref(1) +const average01 = ref(0) +const average02 = ref(1) +const checkboxList = ref([]) +const checkCopy = ref([0]) +const cycleTotal = computed(() => { + cycle01.value = props.check(cycle01.value, 0, 58) + cycle02.value = props.check(cycle02.value, cycle01.value + 1, 59) + return cycle01.value + '-' + cycle02.value +}) +const averageTotal = computed(() => { + average01.value = props.check(average01.value, 0, 58) + average02.value = props.check(average02.value, 1, 59 - average01.value) + return average01.value + '/' + average02.value +}) +const checkboxString = computed(() => { + return checkboxList.value.join(',') +}) +watch(() => props.cron.second, value => changeRadioValue(value)) +watch([radioValue, cycleTotal, averageTotal, checkboxString], () => onRadioChange()) +function changeRadioValue(value) { + if (value === '*') { + radioValue.value = 1 + } else if (value.indexOf('-') > -1) { + const indexArr = value.split('-') + cycle01.value = Number(indexArr[0]) + cycle02.value = Number(indexArr[1]) + radioValue.value = 2 + } else if (value.indexOf('/') > -1) { + const indexArr = value.split('/') + average01.value = Number(indexArr[0]) + average02.value = Number(indexArr[1]) + radioValue.value = 3 + } else { + checkboxList.value = [...new Set(value.split(',').map(item => Number(item)))] + radioValue.value = 4 + } +} +// 鍗曢�夋寜閽�煎彉鍖栨椂 +function onRadioChange() { + switch (radioValue.value) { + case 1: + emit('update', 'second', '*', 'second') + break + case 2: + emit('update', 'second', cycleTotal.value, 'second') + break + case 3: + emit('update', 'second', averageTotal.value, 'second') + break + case 4: + if (checkboxList.value.length === 0) { + checkboxList.value.push(checkCopy.value[0]) + } else { + checkCopy.value = checkboxList.value + } + emit('update', 'second', checkboxString.value, 'second') + break + } +} +</script> + +<style lang="scss" scoped> +.el-input-number--small, .el-select, .el-select--small { + margin: 0 0.2rem; +} +.el-select, .el-select--small { + width: 18.8rem; +} +</style> \ No newline at end of file diff --git a/src/components/Crontab/week.vue b/src/components/Crontab/week.vue new file mode 100644 index 0000000..e59da22 --- /dev/null +++ b/src/components/Crontab/week.vue @@ -0,0 +1,197 @@ +<template> + <el-form> + <el-form-item> + <el-radio v-model='radioValue' :value="1"> + 鍛紝鍏佽鐨勯�氶厤绗, - * ? / L #] + </el-radio> + </el-form-item> + + <el-form-item> + <el-radio v-model='radioValue' :value="2"> + 涓嶆寚瀹� + </el-radio> + </el-form-item> + + <el-form-item> + <el-radio v-model='radioValue' :value="3"> + 鍛ㄦ湡浠� + <el-select clearable v-model="cycle01"> + <el-option + v-for="(item,index) of weekList" + :key="index" + :label="item.value" + :value="item.key" + :disabled="item.key === 7" + >{{item.value}}</el-option> + </el-select> + - + <el-select clearable v-model="cycle02"> + <el-option + v-for="(item,index) of weekList" + :key="index" + :label="item.value" + :value="item.key" + :disabled="item.key <= cycle01" + >{{item.value}}</el-option> + </el-select> + </el-radio> + </el-form-item> + + <el-form-item> + <el-radio v-model='radioValue' :value="4"> + 绗� + <el-input-number v-model='average01' :min="1" :max="4" /> 鍛ㄧ殑 + <el-select clearable v-model="average02"> + <el-option v-for="item in weekList" :key="item.key" :label="item.value" :value="item.key" /> + </el-select> + </el-radio> + </el-form-item> + + <el-form-item> + <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" /> + </el-select> + </el-radio> + </el-form-item> + + <el-form-item> + <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" /> + </el-select> + </el-radio> + </el-form-item> + + </el-form> +</template> + +<script setup> +const emit = defineEmits(['update']) +const props = defineProps({ + cron: { + type: Object, + default: { + second: "*", + min: "*", + hour: "*", + day: "*", + month: "*", + week: "?", + year: "" + } + }, + check: { + type: Function, + default: () => { + } + } +}) +const radioValue = ref(2) +const cycle01 = ref(2) +const cycle02 = ref(3) +const average01 = ref(1) +const average02 = ref(2) +const weekday = ref(2) +const checkboxList = ref([]) +const checkCopy = ref([2]) +const weekList = ref([ + {key: 1, value: '鏄熸湡鏃�'}, + {key: 2, value: '鏄熸湡涓�'}, + {key: 3, value: '鏄熸湡浜�'}, + {key: 4, value: '鏄熸湡涓�'}, + {key: 5, value: '鏄熸湡鍥�'}, + {key: 6, value: '鏄熸湡浜�'}, + {key: 7, value: '鏄熸湡鍏�'} +]) +const cycleTotal = computed(() => { + cycle01.value = props.check(cycle01.value, 1, 6) + cycle02.value = props.check(cycle02.value, cycle01.value + 1, 7) + return cycle01.value + '-' + cycle02.value +}) +const averageTotal = computed(() => { + average01.value = props.check(average01.value, 1, 4) + average02.value = props.check(average02.value, 1, 7) + return average02.value + '#' + average01.value +}) +const weekdayTotal = computed(() => { + weekday.value = props.check(weekday.value, 1, 7) + return weekday.value + 'L' +}) +const checkboxString = computed(() => { + return checkboxList.value.join(',') +}) +watch(() => props.cron.week, value => changeRadioValue(value)) +watch([radioValue, cycleTotal, averageTotal, weekdayTotal, checkboxString], () => onRadioChange()) +function changeRadioValue(value) { + if (value === "*") { + radioValue.value = 1 + } else if (value === "?") { + radioValue.value = 2 + } else if (value.indexOf("-") > -1) { + const indexArr = value.split('-') + cycle01.value = Number(indexArr[0]) + cycle02.value = Number(indexArr[1]) + radioValue.value = 3 + } else if (value.indexOf("#") > -1) { + const indexArr = value.split('#') + average01.value = Number(indexArr[1]) + average02.value = Number(indexArr[0]) + radioValue.value = 4 + } else if (value.indexOf("L") > -1) { + const indexArr = value.split("L") + weekday.value = Number(indexArr[0]) + radioValue.value = 5 + } else { + checkboxList.value = [...new Set(value.split(',').map(item => Number(item)))] + radioValue.value = 6 + } +} +function onRadioChange() { + if (radioValue.value === 2 && props.cron.day === '?') { + emit('update', 'day', '*', 'week') + } + if (radioValue.value !== 2 && props.cron.day !== '?') { + emit('update', 'day', '?', 'week') + } + switch (radioValue.value) { + case 1: + emit('update', 'week', '*', 'week') + break + case 2: + emit('update', 'week', '?', 'week') + break + case 3: + emit('update', 'week', cycleTotal.value, 'week') + break + case 4: + emit('update', 'week', averageTotal.value, 'week') + break + case 5: + emit('update', 'week', weekdayTotal.value, 'week') + break + case 6: + if (checkboxList.value.length === 0) { + checkboxList.value.push(checkCopy.value[0]) + } else { + checkCopy.value = checkboxList.value + } + emit('update', 'week', checkboxString.value, 'week') + break + } +} +</script> + +<style lang="scss" scoped> +.el-input-number--small, .el-select, .el-select--small { + margin: 0 0.5rem; +} +.el-select, .el-select--small { + width: 8rem; +} +.el-select.multiselect, .el-select--small.multiselect { + width: 17.8rem; +} +</style> \ No newline at end of file diff --git a/src/components/Crontab/year.vue b/src/components/Crontab/year.vue new file mode 100644 index 0000000..14758ba --- /dev/null +++ b/src/components/Crontab/year.vue @@ -0,0 +1,143 @@ +<template> + <el-form> + <el-form-item> + <el-radio :value="1" v-model='radioValue'> + 涓嶅~锛屽厑璁哥殑閫氶厤绗, - * /] + </el-radio> + </el-form-item> + + <el-form-item> + <el-radio :value="2" v-model='radioValue'> + 姣忓勾 + </el-radio> + </el-form-item> + + <el-form-item> + <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"/> + </el-radio> + </el-form-item> + + <el-form-item> + <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"/> 骞存墽琛屼竴娆� + </el-radio> + + </el-form-item> + + <el-form-item> + <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" /> + </el-select> + </el-radio> + </el-form-item> + </el-form> +</template> + +<script setup> +const emit = defineEmits(['update']) +const props = defineProps({ + cron: { + type: Object, + default: { + second: "*", + min: "*", + hour: "*", + day: "*", + month: "*", + week: "?", + year: "" + } + }, + check: { + type: Function, + default: () => { + } + } +}) + +const fullYear = Number(new Date().getFullYear()) +const maxFullYear = fullYear + 10 +const radioValue = ref(1) +const cycle01 = ref(fullYear) +const cycle02 = ref(fullYear + 1) +const average01 = ref(fullYear) +const average02 = ref(1) +const checkboxList = ref([]) +const checkCopy = ref([fullYear]) + +const cycleTotal = computed(() => { + 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, maxFullYear - 1) + average02.value = props.check(average02.value, 1, 10) + return average01.value + '/' + average02.value +}) +const checkboxString = computed(() => { + return checkboxList.value.join(',') +}) +watch(() => props.cron.year, value => changeRadioValue(value)) +watch([radioValue, cycleTotal, averageTotal, checkboxString], () => onRadioChange()) +function changeRadioValue(value) { + if (value === '') { + radioValue.value = 1 + } else if (value === "*") { + radioValue.value = 2 + } else if (value.indexOf("-") > -1) { + const indexArr = value.split('-') + cycle01.value = Number(indexArr[0]) + cycle02.value = Number(indexArr[1]) + radioValue.value = 3 + } else if (value.indexOf("/") > -1) { + const indexArr = value.split('/') + average01.value = Number(indexArr[0]) + average02.value = Number(indexArr[1]) + radioValue.value = 4 + } else { + checkboxList.value = [...new Set(value.split(',').map(item => Number(item)))] + radioValue.value = 5 + } +} +function onRadioChange() { + switch (radioValue.value) { + case 1: + emit('update', 'year', '', 'year') + break + case 2: + emit('update', 'year', '*', 'year') + break + case 3: + emit('update', 'year', cycleTotal.value, 'year') + break + case 4: + emit('update', 'year', averageTotal.value, 'year') + break + case 5: + if (checkboxList.value.length === 0) { + checkboxList.value.push(checkCopy.value[0]) + } else { + checkCopy.value = checkboxList.value + } + emit('update', 'year', checkboxString.value, 'year') + break + } +} +</script> + +<style lang="scss" scoped> +.el-input-number--small, .el-select, .el-select--small { + margin: 0 0.2rem; +} +.el-select, .el-select--small { + width: 18.8rem; +} +</style> \ No newline at end of file diff --git a/src/components/DictTag/index.vue b/src/components/DictTag/index.vue new file mode 100644 index 0000000..a966437 --- /dev/null +++ b/src/components/DictTag/index.vue @@ -0,0 +1,82 @@ +<template> + <div> + <template v-for="(item, index) in options"> + <template v-if="values.includes(item.value)"> + <span + v-if="(item.elTagType == 'default' || item.elTagType == '') && (item.elTagClass == '' || item.elTagClass == null)" + :key="item.value" + :index="index" + :class="item.elTagClass" + >{{ item.label + " " }}</span> + <el-tag + v-else + :disable-transitions="true" + :key="item.value + ''" + :index="index" + :type="item.elTagType" + :class="item.elTagClass" + >{{ item.label + " " }}</el-tag> + </template> + </template> + <template v-if="unmatch && showValue"> + {{ unmatchArray | handleArray }} + </template> + </div> +</template> + +<script setup> +// 璁板綍鏈尮閰嶇殑椤� +const unmatchArray = ref([]) + +const props = defineProps({ + // 鏁版嵁 + options: { + type: Array, + default: null, + }, + // 褰撳墠鐨勫�� + value: [Number, String, Array], + // 褰撴湭鎵惧埌鍖归厤鐨勬暟鎹椂锛屾樉绀簐alue + showValue: { + type: Boolean, + default: true, + }, + separator: { + 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) +}) + +const unmatch = computed(() => { + unmatchArray.value = [] + // 娌℃湁value涓嶆樉绀� + 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 => { + if (!props.options.some(v => v.value === item)) { + unmatchArray.value.push(item) + unmatch = true // 濡傛灉鏈夋湭鍖归厤椤癸紝灏嗘爣蹇楄缃负true + } + }) + return unmatch // 杩斿洖鏍囧織鐨勫�� +}) + +function handleArray(array) { + if (array.length === 0) return "" + return array.reduce((pre, cur) => { + return pre + " " + cur + }) +} +</script> + +<style scoped> +.el-tag + .el-tag { + margin-left: 10px; +} +</style> diff --git a/src/components/Editor/index.vue b/src/components/Editor/index.vue new file mode 100644 index 0000000..efd0edc --- /dev/null +++ b/src/components/Editor/index.vue @@ -0,0 +1,276 @@ +<template> + <div> + <el-upload + :action="uploadUrl" + :before-upload="handleBeforeUpload" + :on-success="handleUploadSuccess" + :on-error="handleUploadError" + name="file" + :show-file-list="false" + :headers="headers" + class="editor-img-uploader" + v-if="type == 'url'" + > + <i ref="uploadRef" class="editor-img-uploader"></i> + </el-upload> + </div> + <div class="editor"> + <quill-editor + ref="quillEditorRef" + v-model:content="content" + contentType="html" + @textChange="(e) => $emit('update:modelValue', content)" + :options="options" + :style="styles" + /> + </div> +</template> + +<script setup> +import 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 quillEditorRef = ref() +const uploadUrl = ref(import.meta.env.VITE_APP_BASE_API + "/common/upload") // 涓婁紶鐨勫浘鐗囨湇鍔″櫒鍦板潃 +const headers = ref({ + Authorization: "Bearer " + getToken() +}) + +const props = defineProps({ + /* 缂栬緫鍣ㄧ殑鍐呭 */ + modelValue: { + type: String, + }, + /* 楂樺害 */ + height: { + type: Number, + default: null, + }, + /* 鏈�灏忛珮搴� */ + minHeight: { + type: Number, + default: null, + }, + /* 鍙 */ + readOnly: { + type: Boolean, + default: false, + }, + /* 涓婁紶鏂囦欢澶у皬闄愬埗(MB) */ + fileSize: { + type: Number, + default: 5, + }, + /* 绫诲瀷锛坆ase64鏍煎紡銆乽rl鏍煎紡锛� */ + type: { + type: String, + default: "url", + } +}) + +const options = ref({ + theme: "snow", + bounds: document.body, + debug: "warn", + modules: { + // 宸ュ叿鏍忛厤缃� + toolbar: [ + ["bold", "italic", "underline", "strike"], // 鍔犵矖 鏂滀綋 涓嬪垝绾� 鍒犻櫎绾� + ["blockquote", "code-block"], // 寮曠敤 浠g爜鍧� + [{ list: "ordered" }, { list: "bullet" }], // 鏈夊簭銆佹棤搴忓垪琛� + [{ indent: "-1" }, { indent: "+1" }], // 缂╄繘 + [{ size: ["small", false, "large", "huge"] }], // 瀛椾綋澶у皬 + [{ header: [1, 2, 3, 4, 5, 6, false] }], // 鏍囬 + [{ color: [] }, { background: [] }], // 瀛椾綋棰滆壊銆佸瓧浣撹儗鏅鑹� + [{ align: [] }], // 瀵归綈鏂瑰紡 + ["clean"], // 娓呴櫎鏂囨湰鏍煎紡 + ["link", "image", "video"] // 閾炬帴銆佸浘鐗囥�佽棰� + ], + }, + placeholder: "璇疯緭鍏ュ唴瀹�", + readOnly: props.readOnly +}) + +const styles = computed(() => { + let style = {} + if (props.minHeight) { + style.minHeight = `${props.minHeight}px` + } + if (props.height) { + style.height = `${props.height}px` + } + return style +}) + +const content = ref("") +watch(() => props.modelValue, (v) => { + if (v !== content.value) { + content.value = v == undefined ? "<p></p>" : v + } +}, { immediate: true }) + +// 濡傛灉璁剧疆浜嗕笂浼犲湴鍧�鍒欒嚜瀹氫箟鍥剧墖涓婁紶浜嬩欢 +onMounted(() => { + if (props.type == 'url') { + let quill = quillEditorRef.value.getQuill() + let toolbar = quill.getModule("toolbar") + toolbar.addHandler("image", (value) => { + if (value) { + proxy.$refs.uploadRef.click() + } else { + quill.format("image", false) + } + }) + 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) + //妫�楠屾枃浠舵牸寮� + if (!isJPG) { + proxy.$modal.msgError(`鍥剧墖鏍煎紡閿欒!`) + return false + } + // 鏍℃鏂囦欢澶у皬 + if (props.fileSize) { + const isLt = file.size / 1024 / 1024 < props.fileSize + if (!isLt) { + proxy.$modal.msgError(`涓婁紶鏂囦欢澶у皬涓嶈兘瓒呰繃 ${props.fileSize} MB!`) + return false + } + } + return true +} + +// 涓婁紶鎴愬姛澶勭悊 +function handleUploadSuccess(res, file) { + // 濡傛灉涓婁紶鎴愬姛 + if (res.code == 200) { + // 鑾峰彇瀵屾枃鏈疄渚� + let quill = toRaw(quillEditorRef.value).getQuill() + // 鑾峰彇鍏夋爣浣嶇疆 + let length = quill.selection.savedRange.index + // 鎻掑叆鍥剧墖锛宺es.url涓烘湇鍔″櫒杩斿洖鐨勫浘鐗囬摼鎺ュ湴鍧� + quill.insertEmbed(length, "image", import.meta.env.VITE_APP_BASE_API + res.fileName) + // 璋冩暣鍏夋爣鍒版渶鍚� + quill.setSelection(length + 1) + } else { + proxy.$modal.msgError("鍥剧墖鎻掑叆澶辫触") + } +} + +// 涓婁紶澶辫触澶勭悊 +function handleUploadError() { + proxy.$modal.msgError("鍥剧墖鎻掑叆澶辫触") +} + +// 澶嶅埗绮樿创鍥剧墖澶勭悊 +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> + +<style> +.editor-img-uploader { + display: none; +} +.editor, .ql-toolbar { + white-space: pre-wrap !important; + line-height: normal !important; +} +.quill-img { + display: none; +} +.ql-snow .ql-tooltip[data-mode="link"]::before { + content: "璇疯緭鍏ラ摼鎺ュ湴鍧�:"; +} +.ql-snow .ql-tooltip.ql-editing a.ql-action::after { + border-right: 0px; + content: "淇濆瓨"; + padding-right: 0px; +} +.ql-snow .ql-tooltip[data-mode="video"]::before { + content: "璇疯緭鍏ヨ棰戝湴鍧�:"; +} +.ql-snow .ql-picker.ql-size .ql-picker-label::before, +.ql-snow .ql-picker.ql-size .ql-picker-item::before { + content: "14px"; +} +.ql-snow .ql-picker.ql-size .ql-picker-label[data-value="small"]::before, +.ql-snow .ql-picker.ql-size .ql-picker-item[data-value="small"]::before { + content: "10px"; +} +.ql-snow .ql-picker.ql-size .ql-picker-label[data-value="large"]::before, +.ql-snow .ql-picker.ql-size .ql-picker-item[data-value="large"]::before { + content: "18px"; +} +.ql-snow .ql-picker.ql-size .ql-picker-label[data-value="huge"]::before, +.ql-snow .ql-picker.ql-size .ql-picker-item[data-value="huge"]::before { + content: "32px"; +} +.ql-snow .ql-picker.ql-header .ql-picker-label::before, +.ql-snow .ql-picker.ql-header .ql-picker-item::before { + content: "鏂囨湰"; +} +.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="1"]::before, +.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="1"]::before { + content: "鏍囬1"; +} +.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="2"]::before, +.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="2"]::before { + content: "鏍囬2"; +} +.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="3"]::before, +.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="3"]::before { + content: "鏍囬3"; +} +.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="4"]::before, +.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="4"]::before { + content: "鏍囬4"; +} +.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="5"]::before, +.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="5"]::before { + content: "鏍囬5"; +} +.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="6"]::before, +.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="6"]::before { + content: "鏍囬6"; +} +.ql-snow .ql-picker.ql-font .ql-picker-label::before, +.ql-snow .ql-picker.ql-font .ql-picker-item::before { + content: "鏍囧噯瀛椾綋"; +} +.ql-snow .ql-picker.ql-font .ql-picker-label[data-value="serif"]::before, +.ql-snow .ql-picker.ql-font .ql-picker-item[data-value="serif"]::before { + content: "琛嚎瀛椾綋"; +} +.ql-snow .ql-picker.ql-font .ql-picker-label[data-value="monospace"]::before, +.ql-snow .ql-picker.ql-font .ql-picker-item[data-value="monospace"]::before { + content: "绛夊瀛椾綋"; +} +</style> diff --git a/src/components/FileUpload/index.vue b/src/components/FileUpload/index.vue new file mode 100644 index 0000000..b77d2a1 --- /dev/null +++ b/src/components/FileUpload/index.vue @@ -0,0 +1,256 @@ +<template> + <div class="upload-file"> + <el-upload + multiple + :action="uploadFileUrl" + :before-upload="handleBeforeUpload" + :file-list="fileList" + :data="data" + :limit="limit" + :on-error="handleUploadError" + :on-exceed="handleExceed" + :on-success="handleUploadSuccess" + :show-file-list="false" + :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 && !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 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" v-if="!disabled"> 鍒犻櫎</el-link> + </div> + </li> + </transition-group> + </div> +</template> + +<script setup> +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 + }, + // 澶у皬闄愬埗(MB) + fileSize: { + type: Number, + default: 5 + }, + // 鏂囦欢绫诲瀷, 渚嬪['png', 'jpg', 'jpeg'] + fileType: { + type: Array, + 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 + 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 + // 棣栧厛灏嗗�艰浆涓烘暟缁� + const list = Array.isArray(val) ? val : props.modelValue.split(',') + // 鐒跺悗灏嗘暟缁勮浆涓哄璞℃暟缁� + fileList.value = list.map(item => { + if (typeof item === "string") { + item = { name: item, url: item } + } + item.uid = item.uid || new Date().getTime() + temp++ + return item + }) + } else { + fileList.value = [] + return [] + } +},{ 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 + if (!isTypeOk) { + proxy.$modal.msgError(`鏂囦欢鏍煎紡涓嶆纭紝璇蜂笂浼�${props.fileType.join("/")}鏍煎紡鏂囦欢!`) + return false + } + } + // 鏍℃鏂囦欢鍚嶆槸鍚﹀寘鍚壒娈婂瓧绗� + if (file.name.includes(',')) { + proxy.$modal.msgError('鏂囦欢鍚嶄笉姝g‘锛屼笉鑳藉寘鍚嫳鏂囬�楀彿!') + return false + } + // 鏍℃鏂囦欢澶у皬 + if (props.fileSize) { + const isLt = file.size / 1024 / 1024 < props.fileSize + if (!isLt) { + proxy.$modal.msgError(`涓婁紶鏂囦欢澶у皬涓嶈兘瓒呰繃 ${props.fileSize} MB!`) + return false + } + } + proxy.$modal.loading("姝e湪涓婁紶鏂囦欢锛岃绋嶅��...") + number.value++ + return true +} + +// 鏂囦欢涓暟瓒呭嚭 +function handleExceed() { + proxy.$modal.msgError(`涓婁紶鏂囦欢鏁伴噺涓嶈兘瓒呰繃 ${props.limit} 涓�!`) +} + +// 涓婁紶澶辫触 +function handleUploadError(err) { + proxy.$modal.msgError("涓婁紶鏂囦欢澶辫触") + proxy.$modal.closeLoading() +} + +// 涓婁紶鎴愬姛鍥炶皟 +function handleUploadSuccess(res, file) { + if (res.code === 200) { + 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() + } +} + +// 鍒犻櫎鏂囦欢 +function handleDelete(index) { + 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() + } +} + +// 鑾峰彇鏂囦欢鍚嶇О +function getFileName(name) { + // 濡傛灉鏄痷rl閭d箞鍙栨渶鍚庣殑鍚嶅瓧 濡傛灉涓嶆槸鐩存帴杩斿洖 + if (name.lastIndexOf("/") > -1) { + return name.slice(name.lastIndexOf("/") + 1) + } else { + return name + } +} + +// 瀵硅薄杞垚鎸囧畾瀛楃涓插垎闅� +function listToString(list, separator) { + let strs = "" + separator = separator || "," + for (let i in list) { + if (list[i].url) { + strs += list[i].url + separator + } + } + return strs != '' ? strs.substr(0, strs.length - 1) : '' +} + +// 鍒濆鍖栨嫋鎷芥帓搴� +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; +} +.upload-file-list .el-upload-list__item { + border: 1px solid #e4e7ed; + line-height: 2; + margin-bottom: 10px; + position: relative; + transition: none !important; +} +.upload-file-list .ele-upload-list__item-content { + display: flex; + justify-content: space-between; + align-items: center; + color: inherit; +} +.ele-upload-list__item-content-action .el-link { + margin-right: 10px; +} +</style> diff --git a/src/components/Hamburger/index.vue b/src/components/Hamburger/index.vue new file mode 100644 index 0000000..a2b4980 --- /dev/null +++ b/src/components/Hamburger/index.vue @@ -0,0 +1,42 @@ +<template> + <div style="padding: 0 15px;" @click="toggleClick"> + <svg + :class="{'is-active':isActive}" + class="hamburger" + viewBox="0 0 1024 1024" + 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> + </div> +</template> + +<script setup> +defineProps({ + isActive: { + type: Boolean, + default: false + } +}) + +const emit = defineEmits() +const toggleClick = () => { + emit('toggleClick') +} +</script> + +<style scoped> +.hamburger { + display: inline-block; + vertical-align: middle; + width: 20px; + height: 20px; +} + +.hamburger.is-active { + transform: rotate(180deg); +} +</style> diff --git a/src/components/HeaderSearch/index.vue b/src/components/HeaderSearch/index.vue new file mode 100644 index 0000000..8fae162 --- /dev/null +++ b/src/components/HeaderSearch/index.vue @@ -0,0 +1,252 @@ +<template> + <div class="header-search"> + <svg-icon class-name="search-icon" icon-class="search" @click.stop="click" /> + <el-dialog + v-model="show" + width="600" + @close="close" + :show-close="false" + append-to-body + > + <el-input + v-model="search" + ref="headerSearchSelectRef" + size="large" + @input="querySearch" + prefix-icon="Search" + placeholder="鑿滃崟鎼滅储锛屾敮鎸佹爣棰樸�乁RL妯$硦鏌ヨ" + 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> + +<script setup> +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 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 + if (isHttp(path)) { + // http(s):// 璺緞鏂扮獥鍙f墦寮� + const pindex = path.indexOf("http") + window.open(path.substr(pindex, path.length), "_blank") + } else { + if (query) { + router.push({ path: path, query: JSON.parse(query) }) + } else { + router.push(path) + } + } + + search.value = '' + options.value = [] + nextTick(() => { + show.value = false + }) +} + +function initFuse(list) { + fuse.value = new Fuse(list, { + shouldSort: true, + threshold: 0.4, + location: 0, + distance: 100, + minMatchCharLength: 1, + keys: [{ + name: 'title', + weight: 0.7 + }, { + name: 'path', + weight: 0.3 + }] + }) +} + +// Filter out the routes that can be displayed in the sidebar +// And generate the internationalized title +function generateRoutes(routes, basePath = '', prefixTitle = []) { + let res = [] + + 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 data = { + path: !isHttp(r.path) ? getNormalPath(basePath + p) : r.path, + title: [...prefixTitle], + icon: '' + } + + if (r.meta && r.meta.title) { + data.title = [...data.title, r.meta.title] + 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) + } + } + if (r.query) { + data.query = r.query + } + + // recursive child routes + if (r.children) { + const tempRoutes = generateRoutes(r.children, data.path, data.title) + if (tempRoutes.length >= 1) { + res = [...res, ...tempRoutes] + } + } + } + return res +} + +function querySearch(query) { + activeIndex.value = -1 + if (query !== '') { + options.value = fuse.value.search(query).map((item) => item.item) ?? searchPool.value + } else { + 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) +}) + +watch(searchPool, (list) => { + initFuse(list) +}) +</script> + +<style lang='scss' scoped> +.header-search { + .search-icon { + cursor: pointer; + font-size: 18px; + vertical-align: middle; + } +} + +.result-wrap { + height: 280px; + margin: 6px 0; + + .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; + } + } + } + + .search-item:hover { + cursor: pointer; + } +} +</style> diff --git a/src/components/IconSelect/index.vue b/src/components/IconSelect/index.vue new file mode 100644 index 0000000..b527b7d --- /dev/null +++ b/src/components/IconSelect/index.vue @@ -0,0 +1,111 @@ +<template> + <div class="icon-body"> + <el-input + v-model="iconName" + class="icon-search" + clearable + placeholder="璇疯緭鍏ュ浘鏍囧悕绉�" + @clear="filterIcons" + @input="filterIcons" + > + <template #suffix><i class="el-icon-search el-input__icon" /></template> + </el-input> + <div class="icon-list"> + <div class="list-container"> + <div v-for="(item, index) in iconList" class="icon-item-wrapper" :key="index" @click="selectedIcon(item)"> + <div :class="['icon-item', { active: activeIcon === item }]"> + <svg-icon :icon-class="item" class-name="icon" style="height: 25px;width: 16px;"/> + <span>{{ item }}</span> + </div> + </div> + </div> + </div> + </div> +</template> + +<script setup> +import icons from './requireIcons' + +const props = defineProps({ + activeIcon: { + type: String + } +}) + +const iconName = ref('') +const iconList = ref(icons) +const emit = defineEmits(['selected']) + +function filterIcons() { + iconList.value = icons + if (iconName.value) { + iconList.value = icons.filter(item => item.indexOf(iconName.value) !== -1) + } +} + +function selectedIcon(name) { + emit('selected', name) + document.body.click() +} + +function reset() { + iconName.value = '' + iconList.value = icons +} + +defineExpose({ + reset +}) +</script> + +<style lang='scss' scoped> + .icon-body { + width: 100%; + padding: 10px; + .icon-search { + position: relative; + margin-bottom: 5px; + } + .icon-list { + height: 200px; + overflow: auto; + .list-container { + display: flex; + flex-wrap: wrap; + .icon-item-wrapper { + width: calc(100% / 3); + height: 25px; + line-height: 25px; + cursor: pointer; + display: flex; + .icon-item { + display: flex; + max-width: 100%; + height: 100%; + padding: 0 5px; + &:hover { + background: #ececec; + border-radius: 5px; + } + .icon { + flex-shrink: 0; + } + span { + display: inline-block; + vertical-align: -0.15em; + fill: currentColor; + padding-left: 2px; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + } + } + .icon-item.active { + background: #ececec; + border-radius: 5px; + } + } + } + } + } +</style> \ No newline at end of file diff --git a/src/components/IconSelect/requireIcons.js b/src/components/IconSelect/requireIcons.js new file mode 100644 index 0000000..36b7539 --- /dev/null +++ b/src/components/IconSelect/requireIcons.js @@ -0,0 +1,8 @@ +let icons = [] +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) +} + +export default icons \ No newline at end of file diff --git a/src/components/ImagePreview/index.vue b/src/components/ImagePreview/index.vue new file mode 100644 index 0000000..acd05c5 --- /dev/null +++ b/src/components/ImagePreview/index.vue @@ -0,0 +1,92 @@ +<template> + <el-image + :src="`${realSrc}`" + fit="cover" + :style="`width:${realWidth};height:${realHeight};`" + :preview-src-list="realSrcList" + preview-teleported + > + <template #error> + <div class="image-slot"> + <el-icon><picture-filled /></el-icon> + </div> + </template> + </el-image> +</template> + +<script setup> +import { isExternal } from "@/utils/validate" + +const props = defineProps({ + src: { + type: String, + default: "" + }, + width: { + type: [Number, String], + default: "" + }, + height: { + type: [Number, String], + default: "" + } +}) + +const realSrc = computed(() => { + if (!props.src) { + return + } + let real_src = props.src.split(",")[0] + if (isExternal(real_src)) { + return real_src + } + return import.meta.env.VITE_APP_BASE_API + real_src +}) + +const realSrcList = computed(() => { + if (!props.src) { + return + } + let real_src_list = props.src.split(",") + let srcList = [] + real_src_list.forEach(item => { + if (isExternal(item)) { + return srcList.push(item) + } + 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> +.el-image { + border-radius: 5px; + background-color: #ebeef5; + box-shadow: 0 0 5px 1px #ccc; + :deep(.el-image__inner) { + transition: all 0.3s; + cursor: pointer; + &:hover { + transform: scale(1.2); + } + } + :deep(.image-slot) { + display: flex; + justify-content: center; + align-items: center; + width: 100%; + height: 100%; + color: #909399; + font-size: 30px; + } +} +</style> diff --git a/src/components/ImageUpload/index.vue b/src/components/ImageUpload/index.vue new file mode 100644 index 0000000..de87665 --- /dev/null +++ b/src/components/ImageUpload/index.vue @@ -0,0 +1,258 @@ +<template> + <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" + ref="imageUpload" + :before-remove="handleDelete" + :show-file-list="true" + :headers="headers" + :file-list="fileList" + :on-preview="handlePictureCardPreview" + :class="{ hide: fileList.length >= limit }" + > + <el-icon class="avatar-uploader-icon"><plus /></el-icon> + </el-upload> + <!-- 涓婁紶鎻愮ず --> + <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> + + <el-dialog + v-model="dialogVisible" + title="棰勮" + width="800px" + append-to-body + > + <img + :src="dialogImageUrl" + style="display: block; max-width: 100%; margin: 0 auto" + /> + </el-dialog> + </div> +</template> + +<script setup> +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 + }, + // 澶у皬闄愬埗(MB) + fileSize: { + type: Number, + default: 5 + }, + // 鏂囦欢绫诲瀷, 渚嬪['png', 'jpg', 'jpeg'] + fileType: { + type: Array, + 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 + 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(",") + // 鐒跺悗灏嗘暟缁勮浆涓哄璞℃暟缁� + fileList.value = list.map(item => { + if (typeof item === "string") { + if (item.indexOf(baseUrl) === -1 && !isExternal(item)) { + item = { name: baseUrl + item, url: baseUrl + item } + } else { + item = { name: item, url: item } + } + } + return item + }) + } else { + fileList.value = [] + return [] + } +},{ deep: true, immediate: true }) + +// 涓婁紶鍓峫oading鍔犺浇 +function handleBeforeUpload(file) { + let isImg = false + if (props.fileType.length) { + let fileExtension = "" + if (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 + }) + } else { + isImg = file.type.indexOf("image") > -1 + } + if (!isImg) { + proxy.$modal.msgError(`鏂囦欢鏍煎紡涓嶆纭紝璇蜂笂浼�${props.fileType.join("/")}鍥剧墖鏍煎紡鏂囦欢!`) + return false + } + if (file.name.includes(',')) { + proxy.$modal.msgError('鏂囦欢鍚嶄笉姝g‘锛屼笉鑳藉寘鍚嫳鏂囬�楀彿!') + return false + } + if (props.fileSize) { + const isLt = file.size / 1024 / 1024 < props.fileSize + if (!isLt) { + proxy.$modal.msgError(`涓婁紶澶村儚鍥剧墖澶у皬涓嶈兘瓒呰繃 ${props.fileSize} MB!`) + return false + } + } + proxy.$modal.loading("姝e湪涓婁紶鍥剧墖锛岃绋嶅��...") + number.value++ +} + +// 鏂囦欢涓暟瓒呭嚭 +function handleExceed() { + proxy.$modal.msgError(`涓婁紶鏂囦欢鏁伴噺涓嶈兘瓒呰繃 ${props.limit} 涓�!`) +} + +// 涓婁紶鎴愬姛鍥炶皟 +function handleUploadSuccess(res, file) { + if (res.code === 200) { + 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() + } +} + +// 鍒犻櫎鍥剧墖 +function handleDelete(file) { + 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 + } +} + +// 涓婁紶缁撴潫澶勭悊 +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() + } +} + +// 涓婁紶澶辫触 +function handleUploadError() { + proxy.$modal.msgError("涓婁紶鍥剧墖澶辫触") + proxy.$modal.closeLoading() +} + +// 棰勮 +function handlePictureCardPreview(file) { + dialogImageUrl.value = file.url + dialogVisible.value = true +} + +// 瀵硅薄杞垚鎸囧畾瀛楃涓插垎闅� +function listToString(list, 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 + } + } + 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"> +// .el-upload--picture-card 鎺у埗鍔犲彿閮ㄥ垎 +:deep(.hide .el-upload--picture-card) { + display: none; +} + +:deep(.el-upload.el-upload--picture-card.is-disabled) { + display: none !important; +} +</style> \ No newline at end of file diff --git a/src/components/Pagination/index.vue b/src/components/Pagination/index.vue new file mode 100644 index 0000000..56569f7 --- /dev/null +++ b/src/components/Pagination/index.vue @@ -0,0 +1,105 @@ +<template> + <div :class="{ 'hidden': hidden }" class="pagination-container"> + <el-pagination + :background="background" + v-model:current-page="currentPage" + v-model:page-size="pageSize" + :layout="layout" + :page-sizes="pageSizes" + :pager-count="pagerCount" + :total="total" + @size-change="handleSizeChange" + @current-change="handleCurrentChange" + /> + </div> +</template> + +<script setup> +import { scrollTo } from '@/utils/scroll-to' + +const props = defineProps({ + total: { + required: true, + type: Number + }, + page: { + type: Number, + default: 1 + }, + limit: { + type: Number, + default: 20 + }, + pageSizes: { + type: Array, + default() { + return [10, 20, 30, 50] + } + }, + // 绉诲姩绔〉鐮佹寜閽殑鏁伴噺绔粯璁ゅ��5 + pagerCount: { + type: Number, + default: document.body.clientWidth < 992 ? 5 : 7 + }, + layout: { + type: String, + default: 'total, sizes, prev, pager, next, jumper' + }, + background: { + type: Boolean, + default: true + }, + autoScroll: { + type: Boolean, + default: true + }, + hidden: { + type: Boolean, + default: false + } +}) + +const emit = defineEmits() +const currentPage = computed({ + get() { + return props.page + }, + set(val) { + emit('update:page', val) + } +}) +const pageSize = computed({ + get() { + return props.limit + }, + set(val){ + emit('update:limit', val) + } +}) + +function handleSizeChange(val) { + if (currentPage.value * val > props.total) { + currentPage.value = 1 + } + emit('pagination', { page: currentPage.value, limit: val }) + if (props.autoScroll) { + 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; +} +.pagination-container.hidden { + display: none; +} +</style> \ No newline at end of file diff --git a/src/components/ParentView/index.vue b/src/components/ParentView/index.vue new file mode 100644 index 0000000..7bf6148 --- /dev/null +++ b/src/components/ParentView/index.vue @@ -0,0 +1,3 @@ +<template > + <router-view /> +</template> diff --git a/src/components/RightToolbar/index.vue b/src/components/RightToolbar/index.vue new file mode 100644 index 0000000..349363c --- /dev/null +++ b/src/components/RightToolbar/index.vue @@ -0,0 +1,157 @@ +<template> + <div class="top-right-btn" :style="style"> + <el-row> + <el-tooltip class="item" effect="dark" :content="showSearch ? '闅愯棌鎼滅储' : '鏄剧ず鎼滅储'" placement="top" v-if="search"> + <el-button circle icon="Search" @click="toggleSearch()" /> + </el-tooltip> + <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-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> + <!-- 鍏ㄩ��/鍙嶉�� 鎸夐挳 --> + <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 in columns" :key="item.key"> + <el-dropdown-item> + <el-checkbox v-model="item.visible" @change="checkboxChange($event, item.label)" :label="item.label" /> + </el-dropdown-item> + </template> + </el-dropdown-menu> + </template> + </el-dropdown> + </el-tooltip> + </el-row> + <el-dialog :title="title" v-model="open" append-to-body> + <el-transfer + :titles="['鏄剧ず', '闅愯棌']" + v-model="value" + :data="columns" + @change="dataChange" + ></el-transfer> + </el-dialog> + </div> +</template> + +<script setup> +const props = defineProps({ + /* 鏄惁鏄剧ず妫�绱㈡潯浠� */ + showSearch: { + type: Boolean, + default: true + }, + /* 鏄鹃殣鍒椾俊鎭� */ + columns: { + type: Array + }, + /* 鏄惁鏄剧ず妫�绱㈠浘鏍� */ + search: { + type: Boolean, + default: true + }, + /* 鏄鹃殣鍒楃被鍨嬶紙transfer绌挎妗嗐�乧heckbox澶嶉�夋锛� */ + showColumnsType: { + type: String, + default: "checkbox" + }, + /* 鍙冲杈硅窛 */ + gutter: { + type: Number, + default: 10 + }, +}) + +const emits = defineEmits(['update:showSearch', 'queryTable']) + +// 鏄鹃殣鏁版嵁 +const value = ref([]) +// 寮瑰嚭灞傛爣棰� +const title = ref("鏄剧ず/闅愯棌") +// 鏄惁鏄剧ず寮瑰嚭灞� +const open = ref(false) + +const style = computed(() => { + const ret = {} + if (props.gutter) { + ret.marginRight = `${props.gutter / 2}px` + } + return ret +}) + +// 鏄惁鍏ㄩ��/鍗婇�� 鐘舵�� +const isChecked = computed({ + get: () => props.columns.every(col => col.visible), + set: () => {} +}) +const isIndeterminate = computed(() => props.columns.some((col) => col.visible) && !isChecked.value) + +// 鎼滅储 +function toggleSearch() { + emits("update:showSearch", !props.showSearch) +} + +// 鍒锋柊 +function refresh() { + emits("queryTable") +} + +// 鍙充晶鍒楄〃鍏冪礌鍙樺寲 +function dataChange(data) { + for (let item in props.columns) { + const key = props.columns[item].key + props.columns[item].visible = !data.includes(key) + } +} + +// 鎵撳紑鏄鹃殣鍒梔ialog +function showColumn() { + open.value = true +} + +if (props.showColumnsType == 'transfer') { + // 鏄鹃殣鍒楀垵濮嬮粯璁ら殣钘忓垪 + for (let item in props.columns) { + if (props.columns[item].visible === false) { + value.value.push(parseInt(item)) + } + } +} + +// 鍗曞嬀閫� +function checkboxChange(event, label) { + props.columns.filter(item => item.label == label)[0].visible = event +} + +// 鍒囨崲鍏ㄩ��/鍙嶉�� +function toggleCheckAll() { + const newValue = !isChecked.value + props.columns.forEach((col) => (col.visible = newValue)) +} +</script> + +<style lang='scss' scoped> +:deep(.el-transfer__button) { + border-radius: 50%; + display: block; + margin-left: 0px; +} +:deep(.el-transfer__button:first-child) { + margin-bottom: 10px; +} +:deep(.el-dropdown-menu__item) { + line-height: 30px; + padding: 0 17px; +} +.check-line { + width: 90%; + height: 1px; + background-color: #ccc; + margin: 3px auto; +} +</style> diff --git a/src/components/RuoYi/Doc/index.vue b/src/components/RuoYi/Doc/index.vue new file mode 100644 index 0000000..8355592 --- /dev/null +++ b/src/components/RuoYi/Doc/index.vue @@ -0,0 +1,13 @@ +<template> + <div> + <svg-icon icon-class="question" @click="goto" /> + </div> +</template> + +<script setup> +const url = ref('http://doc.ruoyi.vip/ruoyi-vue') + +function goto() { + window.open(url.value) +} +</script> \ No newline at end of file diff --git a/src/components/RuoYi/Git/index.vue b/src/components/RuoYi/Git/index.vue new file mode 100644 index 0000000..648dde1 --- /dev/null +++ b/src/components/RuoYi/Git/index.vue @@ -0,0 +1,13 @@ +<template> + <div> + <svg-icon icon-class="github" @click="goto" /> + </div> +</template> + +<script setup> +const url = ref('https://gitee.com/y_project/RuoYi-Vue') + +function goto() { + window.open(url.value) +} +</script> diff --git a/src/components/Screenfull/index.vue b/src/components/Screenfull/index.vue new file mode 100644 index 0000000..464d976 --- /dev/null +++ b/src/components/Screenfull/index.vue @@ -0,0 +1,22 @@ +<template> + <div> + <svg-icon :icon-class="isFullscreen ? 'exit-fullscreen' : 'fullscreen'" @click="toggle" /> + </div> +</template> + +<script setup> +import { useFullscreen } from '@vueuse/core' + +const { isFullscreen, enter, exit, toggle } = useFullscreen() +</script> + +<style lang='scss' scoped> +.screenfull-svg { + display: inline-block; + cursor: pointer; + fill: #5a5e66; + width: 20px; + height: 20px; + vertical-align: 10px; +} +</style> \ No newline at end of file diff --git a/src/components/SizeSelect/index.vue b/src/components/SizeSelect/index.vue new file mode 100644 index 0000000..463738c --- /dev/null +++ b/src/components/SizeSelect/index.vue @@ -0,0 +1,45 @@ +<template> + <div> + <el-dropdown trigger="click" @command="handleSetSize"> + <div class="size-icon--style"> + <svg-icon class-name="size-icon" icon-class="size" /> + </div> + <template #dropdown> + <el-dropdown-menu> + <el-dropdown-item v-for="item of sizeOptions" :key="item.value" :disabled="size === item.value" :command="item.value"> + {{ item.label }} + </el-dropdown-item> + </el-dropdown-menu> + </template> + </el-dropdown> + </div> +</template> + +<script setup> +import useAppStore from "@/store/modules/app" + +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("姝e湪璁剧疆甯冨眬澶у皬锛岃绋嶅��...") + appStore.setSize(size) + setTimeout("window.location.reload()", 1000) +} +</script> + +<style lang='scss' scoped> +.size-icon--style { + font-size: 18px; + line-height: 50px; + padding-right: 7px; +} +</style> \ No newline at end of file diff --git a/src/components/SvgIcon/index.vue b/src/components/SvgIcon/index.vue new file mode 100644 index 0000000..8c101f6 --- /dev/null +++ b/src/components/SvgIcon/index.vue @@ -0,0 +1,53 @@ +<template> + <svg :class="svgClass" aria-hidden="true"> + <use :xlink:href="iconName" :fill="color" /> + </svg> +</template> + +<script> +export default defineComponent({ + props: { + iconClass: { + type: String, + required: true + }, + className: { + type: String, + default: '' + }, + color: { + type: String, + default: '' + }, + }, + setup(props) { + return { + iconName: computed(() => `#icon-${props.iconClass}`), + svgClass: computed(() => { + if (props.className) { + return `svg-icon ${props.className}` + } + return 'svg-icon' + }) + } + } +}) +</script> + +<style scope lang="scss"> +.sub-el-icon, +.nav-icon { + display: inline-block; + font-size: 15px; + margin-right: 12px; + position: relative; +} + +.svg-icon { + width: 1em; + height: 1em; + position: relative; + fill: currentColor; + vertical-align: -2px; +} +</style> diff --git a/src/components/SvgIcon/svgicon.js b/src/components/SvgIcon/svgicon.js new file mode 100644 index 0000000..89e39b5 --- /dev/null +++ b/src/components/SvgIcon/svgicon.js @@ -0,0 +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) + } + } +} diff --git a/src/components/TopNav/index.vue b/src/components/TopNav/index.vue new file mode 100644 index 0000000..62028af --- /dev/null +++ b/src/components/TopNav/index.vue @@ -0,0 +1,217 @@ +<template> + <el-menu + :default-active="activeMenu" + mode="horizontal" + @select="handleSelect" + :ellipsis="false" + > + <template v-for="(item, index) in topMenus"> + <el-menu-item :style="{'--theme': theme}" :index="item.path" :key="index" v-if="index < visibleNumber"> + <svg-icon + v-if="item.meta && item.meta.icon && item.meta.icon !== '#'" + :icon-class="item.meta.icon"/> + {{ item.meta.title }} + </el-menu-item> + </template> + + <!-- 椤堕儴鑿滃崟瓒呭嚭鏁伴噺鎶樺彔 --> + <el-sub-menu :style="{'--theme': theme}" index="more" v-if="topMenus.length > visibleNumber"> + <template #title>鏇村鑿滃崟</template> + <template v-for="(item, index) in topMenus"> + <el-menu-item + :index="item.path" + :key="index" + v-if="index >= visibleNumber"> + <svg-icon + v-if="item.meta && item.meta.icon && item.meta.icon !== '#'" + :icon-class="item.meta.icon"/> + {{ item.meta.title }} + </el-menu-item> + </template> + </el-sub-menu> + </el-menu> +</template> + +<script setup> +import { constantRoutes } from "@/router" +import { isHttp } from '@/utils/validate' +import useAppStore from '@/store/modules/app' +import useSettingsStore from '@/store/modules/settings' +import usePermissionStore from '@/store/modules/permission' + +// 椤堕儴鏍忓垵濮嬫暟 +const visibleNumber = ref(null) +// 褰撳墠婵�娲昏彍鍗曠殑 index +const currentIndex = ref(null) +// 闅愯棌渚ц竟鏍忚矾鐢� +const hideList = ['/index', '/user/profile'] + +const appStore = useAppStore() +const settingsStore = useSettingsStore() +const permissionStore = usePermissionStore() +const route = useRoute() +const router = useRouter() + +// 涓婚棰滆壊 +const theme = computed(() => settingsStore.theme) +// 鎵�鏈夌殑璺敱淇℃伅 +const routers = computed(() => permissionStore.topbarRouters) + +// 椤堕儴鏄剧ず鑿滃崟 +const topMenus = computed(() => { + let topMenus = [] + routers.value.map((menu) => { + if (menu.hidden !== true) { + // 鍏煎椤堕儴鏍忎竴绾ц彍鍗曞唴閮ㄨ烦杞� + if (menu.path === '/' && menu.children) { + topMenus.push(menu.children[0]) + } else { + topMenus.push(menu) + } + } + }) + return topMenus +}) + +// 璁剧疆瀛愯矾鐢� +const childrenMenus = computed(() => { + 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 + } else { + if(!isHttp(router.children[item].path)) { + router.children[item].path = router.path + "/" + router.children[item].path + } + } + router.children[item].parentPath = router.path + } + childrenMenus.push(router.children[item]) + } + }) + return constantRoutes.concat(childrenMenus) +}) + +// 榛樿婵�娲荤殑鑿滃崟 +const activeMenu = computed(() => { + const path = route.path + let activePath = path + if (path !== undefined && path.lastIndexOf("/") > 0 && hideList.indexOf(path) === -1) { + const tmpPath = path.substring(1, path.length) + if (!route.meta.link) { + activePath = "/" + tmpPath.substring(0, tmpPath.indexOf("/")) + appStore.toggleSideBarHide(false) + } + } else if(!route.children) { + activePath = path + appStore.toggleSideBarHide(true) + } + activeRoutes(activePath) + return activePath +}) + +function setVisibleNumber() { + 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) + if (isHttp(key)) { + // http(s):// 璺緞鏂扮獥鍙f墦寮� + window.open(key, "_blank") + } else if (!route || !route.children) { + // 娌℃湁瀛愯矾鐢辫矾寰勫唴閮ㄦ墦寮� + const routeMenu = childrenMenus.value.find(item => item.path === key) + if (routeMenu && routeMenu.query) { + let query = JSON.parse(routeMenu.query) + router.push({ path: key, query: query }) + } else { + router.push({ path: key }) + } + appStore.toggleSideBarHide(true) + } else { + // 鏄剧ず宸︿晶鑱斿姩鑿滃崟 + activeRoutes(key) + appStore.toggleSideBarHide(false) + } +} + +function activeRoutes(key) { + let routes = [] + if (childrenMenus.value && childrenMenus.value.length > 0) { + childrenMenus.value.map((item) => { + if (key == item.parentPath || (key == "index" && "" == item.path)) { + routes.push(item) + } + }) + } + if(routes.length > 0) { + permissionStore.setSidebarRouters(routes) + } else { + appStore.toggleSideBarHide(true) + } + return routes +} + +onMounted(() => { + window.addEventListener('resize', setVisibleNumber) +}) + +onBeforeUnmount(() => { + window.removeEventListener('resize', setVisibleNumber) +}) + +onMounted(() => { + setVisibleNumber() +}) +</script> + +<style lang="scss"> +.topmenu-container.el-menu--horizontal > .el-menu-item { + float: left; + height: 50px !important; + line-height: 50px !important; + color: #999093 !important; + padding: 0 5px !important; + margin: 0 10px !important; +} + +.topmenu-container.el-menu--horizontal > .el-menu-item.is-active, .el-menu--horizontal > .el-sub-menu.is-active .el-submenu__title { + border-bottom: 2px solid #{'var(--theme)'} !important; + color: #303133; +} + +/* sub-menu item */ +.topmenu-container.el-menu--horizontal > .el-sub-menu .el-sub-menu__title { + float: left; + height: 50px !important; + line-height: 50px !important; + color: #999093 !important; + padding: 0 5px !important; + margin: 0 10px !important; +} + +/* 鑳屾櫙鑹查殣钘� */ +.topmenu-container.el-menu--horizontal>.el-menu-item:not(.is-disabled):focus, .topmenu-container.el-menu--horizontal>.el-menu-item:not(.is-disabled):hover, .topmenu-container.el-menu--horizontal>.el-submenu .el-submenu__title:hover { + background-color: #ffffff; +} + +/* 鍥炬爣鍙抽棿璺� */ +.topmenu-container .svg-icon { + margin-right: 4px; +} + +/* topmenu more arrow */ +.topmenu-container .el-sub-menu .el-sub-menu__icon-arrow { + position: static; + vertical-align: middle; + margin-left: 8px; + margin-top: 0px; +} + + +</style> diff --git a/src/components/iFrame/index.vue b/src/components/iFrame/index.vue new file mode 100644 index 0000000..0eabaa4 --- /dev/null +++ b/src/components/iFrame/index.vue @@ -0,0 +1,31 @@ +<template> + <div v-loading="loading" :style="'height:' + height"> + <iframe + :src="url" + frameborder="no" + style="width: 100%; height: 100%" + scrolling="auto" /> + </div> +</template> + +<script setup> +const props = defineProps({ + src: { + type: String, + required: true + } +}) + +const height = ref(document.documentElement.clientHeight - 94.5 + "px;") +const loading = ref(true) +const url = computed(() => props.src) + +onMounted(() => { + setTimeout(() => { + loading.value = false + }, 300) + window.onresize = function temp() { + height.value = document.documentElement.clientHeight - 94.5 + "px;" + } +}) +</script> diff --git a/src/directive/common/copyText.js b/src/directive/common/copyText.js new file mode 100644 index 0000000..b5f844d --- /dev/null +++ b/src/directive/common/copyText.js @@ -0,0 +1,65 @@ +/** +* v-copyText 澶嶅埗鏂囨湰鍐呭 +* Copyright (c) 2022 ruoyi +*/ +export default { + beforeMount(el, { value, arg }) { + if (arg === "callback") { + el.$copyCallback = value + } else { + el.$copyValue = value + const handler = () => { + copyTextToClipboard(el.$copyValue) + if (el.$copyCallback) { + el.$copyCallback(el.$copyValue) + } + } + 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 + + element.value = input + + // Prevent keyboard from showing on mobile + element.setAttribute('readonly', '') + + 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) + + target.append(element) + element.select() + + // Explicit selection workaround for iOS + element.selectionStart = 0 + element.selectionEnd = input.length + + let isSuccess = false + try { + isSuccess = document.execCommand('copy') + } catch { } + + element.remove() + + if (originalRange) { + selection.removeAllRanges() + selection.addRange(originalRange) + } + + // Get the focus back on the previously focused element, if any + if (previouslyFocusedElement) { + previouslyFocusedElement.focus() + } + + return isSuccess +} diff --git a/src/directive/index.js b/src/directive/index.js new file mode 100644 index 0000000..86b8f88 --- /dev/null +++ b/src/directive/index.js @@ -0,0 +1,9 @@ +import hasRole from './permission/hasRole' +import hasPermi from './permission/hasPermi' +import copyText from './common/copyText' + +export default function directive(app){ + app.directive('hasRole', hasRole) + app.directive('hasPermi', hasPermi) + app.directive('copyText', copyText) +} \ No newline at end of file diff --git a/src/directive/permission/hasPermi.js b/src/directive/permission/hasPermi.js new file mode 100644 index 0000000..87ed5a6 --- /dev/null +++ b/src/directive/permission/hasPermi.js @@ -0,0 +1,27 @@ + /** + * 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 permissions = useUserStore().permissions + + if (value && value instanceof Array && value.length > 0) { + const permissionFlag = value + + const hasPermissions = permissions.some(permission => { + return all_permission === permission || permissionFlag.includes(permission) + }) + + if (!hasPermissions) { + el.parentNode && el.parentNode.removeChild(el) + } + } else { + throw new Error(`璇疯缃搷浣滄潈闄愭爣绛惧�糮) + } + } +} diff --git a/src/directive/permission/hasRole.js b/src/directive/permission/hasRole.js new file mode 100644 index 0000000..8d57159 --- /dev/null +++ b/src/directive/permission/hasRole.js @@ -0,0 +1,27 @@ + /** + * 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 roles = useUserStore().roles + + if (value && value instanceof Array && value.length > 0) { + const roleFlag = value + + const hasRole = roles.some(role => { + return super_admin === role || roleFlag.includes(role) + }) + + if (!hasRole) { + el.parentNode && el.parentNode.removeChild(el) + } + } else { + throw new Error(`璇疯缃鑹叉潈闄愭爣绛惧�糮) + } + } +} diff --git a/src/layout/components/AppMain.vue b/src/layout/components/AppMain.vue new file mode 100644 index 0000000..21f3039 --- /dev/null +++ b/src/layout/components/AppMain.vue @@ -0,0 +1,83 @@ +<template> + <section class="app-main"> + <router-view v-slot="{ Component, route }"> + <transition name="fade-transform" mode="out-in"> + <keep-alive :include="tagsViewStore.cachedViews"> + <component v-if="!route.meta.link" :is="Component" :key="route.path"/> + </keep-alive> + </transition> + </router-view> + <iframe-toggle /> + </section> +</template> + +<script setup> +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> +.app-main { + /* 50= navbar 50 */ + min-height: calc(100vh - 50px); + width: 100%; + position: relative; + overflow: hidden; +} + +.fixed-header + .app-main { + padding-top: 50px; +} + +.hasTagsView { + .app-main { + /* 84 = navbar + tags-view = 50 + 34 */ + min-height: calc(100vh - 84px); + } + + .fixed-header + .app-main { + padding-top: 84px; + } +} +</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; +} + +::-webkit-scrollbar-track { + background-color: #f1f1f1; +} + +::-webkit-scrollbar-thumb { + background-color: #c0c0c0; + border-radius: 3px; +} +</style> + diff --git a/src/layout/components/IframeToggle/index.vue b/src/layout/components/IframeToggle/index.vue new file mode 100644 index 0000000..9245b7f --- /dev/null +++ b/src/layout/components/IframeToggle/index.vue @@ -0,0 +1,25 @@ +<template> + <inner-link + v-for="(item, index) in tagsViewStore.iframeViews" + :key="item.path" + :iframeId="'iframe' + index" + v-show="route.path === item.path" + :src="iframeUrl(item.meta.link, item.query)" + ></inner-link> +</template> + +<script setup> +import InnerLink from "../InnerLink/index" +import useTagsViewStore from "@/store/modules/tagsView" + +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 + } + return url +} +</script> diff --git a/src/layout/components/InnerLink/index.vue b/src/layout/components/InnerLink/index.vue new file mode 100644 index 0000000..0b43aab --- /dev/null +++ b/src/layout/components/InnerLink/index.vue @@ -0,0 +1,35 @@ +<template> + <div :style="'height:' + height" v-loading="loading" element-loading-text="姝e湪鍔犺浇椤甸潰锛岃绋嶅�欙紒"> + <iframe + :id="iframeId" + style="width: 100%; height: 100%" + :src="src" + ref="iframeRef" + frameborder="no" + ></iframe> + </div> +</template> + +<script setup> +const props = defineProps({ + src: { + type: String, + default: "/" + }, + iframeId: { + type: String + } +}) + +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> diff --git a/src/layout/components/Navbar.vue b/src/layout/components/Navbar.vue new file mode 100644 index 0000000..cab43c5 --- /dev/null +++ b/src/layout/components/Navbar.vue @@ -0,0 +1,224 @@ +<template> + <div class="navbar"> + <hamburger id="hamburger-container" :is-active="appStore.sidebar.opened" class="hamburger-container" @toggleClick="toggleSideBar" /> + <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'"> + <header-search id="header-search" class="right-menu-item" /> + + <el-tooltip content="婧愮爜鍦板潃" effect="dark" placement="bottom"> + <ruo-yi-git id="ruoyi-git" class="right-menu-item hover-effect" /> + </el-tooltip> + + <el-tooltip content="鏂囨。鍦板潃" effect="dark" placement="bottom"> + <ruo-yi-doc id="ruoyi-doc" class="right-menu-item hover-effect" /> + </el-tooltip> + + <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> + + <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 divided command="logout"> + <span>閫�鍑虹櫥褰�</span> + </el-dropdown-item> + </el-dropdown-menu> + </template> + </el-dropdown> + <div class="right-menu-item hover-effect setting" @click="setLayout" v-if="settingsStore.showSettings"> + <svg-icon icon-class="more-up" /> + </div> + </div> + </div> +</template> + +<script setup> +import { ElMessageBox } from 'element-plus' +import Breadcrumb from '@/components/Breadcrumb' +import TopNav from '@/components/TopNav' +import Hamburger from '@/components/Hamburger' +import Screenfull from '@/components/Screenfull' +import SizeSelect from '@/components/SizeSelect' +import HeaderSearch from '@/components/HeaderSearch' +import RuoYiGit from '@/components/RuoYi/Git' +import RuoYiDoc from '@/components/RuoYi/Doc' +import useAppStore from '@/store/modules/app' +import useUserStore from '@/store/modules/user' +import useSettingsStore from '@/store/modules/settings' + +const appStore = useAppStore() +const userStore = useUserStore() +const settingsStore = useSettingsStore() + +function toggleSideBar() { + appStore.toggleSideBar() +} + +function handleCommand(command) { + switch (command) { + case "setLayout": + setLayout() + break + case "logout": + logout() + break + default: + break + } +} + +function logout() { + ElMessageBox.confirm('纭畾娉ㄩ攢骞堕��鍑虹郴缁熷悧锛�', '鎻愮ず', { + confirmButtonText: '纭畾', + cancelButtonText: '鍙栨秷', + type: 'warning' + }).then(() => { + userStore.logOut().then(() => { + location.href = '/index' + }) + }).catch(() => { }) +} + +const emits = defineEmits(['setLayout']) +function setLayout() { + emits('setLayout') +} + +function toggleTheme() { + settingsStore.toggleTheme() +} +</script> + +<style lang='scss' scoped> +.navbar { + height: 50px; + overflow: hidden; + position: relative; + background: var(--navbar-bg); + box-shadow: 0 1px 4px rgba(0, 21, 41, 0.08); + + .hamburger-container { + line-height: 46px; + height: 100%; + float: left; + cursor: pointer; + transition: background 0.3s; + -webkit-tap-highlight-color: transparent; + + &:hover { + background: rgba(0, 0, 0, 0.025); + } + } + + .breadcrumb-container { + float: left; + } + + .topmenu-container { + position: absolute; + left: 50px; + } + + .errLog-container { + display: inline-block; + vertical-align: top; + } + + .right-menu { + float: right; + height: 100%; + line-height: 50px; + display: flex; + + &:focus { + outline: none; + } + + .right-menu-item { + display: inline-block; + padding: 0 8px; + height: 100%; + font-size: 18px; + color: #5a5e66; + vertical-align: text-bottom; + + &.hover-effect { + cursor: pointer; + transition: background 0.3s; + + &:hover { + 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: 0px; + padding-right: 0px; + + .avatar-wrapper { + margin-top: 10px; + right: 5px; + position: relative; + + .user-avatar { + cursor: pointer; + width: 30px; + height: 30px; + border-radius: 50%; + } + + .user-nickname{ + position: relative; + left: 5px; + bottom: 10px; + font-size: 14px; + font-weight: bold; + } + + i { + cursor: pointer; + position: absolute; + right: -20px; + top: 25px; + font-size: 12px; + } + } + } + } +} +</style> diff --git a/src/layout/components/Settings/index.vue b/src/layout/components/Settings/index.vue new file mode 100644 index 0000000..8232e76 --- /dev/null +++ b/src/layout/components/Settings/index.vue @@ -0,0 +1,205 @@ +<template> + <el-drawer v-model="showSettings" :withHeader="false" direction="rtl" size="300px"> + <div class="setting-drawer-title"> + <h3 class="drawer-title">涓婚椋庢牸璁剧疆</h3> + </div> + <div class="setting-drawer-block-checbox"> + <div class="setting-drawer-block-checbox-item" @click="handleTheme('theme-dark')"> + <img src="@/assets/images/dark.svg" alt="dark" /> + <div v-if="sideTheme === 'theme-dark'" class="setting-drawer-block-checbox-selectIcon" style="display: block;"> + <i aria-label="鍥炬爣: check" class="anticon anticon-check"> + <svg viewBox="64 64 896 896" data-icon="check" width="1em" height="1em" :fill="theme" aria-hidden="true" focusable="false" class> + <path d="M912 190h-69.9c-9.8 0-19.1 4.5-25.1 12.2L404.7 724.5 207 474a32 32 0 0 0-25.1-12.2H112c-6.7 0-10.4 7.7-6.3 12.9l273.9 347c12.8 16.2 37.4 16.2 50.3 0l488.4-618.9c4.1-5.1.4-12.8-6.3-12.8z" /> + </svg> + </i> + </div> + </div> + <div class="setting-drawer-block-checbox-item" @click="handleTheme('theme-light')"> + <img src="@/assets/images/light.svg" alt="light" /> + <div v-if="sideTheme === 'theme-light'" class="setting-drawer-block-checbox-selectIcon" style="display: block;"> + <i aria-label="鍥炬爣: check" class="anticon anticon-check"> + <svg viewBox="64 64 896 896" data-icon="check" width="1em" height="1em" :fill="theme" aria-hidden="true" focusable="false" class> + <path d="M912 190h-69.9c-9.8 0-19.1 4.5-25.1 12.2L404.7 724.5 207 474a32 32 0 0 0-25.1-12.2H112c-6.7 0-10.4 7.7-6.3 12.9l273.9 347c12.8 16.2 37.4 16.2 50.3 0l488.4-618.9c4.1-5.1.4-12.8-6.3-12.8z" /> + </svg> + </i> + </div> + </div> + </div> + <div class="drawer-item"> + <span>涓婚棰滆壊</span> + <span class="comp-style"> + <el-color-picker v-model="theme" :predefine="predefineColors" @change="themeChange"/> + </span> + </div> + <el-divider /> + + <h3 class="drawer-title">绯荤粺甯冨眬閰嶇疆</h3> + + <div class="drawer-item"> + <span>寮�鍚� TopNav</span> + <span class="comp-style"> + <el-switch v-model="settingsStore.topNav" @change="topNavChange" class="drawer-switch" /> + </span> + </div> + + <div class="drawer-item"> + <span>寮�鍚� Tags-Views</span> + <span class="comp-style"> + <el-switch v-model="settingsStore.tagsView" class="drawer-switch" /> + </span> + </div> + + <div class="drawer-item"> + <span>鍥哄畾 Header</span> + <span class="comp-style"> + <el-switch v-model="settingsStore.fixedHeader" class="drawer-switch" /> + </span> + </div> + + <div class="drawer-item"> + <span>鏄剧ず Logo</span> + <span class="comp-style"> + <el-switch v-model="settingsStore.sidebarLogo" class="drawer-switch" /> + </span> + </div> + + <div class="drawer-item"> + <span>鍔ㄦ�佹爣棰�</span> + <span class="comp-style"> + <el-switch v-model="settingsStore.dynamicTitle" @change="dynamicTitleChange" class="drawer-switch" /> + </span> + </div> + + <el-divider /> + + <el-button type="primary" plain icon="DocumentAdd" @click="saveSetting">淇濆瓨閰嶇疆</el-button> + <el-button plain icon="Refresh" @click="resetSetting">閲嶇疆閰嶇疆</el-button> + </el-drawer> + +</template> + +<script setup> +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 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"]) + +/** 鏄惁闇�瑕乼opnav */ +function topNavChange(val) { + if (!val) { + appStore.toggleSideBarHide(false) + permissionStore.setSidebarRouters(permissionStore.defaultRoutes) + } +} + +/** 鏄惁闇�瑕乨ynamicTitle */ +function dynamicTitleChange() { + useSettingsStore().setTitle(useSettingsStore().title) +} + +function themeChange(val) { + settingsStore.theme = val + handleThemeStyle(val) +} + +function handleTheme(val) { + settingsStore.sideTheme = val + sideTheme.value = val +} + +function saveSetting() { + proxy.$modal.loading("姝e湪淇濆瓨鍒版湰鍦帮紝璇风◢鍊�...") + let layoutSetting = { + "topNav": storeSettings.value.topNav, + "tagsView": storeSettings.value.tagsView, + "fixedHeader": storeSettings.value.fixedHeader, + "sidebarLogo": storeSettings.value.sidebarLogo, + "dynamicTitle": storeSettings.value.dynamicTitle, + "sideTheme": storeSettings.value.sideTheme, + "theme": storeSettings.value.theme + } + localStorage.setItem("layout-setting", JSON.stringify(layoutSetting)) + setTimeout(proxy.$modal.closeLoading(), 1000) +} + +function resetSetting() { + proxy.$modal.loading("姝e湪娓呴櫎璁剧疆缂撳瓨骞跺埛鏂帮紝璇风◢鍊�...") + localStorage.removeItem("layout-setting") + setTimeout("window.location.reload()", 1000) +} + +function openSetting() { + showSettings.value = true +} + +defineExpose({ + openSetting +}) +</script> + +<style lang='scss' scoped> +.setting-drawer-title { + margin-bottom: 12px; + 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; + align-items: center; + margin-top: 10px; + margin-bottom: 20px; + + .setting-drawer-block-checbox-item { + position: relative; + margin-right: 16px; + border-radius: 2px; + cursor: pointer; + + img { + width: 48px; + height: 48px; + } + + .setting-drawer-block-checbox-selectIcon { + position: absolute; + top: 0; + right: 0; + width: 100%; + height: 100%; + padding-top: 15px; + padding-left: 24px; + color: #1890ff; + font-weight: 700; + font-size: 14px; + } + } +} + +.drawer-item { + color: var(--el-text-color-regular, rgba(0, 0, 0, 0.65)); + padding: 12px 0; + font-size: 14px; + + .comp-style { + float: right; + margin: -3px 8px 0px 0px; + } +} +</style> \ No newline at end of file diff --git a/src/layout/components/Sidebar/Link.vue b/src/layout/components/Sidebar/Link.vue new file mode 100644 index 0000000..8011431 --- /dev/null +++ b/src/layout/components/Sidebar/Link.vue @@ -0,0 +1,40 @@ +<template> + <component :is="type" v-bind="linkProps()"> + <slot /> + </component> +</template> + +<script setup> +import { isExternal } from '@/utils/validate' + +const props = defineProps({ + to: { + type: [String, Object], + required: true + } +}) + +const isExt = computed(() => { + return isExternal(props.to) +}) + +const type = computed(() => { + if (isExt.value) { + return 'a' + } + return 'router-link' +}) + +function linkProps() { + if (isExt.value) { + return { + href: props.to, + target: '_blank', + rel: 'noopener' + } + } + return { + to: props.to + } +} +</script> diff --git a/src/layout/components/Sidebar/Logo.vue b/src/layout/components/Sidebar/Logo.vue new file mode 100644 index 0000000..b2f1595 --- /dev/null +++ b/src/layout/components/Sidebar/Logo.vue @@ -0,0 +1,99 @@ +<template> + <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">{{ 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">{{ title }}</h1> + </router-link> + </transition> + </div> +</template> + +<script setup> +import logo from '@/assets/logo/logo.png' +import useSettingsStore from '@/store/modules/settings' +import variables from '@/assets/styles/variables.module.scss' + +defineProps({ + collapse: { + type: Boolean, + required: true + } +}) + +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> +@import '@/assets/styles/variables.module.scss'; + +.sidebarLogoFade-enter-active { + transition: opacity 1.5s; +} + +.sidebarLogoFade-enter, +.sidebarLogoFade-leave-to { + opacity: 0; +} + +.sidebar-logo-container { + position: relative; + width: 100%; + height: 50px; + line-height: 50px; + background: v-bind(getLogoBackground); + text-align: center; + overflow: hidden; + + & .sidebar-logo-link { + height: 100%; + width: 100%; + + & .sidebar-logo { + width: 32px; + height: 32px; + vertical-align: middle; + margin-right: 12px; + } + + & .sidebar-title { + display: inline-block; + margin: 0; + color: v-bind(getLogoTextColor); + font-weight: 600; + line-height: 50px; + font-size: 14px; + font-family: Avenir, Helvetica Neue, Arial, Helvetica, sans-serif; + vertical-align: middle; + } + } + + &.collapse { + .sidebar-logo { + margin-right: 0px; + } + } +} +</style> \ No newline at end of file diff --git a/src/layout/components/Sidebar/SidebarItem.vue b/src/layout/components/Sidebar/SidebarItem.vue new file mode 100644 index 0000000..2fa82bb --- /dev/null +++ b/src/layout/components/Sidebar/SidebarItem.vue @@ -0,0 +1,100 @@ +<template> + <div v-if="!item.hidden"> + <template v-if="hasOneShowingChild(item.children, item) && (!onlyOneChild.children || onlyOneChild.noShowingChildren) && !item.alwaysShow"> + <app-link v-if="onlyOneChild.meta" :to="resolvePath(onlyOneChild.path, onlyOneChild.query)"> + <el-menu-item :index="resolvePath(onlyOneChild.path)" :class="{ 'submenu-title-noDropdown': !isNest }"> + <svg-icon :icon-class="onlyOneChild.meta.icon || (item.meta && item.meta.icon)"/> + <template #title><span class="menu-title" :title="hasTitle(onlyOneChild.meta.title)">{{ onlyOneChild.meta.title }}</span></template> + </el-menu-item> + </app-link> + </template> + + <el-sub-menu v-else ref="subMenu" :index="resolvePath(item.path)" teleported> + <template v-if="item.meta" #title> + <svg-icon :icon-class="item.meta && item.meta.icon" /> + <span class="menu-title" :title="hasTitle(item.meta.title)">{{ item.meta.title }}</span> + </template> + + <sidebar-item + v-for="(child, index) in item.children" + :key="child.path + index" + :is-nest="true" + :item="child" + :base-path="resolvePath(child.path)" + class="nest-menu" + /> + </el-sub-menu> + </div> +</template> + +<script setup> +import { isExternal } from '@/utils/validate' +import AppLink from './Link' +import { getNormalPath } from '@/utils/ruoyi' + +const props = defineProps({ + // route object + item: { + type: Object, + required: true + }, + isNest: { + type: Boolean, + default: false + }, + basePath: { + type: String, + default: '' + } +}) + +const onlyOneChild = ref({}) + +function hasOneShowingChild(children = [], parent) { + if (!children) { + children = [] + } + const showingChildren = children.filter(item => { + if (item.hidden) { + return false + } + onlyOneChild.value = item + return true + }) + + // When there is only one child router, the child router is displayed by default + if (showingChildren.length === 1) { + return true + } + + // Show parent if there are no child router to display + if (showingChildren.length === 0) { + onlyOneChild.value = { ...parent, path: '', noShowingChildren: true } + return true + } + + return false +} + +function resolvePath(routePath, routeQuery) { + if (isExternal(routePath)) { + return routePath + } + if (isExternal(props.basePath)) { + return props.basePath + } + if (routeQuery) { + let query = JSON.parse(routeQuery) + return { path: getNormalPath(props.basePath + '/' + routePath), query: query } + } + return getNormalPath(props.basePath + '/' + routePath) +} + +function hasTitle(title){ + if (title.length > 5) { + return title + } else { + return "" + } +} +</script> diff --git a/src/layout/components/Sidebar/index.vue b/src/layout/components/Sidebar/index.vue new file mode 100644 index 0000000..9acc406 --- /dev/null +++ b/src/layout/components/Sidebar/index.vue @@ -0,0 +1,104 @@ +<template> + <div :class="{ 'has-logo': showLogo }" class="sidebar-container"> + <logo v-if="showLogo" :collapse="isCollapse" /> + <el-scrollbar wrap-class="scrollbar-wrapper"> + <el-menu + :default-active="activeMenu" + :collapse="isCollapse" + :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" + :key="route.path + index" + :item="route" + :base-path="route.path" + /> + </el-menu> + </el-scrollbar> + </div> +</template> + +<script setup> +import Logo from './Logo' +import SidebarItem from './SidebarItem' +import variables from '@/assets/styles/variables.module.scss' +import useAppStore from '@/store/modules/app' +import useSettingsStore from '@/store/modules/settings' +import usePermissionStore from '@/store/modules/permission' + +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 getMenuBackground = computed(() => { + if (settingsStore.isDark) { + return 'var(--sidebar-bg)' + } + 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> diff --git a/src/layout/components/TagsView/ScrollPane.vue b/src/layout/components/TagsView/ScrollPane.vue new file mode 100644 index 0000000..03be598 --- /dev/null +++ b/src/layout/components/TagsView/ScrollPane.vue @@ -0,0 +1,107 @@ +<template> + <el-scrollbar + ref="scrollContainer" + :vertical="false" + class="scroll-container" + @wheel.prevent="handleScroll" + > + <slot /> + </el-scrollbar> +</template> + +<script setup> +import useTagsViewStore from '@/store/modules/tagsView' + +const tagAndTagSpacing = ref(4) +const { proxy } = getCurrentInstance() + +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 + $scrollWrapper.scrollLeft = $scrollWrapper.scrollLeft + eventDelta / 4 +} + +const emits = defineEmits() +const emitScroll = () => { + emits('scroll') +} + +const tagsViewStore = useTagsViewStore() +const visitedViews = computed(() => tagsViewStore.visitedViews) + +function moveToTarget(currentTag) { + const $container = proxy.$refs.scrollContainer.$el + const $containerWidth = $container.offsetWidth + const $scrollWrapper = scrollWrapper.value + + let firstTag = null + let lastTag = null + + // find first tag and last tag + if (visitedViews.value.length > 0) { + firstTag = visitedViews.value[0] + lastTag = visitedViews.value[visitedViews.value.length - 1] + } + + if (firstTag === currentTag) { + $scrollWrapper.scrollLeft = 0 + } else if (lastTag === currentTag) { + $scrollWrapper.scrollLeft = $scrollWrapper.scrollWidth - $containerWidth + } else { + 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] + } + if (tagListDom[k].dataset.path === visitedViews.value[currentIndex + 1].path) { + nextTag = tagListDom[k] + } + } + } + + // the tag's offsetLeft after of nextTag + const afterNextTagOffsetLeft = nextTag.offsetLeft + nextTag.offsetWidth + tagAndTagSpacing.value + + // the tag's offsetLeft before of prevTag + const beforePrevTagOffsetLeft = prevTag.offsetLeft - tagAndTagSpacing.value + if (afterNextTagOffsetLeft > $scrollWrapper.scrollLeft + $containerWidth) { + $scrollWrapper.scrollLeft = afterNextTagOffsetLeft - $containerWidth + } else if (beforePrevTagOffsetLeft < $scrollWrapper.scrollLeft) { + $scrollWrapper.scrollLeft = beforePrevTagOffsetLeft + } + } +} + +defineExpose({ + moveToTarget, +}) +</script> + +<style lang='scss' scoped> +.scroll-container { + white-space: nowrap; + position: relative; + overflow: hidden; + width: 100%; + :deep(.el-scrollbar__bar) { + bottom: 0px; + } + :deep(.el-scrollbar__wrap) { + height: 39px; + } +} +</style> \ No newline at end of file diff --git a/src/layout/components/TagsView/index.vue b/src/layout/components/TagsView/index.vue new file mode 100644 index 0000000..91e8cfb --- /dev/null +++ b/src/layout/components/TagsView/index.vue @@ -0,0 +1,365 @@ +<template> + <div id="tags-view-container" class="tags-view-container"> + <scroll-pane ref="scrollPaneRef" class="tags-view-wrapper" @scroll="handleScroll"> + <router-link + v-for="tag in visitedViews" + :key="tag.path" + :data-path="tag.path" + :class="isActive(tag) ? 'active' : ''" + :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)" + > + {{ 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;" /> + </span> + </router-link> + </scroll-pane> + <ul v-show="visible" :style="{ left: left + 'px', top: top + 'px' }" class="contextmenu"> + <li @click="refreshSelectedTag(selectedTag)"> + <refresh-right style="width: 1em; height: 1em;" /> 鍒锋柊椤甸潰 + </li> + <li v-if="!isAffix(selectedTag)" @click="closeSelectedTag(selectedTag)"> + <close style="width: 1em; height: 1em;" /> 鍏抽棴褰撳墠 + </li> + <li @click="closeOthersTags"> + <circle-close style="width: 1em; height: 1em;" /> 鍏抽棴鍏朵粬 + </li> + <li v-if="!isFirstView()" @click="closeLeftTags"> + <back style="width: 1em; height: 1em;" /> 鍏抽棴宸︿晶 + </li> + <li v-if="!isLastView()" @click="closeRightTags"> + <right style="width: 1em; height: 1em;" /> 鍏抽棴鍙充晶 + </li> + <li @click="closeAllTags(selectedTag)"> + <circle-close style="width: 1em; height: 1em;" /> 鍏ㄩ儴鍏抽棴 + </li> + </ul> + </div> +</template> + +<script setup> +import ScrollPane from './ScrollPane' +import { getNormalPath } from '@/utils/ruoyi' +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 { proxy } = getCurrentInstance() +const route = useRoute() +const router = useRouter() + +const visitedViews = computed(() => useTagsViewStore().visitedViews) +const routes = computed(() => usePermissionStore().routes) +const theme = computed(() => useSettingsStore().theme) + +watch(route, () => { + addTags() + moveToCurrentTag() +}) + +watch(visible, (value) => { + if (value) { + document.body.addEventListener('click', closeMenu) + } else { + document.body.removeEventListener('click', closeMenu) + } +}) + +onMounted(() => { + initTags() + addTags() +}) + +function isActive(r) { + return r.path === route.path +} + +function activeStyle(tag) { + 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 + } catch (err) { + return false + } +} + +function isLastView() { + try { + return selectedTag.value.fullPath === visitedViews.value[visitedViews.value.length - 1].fullPath + } catch (err) { + return false + } +} + +function filterAffixTags(routes, basePath = '') { + let tags = [] + routes.forEach(route => { + if (route.meta && route.meta.affix) { + const tagPath = getNormalPath(basePath + '/' + route.path) + tags.push({ + fullPath: tagPath, + path: tagPath, + name: route.name, + meta: { ...route.meta } + }) + } + if (route.children) { + const tempTags = filterAffixTags(route.children, route.path) + if (tempTags.length >= 1) { + tags = [...tags, ...tempTags] + } + } + }) + return tags +} + +function initTags() { + const res = filterAffixTags(routes.value) + affixTags.value = res + for (const tag of res) { + // Must have tag name + if (tag.name) { + useTagsViewStore().addVisitedView(tag) + } + } +} + +function addTags() { + const { name } = route + if (name) { + useTagsViewStore().addView(route) + } +} + +function moveToCurrentTag() { + nextTick(() => { + for (const r of visitedViews.value) { + if (r.path === route.path) { + scrollPaneRef.value.moveToTarget(r) + // when query is different then update + if (r.fullPath !== route.fullPath) { + useTagsViewStore().updateVisitedView(route) + } + } + } + }) +} + +function refreshSelectedTag(view) { + proxy.$tab.refreshPage(view) + if (route.meta.link) { + useTagsViewStore().delIframeView(route) + } +} + +function closeSelectedTag(view) { + proxy.$tab.closePage(view).then(({ visitedViews }) => { + if (isActive(view)) { + toLastView(visitedViews, view) + } + }) +} + +function closeRightTags() { + proxy.$tab.closeRightPage(selectedTag.value).then(visitedViews => { + if (!visitedViews.find(i => i.fullPath === route.fullPath)) { + toLastView(visitedViews) + } + }) +} + +function closeLeftTags() { + proxy.$tab.closeLeftPage(selectedTag.value).then(visitedViews => { + if (!visitedViews.find(i => i.fullPath === route.fullPath)) { + toLastView(visitedViews) + } + }) +} + +function closeOthersTags() { + 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)) { + return + } + toLastView(visitedViews, view) + }) +} + +function toLastView(visitedViews, view) { + const latestView = visitedViews.slice(-1)[0] + if (latestView) { + router.push(latestView.fullPath) + } else { + // now the default is to redirect to the home page if there is no tags-view, + // you can adjust it according to your needs. + if (view.name === 'Dashboard') { + // to reload home page + router.replace({ path: '/redirect' + view.fullPath }) + } else { + router.push('/') + } + } +} + +function openMenu(tag, e) { + const menuMinWidth = 105 + const offsetLeft = proxy.$el.getBoundingClientRect().left // container margin left + const offsetWidth = proxy.$el.offsetWidth // container width + const maxLeft = offsetWidth - menuMinWidth // left boundary + const l = e.clientX - offsetLeft + 15 // 15: margin right + + if (l > maxLeft) { + left.value = maxLeft + } else { + left.value = l + } + + top.value = e.clientY + visible.value = true + selectedTag.value = tag +} + +function closeMenu() { + visible.value = false +} + +function handleScroll() { + closeMenu() +} +</script> + +<style lang="scss" scoped> +.tags-view-container { + height: 34px; + width: 100%; + 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; + position: relative; + cursor: pointer; + height: 26px; + line-height: 26px; + 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: ''; + background: #fff; + display: inline-block; + width: 8px; + height: 8px; + border-radius: 50%; + position: relative; + margin-right: 5px; + } + } + } + } + + .contextmenu { + margin: 0; + background: var(--el-bg-color-overlay, #fff); + z-index: 3000; + position: absolute; + list-style-type: none; + padding: 5px 0; + border-radius: 4px; + font-size: 12px; + font-weight: 400; + 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: var(--tags-item-hover, #eee); + } + } + } +} +</style> + +<style lang="scss"> +//reset element css of el-icon-close +.tags-view-wrapper { + .tags-view-item { + .el-icon-close { + width: 16px; + height: 16px; + vertical-align: 2px; + border-radius: 50%; + text-align: center; + transition: all .3s cubic-bezier(.645, .045, .355, 1); + transform-origin: 100% 50%; + + &:before { + transform: scale(.6); + display: inline-block; + vertical-align: -3px; + } + + &:hover { + background-color: var(--tags-close-hover, #b4bccc); + color: #fff; + width: 12px !important; + height: 12px !important; + } + } + } +} +</style> \ No newline at end of file diff --git a/src/layout/components/index.js b/src/layout/components/index.js new file mode 100644 index 0000000..fd57731 --- /dev/null +++ b/src/layout/components/index.js @@ -0,0 +1,4 @@ +export { default as AppMain } from './AppMain' +export { default as Navbar } from './Navbar' +export { default as Settings } from './Settings' +export { default as TagsView } from './TagsView/index.vue' diff --git a/src/layout/index.vue b/src/layout/index.vue new file mode 100644 index 0000000..fcdc475 --- /dev/null +++ b/src/layout/index.vue @@ -0,0 +1,112 @@ +<template> + <div :class="classObj" class="app-wrapper" :style="{ '--current-color': theme }"> + <div v-if="device === 'mobile' && sidebar.opened" class="drawer-bg" @click="handleClickOutside"/> + <sidebar v-if="!sidebar.hide" class="sidebar-container" /> + <div :class="{ hasTagsView: needTagsView, sidebarHide: sidebar.hide }" class="main-container"> + <div :class="{ 'fixed-header': fixedHeader }"> + <navbar @setLayout="setLayout" /> + <tags-view v-if="needTagsView" /> + </div> + <app-main /> + <settings ref="settingRef" /> + </div> + </div> +</template> + +<script setup> +import { useWindowSize } from '@vueuse/core' +import Sidebar from './components/Sidebar/index.vue' +import { AppMain, Navbar, Settings, TagsView } from './components' +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 classObj = computed(() => ({ + hideSidebar: !sidebar.value.opened, + openSidebar: sidebar.value.opened, + withoutAnimation: sidebar.value.withoutAnimation, + mobile: device.value === 'mobile' +})) + +const { width, height } = useWindowSize() +const WIDTH = 992 // refer to Bootstrap's responsive design + +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 }) + } else { + useAppStore().toggleDevice('desktop') + } +}) + +function handleClickOutside() { + useAppStore().closeSideBar({ withoutAnimation: false }) +} + +const settingRef = ref(null) +function setLayout() { + settingRef.value.openSetting() +} +</script> + +<style lang="scss" scoped> + @import "@/assets/styles/mixin.scss"; + @import "@/assets/styles/variables.module.scss"; + +.app-wrapper { + @include clearfix; + position: relative; + height: 100%; + width: 100%; + + &.mobile.openSidebar { + position: fixed; + top: 0; + } +} + +.drawer-bg { + background: #000; + opacity: 0.3; + width: 100%; + top: 0; + height: 100%; + position: absolute; + z-index: 999; +} + +.fixed-header { + position: fixed; + top: 0; + right: 0; + z-index: 9; + width: calc(100% - #{$base-sidebar-width}); + transition: width 0.28s; +} + +.hideSidebar .fixed-header { + width: calc(100% - 54px); +} + +.sidebarHide .fixed-header { + width: 100%; +} + +.mobile .fixed-header { + width: 100%; +} +</style> \ No newline at end of file diff --git a/src/main.js b/src/main.js new file mode 100644 index 0000000..0994583 --- /dev/null +++ b/src/main.js @@ -0,0 +1,82 @@ +import { createApp } from 'vue' + +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 + +import App from './App' +import store from './store' +import router from './router' +import directive from './directive' // directive + +// 娉ㄥ唽鎸囦护 +import plugins from './plugins' // plugins +import { download } from '@/utils/request' + +// svg鍥炬爣 +import 'virtual:svg-icons-register' +import SvgIcon from '@/components/SvgIcon' +import elementIcons from '@/components/SvgIcon/svgicon' + +import './permission' // permission control + +import { useDict } from '@/utils/dict' +import { parseTime, resetForm, addDateRange, handleTree, selectDictLabel, selectDictLabels } from '@/utils/ruoyi' + +// 鍒嗛〉缁勪欢 +import Pagination from '@/components/Pagination' +// 鑷畾涔夎〃鏍煎伐鍏风粍浠� +import RightToolbar from '@/components/RightToolbar' +// 瀵屾枃鏈粍浠� +import Editor from "@/components/Editor" +// 鏂囦欢涓婁紶缁勪欢 +import FileUpload from "@/components/FileUpload" +// 鍥剧墖涓婁紶缁勪欢 +import ImageUpload from "@/components/ImageUpload" +// 鍥剧墖棰勮缁勪欢 +import ImagePreview from "@/components/ImagePreview" +// 瀛楀吀鏍囩缁勪欢 +import DictTag from '@/components/DictTag' + +const app = createApp(App) + +// 鍏ㄥ眬鏂规硶鎸傝浇 +app.config.globalProperties.useDict = useDict +app.config.globalProperties.download = download +app.config.globalProperties.parseTime = parseTime +app.config.globalProperties.resetForm = resetForm +app.config.globalProperties.handleTree = handleTree +app.config.globalProperties.addDateRange = addDateRange +app.config.globalProperties.selectDictLabel = selectDictLabel +app.config.globalProperties.selectDictLabels = selectDictLabels + +// 鍏ㄥ眬缁勪欢鎸傝浇 +app.component('DictTag', DictTag) +app.component('Pagination', Pagination) +app.component('FileUpload', FileUpload) +app.component('ImageUpload', ImageUpload) +app.component('ImagePreview', ImagePreview) +app.component('RightToolbar', RightToolbar) +app.component('Editor', Editor) + +app.use(router) +app.use(store) +app.use(plugins) +app.use(elementIcons) +app.component('svg-icon', SvgIcon) + +directive(app) + +// 浣跨敤element-plus 骞朵笖璁剧疆鍏ㄥ眬鐨勫ぇ灏� +app.use(ElementPlus, { + locale: locale, + // 鏀寔 large銆乨efault銆乻mall + size: Cookies.get('size') || 'default' +}) + +app.mount('#app') diff --git a/src/permission.js b/src/permission.js new file mode 100644 index 0000000..7e3b5bf --- /dev/null +++ b/src/permission.js @@ -0,0 +1,69 @@ +import router from './router' +import { ElMessage } from 'element-plus' +import NProgress from 'nprogress' +import 'nprogress/nprogress.css' +import { getToken } from '@/utils/auth' +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 }) + +const whiteList = ['/login', '/register'] + +const isWhiteList = (path) => { + return whiteList.some(pattern => isPathMatch(pattern, path)) +} + +router.beforeEach((to, from, next) => { + NProgress.start() + if (getToken()) { + to.meta.title && useSettingsStore().setTitle(to.meta.title) + /* has token*/ + if (to.path === '/login') { + next({ path: '/' }) + NProgress.done() + } else if (isWhiteList(to.path)) { + next() + } else { + if (useUserStore().roles.length === 0) { + isRelogin.show = true + // 鍒ゆ柇褰撳墠鐢ㄦ埛鏄惁宸叉媺鍙栧畬user_info淇℃伅 + useUserStore().getInfo().then(() => { + isRelogin.show = false + usePermissionStore().generateRoutes().then(accessRoutes => { + // 鏍规嵁roles鏉冮檺鐢熸垚鍙闂殑璺敱琛� + accessRoutes.forEach(route => { + if (!isHttp(route.path)) { + router.addRoute(route) // 鍔ㄦ�佹坊鍔犲彲璁块棶璺敱琛� + } + }) + next({ ...to, replace: true }) // hack鏂规硶 纭繚addRoutes宸插畬鎴� + }) + }).catch(err => { + useUserStore().logOut().then(() => { + ElMessage.error(err) + next({ path: '/' }) + }) + }) + } else { + next() + } + } + } else { + // 娌℃湁token + if (isWhiteList(to.path)) { + // 鍦ㄥ厤鐧诲綍鐧藉悕鍗曪紝鐩存帴杩涘叆 + next() + } else { + next(`/login?redirect=${to.fullPath}`) // 鍚﹀垯鍏ㄩ儴閲嶅畾鍚戝埌鐧诲綍椤� + NProgress.done() + } + } +}) + +router.afterEach(() => { + NProgress.done() +}) diff --git a/src/plugins/auth.js b/src/plugins/auth.js new file mode 100644 index 0000000..1354e87 --- /dev/null +++ b/src/plugins/auth.js @@ -0,0 +1,60 @@ +import useUserStore from '@/store/modules/user' + +function authPermission(permission) { + const all_permission = "*:*:*" + const permissions = useUserStore().permissions + if (permission && permission.length > 0) { + return permissions.some(v => { + return all_permission === v || v === permission + }) + } else { + return false + } +} + +function authRole(role) { + const super_admin = "admin" + const roles = useUserStore().roles + if (role && role.length > 0) { + return roles.some(v => { + return super_admin === v || v === role + }) + } else { + return false + } +} + +export default { + // 楠岃瘉鐢ㄦ埛鏄惁鍏峰鏌愭潈闄� + hasPermi(permission) { + return authPermission(permission) + }, + // 楠岃瘉鐢ㄦ埛鏄惁鍚湁鎸囧畾鏉冮檺锛屽彧闇�鍖呭惈鍏朵腑涓�涓� + hasPermiOr(permissions) { + return permissions.some(item => { + return authPermission(item) + }) + }, + // 楠岃瘉鐢ㄦ埛鏄惁鍚湁鎸囧畾鏉冮檺锛屽繀椤诲叏閮ㄦ嫢鏈� + hasPermiAnd(permissions) { + return permissions.every(item => { + return authPermission(item) + }) + }, + // 楠岃瘉鐢ㄦ埛鏄惁鍏峰鏌愯鑹� + hasRole(role) { + return authRole(role) + }, + // 楠岃瘉鐢ㄦ埛鏄惁鍚湁鎸囧畾瑙掕壊锛屽彧闇�鍖呭惈鍏朵腑涓�涓� + hasRoleOr(roles) { + return roles.some(item => { + return authRole(item) + }) + }, + // 楠岃瘉鐢ㄦ埛鏄惁鍚湁鎸囧畾瑙掕壊锛屽繀椤诲叏閮ㄦ嫢鏈� + hasRoleAnd(roles) { + return roles.every(item => { + return authRole(item) + }) + } +} diff --git a/src/plugins/cache.js b/src/plugins/cache.js new file mode 100644 index 0000000..ea8a53f --- /dev/null +++ b/src/plugins/cache.js @@ -0,0 +1,79 @@ +const sessionCache = { + set (key, value) { + if (!sessionStorage) { + return + } + if (key != null && value != null) { + sessionStorage.setItem(key, value) + } + }, + get (key) { + if (!sessionStorage) { + return null + } + if (key == null) { + return null + } + return sessionStorage.getItem(key) + }, + setJSON (key, jsonValue) { + if (jsonValue != null) { + this.set(key, JSON.stringify(jsonValue)) + } + }, + getJSON (key) { + const value = this.get(key) + if (value != null) { + return JSON.parse(value) + } + return null + }, + remove (key) { + sessionStorage.removeItem(key) + } +} +const localCache = { + set (key, value) { + if (!localStorage) { + return + } + if (key != null && value != null) { + localStorage.setItem(key, value) + } + }, + get (key) { + if (!localStorage) { + return null + } + if (key == null) { + return null + } + return localStorage.getItem(key) + }, + setJSON (key, jsonValue) { + if (jsonValue != null) { + this.set(key, JSON.stringify(jsonValue)) + } + }, + getJSON (key) { + const value = this.get(key) + if (value != null) { + return JSON.parse(value) + } + return null + }, + remove (key) { + localStorage.removeItem(key) + } +} + +export default { + /** + * 浼氳瘽绾х紦瀛� + */ + session: sessionCache, + /** + * 鏈湴缂撳瓨 + */ + local: localCache +} diff --git a/src/plugins/download.js b/src/plugins/download.js new file mode 100644 index 0000000..e83e43c --- /dev/null +++ b/src/plugins/download.js @@ -0,0 +1,79 @@ +锘縤mport axios from 'axios' +import { ElLoading, ElMessage } from 'element-plus' +import { saveAs } from 'file-saver' +import { getToken } from '@/utils/auth' +import errorCode from '@/utils/errorCode' +import { blobValidate } from '@/utils/ruoyi' + +const baseURL = import.meta.env.VITE_APP_BASE_API +let downloadLoadingInstance + +export default { + name(name, isDelete = true) { + var url = baseURL + "/common/download?fileName=" + encodeURIComponent(name) + "&delete=" + isDelete + axios({ + method: 'get', + url: url, + responseType: 'blob', + headers: { 'Authorization': 'Bearer ' + getToken() } + }).then((res) => { + 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) + } + }) + }, + resource(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) + if (isBlob) { + const blob = new Blob([res.data]) + this.saveAs(blob, decodeURIComponent(res.headers['download-filename'])) + } else { + this.printErrMsg(res.data) + } + }) + }, + zip(url, name) { + var url = baseURL + url + downloadLoadingInstance = ElLoading.service({ text: "姝e湪涓嬭浇鏁版嵁锛岃绋嶅��", background: "rgba(0, 0, 0, 0.7)", }) + axios({ + method: 'get', + url: url, + responseType: 'blob', + headers: { 'Authorization': 'Bearer ' + getToken() } + }).then((res) => { + const isBlob = blobValidate(res.data) + if (isBlob) { + const blob = new Blob([res.data], { type: 'application/zip' }) + this.saveAs(blob, name) + } else { + this.printErrMsg(res.data) + } + downloadLoadingInstance.close() + }).catch((r) => { + console.error(r) + ElMessage.error('涓嬭浇鏂囦欢鍑虹幇閿欒锛岃鑱旂郴绠$悊鍛橈紒') + downloadLoadingInstance.close() + }) + }, + saveAs(text, name, opts) { + saveAs(text, name, opts) + }, + async printErrMsg(data) { + const resText = await data.text() + const rspObj = JSON.parse(resText) + const errMsg = errorCode[rspObj.code] || rspObj.msg || errorCode['default'] + ElMessage.error(errMsg) + } +} + diff --git a/src/plugins/index.js b/src/plugins/index.js new file mode 100644 index 0000000..47d1b41 --- /dev/null +++ b/src/plugins/index.js @@ -0,0 +1,18 @@ +import tab from './tab' +import auth from './auth' +import cache from './cache' +import modal from './modal' +import download from './download' + +export default function installPlugins(app){ + // 椤电鎿嶄綔 + app.config.globalProperties.$tab = tab + // 璁よ瘉瀵硅薄 + app.config.globalProperties.$auth = auth + // 缂撳瓨瀵硅薄 + app.config.globalProperties.$cache = cache + // 妯℃�佹瀵硅薄 + app.config.globalProperties.$modal = modal + // 涓嬭浇鏂囦欢 + app.config.globalProperties.$download = download +} diff --git a/src/plugins/modal.js b/src/plugins/modal.js new file mode 100644 index 0000000..01d6149 --- /dev/null +++ b/src/plugins/modal.js @@ -0,0 +1,82 @@ +import { ElMessage, ElMessageBox, ElNotification, ElLoading } from 'element-plus' + +let loadingInstance + +export default { + // 娑堟伅鎻愮ず + msg(content) { + ElMessage.info(content) + }, + // 閿欒娑堟伅 + msgError(content) { + ElMessage.error(content) + }, + // 鎴愬姛娑堟伅 + msgSuccess(content) { + ElMessage.success(content) + }, + // 璀﹀憡娑堟伅 + msgWarning(content) { + ElMessage.warning(content) + }, + // 寮瑰嚭鎻愮ず + alert(content) { + ElMessageBox.alert(content, "绯荤粺鎻愮ず") + }, + // 閿欒鎻愮ず + alertError(content) { + ElMessageBox.alert(content, "绯荤粺鎻愮ず", { type: 'error' }) + }, + // 鎴愬姛鎻愮ず + alertSuccess(content) { + ElMessageBox.alert(content, "绯荤粺鎻愮ず", { type: 'success' }) + }, + // 璀﹀憡鎻愮ず + alertWarning(content) { + ElMessageBox.alert(content, "绯荤粺鎻愮ず", { type: 'warning' }) + }, + // 閫氱煡鎻愮ず + notify(content) { + ElNotification.info(content) + }, + // 閿欒閫氱煡 + notifyError(content) { + ElNotification.error(content) + }, + // 鎴愬姛閫氱煡 + notifySuccess(content) { + ElNotification.success(content) + }, + // 璀﹀憡閫氱煡 + notifyWarning(content) { + ElNotification.warning(content) + }, + // 纭绐椾綋 + confirm(content) { + return ElMessageBox.confirm(content, "绯荤粺鎻愮ず", { + confirmButtonText: '纭畾', + cancelButtonText: '鍙栨秷', + type: "warning", + }) + }, + // 鎻愪氦鍐呭 + prompt(content) { + return ElMessageBox.prompt(content, "绯荤粺鎻愮ず", { + confirmButtonText: '纭畾', + cancelButtonText: '鍙栨秷', + type: "warning", + }) + }, + // 鎵撳紑閬僵灞� + loading(content) { + loadingInstance = ElLoading.service({ + lock: true, + text: content, + background: "rgba(0, 0, 0, 0.7)", + }) + }, + // 鍏抽棴閬僵灞� + closeLoading() { + loadingInstance.close() + } +} diff --git a/src/plugins/tab.js b/src/plugins/tab.js new file mode 100644 index 0000000..578607c --- /dev/null +++ b/src/plugins/tab.js @@ -0,0 +1,71 @@ +import useTagsViewStore from '@/store/modules/tagsView' +import router from '@/router' + +export default { + // 鍒锋柊褰撳墠tab椤电 + refreshPage(obj) { + 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 } + } + } + }) + } + return useTagsViewStore().delCachedView(obj).then(() => { + const { path, query } = obj + router.replace({ + path: '/redirect' + path, + query: query + }) + }) + }, + // 鍏抽棴褰撳墠tab椤电锛屾墦寮�鏂伴〉绛� + closeOpenPage(obj) { + useTagsViewStore().delView(router.currentRoute.value) + if (obj !== undefined) { + return router.push(obj) + } + }, + // 鍏抽棴鎸囧畾tab椤电 + closePage(obj) { + if (obj === undefined) { + return useTagsViewStore().delView(router.currentRoute.value).then(({ visitedViews }) => { + const latestView = visitedViews.slice(-1)[0] + if (latestView) { + return router.push(latestView.fullPath) + } + return router.push('/') + }) + } + return useTagsViewStore().delView(obj) + }, + // 鍏抽棴鎵�鏈塼ab椤电 + closeAllPage() { + return useTagsViewStore().delAllViews() + }, + // 鍏抽棴宸︿晶tab椤电 + closeLeftPage(obj) { + return useTagsViewStore().delLeftTags(obj || router.currentRoute.value) + }, + // 鍏抽棴鍙充晶tab椤电 + closeRightPage(obj) { + return useTagsViewStore().delRightTags(obj || router.currentRoute.value) + }, + // 鍏抽棴鍏朵粬tab椤电 + closeOtherPage(obj) { + return useTagsViewStore().delOthersViews(obj || router.currentRoute.value) + }, + // 鎵撳紑tab椤电 + 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) + } +} diff --git a/src/router/index.js b/src/router/index.js new file mode 100644 index 0000000..627cc33 --- /dev/null +++ b/src/router/index.js @@ -0,0 +1,174 @@ +import { createWebHistory, createRouter } from 'vue-router' +/* Layout */ +import Layout from '@/layout' + +/** + * Note: 璺敱閰嶇疆椤� + * + * hidden: true // 褰撹缃� true 鐨勬椂鍊欒璺敱涓嶄細鍐嶄晶杈规爮鍑虹幇 濡�401锛宭ogin绛夐〉闈紝鎴栬�呭涓�浜涚紪杈戦〉闈�/edit/1 + * alwaysShow: true // 褰撲綘涓�涓矾鐢变笅闈㈢殑 children 澹版槑鐨勮矾鐢卞ぇ浜�1涓椂锛岃嚜鍔ㄤ細鍙樻垚宓屽鐨勬ā寮�--濡傜粍浠堕〉闈� + * // 鍙湁涓�涓椂锛屼細灏嗛偅涓瓙璺敱褰撳仛鏍硅矾鐢辨樉绀哄湪渚ц竟鏍�--濡傚紩瀵奸〉闈� + * // 鑻ヤ綘鎯充笉绠¤矾鐢变笅闈㈢殑 children 澹版槑鐨勪釜鏁伴兘鏄剧ず浣犵殑鏍硅矾鐢� + * // 浣犲彲浠ヨ缃� alwaysShow: true锛岃繖鏍峰畠灏变細蹇界暐涔嬪墠瀹氫箟鐨勮鍒欙紝涓�鐩存樉绀烘牴璺敱 + * redirect: noRedirect // 褰撹缃� noRedirect 鐨勬椂鍊欒璺敱鍦ㄩ潰鍖呭睉瀵艰埅涓笉鍙鐐瑰嚮 + * name:'router-name' // 璁惧畾璺敱鐨勫悕瀛楋紝涓�瀹氳濉啓涓嶇劧浣跨敤<keep-alive>鏃朵細鍑虹幇鍚勭闂 + * query: '{"id": 1, "name": "ry"}' // 璁块棶璺敱鐨勯粯璁や紶閫掑弬鏁� + * roles: ['admin', 'common'] // 璁块棶璺敱鐨勮鑹叉潈闄� + * permissions: ['a:a:a', 'b:b:b'] // 璁块棶璺敱鐨勮彍鍗曟潈闄� + * meta : { + noCache: true // 濡傛灉璁剧疆涓簍rue锛屽垯涓嶄細琚� <keep-alive> 缂撳瓨(榛樿 false) + title: 'title' // 璁剧疆璇ヨ矾鐢卞湪渚ц竟鏍忓拰闈㈠寘灞戜腑灞曠ず鐨勫悕瀛� + icon: 'svg-name' // 璁剧疆璇ヨ矾鐢辩殑鍥炬爣锛屽搴旇矾寰剆rc/assets/icons/svg + breadcrumb: false // 濡傛灉璁剧疆涓篺alse锛屽垯涓嶄細鍦╞readcrumb闈㈠寘灞戜腑鏄剧ず + activeMenu: '/system/user' // 褰撹矾鐢辫缃簡璇ュ睘鎬э紝鍒欎細楂樹寒鐩稿搴旂殑渚ц竟鏍忋�� + } + */ + +// 鍏叡璺敱 +export const constantRoutes = [ + { + path: '/redirect', + component: Layout, + hidden: true, + children: [ + { + path: '/redirect/:path(.*)', + component: () => import('@/views/redirect/index.vue') + } + ] + }, + { + path: '/login', + component: () => import('@/views/login'), + hidden: true + }, + { + path: '/register', + component: () => import('@/views/register'), + hidden: true + }, + { + path: "/:pathMatch(.*)*", + component: () => import('@/views/error/404'), + hidden: true + }, + { + path: '/401', + component: () => import('@/views/error/401'), + hidden: true + }, + { + path: '', + component: Layout, + redirect: '/index', + children: [ + { + path: '/index', + component: () => import('@/views/index'), + name: 'Index', + meta: { title: '棣栭〉', icon: 'dashboard', affix: true } + } + ] + }, + { + path: '/user', + component: Layout, + hidden: true, + redirect: 'noredirect', + children: [ + { + path: 'profile', + component: () => import('@/views/system/user/profile/index'), + name: 'Profile', + meta: { title: '涓汉涓績', icon: 'user' } + } + ] + } +] + +// 鍔ㄦ�佽矾鐢憋紝鍩轰簬鐢ㄦ埛鏉冮檺鍔ㄦ�佸幓鍔犺浇 +export const dynamicRoutes = [ + { + path: '/system/user-auth', + component: Layout, + hidden: true, + permissions: ['system:user:edit'], + children: [ + { + path: 'role/:userId(\\d+)', + component: () => import('@/views/system/user/authRole'), + name: 'AuthRole', + meta: { title: '鍒嗛厤瑙掕壊', activeMenu: '/system/user' } + } + ] + }, + { + path: '/system/role-auth', + component: Layout, + hidden: true, + permissions: ['system:role:edit'], + children: [ + { + path: 'user/:roleId(\\d+)', + component: () => import('@/views/system/role/authUser'), + name: 'AuthUser', + meta: { title: '鍒嗛厤鐢ㄦ埛', activeMenu: '/system/role' } + } + ] + }, + { + path: '/system/dict-data', + component: Layout, + hidden: true, + permissions: ['system:dict:list'], + children: [ + { + path: 'index/:dictId(\\d+)', + component: () => import('@/views/system/dict/data'), + name: 'Data', + meta: { title: '瀛楀吀鏁版嵁', activeMenu: '/system/dict' } + } + ] + }, + { + path: '/monitor/job-log', + component: Layout, + hidden: true, + permissions: ['monitor:job:list'], + children: [ + { + path: 'index/:jobId(\\d+)', + component: () => import('@/views/monitor/job/log'), + name: 'JobLog', + meta: { title: '璋冨害鏃ュ織', activeMenu: '/monitor/job' } + } + ] + }, + { + path: '/tool/gen-edit', + component: Layout, + hidden: true, + permissions: ['tool:gen:edit'], + children: [ + { + path: 'index/:tableId(\\d+)', + component: () => import('@/views/tool/gen/editTable'), + name: 'GenEdit', + meta: { title: '淇敼鐢熸垚閰嶇疆', activeMenu: '/tool/gen' } + } + ] + } +] + +const router = createRouter({ + history: createWebHistory(), + routes: constantRoutes, + scrollBehavior(to, from, savedPosition) { + if (savedPosition) { + return savedPosition + } + return { top: 0 } + }, +}) + +export default router diff --git a/src/settings.js b/src/settings.js new file mode 100644 index 0000000..d67ff12 --- /dev/null +++ b/src/settings.js @@ -0,0 +1,49 @@ +export default { + /** + * 缃戦〉鏍囬 + */ + title: import.meta.env.VITE_APP_TITLE, + + /** + * 渚ц竟鏍忎富棰� 娣辫壊涓婚theme-dark锛屾祬鑹蹭富棰榯heme-light + */ + sideTheme: 'theme-dark', + + /** + * 鏄惁绯荤粺甯冨眬閰嶇疆 + */ + showSettings: true, + + /** + * 鏄惁鏄剧ず椤堕儴瀵艰埅 + */ + topNav: false, + + /** + * 鏄惁鏄剧ず tagsView + */ + tagsView: true, + + /** + * 鏄惁鍥哄畾澶撮儴 + */ + fixedHeader: false, + + /** + * 鏄惁鏄剧ずlogo + */ + sidebarLogo: true, + + /** + * 鏄惁鏄剧ず鍔ㄦ�佹爣棰� + */ + 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' +} diff --git a/src/store/index.js b/src/store/index.js new file mode 100644 index 0000000..f10f389 --- /dev/null +++ b/src/store/index.js @@ -0,0 +1,3 @@ +const store = createPinia() + +export default store \ No newline at end of file diff --git a/src/store/modules/app.js b/src/store/modules/app.js new file mode 100644 index 0000000..9a92dd1 --- /dev/null +++ b/src/store/modules/app.js @@ -0,0 +1,46 @@ +import Cookies from 'js-cookie' + +const useAppStore = defineStore( + 'app', + { + state: () => ({ + sidebar: { + opened: Cookies.get('sidebarStatus') ? !!+Cookies.get('sidebarStatus') : true, + withoutAnimation: false, + hide: false + }, + device: 'desktop', + size: Cookies.get('size') || 'default' + }), + actions: { + toggleSideBar(withoutAnimation) { + if (this.sidebar.hide) { + return false + } + this.sidebar.opened = !this.sidebar.opened + this.sidebar.withoutAnimation = withoutAnimation + if (this.sidebar.opened) { + Cookies.set('sidebarStatus', 1) + } else { + Cookies.set('sidebarStatus', 0) + } + }, + closeSideBar({ withoutAnimation }) { + Cookies.set('sidebarStatus', 0) + this.sidebar.opened = false + this.sidebar.withoutAnimation = withoutAnimation + }, + toggleDevice(device) { + this.device = device + }, + setSize(size) { + this.size = size + Cookies.set('size', size) + }, + toggleSideBarHide(status) { + this.sidebar.hide = status + } + } + }) + +export default useAppStore diff --git a/src/store/modules/dict.js b/src/store/modules/dict.js new file mode 100644 index 0000000..e58e4ec --- /dev/null +++ b/src/store/modules/dict.js @@ -0,0 +1,57 @@ +const useDictStore = defineStore( + 'dict', + { + state: () => ({ + dict: new Array() + }), + actions: { + // 鑾峰彇瀛楀吀 + getDict(_key) { + if (_key == null && _key == "") { + return null + } + try { + for (let i = 0; i < this.dict.length; i++) { + if (this.dict[i].key == _key) { + return this.dict[i].value + } + } + } catch (e) { + return null + } + }, + // 璁剧疆瀛楀吀 + setDict(_key, value) { + if (_key !== null && _key !== "") { + this.dict.push({ + key: _key, + value: value + }) + } + }, + // 鍒犻櫎瀛楀吀 + removeDict(_key) { + 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 + } + } + } catch (e) { + bln = false + } + return bln + }, + // 娓呯┖瀛楀吀 + cleanDict() { + this.dict = new Array() + }, + // 鍒濆瀛楀吀 + initDict() { + } + } + }) + +export default useDictStore diff --git a/src/store/modules/permission.js b/src/store/modules/permission.js new file mode 100644 index 0000000..36da015 --- /dev/null +++ b/src/store/modules/permission.js @@ -0,0 +1,127 @@ +import auth from '@/plugins/auth' +import router, { constantRoutes, dynamicRoutes } from '@/router' +import { getRouters } from '@/api/menu' +import Layout from '@/layout/index' +import ParentView from '@/components/ParentView' +import InnerLink from '@/layout/components/InnerLink' + +// 鍖归厤views閲岄潰鎵�鏈夌殑.vue鏂囦欢 +const modules = import.meta.glob('./../../views/**/*.vue') + +const usePermissionStore = defineStore( + 'permission', + { + state: () => ({ + routes: [], + addRoutes: [], + defaultRoutes: [], + topbarRouters: [], + sidebarRouters: [] + }), + actions: { + setRoutes(routes) { + this.addRoutes = routes + this.routes = constantRoutes.concat(routes) + }, + setDefaultRoutes(routes) { + this.defaultRoutes = constantRoutes.concat(routes) + }, + setTopbarRoutes(routes) { + this.topbarRouters = routes + }, + setSidebarRouters(routes) { + this.sidebarRouters = routes + }, + generateRoutes(roles) { + return new Promise(resolve => { + // 鍚戝悗绔姹傝矾鐢辨暟鎹� + getRouters().then(res => { + const sdata = JSON.parse(JSON.stringify(res.data)) + const rdata = JSON.parse(JSON.stringify(res.data)) + const defaultData = JSON.parse(JSON.stringify(res.data)) + const sidebarRoutes = filterAsyncRouter(sdata) + const rewriteRoutes = filterAsyncRouter(rdata, false, true) + const defaultRoutes = filterAsyncRouter(defaultData) + const asyncRoutes = filterDynamicRoutes(dynamicRoutes) + asyncRoutes.forEach(route => { router.addRoute(route) }) + this.setRoutes(rewriteRoutes) + this.setSidebarRouters(constantRoutes.concat(sidebarRoutes)) + this.setDefaultRoutes(sidebarRoutes) + this.setTopbarRoutes(defaultRoutes) + resolve(rewriteRoutes) + }) + }) + } + } + }) + +// 閬嶅巻鍚庡彴浼犳潵鐨勮矾鐢卞瓧绗︿覆锛岃浆鎹负缁勪欢瀵硅薄 +function filterAsyncRouter(asyncRouterMap, lastRouter = false, type = false) { + return asyncRouterMap.filter(route => { + if (type && route.children) { + route.children = filterChildren(route.children) + } + if (route.component) { + // Layout ParentView 缁勪欢鐗规畩澶勭悊 + if (route.component === 'Layout') { + route.component = Layout + } else if (route.component === 'ParentView') { + route.component = ParentView + } else if (route.component === 'InnerLink') { + route.component = InnerLink + } else { + route.component = loadView(route.component) + } + } + if (route.children != null && route.children && route.children.length) { + route.children = filterAsyncRouter(route.children, route, type) + } else { + delete route['children'] + delete route['redirect'] + } + return true + }) +} + +function filterChildren(childrenMap, lastRouter = false) { + var children = [] + 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) + } + }) + return children +} + +// 鍔ㄦ�佽矾鐢遍亶鍘嗭紝楠岃瘉鏄惁鍏峰鏉冮檺 +export function filterDynamicRoutes(routes) { + const res = [] + routes.forEach(route => { + if (route.permissions) { + if (auth.hasPermiOr(route.permissions)) { + res.push(route) + } + } else if (route.roles) { + if (auth.hasRoleOr(route.roles)) { + res.push(route) + } + } + }) + return res +} + +export const loadView = (view) => { + let res + for (const path in modules) { + const dir = path.split('views/')[1].split('.vue')[0] + if (dir === view) { + res = () => modules[path]() + } + } + return res +} + +export default usePermissionStore diff --git a/src/store/modules/settings.js b/src/store/modules/settings.js new file mode 100644 index 0000000..194d678 --- /dev/null +++ b/src/store/modules/settings.js @@ -0,0 +1,48 @@ +import defaultSettings from '@/settings' +import { useDark, useToggle } from '@vueuse/core' +import { useDynamicTitle } from '@/utils/dynamicTitle' + +const isDark = useDark() +const toggleDark = useToggle(isDark) + +const { sideTheme, showSettings, topNav, tagsView, fixedHeader, sidebarLogo, dynamicTitle } = defaultSettings + +const storageSetting = JSON.parse(localStorage.getItem('layout-setting')) || '' + +const useSettingsStore = defineStore( + 'settings', + { + state: () => ({ + title: '', + theme: storageSetting.theme || '#409EFF', + sideTheme: storageSetting.sideTheme || sideTheme, + showSettings: showSettings, + topNav: storageSetting.topNav === undefined ? topNav : storageSetting.topNav, + tagsView: storageSetting.tagsView === undefined ? tagsView : storageSetting.tagsView, + fixedHeader: storageSetting.fixedHeader === undefined ? fixedHeader : storageSetting.fixedHeader, + sidebarLogo: storageSetting.sidebarLogo === undefined ? sidebarLogo : storageSetting.sidebarLogo, + dynamicTitle: storageSetting.dynamicTitle === undefined ? dynamicTitle : storageSetting.dynamicTitle, + isDark: isDark.value + }), + actions: { + // 淇敼甯冨眬璁剧疆 + changeSetting(data) { + const { key, value } = data + if (this.hasOwnProperty(key)) { + this[key] = value + } + }, + // 璁剧疆缃戦〉鏍囬 + setTitle(title) { + this.title = title + useDynamicTitle() + }, + // 鍒囨崲鏆楅粦妯″紡 + toggleTheme() { + this.isDark = !this.isDark + toggleDark() + } + } + }) + +export default useSettingsStore diff --git a/src/store/modules/tagsView.js b/src/store/modules/tagsView.js new file mode 100644 index 0000000..9d07f33 --- /dev/null +++ b/src/store/modules/tagsView.js @@ -0,0 +1,182 @@ +const useTagsViewStore = defineStore( + 'tags-view', + { + state: () => ({ + visitedViews: [], + cachedViews: [], + iframeViews: [] + }), + actions: { + addView(view) { + this.addVisitedView(view) + this.addCachedView(view) + }, + addIframeView(view) { + if (this.iframeViews.some(v => v.path === view.path)) return + this.iframeViews.push( + Object.assign({}, view, { + title: view.meta.title || 'no-name' + }) + ) + }, + addVisitedView(view) { + if (this.visitedViews.some(v => v.path === view.path)) return + this.visitedViews.push( + Object.assign({}, view, { + title: view.meta.title || 'no-name' + }) + ) + }, + addCachedView(view) { + if (this.cachedViews.includes(view.name)) return + if (!view.meta.noCache) { + this.cachedViews.push(view.name) + } + }, + delView(view) { + return new Promise(resolve => { + this.delVisitedView(view) + this.delCachedView(view) + resolve({ + visitedViews: [...this.visitedViews], + cachedViews: [...this.cachedViews] + }) + }) + }, + delVisitedView(view) { + return new Promise(resolve => { + for (const [i, v] of this.visitedViews.entries()) { + if (v.path === view.path) { + this.visitedViews.splice(i, 1) + break + } + } + this.iframeViews = this.iframeViews.filter(item => item.path !== view.path) + resolve([...this.visitedViews]) + }) + }, + delIframeView(view) { + return new Promise(resolve => { + this.iframeViews = this.iframeViews.filter(item => item.path !== view.path) + resolve([...this.iframeViews]) + }) + }, + delCachedView(view) { + return new Promise(resolve => { + const index = this.cachedViews.indexOf(view.name) + index > -1 && this.cachedViews.splice(index, 1) + resolve([...this.cachedViews]) + }) + }, + delOthersViews(view) { + return new Promise(resolve => { + this.delOthersVisitedViews(view) + this.delOthersCachedViews(view) + resolve({ + visitedViews: [...this.visitedViews], + cachedViews: [...this.cachedViews] + }) + }) + }, + delOthersVisitedViews(view) { + return new Promise(resolve => { + this.visitedViews = this.visitedViews.filter(v => { + return v.meta.affix || v.path === view.path + }) + this.iframeViews = this.iframeViews.filter(item => item.path === view.path) + resolve([...this.visitedViews]) + }) + }, + delOthersCachedViews(view) { + return new Promise(resolve => { + const index = this.cachedViews.indexOf(view.name) + if (index > -1) { + this.cachedViews = this.cachedViews.slice(index, index + 1) + } else { + this.cachedViews = [] + } + resolve([...this.cachedViews]) + }) + }, + delAllViews(view) { + return new Promise(resolve => { + this.delAllVisitedViews(view) + this.delAllCachedViews(view) + resolve({ + visitedViews: [...this.visitedViews], + cachedViews: [...this.cachedViews] + }) + }) + }, + delAllVisitedViews(view) { + return new Promise(resolve => { + const affixTags = this.visitedViews.filter(tag => tag.meta.affix) + this.visitedViews = affixTags + this.iframeViews = [] + resolve([...this.visitedViews]) + }) + }, + delAllCachedViews(view) { + return new Promise(resolve => { + this.cachedViews = [] + resolve([...this.cachedViews]) + }) + }, + updateVisitedView(view) { + for (let v of this.visitedViews) { + if (v.path === view.path) { + v = Object.assign(v, view) + break + } + } + }, + delRightTags(view) { + return new Promise(resolve => { + const index = this.visitedViews.findIndex(v => v.path === view.path) + if (index === -1) { + return + } + this.visitedViews = this.visitedViews.filter((item, idx) => { + if (idx <= index || (item.meta && item.meta.affix)) { + return true + } + const i = this.cachedViews.indexOf(item.name) + if (i > -1) { + this.cachedViews.splice(i, 1) + } + if(item.meta.link) { + const fi = this.iframeViews.findIndex(v => v.path === item.path) + this.iframeViews.splice(fi, 1) + } + return false + }) + resolve([...this.visitedViews]) + }) + }, + delLeftTags(view) { + return new Promise(resolve => { + const index = this.visitedViews.findIndex(v => v.path === view.path) + if (index === -1) { + return + } + this.visitedViews = this.visitedViews.filter((item, idx) => { + if (idx >= index || (item.meta && item.meta.affix)) { + return true + } + const i = this.cachedViews.indexOf(item.name) + if (i > -1) { + this.cachedViews.splice(i, 1) + } + if(item.meta.link) { + const fi = this.iframeViews.findIndex(v => v.path === item.path) + this.iframeViews.splice(fi, 1) + } + return false + }) + resolve([...this.visitedViews]) + }) + } + } + }) + +export default useTagsViewStore diff --git a/src/store/modules/user.js b/src/store/modules/user.js new file mode 100644 index 0000000..2c6398f --- /dev/null +++ b/src/store/modules/user.js @@ -0,0 +1,77 @@ +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( + 'user', + { + state: () => ({ + token: getToken(), + id: '', + name: '', + nickName: '', + avatar: '', + roles: [], + permissions: [] + }), + actions: { + // 鐧诲綍 + login(userInfo) { + const username = userInfo.username.trim() + const password = userInfo.password + const code = userInfo.code + const uuid = userInfo.uuid + return new Promise((resolve, reject) => { + login(username, password, code, uuid).then(res => { + setToken(res.token) + this.token = res.token + resolve() + }).catch(error => { + reject(error) + }) + }) + }, + // 鑾峰彇鐢ㄦ埛淇℃伅 + getInfo() { + return new Promise((resolve, reject) => { + getInfo().then(res => { + const user = res.user + 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) { // 楠岃瘉杩斿洖鐨剅oles鏄惁鏄竴涓潪绌烘暟缁� + this.roles = res.roles + this.permissions = res.permissions + } else { + this.roles = ['ROLE_DEFAULT'] + } + this.id = user.userId + this.name = user.userName + this.nickName = user.nickName + this.avatar = avatar + resolve(res) + }).catch(error => { + reject(error) + }) + }) + }, + // 閫�鍑虹郴缁� + logOut() { + return new Promise((resolve, reject) => { + logout(this.token).then(() => { + this.token = '' + this.roles = [] + this.permissions = [] + removeToken() + resolve() + }).catch(error => { + reject(error) + }) + }) + } + } + }) + +export default useUserStore diff --git a/src/utils/auth.js b/src/utils/auth.js new file mode 100644 index 0000000..08a43d6 --- /dev/null +++ b/src/utils/auth.js @@ -0,0 +1,15 @@ +import Cookies from 'js-cookie' + +const TokenKey = 'Admin-Token' + +export function getToken() { + return Cookies.get(TokenKey) +} + +export function setToken(token) { + return Cookies.set(TokenKey, token) +} + +export function removeToken() { + return Cookies.remove(TokenKey) +} diff --git a/src/utils/dict.js b/src/utils/dict.js new file mode 100644 index 0000000..b154f72 --- /dev/null +++ b/src/utils/dict.js @@ -0,0 +1,24 @@ +import useDictStore from '@/store/modules/dict' +import { getDicts } from '@/api/system/dict/data' + +/** + * 鑾峰彇瀛楀吀鏁版嵁 + */ +export function useDict(...args) { + const res = ref({}) + return (() => { + args.forEach((dictType, index) => { + res.value[dictType] = [] + const dicts = useDictStore().getDict(dictType) + if (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]) + }) + } + }) + return toRefs(res.value) + })() +} \ No newline at end of file diff --git a/src/utils/dynamicTitle.js b/src/utils/dynamicTitle.js new file mode 100644 index 0000000..ca4aa5d --- /dev/null +++ b/src/utils/dynamicTitle.js @@ -0,0 +1,14 @@ +import defaultSettings from '@/settings' +import useSettingsStore from '@/store/modules/settings' + +/** + * 鍔ㄦ�佷慨鏀规爣棰� + */ +export function useDynamicTitle() { + const settingsStore = useSettingsStore() + if (settingsStore.dynamicTitle) { + document.title = settingsStore.title + ' - ' + defaultSettings.title + } else { + document.title = defaultSettings.title + } +} \ No newline at end of file diff --git a/src/utils/errorCode.js b/src/utils/errorCode.js new file mode 100644 index 0000000..d2111ee --- /dev/null +++ b/src/utils/errorCode.js @@ -0,0 +1,6 @@ +export default { + '401': '璁よ瘉澶辫触锛屾棤娉曡闂郴缁熻祫婧�', + '403': '褰撳墠鎿嶄綔娌℃湁鏉冮檺', + '404': '璁块棶璧勬簮涓嶅瓨鍦�', + 'default': '绯荤粺鏈煡閿欒锛岃鍙嶉缁欑鐞嗗憳' +} diff --git a/src/utils/generator/config.js b/src/utils/generator/config.js new file mode 100644 index 0000000..449715f --- /dev/null +++ b/src/utils/generator/config.js @@ -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鐨勮Е鍙戞柟寮忥紝鏃犺Е鍙戞柟寮忕殑缁勪欢涓嶇敓鎴恟ule +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', +} diff --git a/src/utils/generator/css.js b/src/utils/generator/css.js new file mode 100644 index 0000000..c1c62e6 --- /dev/null +++ b/src/utils/generator/css.js @@ -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') +} diff --git a/src/utils/generator/drawingDefalut.js b/src/utils/generator/drawingDefalut.js new file mode 100644 index 0000000..1105c28 --- /dev/null +++ b/src/utils/generator/drawingDefalut.js @@ -0,0 +1,29 @@ +export default [ + { + 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: '鎵嬫満鍙锋牸寮忛敊璇�' + }] + } +] diff --git a/src/utils/generator/html.js b/src/utils/generator/html.js new file mode 100644 index 0000000..4b29841 --- /dev/null +++ b/src/utils/generator/html.js @@ -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鍖呰9 +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 +} diff --git a/src/utils/generator/icon.json b/src/utils/generator/icon.json new file mode 100644 index 0000000..2d9999a --- /dev/null +++ b/src/utils/generator/icon.json @@ -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"] \ No newline at end of file diff --git a/src/utils/generator/js.js b/src/utils/generator/js.js new file mode 100644 index 0000000..dc38bfe --- /dev/null +++ b/src/utils/generator/js.js @@ -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浠g爜 + * @description: 缁勮js浠g爜鏂规硶 + * @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 +} diff --git a/src/utils/generator/render.js b/src/utils/generator/render.js new file mode 100644 index 0000000..d6d4414 --- /dev/null +++ b/src/utils/generator/render.js @@ -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, + }, + } +}) \ No newline at end of file diff --git a/src/utils/index.js b/src/utils/index.js new file mode 100644 index 0000000..9329fe2 --- /dev/null +++ b/src/utils/index.js @@ -0,0 +1,390 @@ +import { parseTime } from './ruoyi' + +/** + * 琛ㄦ牸鏃堕棿鏍煎紡鍖� + */ +export function formatDate(cellValue) { + 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 + var day = date.getDate() < 10 ? '0' + date.getDate() : date.getDate() + var hours = date.getHours() < 10 ? '0' + date.getHours() : date.getHours() + var minutes = date.getMinutes() < 10 ? '0' + date.getMinutes() : date.getMinutes() + var seconds = date.getSeconds() < 10 ? '0' + date.getSeconds() : date.getSeconds() + return year + '-' + month + '-' + day + ' ' + hours + ':' + minutes + ':' + seconds +} + +/** + * @param {number} time + * @param {string} option + * @returns {string} + */ +export function formatTime(time, option) { + if (('' + time).length === 10) { + time = parseInt(time) * 1000 + } else { + time = +time + } + const d = new Date(time) + const now = Date.now() + + const diff = (now - d) / 1000 + + if (diff < 30) { + return '鍒氬垰' + } else if (diff < 3600) { + // less 1 hour + return Math.ceil(diff / 60) + '鍒嗛挓鍓�' + } else if (diff < 3600 * 24) { + return Math.ceil(diff / 3600) + '灏忔椂鍓�' + } else if (diff < 3600 * 24 * 2) { + return '1澶╁墠' + } + if (option) { + return parseTime(time, option) + } else { + return ( + d.getMonth() + + 1 + + '鏈�' + + d.getDate() + + '鏃�' + + d.getHours() + + '鏃�' + + d.getMinutes() + + '鍒�' + ) + } +} + +/** + * @param {string} url + * @returns {Object} + */ +export function getQueryObject(url) { + url = url == null ? window.location.href : url + const search = url.substring(url.lastIndexOf('?') + 1) + const obj = {} + const reg = /([^?&=]+)=([^?&=]*)/g + search.replace(reg, (rs, $1, $2) => { + const name = decodeURIComponent($1) + let val = decodeURIComponent($2) + val = String(val) + obj[name] = val + return rs + }) + return obj +} + +/** + * @param {string} input value + * @returns {number} output value + */ +export function byteLength(str) { + // returns the byte length of an utf8 string + let s = str.length + for (var i = str.length - 1; i >= 0; i--) { + const code = str.charCodeAt(i) + if (code > 0x7f && code <= 0x7ff) s++ + else if (code > 0x7ff && code <= 0xffff) s += 2 + if (code >= 0xDC00 && code <= 0xDFFF) i-- + } + return s +} + +/** + * @param {Array} actual + * @returns {Array} + */ +export function cleanArray(actual) { + const newArray = [] + for (let i = 0; i < actual.length; i++) { + if (actual[i]) { + newArray.push(actual[i]) + } + } + return newArray +} + +/** + * @param {Object} json + * @returns {Array} + */ +export function param(json) { + if (!json) return '' + return cleanArray( + Object.keys(json).map(key => { + if (json[key] === undefined) return '' + return encodeURIComponent(key) + '=' + encodeURIComponent(json[key]) + }) + ).join('&') +} + +/** + * @param {string} url + * @returns {Object} + */ +export function param2Obj(url) { + const search = decodeURIComponent(url.split('?')[1]).replace(/\+/g, ' ') + if (!search) { + return {} + } + const obj = {} + const searchArr = search.split('&') + searchArr.forEach(v => { + const index = v.indexOf('=') + if (index !== -1) { + const name = v.substring(0, index) + const val = v.substring(index + 1, v.length) + obj[name] = val + } + }) + return obj +} + +/** + * @param {string} val + * @returns {string} + */ +export function html2Text(val) { + const div = document.createElement('div') + div.innerHTML = val + return div.textContent || div.innerText +} + +/** + * Merges two objects, giving the last one precedence + * @param {Object} target + * @param {(Object|Array)} source + * @returns {Object} + */ +export function objectMerge(target, source) { + if (typeof target !== 'object') { + target = {} + } + if (Array.isArray(source)) { + return source.slice() + } + Object.keys(source).forEach(property => { + const sourceProperty = source[property] + if (typeof sourceProperty === 'object') { + target[property] = objectMerge(target[property], sourceProperty) + } else { + target[property] = sourceProperty + } + }) + return target +} + +/** + * @param {HTMLElement} element + * @param {string} className + */ +export function toggleClass(element, className) { + if (!element || !className) { + return + } + let classString = element.className + const nameIndex = classString.indexOf(className) + if (nameIndex === -1) { + classString += '' + className + } else { + classString = + classString.substr(0, nameIndex) + + classString.substr(nameIndex + className.length) + } + element.className = classString +} + +/** + * @param {string} type + * @returns {Date} + */ +export function getTime(type) { + if (type === 'start') { + return new Date().getTime() - 3600 * 1000 * 24 * 90 + } else { + return new Date(new Date().toDateString()) + } +} + +/** + * @param {Function} func + * @param {number} wait + * @param {boolean} immediate + * @return {*} + */ +export function debounce(func, wait, immediate) { + let timeout, args, context, timestamp, result + + const later = function() { + // 鎹笂涓�娆¤Е鍙戞椂闂撮棿闅� + const last = +new Date() - timestamp + + // 涓婃琚寘瑁呭嚱鏁拌璋冪敤鏃堕棿闂撮殧 last 灏忎簬璁惧畾鏃堕棿闂撮殧 wait + if (last < wait && last > 0) { + timeout = setTimeout(later, wait - last) + } else { + timeout = null + // 濡傛灉璁惧畾涓篿mmediate===true锛屽洜涓哄紑濮嬭竟鐣屽凡缁忚皟鐢ㄨ繃浜嗘澶勬棤闇�璋冪敤 + if (!immediate) { + result = func.apply(context, args) + if (!timeout) context = args = null + } + } + } + + return function(...args) { + context = this + timestamp = +new Date() + const callNow = immediate && !timeout + // 濡傛灉寤舵椂涓嶅瓨鍦紝閲嶆柊璁惧畾寤舵椂 + if (!timeout) timeout = setTimeout(later, wait) + if (callNow) { + result = func.apply(context, args) + context = args = null + } + + return result + } +} + +/** + * This is just a simple version of deep copy + * Has a lot of edge cases bug + * If you want to use a perfect deep copy, use lodash's _.cloneDeep + * @param {Object} source + * @returns {Object} + */ +export function deepClone(source) { + if (!source && typeof source !== 'object') { + throw new Error('error arguments', 'deepClone') + } + const targetObj = source.constructor === Array ? [] : {} + Object.keys(source).forEach(keys => { + if (source[keys] && typeof source[keys] === 'object') { + targetObj[keys] = deepClone(source[keys]) + } else { + targetObj[keys] = source[keys] + } + }) + return targetObj +} + +/** + * @param {Array} arr + * @returns {Array} + */ +export function uniqueArr(arr) { + return Array.from(new Set(arr)) +} + +/** + * @returns {string} + */ +export function createUniqueString() { + const timestamp = +new Date() + '' + const randomNum = parseInt((1 + Math.random()) * 65536) + '' + return (+(randomNum + timestamp)).toString(32) +} + +/** + * Check if an element has a class + * @param {HTMLElement} elm + * @param {string} cls + * @returns {boolean} + */ +export function hasClass(ele, cls) { + return !!ele.className.match(new RegExp('(\\s|^)' + cls + '(\\s|$)')) +} + +/** + * Add class to element + * @param {HTMLElement} elm + * @param {string} cls + */ +export function addClass(ele, cls) { + if (!hasClass(ele, cls)) ele.className += ' ' + cls +} + +/** + * Remove class from element + * @param {HTMLElement} elm + * @param {string} cls + */ +export function removeClass(ele, cls) { + if (hasClass(ele, cls)) { + const reg = new RegExp('(\\s|^)' + cls + '(\\s|$)') + ele.className = ele.className.replace(reg, ' ') + } +} + +export function makeMap(str, expectsLowerCase) { + const map = Object.create(null) + const list = str.split(',') + for (let i = 0; i < list.length; i++) { + map[list[i]] = true + } + return expectsLowerCase + ? val => map[val.toLowerCase()] + : val => map[val] +} + +export const exportDefault = 'export default ' + +export const beautifierConf = { + html: { + indent_size: '2', + indent_char: ' ', + max_preserve_newlines: '-1', + preserve_newlines: false, + keep_array_indentation: false, + break_chained_methods: false, + indent_scripts: 'separate', + brace_style: 'end-expand', + space_before_conditional: true, + unescape_strings: false, + jslint_happy: false, + end_with_newline: true, + wrap_line_length: '110', + indent_inner_html: true, + comma_first: false, + e4x: true, + indent_empty_lines: true + }, + js: { + indent_size: '2', + indent_char: ' ', + max_preserve_newlines: '-1', + preserve_newlines: false, + keep_array_indentation: false, + break_chained_methods: false, + indent_scripts: 'normal', + brace_style: 'end-expand', + space_before_conditional: true, + unescape_strings: false, + jslint_happy: true, + end_with_newline: true, + wrap_line_length: '110', + indent_inner_html: true, + comma_first: false, + e4x: true, + indent_empty_lines: true + } +} + +// 棣栧瓧姣嶅ぇ灏� +export function titleCase(str) { + return str.replace(/( |^)[a-z]/g, L => L.toUpperCase()) +} + +// 涓嬪垝杞┘宄� +export function camelCase(str) { + return str.replace(/_[a-z]/g, str1 => str1.substr(-1).toUpperCase()) +} + +export function isNumberStr(str) { + return /^[+-]?(0|([1-9]\d*))(\.\d+)?$/g.test(str) +} + diff --git a/src/utils/jsencrypt.js b/src/utils/jsencrypt.js new file mode 100644 index 0000000..78d9523 --- /dev/null +++ b/src/utils/jsencrypt.js @@ -0,0 +1,30 @@ +import JSEncrypt from 'jsencrypt/bin/jsencrypt.min' + +// 瀵嗛挜瀵圭敓鎴� http://web.chacuo.net/netrsakeypair + +const publicKey = 'MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAKoR8mX0rGKLqzcWmOzbfj64K8ZIgOdH\n' + + 'nzkXSOVOZbFu/TJhZ7rFAN+eaGkl3C4buccQd/EjEsj9ir7ijT7h96MCAwEAAQ==' + +const privateKey = 'MIIBVAIBADANBgkqhkiG9w0BAQEFAASCAT4wggE6AgEAAkEAqhHyZfSsYourNxaY\n' + + '7Nt+PrgrxkiA50efORdI5U5lsW79MmFnusUA355oaSXcLhu5xxB38SMSyP2KvuKN\n' + + 'PuH3owIDAQABAkAfoiLyL+Z4lf4Myxk6xUDgLaWGximj20CUf+5BKKnlrK+Ed8gA\n' + + 'kM0HqoTt2UZwA5E2MzS4EI2gjfQhz5X28uqxAiEA3wNFxfrCZlSZHb0gn2zDpWow\n' + + 'cSxQAgiCstxGUoOqlW8CIQDDOerGKH5OmCJ4Z21v+F25WaHYPxCFMvwxpcw99Ecv\n' + + 'DQIgIdhDTIqD2jfYjPTY8Jj3EDGPbH2HHuffvflECt3Ek60CIQCFRlCkHpi7hthh\n' + + 'YhovyloRYsM+IS9h/0BzlEAuO0ktMQIgSPT3aFAgJYwKpqRYKlLDVcflZFCKY7u3\n' + + 'UP8iWi1Qw0Y=' + +// 鍔犲瘑 +export function encrypt(txt) { + const encryptor = new JSEncrypt() + encryptor.setPublicKey(publicKey) // 璁剧疆鍏挜 + return encryptor.encrypt(txt) // 瀵规暟鎹繘琛屽姞瀵� +} + +// 瑙e瘑 +export function decrypt(txt) { + const encryptor = new JSEncrypt() + encryptor.setPrivateKey(privateKey) // 璁剧疆绉侀挜 + return encryptor.decrypt(txt) // 瀵规暟鎹繘琛岃В瀵� +} + diff --git a/src/utils/permission.js b/src/utils/permission.js new file mode 100644 index 0000000..d354852 --- /dev/null +++ b/src/utils/permission.js @@ -0,0 +1,51 @@ +import useUserStore from '@/store/modules/user' + +/** + * 瀛楃鏉冮檺鏍¢獙 + * @param {Array} value 鏍¢獙鍊� + * @returns {Boolean} + */ +export function checkPermi(value) { + if (value && value instanceof Array && value.length > 0) { + const permissions = useUserStore().permissions + const permissionDatas = value + const all_permission = "*:*:*" + + const hasPermission = permissions.some(permission => { + return all_permission === permission || permissionDatas.includes(permission) + }) + + if (!hasPermission) { + return false + } + return true + } else { + console.error(`need roles! Like checkPermi="['system:user:add','system:user:edit']"`) + return false + } +} + +/** + * 瑙掕壊鏉冮檺鏍¢獙 + * @param {Array} value 鏍¢獙鍊� + * @returns {Boolean} + */ +export function checkRole(value) { + if (value && value instanceof Array && value.length > 0) { + const roles = useUserStore().roles + const permissionRoles = value + const super_admin = "admin" + + const hasRole = roles.some(role => { + return super_admin === role || permissionRoles.includes(role) + }) + + if (!hasRole) { + return false + } + return true + } else { + console.error(`need roles! Like checkRole="['admin','editor']"`) + return false + } +} \ No newline at end of file diff --git a/src/utils/request.js b/src/utils/request.js new file mode 100644 index 0000000..e2e7e49 --- /dev/null +++ b/src/utils/request.js @@ -0,0 +1,152 @@ +import axios from 'axios' +import { ElNotification , ElMessageBox, ElMessage, ElLoading } from 'element-plus' +import { getToken } from '@/utils/auth' +import errorCode from '@/utils/errorCode' +import { tansParams, blobValidate } from '@/utils/ruoyi' +import cache from '@/plugins/cache' +import { saveAs } from 'file-saver' +import useUserStore from '@/store/modules/user' + +let downloadLoadingInstance +// 鏄惁鏄剧ず閲嶆柊鐧诲綍 +export let isRelogin = { show: false } + +axios.defaults.headers['Content-Type'] = 'application/json;charset=utf-8' +// 鍒涘缓axios瀹炰緥 +const service = axios.create({ + // axios涓姹傞厤缃湁baseURL閫夐」锛岃〃绀鸿姹俇RL鍏叡閮ㄥ垎 + baseURL: import.meta.env.VITE_APP_BASE_API, + // 瓒呮椂 + timeout: 10000 +}) + +// request鎷︽埅鍣� +service.interceptors.request.use(config => { + // 鏄惁闇�瑕佽缃� token + const isToken = (config.headers || {}).isToken === false + // 鏄惁闇�瑕侀槻姝㈡暟鎹噸澶嶆彁浜� + const isRepeatSubmit = (config.headers || {}).repeatSubmit === false + if (getToken() && !isToken) { + config.headers['Authorization'] = 'Bearer ' + getToken() // 璁╂瘡涓姹傛惡甯﹁嚜瀹氫箟token 璇锋牴鎹疄闄呮儏鍐佃嚜琛屼慨鏀� + } + // 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 + } + if (!isRepeatSubmit && (config.method === 'post' || config.method === 'put')) { + const requestObj = { + url: config.url, + data: typeof config.data === 'object' ? JSON.stringify(config.data) : config.data, + time: new Date().getTime() + } + const requestSize = Object.keys(JSON.stringify(requestObj)).length // 璇锋眰鏁版嵁澶у皬 + const limitSize = 5 * 1024 * 1024 // 闄愬埗瀛樻斁鏁版嵁5M + if (requestSize >= limitSize) { + console.warn(`[${config.url}]: ` + '璇锋眰鏁版嵁澶у皬瓒呭嚭鍏佽鐨�5M闄愬埗锛屾棤娉曡繘琛岄槻閲嶅鎻愪氦楠岃瘉銆�') + return config + } + const sessionObj = cache.session.getJSON('sessionObj') + if (sessionObj === undefined || sessionObj === null || sessionObj === '') { + cache.session.setJSON('sessionObj', requestObj) + } else { + 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 = '鏁版嵁姝e湪澶勭悊锛岃鍕块噸澶嶆彁浜�' + console.warn(`[${s_url}]: ` + message) + return Promise.reject(new Error(message)) + } else { + cache.session.setJSON('sessionObj', requestObj) + } + } + } + return config +}, error => { + console.log(error) + Promise.reject(error) +}) + +// 鍝嶅簲鎷︽埅鍣� +service.interceptors.response.use(res => { + // 鏈缃姸鎬佺爜鍒欓粯璁ゆ垚鍔熺姸鎬� + const code = res.data.code || 200 + // 鑾峰彇閿欒淇℃伅 + const msg = errorCode[code] || res.data.msg || errorCode['default'] + // 浜岃繘鍒舵暟鎹垯鐩存帴杩斿洖 + if (res.request.responseType === 'blob' || res.request.responseType === 'arraybuffer') { + return res.data + } + if (code === 401) { + if (!isRelogin.show) { + isRelogin.show = true + ElMessageBox.confirm('鐧诲綍鐘舵�佸凡杩囨湡锛屾偍鍙互缁х画鐣欏湪璇ラ〉闈紝鎴栬�呴噸鏂扮櫥褰�', '绯荤粺鎻愮ず', { confirmButtonText: '閲嶆柊鐧诲綍', cancelButtonText: '鍙栨秷', type: 'warning' }).then(() => { + isRelogin.show = false + useUserStore().logOut().then(() => { + location.href = '/index' + }) + }).catch(() => { + isRelogin.show = false + }) + } + return Promise.reject('鏃犳晥鐨勪細璇濓紝鎴栬�呬細璇濆凡杩囨湡锛岃閲嶆柊鐧诲綍銆�') + } else if (code === 500) { + ElMessage({ message: msg, type: 'error' }) + return Promise.reject(new Error(msg)) + } else if (code === 601) { + ElMessage({ message: msg, type: 'warning' }) + return Promise.reject(new Error(msg)) + } else if (code !== 200) { + ElNotification.error({ title: msg }) + return Promise.reject('error') + } else { + return Promise.resolve(res.data) + } + }, + error => { + console.log('err' + error) + let { message } = error + if (message == "Network Error") { + message = "鍚庣鎺ュ彛杩炴帴寮傚父" + } else if (message.includes("timeout")) { + message = "绯荤粺鎺ュ彛璇锋眰瓒呮椂" + } else if (message.includes("Request failed with status code")) { + message = "绯荤粺鎺ュ彛" + message.substr(message.length - 3) + "寮傚父" + } + ElMessage({ message: message, type: 'error', duration: 5 * 1000 }) + return Promise.reject(error) + } +) + +// 閫氱敤涓嬭浇鏂规硶 +export function download(url, params, filename, config) { + downloadLoadingInstance = ElLoading.service({ text: "姝e湪涓嬭浇鏁版嵁锛岃绋嶅��", background: "rgba(0, 0, 0, 0.7)", }) + return service.post(url, params, { + transformRequest: [(params) => { return tansParams(params) }], + headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, + responseType: 'blob', + ...config + }).then(async (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 errMsg = errorCode[rspObj.code] || rspObj.msg || errorCode['default'] + ElMessage.error(errMsg) + } + downloadLoadingInstance.close() + }).catch((r) => { + console.error(r) + ElMessage.error('涓嬭浇鏂囦欢鍑虹幇閿欒锛岃鑱旂郴绠$悊鍛橈紒') + downloadLoadingInstance.close() + }) +} + +export default service diff --git a/src/utils/ruoyi.js b/src/utils/ruoyi.js new file mode 100644 index 0000000..3de2d98 --- /dev/null +++ b/src/utils/ruoyi.js @@ -0,0 +1,228 @@ +/** + * 閫氱敤js鏂规硶灏佽澶勭悊 + * Copyright (c) 2019 ruoyi + */ + +// 鏃ユ湡鏍煎紡鍖� +export function parseTime(time, pattern) { + if (arguments.length === 0 || !time) { + return null + } + const format = pattern || '{y}-{m}-{d} {h}:{i}:{s}' + let date + if (typeof time === 'object') { + date = time + } else { + 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), '') + } + if ((typeof time === 'number') && (time.toString().length === 10)) { + time = time * 1000 + } + date = new Date(time) + } + const formatObj = { + y: date.getFullYear(), + m: date.getMonth() + 1, + d: date.getDate(), + h: date.getHours(), + i: date.getMinutes(), + s: date.getSeconds(), + a: date.getDay() + } + const time_str = format.replace(/{(y|m|d|h|i|s|a)+}/g, (result, key) => { + let value = formatObj[key] + // Note: getDay() returns 0 on Sunday + if (key === 'a') { return ['鏃�', '涓�', '浜�', '涓�', '鍥�', '浜�', '鍏�'][value] } + if (result.length > 0 && value < 10) { + value = '0' + value + } + return value || 0 + }) + return time_str +} + +// 琛ㄥ崟閲嶇疆 +export function resetForm(refName) { + if (this.$refs[refName]) { + 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 : [] + if (typeof (propName) === 'undefined') { + search.params['beginTime'] = dateRange[0] + search.params['endTime'] = dateRange[1] + } else { + search.params['begin' + propName] = dateRange[0] + search.params['end' + propName] = dateRange[1] + } + return search +} + +// 鍥炴樉鏁版嵁瀛楀吀 +export function selectDictLabel(datas, value) { + if (value === undefined) { + return "" + } + var actions = [] + Object.keys(datas).some((key) => { + if (datas[key].value == ('' + value)) { + actions.push(datas[key].label) + return true + } + }) + if (actions.length === 0) { + actions.push(value) + } + return actions.join('') +} + +// 鍥炴樉鏁版嵁瀛楀吀锛堝瓧绗︿覆銆佹暟缁勶級 +export function selectDictLabels(datas, value, separator) { + if (value === undefined || value.length ===0) { + return "" + } + if (Array.isArray(value)) { + value = value.join(",") + } + var actions = [] + var currentSeparator = undefined === separator ? "," : separator + var temp = value.split(currentSeparator) + Object.keys(value.split(currentSeparator)).some((val) => { + var match = false + Object.keys(datas).some((key) => { + if (datas[key].value == ('' + temp[val])) { + actions.push(datas[key].label + currentSeparator) + match = true + } + }) + if (!match) { + actions.push(temp[val] + currentSeparator) + } + }) + return actions.join('').substring(0, actions.join('').length - 1) +} + +// 瀛楃涓叉牸寮忓寲(%s ) +export function sprintf(str) { + var args = arguments, flag = true, i = 1 + str = str.replace(/%s/g, function () { + var arg = args[i++] + if (typeof arg === 'undefined') { + flag = false + return '' + } + return arg + }) + return flag ? str : '' +} + +// 杞崲瀛楃涓诧紝undefined,null绛夎浆鍖栦负"" +export function parseStrEmpty(str) { + if (!str || str == "undefined" || str == "null") { + return "" + } + return str +} + +// 鏁版嵁鍚堝苟 +export function mergeRecursive(source, target) { + for (var p in target) { + try { + if (target[p].constructor == Object) { + source[p] = mergeRecursive(source[p], target[p]) + } else { + source[p] = target[p] + } + } catch (e) { + source[p] = target[p] + } + } + return source +} + +/** + * 鏋勯�犳爲鍨嬬粨鏋勬暟鎹� + * @param {*} data 鏁版嵁婧� + * @param {*} id id瀛楁 榛樿 'id' + * @param {*} parentId 鐖惰妭鐐瑰瓧娈� 榛樿 'parentId' + * @param {*} children 瀛╁瓙鑺傜偣瀛楁 榛樿 'children' + */ +export function handleTree(data, id, parentId, children) { + let config = { + id: id || 'id', + parentId: parentId || 'parentId', + childrenList: children || 'children' + } + + var childrenListMap = {} + var tree = [] + for (let d of data) { + let id = d[config.id] + childrenListMap[id] = d + if (!d[config.childrenList]) { + d[config.childrenList] = [] + } + } + + for (let d of data) { + let parentId = d[config.parentId] + let parentObj = childrenListMap[parentId] + if (!parentObj) { + tree.push(d) + } else { + parentObj[config.childrenList].push(d) + } + } + return tree +} + +/** +* 鍙傛暟澶勭悊 +* @param {*} params 鍙傛暟 +*/ +export function tansParams(params) { + let result = '' + for (const propName of Object.keys(params)) { + 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]) + "&" + } + } + } else { + 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 +} + +// 楠岃瘉鏄惁涓篵lob鏍煎紡 +export function blobValidate(data) { + return data.type !== 'application/json' +} diff --git a/src/utils/scroll-to.js b/src/utils/scroll-to.js new file mode 100644 index 0000000..c5d8e04 --- /dev/null +++ b/src/utils/scroll-to.js @@ -0,0 +1,58 @@ +Math.easeInOutQuad = function(t, b, c, d) { + t /= d / 2 + if (t < 1) { + return c / 2 * t * t + b + } + t-- + return -c / 2 * (t * (t - 2) - 1) + b +} + +// requestAnimationFrame for Smart Animating http://goo.gl/sx5sts +var requestAnimFrame = (function() { + return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || function(callback) { window.setTimeout(callback, 1000 / 60) } +})() + +/** + * Because it's so fucking difficult to detect the scrolling element, just move them all + * @param {number} amount + */ +function move(amount) { + document.documentElement.scrollTop = amount + document.body.parentNode.scrollTop = amount + document.body.scrollTop = amount +} + +function position() { + return document.documentElement.scrollTop || document.body.parentNode.scrollTop || document.body.scrollTop +} + +/** + * @param {number} to + * @param {number} duration + * @param {Function} callback + */ +export function scrollTo(to, duration, callback) { + const start = position() + const change = to - start + const increment = 20 + let currentTime = 0 + duration = (typeof (duration) === 'undefined') ? 500 : duration + var animateScroll = function() { + // increment the time + currentTime += increment + // find the value with the quadratic in-out easing function + var val = Math.easeInOutQuad(currentTime, start, change, duration) + // move the document.body + move(val) + // do the animation unless its over + if (currentTime < duration) { + requestAnimFrame(animateScroll) + } else { + if (callback && typeof (callback) === 'function') { + // the animation is done so lets callback + callback() + } + } + } + animateScroll() +} diff --git a/src/utils/theme.js b/src/utils/theme.js new file mode 100644 index 0000000..f4badc6 --- /dev/null +++ b/src/utils/theme.js @@ -0,0 +1,49 @@ +// 澶勭悊涓婚鏍峰紡 +export function handleThemeStyle(theme) { + document.documentElement.style.setProperty('--el-color-primary', theme) + for (let i = 1; i <= 9; i++) { + document.documentElement.style.setProperty(`--el-color-primary-light-${i}`, `${getLightColor(theme, i / 10)}`) + } + for (let i = 1; i <= 9; i++) { + document.documentElement.style.setProperty(`--el-color-primary-dark-${i}`, `${getDarkColor(theme, i / 10)}`) + } +} + +// hex棰滆壊杞瑀gb棰滆壊 +export function hexToRgb(str) { + str = str.replace('#', '') + let hexs = str.match(/../g) + for (let i = 0; i < 3; i++) { + hexs[i] = parseInt(hexs[i], 16) + } + return hexs +} + +// rgb棰滆壊杞琀ex棰滆壊 +export function rgbToHex(r, g, b) { + let hexs = [r.toString(16), g.toString(16), b.toString(16)] + for (let i = 0; i < 3; i++) { + if (hexs[i].length == 1) { + hexs[i] = `0${hexs[i]}` + } + } + return `#${hexs.join('')}` +} + +// 鍙樻祬棰滆壊鍊� +export function getLightColor(color, level) { + let rgb = hexToRgb(color) + for (let i = 0; i < 3; i++) { + rgb[i] = Math.floor((255 - rgb[i]) * level + rgb[i]) + } + return rgbToHex(rgb[0], rgb[1], rgb[2]) +} + +// 鍙樻繁棰滆壊鍊� +export function getDarkColor(color, level) { + let rgb = hexToRgb(color) + for (let i = 0; i < 3; i++) { + rgb[i] = Math.floor(rgb[i] * (1 - level)) + } + return rgbToHex(rgb[0], rgb[1], rgb[2]) +} diff --git a/src/utils/validate.js b/src/utils/validate.js new file mode 100644 index 0000000..6a4c0c5 --- /dev/null +++ b/src/utils/validate.js @@ -0,0 +1,114 @@ +/** + * 璺緞鍖归厤鍣� + * @param {string} pattern + * @param {string} path + * @returns {Boolean} + */ +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鏄惁鏄痟ttp鎴杊ttps + * @param {string} url + * @returns {Boolean} + */ +export function isHttp(url) { + return url.indexOf('http://') !== -1 || url.indexOf('https://') !== -1 +} + +/** + * 鍒ゆ柇path鏄惁涓哄閾� + * @param {string} path + * @returns {Boolean} + */ +export function isExternal(path) { + return /^(https?:|mailto:|tel:)/.test(path) +} + +/** + * @param {string} str + * @returns {Boolean} + */ +export function validUsername(str) { + const valid_map = ['admin', 'editor'] + return valid_map.indexOf(str.trim()) >= 0 +} + +/** + * @param {string} url + * @returns {Boolean} + */ +export function validURL(url) { + const reg = /^(https?|ftp):\/\/([a-zA-Z0-9.-]+(:[a-zA-Z0-9.&%$-]+)*@)*((25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9][0-9]?)(\.(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3}|([a-zA-Z0-9-]+\.)*[a-zA-Z0-9-]+\.(com|edu|gov|int|mil|net|org|biz|arpa|info|name|pro|aero|coop|museum|[a-zA-Z]{2}))(:[0-9]+)*(\/($|[a-zA-Z0-9.,?'\\+&%$#=~_-]+))*$/ + return reg.test(url) +} + +/** + * @param {string} str + * @returns {Boolean} + */ +export function validLowerCase(str) { + const reg = /^[a-z]+$/ + return reg.test(str) +} + +/** + * @param {string} str + * @returns {Boolean} + */ +export function validUpperCase(str) { + const reg = /^[A-Z]+$/ + return reg.test(str) +} + +/** + * @param {string} str + * @returns {Boolean} + */ +export function validAlphabets(str) { + const reg = /^[A-Za-z]+$/ + return reg.test(str) +} + +/** + * @param {string} email + * @returns {Boolean} + */ +export function validEmail(email) { + const reg = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/ + return reg.test(email) +} + +/** + * @param {string} str + * @returns {Boolean} + */ +export function isString(str) { + return typeof str === 'string' || str instanceof String +} + +/** + * @param {Array} arg + * @returns {Boolean} + */ +export function isArray(arg) { + if (typeof Array.isArray === 'undefined') { + return Object.prototype.toString.call(arg) === '[object Array]' + } + return Array.isArray(arg) +} diff --git a/src/views/error/401.vue b/src/views/error/401.vue new file mode 100644 index 0000000..b361368 --- /dev/null +++ b/src/views/error/401.vue @@ -0,0 +1,82 @@ +<template> + <div class="errPage-container"> + <el-button icon="arrow-left" class="pan-back-btn" @click="back"> + 杩斿洖 + </el-button> + <el-row> + <el-col :span="12"> + <h1 class="text-jumbo text-ginormous"> + 401閿欒! + </h1> + <h2>鎮ㄦ病鏈夎闂潈闄愶紒</h2> + <h6>瀵逛笉璧凤紝鎮ㄦ病鏈夎闂潈闄愶紝璇蜂笉瑕佽繘琛岄潪娉曟搷浣滐紒鎮ㄥ彲浠ヨ繑鍥炰富椤甸潰</h6> + <ul class="list-unstyled"> + <li class="link-type"> + <router-link to="/"> + 鍥為椤� + </router-link> + </li> + </ul> + </el-col> + <el-col :span="12"> + <img :src="errGif" width="313" height="428" alt="Girl has dropped her ice cream."> + </el-col> + </el-row> + </div> +</template> + +<script setup> +import errImage from "@/assets/401_images/401.gif" + +let { proxy } = getCurrentInstance() + +const errGif = ref(errImage + "?" + +new Date()) + +function back() { + if (proxy.$route.query.noGoBack) { + proxy.$router.push({ path: "/" }) + } else { + proxy.$router.go(-1) + } +} +</script> + +<style lang="scss" scoped> +.errPage-container { + width: 800px; + max-width: 100%; + margin: 100px auto; + .pan-back-btn { + background: #008489; + color: #fff; + border: none !important; + } + .pan-gif { + margin: 0 auto; + display: block; + } + .pan-img { + display: block; + margin: 0 auto; + width: 100%; + } + .text-jumbo { + font-size: 60px; + font-weight: 700; + color: #484848; + } + .list-unstyled { + font-size: 14px; + li { + padding-bottom: 5px; + } + a { + color: #008489; + text-decoration: none; + &:hover { + text-decoration: underline; + } + } + } +} +</style> diff --git a/src/views/error/404.vue b/src/views/error/404.vue new file mode 100644 index 0000000..f205303 --- /dev/null +++ b/src/views/error/404.vue @@ -0,0 +1,227 @@ +<template> + <div class="wscn-http404-container"> + <div class="wscn-http404"> + <div class="pic-404"> + <img class="pic-404__parent" src="@/assets/404_images/404.png" alt="404"> + <img class="pic-404__child left" src="@/assets/404_images/404_cloud.png" alt="404"> + <img class="pic-404__child mid" src="@/assets/404_images/404_cloud.png" alt="404"> + <img class="pic-404__child right" src="@/assets/404_images/404_cloud.png" alt="404"> + </div> + <div class="bullshit"> + <div class="bullshit__oops"> + 404閿欒! + </div> + <div class="bullshit__headline"> + {{ message }} + </div> + <div class="bullshit__info"> + 瀵逛笉璧凤紝鎮ㄦ鍦ㄥ鎵剧殑椤甸潰涓嶅瓨鍦ㄣ�傚皾璇曟鏌RL鐨勯敊璇紝鐒跺悗鎸夋祻瑙堝櫒涓婄殑鍒锋柊鎸夐挳鎴栧皾璇曞湪鎴戜滑鐨勫簲鐢ㄧ▼搴忎腑鎵惧埌鍏朵粬鍐呭銆� + </div> + <router-link to="/index" class="bullshit__return-home"> + 杩斿洖棣栭〉 + </router-link> + </div> + </div> + </div> +</template> + +<script setup> +let message = computed(() => { + return '鎵句笉鍒扮綉椤碉紒' +}) +</script> + +<style lang="scss" scoped> +.wscn-http404-container{ + transform: translate(-50%,-50%); + position: absolute; + top: 40%; + left: 50%; +} +.wscn-http404 { + position: relative; + width: 1200px; + padding: 0 50px; + overflow: hidden; + .pic-404 { + position: relative; + float: left; + width: 600px; + overflow: hidden; + &__parent { + width: 100%; + } + &__child { + position: absolute; + &.left { + width: 80px; + top: 17px; + left: 220px; + opacity: 0; + animation-name: cloudLeft; + animation-duration: 2s; + animation-timing-function: linear; + animation-fill-mode: forwards; + animation-delay: 1s; + } + &.mid { + width: 46px; + top: 10px; + left: 420px; + opacity: 0; + animation-name: cloudMid; + animation-duration: 2s; + animation-timing-function: linear; + animation-fill-mode: forwards; + animation-delay: 1.2s; + } + &.right { + width: 62px; + top: 100px; + left: 500px; + opacity: 0; + animation-name: cloudRight; + animation-duration: 2s; + animation-timing-function: linear; + animation-fill-mode: forwards; + animation-delay: 1s; + } + @keyframes cloudLeft { + 0% { + top: 17px; + left: 220px; + opacity: 0; + } + 20% { + top: 33px; + left: 188px; + opacity: 1; + } + 80% { + top: 81px; + left: 92px; + opacity: 1; + } + 100% { + top: 97px; + left: 60px; + opacity: 0; + } + } + @keyframes cloudMid { + 0% { + top: 10px; + left: 420px; + opacity: 0; + } + 20% { + top: 40px; + left: 360px; + opacity: 1; + } + 70% { + top: 130px; + left: 180px; + opacity: 1; + } + 100% { + top: 160px; + left: 120px; + opacity: 0; + } + } + @keyframes cloudRight { + 0% { + top: 100px; + left: 500px; + opacity: 0; + } + 20% { + top: 120px; + left: 460px; + opacity: 1; + } + 80% { + top: 180px; + left: 340px; + opacity: 1; + } + 100% { + top: 200px; + left: 300px; + opacity: 0; + } + } + } + } + .bullshit { + position: relative; + float: left; + width: 300px; + padding: 30px 0; + overflow: hidden; + &__oops { + font-size: 32px; + font-weight: bold; + line-height: 40px; + color: #1482f0; + opacity: 0; + margin-bottom: 20px; + animation-name: slideUp; + animation-duration: 0.5s; + animation-fill-mode: forwards; + } + &__headline { + font-size: 20px; + line-height: 24px; + color: #222; + font-weight: bold; + opacity: 0; + margin-bottom: 10px; + animation-name: slideUp; + animation-duration: 0.5s; + animation-delay: 0.1s; + animation-fill-mode: forwards; + } + &__info { + font-size: 13px; + line-height: 21px; + color: grey; + opacity: 0; + margin-bottom: 30px; + animation-name: slideUp; + animation-duration: 0.5s; + animation-delay: 0.2s; + animation-fill-mode: forwards; + } + &__return-home { + display: block; + float: left; + width: 110px; + height: 36px; + background: #1482f0; + border-radius: 100px; + text-align: center; + color: #ffffff; + opacity: 0; + font-size: 14px; + line-height: 36px; + cursor: pointer; + animation-name: slideUp; + animation-duration: 0.5s; + animation-delay: 0.3s; + animation-fill-mode: forwards; + } + @keyframes slideUp { + 0% { + transform: translateY(60px); + opacity: 0; + } + 100% { + transform: translateY(0); + opacity: 1; + } + } + } +} +</style> diff --git a/src/views/index.vue b/src/views/index.vue new file mode 100644 index 0000000..76688c0 --- /dev/null +++ b/src/views/index.vue @@ -0,0 +1,1095 @@ +<template> + <div class="app-container home"> + <el-row :gutter="20"> + <el-col :sm="24" :lg="12" style="padding-left: 20px"> + <h2>鑻ヤ緷鍚庡彴绠$悊妗嗘灦</h2> + <p> + 涓�鐩存兂鍋氫竴娆惧悗鍙扮鐞嗙郴缁燂紝鐪嬩簡寰堝浼樼鐨勫紑婧愰」鐩絾鏄彂鐜版病鏈夊悎閫傝嚜宸辩殑銆備簬鏄埄鐢ㄧ┖闂蹭紤鎭椂闂村紑濮嬭嚜宸卞啓涓�濂楀悗鍙扮郴缁熴�傚姝ゆ湁浜嗚嫢渚濈鐞嗙郴缁燂紝濂瑰彲浠ョ敤浜庢墍鏈夌殑Web搴旂敤绋嬪簭锛屽缃戠珯绠$悊鍚庡彴锛岀綉绔欎細鍛樹腑蹇冿紝CMS锛孋RM锛孫A绛夌瓑锛屽綋鐒讹紝鎮ㄤ篃鍙互瀵瑰ス杩涜娣卞害瀹氬埗锛屼互鍋氬嚭鏇村己绯荤粺銆傛墍鏈夊墠绔悗鍙颁唬鐮佸皝瑁呰繃鍚庡崄鍒嗙簿绠�鏄撲笂鎵嬶紝鍑洪敊姒傜巼浣庛�傚悓鏃舵敮鎸佺Щ鍔ㄥ鎴风璁块棶銆傜郴缁熶細闄嗙画鏇存柊涓�浜涘疄鐢ㄥ姛鑳姐�� + </p> + <p> + <b>褰撳墠鐗堟湰:</b> <span>v{{ version }}</span> + </p> + <p> + <el-tag type="danger">¥鍏嶈垂寮�婧�</el-tag> + </p> + <p> + <el-button + type="primary" + icon="Cloudy" + plain + @click="goTarget('https://gitee.com/y_project/RuoYi-Vue')" + >璁块棶鐮佷簯</el-button + > + <el-button + icon="HomeFilled" + plain + @click="goTarget('http://ruoyi.vip')" + >璁块棶涓婚〉</el-button + > + </p> + </el-col> + + <el-col :sm="24" :lg="12" style="padding-left: 50px"> + <el-row> + <el-col :span="12"> + <h2>鎶�鏈�夊瀷</h2> + </el-col> + </el-row> + <el-row> + <el-col :span="6"> + <h4>鍚庣鎶�鏈�</h4> + <ul> + <li>SpringBoot</li> + <li>Spring Security</li> + <li>JWT</li> + <li>MyBatis</li> + <li>Druid</li> + <li>Fastjson</li> + <li>...</li> + </ul> + </el-col> + <el-col :span="6"> + <h4>鍓嶇鎶�鏈�</h4> + <ul> + <li>Vue</li> + <li>Vuex</li> + <li>Element-ui</li> + <li>Axios</li> + <li>Sass</li> + <li>Quill</li> + <li>...</li> + </ul> + </el-col> + </el-row> + </el-col> + </el-row> + <el-divider /> + <el-row :gutter="20"> + <el-col :xs="24" :sm="24" :md="12" :lg="8"> + <el-card class="update-log"> + <template v-slot:header> + <div class="clearfix"> + <span>鑱旂郴淇℃伅</span> + </div> + </template> + <div class="body"> + <p> + <i class="el-icon-s-promotion"></i> 瀹樼綉锛�<el-link + href="http://www.ruoyi.vip" + target="_blank" + >http://www.ruoyi.vip</el-link + > + </p> + <p> + <i class="el-icon-user-solid"></i> QQ缇わ細<s> 婊�937441 </s> <s> 婊�887144332 </s> + <s> 婊�180251782 </s> <s> 婊�104180207 </s> <s> 婊�186866453 </s> <s> 婊�201396349 </s> + <s> 婊�101456076 </s> <s> 婊�101539465 </s> <s> 婊�264312783 </s> <s> 婊�167385320 </s> + <s> 婊�104748341 </s> <s> 婊�160110482 </s> <s> 婊�170801498 </s> <s> 婊�108482800 </s> + <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> <a href="http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=G6r5KGCaa3pqdbUSXNIgYloyb8e0_L0D&authKey=4w8tF1eGW7%2FedWn%2FHAypQksdrML%2BDHolQSx7094Agm7Luakj9EbfPnSTxSi2T1LQ&noverify=0&group_code=228578329" target="_blank">228578329</a> + </p> + <p> + <i class="el-icon-chat-dot-round"></i> 寰俊锛�<a + href="javascript:;" + >/ *鑻ヤ緷</a + > + </p> + <p> + <i class="el-icon-money"></i> 鏀粯瀹濓細<a + href="javascript:;" + class="鏀粯瀹濅俊鎭�" + >/ *鑻ヤ緷</a + > + </p> + </div> + </el-card> + </el-col> + <el-col :xs="24" :sm="24" :md="12" :lg="8"> + <el-card class="update-log"> + <template v-slot:header> + <div class="clearfix"> + <span>鏇存柊鏃ュ織</span> + </div> + </template> + <el-collapse accordion> + <el-collapse-item title="v3.8.9 - 2024-12-30"> + <ol> + <li>鐢ㄦ埛绠$悊鏀寔鍒嗘爮鎷栧姩</li> + <li>淇敼涓婚鏍峰紡鏈湴璇诲彇</li> + <li>鐢ㄦ埛澶村儚http(s)閾炬帴鏀寔</li> + <li>鐢ㄦ埛绠$悊杩囨护鎺夊凡绂佺敤閮ㄩ棬</li> + <li>鏀寔鑷畾涔夋樉绀篍xcel灞炴�у垪</li> + <li>鎿嶄綔鏃ュ織璁板綍DELETE璇锋眰鍙傛暟</li> + <li>鐧藉悕鍗曟敮鎸佸閫氶厤绗﹁矾寰勫尮閰�</li> + <li>鏍℃鏂囦欢鍚嶆槸鍚﹀寘鍚壒娈婂瓧绗�</li> + <li>浠g爜鐢熸垚鍒涘缓琛ㄥ睆钄借繚瑙勭殑瀛楃</li> + <li>鑿滃崟闈㈠寘灞戝鑸敮鎸佸灞傜骇鏄剧ず</li> + <li>Excel娉ㄨВ鏀寔wrapText鏄惁鍏佽鍐呭鎹㈣</li> + <li>浠g爜鐢熸垚鏂板閰嶇疆鏄惁鍏佽鏂囦欢瑕嗙洊鍒版湰鍦�</li> + <li>淇瑙掕壊绂佺敤鏉冮檺涓嶅け鏁堥棶棰�</li> + <li>淇浠g爜鐢熸垚涓婄骇鑿滃崟鏄剧ず闂</li> + <li>淇瀵煎嚭瀛愬垪琛ㄥ璞″彧鑳藉湪鏈�鍚庣殑闂</li> + <li>淇TopNav鏃犳硶姝g‘鑾峰彇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>浠g爜鐢熸垚鏂板鍒涘缓琛ㄧ粨鏋勫姛鑳�</li> + <li>瀹氭椂浠诲姟鐧藉悕鍗曢厤缃寖鍥寸缉灏�</li> + <li>浼樺寲浠g爜鐢熸垚涓诲瓙琛ㄥ叧鑱旀煡璇㈡柟寮�</li> + <li>Excel娉ㄨВ鏂板灞炴�omboReadDict</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浠ュ吋瀹筺ode18+</li> + <li>鍗囩骇spring-security鍒板畨鍏ㄧ増鏈紝闃叉婕忔礊椋庨櫓</li> + <li>鍗囩骇spring-framework鍒板畨鍏ㄧ増鏈紝闃叉婕忔礊椋庨櫓</li> + <li>浼樺寲鑷畾涔塜SS娉ㄨВ鍖归厤鏂瑰紡</li> + <li>浼樺寲缂撳瓨鐩戞帶閿悕鍒楄〃鎺掑簭鏄剧ず</li> + <li>浼樺寲瀹氭椂浠诲姟鏃ュ織榛樿鎸夋椂闂存帓搴�</li> + <li>浼樺寲榛樿鏂囦欢澶у皬瓒呰繃2G鏃犳晥鐨勯棶棰�</li> + <li>浼樺寲鏌ヨ〃鐗规畩瀛楃浣跨敤鍙嶆枩鏉犺繘琛岃浆涔�</li> + <li>浼樺寲瀹氭椂浠诲姟cron琛ㄨ揪寮忓皬鏃堕厤缃樉绀洪敊璇棶棰�</li> + <li>浼樺寲澶氫釜鑷畾鏁版嵁鏉冮檺浣跨敤in鏌ヨ,閬垮厤澶氭鎷兼帴</li> + <li>浼樺寲瀵煎叆Excel鏃惰缃甦ictType灞炴�ч噸澶嶆煡缂撳瓨闂</li> + <li>鍏朵粬缁嗚妭浼樺寲</li> + </ol> + </el-collapse-item> + <el-collapse-item title="v3.8.7 - 2023-12-08"> + <ol> + <li>鎿嶄綔鏃ュ織璁板綍閮ㄩ棬鍚嶇О</li> + <li>鍏ㄥ眬鏁版嵁瀛樺偍鐢ㄦ埛缂栧彿</li> + <li>鏂板缂栫▼寮忓垽鏂祫婧愯闂潈闄�</li> + <li>鎿嶄綔鏃ュ織鍒楄〃鏂板IP鍦板潃鏌ヨ</li> + <li>瀹氭椂浠诲姟鏂板椤靛幓闄ょ姸鎬侀�夐」</li> + <li>浠g爜鐢熸垚鏀寔閫夋嫨鍓嶇妯℃澘绫诲瀷</li> + <li>鏄鹃殣鍒楃粍浠舵敮鎸佸閫夋寮瑰嚭绫诲瀷</li> + <li>閫氱敤鎺掑簭灞炴�rderBy鍙傛暟闄愬埗闀垮害</li> + <li>Excel鑷畾涔夋暟鎹鐞嗗櫒澧炲姞鍗曞厓鏍�/宸ヤ綔绨垮璞�</li> + <li>鍗囩骇oshi鍒版渶鏂扮増鏈�6.4.8</li> + <li>鍗囩骇druid鍒版渶鏂扮増鏈�1.2.20</li> + <li>鍗囩骇fastjson鍒版渶鏂扮増2.0.43</li> + <li>鍗囩骇pagehelper鍒版渶鏂扮増1.4.7</li> + <li>鍗囩骇commons.io鍒版渶鏂扮増鏈�2.13.0</li> + <li>鍗囩骇element-ui鍒版渶鏂扮増鏈�2.15.14</li> + <li>淇浜旂骇璺敱缂撳瓨鏃犳晥闂</li> + <li>淇澶栭摼甯︾鍙e嚭鐜扮殑寮傚父</li> + <li>淇鏍戞ā鏉跨埗绾х紪鐮佸彉閲忛敊璇�</li> + <li>淇瀛楀吀琛ㄨ鎯呴〉闈㈡悳绱㈤棶棰�</li> + <li>淇鍐呴摼iframe娌℃湁浼犻�掑弬鏁伴棶棰�</li> + <li>淇鑷畾涔夊瓧鍏告牱寮忎笉鐢熸晥鐨勯棶棰�</li> + <li>淇瀛楀吀缂撳瓨鍒犻櫎鏂规硶鍙傛暟閿欒闂</li> + <li>淇Excel瀵煎叆鏁版嵁涓存椂鏂囦欢鏃犳硶鍒犻櫎闂</li> + <li>淇鏈櫥褰曞甫鍙傛暟璁块棶鎴愬姛鍚庡弬鏁颁涪澶遍棶棰�</li> + <li>淇HeaderSearch缁勪欢璺宠浆query鍙傛暟涓㈠け闂</li> + <li>淇浠g爜鐢熸垚瀵煎叆鍚庡繀濉」涓庢暟鎹簱涓嶅尮閰嶉棶棰�</li> + <li>淇Excels瀵煎叆鏃舵棤娉曡幏鍙栧埌dictType瀛楀吀鍊奸棶棰�</li> + <li>浼樺寲涓嬭浇zip鏂规硶鏂板閬僵灞�</li> + <li>浼樺寲澶村儚涓婁紶鍙傛暟鏂板鏂囦欢鍚嶇О</li> + <li>浼樺寲瀛楀吀鏍囩鏀寔鑷畾涔夊垎闅旂</li> + <li>浼樺寲鑿滃崟绠$悊绫诲瀷涓烘寜閽姸鎬佸彲閫�</li> + <li>浼樺寲鍓嶇闃查噸澶嶆彁浜ゆ暟鎹ぇ灏忛檺鍒�</li> + <li>浼樺寲TopNav鑿滃崟娌℃湁鍥炬爣svg涓嶆樉绀�</li> + <li>浼樺寲鏁板瓧閲戦澶у啓杞崲绮惧害涓㈠け闂</li> + <li>浼樺寲瀵屾枃鏈珽ditor缁勪欢妫�楠屽浘鐗囨牸寮�</li> + <li>浼樺寲椤电鍦‵irefox娴忚鍣ㄨ閬尅鐨勯棶棰�</li> + <li>浼樺寲涓汉涓績/鍩烘湰璧勬枡淇敼鏃舵暟鎹樉绀洪棶棰�</li> + <li>浼樺寲缂撳瓨鐩戞帶鍥捐〃鏀寔璺熼殢灞忓箷澶у皬鑷�傚簲璋冩暣</li> + <li>鍏朵粬缁嗚妭浼樺寲</li> + </ol> + </el-collapse-item> + <el-collapse-item title="v3.8.6 - 2023-06-30"> + <ol> + <li>鏀寔鐧诲綍IP榛戝悕鍗曢檺鍒�</li> + <li>鏂板鐩戞帶椤甸潰鍥炬爣鏄剧ず</li> + <li>鎿嶄綔鏃ュ織鏂板娑堣�楁椂闂村睘鎬�</li> + <li>灞忚斀瀹氭椂浠诲姟bean杩濊鐨勫瓧绗�</li> + <li>鏃ュ織绠$悊浣跨敤绱㈠紩鎻愬崌鏌ヨ鎬ц兘</li> + <li>鏃ュ織娉ㄨВ鏀寔鎺掗櫎鎸囧畾鐨勮姹傚弬鏁�</li> + <li>鏀寔鑷畾涔夐殣钘忓睘鎬у垪杩囨护瀛愬璞�</li> + <li>鍗囩骇oshi鍒版渶鏂扮増鏈�6.4.3</li> + <li>鍗囩骇druid鍒版渶鏂扮増鏈�1.2.16</li> + <li>鍗囩骇fastjson鍒版渶鏂扮増2.0.34</li> + <li>鍗囩骇spring-boot鍒版渶鏂扮増鏈�2.5.15</li> + <li>鍗囩骇element-ui鍒版渶鏂扮増鏈�2.15.13</li> + <li>绉婚櫎apache/commons-fileupload渚濊禆</li> + <li>淇椤甸潰鍒囨崲鏃跺竷灞�閿欎贡鐨勯棶棰�</li> + <li>淇鍖垮悕娉ㄨВAnonymous绌烘寚閽堥棶棰�</li> + <li>淇璺敱璺宠浆琚樆姝㈡椂鍐呴儴浜х敓鎶ラ敊淇℃伅闂</li> + <li>淇isMatchedIp鐨勫弬鏁板垽鏂骇鐢熺┖鎸囬拡鐨勯棶棰�</li> + <li>淇鐢ㄦ埛澶氳鑹叉暟鎹潈闄愬彲鑳藉嚭鐜版潈闄愭姮鍗囩殑鎯呭喌</li> + <li>淇寮�鍚疶opNav鍚庝竴绾ц彍鍗曡矾鐢卞弬鏁拌缃棤鏁堥棶棰�</li> + <li>淇DictTag缁勪欢value娌℃湁鍖归厤鐨勫�兼椂鍒欏睍绀簐alue</li> + <li>浼樺寲鏂囦欢涓嬭浇鍑虹幇鐨勫紓甯�</li> + <li>浼樺寲閫夋嫨鍥炬爣缁勪欢楂樹寒鍥炴樉</li> + <li>浼樺寲寮圭獥鍚庡鑸爮鍋忕Щ鐨勯棶棰�</li> + <li>浼樺寲淇敼瀵嗙爜鏃ュ織瀛樺偍鏄庢枃闂</li> + <li>浼樺寲椤电鏍忓叧闂叾浠栧嚭鐜扮殑寮傚父闂</li> + <li>浼樺寲椤电鍏抽棴宸︿晶閫夐」鎺掗櫎棣栭〉閫夐」</li> + <li>浼樺寲鍏抽棴褰撳墠tab椤佃烦杞渶鍙充晶tab椤�</li> + <li>浼樺寲缂撳瓨鍒楄〃娓呴櫎鎿嶄綔鎻愮ず涓嶅彉鐨勯棶棰�</li> + <li>浼樺寲瀛楃鏈娇鐢ㄤ笅鍒掔嚎涓嶈繘琛岄┘宄板紡澶勭悊</li> + <li>浼樺寲鐢ㄦ埛瀵煎叆鏇存柊鏃堕渶鑾峰彇鐢ㄦ埛缂栧彿闂</li> + <li>浼樺寲渚ц竟鏍忕殑骞冲彴鏍囬涓嶸UE_APP_TITLE淇濇寔鍚屾</li> + <li>浼樺寲瀵煎嚭Excel鏃惰缃甦ictType灞炴�ч噸澶嶆煡缂撳瓨闂</li> + <li>杩炴帴姹燚ruid鏀寔鏂扮殑閰嶇疆connectTimeout鍜宻ocketTimeout</li> + <li>鍏朵粬缁嗚妭浼樺寲</li> + </ol> + </el-collapse-item> + <el-collapse-item title="v3.8.5 - 2023-01-01"> + <ol> + <li>瀹氭椂浠诲姟杩濊鐨勫瓧绗�</li> + <li>閲嶇疆鏃跺彇娑堥儴闂ㄩ�変腑</li> + <li>鏂板杩斿洖璀﹀憡娑堟伅鎻愮ず</li> + <li>蹇界暐涓嶅繀瑕佺殑灞炴�ф暟鎹繑鍥�</li> + <li>淇敼鍙傛暟閿悕鏃剁Щ闄ゅ墠缂撳瓨閰嶇疆</li> + <li>瀵煎叆鏇存柊鐢ㄦ埛鏁版嵁鍓嶆牎楠屾暟鎹潈闄�</li> + <li>鍏煎Excel涓嬫媺妗嗗唴瀹硅繃澶氭棤娉曟樉绀虹殑闂</li> + <li>鍗囩骇echarts鍒版渶鏂扮増鏈�5.4.0</li> + <li>鍗囩骇core-js鍒版渶鏂扮増鏈�3.25.3</li> + <li>鍗囩骇oshi鍒版渶鏂扮増鏈�6.4.0</li> + <li>鍗囩骇kaptcha鍒版渶鏂扮増2.3.3</li> + <li>鍗囩骇druid鍒版渶鏂扮増鏈�1.2.15</li> + <li>鍗囩骇fastjson鍒版渶鏂扮増2.0.20</li> + <li>鍗囩骇pagehelper鍒版渶鏂扮増1.4.6</li> + <li>浼樺寲寮圭獥鍐呭杩囧灞曠ず涓嶅叏闂</li> + <li>浼樺寲swagger-ui闈欐�佽祫婧愪娇鐢ㄧ紦瀛�</li> + <li>寮�鍚疶opNav娌℃湁瀛愯彍鍗曢殣钘忎晶杈规爮</li> + <li>鍒犻櫎fuse鏃犳晥閫夐」maxPatternLength</li> + <li>浼樺寲瀵煎嚭瀵硅薄鐨勫瓙鍒楄〃涓虹┖浼氬嚭鐜癧]闂</li> + <li>浼樺寲缂栬緫澶村儚鏃堕�忔槑閮ㄥ垎浼氬彉鎴愰粦鑹查棶棰�</li> + <li>浼樺寲灏忓睆骞曚笂淇敼澶村儚鐣岄潰甯冨眬閿欎綅鐨勯棶棰�</li> + <li>淇浠g爜鐢熸垚鍕鹃�夊睘鎬ф棤鏁堥棶棰�</li> + <li>淇鏂囦欢涓婁紶缁勪欢鏍煎紡楠岃瘉闂</li> + <li>淇鍥炴樉鏁版嵁瀛楀吀鏁扮粍寮傚父闂</li> + <li>淇sheet瓒呭嚭鏈�澶ц鏁板紓甯搁棶棰�</li> + <li>淇Log娉ㄨВGET璇锋眰璁板綍涓嶅埌鍙傛暟闂</li> + <li>淇璋冨害鏃ュ織鐐瑰嚮澶氭鏁版嵁涓嶅彉鍖栫殑闂</li> + <li>淇涓婚棰滆壊鍦―rawer缁勪欢涓嶄細鍔犺浇闂</li> + <li>淇鏂囦欢鍚嶅寘鍚壒娈婂瓧绗︾殑鏂囦欢鏃犳硶涓嬭浇闂</li> + <li>淇table涓洿澶氭寜閽垏鎹富棰樿壊鏈敓鏁堜慨澶嶉棶棰�</li> + <li>淇鏌愪簺鐗规�х殑鐜鐢熸垚浠g爜鍙樹贡鐮乀XT鏂囦欢闂</li> + <li>淇浠g爜鐢熸垚鍥剧墖/鏂囦欢/鍗曢�夋椂閫夋嫨蹇呭~鏃犳硶鏍¢獙闂</li> + <li>淇鏌愪簺鐗规�х殑鎯呭喌鐢ㄦ埛缂栬緫瀵硅瘽妗嗕腑瑙掕壊鍜岄儴闂ㄦ棤娉曚慨鏀归棶棰�</li> + <li>鍏朵粬缁嗚妭浼樺寲</li> + </ol> + </el-collapse-item> + <el-collapse-item title="v3.8.4 - 2022-09-26"> + <ol> + <li>鏁版嵁閫昏緫鍒犻櫎涓嶈繘琛屽敮涓�楠岃瘉</li> + <li>Excel娉ㄨВ鏀寔瀵煎嚭瀵硅薄鐨勫瓙鍒楄〃鏂规硶</li> + <li>Excel娉ㄨВ鏀寔鑷畾涔夐殣钘忓睘鎬у垪</li> + <li>Excel娉ㄨВ鏀寔backgroundColor灞炴�ц缃儗鏅壊</li> + <li>鏀寔閰嶇疆瀵嗙爜鏈�澶ч敊璇鏁�/閿佸畾鏃堕棿</li> + <li>鐧诲綍鏃ュ織鏂板瑙i攣璐︽埛鍔熻兘</li> + <li>閫氱敤涓嬭浇鏂规硶鏂板config閰嶇疆閫夐」</li> + <li>鏀寔澶氭潈闄愬瓧绗﹀尮閰嶈鑹叉暟鎹潈闄�</li> + <li>椤甸潰鍐呭祵iframe鍒囨崲tab涓嶅埛鏂版暟鎹�</li> + <li>鎿嶄綔鏃ュ織璁板綍鏀寔鎺掗櫎鏁忔劅灞炴�у瓧娈�</li> + <li>淇澶氭枃浠朵笂浼犳姤閿欏嚭鐜扮殑寮傚父闂</li> + <li>淇鍥剧墖棰勮缁勪欢src灞炴�т负null鍊兼帶鍒跺彴鎶ラ敊闂</li> + <li>鍗囩骇oshi鍒版渶鏂扮増鏈�6.2.2</li> + <li>鍗囩骇fastjson鍒版渶鏂扮増2.0.14</li> + <li>鍗囩骇pagehelper鍒版渶鏂扮増1.4.3</li> + <li>鍗囩骇core-js鍒版渶鏂扮増鏈�3.25.2</li> + <li>鍗囩骇element-ui鍒版渶鏂扮増鏈�2.15.10</li> + <li>浼樺寲浠诲姟杩囨湡涓嶆墽琛岃皟搴�</li> + <li>浼樺寲瀛楀吀鏁版嵁浣跨敤store瀛樺彇</li> + <li>浼樺寲淇敼璧勬枡澶村儚琚鐩栫殑闂</li> + <li>浼樺寲淇敼鐢ㄦ埛鐧诲綍璐﹀彿閲嶅楠岃瘉</li> + <li>浼樺寲浠g爜鐢熸垚鍚屾鍚庡�糔ULL闂</li> + <li>浼樺寲瀹氭椂浠诲姟鏀寔鎵ц鐖剁被鏂规硶</li> + <li>浼樺寲鐢ㄦ埛涓汉淇℃伅鎺ュ彛闃叉淇敼閮ㄩ棬</li> + <li>浼樺寲甯冨眬璁剧疆浣跨敤el-drawer鎶藉眽鏄剧ず</li> + <li>浼樺寲娌℃湁鏉冮檺鐨勭敤鎴风紪杈戦儴闂ㄧ己灏戞暟鎹�</li> + <li>浼樺寲鏃ュ織娉ㄨВ璁板綍闄愬埗璇锋眰鍦板潃鐨勯暱搴�</li> + <li>浼樺寲excel/scale灞炴�у鍑哄崟鍏冩牸鏁板�肩被鍨�</li> + <li>浼樺寲鏃ュ織鎿嶄綔涓噸缃寜閽椂閲嶅鏌ヨ鐨勯棶棰�</li> + <li>浼樺寲澶氫釜鐩稿悓瑙掕壊鏁版嵁瀵艰嚧鏉冮檺SQL閲嶅闂</li> + <li>浼樺寲琛ㄦ牸涓婂彸渚у伐鍏锋潯锛堟悳绱㈡寜閽樉闅�&鍙充晶鏍峰紡鍑稿嚭锛�</li> + <li>鍏朵粬缁嗚妭浼樺寲</li> + </ol> + </el-collapse-item> + <el-collapse-item title="v3.8.3 - 2022-06-27"> + <ol> + <li>鏂板缂撳瓨鍒楄〃鑿滃崟鍔熻兘</li> + <li>浠g爜鐢熸垚鏍戣〃鏂板(灞曞紑/鎶樺彔)</li> + <li>Excel娉ㄨВ鏀寔color瀛椾綋棰滆壊</li> + <li>鏂板Anonymous鍖垮悕璁块棶涓嶉壌鏉冩敞瑙�</li> + <li>鐢ㄦ埛澶村儚涓婁紶闄愬埗鍙兘涓哄浘鐗囨牸寮�</li> + <li>鎺ュ彛浣跨敤娉涘瀷浣垮叾鐪嬪埌鍝嶅簲灞炴�у瓧娈�</li> + <li>妫�鏌ュ畾鏃朵换鍔ean鎵�鍦ㄥ寘鍚嶆槸鍚︿负鐧藉悕鍗曢厤缃�</li> + <li>娣诲姞椤电openPage鏀寔浼犻�掑弬鏁�</li> + <li>鐢ㄦ埛缂撳瓨淇℃伅娣诲姞閮ㄩ棬ancestors绁栫骇鍒楄〃</li> + <li>鍗囩骇element-ui鍒版渶鏂扮増鏈�2.15.8</li> + <li>鍗囩骇oshi鍒版渶鏂扮増鏈�6.1.6</li> + <li>鍗囩骇druid鍒版渶鏂扮増鏈�1.2.11</li> + <li>鍗囩骇fastjson鍒版渶鏂扮増2.0.8</li> + <li>鍗囩骇spring-boot鍒版渶鏂扮増鏈�2.5.14</li> + <li>闄嶇骇jsencrypt鐗堟湰鍏煎IE娴忚鍣�</li> + <li>鍒犻櫎澶氫綑鐨剆alt瀛楁</li> + <li>鏂板鑾峰彇涓嶅甫鍚庣紑鏂囦欢鍚嶇О鏂规硶</li> + <li>鏂板鑾峰彇閰嶇疆鏂囦欢涓殑灞炴�у�兼柟娉�</li> + <li>鏂板鍐呭缂栫爜/瑙g爜鏂逛究鎻掍欢闆嗘垚浣跨敤</li> + <li>瀛楀吀绫诲瀷蹇呴』浠ュ瓧姣嶅紑澶达紝涓斿彧鑳戒负锛堝皬鍐欏瓧姣嶏紝鏁板瓧锛屼笅婊戠嚎锛�</li> + <li>浼樺寲璁剧疆鍒嗛〉鍙傛暟榛樿鍊�</li> + <li>浼樺寲瀵圭┖瀛楃涓插弬鏁板鐞嗙殑杩囨护</li> + <li>浼樺寲鏄剧ず椤哄簭orderNum绫诲瀷涓烘暣鍨�</li> + <li>浼樺寲琛ㄥ崟鏋勫缓鎸夐挳涓嶆樉绀烘鍒欐牎楠�</li> + <li>浼樺寲瀛楀吀鏁版嵁鍥炴樉鏍峰紡涓嬫媺妗嗘樉绀哄��</li> + <li>浼樺寲R鍝嶅簲鎴愬姛鐘舵�佺爜涓庡叏灞�淇濇寔涓�鑷�</li> + <li>浼樺寲druid寮�鍚痺all杩囨护鍣ㄥ嚭鐜扮殑寮傚父闂</li> + <li>浼樺寲鐢ㄦ埛绠$悊宸︿晶鏍戝瀷缁勪欢澧炲姞閫変腑楂樹寒淇濇寔</li> + <li>浼樺寲鏂板鐢ㄦ埛涓庤鑹蹭俊鎭�&鐢ㄦ埛涓庡矖浣嶄俊鎭�昏緫</li> + <li>浼樺寲榛樿涓嶅惎鐢ㄥ帇缂╂枃浠剁紦瀛橀槻姝ode_modules杩囧ぇ</li> + <li>淇瀛楀吀鏁版嵁鏄剧ず涓嶅叏闂</li> + <li>淇鎿嶄綔鏃ュ織鏌ヨ绫诲瀷鏉′欢涓�0鏃朵細鏌ュ埌鎵�鏈夋暟鎹�</li> + <li>淇Excel娉ㄨВprompt/combo鍚屾椂浣跨敤涓嶇敓鏁堥棶棰�</li> + <li>鍏朵粬缁嗚妭浼樺寲</li> + </ol> + </el-collapse-item> + <el-collapse-item title="v3.8.2 - 2022-04-01"> + <ol> + <li>鍓嶇鏀寔璁剧疆鏄惁闇�瑕侀槻姝㈡暟鎹噸澶嶆彁浜�</li> + <li>寮�鍚疶opNav娌℃湁瀛愯彍鍗曟儏鍐甸殣钘忎晶杈规爮</li> + <li>渚ц竟鏍忚彍鍗曞悕绉拌繃闀挎偓鍋滄樉绀烘爣棰�</li> + <li>鐢ㄦ埛璁块棶鎺у埗鏃舵牎楠屾暟鎹潈闄愶紝闃叉瓒婃潈</li> + <li>瀵煎嚭Excel鏃跺睆钄藉叕寮忥紝闃叉CSV娉ㄥ叆椋庨櫓</li> + <li>缁勪欢ImagePreview鏀寔澶氬浘棰勮鏄剧ず</li> + <li>缁勪欢ImageUpload鏀寔澶氬浘鍚屾椂閫夋嫨涓婁紶</li> + <li>缁勪欢FileUpload鏀寔澶氭枃浠跺悓鏃堕�夋嫨涓婁紶</li> + <li>鏈嶅姟鐩戞帶鏂板杩愯鍙傛暟淇℃伅鏄剧ず</li> + <li>瀹氭椂浠诲姟鐩爣瀛楃涓茶繃婊ょ壒娈婂瓧绗�</li> + <li>瀹氭椂浠诲姟鐩爣瀛楃涓查獙璇佸寘鍚嶇櫧鍚嶅崟</li> + <li>浠g爜鐢熸垚鍒楄〃鍥剧墖鏀寔棰勮</li> + <li>浠g爜鐢熸垚缂栬緫淇敼鎵撳紑鏂伴〉绛�</li> + <li>浠g爜鐢熸垚鏂板Java绫诲瀷Boolean</li> + <li>浠g爜鐢熸垚瀛愯〃鏀寔鏃ユ湡/瀛楀吀閰嶇疆</li> + <li>浠g爜鐢熸垚鍚屾淇濈暀蹇呭~/绫诲瀷閫夐」</li> + <li>鍗囩骇oshi鍒版渶鏂扮増鏈�6.1.2</li> + <li>鍗囩骇fastjson鍒版渶鏂扮増1.2.80</li> + <li>鍗囩骇pagehelper鍒版渶鏂扮増1.4.1</li> + <li>鍗囩骇spring-boot鍒版渶鏂扮増鏈�2.5.11</li> + <li>鍗囩骇spring-boot-mybatis鍒版渶鏂扮増2.2.2</li> + <li>娣诲姞閬楁紡鐨勫垎椤靛弬鏁板悎鐞嗗寲灞炴��</li> + <li>淇敼npm鍗冲皢杩囨湡鐨勬敞鍐屾簮鍦板潃</li> + <li>淇鍒嗛〉缁勪欢璇锋眰涓ゆ闂</li> + <li>淇閫氱敤鏂囦欢涓嬭浇鎺ュ彛璺ㄥ煙闂</li> + <li>淇Xss娉ㄨВ瀛楁鍊间负绌烘椂鐨勫紓甯搁棶棰�</li> + <li>淇閫夐」鍗$偣鍑诲彸閿埛鏂颁涪澶卞弬鏁伴棶棰�</li> + <li>淇琛ㄥ崟娓呴櫎鍏冪礌浣嶇疆鏈瀭鐩村眳涓棶棰�</li> + <li>淇鏈嶅姟鐩戞帶涓繍琛屽弬鏁版樉绀烘潯浠堕敊璇�</li> + <li>淇瀵煎叆Excel鏃跺瓧鍏稿瓧娈电被鍨嬩负Long杞箟涓虹┖闂</li> + <li>淇鐧诲綍瓒呮椂鍒锋柊椤甸潰璺宠浆鐧诲綍椤甸潰杩樻彁绀洪噸鏂扮櫥褰曢棶棰�</li> + <li>浼樺寲鍔犺浇瀛楀吀缂撳瓨鏁版嵁</li> + <li>浼樺寲IP鍦板潃鑾峰彇鍒板涓殑闂</li> + <li>浼樺寲浠诲姟闃熷垪婊℃椂浠诲姟鎷掔粷绛栫暐</li> + <li>浼樺寲鏂囦欢涓婁紶鍏煎Weblogic鐜</li> + <li>浼樺寲瀹氭椂浠诲姟榛樿淇濆瓨鍒板唴瀛樹腑鎵ц</li> + <li>浼樺寲閮ㄩ棬淇敼缂╂斁鍚庡嚭鐜扮殑閿欎綅闂</li> + <li>浼樺寲Excel鏍煎紡鍖栦笉鍚岀被鍨嬬殑鏃ユ湡瀵硅薄</li> + <li>浼樺寲鑿滃崟琛ㄥ叧閿瓧瀵艰嚧鐨勬彃浠舵姤閿欓棶棰�</li> + <li>浼樺寲Oracle鐢ㄦ埛澶村儚鍒椾负绌烘椂涓嶆樉绀洪棶棰�</li> + <li>浼樺寲椤甸潰鑻ユ湭鍖归厤鍒板瓧鍏告爣绛惧垯杩斿洖鍘熷瓧鍏稿��</li> + <li>浼樺寲淇鐧诲綍澶辨晥鍚庡娆¤姹傛彁绀哄娆″脊绐楅棶棰�</li> + <li>鍏朵粬缁嗚妭浼樺寲</li> + </ol> + </el-collapse-item> + <el-collapse-item title="v3.8.1 - 2022-01-01"> + <ol> + <li>鏂板Vue3鍓嶇浠g爜鐢熸垚妯℃澘</li> + <li>鏂板鍥剧墖棰勮缁勪欢</li> + <li>鏂板鍘嬬缉鎻掍欢瀹炵幇鎵撳寘Gzip</li> + <li>鑷畾涔墄ss鏍¢獙娉ㄨВ瀹炵幇</li> + <li>鑷畾涔夋枃瀛楀鍒跺壀璐存寚浠�</li> + <li>浠g爜鐢熸垚棰勮鏀寔澶嶅埗鍐呭</li> + <li>璺敱鏀寔鍗曠嫭閰嶇疆鑿滃崟鎴栬鑹叉潈闄�</li> + <li>鐢ㄦ埛绠$悊閮ㄩ棬鏌ヨ閫夋嫨鑺傜偣鍚庡垎椤靛弬鏁板垵濮�</li> + <li>淇鐢ㄦ埛鍒嗛厤瑙掕壊灞炴�ч敊璇�</li> + <li>淇鎵撳寘鍚庡瓧浣撳浘鏍囧伓鐜扮殑涔辩爜闂</li> + <li>淇鑿滃崟绠$悊閲嶇疆琛ㄥ崟鍑虹幇鐨勯敊璇�</li> + <li>淇鐗堟湰宸紓瀵艰嚧鐨勬噿鍔犺浇鎶ラ敊闂</li> + <li>淇Cron缁勪欢涓懆鍥炴樉闂</li> + <li>淇瀹氭椂浠诲姟澶氬弬鏁伴�楀彿鍒嗛殧鐨勯棶棰�</li> + <li>淇鏍规嵁ID鏌ヨ鍒楄〃鍙兘鍑虹幇鐨勪富閿孩鍑洪棶棰�</li> + <li>淇tomcat閰嶇疆鍙傛暟宸茶繃鏈熼棶棰�</li> + <li>鍗囩骇clipboard鍒版渶鏂扮増鏈�2.0.8</li> + <li>鍗囩骇oshi鍒版渶鏂扮増鏈瑅5.8.6</li> + <li>鍗囩骇fastjson鍒版渶鏂扮増1.2.79</li> + <li>鍗囩骇spring-boot鍒版渶鏂扮増鏈�2.5.8</li> + <li>鍗囩骇log4j2鍒�2.17.1锛岄槻姝㈡紡娲為闄�</li> + <li>浼樺寲涓嬭浇瑙f瀽blob寮傚父鎻愮ず</li> + <li>浼樺寲浠g爜鐢熸垚瀛楀吀缁勯噸澶嶉棶棰�</li> + <li>浼樺寲鏌ヨ鐢ㄦ埛鐨勮鑹茬粍&宀椾綅缁勪唬鐮�</li> + <li>浼樺寲瀹氭椂浠诲姟cron琛ㄨ揪寮忓皬鏃惰缃�24</li> + <li>浼樺寲鐢ㄦ埛瀵煎叆鎻愮ず婧㈠嚭鍒欐樉绀烘粴鍔ㄦ潯</li> + <li>浼樺寲闃查噸澶嶆彁浜ゆ爣璇嗙粍鍚堜负(key+url+header)</li> + <li>浼樺寲鍒嗛〉鏂规硶璁剧疆鎴愰�氱敤鏂逛究鐏垫椿璋冪敤</li> + <li>鍏朵粬缁嗚妭浼樺寲</li> + </ol> + </el-collapse-item> + <el-collapse-item title="v3.8.0 - 2021-12-01"> + <ol> + <li>鏂板閰嶅骞跺悓姝ョ殑Vue3鍓嶇鐗堟湰</li> + <li>鏂板閫氱敤鏂规硶绠�鍖栨ā鎬�/缂撳瓨/涓嬭浇/鏉冮檺/椤电浣跨敤</li> + <li>浼樺寲瀵煎嚭鏁版嵁/浣跨敤閫氱敤涓嬭浇鏂规硶</li> + <li>Excel娉ㄨВ鏀寔鑷畾涔夋暟鎹鐞嗗櫒</li> + <li>Excel娉ㄨВ鏀寔瀵煎叆瀵煎嚭鏍囬淇℃伅</li> + <li>Excel瀵煎叆鏀寔@Excels娉ㄨВ</li> + <li>鏂板缁勪欢data-dict锛岀畝鍖栨暟鎹瓧鍏镐娇鐢�</li> + <li>鏂板Jaxb渚濊禆锛岄槻姝dk8浠ヤ笂鍑虹幇鐨勫吋瀹归敊璇�</li> + <li>鐢熶骇鐜浣跨敤璺敱鎳掑姞杞芥彁鍗囬〉闈㈠搷搴旈�熷害</li> + <li>淇浜旂骇浠ヤ笂鑿滃崟鍑虹幇鐨�404闂</li> + <li>闃查噸鎻愪氦娉ㄨВ鏀寔閰嶇疆闂撮殧鏃堕棿/鎻愮ず娑堟伅</li> + <li>鏃ュ織娉ㄨВ鏂板鏄惁淇濆瓨鍝嶅簲鍙傛暟</li> + <li>浠诲姟灞忚斀杩濊瀛楃&鍙傛暟蹇界暐鍙屽紩鍙蜂腑鐨勯�楀彿</li> + <li>鍗囩骇SpringBoot鍒版渶鏂扮増鏈�2.5.6</li> + <li>鍗囩骇pagehelper鍒版渶鏂扮増1.4.0</li> + <li>鍗囩骇spring-boot-mybatis鍒版渶鏂扮増2.2.0</li> + <li>鍗囩骇oshi鍒版渶鏂扮増鏈瑅5.8.2</li> + <li>鍗囩骇druid鍒版渶鏂扮増1.2.8</li> + <li>鍗囩骇velocity鍒版渶鏂扮増鏈�2.3</li> + <li>鍗囩骇fastjson鍒版渶鏂扮増1.2.78</li> + <li>鍗囩骇axios鍒版渶鏂扮増鏈�0.24.0</li> + <li>鍗囩骇dart-sass鍒扮増鏈�1.32.13</li> + <li>鍗囩骇core-js鍒版渶鏂扮増鏈�3.19.1</li> + <li>鍗囩骇jsencrypt鍒版渶鏂扮増鏈�3.2.1</li> + <li>鍗囩骇js-cookie鍒版渶鏂扮増鏈�3.0.1</li> + <li>鍗囩骇file-saver鍒版渶鏂扮増鏈�2.0.5</li> + <li>鍗囩骇sass-loader鍒版渶鏂扮増鏈�10.1.1</li> + <li>鍗囩骇element-ui鍒版渶鏂扮増鏈�2.15.6</li> + <li>鏂板sendGet鏃犲弬璇锋眰鏂规硶</li> + <li>绂佺敤el-tag缁勪欢鐨勬笎鍙樺姩鐢�</li> + <li>浠g爜鐢熸垚鐐瑰嚮棰勮閲嶇疆婵�娲籺ab</li> + <li>AjaxResult閲嶅啓put鏂规硶锛屼互鏂逛究閾惧紡璋冪敤</li> + <li>浼樺寲鐧诲綍/楠岃瘉鐮佽姹俬eaders涓嶈缃畉oken</li> + <li>浼樺寲鐢ㄦ埛涓汉淇℃伅鎺ュ彛闃叉淇敼鐢ㄦ埛鍚�</li> + <li>浼樺寲Cron琛ㄨ揪寮忕敓鎴愬櫒鍏抽棴鏃堕攢姣侀伩鍏嶇紦瀛�</li> + <li>浼樺寲娉ㄥ唽鎴愬姛鎻愮ず娑堟伅绫诲瀷success</li> + <li>浼樺寲aop璇硶锛屼娇鐢╯pring鑷姩娉ㄥ叆娉ㄨВ</li> + <li>浼樺寲璁板綍鐧诲綍淇℃伅锛岀Щ闄や笉蹇呰鐨勪慨鏀�</li> + <li>浼樺寲mybatis鍏ㄥ眬榛樿鐨勬墽琛屽櫒</li> + <li>浼樺寲Excel瀵煎叆鍥剧墖鍙兘鍑虹幇鐨勫紓甯�</li> + <li>淇浠g爜鐢熸垚妯℃澘涓诲瓙琛ㄥ垹闄ょ己灏戜簨鍔�</li> + <li>淇鏃ュ織璁板綍鍙兘鍑虹幇鐨勮浆鎹㈠紓甯�</li> + <li>淇浠g爜鐢熸垚澶嶉�夋瀛楀吀閬楁紡闂</li> + <li>淇鍏抽棴xss鍔熻兘瀵艰嚧鍙噸澶嶈RepeatableFilter澶辨晥</li> + <li>淇瀛楃涓叉棤娉曡鍙嶈浆涔夐棶棰�</li> + <li>淇鍚庣涓诲瓙琛ㄤ唬鐮佹ā鏉挎柟娉曞悕鐢熸垚閿欒闂</li> + <li>淇xss杩囨护鍚庢牸寮忓嚭鐜扮殑寮傚父</li> + <li>淇swagger娌℃湁鎸囧畾dataTypeClass瀵艰嚧鍚姩鍑虹幇warn鏃ュ織</li> + <li>鍏朵粬缁嗚妭浼樺寲</li> + </ol> + </el-collapse-item> + <el-collapse-item title="v3.7.0 - 2021-09-13"> + <ol> + <li>鍙傛暟绠$悊鏀寔閰嶇疆楠岃瘉鐮佸紑鍏�</li> + <li>鏂板鏄惁寮�鍚敤鎴锋敞鍐屽姛鑳�</li> + <li>瀹氭椂浠诲姟鏀寔鍦ㄧ嚎鐢熸垚cron琛ㄨ揪寮�</li> + <li>鑿滃崟绠$悊鏀寔閰嶇疆璺敱鍙傛暟</li> + <li>鏀寔鑷畾涔夋敞瑙e疄鐜版帴鍙i檺娴�</li> + <li>Excel娉ㄨВ鏀寔Image鍥剧墖瀵煎叆</li> + <li>鑷畾涔夊脊灞傛孩鍑烘粴鍔ㄦ牱寮�</li> + <li>鑷畾涔夊彲鎷栧姩寮圭獥瀹藉害鎸囦护</li> + <li>鑷畾涔夊彲鎷栧姩寮圭獥楂樺害鎸囦护</li> + <li>淇浠绘剰璐︽埛瓒婃潈闂</li> + <li>淇敼鏃舵鏌ョ敤鎴锋暟鎹潈闄愯寖鍥�</li> + <li>淇淇濆瓨閰嶇疆涓婚棰滆壊澶辨晥闂</li> + <li>鏂板鏆楄壊鑿滃崟椋庢牸涓婚</li> + <li>鑿滃崟&閮ㄩ棬鏂板灞曞紑/鎶樺彔鍔熻兘</li> + <li>椤电鏂板鍏抽棴宸︿晶&娣诲姞鍥炬爣</li> + <li>椤堕儴鑿滃崟鎺掗櫎闅愯棌鐨勯粯璁よ矾鐢�</li> + <li>椤堕儴鑿滃崟鍚屾绯荤粺涓婚鏍峰紡</li> + <li>璺宠浆璺敱楂樹寒鐩稿搴旂殑鑿滃崟鏍�</li> + <li>浠g爜鐢熸垚涓诲瓙琛ㄥ閫夎鏁版嵁</li> + <li>鏃ユ湡鑼冨洿鏀寔娣诲姞澶氱粍</li> + <li>鍗囩骇element-ui鍒版渶鏂扮増鏈�2.15.5</li> + <li>鍗囩骇oshi鍒版渶鏂扮増鏈瑅5.8.0</li> + <li>鍗囩骇commons.io鍒版渶鏂扮増鏈瑅2.11.0</li> + <li>瀹氭椂浠诲姟灞忚斀ldap杩滅▼璋冪敤</li> + <li>瀹氭椂浠诲姟灞忚斀http(s)杩滅▼璋冪敤</li> + <li>琛ュ厖瀹氭椂浠诲姟琛ㄥ瓧娈垫敞閲�</li> + <li>瀹氭椂浠诲姟瀵规鏌ュ紓甯歌繘琛屼簨鍔″洖婊�</li> + <li>鍚敤鐖堕儴闂ㄧ姸鎬佹帓闄ら《绾ц妭鐐�</li> + <li>瀵屾枃鏈柊澧炰笂浼犳枃浠跺ぇ灏忛檺鍒�</li> + <li>榛樿棣栭〉浣跨敤keep-alive缂撳瓨</li> + <li>淇敼浠g爜鐢熸垚瀛楀吀鍥炴樉鏍峰紡</li> + <li>鑷畾涔夊垎椤靛悎鐞嗗寲浼犲叆鍙傛暟</li> + <li>淇瀛楀吀缁勪欢鍊间负鏁村舰涓嶆樉绀洪棶棰�</li> + <li>淇瀹氭椂浠诲姟鏃ュ織鎵ц鐘舵�佹樉绀�</li> + <li>瑙掕壊&鑿滃崟鏂板瀛楁灞炴�ф彁绀轰俊鎭�</li> + <li>淇瑙掕壊鍒嗛厤鐢ㄦ埛椤甸潰鍙傛暟绫诲瀷閿欒鎻愰啋</li> + <li>浼樺寲甯冨眬璁剧疆鍔ㄧ敾鐗规晥</li> + <li>浼樺寲寮傚父澶勭悊淇℃伅</li> + <li>浼樺寲閿欒token瀵艰嚧鐨勮В鏋愬紓甯�</li> + <li>瀵嗙爜妗嗘柊澧炴樉绀哄垏鎹㈠瘑鐮佸浘鏍�</li> + <li>瀹氭椂浠诲姟鏂板鏇村鎿嶄綔</li> + <li>鏇村鎿嶄綔鎸夐挳娣诲姞鏉冮檺鎺у埗</li> + <li>瀵煎叆鐢ㄦ埛鏍峰紡浼樺寲</li> + <li>鎻愬彇閫氱敤鏂规硶鍒板熀绫绘帶鍒跺櫒</li> + <li>浼樺寲浣跨敤鏉冮檺宸ュ叿鑾峰彇鐢ㄦ埛淇℃伅</li> + <li>浼樺寲鐢ㄦ埛涓嶈兘鍒犻櫎鑷繁</li> + <li>浼樺寲XSS璺ㄧ珯鑴氭湰杩囨护</li> + <li>浼樺寲浠g爜鐢熸垚妯℃澘</li> + <li>楠岃瘉鐮侀粯璁�20s瓒呮椂</li> + <li>BLOB涓嬭浇鏃舵竻闄RL瀵硅薄寮曠敤</li> + <li>浠g爜鐢熸垚瀵煎叆琛ㄦ寜鍒涘缓鏃堕棿鎺掑簭</li> + <li>淇浠g爜鐢熸垚椤甸潰鏁版嵁缂栬緫淇濆瓨涔嬪悗鎬绘槸璺宠浆绗竴椤电殑闂</li> + <li>淇甯afari娴忚鍣ㄦ棤娉曟牸寮忓寲utc鏃ユ湡鏍煎紡yyyy-MM-dd'T'HH:mm:ss.SSS闂</li> + <li>澶氬浘涓婁紶缁勪欢绉婚櫎澶氫綑鐨刟pi鍦板潃&楠岃瘉澶辫触瀵艰嚧鍥剧墖鍒犻櫎闂&鏃犳硶鍒犻櫎鐩稿簲鍥剧墖淇</li> + <li>鍏朵粬缁嗚妭浼樺寲</li> + </ol> + </el-collapse-item> + <el-collapse-item title="v3.6.0 - 2021-07-12"> + <ol> + <li>瑙掕壊绠$悊鏂板鍒嗛厤鐢ㄦ埛鍔熻兘</li> + <li>鐢ㄦ埛绠$悊鏂板鍒嗛厤瑙掕壊鍔熻兘</li> + <li>鏃ュ織鍒楄〃鏀寔鎺掑簭鎿嶄綔</li> + <li>浼樺寲鍙傛暟&瀛楀吀缂撳瓨鎿嶄綔</li> + <li>绯荤粺甯冨眬閰嶇疆鏀寔鍔ㄦ�佹爣棰樺紑鍏�</li> + <li>鑿滃崟璺敱閰嶇疆鏀寔鍐呴摼璁块棶</li> + <li>榛樿璁块棶鍚庣棣栭〉鏂板鎻愮ず璇�</li> + <li>瀵屾枃鏈粯璁や笂浼犺繑鍥瀠rl绫诲瀷</li> + <li>鏂板鑷畾涔夊脊绐楁嫋鎷芥寚浠�</li> + <li>鍏ㄥ眬娉ㄥ唽甯哥敤閫氱敤缁勪欢</li> + <li>鍏ㄥ眬鎸傝浇瀛楀吀鏍囩缁勪欢</li> + <li>ImageUpload缁勪欢鏀寔澶氬浘鐗囦笂浼�</li> + <li>FileUpload缁勪欢鏀寔澶氭枃浠朵笂浼�</li> + <li>鏂囦欢涓婁紶缁勪欢娣诲姞鏁伴噺闄愬埗灞炴��</li> + <li>瀵屾枃鏈紪杈戠粍浠舵坊鍔犵被鍨嬪睘鎬�</li> + <li>瀵屾枃鏈粍浠跺伐鍏锋爮閰嶇疆瑙嗛</li> + <li>灏佽閫氱敤iframe缁勪欢</li> + <li>闄愬埗瓒呯骇绠$悊鍛樹笉鍏佽鎿嶄綔</li> + <li>鐢ㄦ埛淇℃伅闀垮害鏍¢獙闄愬埗</li> + <li>鍒嗛〉缁勪欢鏂板pagerCount灞炴��</li> + <li>娣诲姞bat鑴氭湰鎵ц搴旂敤</li> + <li>鍗囩骇oshi鍒版渶鏂扮増鏈瑅5.7.4</li> + <li>鍗囩骇element-ui鍒版渶鏂扮増鏈�2.15.2</li> + <li>鍗囩骇pagehelper鍒版渶鏂扮増1.3.1</li> + <li>鍗囩骇commons.io鍒版渶鏂扮増鏈瑅2.10.0</li> + <li>鍗囩骇commons.fileupload鍒版渶鏂扮増鏈瑅1.4</li> + <li>鍗囩骇swagger鍒版渶鏂扮増鏈瑅3.0.0</li> + <li>淇鍏抽棴confirm鎻愮ず妗嗘帶鍒跺彴鎶ラ敊闂</li> + <li>淇瀛樺湪鐨凷QL娉ㄥ叆婕忔礊闂</li> + <li>瀹氭椂浠诲姟灞忚斀rmi杩滅▼璋冪敤</li> + <li>淇鐢ㄦ埛鎼滅储鍒嗛〉鍙橀噺閿欒</li> + <li>淇瀵煎嚭瑙掕壊鏁版嵁鑼冨洿缈昏瘧缂哄皯浠呮湰浜�</li> + <li>淇琛ㄥ崟鏋勫缓閫夋嫨涓嬫媺閫夋嫨鎺у埗鍙版姤閿欓棶棰�</li> + <li>浼樺寲鍥剧墖宸ュ叿绫昏鍙栨枃浠�</li> + <li>鍏朵粬缁嗚妭浼樺寲</li> + </ol> + </el-collapse-item> + <el-collapse-item title="v3.5.0 - 2021-05-25"> + <ol> + <li>鏂板鑿滃崟瀵艰埅鏄剧ず椋庢牸TopNav锛坒alse涓哄乏渚у鑸彍鍗曪紝true涓洪《閮ㄥ鑸彍鍗曪級</li> + <li>甯冨眬璁剧疆鏀寔淇濆瓨&閲嶇疆閰嶇疆</li> + <li>淇鏍戣〃鏁版嵁鏄剧ず涓嶅叏&鍔犺浇鎱㈤棶棰�</li> + <li>鏂板IE娴忚鍣ㄧ増鏈繃浣庢彁绀洪〉闈�</li> + <li>鐢ㄦ埛鐧诲綍鍚庤褰曟渶鍚庣櫥褰旾P&鏃堕棿</li> + <li>椤甸潰瀵煎嚭鎸夐挳鐐瑰嚮涔嬪悗娣诲姞閬僵</li> + <li>瀵屾枃鏈紪杈戝櫒鏀寔鑷畾涔変笂浼犲湴鍧�</li> + <li>瀵屾枃鏈紪杈戠粍浠舵柊澧瀝eadOnly灞炴��</li> + <li>椤电TagsView鏂板鍏抽棴鍙充晶鍔熻兘</li> + <li>鏄鹃殣鍒楃粍浠跺姞杞藉垵濮嬮粯璁ら殣钘忓垪</li> + <li>鍏抽棴澶村儚涓婁紶绐楀彛杩樺師榛樿鍥剧墖</li> + <li>涓汉淇℃伅娣诲姞鎵嬫満&閭閲嶅楠岃瘉</li> + <li>浠g爜鐢熸垚妯℃澘瀵煎嚭鎸夐挳鐐瑰嚮鍚庢坊鍔犻伄缃�</li> + <li>浠g爜鐢熸垚妯℃澘鏍戣〃鎿嶄綔鍒楁坊鍔犳柊澧炴寜閽�</li> + <li>浠g爜鐢熸垚妯℃澘淇涓诲瓙琛ㄥ瓧娈甸噸鍚嶉棶棰�</li> + <li>鍗囩骇fastjson鍒版渶鏂扮増1.2.76</li> + <li>鍗囩骇druid鍒版渶鏂扮増鏈瑅1.2.6</li> + <li>鍗囩骇mybatis鍒版渶鏂扮増3.5.6 闃绘杩滅▼浠g爜鎵ц婕忔礊</li> + <li>鍗囩骇oshi鍒版渶鏂扮増鏈瑅5.6.0</li> + <li>velocity鍓旈櫎commons-collections鐗堟湰锛岄槻姝�3.2.1鐗堟湰鐨勫弽搴忓垪鍖栨紡娲�</li> + <li>鏁版嵁鐩戞帶椤甸粯璁よ处鎴峰瘑鐮侀槻姝㈣秺鏉冭闂�</li> + <li>淇firefox涓嬭〃鍗曟瀯寤烘嫋鎷戒細鏂版墦鍗′竴涓�夐」鍗�</li> + <li>淇鍚庣瀵煎叆琛ㄦ潈闄愭爣璇�</li> + <li>淇鍓嶇鎿嶄綔鏃ュ織&鐧诲綍鏃ュ織鏉冮檺鏍囪瘑</li> + <li>璁剧疆Redis閰嶇疆HashKey搴忓垪鍖�</li> + <li>鍒犻櫎鎿嶄綔鏃ュ織璁板綍淇℃伅</li> + <li>涓婁紶濯掍綋绫诲瀷娣诲姞瑙嗛鏍煎紡</li> + <li>淇璇锋眰褰㈠弬鏈紶鍊艰褰曟棩蹇楀紓甯搁棶棰�</li> + <li>浼樺寲xss鏍¢獙json璇锋眰鏉′欢</li> + <li>鏍戠骇缁撴瀯鏇存柊瀛愯妭鐐逛娇鐢╮eplaceFirst</li> + <li>浼樺寲ExcelUtil绌哄�煎鐞�</li> + <li>鏃ュ織璁板綍杩囨护BindingResult瀵硅薄锛岄槻姝㈠紓甯�</li> + <li>淇敼涓婚鍚巑ini绫诲瀷鎸夐挳鏃犳晥闂</li> + <li>浼樺寲閫氱敤涓嬭浇瀹屾垚鍚庡垹闄よ妭鐐�</li> + <li>閫氱敤Controller娣诲姞鍝嶅簲杩斿洖娑堟伅</li> + <li>鍏朵粬缁嗚妭浼樺寲</li> + </ol> + </el-collapse-item> + <el-collapse-item title="v3.4.0 - 2021-02-22"> + <ol> + <li>浠g爜鐢熸垚妯℃澘鏀寔涓诲瓙琛�</li> + <li>琛ㄦ牸鍙充晶宸ュ叿鏍忕粍浠舵敮鎸佹樉闅愬垪</li> + <li>鍥剧墖缁勪欢娣诲姞棰勮&绉婚櫎鍔熻兘</li> + <li>Excel娉ㄨВ鏀寔Image鍥剧墖瀵煎嚭</li> + <li>鎿嶄綔鎸夐挳缁勮皟鏁翠负鏈寸礌鎸夐挳鏍峰紡</li> + <li>浠g爜鐢熸垚鏀寔鏂囦欢涓婁紶缁勪欢</li> + <li>浠g爜鐢熸垚鏃ユ湡鎺т欢鍖哄垎鑼冨洿</li> + <li>浠g爜鐢熸垚鏁版嵁搴撴枃鏈被鍨嬬敓鎴愯〃鍗曟枃鏈煙</li> + <li>鐢ㄦ埛鎵嬫満閭&鑿滃崟缁勪欢淇敼鍏佽绌哄瓧绗︿覆</li> + <li>鍗囩骇SpringBoot鍒版渶鏂扮増鏈�2.2.13 鎻愬崌鍚姩閫熷害</li> + <li>鍗囩骇druid鍒版渶鏂扮増鏈瑅1.2.4</li> + <li>鍗囩骇fastjson鍒版渶鏂扮増1.2.75</li> + <li>鍗囩骇element-ui鍒版渶鏂扮増鏈�2.15.0</li> + <li>淇IE11娴忚鍣ㄦ姤閿欓棶棰�</li> + <li>浼樺寲澶氱骇鑿滃崟涔嬮棿鍒囨崲鏃犳硶缂撳瓨鐨勯棶棰�</li> + <li>淇鍥涚骇鑿滃崟鏃犳硶鏄剧ず闂</li> + <li>淇渚ц竟鏍忛潤鎬佽矾鐢变涪澶遍棶棰�</li> + <li>淇瑙掕壊绠$悊-缂栬緫瑙掕壊-鍔熻兘鏉冮檺鏄剧ず寮傚父</li> + <li>閰嶇疆鏂囦欢鏂板redis鏁版嵁搴撶储寮曞睘鎬�</li> + <li>鏉冮檺宸ュ叿绫诲鍔燼dmin鍒ゆ柇</li> + <li>瑙掕壊闈炶嚜瀹氫箟鏉冮檺鑼冨洿娓呯┖閫夋嫨鍊�</li> + <li>淇瀵煎叆鏁版嵁涓鸿礋娴偣鏁版椂涓㈠け绮惧害闂</li> + <li>绉婚櫎path-to-regexp姝e垯鍖归厤鎻掍欢</li> + <li>淇鐢熸垚鏍戣〃浠g爜寮傚父</li> + <li>淇敼ip瀛楁闀垮害闃叉ipv6鍦板潃闀垮害涓嶅</li> + <li>闃叉get璇锋眰鍙傛暟鍊间负false鎴�0绛夌壒娈婂�间細瀵艰嚧鏃犳硶姝g‘鐨勪紶鍙�</li> + <li>鐧诲綍鍚巔ush娣诲姞catch闃叉鍑虹幇妫�鏌ラ敊璇�</li> + <li>鍏朵粬缁嗚妭浼樺寲</li> + </ol> + </el-collapse-item> + <el-collapse-item title="v3.3.0 - 2020-12-14"> + <ol> + <li>鏂板缂撳瓨鐩戞帶鍔熻兘</li> + <li>鏀寔涓婚椋庢牸閰嶇疆</li> + <li>淇澶氱骇鑿滃崟涔嬮棿鍒囨崲鏃犳硶缂撳瓨鐨勯棶棰�</li> + <li>澶氱骇鑿滃崟鑷姩閰嶇疆缁勪欢</li> + <li>浠g爜鐢熸垚棰勮鏀寔楂樹寒鏄剧ず</li> + <li>鏀寔Get璇锋眰鏄犲皠Params鍙傛暟</li> + <li>鍒犻櫎鐢ㄦ埛鍜岃鑹茶В缁戝叧鑱�</li> + <li>鍘婚櫎鐢ㄦ埛鎵嬫満閭閮ㄩ棬蹇呭~楠岃瘉</li> + <li>Excel鏀寔娉ㄨВalign瀵归綈鏂瑰紡</li> + <li>Excel鏀寔瀵煎叆Boolean鍨嬫暟鎹�</li> + <li>浼樺寲澶村儚鏍峰紡锛岄紶鏍囩Щ鍏ユ偓鍋滈伄缃�</li> + <li>浠g爜鐢熸垚棰勮鎻愪緵婊氬姩鏈哄埗</li> + <li>浠g爜鐢熸垚鍒犻櫎澶氫綑鐨勬暟瀛梖loat绫诲瀷</li> + <li>淇杞崲瀛楃涓茬殑鐩爣瀛楃闆嗗睘鎬�</li> + <li>鍥炴樉鏁版嵁瀛楀吀闃叉绌哄�兼姤閿�</li> + <li>鏃ュ織璁板綍澧炲姞杩囨护澶氭枃浠跺満鏅�</li> + <li>淇敼缂撳瓨Set鏂规硶鍙兘瀵艰嚧宓屽鐨勯棶棰�</li> + <li>绉婚櫎鍓嶇涓�浜涘浣欑殑渚濊禆</li> + <li>闃叉瀹夊叏鎵弿YUI鍑虹幇鐨勯闄╂彁绀�</li> + <li>淇敼node-sass涓篸art-sass</li> + <li>鍗囩骇SpringBoot鍒版渶鏂扮増鏈�2.1.18</li> + <li>鍗囩骇poi鍒版渶鏂扮増鏈�4.1.2</li> + <li>鍗囩骇oshi鍒版渶鏂扮増鏈瑅5.3.6</li> + <li>鍗囩骇bitwalker鍒版渶鏂扮増鏈�1.21</li> + <li>鍗囩骇axios鍒版渶鏂扮増鏈�0.21.0</li> + <li>鍗囩骇element-ui鍒版渶鏂扮増鏈�2.14.1</li> + <li>鍗囩骇vue鍒版渶鏂扮増鏈�2.6.12</li> + <li>鍗囩骇vuex鍒版渶鏂扮増鏈�3.6.0</li> + <li>鍗囩骇vue-cli鍒扮増鏈�4.5.9</li> + <li>鍗囩骇vue-router鍒版渶鏂扮増鏈�3.4.9</li> + <li>鍗囩骇vue-cli鍒版渶鏂扮増鏈�4.4.6</li> + <li>鍗囩骇vue-cropper鍒版渶鏂扮増鏈�0.5.5</li> + <li>鍗囩骇clipboard鍒版渶鏂扮増鏈�2.0.6</li> + <li>鍗囩骇core-js鍒版渶鏂扮増鏈�3.8.1</li> + <li>鍗囩骇echarts鍒版渶鏂扮増鏈�4.9.0</li> + <li>鍗囩骇file-saver鍒版渶鏂扮増鏈�2.0.4</li> + <li>鍗囩骇fuse.js鍒版渶鏂扮増鏈�6.4.3</li> + <li>鍗囩骇js-beautify鍒版渶鏂扮増鏈�1.13.0</li> + <li>鍗囩骇js-cookie鍒版渶鏂扮増鏈�2.2.1</li> + <li>鍗囩骇path-to-regexp鍒版渶鏂扮増鏈�6.2.0</li> + <li>鍗囩骇quill鍒版渶鏂扮増鏈�1.3.7</li> + <li>鍗囩骇screenfull鍒版渶鏂扮増鏈�5.0.2</li> + <li>鍗囩骇sortablejs鍒版渶鏂扮増鏈�1.10.2</li> + <li>鍗囩骇vuedraggable鍒版渶鏂扮増鏈�2.24.3</li> + <li>鍗囩骇chalk鍒版渶鏂扮増鏈�4.1.0</li> + <li>鍗囩骇eslint鍒版渶鏂扮増鏈�7.15.0</li> + <li>鍗囩骇eslint-plugin-vue鍒版渶鏂扮増鏈�7.2.0</li> + <li>鍗囩骇lint-staged鍒版渶鏂扮増鏈�10.5.3</li> + <li>鍗囩骇runjs鍒版渶鏂扮増鏈�4.4.2</li> + <li>鍗囩骇sass-loader鍒版渶鏂扮増鏈�10.1.0</li> + <li>鍗囩骇script-ext-html-webpack-plugin鍒版渶鏂扮増鏈�2.1.5</li> + <li>鍗囩骇svg-sprite-loader鍒版渶鏂扮増鏈�5.1.1</li> + <li>鍗囩骇vue-template-compiler鍒版渶鏂扮増鏈�2.6.12</li> + <li>鍏朵粬缁嗚妭浼樺寲</li> + </ol> + </el-collapse-item> + <el-collapse-item title="v3.2.1 - 2020-11-18"> + <ol> + <li>闃绘浠绘剰鏂囦欢涓嬭浇婕忔礊</li> + <li>浠g爜鐢熸垚鏀寔涓婁紶鎺т欢</li> + <li>鏂板鍥剧墖涓婁紶缁勪欢</li> + <li>璋冩暣榛樿棣栭〉</li> + <li>鍗囩骇druid鍒版渶鏂扮増鏈瑅1.2.2</li> + <li>mapperLocations閰嶇疆鏀寔鍒嗛殧绗�</li> + <li>鏉冮檺淇℃伅璋冩暣</li> + <li>璋冩暣sql榛樿鏃堕棿</li> + <li>瑙e喅浠g爜鐢熸垚娌℃湁bit绫诲瀷鐨勯棶棰�</li> + <li>鍗囩骇pagehelper鍒版渶鏂扮増1.3.0</li> + </ol> + </el-collapse-item> + <el-collapse-item title="v3.2.0 - 2020-10-10"> + <ol> + <li>鍗囩骇springboot鐗堟湰鍒�2.1.17 鎻愬崌瀹夊叏鎬�</li> + <li>鍗囩骇oshi鍒版渶鏂扮増鏈瑅5.2.5</li> + <li>鍗囩骇druid鍒版渶鏂扮増鏈瑅1.2.1</li> + <li>鍗囩骇jjwt鍒扮増鏈�0.9.1</li> + <li>鍗囩骇fastjson鍒版渶鏂扮増1.2.74</li> + <li>淇敼sass涓簄ode-sass锛岄伩鍏峞l-icon鍥炬爣涔辩爜</li> + <li>浠g爜鐢熸垚鏀寔鍚屾鏁版嵁搴�</li> + <li>浠g爜鐢熸垚鏀寔瀵屾枃鏈帶浠�</li> + <li>浠g爜鐢熸垚椤甸潰鏃朵笉蹇界暐remark灞炴��</li> + <li>浠g爜鐢熸垚娣诲姞select蹇呭~閫夐」</li> + <li>Excel瀵煎嚭绫诲瀷NUMERIC鏀寔绮惧害娴偣绫诲瀷</li> + <li>Excel瀵煎嚭targetAttr浼樺寲鑾峰彇鍊硷紝闃叉get鏂规硶涓嶈鑼�</li> + <li>Excel娉ㄨВ鏀寔鑷姩缁熻鏁版嵁鎬诲拰</li> + <li>Excel娉ㄨВ鏀寔璁剧疆BigDecimal绮惧害&鑸嶅叆瑙勫垯</li> + <li>鑿滃崟&鏁版嵁鏉冮檺鏂板锛堝睍寮�/鎶樺彔 鍏ㄩ��/鍏ㄤ笉閫� 鐖跺瓙鑱斿姩锛�</li> + <li>鍏佽鐢ㄦ埛鍒嗛厤鍒伴儴闂ㄧ埗鑺傜偣</li> + <li>鑿滃崟鏂板鏄惁缂撳瓨keep-alive</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>淇瀹氭椂浠诲姟鎵ц涓�娆℃潈闄愭爣璇�</li> + <li>淇鏁版嵁搴撳瓧绗︿覆绫诲瀷nvarchar</li> + <li>浼樺寲閫掑綊瀛愯妭鐐�</li> + <li>浼樺寲鏁版嵁鏉冮檺鍒ゆ柇</li> + <li>鍏朵粬缁嗚妭浼樺寲</li> + </ol> + </el-collapse-item> + + <el-collapse-item title="v3.1.0 - 2020-08-13"> + <ol> + <li>琛ㄦ牸宸ュ叿鏍忓彸渚ф坊鍔犲埛鏂�&鏄鹃殣鏌ヨ缁勪欢</li> + <li>鍚庣鏀寔CORS璺ㄥ煙璇锋眰</li> + <li>浠g爜鐢熸垚鏀寔閫夋嫨涓婄骇鑿滃崟</li> + <li>浠g爜鐢熸垚鏀寔鑷畾涔夎矾寰�</li> + <li>浠g爜鐢熸垚鏀寔澶嶉�夋</li> + <li>Excel瀵煎嚭瀵煎叆鏀寔dictType瀛楀吀绫诲瀷</li> + <li>Excel鏀寔鍒嗗壊瀛楃涓茬粍鍐呭</li> + <li>楠岃瘉鐮佺被鍨嬫敮鎸侊紙鏁扮粍璁$畻銆佸瓧绗﹂獙璇侊級</li> + <li>鍗囩骇vue-cli鐗堟湰鍒�4.4.4</li> + <li>淇敼 node-sass 涓� dart-sass</li> + <li>琛ㄥ崟绫诲瀷涓篒nteger/Long璁剧疆鏁村舰榛樿鍊�</li> + <li>浠g爜鐢熸垚鍣ㄩ粯璁apper璺緞涓庨粯璁apperScan璺緞涓嶄竴鑷�</li> + <li>浼樺寲闃查噸澶嶆彁浜ゆ嫤鎴櫒</li> + <li>浼樺寲涓婄骇鑿滃崟涓嶈兘閫夋嫨鑷繁</li> + <li>淇瑙掕壊鐨勬潈闄愬垎閰嶅悗锛屾湭瀹炴椂鐢熸晥闂</li> + <li>淇鍦ㄧ嚎鐢ㄦ埛鏃ュ織璁板綍绫诲瀷</li> + <li>淇瀵屾枃鏈┖鏍煎拰缂╄繘淇濆瓨鍚庝笉鐢熸晥闂</li> + <li>淇鍦ㄧ嚎鐢ㄦ埛鍒ゆ柇閫昏緫</li> + <li>鍞竴闄愬埗鏉′欢鍙繑鍥炲崟鏉℃暟鎹�</li> + <li>娣诲姞鑾峰彇褰撳墠鐨勭幆澧冮厤缃柟娉�</li> + <li>瓒呮椂鐧诲綍鍚庨〉闈㈣烦杞埌棣栭〉</li> + <li>鍏ㄥ眬寮傚父鐘舵�佹眽鍖栨嫤鎴鐞�</li> + <li>HTML杩囨护鍣ㄦ敼涓哄皢html杞箟</li> + <li>妫�鏌ュ瓧绗︽敮鎸佸皬鏁扮偣&闄嶇骇鏀规垚寮傚父鎻愰啋</li> + <li>鍏朵粬缁嗚妭浼樺寲</li> + </ol> + </el-collapse-item> + + <el-collapse-item title="v3.0.0 - 2020-07-20"> + <ol> + <li>鍗曞簲鐢ㄨ皟鏁翠负澶氭ā鍧楅」鐩�</li> + <li>鍗囩骇element-ui鐗堟湰鍒�2.13.2</li> + <li>鍒犻櫎babel锛屾彁楂樼紪璇戦�熷害銆�</li> + <li>鏂板鑿滃崟榛樿涓荤被鐩�</li> + <li>缂栫爜鏂囦欢鍚嶄慨鏀逛负uuid鏂瑰紡</li> + <li>瀹氭椂浠诲姟cron琛ㄨ揪寮忛獙璇�</li> + <li>瑙掕壊鏉冮檺淇敼鏃跺凡鏈夋潈闄愭湭鑷姩鍕鹃�夊紓甯镐慨澶�</li> + <li>闃叉鍒囨崲鏉冮檺鐢ㄦ埛鍚庣櫥褰曞嚭鐜�404</li> + <li>Excel鏀寔sort瀵煎嚭鎺掑簭</li> + <li>鍒涘缓鐢ㄦ埛涓嶅厑璁搁�夋嫨瓒呯骇绠$悊鍛樿鑹�</li> + <li>淇浠g爜鐢熸垚瀵煎叆琛ㄧ粨鏋勫嚭鐜板紓甯搁〉闈笉鎻愰啋闂</li> + <li>淇浠g爜鐢熸垚鐐瑰嚮澶氭琛ㄤ慨鏀规暟鎹笉鍙樺寲鐨勯棶棰�</li> + <li>淇澶村儚涓婁紶鎴愬姛浜屾鎵撳紑鏃犳硶鏀瑰彉瑁佸壀妗嗗ぇ灏忓拰浣嶇疆闂</li> + <li>淇甯冨眬涓簊mall鑰卪ini鐢ㄦ埛琛ㄥ崟鏄剧ず閿欎綅闂</li> + <li>淇鐑儴缃插鑷寸殑寮烘崲寮傚父闂</li> + <li>淇敼鐢ㄦ埛绠$悊澶嶉�夋瀹藉害锛岄槻姝㈤儴鍒嗘祻瑙堝櫒鍑虹幇鐪佺暐鍙�</li> + <li>IpUtils宸ュ叿锛屾竻闄ss鐗规畩瀛楃锛岄槻姝ff娉ㄥ叆鏀诲嚮</li> + <li>鐢熸垚domain 濡傛灉鏄诞鐐瑰瀷 缁熶竴鐢˙igDecimal</li> + <li>瀹氭椂浠诲姟璋冩暣label-width锛岄槻姝㈤儴缃插嚭鐜伴敊浣�</li> + <li>璋冩暣琛ㄥご鍥哄畾鍒楅粯璁ゆ牱寮�</li> + <li>浠g爜鐢熸垚妯℃澘璋冩暣锛屽瓧娈典负String骞朵笖蹇呭~鍒欏姞绌轰覆鏉′欢</li> + <li>浠g爜鐢熸垚瀛楀吀Integer/Long浣跨敤parseInt</li> + <li> + 淇dict_sort涓嶅彲update涓�0鐨勯棶棰�&鏌ヨ杩斿洖澧炲姞dict_sort鍗囧簭鎺掑簭 + </li> + <li>淇宀椾綅瀵煎嚭鏉冮檺娉ㄨВ</li> + <li>绂佹鍔犲瘑瀵嗘枃杩斿洖鍓嶇</li> + <li>淇浠g爜鐢熸垚椤甸潰涓殑鏌ヨ鏉′欢鍒涘缓鏃堕棿鏈敓鏁堢殑闂</li> + <li>淇棣栭〉鎼滅储鑿滃崟澶栭摼鏃犳硶鐐瑰嚮璺宠浆闂</li> + <li>淇鑿滃崟绠$悊閫夋嫨鍥炬爣锛宐ackspace鍒犻櫎鏃朵笉杩囨护鏁版嵁</li> + <li>鐢ㄦ埛绠$悊閮ㄩ棬鍒嗘敮鑺傜偣涓嶅彲妫�鏌�&鏄剧ず璁℃暟</li> + <li>鏁版嵁鑼冨洿杩囨护灞炴�ц皟鏁�</li> + <li>鍏朵粬缁嗚妭浼樺寲</li> + </ol> + </el-collapse-item> + + <el-collapse-item title="v2.3.0 - 2020-06-01"> + <ol> + <li>鍗囩骇fastjson鍒版渶鏂扮増1.2.70 淇楂樺嵄瀹夊叏婕忔礊</li> + <li>dev鍚姩榛樿鎵撳紑娴忚鍣�</li> + <li>vue-cli浣跨敤榛樿source-map</li> + <li>slidebar eslint鎶ラ敊浼樺寲</li> + <li>褰搕ags-view婊氬姩鍏抽棴鍙抽敭鑿滃崟</li> + <li>瀛楀吀绠$悊娣诲姞缂撳瓨璇诲彇</li> + <li>鍙傛暟绠$悊鏀寔缂撳瓨鎿嶄綔</li> + <li>鏀寔涓�绾ц彍鍗曪紙鍜屼富椤靛悓绾э級鍦╩ain鍖哄煙鏄剧ず</li> + <li>闄愬埗澶栭摼鍦板潃蹇呴』浠ttp(s)寮�澶�</li> + <li>tagview & sidebar 涓婚棰滆壊涓巈lement ui(鍏ㄥ眬)鍚屾</li> + <li>淇敼鏁版嵁婧愮被鍨嬩紭鍏堢骇锛屽厛鏍规嵁鏂规硶锛屽啀鏍规嵁绫�</li> + <li>鏀寔鏄惁闇�瑕佽缃畉oken灞炴�э紝鑷畾涔夎繑鍥炵爜娑堟伅銆�</li> + <li>swagger璇锋眰鍓嶇紑鍔犲叆閰嶇疆銆�</li> + <li>鐧诲綍鍦扮偣璁剧疆鍐呭杩囬暱鍒欓殣钘忔樉绀�</li> + <li>淇瀹氭椂浠诲姟鎵ц涓�娆℃寜閽悗涓嶆彁绀烘秷鎭棶棰�</li> + <li>淇敼涓婄骇閮ㄩ棬锛堥�夋嫨椤规帓闄ゆ湰韬拰涓嬬骇锛�</li> + <li>閫氱敤http鍙戦�佹柟娉曞鍔犲弬鏁� contentType 缂栫爜绫诲瀷</li> + <li>鏇存崲IP鍦板潃鏌ヨ鎺ュ彛</li> + <li>淇椤电鍙橀噺undefined</li> + <li>娣诲姞鏍¢獙閮ㄩ棬鍖呭惈鏈仠鐢ㄧ殑瀛愰儴闂�</li> + <li>淇敼瀹氭椂浠诲姟璇︽儏涓嬫鎵ц鏃堕棿鏃ユ湡鏄剧ず閿欒</li> + <li>瑙掕壊绠$悊鏌ヨ璁剧疆榛樿鎺掑簭瀛楁</li> + <li>swagger娣诲姞enable鍙傛暟鎺у埗鏄惁鍚敤</li> + <li>鍙json绫诲瀷璇锋眰鏋勫缓鍙噸澶嶈鍙杋nputStream鐨剅equest</li> + <li>淇敼浠g爜鐢熸垚瀛楀吀瀛楁int绫诲瀷娌℃湁鑷姩閫変腑闂</li> + <li>vuex鐢ㄦ埛鍚嶅彇鍊间慨姝�</li> + <li>琛ㄦ牸鏍戞ā鏉垮幓鎺夊浣欑殑)</li> + <li>浠g爜鐢熸垚搴忓彿淇</li> + <li>鍏ㄥ睆鎯呭喌涓嬩笉璋冩暣涓婂杈硅窛</li> + <li>浠g爜鐢熸垚Date瀛楁娣诲姞榛樿鏍煎紡</li> + <li>鐢ㄦ埛绠$悊瑙掕壊閫夋嫨鏉冮檺鎺у埗</li> + <li>淇璺敱鎳掑姞杞芥姤閿欓棶棰�</li> + <li>妯℃澘sql.vm娣诲姞鑿滃崟鐘舵��</li> + <li>璁剧疆鐢ㄦ埛鍚嶇О涓嶈兘淇敼</li> + <li>dialog娣诲姞append-to-body灞炴�э紝闃叉ie閬僵</li> + <li>鑿滃崟鍖哄垎鐘舵�佸拰鏄剧ず闅愯棌鍔熻兘</li> + <li>鍗囩骇fastjson鍒版渶鏂扮増1.2.68 淇瀹夊叏鍔犲浐</li> + <li>淇浠g爜鐢熸垚濡傛灉閫夋嫨瀛楀吀绫诲瀷缂哄け閫楀彿闂</li> + <li>鐧诲綍璇锋眰params鏇存崲涓篸ata锛岄槻姝㈡毚闇瞮rl</li> + <li>鏃ュ織杩斿洖鏃堕棿鏍煎紡澶勭悊</li> + <li>娣诲姞handle鎺у埗鍏佽鎷栧姩鐨勫厓绱�</li> + <li>甯冨眬璁剧疆鐐瑰嚮鎵╁ぇ鑼冨洿</li> + <li>浠g爜鐢熸垚鍒楀睘鎬ф帓搴忔煡璇�</li> + <li>浠g爜鐢熸垚鍒楁敮鎸佹嫋鍔ㄦ帓搴�</li> + <li>淇鏃堕棿鏍煎紡涓嶆敮鎸乮os闂</li> + <li>琛ㄥ崟鏋勫缓娣诲姞鐖剁骇class锛岄槻姝㈠啿绐�</li> + <li>瀹氭椂浠诲姟骞跺彂灞炴�т慨姝�</li> + <li>瑙掕壊绂佺敤&鑿滃崟闅愯棌涓嶆煡璇㈡潈闄�</li> + <li>鍏朵粬缁嗚妭浼樺寲</li> + </ol> + </el-collapse-item> + + <el-collapse-item title="v2.2.0 - 2020-03-18"> + <ol> + <li>绯荤粺鐩戞帶鏂板瀹氭椂浠诲姟鍔熻兘</li> + <li>娣诲姞涓�涓墦鍖匴eb宸ョ▼bat</li> + <li>淇椤电榧犳爣婊氳疆鎸変笅鐨勬椂鍊欙紝鍙互鍏抽棴涓嶅彲鍏抽棴鐨則ag</li> + <li>淇鐐瑰嚮閫�鍑虹櫥褰曟湁鏃朵細鏃犳彁绀洪棶棰�</li> + <li>淇闃查噸澶嶆彁浜ゆ敞瑙f棤鏁堥棶棰�</li> + <li>淇閫氱煡鍏憡鎵归噺鍒犻櫎寮傚父闂</li> + <li>娣诲姞鑿滃崟鏃惰矾鐢卞湴鍧�蹇呭~闄愬埗</li> + <li>浠g爜鐢熸垚瀛楁鎻忚堪鍙紪杈�</li> + <li>淇鐢ㄦ埛淇敼涓汉淇℃伅瀵艰嚧缂撳瓨涓嶈繃鏈熼棶棰�</li> + <li>涓汉淇℃伅鍒涘缓鏃堕棿鑾峰彇姝g‘灞炴�у��</li> + <li>鎿嶄綔鏃ュ織璇︾粏鏄剧ず姝g‘绫诲瀷</li> + <li>瀵煎叆琛ㄥ崟鍑昏鏁版嵁鏃堕�変腑瀵瑰簲鐨勫閫夋</li> + <li>鎵归噺鏇挎崲琛ㄥ墠缂�閫昏緫璋冩暣</li> + <li>鍥哄畾閲嶅畾鍚戣矾寰勮〃杈惧紡</li> + <li>鍗囩骇element-ui鐗堟湰鍒�2.13.0</li> + <li>鎿嶄綔鏃ュ織鎺掑簭璋冩暣</li> + <li>淇charts鍒囨崲渚ц竟鏍忔垨鑰呯缉鏀剧獥鍙f樉绀篵ug</li> + <li>鍏朵粬缁嗚妭浼樺寲</li> + </ol> + </el-collapse-item> + + <el-collapse-item title="v2.1.0 - 2020-02-24"> + <ol> + <li>鏂板琛ㄥ崟鏋勫缓</li> + <li>浠g爜鐢熸垚鏀寔鏍戣〃缁撴瀯</li> + <li>鏂板鐢ㄦ埛瀵煎叆</li> + <li>淇鍔ㄦ�佸姞杞借矾鐢遍〉闈㈠埛鏂伴棶棰�</li> + <li>淇鍦板潃寮�鍏虫棤鏁堥棶棰�</li> + <li>姹夊寲閿欒鎻愮ず椤甸潰</li> + <li>浠g爜鐢熸垚宸茬煡闂淇敼</li> + <li>淇澶氭暟鎹簮涓嬮厤缃叧闂嚭鐜板紓甯稿鐞�</li> + <li>娣诲姞HTML杩囨护鍣紝鐢ㄤ簬鍘婚櫎XSS婕忔礊闅愭偅</li> + <li>淇涓婁紶澶村儚鎺у埗鍙板嚭鐜板紓甯�</li> + <li>淇敼鐢ㄦ埛绠$悊鍒嗛〉涓嶆纭殑闂</li> + <li>淇楠岃瘉鐮佽褰曟彁绀洪敊璇�</li> + <li>淇request.js缂哄皯Message寮曠敤</li> + <li>淇琛ㄦ牸鏃堕棿涓虹┖鍑虹幇鐨勫紓甯�</li> + <li>娣诲姞Jackson鏃ユ湡鍙嶅簭鍒楀寲鏃跺尯閰嶇疆</li> + <li>璋冩暣鏍规嵁鐢ㄦ埛鏉冮檺鍔犺浇鑿滃崟鏁版嵁鏍戝舰缁撴瀯</li> + <li>璋冩暣鎴愬姛鐧诲綍涓嶆仮澶嶆寜閽紝闃叉澶氭鐐瑰嚮</li> + <li>淇敼鐢ㄦ埛涓汉璧勬枡鍚屾缂撳瓨淇℃伅</li> + <li>淇椤甸潰鍚屾椂鍑虹幇el-upload鍜孍ditor涓嶆樉绀哄鐞�</li> + <li>淇鍦ㄨ鑹茬鐞嗛〉淇敼鑿滃崟鏉冮檺鍋跺皵鏈�変腑闂</li> + <li>閰嶇疆鏂囦欢鏂板redis瀵嗙爜灞炴��</li> + <li>璁剧疆mybatis鍏ㄥ眬鐨勯厤缃枃浠�</li> + <li>鍏朵粬缁嗚妭浼樺寲</li> + </ol> + </el-collapse-item> + + <el-collapse-item title="v2.0.0 - 2019-12-02"> + <ol> + <li>鏂板浠g爜鐢熸垚</li> + <li>鏂板@RepeatSubmit娉ㄨВ锛岄槻姝㈤噸澶嶆彁浜�</li> + <li>鏂板鑿滃崟涓荤洰褰曟坊鍔�/鍒犻櫎鎿嶄綔</li> + <li>鏃ュ織璁板綍杩囨护鐗规畩瀵硅薄锛岄槻姝㈣浆鎹㈠紓甯�</li> + <li>淇敼浠g爜鐢熸垚璺敱鑴氭湰閿欒</li> + <li>鐢ㄦ埛涓婁紶澶村儚瀹炴椂鍚屾缂撳瓨锛屾棤闇�閲嶆柊鐧诲綍</li> + <li>璋冩暣鍒囨崲椤电鍚庝笉閲嶆柊鍔犺浇鏁版嵁</li> + <li>娣诲姞jsencrypt瀹炵幇鍙傛暟鐨勫墠绔姞瀵�</li> + <li>绯荤粺閫�鍑哄垹闄ょ敤鎴风紦瀛樿褰�</li> + <li>鍏朵粬缁嗚妭浼樺寲</li> + </ol> + </el-collapse-item> + <el-collapse-item title="v1.1.0 - 2019-11-11"> + <ol> + <li>鏂板鍦ㄧ嚎鐢ㄦ埛绠$悊</li> + <li>鏂板鎸夐挳缁勫姛鑳藉疄鐜帮紙鎵归噺鍒犻櫎銆佸鍑恒�佹竻绌猴級</li> + <li>鏂板鏌ヨ鏉′欢閲嶇疆鎸夐挳</li> + <li>鏂板Swagger鍏ㄥ眬Token閰嶇疆</li> + <li>鏂板鍚庣鍙傛暟鏍¢獙</li> + <li>淇瀛楀吀绠$悊椤甸潰鐨勬棩鏈熸煡璇㈠紓甯�</li> + <li>淇敼鏃堕棿鍑芥暟鍛藉悕闃叉鍐茬獊</li> + <li>鍘婚櫎鑿滃崟涓婄骇鏍¢獙锛岄粯璁や负椤剁骇</li> + <li>淇鐢ㄦ埛瀵嗙爜鏃犳硶淇敼闂</li> + <li>淇鑿滃崟绫诲瀷涓烘寜閽椂涓嶆樉绀烘潈闄愭爣璇�</li> + <li>鍏朵粬缁嗚妭浼樺寲</li> + </ol> + </el-collapse-item> + <el-collapse-item title="v1.0.0 - 2019-10-08"> + <ol> + <li>鑻ヤ緷鍓嶅悗绔垎绂荤郴缁熸寮忓彂甯�</li> + </ol> + </el-collapse-item> + </el-collapse> + </el-card> + </el-col> + <el-col :xs="24" :sm="24" :md="12" :lg="8"> + <el-card class="update-log"> + <template v-slot:header> + <div class="clearfix"> + <span>鎹愯禒鏀寔</span> + </div> + </template> + <div class="body"> + <img + src="@/assets/images/pay.png" + alt="donate" + style="width:100%" + /> + <span style="display: inline-block; height: 30px; line-height: 30px" + >浣犲彲浠ヨ浣滆�呭枬鏉挅鍟¤〃绀洪紦鍔�</span + > + </div> + </el-card> + </el-col> + </el-row> + </div> +</template> + +<script setup name="Index"> +const version = ref('3.8.9') + +function goTarget(url) { + window.open(url, '__blank') +} +</script> + +<style scoped lang="scss"> +.home { + blockquote { + padding: 10px 20px; + margin: 0 0 20px; + font-size: 17.5px; + border-left: 5px solid #eee; + } + hr { + margin-top: 20px; + margin-bottom: 20px; + border: 0; + border-top: 1px solid #eee; + } + .col-item { + margin-bottom: 20px; + } + + ul { + padding: 0; + margin: 0; + } + + font-family: "open sans", "Helvetica Neue", Helvetica, Arial, sans-serif; + font-size: 13px; + color: #676a6c; + overflow-x: hidden; + + ul { + list-style-type: none; + } + + h4 { + margin-top: 0px; + } + + h2 { + margin-top: 10px; + font-size: 26px; + font-weight: 100; + } + + p { + margin-top: 10px; + + b { + font-weight: 700; + } + } + + .update-log { + ol { + display: block; + list-style-type: decimal; + margin-block-start: 1em; + margin-block-end: 1em; + margin-inline-start: 0; + margin-inline-end: 0; + padding-inline-start: 40px; + } + } +} +</style> + diff --git a/src/views/login.vue b/src/views/login.vue new file mode 100644 index 0000000..8b64e6b --- /dev/null +++ b/src/views/login.vue @@ -0,0 +1,229 @@ +<template> + <div class="login"> + <el-form ref="loginRef" :model="loginForm" :rules="loginRules" class="login-form"> + <h3 class="title">{{ title }}</h3> + <el-form-item prop="username"> + <el-input + v-model="loginForm.username" + type="text" + size="large" + auto-complete="off" + placeholder="璐﹀彿" + > + <template #prefix><svg-icon icon-class="user" class="el-input__icon input-icon" /></template> + </el-input> + </el-form-item> + <el-form-item prop="password"> + <el-input + v-model="loginForm.password" + type="password" + size="large" + auto-complete="off" + placeholder="瀵嗙爜" + @keyup.enter="handleLogin" + > + <template #prefix><svg-icon icon-class="password" class="el-input__icon input-icon" /></template> + </el-input> + </el-form-item> + <el-form-item prop="code" v-if="captchaEnabled"> + <el-input + v-model="loginForm.code" + size="large" + auto-complete="off" + placeholder="楠岃瘉鐮�" + style="width: 63%" + @keyup.enter="handleLogin" + > + <template #prefix><svg-icon icon-class="validCode" class="el-input__icon input-icon" /></template> + </el-input> + <div class="login-code"> + <img :src="codeUrl" @click="getCode" class="login-code-img"/> + </div> + </el-form-item> + <el-checkbox v-model="loginForm.rememberMe" style="margin:0px 0px 25px 0px;">璁颁綇瀵嗙爜</el-checkbox> + <el-form-item style="width:100%;"> + <el-button + :loading="loading" + size="large" + type="primary" + style="width:100%;" + @click.prevent="handleLogin" + > + <span v-if="!loading">鐧� 褰�</span> + <span v-else>鐧� 褰� 涓�...</span> + </el-button> + <div style="float: right;" v-if="register"> + <router-link class="link-type" :to="'/register'">绔嬪嵆娉ㄥ唽</router-link> + </div> + </el-form-item> + </el-form> + <!-- 搴曢儴 --> + <div class="el-login-footer"> + <span>Copyright 漏 2018-2025 ruoyi.vip All Rights Reserved.</span> + </div> + </div> +</template> + +<script setup> +import { getCodeImg } from "@/api/login" +import Cookies from "js-cookie" +import { encrypt, decrypt } from "@/utils/jsencrypt" +import useUserStore from '@/store/modules/user' + +const title = import.meta.env.VITE_APP_TITLE +const userStore = useUserStore() +const route = useRoute() +const router = useRouter() +const { proxy } = getCurrentInstance() + +const loginForm = ref({ + username: "admin", + password: "admin123", + 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 captchaEnabled = ref(true) +// 娉ㄥ唽寮�鍏� +const register = ref(false) +const redirect = ref(undefined) + +watch(route, (newRoute) => { + redirect.value = newRoute.query && newRoute.query.redirect +}, { immediate: true }) + +function handleLogin() { + proxy.$refs.loginRef.validate(valid => { + if (valid) { + 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 }) + } else { + // 鍚﹀垯绉婚櫎 + Cookies.remove("username") + Cookies.remove("password") + Cookies.remove("rememberMe") + } + // 璋冪敤action鐨勭櫥褰曟柟娉� + userStore.login(loginForm.value).then(() => { + const query = route.query + const otherQueryParams = Object.keys(query).reduce((acc, cur) => { + if (cur !== "redirect") { + acc[cur] = query[cur] + } + return acc + }, {}) + router.push({ path: redirect.value || "/", query: otherQueryParams }) + }).catch(() => { + loading.value = false + // 閲嶆柊鑾峰彇楠岃瘉鐮� + if (captchaEnabled.value) { + getCode() + } + }) + } + }) +} + +function getCode() { + getCodeImg().then(res => { + captchaEnabled.value = res.captchaEnabled === undefined ? true : res.captchaEnabled + if (captchaEnabled.value) { + 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") + 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() +</script> + +<style lang='scss' scoped> +.login { + display: flex; + justify-content: center; + align-items: center; + height: 100%; + background-image: url("../assets/images/login-background.jpg"); + background-size: cover; +} +.title { + margin: 0px auto 30px auto; + text-align: center; + color: #707070; +} + +.login-form { + border-radius: 6px; + background: #ffffff; + width: 400px; + padding: 25px 25px 5px 25px; + z-index: 1; + .el-input { + height: 40px; + input { + height: 40px; + } + } + .input-icon { + height: 39px; + width: 14px; + margin-left: 0px; + } +} +.login-tip { + font-size: 13px; + text-align: center; + color: #bfbfbf; +} +.login-code { + width: 33%; + height: 40px; + float: right; + img { + cursor: pointer; + vertical-align: middle; + } +} +.el-login-footer { + height: 40px; + line-height: 40px; + position: fixed; + bottom: 0; + width: 100%; + text-align: center; + color: #fff; + font-family: Arial; + font-size: 12px; + letter-spacing: 1px; +} +.login-code-img { + height: 40px; + padding-left: 12px; +} +</style> diff --git a/src/views/monitor/cache/index.vue b/src/views/monitor/cache/index.vue new file mode 100644 index 0000000..271cc8e --- /dev/null +++ b/src/views/monitor/cache/index.vue @@ -0,0 +1,132 @@ +<template> + <div class="app-container"> + <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> + <div class="el-table el-table--enable-row-hover el-table--medium"> + <table cellspacing="0" style="width: 100%"> + <tbody> + <tr> + <td class="el-table__cell is-leaf"><div class="cell">Redis鐗堟湰</div></td> + <td class="el-table__cell is-leaf"><div class="cell" v-if="cache.info">{{ cache.info.redis_version }}</div></td> + <td class="el-table__cell is-leaf"><div class="cell">杩愯妯″紡</div></td> + <td class="el-table__cell is-leaf"><div class="cell" v-if="cache.info">{{ cache.info.redis_mode == "standalone" ? "鍗曟満" : "闆嗙兢" }}</div></td> + <td class="el-table__cell is-leaf"><div class="cell">绔彛</div></td> + <td class="el-table__cell is-leaf"><div class="cell" v-if="cache.info">{{ cache.info.tcp_port }}</div></td> + <td class="el-table__cell is-leaf"><div class="cell">瀹㈡埛绔暟</div></td> + <td class="el-table__cell is-leaf"><div class="cell" v-if="cache.info">{{ cache.info.connected_clients }}</div></td> + </tr> + <tr> + <td class="el-table__cell is-leaf"><div class="cell">杩愯鏃堕棿(澶�)</div></td> + <td class="el-table__cell is-leaf"><div class="cell" v-if="cache.info">{{ cache.info.uptime_in_days }}</div></td> + <td class="el-table__cell is-leaf"><div class="cell">浣跨敤鍐呭瓨</div></td> + <td class="el-table__cell is-leaf"><div class="cell" v-if="cache.info">{{ cache.info.used_memory_human }}</div></td> + <td class="el-table__cell is-leaf"><div class="cell">浣跨敤CPU</div></td> + <td class="el-table__cell is-leaf"><div class="cell" v-if="cache.info">{{ parseFloat(cache.info.used_cpu_user_children).toFixed(2) }}</div></td> + <td class="el-table__cell is-leaf"><div class="cell">鍐呭瓨閰嶇疆</div></td> + <td class="el-table__cell is-leaf"><div class="cell" v-if="cache.info">{{ cache.info.maxmemory_human }}</div></td> + </tr> + <tr> + <td class="el-table__cell is-leaf"><div class="cell">AOF鏄惁寮�鍚�</div></td> + <td class="el-table__cell is-leaf"><div class="cell" v-if="cache.info">{{ cache.info.aof_enabled == "0" ? "鍚�" : "鏄�" }}</div></td> + <td class="el-table__cell is-leaf"><div class="cell">RDB鏄惁鎴愬姛</div></td> + <td class="el-table__cell is-leaf"><div class="cell" v-if="cache.info">{{ cache.info.rdb_last_bgsave_status }}</div></td> + <td class="el-table__cell is-leaf"><div class="cell">Key鏁伴噺</div></td> + <td class="el-table__cell is-leaf"><div class="cell" v-if="cache.dbSize">{{ cache.dbSize }} </div></td> + <td class="el-table__cell is-leaf"><div class="cell">缃戠粶鍏ュ彛/鍑哄彛</div></td> + <td class="el-table__cell is-leaf"><div class="cell" v-if="cache.info">{{ cache.info.instantaneous_input_kbps }}kps/{{cache.info.instantaneous_output_kbps}}kps</div></td> + </tr> + </tbody> + </table> + </div> + </el-card> + </el-col> + + <el-col :span="12" class="card-box"> + <el-card> + <template #header><PieChart style="width: 1em; height: 1em; vertical-align: middle;" /> <span style="vertical-align: middle;">鍛戒护缁熻</span></template> + <div class="el-table el-table--enable-row-hover el-table--medium"> + <div ref="commandstats" style="height: 420px" /> + </div> + </el-card> + </el-col> + + <el-col :span="12" class="card-box"> + <el-card> + <template #header><Odometer style="width: 1em; height: 1em; vertical-align: middle;" /> <span style="vertical-align: middle;">鍐呭瓨淇℃伅</span></template> + <div class="el-table el-table--enable-row-hover el-table--medium"> + <div ref="usedmemory" style="height: 420px" /> + </div> + </el-card> + </el-col> + </el-row> + </div> +</template> + +<script setup name="Cache"> +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() + +function getList() { + proxy.$modal.loading("姝e湪鍔犺浇缂撳瓨鐩戞帶鏁版嵁锛岃绋嶅�欙紒") + getCache().then(response => { + proxy.$modal.closeLoading() + cache.value = response.data + + const commandstatsIntance = echarts.init(commandstats.value, "macarons") + commandstatsIntance.setOption({ + tooltip: { + trigger: "item", + formatter: "{a} <br/>{b} : {c} ({d}%)" + }, + series: [ + { + name: "鍛戒护", + type: "pie", + roseType: "radius", + radius: [15, 95], + center: ["50%", "38%"], + data: response.data.commandStats, + animationEasing: "cubicInOut", + animationDuration: 1000 + } + ] + }) + const usedmemoryInstance = echarts.init(usedmemory.value, "macarons") + usedmemoryInstance.setOption({ + tooltip: { + formatter: "{b} <br/>{a} : " + cache.value.info.used_memory_human + }, + series: [ + { + name: "宄板��", + type: "gauge", + min: 0, + max: 1000, + detail: { + formatter: cache.value.info.used_memory_human + }, + data: [ + { + value: parseFloat(cache.value.info.used_memory_human), + name: "鍐呭瓨娑堣��" + } + ] + } + ] + }) + window.addEventListener("resize", () => { + commandstatsIntance.resize() + usedmemoryInstance.resize() + }) + }) +} + +getList() +</script> diff --git a/src/views/monitor/cache/list.vue b/src/views/monitor/cache/list.vue new file mode 100644 index 0000000..6515fcd --- /dev/null +++ b/src/views/monitor/cache/list.vue @@ -0,0 +1,246 @@ +<template> + <div class="app-container"> + <el-row :gutter="10"> + <el-col :span="8"> + <el-card style="height: calc(100vh - 125px)"> + <template #header> + <Collection style="width: 1em; height: 1em; vertical-align: middle;" /> <span style="vertical-align: middle;">缂撳瓨鍒楄〃</span> + <el-button + style="float: right; padding: 3px 0" + link + type="primary" + icon="Refresh" + @click="refreshCacheNames()" + ></el-button> + </template> + <el-table + v-loading="loading" + :data="cacheNames" + :height="tableHeight" + highlight-current-row + @row-click="getCacheKeys" + style="width: 100%" + > + <el-table-column + label="搴忓彿" + width="60" + type="index" + ></el-table-column> + + <el-table-column + label="缂撳瓨鍚嶇О" + align="center" + prop="cacheName" + :show-overflow-tooltip="true" + :formatter="nameFormatter" + ></el-table-column> + + <el-table-column + label="澶囨敞" + align="center" + prop="remark" + :show-overflow-tooltip="true" + /> + <el-table-column + label="鎿嶄綔" + width="60" + align="center" + class-name="small-padding fixed-width" + > + <template #default="scope"> + <el-button + link + type="primary" + icon="Delete" + @click="handleClearCacheName(scope.row)" + ></el-button> + </template> + </el-table-column> + </el-table> + </el-card> + </el-col> + + <el-col :span="8"> + <el-card style="height: calc(100vh - 125px)"> + <template #header> + <Key style="width: 1em; height: 1em; vertical-align: middle;" /> <span style="vertical-align: middle;">閿悕鍒楄〃</span> + <el-button + style="float: right; padding: 3px 0" + link + type="primary" + icon="Refresh" + @click="refreshCacheKeys()" + ></el-button> + </template> + <el-table + v-loading="subLoading" + :data="cacheKeys" + :height="tableHeight" + highlight-current-row + @row-click="handleCacheValue" + style="width: 100%" + > + <el-table-column + label="搴忓彿" + width="60" + type="index" + ></el-table-column> + <el-table-column + label="缂撳瓨閿悕" + align="center" + :show-overflow-tooltip="true" + :formatter="keyFormatter" + > + </el-table-column> + <el-table-column + label="鎿嶄綔" + width="60" + align="center" + class-name="small-padding fixed-width" + > + <template #default="scope"> + <el-button + link + type="primary" + icon="Delete" + @click="handleClearCacheKey(scope.row)" + ></el-button> + </template> + </el-table-column> + </el-table> + </el-card> + </el-col> + + <el-col :span="8"> + <el-card :bordered="false" style="height: calc(100vh - 125px)"> + <template #header> + <Document style="width: 1em; height: 1em; vertical-align: middle;" /> <span style="vertical-align: middle;">缂撳瓨鍐呭</span> + <el-button + style="float: right; padding: 3px 0" + link + type="primary" + icon="Refresh" + @click="handleClearCacheAll()" + >娓呯悊鍏ㄩ儴</el-button + > + </template> + <el-form :model="cacheForm"> + <el-row :gutter="32"> + <el-col :offset="1" :span="22"> + <el-form-item label="缂撳瓨鍚嶇О:" prop="cacheName"> + <el-input v-model="cacheForm.cacheName" :readOnly="true" /> + </el-form-item> + </el-col> + <el-col :offset="1" :span="22"> + <el-form-item label="缂撳瓨閿悕:" prop="cacheKey"> + <el-input v-model="cacheForm.cacheKey" :readOnly="true" /> + </el-form-item> + </el-col> + <el-col :offset="1" :span="22"> + <el-form-item label="缂撳瓨鍐呭:" prop="cacheValue"> + <el-input + v-model="cacheForm.cacheValue" + type="textarea" + :rows="8" + :readOnly="true" + /> + </el-form-item> + </el-col> + </el-row> + </el-form> + </el-card> + </el-col> + </el-row> + </div> +</template> + +<script setup name="CacheList"> +import { listCacheName, listCacheKey, getCacheValue, clearCacheName, clearCacheKey, clearCacheAll } from "@/api/monitor/cache" + +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) + +/** 鏌ヨ缂撳瓨鍚嶇О鍒楄〃 */ +function getCacheNames() { + loading.value = true + listCacheName().then(response => { + cacheNames.value = response.data + loading.value = false + }) +} + +/** 鍒锋柊缂撳瓨鍚嶇О鍒楄〃 */ +function refreshCacheNames() { + getCacheNames() + proxy.$modal.msgSuccess("鍒锋柊缂撳瓨鍒楄〃鎴愬姛") +} + +/** 娓呯悊鎸囧畾鍚嶇О缂撳瓨 */ +function handleClearCacheName(row) { + clearCacheName(row.cacheName).then(response => { + proxy.$modal.msgSuccess("娓呯悊缂撳瓨鍚嶇О[" + row.cacheName + "]鎴愬姛") + getCacheKeys() + }) +} + +/** 鏌ヨ缂撳瓨閿悕鍒楄〃 */ +function getCacheKeys(row) { + const cacheName = row !== undefined ? row.cacheName : nowCacheName.value + if (cacheName === "") { + return + } + subLoading.value = true + listCacheKey(cacheName).then(response => { + cacheKeys.value = response.data + subLoading.value = false + nowCacheName.value = cacheName + }) +} + +/** 鍒锋柊缂撳瓨閿悕鍒楄〃 */ +function refreshCacheKeys() { + getCacheKeys() + proxy.$modal.msgSuccess("鍒锋柊閿悕鍒楄〃鎴愬姛") +} + +/** 娓呯悊鎸囧畾閿悕缂撳瓨 */ +function handleClearCacheKey(cacheKey) { + clearCacheKey(cacheKey).then(response => { + proxy.$modal.msgSuccess("娓呯悊缂撳瓨閿悕[" + cacheKey + "]鎴愬姛") + getCacheKeys() + }) +} + +/** 鍒楄〃鍓嶇紑鍘婚櫎 */ +function nameFormatter(row) { + return row.cacheName.replace(":", "") +} + +/** 閿悕鍓嶇紑鍘婚櫎 */ +function keyFormatter(cacheKey) { + return cacheKey.replace(nowCacheName.value, "") +} + +/** 鏌ヨ缂撳瓨鍐呭璇︾粏 */ +function handleCacheValue(cacheKey) { + getCacheValue(nowCacheName.value, cacheKey).then(response => { + cacheForm.value = response.data + }) +} + +/** 娓呯悊鍏ㄩ儴缂撳瓨 */ +function handleClearCacheAll() { + clearCacheAll().then(response => { + proxy.$modal.msgSuccess("娓呯悊鍏ㄩ儴缂撳瓨鎴愬姛") + }) +} + +getCacheNames() +</script> diff --git a/src/views/monitor/druid/index.vue b/src/views/monitor/druid/index.vue new file mode 100644 index 0000000..29c1aa7 --- /dev/null +++ b/src/views/monitor/druid/index.vue @@ -0,0 +1,13 @@ +<template> + <div> + <i-frame v-model:src="url"></i-frame> + </div> +</template> + +<script setup> +import iFrame from '@/components/iFrame' + +import { ref } from 'vue' + +const url = ref(import.meta.env.VITE_APP_BASE_API + '/druid/login.html') +</script> diff --git a/src/views/monitor/job/index.vue b/src/views/monitor/job/index.vue new file mode 100644 index 0000000..3af06c1 --- /dev/null +++ b/src/views/monitor/job/index.vue @@ -0,0 +1,502 @@ +<template> + <div class="app-container"> + <el-form :model="queryParams" ref="queryRef" :inline="true" v-show="showSearch"> + <el-form-item label="浠诲姟鍚嶇О" prop="jobName"> + <el-input + v-model="queryParams.jobName" + placeholder="璇疯緭鍏ヤ换鍔″悕绉�" + clearable + style="width: 200px" + @keyup.enter="handleQuery" + /> + </el-form-item> + <el-form-item label="浠诲姟缁勫悕" prop="jobGroup"> + <el-select v-model="queryParams.jobGroup" placeholder="璇烽�夋嫨浠诲姟缁勫悕" clearable style="width: 200px"> + <el-option + v-for="dict in sys_job_group" + :key="dict.value" + :label="dict.label" + :value="dict.value" + /> + </el-select> + </el-form-item> + <el-form-item label="浠诲姟鐘舵��" prop="status"> + <el-select v-model="queryParams.status" placeholder="璇烽�夋嫨浠诲姟鐘舵��" clearable style="width: 200px"> + <el-option + v-for="dict in sys_job_status" + :key="dict.value" + :label="dict.label" + :value="dict.value" + /> + </el-select> + </el-form-item> + <el-form-item> + <el-button type="primary" icon="Search" @click="handleQuery">鎼滅储</el-button> + <el-button icon="Refresh" @click="resetQuery">閲嶇疆</el-button> + </el-form-item> + </el-form> + + <el-row :gutter="10" class="mb8"> + <el-col :span="1.5"> + <el-button + type="primary" + plain + icon="Plus" + @click="handleAdd" + v-hasPermi="['monitor:job:add']" + >鏂板</el-button> + </el-col> + <el-col :span="1.5"> + <el-button + type="success" + plain + icon="Edit" + :disabled="single" + @click="handleUpdate" + v-hasPermi="['monitor:job:edit']" + >淇敼</el-button> + </el-col> + <el-col :span="1.5"> + <el-button + type="danger" + plain + icon="Delete" + :disabled="multiple" + @click="handleDelete" + v-hasPermi="['monitor:job:remove']" + >鍒犻櫎</el-button> + </el-col> + <el-col :span="1.5"> + <el-button + type="warning" + plain + icon="Download" + @click="handleExport" + v-hasPermi="['monitor:job:export']" + >瀵煎嚭</el-button> + </el-col> + <el-col :span="1.5"> + <el-button + type="info" + plain + icon="Operation" + @click="handleJobLog" + v-hasPermi="['monitor:job:query']" + >鏃ュ織</el-button> + </el-col> + <right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar> + </el-row> + + <el-table v-loading="loading" :data="jobList" @selection-change="handleSelectionChange"> + <el-table-column type="selection" width="55" align="center" /> + <el-table-column label="浠诲姟缂栧彿" width="100" align="center" prop="jobId" /> + <el-table-column label="浠诲姟鍚嶇О" align="center" prop="jobName" :show-overflow-tooltip="true" /> + <el-table-column label="浠诲姟缁勫悕" align="center" prop="jobGroup"> + <template #default="scope"> + <dict-tag :options="sys_job_group" :value="scope.row.jobGroup" /> + </template> + </el-table-column> + <el-table-column label="璋冪敤鐩爣瀛楃涓�" align="center" prop="invokeTarget" :show-overflow-tooltip="true" /> + <el-table-column label="cron鎵ц琛ㄨ揪寮�" align="center" prop="cronExpression" :show-overflow-tooltip="true" /> + <el-table-column label="鐘舵��" align="center"> + <template #default="scope"> + <el-switch + v-model="scope.row.status" + active-value="0" + inactive-value="1" + @change="handleStatusChange(scope.row)" + ></el-switch> + </template> + </el-table-column> + <el-table-column label="鎿嶄綔" align="center" width="200" class-name="small-padding fixed-width"> + <template #default="scope"> + <el-tooltip content="淇敼" placement="top"> + <el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['monitor:job:edit']"></el-button> + </el-tooltip> + <el-tooltip content="鍒犻櫎" placement="top"> + <el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['monitor:job:remove']"></el-button> + </el-tooltip> + <el-tooltip content="鎵ц涓�娆�" placement="top"> + <el-button link type="primary" icon="CaretRight" @click="handleRun(scope.row)" v-hasPermi="['monitor:job:changeStatus']"></el-button> + </el-tooltip> + <el-tooltip content="浠诲姟璇︾粏" placement="top"> + <el-button link type="primary" icon="View" @click="handleView(scope.row)" v-hasPermi="['monitor:job:query']"></el-button> + </el-tooltip> + <el-tooltip content="璋冨害鏃ュ織" placement="top"> + <el-button link type="primary" icon="Operation" @click="handleJobLog(scope.row)" v-hasPermi="['monitor:job:query']"></el-button> + </el-tooltip> + </template> + </el-table-column> + </el-table> + + <pagination + v-show="total > 0" + :total="total" + v-model:page="queryParams.pageNum" + v-model:limit="queryParams.pageSize" + @pagination="getList" + /> + + <!-- 娣诲姞鎴栦慨鏀瑰畾鏃朵换鍔″璇濇 --> + <el-dialog :title="title" v-model="open" width="820px" append-to-body> + <el-form ref="jobRef" :model="form" :rules="rules" label-width="120px"> + <el-row> + <el-col :span="12"> + <el-form-item label="浠诲姟鍚嶇О" prop="jobName"> + <el-input v-model="form.jobName" placeholder="璇疯緭鍏ヤ换鍔″悕绉�" /> + </el-form-item> + </el-col> + <el-col :span="12"> + <el-form-item label="浠诲姟鍒嗙粍" prop="jobGroup"> + <el-select v-model="form.jobGroup" placeholder="璇烽�夋嫨"> + <el-option + v-for="dict in sys_job_group" + :key="dict.value" + :label="dict.label" + :value="dict.value" + ></el-option> + </el-select> + </el-form-item> + </el-col> + <el-col :span="24"> + <el-form-item prop="invokeTarget"> + <template #label> + <span> + 璋冪敤鏂规硶 + <el-tooltip placement="top"> + <template #content> + <div> + Bean璋冪敤绀轰緥锛歳yTask.ryParams('ry') + <br />Class绫昏皟鐢ㄧず渚嬶細com.ruoyi.quartz.task.RyTask.ryParams('ry') + <br />鍙傛暟璇存槑锛氭敮鎸佸瓧绗︿覆锛屽竷灏旂被鍨嬶紝闀挎暣鍨嬶紝娴偣鍨嬶紝鏁村瀷 + </div> + </template> + <el-icon><question-filled /></el-icon> + </el-tooltip> + </span> + </template> + <el-input v-model="form.invokeTarget" placeholder="璇疯緭鍏ヨ皟鐢ㄧ洰鏍囧瓧绗︿覆" /> + </el-form-item> + </el-col> + <el-col :span="24"> + <el-form-item label="cron琛ㄨ揪寮�" prop="cronExpression"> + <el-input v-model="form.cronExpression" placeholder="璇疯緭鍏ron鎵ц琛ㄨ揪寮�"> + <template #append> + <el-button type="primary" @click="handleShowCron"> + 鐢熸垚琛ㄨ揪寮� + <i class="el-icon-time el-icon--right"></i> + </el-button> + </template> + </el-input> + </el-form-item> + </el-col> + <el-col :span="24" v-if="form.jobId !== undefined"> + <el-form-item label="鐘舵��"> + <el-radio-group v-model="form.status"> + <el-radio + v-for="dict in sys_job_status" + :key="dict.value" + :value="dict.value" + >{{ dict.label }}</el-radio> + </el-radio-group> + </el-form-item> + </el-col> + <el-col :span="12"> + <el-form-item label="鎵ц绛栫暐" prop="misfirePolicy"> + <el-radio-group v-model="form.misfirePolicy"> + <el-radio-button 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 value="0">鍏佽</el-radio-button> + <el-radio-button value="1">绂佹</el-radio-button> + </el-radio-group> + </el-form-item> + </el-col> + </el-row> + </el-form> + <template #footer> + <div class="dialog-footer"> + <el-button type="primary" @click="submitForm">纭� 瀹�</el-button> + <el-button @click="cancel">鍙� 娑�</el-button> + </div> + </template> + </el-dialog> + + <el-dialog title="Cron琛ㄨ揪寮忕敓鎴愬櫒" v-model="openCron" append-to-body destroy-on-close> + <crontab ref="crontabRef" @hide="openCron=false" @fill="crontabFill" :expression="expression"></crontab> + </el-dialog> + + <!-- 浠诲姟鏃ュ織璇︾粏 --> + <el-dialog title="浠诲姟璇︾粏" v-model="openView" width="700px" append-to-body> + <el-form :model="form" label-width="120px"> + <el-row> + <el-col :span="12"> + <el-form-item label="浠诲姟缂栧彿锛�">{{ form.jobId }}</el-form-item> + <el-form-item label="浠诲姟鍚嶇О锛�">{{ form.jobName }}</el-form-item> + </el-col> + <el-col :span="12"> + <el-form-item label="浠诲姟鍒嗙粍锛�">{{ jobGroupFormat(form) }}</el-form-item> + <el-form-item label="鍒涘缓鏃堕棿锛�">{{ form.createTime }}</el-form-item> + </el-col> + <el-col :span="12"> + <el-form-item label="cron琛ㄨ揪寮忥細">{{ form.cronExpression }}</el-form-item> + </el-col> + <el-col :span="12"> + <el-form-item label="涓嬫鎵ц鏃堕棿锛�">{{ parseTime(form.nextValidTime) }}</el-form-item> + </el-col> + <el-col :span="24"> + <el-form-item label="璋冪敤鐩爣鏂规硶锛�">{{ form.invokeTarget }}</el-form-item> + </el-col> + <el-col :span="12"> + <el-form-item label="浠诲姟鐘舵�侊細"> + <div v-if="form.status == 0">姝e父</div> + <div v-else-if="form.status == 1">鏆傚仠</div> + </el-form-item> + </el-col> + <el-col :span="12"> + <el-form-item label="鏄惁骞跺彂锛�"> + <div v-if="form.concurrent == 0">鍏佽</div> + <div v-else-if="form.concurrent == 1">绂佹</div> + </el-form-item> + </el-col> + <el-col :span="12"> + <el-form-item label="鎵ц绛栫暐锛�"> + <div v-if="form.misfirePolicy == 0">榛樿绛栫暐</div> + <div v-else-if="form.misfirePolicy == 1">绔嬪嵆鎵ц</div> + <div v-else-if="form.misfirePolicy == 2">鎵ц涓�娆�</div> + <div v-else-if="form.misfirePolicy == 3">鏀惧純鎵ц</div> + </el-form-item> + </el-col> + </el-row> + </el-form> + <template #footer> + <div class="dialog-footer"> + <el-button @click="openView = false">鍏� 闂�</el-button> + </div> + </template> + </el-dialog> + </div> +</template> + +<script setup name="Job"> +import Crontab from '@/components/Crontab' +import { listJob, getJob, delJob, addJob, updateJob, runJob, changeJobStatus } from "@/api/monitor/job" + +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: {}, + queryParams: { + pageNum: 1, + pageSize: 10, + jobName: undefined, + jobGroup: undefined, + status: undefined + }, + rules: { + jobName: [{ required: true, message: "浠诲姟鍚嶇О涓嶈兘涓虹┖", trigger: "blur" }], + invokeTarget: [{ required: true, message: "璋冪敤鐩爣瀛楃涓蹭笉鑳戒负绌�", trigger: "blur" }], + cronExpression: [{ required: true, message: "cron鎵ц琛ㄨ揪寮忎笉鑳戒负绌�", trigger: "change" }] + } +}) + +const { queryParams, form, rules } = toRefs(data) + +/** 鏌ヨ瀹氭椂浠诲姟鍒楄〃 */ +function getList() { + loading.value = true + listJob(queryParams.value).then(response => { + jobList.value = response.rows + total.value = response.total + loading.value = false + }) +} + +/** 浠诲姟缁勫悕瀛楀吀缈昏瘧 */ +function jobGroupFormat(row, column) { + return proxy.selectDictLabel(sys_job_group.value, row.jobGroup) +} + +/** 鍙栨秷鎸夐挳 */ +function cancel() { + open.value = false + reset() +} + +/** 琛ㄥ崟閲嶇疆 */ +function reset() { + form.value = { + jobId: undefined, + jobName: undefined, + jobGroup: undefined, + invokeTarget: undefined, + cronExpression: undefined, + misfirePolicy: 1, + concurrent: 1, + status: "0" + } + proxy.resetForm("jobRef") +} + +/** 鎼滅储鎸夐挳鎿嶄綔 */ +function handleQuery() { + queryParams.value.pageNum = 1 + getList() +} + +/** 閲嶇疆鎸夐挳鎿嶄綔 */ +function resetQuery() { + proxy.resetForm("queryRef") + handleQuery() +} + +// 澶氶�夋閫変腑鏁版嵁 +function handleSelectionChange(selection) { + 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 + case "handleView": + handleView(row) + break + case "handleJobLog": + handleJobLog(row) + break + default: + break + } +} + +// 浠诲姟鐘舵�佷慨鏀� +function handleStatusChange(row) { + let text = row.status === "0" ? "鍚敤" : "鍋滅敤" + proxy.$modal.confirm('纭瑕�"' + text + '""' + row.jobName + '"浠诲姟鍚�?').then(function () { + return changeJobStatus(row.jobId, row.status) + }).then(() => { + proxy.$modal.msgSuccess(text + "鎴愬姛") + }).catch(function () { + row.status = row.status === "0" ? "1" : "0" + }) +} + +/* 绔嬪嵆鎵ц涓�娆� */ +function handleRun(row) { + proxy.$modal.confirm('纭瑕佺珛鍗虫墽琛屼竴娆�"' + row.jobName + '"浠诲姟鍚�?').then(function () { + return runJob(row.jobId, row.jobGroup) + }).then(() => { + proxy.$modal.msgSuccess("鎵ц鎴愬姛")}) + .catch(() => {}) +} + +/** 浠诲姟璇︾粏淇℃伅 */ +function handleView(row) { + getJob(row.jobId).then(response => { + form.value = response.data + openView.value = true + }) +} + +/** cron琛ㄨ揪寮忔寜閽搷浣� */ +function handleShowCron() { + expression.value = form.value.cronExpression + openCron.value = true +} + +/** 纭畾鍚庡洖浼犲�� */ +function crontabFill(value) { + form.value.cronExpression = value +} + +/** 浠诲姟鏃ュ織鍒楄〃鏌ヨ */ +function handleJobLog(row) { + const jobId = row.jobId || 0 + router.push('/monitor/job-log/index/' + jobId) +} + +/** 鏂板鎸夐挳鎿嶄綔 */ +function handleAdd() { + reset() + open.value = true + title.value = "娣诲姞浠诲姟" +} + +/** 淇敼鎸夐挳鎿嶄綔 */ +function handleUpdate(row) { + reset() + const jobId = row.jobId || ids.value + getJob(jobId).then(response => { + 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() + }) + } else { + addJob(form.value).then(response => { + proxy.$modal.msgSuccess("鏂板鎴愬姛") + open.value = false + getList() + }) + } + } + }) +} + +/** 鍒犻櫎鎸夐挳鎿嶄綔 */ +function handleDelete(row) { + const jobIds = row.jobId || ids.value + proxy.$modal.confirm('鏄惁纭鍒犻櫎瀹氭椂浠诲姟缂栧彿涓�"' + jobIds + '"鐨勬暟鎹」?').then(function () { + return delJob(jobIds) + }).then(() => { + getList() + proxy.$modal.msgSuccess("鍒犻櫎鎴愬姛") + }).catch(() => {}) +} + +/** 瀵煎嚭鎸夐挳鎿嶄綔 */ +function handleExport() { + proxy.download("monitor/job/export", { + ...queryParams.value, + }, `job_${new Date().getTime()}.xlsx`) +} + +getList() +</script> diff --git a/src/views/monitor/job/log.vue b/src/views/monitor/job/log.vue new file mode 100644 index 0000000..ef581fd --- /dev/null +++ b/src/views/monitor/job/log.vue @@ -0,0 +1,283 @@ +<template> + <div class="app-container"> + <el-form :model="queryParams" ref="queryRef" :inline="true" v-show="showSearch" label-width="68px"> + <el-form-item label="浠诲姟鍚嶇О" prop="jobName"> + <el-input + v-model="queryParams.jobName" + placeholder="璇疯緭鍏ヤ换鍔″悕绉�" + clearable + style="width: 240px" + @keyup.enter="handleQuery" + /> + </el-form-item> + <el-form-item label="浠诲姟缁勫悕" prop="jobGroup"> + <el-select + v-model="queryParams.jobGroup" + placeholder="璇烽�夋嫨浠诲姟缁勫悕" + clearable + style="width: 240px" + > + <el-option + v-for="dict in sys_job_group" + :key="dict.value" + :label="dict.label" + :value="dict.value" + /> + </el-select> + </el-form-item> + <el-form-item label="鎵ц鐘舵��" prop="status"> + <el-select + v-model="queryParams.status" + placeholder="璇烽�夋嫨鎵ц鐘舵��" + clearable + style="width: 240px" + > + <el-option + v-for="dict in sys_common_status" + :key="dict.value" + :label="dict.label" + :value="dict.value" + /> + </el-select> + </el-form-item> + <el-form-item label="鎵ц鏃堕棿" style="width: 308px"> + <el-date-picker + v-model="dateRange" + value-format="YYYY-MM-DD" + type="daterange" + range-separator="-" + start-placeholder="寮�濮嬫棩鏈�" + end-placeholder="缁撴潫鏃ユ湡" + ></el-date-picker> + </el-form-item> + <el-form-item> + <el-button type="primary" icon="Search" @click="handleQuery">鎼滅储</el-button> + <el-button icon="Refresh" @click="resetQuery">閲嶇疆</el-button> + </el-form-item> + </el-form> + + <el-row :gutter="10" class="mb8"> + <el-col :span="1.5"> + <el-button + type="danger" + plain + icon="Delete" + :disabled="multiple" + @click="handleDelete" + v-hasPermi="['monitor:job:remove']" + >鍒犻櫎</el-button> + </el-col> + <el-col :span="1.5"> + <el-button + type="danger" + plain + icon="Delete" + @click="handleClean" + v-hasPermi="['monitor:job:remove']" + >娓呯┖</el-button> + </el-col> + <el-col :span="1.5"> + <el-button + type="warning" + plain + icon="Download" + @click="handleExport" + v-hasPermi="['monitor:job:export']" + >瀵煎嚭</el-button> + </el-col> + <el-col :span="1.5"> + <el-button + type="warning" + plain + icon="Close" + @click="handleClose" + >鍏抽棴</el-button> + </el-col> + <right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar> + </el-row> + + <el-table v-loading="loading" :data="jobLogList" @selection-change="handleSelectionChange"> + <el-table-column type="selection" width="55" align="center" /> + <el-table-column label="鏃ュ織缂栧彿" width="80" align="center" prop="jobLogId" /> + <el-table-column label="浠诲姟鍚嶇О" align="center" prop="jobName" :show-overflow-tooltip="true" /> + <el-table-column label="浠诲姟缁勫悕" align="center" prop="jobGroup" :show-overflow-tooltip="true"> + <template #default="scope"> + <dict-tag :options="sys_job_group" :value="scope.row.jobGroup" /> + </template> + </el-table-column> + <el-table-column label="璋冪敤鐩爣瀛楃涓�" align="center" prop="invokeTarget" :show-overflow-tooltip="true" /> + <el-table-column label="鏃ュ織淇℃伅" align="center" prop="jobMessage" :show-overflow-tooltip="true" /> + <el-table-column label="鎵ц鐘舵��" align="center" prop="status"> + <template #default="scope"> + <dict-tag :options="sys_common_status" :value="scope.row.status" /> + </template> + </el-table-column> + <el-table-column label="鎵ц鏃堕棿" align="center" prop="createTime" width="180"> + <template #default="scope"> + <span>{{ parseTime(scope.row.createTime) }}</span> + </template> + </el-table-column> + <el-table-column label="鎿嶄綔" align="center" class-name="small-padding fixed-width"> + <template #default="scope"> + <el-button link type="primary" icon="View" @click="handleView(scope.row)" v-hasPermi="['monitor:job:query']">璇︾粏</el-button> + </template> + </el-table-column> + </el-table> + + <pagination + v-show="total > 0" + :total="total" + v-model:page="queryParams.pageNum" + v-model:limit="queryParams.pageSize" + @pagination="getList" + /> + + <!-- 璋冨害鏃ュ織璇︾粏 --> + <el-dialog title="璋冨害鏃ュ織璇︾粏" v-model="open" width="700px" append-to-body> + <el-form :model="form" label-width="100px"> + <el-row> + <el-col :span="12"> + <el-form-item label="鏃ュ織搴忓彿锛�">{{ form.jobLogId }}</el-form-item> + <el-form-item label="浠诲姟鍚嶇О锛�">{{ form.jobName }}</el-form-item> + </el-col> + <el-col :span="12"> + <el-form-item label="浠诲姟鍒嗙粍锛�">{{ form.jobGroup }}</el-form-item> + <el-form-item label="鎵ц鏃堕棿锛�">{{ form.createTime }}</el-form-item> + </el-col> + <el-col :span="24"> + <el-form-item label="璋冪敤鏂规硶锛�">{{ form.invokeTarget }}</el-form-item> + </el-col> + <el-col :span="24"> + <el-form-item label="鏃ュ織淇℃伅锛�">{{ form.jobMessage }}</el-form-item> + </el-col> + <el-col :span="24"> + <el-form-item label="鎵ц鐘舵�侊細"> + <div v-if="form.status == 0">姝e父</div> + <div v-else-if="form.status == 1">澶辫触</div> + </el-form-item> + </el-col> + <el-col :span="24"> + <el-form-item label="寮傚父淇℃伅锛�" v-if="form.status == 1">{{ form.exceptionInfo }}</el-form-item> + </el-col> + </el-row> + </el-form> + <template #footer> + <div class="dialog-footer"> + <el-button @click="open = false">鍏� 闂�</el-button> + </div> + </template> + </el-dialog> + </div> +</template> + +<script setup name="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 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: {}, + queryParams: { + pageNum: 1, + pageSize: 10, + dictName: undefined, + dictType: undefined, + status: undefined + } +}) + +const { queryParams, form, rules } = toRefs(data) + +/** 鏌ヨ璋冨害鏃ュ織鍒楄〃 */ +function getList() { + loading.value = true + listJobLog(proxy.addDateRange(queryParams.value, dateRange.value)).then(response => { + jobLogList.value = response.rows + total.value = response.total + loading.value = false + }) +} + +// 杩斿洖鎸夐挳 +function handleClose() { + const obj = { path: "/monitor/job" } + proxy.$tab.closeOpenPage(obj) +} + +/** 鎼滅储鎸夐挳鎿嶄綔 */ +function handleQuery() { + queryParams.value.pageNum = 1 + getList() +} + +/** 閲嶇疆鎸夐挳鎿嶄綔 */ +function resetQuery() { + dateRange.value = [] + proxy.resetForm("queryRef") + handleQuery() +} + +// 澶氶�夋閫変腑鏁版嵁 +function handleSelectionChange(selection) { + ids.value = selection.map(item => item.jobLogId) + multiple.value = !selection.length +} + +/** 璇︾粏鎸夐挳鎿嶄綔 */ +function handleView(row) { + open.value = true + form.value = row +} + +/** 鍒犻櫎鎸夐挳鎿嶄綔 */ +function handleDelete(row) { + proxy.$modal.confirm('鏄惁纭鍒犻櫎璋冨害鏃ュ織缂栧彿涓�"' + ids.value + '"鐨勬暟鎹」?').then(function () { + return delJobLog(ids.value) + }).then(() => { + getList() + proxy.$modal.msgSuccess("鍒犻櫎鎴愬姛") + }).catch(() => {}) +} + +/** 娓呯┖鎸夐挳鎿嶄綔 */ +function handleClean() { + proxy.$modal.confirm("鏄惁纭娓呯┖鎵�鏈夎皟搴︽棩蹇楁暟鎹」?").then(function () { + return cleanJobLog() + }).then(() => { + getList() + proxy.$modal.msgSuccess("娓呯┖鎴愬姛") + }).catch(() => {}) +} + +/** 瀵煎嚭鎸夐挳鎿嶄綔 */ +function handleExport() { + proxy.download("monitor/jobLog/export", { + ...queryParams.value, + }, `job_log_${new Date().getTime()}.xlsx`) +} + +(() => { + 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() + }) + } else { + getList() + } +})() +</script> diff --git a/src/views/monitor/logininfor/index.vue b/src/views/monitor/logininfor/index.vue new file mode 100644 index 0000000..6f38e35 --- /dev/null +++ b/src/views/monitor/logininfor/index.vue @@ -0,0 +1,233 @@ +<template> + <div class="app-container"> + <el-form :model="queryParams" ref="queryRef" :inline="true" v-show="showSearch" label-width="68px"> + <el-form-item label="鐧诲綍鍦板潃" prop="ipaddr"> + <el-input + v-model="queryParams.ipaddr" + placeholder="璇疯緭鍏ョ櫥褰曞湴鍧�" + clearable + style="width: 240px;" + @keyup.enter="handleQuery" + /> + </el-form-item> + <el-form-item label="鐢ㄦ埛鍚嶇О" prop="userName"> + <el-input + v-model="queryParams.userName" + placeholder="璇疯緭鍏ョ敤鎴峰悕绉�" + clearable + style="width: 240px;" + @keyup.enter="handleQuery" + /> + </el-form-item> + <el-form-item label="鐘舵��" prop="status"> + <el-select + v-model="queryParams.status" + placeholder="鐧诲綍鐘舵��" + clearable + style="width: 240px" + > + <el-option + v-for="dict in sys_common_status" + :key="dict.value" + :label="dict.label" + :value="dict.value" + /> + </el-select> + </el-form-item> + <el-form-item label="鐧诲綍鏃堕棿" style="width: 308px"> + <el-date-picker + v-model="dateRange" + value-format="YYYY-MM-DD HH:mm:ss" + type="daterange" + range-separator="-" + start-placeholder="寮�濮嬫棩鏈�" + end-placeholder="缁撴潫鏃ユ湡" + :default-time="[new Date(2000, 1, 1, 0, 0, 0), new Date(2000, 1, 1, 23, 59, 59)]" + ></el-date-picker> + </el-form-item> + <el-form-item> + <el-button type="primary" icon="Search" @click="handleQuery">鎼滅储</el-button> + <el-button icon="Refresh" @click="resetQuery">閲嶇疆</el-button> + </el-form-item> + </el-form> + + <el-row :gutter="10" class="mb8"> + <el-col :span="1.5"> + <el-button + type="danger" + plain + icon="Delete" + :disabled="multiple" + @click="handleDelete" + v-hasPermi="['monitor:logininfor:remove']" + >鍒犻櫎</el-button> + </el-col> + <el-col :span="1.5"> + <el-button + type="danger" + plain + icon="Delete" + @click="handleClean" + v-hasPermi="['monitor:logininfor:remove']" + >娓呯┖</el-button> + </el-col> + <el-col :span="1.5"> + <el-button + type="primary" + plain + icon="Unlock" + :disabled="single" + @click="handleUnlock" + v-hasPermi="['monitor:logininfor:unlock']" + >瑙i攣</el-button> + </el-col> + <el-col :span="1.5"> + <el-button + type="warning" + plain + icon="Download" + @click="handleExport" + v-hasPermi="['monitor:logininfor:export']" + >瀵煎嚭</el-button> + </el-col> + <right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar> + </el-row> + + <el-table ref="logininforRef" v-loading="loading" :data="logininforList" @selection-change="handleSelectionChange" :default-sort="defaultSort" @sort-change="handleSortChange"> + <el-table-column type="selection" width="55" align="center" /> + <el-table-column label="璁块棶缂栧彿" align="center" prop="infoId" /> + <el-table-column label="鐢ㄦ埛鍚嶇О" align="center" prop="userName" :show-overflow-tooltip="true" sortable="custom" :sort-orders="['descending', 'ascending']" /> + <el-table-column label="鍦板潃" align="center" prop="ipaddr" :show-overflow-tooltip="true" /> + <el-table-column label="鐧诲綍鍦扮偣" align="center" prop="loginLocation" :show-overflow-tooltip="true" /> + <el-table-column label="鎿嶄綔绯荤粺" align="center" prop="os" :show-overflow-tooltip="true" /> + <el-table-column label="娴忚鍣�" align="center" prop="browser" :show-overflow-tooltip="true" /> + <el-table-column label="鐧诲綍鐘舵��" align="center" prop="status"> + <template #default="scope"> + <dict-tag :options="sys_common_status" :value="scope.row.status" /> + </template> + </el-table-column> + <el-table-column label="鎻忚堪" align="center" prop="msg" :show-overflow-tooltip="true" /> + <el-table-column label="璁块棶鏃堕棿" align="center" prop="loginTime" sortable="custom" :sort-orders="['descending', 'ascending']" width="180"> + <template #default="scope"> + <span>{{ parseTime(scope.row.loginTime) }}</span> + </template> + </el-table-column> + </el-table> + + <pagination + v-show="total > 0" + :total="total" + v-model:page="queryParams.pageNum" + v-model:limit="queryParams.pageSize" + @pagination="getList" + /> + </div> +</template> + +<script setup name="Logininfor"> +import { list, delLogininfor, cleanLogininfor, unlockLogininfor } from "@/api/monitor/logininfor" + +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 queryParams = ref({ + pageNum: 1, + pageSize: 10, + ipaddr: undefined, + userName: undefined, + status: undefined, + orderByColumn: undefined, + isAsc: undefined +}) + +/** 鏌ヨ鐧诲綍鏃ュ織鍒楄〃 */ +function getList() { + loading.value = true + list(proxy.addDateRange(queryParams.value, dateRange.value)).then(response => { + logininforList.value = response.rows + total.value = response.total + loading.value = false + }) +} + +/** 鎼滅储鎸夐挳鎿嶄綔 */ +function handleQuery() { + 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) +} + +/** 澶氶�夋閫変腑鏁版嵁 */ +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) +} + +/** 鎺掑簭瑙﹀彂浜嬩欢 */ +function handleSortChange(column, prop, order) { + queryParams.value.orderByColumn = column.prop + queryParams.value.isAsc = column.order + getList() +} + +/** 鍒犻櫎鎸夐挳鎿嶄綔 */ +function handleDelete(row) { + const infoIds = row.infoId || ids.value + proxy.$modal.confirm('鏄惁纭鍒犻櫎璁块棶缂栧彿涓�"' + infoIds + '"鐨勬暟鎹」?').then(function () { + return delLogininfor(infoIds) + }).then(() => { + getList() + proxy.$modal.msgSuccess("鍒犻櫎鎴愬姛") + }).catch(() => {}) +} + +/** 娓呯┖鎸夐挳鎿嶄綔 */ +function handleClean() { + proxy.$modal.confirm("鏄惁纭娓呯┖鎵�鏈夌櫥褰曟棩蹇楁暟鎹」?").then(function () { + return cleanLogininfor() + }).then(() => { + getList() + proxy.$modal.msgSuccess("娓呯┖鎴愬姛") + }).catch(() => {}) +} + +/** 瑙i攣鎸夐挳鎿嶄綔 */ +function handleUnlock() { + const username = selectName.value + proxy.$modal.confirm('鏄惁纭瑙i攣鐢ㄦ埛"' + username + '"鏁版嵁椤�?').then(function () { + return unlockLogininfor(username) + }).then(() => { + proxy.$modal.msgSuccess("鐢ㄦ埛" + username + "瑙i攣鎴愬姛") + }).catch(() => {}) +} + +/** 瀵煎嚭鎸夐挳鎿嶄綔 */ +function handleExport() { + proxy.download("monitor/logininfor/export", { + ...queryParams.value, + }, `logininfor_${new Date().getTime()}.xlsx`) +} + +getList() +</script> diff --git a/src/views/monitor/online/index.vue b/src/views/monitor/online/index.vue new file mode 100644 index 0000000..d6ec869 --- /dev/null +++ b/src/views/monitor/online/index.vue @@ -0,0 +1,109 @@ +<template> + <div class="app-container"> + <el-form :model="queryParams" ref="queryRef" :inline="true"> + <el-form-item label="鐧诲綍鍦板潃" prop="ipaddr"> + <el-input + v-model="queryParams.ipaddr" + placeholder="璇疯緭鍏ョ櫥褰曞湴鍧�" + clearable + style="width: 200px" + @keyup.enter="handleQuery" + /> + </el-form-item> + <el-form-item label="鐢ㄦ埛鍚嶇О" prop="userName"> + <el-input + v-model="queryParams.userName" + placeholder="璇疯緭鍏ョ敤鎴峰悕绉�" + clearable + style="width: 200px" + @keyup.enter="handleQuery" + /> + </el-form-item> + <el-form-item> + <el-button type="primary" icon="Search" @click="handleQuery">鎼滅储</el-button> + <el-button icon="Refresh" @click="resetQuery">閲嶇疆</el-button> + </el-form-item> + </el-form> + <el-table + v-loading="loading" + :data="onlineList.slice((pageNum - 1) * pageSize, pageNum * pageSize)" + style="width: 100%;" + > + <el-table-column label="搴忓彿" width="50" type="index" align="center"> + <template #default="scope"> + <span>{{ (pageNum - 1) * pageSize + scope.$index + 1 }}</span> + </template> + </el-table-column> + <el-table-column label="浼氳瘽缂栧彿" align="center" prop="tokenId" :show-overflow-tooltip="true" /> + <el-table-column label="鐧诲綍鍚嶇О" align="center" prop="userName" :show-overflow-tooltip="true" /> + <el-table-column label="鎵�灞為儴闂�" align="center" prop="deptName" :show-overflow-tooltip="true" /> + <el-table-column label="涓绘満" align="center" prop="ipaddr" :show-overflow-tooltip="true" /> + <el-table-column label="鐧诲綍鍦扮偣" align="center" prop="loginLocation" :show-overflow-tooltip="true" /> + <el-table-column label="鎿嶄綔绯荤粺" align="center" prop="os" :show-overflow-tooltip="true" /> + <el-table-column label="娴忚鍣�" align="center" prop="browser" :show-overflow-tooltip="true" /> + <el-table-column label="鐧诲綍鏃堕棿" align="center" prop="loginTime" width="180"> + <template #default="scope"> + <span>{{ parseTime(scope.row.loginTime) }}</span> + </template> + </el-table-column> + <el-table-column label="鎿嶄綔" align="center" class-name="small-padding fixed-width"> + <template #default="scope"> + <el-button link type="primary" icon="Delete" @click="handleForceLogout(scope.row)" v-hasPermi="['monitor:online:forceLogout']">寮洪��</el-button> + </template> + </el-table-column> + </el-table> + + <pagination v-show="total > 0" :total="total" v-model:page="pageNum" v-model:limit="pageSize" /> + </div> +</template> + +<script setup name="Online"> +import { forceLogout, list as initData } from "@/api/monitor/online" + +const { proxy } = getCurrentInstance() + +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 + initData(queryParams.value).then(response => { + onlineList.value = response.rows + total.value = response.total + loading.value = false + }) +} + +/** 鎼滅储鎸夐挳鎿嶄綔 */ +function handleQuery() { + pageNum.value = 1 + getList() +} + +/** 閲嶇疆鎸夐挳鎿嶄綔 */ +function resetQuery() { + proxy.resetForm("queryRef") + handleQuery() +} + +/** 寮洪��鎸夐挳鎿嶄綔 */ +function handleForceLogout(row) { + proxy.$modal.confirm('鏄惁纭寮洪��鍚嶇О涓�"' + row.userName + '"鐨勭敤鎴�?').then(function () { + return forceLogout(row.tokenId) + }).then(() => { + getList() + proxy.$modal.msgSuccess("鍒犻櫎鎴愬姛") + }).catch(() => {}) +} + +getList() +</script> diff --git a/src/views/monitor/operlog/index.vue b/src/views/monitor/operlog/index.vue new file mode 100644 index 0000000..675a56c --- /dev/null +++ b/src/views/monitor/operlog/index.vue @@ -0,0 +1,310 @@ +<template> + <div class="app-container"> + <el-form :model="queryParams" ref="queryRef" :inline="true" v-show="showSearch" label-width="68px"> + <el-form-item label="鎿嶄綔鍦板潃" prop="operIp"> + <el-input + v-model="queryParams.operIp" + placeholder="璇疯緭鍏ユ搷浣滃湴鍧�" + clearable + style="width: 240px;" + @keyup.enter="handleQuery" + /> + </el-form-item> + <el-form-item label="绯荤粺妯″潡" prop="title"> + <el-input + v-model="queryParams.title" + placeholder="璇疯緭鍏ョ郴缁熸ā鍧�" + clearable + style="width: 240px;" + @keyup.enter="handleQuery" + /> + </el-form-item> + <el-form-item label="鎿嶄綔浜哄憳" prop="operName"> + <el-input + v-model="queryParams.operName" + placeholder="璇疯緭鍏ユ搷浣滀汉鍛�" + clearable + style="width: 240px;" + @keyup.enter="handleQuery" + /> + </el-form-item> + <el-form-item label="绫诲瀷" prop="businessType"> + <el-select + v-model="queryParams.businessType" + placeholder="鎿嶄綔绫诲瀷" + clearable + style="width: 240px" + > + <el-option + v-for="dict in sys_oper_type" + :key="dict.value" + :label="dict.label" + :value="dict.value" + /> + </el-select> + </el-form-item> + <el-form-item label="鐘舵��" prop="status"> + <el-select + v-model="queryParams.status" + placeholder="鎿嶄綔鐘舵��" + clearable + style="width: 240px" + > + <el-option + v-for="dict in sys_common_status" + :key="dict.value" + :label="dict.label" + :value="dict.value" + /> + </el-select> + </el-form-item> + <el-form-item label="鎿嶄綔鏃堕棿" style="width: 308px"> + <el-date-picker + v-model="dateRange" + value-format="YYYY-MM-DD HH:mm:ss" + type="daterange" + range-separator="-" + start-placeholder="寮�濮嬫棩鏈�" + end-placeholder="缁撴潫鏃ユ湡" + :default-time="[new Date(2000, 1, 1, 0, 0, 0), new Date(2000, 1, 1, 23, 59, 59)]" + ></el-date-picker> + </el-form-item> + <el-form-item> + <el-button type="primary" icon="Search" @click="handleQuery">鎼滅储</el-button> + <el-button icon="Refresh" @click="resetQuery">閲嶇疆</el-button> + </el-form-item> + </el-form> + + <el-row :gutter="10" class="mb8"> + <el-col :span="1.5"> + <el-button + type="danger" + plain + icon="Delete" + :disabled="multiple" + @click="handleDelete" + v-hasPermi="['monitor:operlog:remove']" + >鍒犻櫎</el-button> + </el-col> + <el-col :span="1.5"> + <el-button + type="danger" + plain + icon="Delete" + @click="handleClean" + v-hasPermi="['monitor:operlog:remove']" + >娓呯┖</el-button> + </el-col> + <el-col :span="1.5"> + <el-button + type="warning" + plain + icon="Download" + @click="handleExport" + v-hasPermi="['monitor:operlog:export']" + >瀵煎嚭</el-button> + </el-col> + <right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar> + </el-row> + + <el-table ref="operlogRef" v-loading="loading" :data="operlogList" @selection-change="handleSelectionChange" :default-sort="defaultSort" @sort-change="handleSortChange"> + <el-table-column type="selection" width="50" align="center" /> + <el-table-column label="鏃ュ織缂栧彿" align="center" prop="operId" /> + <el-table-column label="绯荤粺妯″潡" align="center" prop="title" :show-overflow-tooltip="true" /> + <el-table-column label="鎿嶄綔绫诲瀷" align="center" prop="businessType"> + <template #default="scope"> + <dict-tag :options="sys_oper_type" :value="scope.row.businessType" /> + </template> + </el-table-column> + <el-table-column label="鎿嶄綔浜哄憳" align="center" width="110" prop="operName" :show-overflow-tooltip="true" sortable="custom" :sort-orders="['descending', 'ascending']" /> + <el-table-column label="鎿嶄綔鍦板潃" align="center" prop="operIp" width="130" :show-overflow-tooltip="true" /> + <el-table-column label="鎿嶄綔鐘舵��" align="center" prop="status"> + <template #default="scope"> + <dict-tag :options="sys_common_status" :value="scope.row.status" /> + </template> + </el-table-column> + <el-table-column label="鎿嶄綔鏃ユ湡" align="center" prop="operTime" width="180" sortable="custom" :sort-orders="['descending', 'ascending']"> + <template #default="scope"> + <span>{{ parseTime(scope.row.operTime) }}</span> + </template> + </el-table-column> + <el-table-column label="娑堣�楁椂闂�" align="center" prop="costTime" width="110" :show-overflow-tooltip="true" sortable="custom" :sort-orders="['descending', 'ascending']"> + <template #default="scope"> + <span>{{ scope.row.costTime }}姣</span> + </template> + </el-table-column> + <el-table-column label="鎿嶄綔" align="center" class-name="small-padding fixed-width"> + <template #default="scope"> + <el-button link type="primary" icon="View" @click="handleView(scope.row, scope.index)" v-hasPermi="['monitor:operlog:query']">璇︾粏</el-button> + </template> + </el-table-column> + </el-table> + + <pagination + v-show="total > 0" + :total="total" + v-model:page="queryParams.pageNum" + v-model:limit="queryParams.pageSize" + @pagination="getList" + /> + + <!-- 鎿嶄綔鏃ュ織璇︾粏 --> + <el-dialog title="鎿嶄綔鏃ュ織璇︾粏" v-model="open" width="800px" append-to-body> + <el-form :model="form" label-width="100px"> + <el-row> + <el-col :span="12"> + <el-form-item label="鎿嶄綔妯″潡锛�">{{ form.title }} / {{ typeFormat(form) }}</el-form-item> + <el-form-item + label="鐧诲綍淇℃伅锛�" + >{{ form.operName }} / {{ form.operIp }} / {{ form.operLocation }}</el-form-item> + </el-col> + <el-col :span="12"> + <el-form-item label="璇锋眰鍦板潃锛�">{{ form.operUrl }}</el-form-item> + <el-form-item label="璇锋眰鏂瑰紡锛�">{{ form.requestMethod }}</el-form-item> + </el-col> + <el-col :span="24"> + <el-form-item label="鎿嶄綔鏂规硶锛�">{{ form.method }}</el-form-item> + </el-col> + <el-col :span="24"> + <el-form-item label="璇锋眰鍙傛暟锛�">{{ form.operParam }}</el-form-item> + </el-col> + <el-col :span="24"> + <el-form-item label="杩斿洖鍙傛暟锛�">{{ form.jsonResult }}</el-form-item> + </el-col> + <el-col :span="8"> + <el-form-item label="鎿嶄綔鐘舵�侊細"> + <div v-if="form.status === 0">姝e父</div> + <div v-else-if="form.status === 1">澶辫触</div> + </el-form-item> + </el-col> + <el-col :span="8"> + <el-form-item label="娑堣�楁椂闂达細">{{ form.costTime }}姣</el-form-item> + </el-col> + <el-col :span="8"> + <el-form-item label="鎿嶄綔鏃堕棿锛�">{{ parseTime(form.operTime) }}</el-form-item> + </el-col> + <el-col :span="24"> + <el-form-item label="寮傚父淇℃伅锛�" v-if="form.status === 1">{{ form.errorMsg }}</el-form-item> + </el-col> + </el-row> + </el-form> + <template #footer> + <div class="dialog-footer"> + <el-button @click="open = false">鍏� 闂�</el-button> + </div> + </template> + </el-dialog> + </div> +</template> + +<script setup name="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 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: {}, + queryParams: { + pageNum: 1, + pageSize: 10, + operIp: undefined, + title: undefined, + operName: undefined, + businessType: undefined, + status: undefined + } +}) + +const { queryParams, form } = toRefs(data) + +/** 鏌ヨ鐧诲綍鏃ュ織 */ +function getList() { + loading.value = true + list(proxy.addDateRange(queryParams.value, dateRange.value)).then(response => { + operlogList.value = response.rows + total.value = response.total + loading.value = false + }) +} + +/** 鎿嶄綔鏃ュ織绫诲瀷瀛楀吀缈昏瘧 */ +function typeFormat(row, column) { + return proxy.selectDictLabel(sys_oper_type.value, row.businessType) +} + +/** 鎼滅储鎸夐挳鎿嶄綔 */ +function handleQuery() { + 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) +} + +/** 澶氶�夋閫変腑鏁版嵁 */ +function handleSelectionChange(selection) { + 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() +} + +/** 璇︾粏鎸夐挳鎿嶄綔 */ +function handleView(row) { + open.value = true + form.value = row +} + +/** 鍒犻櫎鎸夐挳鎿嶄綔 */ +function handleDelete(row) { + const operIds = row.operId || ids.value + proxy.$modal.confirm('鏄惁纭鍒犻櫎鏃ュ織缂栧彿涓�"' + operIds + '"鐨勬暟鎹」?').then(function () { + return delOperlog(operIds) + }).then(() => { + getList() + proxy.$modal.msgSuccess("鍒犻櫎鎴愬姛") + }).catch(() => {}) +} + +/** 娓呯┖鎸夐挳鎿嶄綔 */ +function handleClean() { + proxy.$modal.confirm("鏄惁纭娓呯┖鎵�鏈夋搷浣滄棩蹇楁暟鎹」?").then(function () { + return cleanOperlog() + }).then(() => { + getList() + proxy.$modal.msgSuccess("娓呯┖鎴愬姛") + }).catch(() => {}) +} + +/** 瀵煎嚭鎸夐挳鎿嶄綔 */ +function handleExport() { + proxy.download("monitor/operlog/export",{ + ...queryParams.value, + }, `config_${new Date().getTime()}.xlsx`) +} + +getList() +</script> diff --git a/src/views/monitor/server/index.vue b/src/views/monitor/server/index.vue new file mode 100644 index 0000000..60ff80c --- /dev/null +++ b/src/views/monitor/server/index.vue @@ -0,0 +1,187 @@ +<template> + <div class="app-container"> + <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> + <div class="el-table el-table--enable-row-hover el-table--medium"> + <table cellspacing="0" style="width: 100%;"> + <thead> + <tr> + <th class="el-table__cell is-leaf"><div class="cell">灞炴��</div></th> + <th class="el-table__cell is-leaf"><div class="cell">鍊�</div></th> + </tr> + </thead> + <tbody> + <tr> + <td class="el-table__cell is-leaf"><div class="cell">鏍稿績鏁�</div></td> + <td class="el-table__cell is-leaf"><div class="cell" v-if="server.cpu">{{ server.cpu.cpuNum }}</div></td> + </tr> + <tr> + <td class="el-table__cell is-leaf"><div class="cell">鐢ㄦ埛浣跨敤鐜�</div></td> + <td class="el-table__cell is-leaf"><div class="cell" v-if="server.cpu">{{ server.cpu.used }}%</div></td> + </tr> + <tr> + <td class="el-table__cell is-leaf"><div class="cell">绯荤粺浣跨敤鐜�</div></td> + <td class="el-table__cell is-leaf"><div class="cell" v-if="server.cpu">{{ server.cpu.sys }}%</div></td> + </tr> + <tr> + <td class="el-table__cell is-leaf"><div class="cell">褰撳墠绌洪棽鐜�</div></td> + <td class="el-table__cell is-leaf"><div class="cell" v-if="server.cpu">{{ server.cpu.free }}%</div></td> + </tr> + </tbody> + </table> + </div> + </el-card> + </el-col> + + <el-col :span="12" class="card-box"> + <el-card> + <template #header><Tickets style="width: 1em; height: 1em; vertical-align: middle;" /> <span style="vertical-align: middle;">鍐呭瓨</span></template> + <div class="el-table el-table--enable-row-hover el-table--medium"> + <table cellspacing="0" style="width: 100%;"> + <thead> + <tr> + <th class="el-table__cell is-leaf"><div class="cell">灞炴��</div></th> + <th class="el-table__cell is-leaf"><div class="cell">鍐呭瓨</div></th> + <th class="el-table__cell is-leaf"><div class="cell">JVM</div></th> + </tr> + </thead> + <tbody> + <tr> + <td class="el-table__cell is-leaf"><div class="cell">鎬诲唴瀛�</div></td> + <td class="el-table__cell is-leaf"><div class="cell" v-if="server.mem">{{ server.mem.total }}G</div></td> + <td class="el-table__cell is-leaf"><div class="cell" v-if="server.jvm">{{ server.jvm.total }}M</div></td> + </tr> + <tr> + <td class="el-table__cell is-leaf"><div class="cell">宸茬敤鍐呭瓨</div></td> + <td class="el-table__cell is-leaf"><div class="cell" v-if="server.mem">{{ server.mem.used}}G</div></td> + <td class="el-table__cell is-leaf"><div class="cell" v-if="server.jvm">{{ server.jvm.used}}M</div></td> + </tr> + <tr> + <td class="el-table__cell is-leaf"><div class="cell">鍓╀綑鍐呭瓨</div></td> + <td class="el-table__cell is-leaf"><div class="cell" v-if="server.mem">{{ server.mem.free }}G</div></td> + <td class="el-table__cell is-leaf"><div class="cell" v-if="server.jvm">{{ server.jvm.free }}M</div></td> + </tr> + <tr> + <td class="el-table__cell is-leaf"><div class="cell">浣跨敤鐜�</div></td> + <td class="el-table__cell is-leaf"><div class="cell" v-if="server.mem" :class="{'text-danger': server.mem.usage > 80}">{{ server.mem.usage }}%</div></td> + <td class="el-table__cell is-leaf"><div class="cell" v-if="server.jvm" :class="{'text-danger': server.jvm.usage > 80}">{{ server.jvm.usage }}%</div></td> + </tr> + </tbody> + </table> + </div> + </el-card> + </el-col> + + <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> + <div class="el-table el-table--enable-row-hover el-table--medium"> + <table cellspacing="0" style="width: 100%;"> + <tbody> + <tr> + <td class="el-table__cell is-leaf"><div class="cell">鏈嶅姟鍣ㄥ悕绉�</div></td> + <td class="el-table__cell is-leaf"><div class="cell" v-if="server.sys">{{ server.sys.computerName }}</div></td> + <td class="el-table__cell is-leaf"><div class="cell">鎿嶄綔绯荤粺</div></td> + <td class="el-table__cell is-leaf"><div class="cell" v-if="server.sys">{{ server.sys.osName }}</div></td> + </tr> + <tr> + <td class="el-table__cell is-leaf"><div class="cell">鏈嶅姟鍣↖P</div></td> + <td class="el-table__cell is-leaf"><div class="cell" v-if="server.sys">{{ server.sys.computerIp }}</div></td> + <td class="el-table__cell is-leaf"><div class="cell">绯荤粺鏋舵瀯</div></td> + <td class="el-table__cell is-leaf"><div class="cell" v-if="server.sys">{{ server.sys.osArch }}</div></td> + </tr> + </tbody> + </table> + </div> + </el-card> + </el-col> + + <el-col :span="24" class="card-box"> + <el-card> + <template #header><CoffeeCup style="width: 1em; height: 1em; vertical-align: middle;" /> <span style="vertical-align: middle;">Java铏氭嫙鏈轰俊鎭�</span></template> + <div class="el-table el-table--enable-row-hover el-table--medium"> + <table cellspacing="0" style="width: 100%;table-layout:fixed;"> + <tbody> + <tr> + <td class="el-table__cell is-leaf"><div class="cell">Java鍚嶇О</div></td> + <td class="el-table__cell is-leaf"><div class="cell" v-if="server.jvm">{{ server.jvm.name }}</div></td> + <td class="el-table__cell is-leaf"><div class="cell">Java鐗堟湰</div></td> + <td class="el-table__cell is-leaf"><div class="cell" v-if="server.jvm">{{ server.jvm.version }}</div></td> + </tr> + <tr> + <td class="el-table__cell is-leaf"><div class="cell">鍚姩鏃堕棿</div></td> + <td class="el-table__cell is-leaf"><div class="cell" v-if="server.jvm">{{ server.jvm.startTime }}</div></td> + <td class="el-table__cell is-leaf"><div class="cell">杩愯鏃堕暱</div></td> + <td class="el-table__cell is-leaf"><div class="cell" v-if="server.jvm">{{ server.jvm.runTime }}</div></td> + </tr> + <tr> + <td colspan="1" class="el-table__cell is-leaf"><div class="cell">瀹夎璺緞</div></td> + <td colspan="3" class="el-table__cell is-leaf"><div class="cell" v-if="server.jvm">{{ server.jvm.home }}</div></td> + </tr> + <tr> + <td colspan="1" class="el-table__cell is-leaf"><div class="cell">椤圭洰璺緞</div></td> + <td colspan="3" class="el-table__cell is-leaf"><div class="cell" v-if="server.sys">{{ server.sys.userDir }}</div></td> + </tr> + <tr> + <td colspan="1" class="el-table__cell is-leaf"><div class="cell">杩愯鍙傛暟</div></td> + <td colspan="3" class="el-table__cell is-leaf"><div class="cell" v-if="server.jvm">{{ server.jvm.inputArgs }}</div></td> + </tr> + </tbody> + </table> + </div> + </el-card> + </el-col> + + <el-col :span="24" class="card-box"> + <el-card> + <template #header><MessageBox style="width: 1em; height: 1em; vertical-align: middle;" /> <span style="vertical-align: middle;">纾佺洏鐘舵��</span></template> + <div class="el-table el-table--enable-row-hover el-table--medium"> + <table cellspacing="0" style="width: 100%;"> + <thead> + <tr> + <th class="el-table__cell el-table__cell is-leaf"><div class="cell">鐩樼璺緞</div></th> + <th class="el-table__cell is-leaf"><div class="cell">鏂囦欢绯荤粺</div></th> + <th class="el-table__cell is-leaf"><div class="cell">鐩樼绫诲瀷</div></th> + <th class="el-table__cell is-leaf"><div class="cell">鎬诲ぇ灏�</div></th> + <th class="el-table__cell is-leaf"><div class="cell">鍙敤澶у皬</div></th> + <th class="el-table__cell is-leaf"><div class="cell">宸茬敤澶у皬</div></th> + <th class="el-table__cell is-leaf"><div class="cell">宸茬敤鐧惧垎姣�</div></th> + </tr> + </thead> + <tbody v-if="server.sysFiles"> + <tr v-for="(sysFile, index) in server.sysFiles" :key="index"> + <td class="el-table__cell is-leaf"><div class="cell">{{ sysFile.dirName }}</div></td> + <td class="el-table__cell is-leaf"><div class="cell">{{ sysFile.sysTypeName }}</div></td> + <td class="el-table__cell is-leaf"><div class="cell">{{ sysFile.typeName }}</div></td> + <td class="el-table__cell is-leaf"><div class="cell">{{ sysFile.total }}</div></td> + <td class="el-table__cell is-leaf"><div class="cell">{{ sysFile.free }}</div></td> + <td class="el-table__cell is-leaf"><div class="cell">{{ sysFile.used }}</div></td> + <td class="el-table__cell is-leaf"><div class="cell" :class="{'text-danger': sysFile.usage > 80}">{{ sysFile.usage }}%</div></td> + </tr> + </tbody> + </table> + </div> + </el-card> + </el-col> + </el-row> + </div> +</template> + +<script setup> +import { getServer } from '@/api/monitor/server' + +const server = ref([]) +const { proxy } = getCurrentInstance() + +function getList() { + proxy.$modal.loading("姝e湪鍔犺浇鏈嶅姟鐩戞帶鏁版嵁锛岃绋嶅�欙紒") + getServer().then(response => { + server.value = response.data + proxy.$modal.closeLoading() + }) +} + +getList() +</script> diff --git a/src/views/redirect/index.vue b/src/views/redirect/index.vue new file mode 100644 index 0000000..99dd4f5 --- /dev/null +++ b/src/views/redirect/index.vue @@ -0,0 +1,14 @@ +<template> + <div></div> +</template> + +<script setup> +import { useRoute, useRouter } from 'vue-router' + +const route = useRoute() +const router = useRouter() +const { params, query } = route +const { path } = params + +router.replace({ path: '/' + path, query }) +</script> \ No newline at end of file diff --git a/src/views/register.vue b/src/views/register.vue new file mode 100644 index 0000000..badd966 --- /dev/null +++ b/src/views/register.vue @@ -0,0 +1,220 @@ +<template> + <div class="register"> + <el-form ref="registerRef" :model="registerForm" :rules="registerRules" class="register-form"> + <h3 class="title">{{ title }}</h3> + <el-form-item prop="username"> + <el-input + v-model="registerForm.username" + type="text" + size="large" + auto-complete="off" + placeholder="璐﹀彿" + > + <template #prefix><svg-icon icon-class="user" class="el-input__icon input-icon" /></template> + </el-input> + </el-form-item> + <el-form-item prop="password"> + <el-input + v-model="registerForm.password" + type="password" + size="large" + auto-complete="off" + placeholder="瀵嗙爜" + @keyup.enter="handleRegister" + > + <template #prefix><svg-icon icon-class="password" class="el-input__icon input-icon" /></template> + </el-input> + </el-form-item> + <el-form-item prop="confirmPassword"> + <el-input + v-model="registerForm.confirmPassword" + type="password" + size="large" + auto-complete="off" + placeholder="纭瀵嗙爜" + @keyup.enter="handleRegister" + > + <template #prefix><svg-icon icon-class="password" class="el-input__icon input-icon" /></template> + </el-input> + </el-form-item> + <el-form-item prop="code" v-if="captchaEnabled"> + <el-input + size="large" + v-model="registerForm.code" + auto-complete="off" + placeholder="楠岃瘉鐮�" + style="width: 63%" + @keyup.enter="handleRegister" + > + <template #prefix><svg-icon icon-class="validCode" class="el-input__icon input-icon" /></template> + </el-input> + <div class="register-code"> + <img :src="codeUrl" @click="getCode" class="register-code-img"/> + </div> + </el-form-item> + <el-form-item style="width:100%;"> + <el-button + :loading="loading" + size="large" + type="primary" + style="width:100%;" + @click.prevent="handleRegister" + > + <span v-if="!loading">娉� 鍐�</span> + <span v-else>娉� 鍐� 涓�...</span> + </el-button> + <div style="float: right;"> + <router-link class="link-type" :to="'/login'">浣跨敤宸叉湁璐︽埛鐧诲綍</router-link> + </div> + </el-form-item> + </el-form> + <!-- 搴曢儴 --> + <div class="el-register-footer"> + <span>Copyright 漏 2018-2025 ruoyi.vip All Rights Reserved.</span> + </div> + </div> +</template> + +<script setup> +import { ElMessageBox } from "element-plus" +import { getCodeImg, register } from "@/api/login" + +const title = import.meta.env.VITE_APP_TITLE +const router = useRouter() +const { proxy } = getCurrentInstance() + +const registerForm = ref({ + username: "", + password: "", + confirmPassword: "", + code: "", + uuid: "" +}) + +const equalToPassword = (rule, value, callback) => { + if (registerForm.value.password !== value) { + callback(new Error("涓ゆ杈撳叆鐨勫瘑鐮佷笉涓�鑷�")) + } else { + callback() + } +} + +const registerRules = { + username: [ + { required: true, trigger: "blur", message: "璇疯緭鍏ユ偍鐨勮处鍙�" }, + { min: 2, max: 20, message: "鐢ㄦ埛璐﹀彿闀垮害蹇呴』浠嬩簬 2 鍜� 20 涔嬮棿", trigger: "blur" } + ], + password: [ + { required: true, trigger: "blur", message: "璇疯緭鍏ユ偍鐨勫瘑鐮�" }, + { 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) + +function handleRegister() { + proxy.$refs.registerRef.validate(valid => { + if (valid) { + loading.value = true + register(registerForm.value).then(res => { + const username = registerForm.value.username + ElMessageBox.alert("<font color='red'>鎭枩浣狅紝鎮ㄧ殑璐﹀彿 " + username + " 娉ㄥ唽鎴愬姛锛�</font>", "绯荤粺鎻愮ず", { + dangerouslyUseHTMLString: true, + type: "success", + }).then(() => { + router.push("/login") + }).catch(() => {}) + }).catch(() => { + loading.value = false + if (captchaEnabled) { + getCode() + } + }) + } + }) +} + +function getCode() { + getCodeImg().then(res => { + captchaEnabled.value = res.captchaEnabled === undefined ? true : res.captchaEnabled + if (captchaEnabled.value) { + codeUrl.value = "data:image/gif;base64," + res.img + registerForm.value.uuid = res.uuid + } + }) +} + +getCode() +</script> + +<style lang='scss' scoped> +.register { + display: flex; + justify-content: center; + align-items: center; + height: 100%; + background-image: url("../assets/images/login-background.jpg"); + background-size: cover; +} +.title { + margin: 0px auto 30px auto; + text-align: center; + color: #707070; +} + +.register-form { + border-radius: 6px; + background: #ffffff; + width: 400px; + padding: 25px 25px 5px 25px; + .el-input { + height: 40px; + input { + height: 40px; + } + } + .input-icon { + height: 39px; + width: 14px; + margin-left: 0px; + } +} +.register-tip { + font-size: 13px; + text-align: center; + color: #bfbfbf; +} +.register-code { + width: 33%; + height: 40px; + float: right; + img { + cursor: pointer; + vertical-align: middle; + } +} +.el-register-footer { + height: 40px; + line-height: 40px; + position: fixed; + bottom: 0; + width: 100%; + text-align: center; + color: #fff; + font-family: Arial; + font-size: 12px; + letter-spacing: 1px; +} +.register-code-img { + height: 40px; + padding-left: 12px; +} +</style> diff --git a/src/views/system/config/index.vue b/src/views/system/config/index.vue new file mode 100644 index 0000000..a358b0d --- /dev/null +++ b/src/views/system/config/index.vue @@ -0,0 +1,316 @@ +<template> + <div class="app-container"> + <el-form :model="queryParams" ref="queryRef" :inline="true" v-show="showSearch" label-width="68px"> + <el-form-item label="鍙傛暟鍚嶇О" prop="configName"> + <el-input + v-model="queryParams.configName" + placeholder="璇疯緭鍏ュ弬鏁板悕绉�" + clearable + style="width: 240px" + @keyup.enter="handleQuery" + /> + </el-form-item> + <el-form-item label="鍙傛暟閿悕" prop="configKey"> + <el-input + v-model="queryParams.configKey" + placeholder="璇疯緭鍏ュ弬鏁伴敭鍚�" + clearable + style="width: 240px" + @keyup.enter="handleQuery" + /> + </el-form-item> + <el-form-item label="绯荤粺鍐呯疆" prop="configType"> + <el-select v-model="queryParams.configType" placeholder="绯荤粺鍐呯疆" clearable style="width: 240px"> + <el-option + v-for="dict in sys_yes_no" + :key="dict.value" + :label="dict.label" + :value="dict.value" + /> + </el-select> + </el-form-item> + <el-form-item label="鍒涘缓鏃堕棿" style="width: 308px;"> + <el-date-picker + v-model="dateRange" + value-format="YYYY-MM-DD" + type="daterange" + range-separator="-" + start-placeholder="寮�濮嬫棩鏈�" + end-placeholder="缁撴潫鏃ユ湡" + ></el-date-picker> + </el-form-item> + <el-form-item> + <el-button type="primary" icon="Search" @click="handleQuery">鎼滅储</el-button> + <el-button icon="Refresh" @click="resetQuery">閲嶇疆</el-button> + </el-form-item> + </el-form> + + <el-row :gutter="10" class="mb8"> + <el-col :span="1.5"> + <el-button + type="primary" + plain + icon="Plus" + @click="handleAdd" + v-hasPermi="['system:config:add']" + >鏂板</el-button> + </el-col> + <el-col :span="1.5"> + <el-button + type="success" + plain + icon="Edit" + :disabled="single" + @click="handleUpdate" + v-hasPermi="['system:config:edit']" + >淇敼</el-button> + </el-col> + <el-col :span="1.5"> + <el-button + type="danger" + plain + icon="Delete" + :disabled="multiple" + @click="handleDelete" + v-hasPermi="['system:config:remove']" + >鍒犻櫎</el-button> + </el-col> + <el-col :span="1.5"> + <el-button + type="warning" + plain + icon="Download" + @click="handleExport" + v-hasPermi="['system:config:export']" + >瀵煎嚭</el-button> + </el-col> + <el-col :span="1.5"> + <el-button + type="danger" + plain + icon="Refresh" + @click="handleRefreshCache" + v-hasPermi="['system:config:remove']" + >鍒锋柊缂撳瓨</el-button> + </el-col> + <right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar> + </el-row> + + <el-table v-loading="loading" :data="configList" @selection-change="handleSelectionChange"> + <el-table-column type="selection" width="55" align="center" /> + <el-table-column label="鍙傛暟涓婚敭" align="center" prop="configId" /> + <el-table-column label="鍙傛暟鍚嶇О" align="center" prop="configName" :show-overflow-tooltip="true" /> + <el-table-column label="鍙傛暟閿悕" align="center" prop="configKey" :show-overflow-tooltip="true" /> + <el-table-column label="鍙傛暟閿��" align="center" prop="configValue" :show-overflow-tooltip="true" /> + <el-table-column label="绯荤粺鍐呯疆" align="center" prop="configType"> + <template #default="scope"> + <dict-tag :options="sys_yes_no" :value="scope.row.configType" /> + </template> + </el-table-column> + <el-table-column label="澶囨敞" align="center" prop="remark" :show-overflow-tooltip="true" /> + <el-table-column label="鍒涘缓鏃堕棿" align="center" prop="createTime" width="180"> + <template #default="scope"> + <span>{{ parseTime(scope.row.createTime) }}</span> + </template> + </el-table-column> + <el-table-column label="鎿嶄綔" align="center" width="150" class-name="small-padding fixed-width"> + <template #default="scope"> + <el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['system:config:edit']" >淇敼</el-button> + <el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['system:config:remove']">鍒犻櫎</el-button> + </template> + </el-table-column> + </el-table> + + <pagination + v-show="total > 0" + :total="total" + v-model:page="queryParams.pageNum" + v-model:limit="queryParams.pageSize" + @pagination="getList" + /> + + <!-- 娣诲姞鎴栦慨鏀瑰弬鏁伴厤缃璇濇 --> + <el-dialog :title="title" v-model="open" width="500px" append-to-body> + <el-form ref="configRef" :model="form" :rules="rules" label-width="80px"> + <el-form-item label="鍙傛暟鍚嶇О" prop="configName"> + <el-input v-model="form.configName" placeholder="璇疯緭鍏ュ弬鏁板悕绉�" /> + </el-form-item> + <el-form-item label="鍙傛暟閿悕" prop="configKey"> + <el-input v-model="form.configKey" placeholder="璇疯緭鍏ュ弬鏁伴敭鍚�" /> + </el-form-item> + <el-form-item label="鍙傛暟閿��" prop="configValue"> + <el-input v-model="form.configValue" type="textarea" placeholder="璇疯緭鍏ュ弬鏁伴敭鍊�" /> + </el-form-item> + <el-form-item label="绯荤粺鍐呯疆" prop="configType"> + <el-radio-group v-model="form.configType"> + <el-radio + v-for="dict in sys_yes_no" + :key="dict.value" + :value="dict.value" + >{{ dict.label }}</el-radio> + </el-radio-group> + </el-form-item> + <el-form-item label="澶囨敞" prop="remark"> + <el-input v-model="form.remark" type="textarea" placeholder="璇疯緭鍏ュ唴瀹�" /> + </el-form-item> + </el-form> + <template #footer> + <div class="dialog-footer"> + <el-button type="primary" @click="submitForm">纭� 瀹�</el-button> + <el-button @click="cancel">鍙� 娑�</el-button> + </div> + </template> + </el-dialog> + </div> +</template> + +<script setup name="Config"> +import { listConfig, getConfig, delConfig, addConfig, updateConfig, refreshCache } from "@/api/system/config" + +const { proxy } = getCurrentInstance() +const { sys_yes_no } = proxy.useDict("sys_yes_no") + +const configList = 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 data = reactive({ + form: {}, + queryParams: { + pageNum: 1, + pageSize: 10, + configName: undefined, + configKey: undefined, + configType: undefined + }, + rules: { + configName: [{ required: true, message: "鍙傛暟鍚嶇О涓嶈兘涓虹┖", trigger: "blur" }], + configKey: [{ required: true, message: "鍙傛暟閿悕涓嶈兘涓虹┖", trigger: "blur" }], + configValue: [{ required: true, message: "鍙傛暟閿�间笉鑳戒负绌�", trigger: "blur" }] + } +}) + +const { queryParams, form, rules } = toRefs(data) + +/** 鏌ヨ鍙傛暟鍒楄〃 */ +function getList() { + loading.value = true + listConfig(proxy.addDateRange(queryParams.value, dateRange.value)).then(response => { + configList.value = response.rows + total.value = response.total + loading.value = false + }) +} + +/** 鍙栨秷鎸夐挳 */ +function cancel() { + open.value = false + reset() +} + +/** 琛ㄥ崟閲嶇疆 */ +function reset() { + form.value = { + configId: undefined, + configName: undefined, + configKey: undefined, + configValue: undefined, + configType: "Y", + remark: undefined + } + proxy.resetForm("configRef") +} + +/** 鎼滅储鎸夐挳鎿嶄綔 */ +function handleQuery() { + queryParams.value.pageNum = 1 + getList() +} + +/** 閲嶇疆鎸夐挳鎿嶄綔 */ +function resetQuery() { + dateRange.value = [] + proxy.resetForm("queryRef") + handleQuery() +} + +/** 澶氶�夋閫変腑鏁版嵁 */ +function handleSelectionChange(selection) { + ids.value = selection.map(item => item.configId) + single.value = selection.length != 1 + multiple.value = !selection.length +} + +/** 鏂板鎸夐挳鎿嶄綔 */ +function handleAdd() { + reset() + open.value = true + title.value = "娣诲姞鍙傛暟" +} + +/** 淇敼鎸夐挳鎿嶄綔 */ +function handleUpdate(row) { + reset() + const configId = row.configId || ids.value + getConfig(configId).then(response => { + form.value = response.data + open.value = true + title.value = "淇敼鍙傛暟" + }) +} + +/** 鎻愪氦鎸夐挳 */ +function submitForm() { + proxy.$refs["configRef"].validate(valid => { + if (valid) { + if (form.value.configId != undefined) { + updateConfig(form.value).then(response => { + proxy.$modal.msgSuccess("淇敼鎴愬姛") + open.value = false + getList() + }) + } else { + addConfig(form.value).then(response => { + proxy.$modal.msgSuccess("鏂板鎴愬姛") + open.value = false + getList() + }) + } + } + }) +} + +/** 鍒犻櫎鎸夐挳鎿嶄綔 */ +function handleDelete(row) { + const configIds = row.configId || ids.value + proxy.$modal.confirm('鏄惁纭鍒犻櫎鍙傛暟缂栧彿涓�"' + configIds + '"鐨勬暟鎹」锛�').then(function () { + return delConfig(configIds) + }).then(() => { + getList() + proxy.$modal.msgSuccess("鍒犻櫎鎴愬姛") + }).catch(() => {}) +} + +/** 瀵煎嚭鎸夐挳鎿嶄綔 */ +function handleExport() { + proxy.download("system/config/export", { + ...queryParams.value + }, `config_${new Date().getTime()}.xlsx`) +} + +/** 鍒锋柊缂撳瓨鎸夐挳鎿嶄綔 */ +function handleRefreshCache() { + refreshCache().then(() => { + proxy.$modal.msgSuccess("鍒锋柊缂撳瓨鎴愬姛") + }) +} + +getList() +</script> diff --git a/src/views/system/dept/index.vue b/src/views/system/dept/index.vue new file mode 100644 index 0000000..c82e24d --- /dev/null +++ b/src/views/system/dept/index.vue @@ -0,0 +1,283 @@ +<template> + <div class="app-container"> + <el-form :model="queryParams" ref="queryRef" :inline="true" v-show="showSearch"> + <el-form-item label="閮ㄩ棬鍚嶇О" prop="deptName"> + <el-input + v-model="queryParams.deptName" + placeholder="璇疯緭鍏ラ儴闂ㄥ悕绉�" + clearable + style="width: 200px" + @keyup.enter="handleQuery" + /> + </el-form-item> + <el-form-item label="鐘舵��" prop="status"> + <el-select v-model="queryParams.status" placeholder="閮ㄩ棬鐘舵��" clearable style="width: 200px"> + <el-option + v-for="dict in sys_normal_disable" + :key="dict.value" + :label="dict.label" + :value="dict.value" + /> + </el-select> + </el-form-item> + <el-form-item> + <el-button type="primary" icon="Search" @click="handleQuery">鎼滅储</el-button> + <el-button icon="Refresh" @click="resetQuery">閲嶇疆</el-button> + </el-form-item> + </el-form> + + <el-row :gutter="10" class="mb8"> + <el-col :span="1.5"> + <el-button + type="primary" + plain + icon="Plus" + @click="handleAdd" + v-hasPermi="['system:dept:add']" + >鏂板</el-button> + </el-col> + <el-col :span="1.5"> + <el-button + type="info" + plain + icon="Sort" + @click="toggleExpandAll" + >灞曞紑/鎶樺彔</el-button> + </el-col> + <right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar> + </el-row> + + <el-table + v-if="refreshTable" + v-loading="loading" + :data="deptList" + row-key="deptId" + :default-expand-all="isExpandAll" + :tree-props="{ children: 'children', hasChildren: 'hasChildren' }" + > + <el-table-column prop="deptName" label="閮ㄩ棬鍚嶇О" width="260"></el-table-column> + <el-table-column prop="orderNum" label="鎺掑簭" width="200"></el-table-column> + <el-table-column prop="status" label="鐘舵��" width="100"> + <template #default="scope"> + <dict-tag :options="sys_normal_disable" :value="scope.row.status" /> + </template> + </el-table-column> + <el-table-column label="鍒涘缓鏃堕棿" align="center" prop="createTime" width="200"> + <template #default="scope"> + <span>{{ parseTime(scope.row.createTime) }}</span> + </template> + </el-table-column> + <el-table-column label="鎿嶄綔" align="center" class-name="small-padding fixed-width"> + <template #default="scope"> + <el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['system:dept:edit']">淇敼</el-button> + <el-button link type="primary" icon="Plus" @click="handleAdd(scope.row)" v-hasPermi="['system:dept:add']">鏂板</el-button> + <el-button v-if="scope.row.parentId != 0" link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['system:dept:remove']">鍒犻櫎</el-button> + </template> + </el-table-column> + </el-table> + + <!-- 娣诲姞鎴栦慨鏀归儴闂ㄥ璇濇 --> + <el-dialog :title="title" v-model="open" width="600px" append-to-body> + <el-form ref="deptRef" :model="form" :rules="rules" label-width="80px"> + <el-row> + <el-col :span="24" v-if="form.parentId !== 0"> + <el-form-item label="涓婄骇閮ㄩ棬" prop="parentId"> + <el-tree-select + v-model="form.parentId" + :data="deptOptions" + :props="{ value: 'deptId', label: 'deptName', children: 'children' }" + value-key="deptId" + placeholder="閫夋嫨涓婄骇閮ㄩ棬" + check-strictly + /> + </el-form-item> + </el-col> + <el-col :span="12"> + <el-form-item label="閮ㄩ棬鍚嶇О" prop="deptName"> + <el-input v-model="form.deptName" placeholder="璇疯緭鍏ラ儴闂ㄥ悕绉�" /> + </el-form-item> + </el-col> + <el-col :span="12"> + <el-form-item label="鏄剧ず鎺掑簭" prop="orderNum"> + <el-input-number v-model="form.orderNum" controls-position="right" :min="0" /> + </el-form-item> + </el-col> + <el-col :span="12"> + <el-form-item label="璐熻矗浜�" prop="leader"> + <el-input v-model="form.leader" placeholder="璇疯緭鍏ヨ礋璐d汉" maxlength="20" /> + </el-form-item> + </el-col> + <el-col :span="12"> + <el-form-item label="鑱旂郴鐢佃瘽" prop="phone"> + <el-input v-model="form.phone" placeholder="璇疯緭鍏ヨ仈绯荤數璇�" maxlength="11" /> + </el-form-item> + </el-col> + <el-col :span="12"> + <el-form-item label="閭" prop="email"> + <el-input v-model="form.email" placeholder="璇疯緭鍏ラ偖绠�" maxlength="50" /> + </el-form-item> + </el-col> + <el-col :span="12"> + <el-form-item label="閮ㄩ棬鐘舵��"> + <el-radio-group v-model="form.status"> + <el-radio + v-for="dict in sys_normal_disable" + :key="dict.value" + :value="dict.value" + >{{ dict.label }}</el-radio> + </el-radio-group> + </el-form-item> + </el-col> + </el-row> + </el-form> + <template #footer> + <div class="dialog-footer"> + <el-button type="primary" @click="submitForm">纭� 瀹�</el-button> + <el-button @click="cancel">鍙� 娑�</el-button> + </div> + </template> + </el-dialog> + </div> +</template> + +<script setup name="Dept"> +import { listDept, getDept, delDept, addDept, updateDept, listDeptExcludeChild } from "@/api/system/dept" + +const { proxy } = getCurrentInstance() +const { sys_normal_disable } = proxy.useDict("sys_normal_disable") + +const deptList = ref([]) +const open = ref(false) +const loading = ref(true) +const showSearch = ref(true) +const title = ref("") +const deptOptions = ref([]) +const isExpandAll = ref(true) +const refreshTable = ref(true) + +const data = reactive({ + form: {}, + queryParams: { + deptName: undefined, + status: undefined + }, + rules: { + parentId: [{ required: true, message: "涓婄骇閮ㄩ棬涓嶈兘涓虹┖", trigger: "blur" }], + deptName: [{ required: true, message: "閮ㄩ棬鍚嶇О涓嶈兘涓虹┖", trigger: "blur" }], + orderNum: [{ required: true, message: "鏄剧ず鎺掑簭涓嶈兘涓虹┖", trigger: "blur" }], + email: [{ type: "email", message: "璇疯緭鍏ユ纭殑閭鍦板潃", trigger: ["blur", "change"] }], + phone: [{ pattern: /^1[3|4|5|6|7|8|9][0-9]\d{8}$/, message: "璇疯緭鍏ユ纭殑鎵嬫満鍙风爜", trigger: "blur" }] + }, +}) + +const { queryParams, form, rules } = toRefs(data) + +/** 鏌ヨ閮ㄩ棬鍒楄〃 */ +function getList() { + loading.value = true + listDept(queryParams.value).then(response => { + deptList.value = proxy.handleTree(response.data, "deptId") + loading.value = false + }) +} + +/** 鍙栨秷鎸夐挳 */ +function cancel() { + open.value = false + reset() +} + +/** 琛ㄥ崟閲嶇疆 */ +function reset() { + form.value = { + deptId: undefined, + parentId: undefined, + deptName: undefined, + orderNum: 0, + leader: undefined, + phone: undefined, + email: undefined, + status: "0" + } + proxy.resetForm("deptRef") +} + +/** 鎼滅储鎸夐挳鎿嶄綔 */ +function handleQuery() { + getList() +} + +/** 閲嶇疆鎸夐挳鎿嶄綔 */ +function resetQuery() { + proxy.resetForm("queryRef") + handleQuery() +} + +/** 鏂板鎸夐挳鎿嶄綔 */ +function handleAdd(row) { + reset() + listDept().then(response => { + deptOptions.value = proxy.handleTree(response.data, "deptId") + }) + if (row != undefined) { + form.value.parentId = row.deptId + } + open.value = true + title.value = "娣诲姞閮ㄩ棬" +} + +/** 灞曞紑/鎶樺彔鎿嶄綔 */ +function toggleExpandAll() { + refreshTable.value = false + isExpandAll.value = !isExpandAll.value + nextTick(() => { + refreshTable.value = true + }) +} + +/** 淇敼鎸夐挳鎿嶄綔 */ +function handleUpdate(row) { + reset() + listDeptExcludeChild(row.deptId).then(response => { + deptOptions.value = proxy.handleTree(response.data, "deptId") + }) + getDept(row.deptId).then(response => { + form.value = response.data + open.value = true + title.value = "淇敼閮ㄩ棬" + }) +} + +/** 鎻愪氦鎸夐挳 */ +function submitForm() { + proxy.$refs["deptRef"].validate(valid => { + if (valid) { + if (form.value.deptId != undefined) { + updateDept(form.value).then(response => { + proxy.$modal.msgSuccess("淇敼鎴愬姛") + open.value = false + getList() + }) + } else { + addDept(form.value).then(response => { + proxy.$modal.msgSuccess("鏂板鎴愬姛") + open.value = false + getList() + }) + } + } + }) +} + +/** 鍒犻櫎鎸夐挳鎿嶄綔 */ +function handleDelete(row) { + proxy.$modal.confirm('鏄惁纭鍒犻櫎鍚嶇О涓�"' + row.deptName + '"鐨勬暟鎹」?').then(function() { + return delDept(row.deptId) + }).then(() => { + getList() + proxy.$modal.msgSuccess("鍒犻櫎鎴愬姛") + }).catch(() => {}) +} + +getList() +</script> diff --git a/src/views/system/dict/data.vue b/src/views/system/dict/data.vue new file mode 100644 index 0000000..4869f94 --- /dev/null +++ b/src/views/system/dict/data.vue @@ -0,0 +1,362 @@ +<template> + <div class="app-container"> + <el-form :model="queryParams" ref="queryRef" :inline="true" v-show="showSearch"> + <el-form-item label="瀛楀吀鍚嶇О" prop="dictType"> + <el-select v-model="queryParams.dictType" style="width: 200px"> + <el-option + v-for="item in typeOptions" + :key="item.dictId" + :label="item.dictName" + :value="item.dictType" + /> + </el-select> + </el-form-item> + <el-form-item label="瀛楀吀鏍囩" prop="dictLabel"> + <el-input + v-model="queryParams.dictLabel" + placeholder="璇疯緭鍏ュ瓧鍏告爣绛�" + clearable + style="width: 200px" + @keyup.enter="handleQuery" + /> + </el-form-item> + <el-form-item label="鐘舵��" prop="status"> + <el-select v-model="queryParams.status" placeholder="鏁版嵁鐘舵��" clearable style="width: 200px"> + <el-option + v-for="dict in sys_normal_disable" + :key="dict.value" + :label="dict.label" + :value="dict.value" + /> + </el-select> + </el-form-item> + <el-form-item> + <el-button type="primary" icon="Search" @click="handleQuery">鎼滅储</el-button> + <el-button icon="Refresh" @click="resetQuery">閲嶇疆</el-button> + </el-form-item> + </el-form> + + <el-row :gutter="10" class="mb8"> + <el-col :span="1.5"> + <el-button + type="primary" + plain + icon="Plus" + @click="handleAdd" + v-hasPermi="['system:dict:add']" + >鏂板</el-button> + </el-col> + <el-col :span="1.5"> + <el-button + type="success" + plain + icon="Edit" + :disabled="single" + @click="handleUpdate" + v-hasPermi="['system:dict:edit']" + >淇敼</el-button> + </el-col> + <el-col :span="1.5"> + <el-button + type="danger" + plain + icon="Delete" + :disabled="multiple" + @click="handleDelete" + v-hasPermi="['system:dict:remove']" + >鍒犻櫎</el-button> + </el-col> + <el-col :span="1.5"> + <el-button + type="warning" + plain + icon="Download" + @click="handleExport" + v-hasPermi="['system:dict:export']" + >瀵煎嚭</el-button> + </el-col> + <el-col :span="1.5"> + <el-button + type="warning" + plain + icon="Close" + @click="handleClose" + >鍏抽棴</el-button> + </el-col> + <right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar> + </el-row> + + <el-table v-loading="loading" :data="dataList" @selection-change="handleSelectionChange"> + <el-table-column type="selection" width="55" align="center" /> + <el-table-column label="瀛楀吀缂栫爜" align="center" prop="dictCode" /> + <el-table-column label="瀛楀吀鏍囩" align="center" prop="dictLabel"> + <template #default="scope"> + <span v-if="(scope.row.listClass == '' || scope.row.listClass == 'default') && (scope.row.cssClass == '' || scope.row.cssClass == null)">{{ scope.row.dictLabel }}</span> + <el-tag v-else :type="scope.row.listClass == 'primary' ? '' : scope.row.listClass" :class="scope.row.cssClass">{{ scope.row.dictLabel }}</el-tag> + </template> + </el-table-column> + <el-table-column label="瀛楀吀閿��" align="center" prop="dictValue" /> + <el-table-column label="瀛楀吀鎺掑簭" align="center" prop="dictSort" /> + <el-table-column label="鐘舵��" align="center" prop="status"> + <template #default="scope"> + <dict-tag :options="sys_normal_disable" :value="scope.row.status" /> + </template> + </el-table-column> + <el-table-column label="澶囨敞" align="center" prop="remark" :show-overflow-tooltip="true" /> + <el-table-column label="鍒涘缓鏃堕棿" align="center" prop="createTime" width="180"> + <template #default="scope"> + <span>{{ parseTime(scope.row.createTime) }}</span> + </template> + </el-table-column> + <el-table-column label="鎿嶄綔" align="center" width="160" class-name="small-padding fixed-width"> + <template #default="scope"> + <el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['system:dict:edit']">淇敼</el-button> + <el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['system:dict:remove']">鍒犻櫎</el-button> + </template> + </el-table-column> + </el-table> + + <pagination + v-show="total > 0" + :total="total" + v-model:page="queryParams.pageNum" + v-model:limit="queryParams.pageSize" + @pagination="getList" + /> + + <!-- 娣诲姞鎴栦慨鏀瑰弬鏁伴厤缃璇濇 --> + <el-dialog :title="title" v-model="open" width="500px" append-to-body> + <el-form ref="dataRef" :model="form" :rules="rules" label-width="80px"> + <el-form-item label="瀛楀吀绫诲瀷"> + <el-input v-model="form.dictType" :disabled="true" /> + </el-form-item> + <el-form-item label="鏁版嵁鏍囩" prop="dictLabel"> + <el-input v-model="form.dictLabel" placeholder="璇疯緭鍏ユ暟鎹爣绛�" /> + </el-form-item> + <el-form-item label="鏁版嵁閿��" prop="dictValue"> + <el-input v-model="form.dictValue" placeholder="璇疯緭鍏ユ暟鎹敭鍊�" /> + </el-form-item> + <el-form-item label="鏍峰紡灞炴��" prop="cssClass"> + <el-input v-model="form.cssClass" placeholder="璇疯緭鍏ユ牱寮忓睘鎬�" /> + </el-form-item> + <el-form-item label="鏄剧ず鎺掑簭" prop="dictSort"> + <el-input-number v-model="form.dictSort" controls-position="right" :min="0" /> + </el-form-item> + <el-form-item label="鍥炴樉鏍峰紡" prop="listClass"> + <el-select v-model="form.listClass"> + <el-option + v-for="item in listClassOptions" + :key="item.value" + :label="item.label + '(' + item.value + ')'" + :value="item.value" + ></el-option> + </el-select> + </el-form-item> + <el-form-item label="鐘舵��" prop="status"> + <el-radio-group v-model="form.status"> + <el-radio + v-for="dict in sys_normal_disable" + :key="dict.value" + :value="dict.value" + >{{ dict.label }}</el-radio> + </el-radio-group> + </el-form-item> + <el-form-item label="澶囨敞" prop="remark"> + <el-input v-model="form.remark" type="textarea" placeholder="璇疯緭鍏ュ唴瀹�"></el-input> + </el-form-item> + </el-form> + <template #footer> + <div class="dialog-footer"> + <el-button type="primary" @click="submitForm">纭� 瀹�</el-button> + <el-button @click="cancel">鍙� 娑�</el-button> + </div> + </template> + </el-dialog> + </div> +</template> + +<script setup name="Data"> +import useDictStore from '@/store/modules/dict' +import { optionselect as getDictOptionselect, getType } from "@/api/system/dict/type" +import { listData, getData, delData, addData, updateData } from "@/api/system/dict/data" + +const { proxy } = getCurrentInstance() +const { sys_normal_disable } = proxy.useDict("sys_normal_disable") + +const dataList = 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 defaultDictType = ref("") +const typeOptions = ref([]) +const route = useRoute() +// 鏁版嵁鏍囩鍥炴樉鏍峰紡 +const listClassOptions = ref([ + { value: "default", label: "榛樿" }, + { value: "primary", label: "涓昏" }, + { value: "success", label: "鎴愬姛" }, + { value: "info", label: "淇℃伅" }, + { value: "warning", label: "璀﹀憡" }, + { value: "danger", label: "鍗遍櫓" } +]) + +const data = reactive({ + form: {}, + queryParams: { + pageNum: 1, + pageSize: 10, + dictType: undefined, + dictLabel: undefined, + status: undefined + }, + rules: { + dictLabel: [{ required: true, message: "鏁版嵁鏍囩涓嶈兘涓虹┖", trigger: "blur" }], + dictValue: [{ required: true, message: "鏁版嵁閿�间笉鑳戒负绌�", trigger: "blur" }], + dictSort: [{ required: true, message: "鏁版嵁椤哄簭涓嶈兘涓虹┖", trigger: "blur" }] + } +}) + +const { queryParams, form, rules } = toRefs(data) + +/** 鏌ヨ瀛楀吀绫诲瀷璇︾粏 */ +function getTypes(dictId) { + getType(dictId).then(response => { + queryParams.value.dictType = response.data.dictType + defaultDictType.value = response.data.dictType + getList() + }) +} + +/** 鏌ヨ瀛楀吀绫诲瀷鍒楄〃 */ +function getTypeList() { + getDictOptionselect().then(response => { + typeOptions.value = response.data + }) +} + +/** 鏌ヨ瀛楀吀鏁版嵁鍒楄〃 */ +function getList() { + loading.value = true + listData(queryParams.value).then(response => { + dataList.value = response.rows + total.value = response.total + loading.value = false + }) +} + +/** 鍙栨秷鎸夐挳 */ +function cancel() { + open.value = false + reset() +} + +/** 琛ㄥ崟閲嶇疆 */ +function reset() { + form.value = { + dictCode: undefined, + dictLabel: undefined, + dictValue: undefined, + cssClass: undefined, + listClass: "default", + dictSort: 0, + status: "0", + remark: undefined + } + proxy.resetForm("dataRef") +} + +/** 鎼滅储鎸夐挳鎿嶄綔 */ +function handleQuery() { + queryParams.value.pageNum = 1 + getList() +} + +/** 杩斿洖鎸夐挳鎿嶄綔 */ +function handleClose() { + const obj = { path: "/system/dict" } + proxy.$tab.closeOpenPage(obj) +} + +/** 閲嶇疆鎸夐挳鎿嶄綔 */ +function resetQuery() { + proxy.resetForm("queryRef") + queryParams.value.dictType = defaultDictType.value + handleQuery() +} + +/** 鏂板鎸夐挳鎿嶄綔 */ +function handleAdd() { + reset() + open.value = true + title.value = "娣诲姞瀛楀吀鏁版嵁" + form.value.dictType = queryParams.value.dictType +} + +/** 澶氶�夋閫変腑鏁版嵁 */ +function handleSelectionChange(selection) { + ids.value = selection.map(item => item.dictCode) + single.value = selection.length != 1 + multiple.value = !selection.length +} + +/** 淇敼鎸夐挳鎿嶄綔 */ +function handleUpdate(row) { + reset() + const dictCode = row.dictCode || ids.value + getData(dictCode).then(response => { + form.value = response.data + open.value = true + title.value = "淇敼瀛楀吀鏁版嵁" + }) +} + +/** 鎻愪氦鎸夐挳 */ +function submitForm() { + proxy.$refs["dataRef"].validate(valid => { + if (valid) { + if (form.value.dictCode != undefined) { + updateData(form.value).then(response => { + useDictStore().removeDict(queryParams.value.dictType) + proxy.$modal.msgSuccess("淇敼鎴愬姛") + open.value = false + getList() + }) + } else { + addData(form.value).then(response => { + useDictStore().removeDict(queryParams.value.dictType) + proxy.$modal.msgSuccess("鏂板鎴愬姛") + open.value = false + getList() + }) + } + } + }) +} + +/** 鍒犻櫎鎸夐挳鎿嶄綔 */ +function handleDelete(row) { + const dictCodes = row.dictCode || ids.value + proxy.$modal.confirm('鏄惁纭鍒犻櫎瀛楀吀缂栫爜涓�"' + dictCodes + '"鐨勬暟鎹」锛�').then(function() { + return delData(dictCodes) + }).then(() => { + getList() + proxy.$modal.msgSuccess("鍒犻櫎鎴愬姛") + useDictStore().removeDict(queryParams.value.dictType) + }).catch(() => {}) +} + +/** 瀵煎嚭鎸夐挳鎿嶄綔 */ +function handleExport() { + proxy.download("system/dict/data/export", { + ...queryParams.value + }, `dict_data_${new Date().getTime()}.xlsx`) +} + +getTypes(route.params && route.params.dictId) +getTypeList() +</script> diff --git a/src/views/system/dict/index.vue b/src/views/system/dict/index.vue new file mode 100644 index 0000000..3671b74 --- /dev/null +++ b/src/views/system/dict/index.vue @@ -0,0 +1,323 @@ +<template> + <div class="app-container"> + <el-form :model="queryParams" ref="queryRef" :inline="true" v-show="showSearch" label-width="68px"> + <el-form-item label="瀛楀吀鍚嶇О" prop="dictName"> + <el-input + v-model="queryParams.dictName" + placeholder="璇疯緭鍏ュ瓧鍏稿悕绉�" + clearable + style="width: 240px" + @keyup.enter="handleQuery" + /> + </el-form-item> + <el-form-item label="瀛楀吀绫诲瀷" prop="dictType"> + <el-input + v-model="queryParams.dictType" + placeholder="璇疯緭鍏ュ瓧鍏哥被鍨�" + clearable + style="width: 240px" + @keyup.enter="handleQuery" + /> + </el-form-item> + <el-form-item label="鐘舵��" prop="status"> + <el-select + v-model="queryParams.status" + placeholder="瀛楀吀鐘舵��" + clearable + style="width: 240px" + > + <el-option + v-for="dict in sys_normal_disable" + :key="dict.value" + :label="dict.label" + :value="dict.value" + /> + </el-select> + </el-form-item> + <el-form-item label="鍒涘缓鏃堕棿" style="width: 308px"> + <el-date-picker + v-model="dateRange" + value-format="YYYY-MM-DD" + type="daterange" + range-separator="-" + start-placeholder="寮�濮嬫棩鏈�" + end-placeholder="缁撴潫鏃ユ湡" + ></el-date-picker> + </el-form-item> + <el-form-item> + <el-button type="primary" icon="Search" @click="handleQuery">鎼滅储</el-button> + <el-button icon="Refresh" @click="resetQuery">閲嶇疆</el-button> + </el-form-item> + </el-form> + + <el-row :gutter="10" class="mb8"> + <el-col :span="1.5"> + <el-button + type="primary" + plain + icon="Plus" + @click="handleAdd" + v-hasPermi="['system:dict:add']" + >鏂板</el-button> + </el-col> + <el-col :span="1.5"> + <el-button + type="success" + plain + icon="Edit" + :disabled="single" + @click="handleUpdate" + v-hasPermi="['system:dict:edit']" + >淇敼</el-button> + </el-col> + <el-col :span="1.5"> + <el-button + type="danger" + plain + icon="Delete" + :disabled="multiple" + @click="handleDelete" + v-hasPermi="['system:dict:remove']" + >鍒犻櫎</el-button> + </el-col> + <el-col :span="1.5"> + <el-button + type="warning" + plain + icon="Download" + @click="handleExport" + v-hasPermi="['system:dict:export']" + >瀵煎嚭</el-button> + </el-col> + <el-col :span="1.5"> + <el-button + type="danger" + plain + icon="Refresh" + @click="handleRefreshCache" + v-hasPermi="['system:dict:remove']" + >鍒锋柊缂撳瓨</el-button> + </el-col> + <right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar> + </el-row> + + <el-table v-loading="loading" :data="typeList" @selection-change="handleSelectionChange"> + <el-table-column type="selection" width="55" align="center" /> + <el-table-column label="瀛楀吀缂栧彿" align="center" prop="dictId" /> + <el-table-column label="瀛楀吀鍚嶇О" align="center" prop="dictName" :show-overflow-tooltip="true"/> + <el-table-column label="瀛楀吀绫诲瀷" align="center" :show-overflow-tooltip="true"> + <template #default="scope"> + <router-link :to="'/system/dict-data/index/' + scope.row.dictId" class="link-type"> + <span>{{ scope.row.dictType }}</span> + </router-link> + </template> + </el-table-column> + <el-table-column label="鐘舵��" align="center" prop="status"> + <template #default="scope"> + <dict-tag :options="sys_normal_disable" :value="scope.row.status" /> + </template> + </el-table-column> + <el-table-column label="澶囨敞" align="center" prop="remark" :show-overflow-tooltip="true" /> + <el-table-column label="鍒涘缓鏃堕棿" align="center" prop="createTime" width="180"> + <template #default="scope"> + <span>{{ parseTime(scope.row.createTime) }}</span> + </template> + </el-table-column> + <el-table-column label="鎿嶄綔" align="center" width="160" class-name="small-padding fixed-width"> + <template #default="scope"> + <el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['system:dict:edit']">淇敼</el-button> + <el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['system:dict:remove']">鍒犻櫎</el-button> + </template> + </el-table-column> + </el-table> + + <pagination + v-show="total > 0" + :total="total" + v-model:page="queryParams.pageNum" + v-model:limit="queryParams.pageSize" + @pagination="getList" + /> + + <!-- 娣诲姞鎴栦慨鏀瑰弬鏁伴厤缃璇濇 --> + <el-dialog :title="title" v-model="open" width="500px" append-to-body> + <el-form ref="dictRef" :model="form" :rules="rules" label-width="80px"> + <el-form-item label="瀛楀吀鍚嶇О" prop="dictName"> + <el-input v-model="form.dictName" placeholder="璇疯緭鍏ュ瓧鍏稿悕绉�" /> + </el-form-item> + <el-form-item label="瀛楀吀绫诲瀷" prop="dictType"> + <el-input v-model="form.dictType" placeholder="璇疯緭鍏ュ瓧鍏哥被鍨�" /> + </el-form-item> + <el-form-item label="鐘舵��" prop="status"> + <el-radio-group v-model="form.status"> + <el-radio + v-for="dict in sys_normal_disable" + :key="dict.value" + :value="dict.value" + >{{ dict.label }}</el-radio> + </el-radio-group> + </el-form-item> + <el-form-item label="澶囨敞" prop="remark"> + <el-input v-model="form.remark" type="textarea" placeholder="璇疯緭鍏ュ唴瀹�"></el-input> + </el-form-item> + </el-form> + <template #footer> + <div class="dialog-footer"> + <el-button type="primary" @click="submitForm">纭� 瀹�</el-button> + <el-button @click="cancel">鍙� 娑�</el-button> + </div> + </template> + </el-dialog> + </div> +</template> + +<script setup name="Dict"> +import useDictStore from '@/store/modules/dict' +import { listType, getType, delType, addType, updateType, refreshCache } from "@/api/system/dict/type" + +const { proxy } = getCurrentInstance() +const { sys_normal_disable } = proxy.useDict("sys_normal_disable") + +const typeList = 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 data = reactive({ + form: {}, + queryParams: { + pageNum: 1, + pageSize: 10, + dictName: undefined, + dictType: undefined, + status: undefined + }, + rules: { + dictName: [{ required: true, message: "瀛楀吀鍚嶇О涓嶈兘涓虹┖", trigger: "blur" }], + dictType: [{ required: true, message: "瀛楀吀绫诲瀷涓嶈兘涓虹┖", trigger: "blur" }] + }, +}) + +const { queryParams, form, rules } = toRefs(data) + +/** 鏌ヨ瀛楀吀绫诲瀷鍒楄〃 */ +function getList() { + loading.value = true + listType(proxy.addDateRange(queryParams.value, dateRange.value)).then(response => { + typeList.value = response.rows + total.value = response.total + loading.value = false + }) +} + +/** 鍙栨秷鎸夐挳 */ +function cancel() { + open.value = false + reset() +} + +/** 琛ㄥ崟閲嶇疆 */ +function reset() { + form.value = { + dictId: undefined, + dictName: undefined, + dictType: undefined, + status: "0", + remark: undefined + } + proxy.resetForm("dictRef") +} + +/** 鎼滅储鎸夐挳鎿嶄綔 */ +function handleQuery() { + queryParams.value.pageNum = 1 + getList() +} + +/** 閲嶇疆鎸夐挳鎿嶄綔 */ +function resetQuery() { + dateRange.value = [] + proxy.resetForm("queryRef") + handleQuery() +} + +/** 鏂板鎸夐挳鎿嶄綔 */ +function handleAdd() { + reset() + open.value = true + title.value = "娣诲姞瀛楀吀绫诲瀷" +} + +/** 澶氶�夋閫変腑鏁版嵁 */ +function handleSelectionChange(selection) { + ids.value = selection.map(item => item.dictId) + single.value = selection.length != 1 + multiple.value = !selection.length +} + +/** 淇敼鎸夐挳鎿嶄綔 */ +function handleUpdate(row) { + reset() + const dictId = row.dictId || ids.value + getType(dictId).then(response => { + form.value = response.data + open.value = true + title.value = "淇敼瀛楀吀绫诲瀷" + }) +} + +/** 鎻愪氦鎸夐挳 */ +function submitForm() { + proxy.$refs["dictRef"].validate(valid => { + if (valid) { + if (form.value.dictId != undefined) { + updateType(form.value).then(response => { + proxy.$modal.msgSuccess("淇敼鎴愬姛") + open.value = false + getList() + }) + } else { + addType(form.value).then(response => { + proxy.$modal.msgSuccess("鏂板鎴愬姛") + open.value = false + getList() + }) + } + } + }) +} + +/** 鍒犻櫎鎸夐挳鎿嶄綔 */ +function handleDelete(row) { + const dictIds = row.dictId || ids.value + proxy.$modal.confirm('鏄惁纭鍒犻櫎瀛楀吀缂栧彿涓�"' + dictIds + '"鐨勬暟鎹」锛�').then(function() { + return delType(dictIds) + }).then(() => { + getList() + proxy.$modal.msgSuccess("鍒犻櫎鎴愬姛") + }).catch(() => {}) +} + +/** 瀵煎嚭鎸夐挳鎿嶄綔 */ +function handleExport() { + proxy.download("system/dict/type/export", { + ...queryParams.value + }, `dict_${new Date().getTime()}.xlsx`) +} + +/** 鍒锋柊缂撳瓨鎸夐挳鎿嶄綔 */ +function handleRefreshCache() { + refreshCache().then(() => { + proxy.$modal.msgSuccess("鍒锋柊鎴愬姛") + useDictStore().cleanDict() + }) +} + +getList() +</script> diff --git a/src/views/system/menu/index.vue b/src/views/system/menu/index.vue new file mode 100644 index 0000000..ed41531 --- /dev/null +++ b/src/views/system/menu/index.vue @@ -0,0 +1,452 @@ +<template> + <div class="app-container"> + <el-form :model="queryParams" ref="queryRef" :inline="true" v-show="showSearch"> + <el-form-item label="鑿滃崟鍚嶇О" prop="menuName"> + <el-input + v-model="queryParams.menuName" + placeholder="璇疯緭鍏ヨ彍鍗曞悕绉�" + clearable + style="width: 200px" + @keyup.enter="handleQuery" + /> + </el-form-item> + <el-form-item label="鐘舵��" prop="status"> + <el-select v-model="queryParams.status" placeholder="鑿滃崟鐘舵��" clearable style="width: 200px"> + <el-option + v-for="dict in sys_normal_disable" + :key="dict.value" + :label="dict.label" + :value="dict.value" + /> + </el-select> + </el-form-item> + <el-form-item> + <el-button type="primary" icon="Search" @click="handleQuery">鎼滅储</el-button> + <el-button icon="Refresh" @click="resetQuery">閲嶇疆</el-button> + </el-form-item> + </el-form> + + <el-row :gutter="10" class="mb8"> + <el-col :span="1.5"> + <el-button + type="primary" + plain + icon="Plus" + @click="handleAdd" + v-hasPermi="['system:menu:add']" + >鏂板</el-button> + </el-col> + <el-col :span="1.5"> + <el-button + type="info" + plain + icon="Sort" + @click="toggleExpandAll" + >灞曞紑/鎶樺彔</el-button> + </el-col> + <right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar> + </el-row> + + <el-table + v-if="refreshTable" + v-loading="loading" + :data="menuList" + row-key="menuId" + :default-expand-all="isExpandAll" + :tree-props="{ children: 'children', hasChildren: 'hasChildren' }" + > + <el-table-column prop="menuName" label="鑿滃崟鍚嶇О" :show-overflow-tooltip="true" width="160"></el-table-column> + <el-table-column prop="icon" label="鍥炬爣" align="center" width="100"> + <template #default="scope"> + <svg-icon :icon-class="scope.row.icon" /> + </template> + </el-table-column> + <el-table-column prop="orderNum" label="鎺掑簭" width="60"></el-table-column> + <el-table-column prop="perms" label="鏉冮檺鏍囪瘑" :show-overflow-tooltip="true"></el-table-column> + <el-table-column prop="component" label="缁勪欢璺緞" :show-overflow-tooltip="true"></el-table-column> + <el-table-column prop="status" label="鐘舵��" width="80"> + <template #default="scope"> + <dict-tag :options="sys_normal_disable" :value="scope.row.status" /> + </template> + </el-table-column> + <el-table-column label="鍒涘缓鏃堕棿" align="center" width="160" prop="createTime"> + <template #default="scope"> + <span>{{ parseTime(scope.row.createTime) }}</span> + </template> + </el-table-column> + <el-table-column label="鎿嶄綔" align="center" width="210" class-name="small-padding fixed-width"> + <template #default="scope"> + <el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['system:menu:edit']">淇敼</el-button> + <el-button link type="primary" icon="Plus" @click="handleAdd(scope.row)" v-hasPermi="['system:menu:add']">鏂板</el-button> + <el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['system:menu:remove']">鍒犻櫎</el-button> + </template> + </el-table-column> + </el-table> + + <!-- 娣诲姞鎴栦慨鏀硅彍鍗曞璇濇 --> + <el-dialog :title="title" v-model="open" width="680px" append-to-body> + <el-form ref="menuRef" :model="form" :rules="rules" label-width="100px"> + <el-row> + <el-col :span="24"> + <el-form-item label="涓婄骇鑿滃崟"> + <el-tree-select + v-model="form.parentId" + :data="menuOptions" + :props="{ value: 'menuId', label: 'menuName', children: 'children' }" + value-key="menuId" + placeholder="閫夋嫨涓婄骇鑿滃崟" + check-strictly + /> + </el-form-item> + </el-col> + <el-col :span="24"> + <el-form-item label="鑿滃崟绫诲瀷" prop="menuType"> + <el-radio-group v-model="form.menuType"> + <el-radio value="M">鐩綍</el-radio> + <el-radio value="C">鑿滃崟</el-radio> + <el-radio value="F">鎸夐挳</el-radio> + </el-radio-group> + </el-form-item> + </el-col> + <el-col :span="12" v-if="form.menuType != 'F'"> + <el-form-item label="鑿滃崟鍥炬爣" prop="icon"> + <el-popover + placement="bottom-start" + :width="540" + trigger="click" + > + <template #reference> + <el-input v-model="form.icon" placeholder="鐐瑰嚮閫夋嫨鍥炬爣" @blur="showSelectIcon" readonly> + <template #prefix> + <svg-icon + v-if="form.icon" + :icon-class="form.icon" + class="el-input__icon" + style="height: 32px;width: 16px;" + /> + <el-icon v-else style="height: 32px;width: 16px;"><search /></el-icon> + </template> + </el-input> + </template> + <icon-select ref="iconSelectRef" @selected="selected" :active-icon="form.icon" /> + </el-popover> + </el-form-item> + </el-col> + <el-col :span="12"> + <el-form-item label="鏄剧ず鎺掑簭" prop="orderNum"> + <el-input-number v-model="form.orderNum" controls-position="right" :min="0" /> + </el-form-item> + </el-col> + <el-col :span="12"> + <el-form-item label="鑿滃崟鍚嶇О" prop="menuName"> + <el-input v-model="form.menuName" placeholder="璇疯緭鍏ヨ彍鍗曞悕绉�" /> + </el-form-item> + </el-col> + <el-col :span="12" v-if="form.menuType == 'C'"> + <el-form-item prop="routeName"> + <template #label> + <span> + <el-tooltip content="榛樿涓嶅~鍒欏拰璺敱鍦板潃鐩稿悓锛氬鍦板潃涓猴細`user`锛屽垯鍚嶇О涓篳User`锛堟敞鎰忥細鍥犱负router浼氬垹闄ゅ悕绉扮浉鍚岃矾鐢憋紝涓洪伩鍏嶅悕瀛楃殑鍐茬獊锛岀壒娈婃儏鍐典笅璇疯嚜瀹氫箟锛屼繚璇佸敮涓�鎬э級" placement="top"> + <el-icon><question-filled /></el-icon> + </el-tooltip> + 璺敱鍚嶇О + </span> + </template> + <el-input v-model="form.routeName" placeholder="璇疯緭鍏ヨ矾鐢卞悕绉�" /> + </el-form-item> + </el-col> + <el-col :span="12" v-if="form.menuType != 'F'"> + <el-form-item> + <template #label> + <span> + <el-tooltip content="閫夋嫨鏄閾惧垯璺敱鍦板潃闇�瑕佷互`http(s)://`寮�澶�" placement="top"> + <el-icon><question-filled /></el-icon> + </el-tooltip>鏄惁澶栭摼 + </span> + </template> + <el-radio-group v-model="form.isFrame"> + <el-radio value="0">鏄�</el-radio> + <el-radio value="1">鍚�</el-radio> + </el-radio-group> + </el-form-item> + </el-col> + <el-col :span="12" v-if="form.menuType != 'F'"> + <el-form-item prop="path"> + <template #label> + <span> + <el-tooltip content="璁块棶鐨勮矾鐢卞湴鍧�锛屽锛歚user`锛屽澶栫綉鍦板潃闇�鍐呴摼璁块棶鍒欎互`http(s)://`寮�澶�" placement="top"> + <el-icon><question-filled /></el-icon> + </el-tooltip> + 璺敱鍦板潃 + </span> + </template> + <el-input v-model="form.path" placeholder="璇疯緭鍏ヨ矾鐢卞湴鍧�" /> + </el-form-item> + </el-col> + <el-col :span="12" v-if="form.menuType == 'C'"> + <el-form-item prop="component"> + <template #label> + <span> + <el-tooltip content="璁块棶鐨勭粍浠惰矾寰勶紝濡傦細`system/user/index`锛岄粯璁ゅ湪`views`鐩綍涓�" placement="top"> + <el-icon><question-filled /></el-icon> + </el-tooltip> + 缁勪欢璺緞 + </span> + </template> + <el-input v-model="form.component" placeholder="璇疯緭鍏ョ粍浠惰矾寰�" /> + </el-form-item> + </el-col> + <el-col :span="12" v-if="form.menuType != 'M'"> + <el-form-item> + <el-input v-model="form.perms" placeholder="璇疯緭鍏ユ潈闄愭爣璇�" maxlength="100" /> + <template #label> + <span> + <el-tooltip content="鎺у埗鍣ㄤ腑瀹氫箟鐨勬潈闄愬瓧绗︼紝濡傦細@PreAuthorize(`@ss.hasPermi('system:user:list')`)" placement="top"> + <el-icon><question-filled /></el-icon> + </el-tooltip> + 鏉冮檺瀛楃 + </span> + </template> + </el-form-item> + </el-col> + <el-col :span="12" v-if="form.menuType == 'C'"> + <el-form-item> + <el-input v-model="form.query" placeholder="璇疯緭鍏ヨ矾鐢卞弬鏁�" maxlength="255" /> + <template #label> + <span> + <el-tooltip content='璁块棶璺敱鐨勯粯璁や紶閫掑弬鏁帮紝濡傦細`{"id": 1, "name": "ry"}`' placement="top"> + <el-icon><question-filled /></el-icon> + </el-tooltip> + 璺敱鍙傛暟 + </span> + </template> + </el-form-item> + </el-col> + <el-col :span="12" v-if="form.menuType == 'C'"> + <el-form-item> + <template #label> + <span> + <el-tooltip content="閫夋嫨鏄垯浼氳`keep-alive`缂撳瓨锛岄渶瑕佸尮閰嶇粍浠剁殑`name`鍜屽湴鍧�淇濇寔涓�鑷�" placement="top"> + <el-icon><question-filled /></el-icon> + </el-tooltip> + 鏄惁缂撳瓨 + </span> + </template> + <el-radio-group v-model="form.isCache"> + <el-radio value="0">缂撳瓨</el-radio> + <el-radio value="1">涓嶇紦瀛�</el-radio> + </el-radio-group> + </el-form-item> + </el-col> + <el-col :span="12" v-if="form.menuType != 'F'"> + <el-form-item> + <template #label> + <span> + <el-tooltip content="閫夋嫨闅愯棌鍒欒矾鐢卞皢涓嶄細鍑虹幇鍦ㄤ晶杈规爮锛屼絾浠嶇劧鍙互璁块棶" placement="top"> + <el-icon><question-filled /></el-icon> + </el-tooltip> + 鏄剧ず鐘舵�� + </span> + </template> + <el-radio-group v-model="form.visible"> + <el-radio + v-for="dict in sys_show_hide" + :key="dict.value" + :value="dict.value" + >{{ dict.label }}</el-radio> + </el-radio-group> + </el-form-item> + </el-col> + <el-col :span="12"> + <el-form-item> + <template #label> + <span> + <el-tooltip content="閫夋嫨鍋滅敤鍒欒矾鐢卞皢涓嶄細鍑虹幇鍦ㄤ晶杈规爮锛屼篃涓嶈兘琚闂�" placement="top"> + <el-icon><question-filled /></el-icon> + </el-tooltip> + 鑿滃崟鐘舵�� + </span> + </template> + <el-radio-group v-model="form.status"> + <el-radio + v-for="dict in sys_normal_disable" + :key="dict.value" + :value="dict.value" + >{{ dict.label }}</el-radio> + </el-radio-group> + </el-form-item> + </el-col> + </el-row> + </el-form> + <template #footer> + <div class="dialog-footer"> + <el-button type="primary" @click="submitForm">纭� 瀹�</el-button> + <el-button @click="cancel">鍙� 娑�</el-button> + </div> + </template> + </el-dialog> + </div> +</template> + +<script setup name="Menu"> +import { addMenu, delMenu, getMenu, listMenu, updateMenu } from "@/api/system/menu" +import SvgIcon from "@/components/SvgIcon" +import IconSelect from "@/components/IconSelect" + +const { proxy } = getCurrentInstance() +const { sys_show_hide, sys_normal_disable } = proxy.useDict("sys_show_hide", "sys_normal_disable") + +const menuList = ref([]) +const open = ref(false) +const loading = ref(true) +const showSearch = ref(true) +const title = ref("") +const menuOptions = ref([]) +const isExpandAll = ref(false) +const refreshTable = ref(true) +const iconSelectRef = ref(null) + +const data = reactive({ + form: {}, + queryParams: { + menuName: undefined, + visible: undefined + }, + rules: { + menuName: [{ required: true, message: "鑿滃崟鍚嶇О涓嶈兘涓虹┖", trigger: "blur" }], + orderNum: [{ required: true, message: "鑿滃崟椤哄簭涓嶈兘涓虹┖", trigger: "blur" }], + path: [{ required: true, message: "璺敱鍦板潃涓嶈兘涓虹┖", trigger: "blur" }] + }, +}) + +const { queryParams, form, rules } = toRefs(data) + +/** 鏌ヨ鑿滃崟鍒楄〃 */ +function getList() { + loading.value = true + listMenu(queryParams.value).then(response => { + menuList.value = proxy.handleTree(response.data, "menuId") + loading.value = false + }) +} + +/** 鏌ヨ鑿滃崟涓嬫媺鏍戠粨鏋� */ +function getTreeselect() { + menuOptions.value = [] + listMenu().then(response => { + const menu = { menuId: 0, menuName: "涓荤被鐩�", children: [] } + menu.children = proxy.handleTree(response.data, "menuId") + menuOptions.value.push(menu) + }) +} + +/** 鍙栨秷鎸夐挳 */ +function cancel() { + open.value = false + reset() +} + +/** 琛ㄥ崟閲嶇疆 */ +function reset() { + form.value = { + menuId: undefined, + parentId: 0, + menuName: undefined, + icon: undefined, + menuType: "M", + orderNum: undefined, + isFrame: "1", + isCache: "0", + visible: "0", + status: "0" + } + proxy.resetForm("menuRef") +} + +/** 灞曠ず涓嬫媺鍥炬爣 */ +function showSelectIcon() { + iconSelectRef.value.reset() +} + +/** 閫夋嫨鍥炬爣 */ +function selected(name) { + form.value.icon = name +} + +/** 鎼滅储鎸夐挳鎿嶄綔 */ +function handleQuery() { + getList() +} + +/** 閲嶇疆鎸夐挳鎿嶄綔 */ +function resetQuery() { + proxy.resetForm("queryRef") + handleQuery() +} + +/** 鏂板鎸夐挳鎿嶄綔 */ +function handleAdd(row) { + reset() + getTreeselect() + if (row != null && row.menuId) { + form.value.parentId = row.menuId + } else { + form.value.parentId = 0 + } + open.value = true + title.value = "娣诲姞鑿滃崟" +} + +/** 灞曞紑/鎶樺彔鎿嶄綔 */ +function toggleExpandAll() { + refreshTable.value = false + isExpandAll.value = !isExpandAll.value + nextTick(() => { + refreshTable.value = true + }) +} + +/** 淇敼鎸夐挳鎿嶄綔 */ +async function handleUpdate(row) { + reset() + await getTreeselect() + getMenu(row.menuId).then(response => { + form.value = response.data + open.value = true + title.value = "淇敼鑿滃崟" + }) +} + +/** 鎻愪氦鎸夐挳 */ +function submitForm() { + proxy.$refs["menuRef"].validate(valid => { + if (valid) { + if (form.value.menuId != undefined) { + updateMenu(form.value).then(response => { + proxy.$modal.msgSuccess("淇敼鎴愬姛") + open.value = false + getList() + }) + } else { + addMenu(form.value).then(response => { + proxy.$modal.msgSuccess("鏂板鎴愬姛") + open.value = false + getList() + }) + } + } + }) +} + +/** 鍒犻櫎鎸夐挳鎿嶄綔 */ +function handleDelete(row) { + proxy.$modal.confirm('鏄惁纭鍒犻櫎鍚嶇О涓�"' + row.menuName + '"鐨勬暟鎹」?').then(function() { + return delMenu(row.menuId) + }).then(() => { + getList() + proxy.$modal.msgSuccess("鍒犻櫎鎴愬姛") + }).catch(() => {}) +} + +getList() +</script> diff --git a/src/views/system/notice/index.vue b/src/views/system/notice/index.vue new file mode 100644 index 0000000..7bb7c11 --- /dev/null +++ b/src/views/system/notice/index.vue @@ -0,0 +1,292 @@ +<template> + <div class="app-container"> + <el-form :model="queryParams" ref="queryRef" :inline="true" v-show="showSearch"> + <el-form-item label="鍏憡鏍囬" prop="noticeTitle"> + <el-input + v-model="queryParams.noticeTitle" + placeholder="璇疯緭鍏ュ叕鍛婃爣棰�" + clearable + style="width: 200px" + @keyup.enter="handleQuery" + /> + </el-form-item> + <el-form-item label="鎿嶄綔浜哄憳" prop="createBy"> + <el-input + v-model="queryParams.createBy" + placeholder="璇疯緭鍏ユ搷浣滀汉鍛�" + clearable + style="width: 200px" + @keyup.enter="handleQuery" + /> + </el-form-item> + <el-form-item label="绫诲瀷" prop="noticeType"> + <el-select v-model="queryParams.noticeType" placeholder="鍏憡绫诲瀷" clearable style="width: 200px"> + <el-option + v-for="dict in sys_notice_type" + :key="dict.value" + :label="dict.label" + :value="dict.value" + /> + </el-select> + </el-form-item> + <el-form-item> + <el-button type="primary" icon="Search" @click="handleQuery">鎼滅储</el-button> + <el-button icon="Refresh" @click="resetQuery">閲嶇疆</el-button> + </el-form-item> + </el-form> + + <el-row :gutter="10" class="mb8"> + <el-col :span="1.5"> + <el-button + type="primary" + plain + icon="Plus" + @click="handleAdd" + v-hasPermi="['system:notice:add']" + >鏂板</el-button> + </el-col> + <el-col :span="1.5"> + <el-button + type="success" + plain + icon="Edit" + :disabled="single" + @click="handleUpdate" + v-hasPermi="['system:notice:edit']" + >淇敼</el-button> + </el-col> + <el-col :span="1.5"> + <el-button + type="danger" + plain + icon="Delete" + :disabled="multiple" + @click="handleDelete" + v-hasPermi="['system:notice:remove']" + >鍒犻櫎</el-button> + </el-col> + <right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar> + </el-row> + + <el-table v-loading="loading" :data="noticeList" @selection-change="handleSelectionChange"> + <el-table-column type="selection" width="55" align="center" /> + <el-table-column label="搴忓彿" align="center" prop="noticeId" width="100" /> + <el-table-column + label="鍏憡鏍囬" + align="center" + prop="noticeTitle" + :show-overflow-tooltip="true" + /> + <el-table-column label="鍏憡绫诲瀷" align="center" prop="noticeType" width="100"> + <template #default="scope"> + <dict-tag :options="sys_notice_type" :value="scope.row.noticeType" /> + </template> + </el-table-column> + <el-table-column label="鐘舵��" align="center" prop="status" width="100"> + <template #default="scope"> + <dict-tag :options="sys_notice_status" :value="scope.row.status" /> + </template> + </el-table-column> + <el-table-column label="鍒涘缓鑰�" align="center" prop="createBy" width="100" /> + <el-table-column label="鍒涘缓鏃堕棿" align="center" prop="createTime" width="100"> + <template #default="scope"> + <span>{{ parseTime(scope.row.createTime, '{y}-{m}-{d}') }}</span> + </template> + </el-table-column> + <el-table-column label="鎿嶄綔" align="center" class-name="small-padding fixed-width"> + <template #default="scope"> + <el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['system:notice:edit']">淇敼</el-button> + <el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['system:notice:remove']" >鍒犻櫎</el-button> + </template> + </el-table-column> + </el-table> + + <pagination + v-show="total > 0" + :total="total" + v-model:page="queryParams.pageNum" + v-model:limit="queryParams.pageSize" + @pagination="getList" + /> + + <!-- 娣诲姞鎴栦慨鏀瑰叕鍛婂璇濇 --> + <el-dialog :title="title" v-model="open" width="780px" append-to-body> + <el-form ref="noticeRef" :model="form" :rules="rules" label-width="80px"> + <el-row> + <el-col :span="12"> + <el-form-item label="鍏憡鏍囬" prop="noticeTitle"> + <el-input v-model="form.noticeTitle" placeholder="璇疯緭鍏ュ叕鍛婃爣棰�" /> + </el-form-item> + </el-col> + <el-col :span="12"> + <el-form-item label="鍏憡绫诲瀷" prop="noticeType"> + <el-select v-model="form.noticeType" placeholder="璇烽�夋嫨"> + <el-option + v-for="dict in sys_notice_type" + :key="dict.value" + :label="dict.label" + :value="dict.value" + ></el-option> + </el-select> + </el-form-item> + </el-col> + <el-col :span="24"> + <el-form-item label="鐘舵��"> + <el-radio-group v-model="form.status"> + <el-radio + v-for="dict in sys_notice_status" + :key="dict.value" + :value="dict.value" + >{{ dict.label }}</el-radio> + </el-radio-group> + </el-form-item> + </el-col> + <el-col :span="24"> + <el-form-item label="鍐呭"> + <editor v-model="form.noticeContent" :min-height="192"/> + </el-form-item> + </el-col> + </el-row> + </el-form> + <template #footer> + <div class="dialog-footer"> + <el-button type="primary" @click="submitForm">纭� 瀹�</el-button> + <el-button @click="cancel">鍙� 娑�</el-button> + </div> + </template> + </el-dialog> + </div> +</template> + +<script setup name="Notice"> +import { listNotice, getNotice, delNotice, addNotice, updateNotice } from "@/api/system/notice" + +const { proxy } = getCurrentInstance() +const { sys_notice_status, sys_notice_type } = proxy.useDict("sys_notice_status", "sys_notice_type") + +const noticeList = 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 data = reactive({ + form: {}, + queryParams: { + pageNum: 1, + pageSize: 10, + noticeTitle: undefined, + createBy: undefined, + status: undefined + }, + rules: { + noticeTitle: [{ required: true, message: "鍏憡鏍囬涓嶈兘涓虹┖", trigger: "blur" }], + noticeType: [{ required: true, message: "鍏憡绫诲瀷涓嶈兘涓虹┖", trigger: "change" }] + }, +}) + +const { queryParams, form, rules } = toRefs(data) + +/** 鏌ヨ鍏憡鍒楄〃 */ +function getList() { + loading.value = true + listNotice(queryParams.value).then(response => { + noticeList.value = response.rows + total.value = response.total + loading.value = false + }) +} + +/** 鍙栨秷鎸夐挳 */ +function cancel() { + open.value = false + reset() +} + +/** 琛ㄥ崟閲嶇疆 */ +function reset() { + form.value = { + noticeId: undefined, + noticeTitle: undefined, + noticeType: undefined, + noticeContent: undefined, + status: "0" + } + proxy.resetForm("noticeRef") +} + +/** 鎼滅储鎸夐挳鎿嶄綔 */ +function handleQuery() { + queryParams.value.pageNum = 1 + getList() +} + +/** 閲嶇疆鎸夐挳鎿嶄綔 */ +function resetQuery() { + proxy.resetForm("queryRef") + handleQuery() +} + +/** 澶氶�夋閫変腑鏁版嵁 */ +function handleSelectionChange(selection) { + ids.value = selection.map(item => item.noticeId) + single.value = selection.length != 1 + multiple.value = !selection.length +} + +/** 鏂板鎸夐挳鎿嶄綔 */ +function handleAdd() { + reset() + open.value = true + title.value = "娣诲姞鍏憡" +} + +/**淇敼鎸夐挳鎿嶄綔 */ +function handleUpdate(row) { + reset() + const noticeId = row.noticeId || ids.value + getNotice(noticeId).then(response => { + form.value = response.data + open.value = true + title.value = "淇敼鍏憡" + }) +} + +/** 鎻愪氦鎸夐挳 */ +function submitForm() { + proxy.$refs["noticeRef"].validate(valid => { + if (valid) { + if (form.value.noticeId != undefined) { + updateNotice(form.value).then(response => { + proxy.$modal.msgSuccess("淇敼鎴愬姛") + open.value = false + getList() + }) + } else { + addNotice(form.value).then(response => { + proxy.$modal.msgSuccess("鏂板鎴愬姛") + open.value = false + getList() + }) + } + } + }) +} + +/** 鍒犻櫎鎸夐挳鎿嶄綔 */ +function handleDelete(row) { + const noticeIds = row.noticeId || ids.value + proxy.$modal.confirm('鏄惁纭鍒犻櫎鍏憡缂栧彿涓�"' + noticeIds + '"鐨勬暟鎹」锛�').then(function() { + return delNotice(noticeIds) + }).then(() => { + getList() + proxy.$modal.msgSuccess("鍒犻櫎鎴愬姛") + }).catch(() => {}) +} + +getList() +</script> diff --git a/src/views/system/post/index.vue b/src/views/system/post/index.vue new file mode 100644 index 0000000..9e760d1 --- /dev/null +++ b/src/views/system/post/index.vue @@ -0,0 +1,287 @@ +<template> + <div class="app-container"> + <el-form :model="queryParams" ref="queryRef" :inline="true" v-show="showSearch"> + <el-form-item label="宀椾綅缂栫爜" prop="postCode"> + <el-input + v-model="queryParams.postCode" + placeholder="璇疯緭鍏ュ矖浣嶇紪鐮�" + clearable + style="width: 200px" + @keyup.enter="handleQuery" + /> + </el-form-item> + <el-form-item label="宀椾綅鍚嶇О" prop="postName"> + <el-input + v-model="queryParams.postName" + placeholder="璇疯緭鍏ュ矖浣嶅悕绉�" + clearable + style="width: 200px" + @keyup.enter="handleQuery" + /> + </el-form-item> + <el-form-item label="鐘舵��" prop="status"> + <el-select v-model="queryParams.status" placeholder="宀椾綅鐘舵��" clearable style="width: 200px"> + <el-option + v-for="dict in sys_normal_disable" + :key="dict.value" + :label="dict.label" + :value="dict.value" + /> + </el-select> + </el-form-item> + <el-form-item> + <el-button type="primary" icon="Search" @click="handleQuery">鎼滅储</el-button> + <el-button icon="Refresh" @click="resetQuery">閲嶇疆</el-button> + </el-form-item> + </el-form> + + <el-row :gutter="10" class="mb8"> + <el-col :span="1.5"> + <el-button + type="primary" + plain + icon="Plus" + @click="handleAdd" + v-hasPermi="['system:post:add']" + >鏂板</el-button> + </el-col> + <el-col :span="1.5"> + <el-button + type="success" + plain + icon="Edit" + :disabled="single" + @click="handleUpdate" + v-hasPermi="['system:post:edit']" + >淇敼</el-button> + </el-col> + <el-col :span="1.5"> + <el-button + type="danger" + plain + icon="Delete" + :disabled="multiple" + @click="handleDelete" + v-hasPermi="['system:post:remove']" + >鍒犻櫎</el-button> + </el-col> + <el-col :span="1.5"> + <el-button + type="warning" + plain + icon="Download" + @click="handleExport" + v-hasPermi="['system:post:export']" + >瀵煎嚭</el-button> + </el-col> + <right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar> + </el-row> + + <el-table v-loading="loading" :data="postList" @selection-change="handleSelectionChange"> + <el-table-column type="selection" width="55" align="center" /> + <el-table-column label="宀椾綅缂栧彿" align="center" prop="postId" /> + <el-table-column label="宀椾綅缂栫爜" align="center" prop="postCode" /> + <el-table-column label="宀椾綅鍚嶇О" align="center" prop="postName" /> + <el-table-column label="宀椾綅鎺掑簭" align="center" prop="postSort" /> + <el-table-column label="鐘舵��" align="center" prop="status"> + <template #default="scope"> + <dict-tag :options="sys_normal_disable" :value="scope.row.status" /> + </template> + </el-table-column> + <el-table-column label="鍒涘缓鏃堕棿" align="center" prop="createTime" width="180"> + <template #default="scope"> + <span>{{ parseTime(scope.row.createTime) }}</span> + </template> + </el-table-column> + <el-table-column label="鎿嶄綔" width="180" align="center" class-name="small-padding fixed-width"> + <template #default="scope"> + <el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['system:post:edit']">淇敼</el-button> + <el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['system:post:remove']">鍒犻櫎</el-button> + </template> + </el-table-column> + </el-table> + + <pagination + v-show="total > 0" + :total="total" + v-model:page="queryParams.pageNum" + v-model:limit="queryParams.pageSize" + @pagination="getList" + /> + + <!-- 娣诲姞鎴栦慨鏀瑰矖浣嶅璇濇 --> + <el-dialog :title="title" v-model="open" width="500px" append-to-body> + <el-form ref="postRef" :model="form" :rules="rules" label-width="80px"> + <el-form-item label="宀椾綅鍚嶇О" prop="postName"> + <el-input v-model="form.postName" placeholder="璇疯緭鍏ュ矖浣嶅悕绉�" /> + </el-form-item> + <el-form-item label="宀椾綅缂栫爜" prop="postCode"> + <el-input v-model="form.postCode" placeholder="璇疯緭鍏ョ紪鐮佸悕绉�" /> + </el-form-item> + <el-form-item label="宀椾綅椤哄簭" prop="postSort"> + <el-input-number v-model="form.postSort" controls-position="right" :min="0" /> + </el-form-item> + <el-form-item label="宀椾綅鐘舵��" prop="status"> + <el-radio-group v-model="form.status"> + <el-radio + v-for="dict in sys_normal_disable" + :key="dict.value" + :value="dict.value" + >{{ dict.label }}</el-radio> + </el-radio-group> + </el-form-item> + <el-form-item label="澶囨敞" prop="remark"> + <el-input v-model="form.remark" type="textarea" placeholder="璇疯緭鍏ュ唴瀹�" /> + </el-form-item> + </el-form> + <template #footer> + <div class="dialog-footer"> + <el-button type="primary" @click="submitForm">纭� 瀹�</el-button> + <el-button @click="cancel">鍙� 娑�</el-button> + </div> + </template> + </el-dialog> + </div> +</template> + +<script setup name="Post"> +import { listPost, addPost, delPost, getPost, updatePost } from "@/api/system/post" + +const { proxy } = getCurrentInstance() +const { sys_normal_disable } = proxy.useDict("sys_normal_disable") + +const postList = 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 data = reactive({ + form: {}, + queryParams: { + pageNum: 1, + pageSize: 10, + postCode: undefined, + postName: undefined, + status: undefined + }, + rules: { + postName: [{ required: true, message: "宀椾綅鍚嶇О涓嶈兘涓虹┖", trigger: "blur" }], + postCode: [{ required: true, message: "宀椾綅缂栫爜涓嶈兘涓虹┖", trigger: "blur" }], + postSort: [{ required: true, message: "宀椾綅椤哄簭涓嶈兘涓虹┖", trigger: "blur" }], + } +}) + +const { queryParams, form, rules } = toRefs(data) + +/** 鏌ヨ宀椾綅鍒楄〃 */ +function getList() { + loading.value = true + listPost(queryParams.value).then(response => { + postList.value = response.rows + total.value = response.total + loading.value = false + }) +} + +/** 鍙栨秷鎸夐挳 */ +function cancel() { + open.value = false + reset() +} + +/** 琛ㄥ崟閲嶇疆 */ +function reset() { + form.value = { + postId: undefined, + postCode: undefined, + postName: undefined, + postSort: 0, + status: "0", + remark: undefined + } + proxy.resetForm("postRef") +} + +/** 鎼滅储鎸夐挳鎿嶄綔 */ +function handleQuery() { + queryParams.value.pageNum = 1 + getList() +} + +/** 閲嶇疆鎸夐挳鎿嶄綔 */ +function resetQuery() { + proxy.resetForm("queryRef") + handleQuery() +} + +/** 澶氶�夋閫変腑鏁版嵁 */ +function handleSelectionChange(selection) { + ids.value = selection.map(item => item.postId) + single.value = selection.length != 1 + multiple.value = !selection.length +} + +/** 鏂板鎸夐挳鎿嶄綔 */ +function handleAdd() { + reset() + open.value = true + title.value = "娣诲姞宀椾綅" +} + +/** 淇敼鎸夐挳鎿嶄綔 */ +function handleUpdate(row) { + reset() + const postId = row.postId || ids.value + getPost(postId).then(response => { + form.value = response.data + open.value = true + title.value = "淇敼宀椾綅" + }) +} + +/** 鎻愪氦鎸夐挳 */ +function submitForm() { + proxy.$refs["postRef"].validate(valid => { + if (valid) { + if (form.value.postId != undefined) { + updatePost(form.value).then(response => { + proxy.$modal.msgSuccess("淇敼鎴愬姛") + open.value = false + getList() + }) + } else { + addPost(form.value).then(response => { + proxy.$modal.msgSuccess("鏂板鎴愬姛") + open.value = false + getList() + }) + } + } + }) +} + +/** 鍒犻櫎鎸夐挳鎿嶄綔 */ +function handleDelete(row) { + const postIds = row.postId || ids.value + proxy.$modal.confirm('鏄惁纭鍒犻櫎宀椾綅缂栧彿涓�"' + postIds + '"鐨勬暟鎹」锛�').then(function() { + return delPost(postIds) + }).then(() => { + getList() + proxy.$modal.msgSuccess("鍒犻櫎鎴愬姛") + }).catch(() => {}) +} + +/** 瀵煎嚭鎸夐挳鎿嶄綔 */ +function handleExport() { + proxy.download("system/post/export", { + ...queryParams.value + }, `post_${new Date().getTime()}.xlsx`) +} + +getList() +</script> diff --git a/src/views/system/role/authUser.vue b/src/views/system/role/authUser.vue new file mode 100644 index 0000000..c607f63 --- /dev/null +++ b/src/views/system/role/authUser.vue @@ -0,0 +1,179 @@ + +<template> + <div class="app-container"> + <el-form :model="queryParams" ref="queryRef" v-show="showSearch" :inline="true"> + <el-form-item label="鐢ㄦ埛鍚嶇О" prop="userName"> + <el-input + v-model="queryParams.userName" + placeholder="璇疯緭鍏ョ敤鎴峰悕绉�" + clearable + style="width: 240px" + @keyup.enter="handleQuery" + /> + </el-form-item> + <el-form-item label="鎵嬫満鍙风爜" prop="phonenumber"> + <el-input + v-model="queryParams.phonenumber" + placeholder="璇疯緭鍏ユ墜鏈哄彿鐮�" + clearable + style="width: 240px" + @keyup.enter="handleQuery" + /> + </el-form-item> + <el-form-item> + <el-button type="primary" icon="Search" @click="handleQuery">鎼滅储</el-button> + <el-button icon="Refresh" @click="resetQuery">閲嶇疆</el-button> + </el-form-item> + </el-form> + + <el-row :gutter="10" class="mb8"> + <el-col :span="1.5"> + <el-button + type="primary" + plain + icon="Plus" + @click="openSelectUser" + v-hasPermi="['system:role:add']" + >娣诲姞鐢ㄦ埛</el-button> + </el-col> + <el-col :span="1.5"> + <el-button + type="danger" + plain + icon="CircleClose" + :disabled="multiple" + @click="cancelAuthUserAll" + v-hasPermi="['system:role:remove']" + >鎵归噺鍙栨秷鎺堟潈</el-button> + </el-col> + <el-col :span="1.5"> + <el-button + type="warning" + plain + icon="Close" + @click="handleClose" + >鍏抽棴</el-button> + </el-col> + <right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar> + </el-row> + + <el-table v-loading="loading" :data="userList" @selection-change="handleSelectionChange"> + <el-table-column type="selection" width="55" align="center" /> + <el-table-column label="鐢ㄦ埛鍚嶇О" prop="userName" :show-overflow-tooltip="true" /> + <el-table-column label="鐢ㄦ埛鏄电О" prop="nickName" :show-overflow-tooltip="true" /> + <el-table-column label="閭" prop="email" :show-overflow-tooltip="true" /> + <el-table-column label="鎵嬫満" prop="phonenumber" :show-overflow-tooltip="true" /> + <el-table-column label="鐘舵��" align="center" prop="status"> + <template #default="scope"> + <dict-tag :options="sys_normal_disable" :value="scope.row.status" /> + </template> + </el-table-column> + <el-table-column label="鍒涘缓鏃堕棿" align="center" prop="createTime" width="180"> + <template #default="scope"> + <span>{{ parseTime(scope.row.createTime) }}</span> + </template> + </el-table-column> + <el-table-column label="鎿嶄綔" align="center" class-name="small-padding fixed-width"> + <template #default="scope"> + <el-button link type="primary" icon="CircleClose" @click="cancelAuthUser(scope.row)" v-hasPermi="['system:role:remove']">鍙栨秷鎺堟潈</el-button> + </template> + </el-table-column> + </el-table> + + <pagination + v-show="total > 0" + :total="total" + v-model:page="queryParams.pageNum" + v-model:limit="queryParams.pageSize" + @pagination="getList" + /> + <select-user ref="selectRef" :roleId="queryParams.roleId" @ok="handleQuery" /> + </div> +</template> + +<script setup name="AuthUser"> +import selectUser from "./selectUser" +import { allocatedUserList, authUserCancel, authUserCancelAll } from "@/api/system/role" + +const route = useRoute() +const { proxy } = getCurrentInstance() +const { sys_normal_disable } = proxy.useDict("sys_normal_disable") + +const userList = ref([]) +const loading = ref(true) +const showSearch = ref(true) +const multiple = ref(true) +const total = ref(0) +const userIds = ref([]) + +const queryParams = reactive({ + pageNum: 1, + pageSize: 10, + roleId: route.params.roleId, + userName: undefined, + phonenumber: undefined, +}) + +/** 鏌ヨ鎺堟潈鐢ㄦ埛鍒楄〃 */ +function getList() { + loading.value = true + allocatedUserList(queryParams).then(response => { + userList.value = response.rows + total.value = response.total + loading.value = false + }) +} + +/** 杩斿洖鎸夐挳 */ +function handleClose() { + const obj = { path: "/system/role" } + proxy.$tab.closeOpenPage(obj) +} + +/** 鎼滅储鎸夐挳鎿嶄綔 */ +function handleQuery() { + queryParams.pageNum = 1 + getList() +} + +/** 閲嶇疆鎸夐挳鎿嶄綔 */ +function resetQuery() { + proxy.resetForm("queryRef") + handleQuery() +} + +/** 澶氶�夋閫変腑鏁版嵁 */ +function handleSelectionChange(selection) { + userIds.value = selection.map(item => item.userId) + multiple.value = !selection.length +} + +/** 鎵撳紑鎺堟潈鐢ㄦ埛琛ㄥ脊绐� */ +function openSelectUser() { + proxy.$refs["selectRef"].show() +} + +/** 鍙栨秷鎺堟潈鎸夐挳鎿嶄綔 */ +function cancelAuthUser(row) { + proxy.$modal.confirm('纭瑕佸彇娑堣鐢ㄦ埛"' + row.userName + '"瑙掕壊鍚楋紵').then(function () { + return authUserCancel({ userId: row.userId, roleId: queryParams.roleId }) + }).then(() => { + getList() + proxy.$modal.msgSuccess("鍙栨秷鎺堟潈鎴愬姛") + }).catch(() => {}) +} + +/** 鎵归噺鍙栨秷鎺堟潈鎸夐挳鎿嶄綔 */ +function cancelAuthUserAll(row) { + const roleId = queryParams.roleId + const uIds = userIds.value.join(",") + proxy.$modal.confirm("鏄惁鍙栨秷閫変腑鐢ㄦ埛鎺堟潈鏁版嵁椤�?").then(function () { + return authUserCancelAll({ roleId: roleId, userIds: uIds }) + }).then(() => { + getList() + proxy.$modal.msgSuccess("鍙栨秷鎺堟潈鎴愬姛") + }).catch(() => {}) +} + +getList() +</script> diff --git a/src/views/system/role/index.vue b/src/views/system/role/index.vue new file mode 100644 index 0000000..793cba6 --- /dev/null +++ b/src/views/system/role/index.vue @@ -0,0 +1,584 @@ +<template> + <div class="app-container"> + <el-form :model="queryParams" ref="queryRef" v-show="showSearch" :inline="true" label-width="68px"> + <el-form-item label="瑙掕壊鍚嶇О" prop="roleName"> + <el-input + v-model="queryParams.roleName" + placeholder="璇疯緭鍏ヨ鑹插悕绉�" + clearable + style="width: 240px" + @keyup.enter="handleQuery" + /> + </el-form-item> + <el-form-item label="鏉冮檺瀛楃" prop="roleKey"> + <el-input + v-model="queryParams.roleKey" + placeholder="璇疯緭鍏ユ潈闄愬瓧绗�" + clearable + style="width: 240px" + @keyup.enter="handleQuery" + /> + </el-form-item> + <el-form-item label="鐘舵��" prop="status"> + <el-select + v-model="queryParams.status" + placeholder="瑙掕壊鐘舵��" + clearable + style="width: 240px" + > + <el-option + v-for="dict in sys_normal_disable" + :key="dict.value" + :label="dict.label" + :value="dict.value" + /> + </el-select> + </el-form-item> + <el-form-item label="鍒涘缓鏃堕棿" style="width: 308px"> + <el-date-picker + v-model="dateRange" + value-format="YYYY-MM-DD" + type="daterange" + range-separator="-" + start-placeholder="寮�濮嬫棩鏈�" + end-placeholder="缁撴潫鏃ユ湡" + ></el-date-picker> + </el-form-item> + <el-form-item> + <el-button type="primary" icon="Search" @click="handleQuery">鎼滅储</el-button> + <el-button icon="Refresh" @click="resetQuery">閲嶇疆</el-button> + </el-form-item> + </el-form> + <el-row :gutter="10" class="mb8"> + <el-col :span="1.5"> + <el-button + type="primary" + plain + icon="Plus" + @click="handleAdd" + v-hasPermi="['system:role:add']" + >鏂板</el-button> + </el-col> + <el-col :span="1.5"> + <el-button + type="success" + plain + icon="Edit" + :disabled="single" + @click="handleUpdate" + v-hasPermi="['system:role:edit']" + >淇敼</el-button> + </el-col> + <el-col :span="1.5"> + <el-button + type="danger" + plain + icon="Delete" + :disabled="multiple" + @click="handleDelete" + v-hasPermi="['system:role:remove']" + >鍒犻櫎</el-button> + </el-col> + <el-col :span="1.5"> + <el-button + type="warning" + plain + icon="Download" + @click="handleExport" + v-hasPermi="['system:role:export']" + >瀵煎嚭</el-button> + </el-col> + <right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar> + </el-row> + + <!-- 琛ㄦ牸鏁版嵁 --> + <el-table v-loading="loading" :data="roleList" @selection-change="handleSelectionChange"> + <el-table-column type="selection" width="55" align="center" /> + <el-table-column label="瑙掕壊缂栧彿" prop="roleId" width="120" /> + <el-table-column label="瑙掕壊鍚嶇О" prop="roleName" :show-overflow-tooltip="true" width="150" /> + <el-table-column label="鏉冮檺瀛楃" prop="roleKey" :show-overflow-tooltip="true" width="150" /> + <el-table-column label="鏄剧ず椤哄簭" prop="roleSort" width="100" /> + <el-table-column label="鐘舵��" align="center" width="100"> + <template #default="scope"> + <el-switch + v-model="scope.row.status" + active-value="0" + inactive-value="1" + @change="handleStatusChange(scope.row)" + ></el-switch> + </template> + </el-table-column> + <el-table-column label="鍒涘缓鏃堕棿" align="center" prop="createTime"> + <template #default="scope"> + <span>{{ parseTime(scope.row.createTime) }}</span> + </template> + </el-table-column> + <el-table-column label="鎿嶄綔" align="center" class-name="small-padding fixed-width"> + <template #default="scope"> + <el-tooltip content="淇敼" placement="top" v-if="scope.row.roleId !== 1"> + <el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['system:role:edit']"></el-button> + </el-tooltip> + <el-tooltip content="鍒犻櫎" placement="top" v-if="scope.row.roleId !== 1"> + <el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['system:role:remove']"></el-button> + </el-tooltip> + <el-tooltip content="鏁版嵁鏉冮檺" placement="top" v-if="scope.row.roleId !== 1"> + <el-button link type="primary" icon="CircleCheck" @click="handleDataScope(scope.row)" v-hasPermi="['system:role:edit']"></el-button> + </el-tooltip> + <el-tooltip content="鍒嗛厤鐢ㄦ埛" placement="top" v-if="scope.row.roleId !== 1"> + <el-button link type="primary" icon="User" @click="handleAuthUser(scope.row)" v-hasPermi="['system:role:edit']"></el-button> + </el-tooltip> + </template> + </el-table-column> + </el-table> + + <pagination + v-show="total > 0" + :total="total" + v-model:page="queryParams.pageNum" + v-model:limit="queryParams.pageSize" + @pagination="getList" + /> + + <!-- 娣诲姞鎴栦慨鏀硅鑹查厤缃璇濇 --> + <el-dialog :title="title" v-model="open" width="500px" append-to-body> + <el-form ref="roleRef" :model="form" :rules="rules" label-width="100px"> + <el-form-item label="瑙掕壊鍚嶇О" prop="roleName"> + <el-input v-model="form.roleName" placeholder="璇疯緭鍏ヨ鑹插悕绉�" /> + </el-form-item> + <el-form-item prop="roleKey"> + <template #label> + <span> + <el-tooltip content="鎺у埗鍣ㄤ腑瀹氫箟鐨勬潈闄愬瓧绗︼紝濡傦細@PreAuthorize(`@ss.hasRole('admin')`)" placement="top"> + <el-icon><question-filled /></el-icon> + </el-tooltip> + 鏉冮檺瀛楃 + </span> + </template> + <el-input v-model="form.roleKey" placeholder="璇疯緭鍏ユ潈闄愬瓧绗�" /> + </el-form-item> + <el-form-item label="瑙掕壊椤哄簭" prop="roleSort"> + <el-input-number v-model="form.roleSort" controls-position="right" :min="0" /> + </el-form-item> + <el-form-item label="鐘舵��"> + <el-radio-group v-model="form.status"> + <el-radio + v-for="dict in sys_normal_disable" + :key="dict.value" + :value="dict.value" + >{{ dict.label }}</el-radio> + </el-radio-group> + </el-form-item> + <el-form-item label="鑿滃崟鏉冮檺"> + <el-checkbox v-model="menuExpand" @change="handleCheckedTreeExpand($event, 'menu')">灞曞紑/鎶樺彔</el-checkbox> + <el-checkbox v-model="menuNodeAll" @change="handleCheckedTreeNodeAll($event, 'menu')">鍏ㄩ��/鍏ㄤ笉閫�</el-checkbox> + <el-checkbox v-model="form.menuCheckStrictly" @change="handleCheckedTreeConnect($event, 'menu')">鐖跺瓙鑱斿姩</el-checkbox> + <el-tree + class="tree-border" + :data="menuOptions" + show-checkbox + ref="menuRef" + node-key="id" + :check-strictly="!form.menuCheckStrictly" + empty-text="鍔犺浇涓紝璇风◢鍊�" + :props="{ label: 'label', children: 'children' }" + ></el-tree> + </el-form-item> + <el-form-item label="澶囨敞"> + <el-input v-model="form.remark" type="textarea" placeholder="璇疯緭鍏ュ唴瀹�"></el-input> + </el-form-item> + </el-form> + <template #footer> + <div class="dialog-footer"> + <el-button type="primary" @click="submitForm">纭� 瀹�</el-button> + <el-button @click="cancel">鍙� 娑�</el-button> + </div> + </template> + </el-dialog> + + <!-- 鍒嗛厤瑙掕壊鏁版嵁鏉冮檺瀵硅瘽妗� --> + <el-dialog :title="title" v-model="openDataScope" width="500px" append-to-body> + <el-form :model="form" label-width="80px"> + <el-form-item label="瑙掕壊鍚嶇О"> + <el-input v-model="form.roleName" :disabled="true" /> + </el-form-item> + <el-form-item label="鏉冮檺瀛楃"> + <el-input v-model="form.roleKey" :disabled="true" /> + </el-form-item> + <el-form-item label="鏉冮檺鑼冨洿"> + <el-select v-model="form.dataScope" @change="dataScopeSelectChange"> + <el-option + v-for="item in dataScopeOptions" + :key="item.value" + :label="item.label" + :value="item.value" + ></el-option> + </el-select> + </el-form-item> + <el-form-item label="鏁版嵁鏉冮檺" v-show="form.dataScope == 2"> + <el-checkbox v-model="deptExpand" @change="handleCheckedTreeExpand($event, 'dept')">灞曞紑/鎶樺彔</el-checkbox> + <el-checkbox v-model="deptNodeAll" @change="handleCheckedTreeNodeAll($event, 'dept')">鍏ㄩ��/鍏ㄤ笉閫�</el-checkbox> + <el-checkbox v-model="form.deptCheckStrictly" @change="handleCheckedTreeConnect($event, 'dept')">鐖跺瓙鑱斿姩</el-checkbox> + <el-tree + class="tree-border" + :data="deptOptions" + show-checkbox + default-expand-all + ref="deptRef" + node-key="id" + :check-strictly="!form.deptCheckStrictly" + empty-text="鍔犺浇涓紝璇风◢鍊�" + :props="{ label: 'label', children: 'children' }" + ></el-tree> + </el-form-item> + </el-form> + <template #footer> + <div class="dialog-footer"> + <el-button type="primary" @click="submitDataScope">纭� 瀹�</el-button> + <el-button @click="cancelDataScope">鍙� 娑�</el-button> + </div> + </template> + </el-dialog> + </div> +</template> + +<script setup name="Role"> +import { addRole, changeRoleStatus, dataScope, delRole, getRole, listRole, updateRole, deptTreeSelect } from "@/api/system/role" +import { roleMenuTreeselect, treeselect as menuTreeselect } from "@/api/system/menu" + +const router = useRouter() +const { proxy } = getCurrentInstance() +const { sys_normal_disable } = proxy.useDict("sys_normal_disable") + +const roleList = 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 menuOptions = ref([]) +const menuExpand = ref(false) +const menuNodeAll = ref(false) +const deptExpand = ref(true) +const deptNodeAll = ref(false) +const deptOptions = ref([]) +const openDataScope = ref(false) +const menuRef = ref(null) +const deptRef = ref(null) + +/** 鏁版嵁鑼冨洿閫夐」*/ +const dataScopeOptions = ref([ + { value: "1", label: "鍏ㄩ儴鏁版嵁鏉冮檺" }, + { value: "2", label: "鑷畾鏁版嵁鏉冮檺" }, + { value: "3", label: "鏈儴闂ㄦ暟鎹潈闄�" }, + { value: "4", label: "鏈儴闂ㄥ強浠ヤ笅鏁版嵁鏉冮檺" }, + { value: "5", label: "浠呮湰浜烘暟鎹潈闄�" } +]) + +const data = reactive({ + form: {}, + queryParams: { + pageNum: 1, + pageSize: 10, + roleName: undefined, + roleKey: undefined, + status: undefined + }, + rules: { + roleName: [{ required: true, message: "瑙掕壊鍚嶇О涓嶈兘涓虹┖", trigger: "blur" }], + roleKey: [{ required: true, message: "鏉冮檺瀛楃涓嶈兘涓虹┖", trigger: "blur" }], + roleSort: [{ required: true, message: "瑙掕壊椤哄簭涓嶈兘涓虹┖", trigger: "blur" }] + }, +}) + +const { queryParams, form, rules } = toRefs(data) + +/** 鏌ヨ瑙掕壊鍒楄〃 */ +function getList() { + loading.value = true + listRole(proxy.addDateRange(queryParams.value, dateRange.value)).then(response => { + roleList.value = response.rows + total.value = response.total + loading.value = false + }) +} + +/** 鎼滅储鎸夐挳鎿嶄綔 */ +function handleQuery() { + queryParams.value.pageNum = 1 + getList() +} + +/** 閲嶇疆鎸夐挳鎿嶄綔 */ +function resetQuery() { + dateRange.value = [] + proxy.resetForm("queryRef") + handleQuery() +} + +/** 鍒犻櫎鎸夐挳鎿嶄綔 */ +function handleDelete(row) { + const roleIds = row.roleId || ids.value + proxy.$modal.confirm('鏄惁纭鍒犻櫎瑙掕壊缂栧彿涓�"' + roleIds + '"鐨勬暟鎹」?').then(function () { + return delRole(roleIds) + }).then(() => { + getList() + proxy.$modal.msgSuccess("鍒犻櫎鎴愬姛") + }).catch(() => {}) +} + +/** 瀵煎嚭鎸夐挳鎿嶄綔 */ +function handleExport() { + proxy.download("system/role/export", { + ...queryParams.value, + }, `role_${new Date().getTime()}.xlsx`) +} + +/** 澶氶�夋閫変腑鏁版嵁 */ +function handleSelectionChange(selection) { + ids.value = selection.map(item => item.roleId) + single.value = selection.length != 1 + multiple.value = !selection.length +} + +/** 瑙掕壊鐘舵�佷慨鏀� */ +function handleStatusChange(row) { + let text = row.status === "0" ? "鍚敤" : "鍋滅敤" + proxy.$modal.confirm('纭瑕�"' + text + '""' + row.roleName + '"瑙掕壊鍚�?').then(function () { + return changeRoleStatus(row.roleId, row.status) + }).then(() => { + proxy.$modal.msgSuccess(text + "鎴愬姛") + }).catch(function () { + row.status = row.status === "0" ? "1" : "0" + }) +} + +/** 鏇村鎿嶄綔 */ +function handleCommand(command, row) { + switch (command) { + case "handleDataScope": + handleDataScope(row) + break + case "handleAuthUser": + handleAuthUser(row) + break + default: + break + } +} + +/** 鍒嗛厤鐢ㄦ埛 */ +function handleAuthUser(row) { + router.push("/system/role-auth/user/" + row.roleId) +} + +/** 鏌ヨ鑿滃崟鏍戠粨鏋� */ +function getMenuTreeselect() { + menuTreeselect().then(response => { + menuOptions.value = response.data + }) +} + +/** 鎵�鏈夐儴闂ㄨ妭鐐规暟鎹� */ +function getDeptAllCheckedKeys() { + // 鐩墠琚�変腑鐨勯儴闂ㄨ妭鐐� + let checkedKeys = deptRef.value.getCheckedKeys() + // 鍗婇�変腑鐨勯儴闂ㄨ妭鐐� + let halfCheckedKeys = deptRef.value.getHalfCheckedKeys() + checkedKeys.unshift.apply(checkedKeys, halfCheckedKeys) + return checkedKeys +} + +/** 閲嶇疆鏂板鐨勮〃鍗曚互鍙婂叾浠栨暟鎹� */ +function reset() { + if (menuRef.value != undefined) { + menuRef.value.setCheckedKeys([]) + } + menuExpand.value = false + menuNodeAll.value = false + deptExpand.value = true + deptNodeAll.value = false + form.value = { + roleId: undefined, + roleName: undefined, + roleKey: undefined, + roleSort: 0, + status: "0", + menuIds: [], + deptIds: [], + menuCheckStrictly: true, + deptCheckStrictly: true, + remark: undefined + } + proxy.resetForm("roleRef") +} + +/** 娣诲姞瑙掕壊 */ +function handleAdd() { + reset() + getMenuTreeselect() + open.value = true + title.value = "娣诲姞瑙掕壊" +} + +/** 淇敼瑙掕壊 */ +function handleUpdate(row) { + reset() + const roleId = row.roleId || ids.value + const roleMenu = getRoleMenuTreeselect(roleId) + getRole(roleId).then(response => { + form.value = response.data + form.value.roleSort = Number(form.value.roleSort) + open.value = true + nextTick(() => { + roleMenu.then((res) => { + let checkedKeys = res.checkedKeys + checkedKeys.forEach((v) => { + nextTick(() => { + menuRef.value.setChecked(v, true, false) + }) + }) + }) + }) + }) + title.value = "淇敼瑙掕壊" +} + +/** 鏍规嵁瑙掕壊ID鏌ヨ鑿滃崟鏍戠粨鏋� */ +function getRoleMenuTreeselect(roleId) { + return roleMenuTreeselect(roleId).then(response => { + menuOptions.value = response.menus + return response + }) +} + +/** 鏍规嵁瑙掕壊ID鏌ヨ閮ㄩ棬鏍戠粨鏋� */ +function getDeptTree(roleId) { + return deptTreeSelect(roleId).then(response => { + deptOptions.value = response.depts + return response + }) +} + +/** 鏍戞潈闄愶紙灞曞紑/鎶樺彔锛�*/ +function handleCheckedTreeExpand(value, type) { + if (type == "menu") { + let treeList = menuOptions.value + for (let i = 0; i < treeList.length; i++) { + menuRef.value.store.nodesMap[treeList[i].id].expanded = value + } + } else if (type == "dept") { + let treeList = deptOptions.value + for (let i = 0; i < treeList.length; i++) { + deptRef.value.store.nodesMap[treeList[i].id].expanded = value + } + } +} + +/** 鏍戞潈闄愶紙鍏ㄩ��/鍏ㄤ笉閫夛級 */ +function handleCheckedTreeNodeAll(value, type) { + if (type == "menu") { + menuRef.value.setCheckedNodes(value ? menuOptions.value : []) + } else if (type == "dept") { + deptRef.value.setCheckedNodes(value ? deptOptions.value : []) + } +} + +/** 鏍戞潈闄愶紙鐖跺瓙鑱斿姩锛� */ +function handleCheckedTreeConnect(value, type) { + if (type == "menu") { + form.value.menuCheckStrictly = value ? true : false + } else if (type == "dept") { + form.value.deptCheckStrictly = value ? true : false + } +} + +/** 鎵�鏈夎彍鍗曡妭鐐规暟鎹� */ +function getMenuAllCheckedKeys() { + // 鐩墠琚�変腑鐨勮彍鍗曡妭鐐� + let checkedKeys = menuRef.value.getCheckedKeys() + // 鍗婇�変腑鐨勮彍鍗曡妭鐐� + let halfCheckedKeys = menuRef.value.getHalfCheckedKeys() + checkedKeys.unshift.apply(checkedKeys, halfCheckedKeys) + return checkedKeys +} + +/** 鎻愪氦鎸夐挳 */ +function submitForm() { + proxy.$refs["roleRef"].validate(valid => { + if (valid) { + if (form.value.roleId != undefined) { + form.value.menuIds = getMenuAllCheckedKeys() + updateRole(form.value).then(response => { + proxy.$modal.msgSuccess("淇敼鎴愬姛") + open.value = false + getList() + }) + } else { + form.value.menuIds = getMenuAllCheckedKeys() + addRole(form.value).then(response => { + proxy.$modal.msgSuccess("鏂板鎴愬姛") + open.value = false + getList() + }) + } + } + }) +} + +/** 鍙栨秷鎸夐挳 */ +function cancel() { + open.value = false + reset() +} + +/** 閫夋嫨瑙掕壊鏉冮檺鑼冨洿瑙﹀彂 */ +function dataScopeSelectChange(value) { + if (value !== "2") { + deptRef.value.setCheckedKeys([]) + } +} + +/** 鍒嗛厤鏁版嵁鏉冮檺鎿嶄綔 */ +function handleDataScope(row) { + reset() + const deptTreeSelect = getDeptTree(row.roleId) + getRole(row.roleId).then(response => { + form.value = response.data + openDataScope.value = true + nextTick(() => { + deptTreeSelect.then(res => { + nextTick(() => { + if (deptRef.value) { + deptRef.value.setCheckedKeys(res.checkedKeys) + } + }) + }) + }) + }) + title.value = "鍒嗛厤鏁版嵁鏉冮檺" +} + +/** 鎻愪氦鎸夐挳锛堟暟鎹潈闄愶級 */ +function submitDataScope() { + if (form.value.roleId != undefined) { + form.value.deptIds = getDeptAllCheckedKeys() + dataScope(form.value).then(response => { + proxy.$modal.msgSuccess("淇敼鎴愬姛") + openDataScope.value = false + getList() + }) + } +} + +/** 鍙栨秷鎸夐挳锛堟暟鎹潈闄愶級*/ +function cancelDataScope() { + openDataScope.value = false + reset() +} + +getList() +</script> diff --git a/src/views/system/role/selectUser.vue b/src/views/system/role/selectUser.vue new file mode 100644 index 0000000..0796618 --- /dev/null +++ b/src/views/system/role/selectUser.vue @@ -0,0 +1,144 @@ +<template> + <!-- 鎺堟潈鐢ㄦ埛 --> + <el-dialog title="閫夋嫨鐢ㄦ埛" v-model="visible" width="800px" top="5vh" append-to-body> + <el-form :model="queryParams" ref="queryRef" :inline="true"> + <el-form-item label="鐢ㄦ埛鍚嶇О" prop="userName"> + <el-input + v-model="queryParams.userName" + placeholder="璇疯緭鍏ョ敤鎴峰悕绉�" + clearable + style="width: 180px" + @keyup.enter="handleQuery" + /> + </el-form-item> + <el-form-item label="鎵嬫満鍙风爜" prop="phonenumber"> + <el-input + v-model="queryParams.phonenumber" + placeholder="璇疯緭鍏ユ墜鏈哄彿鐮�" + clearable + style="width: 180px" + @keyup.enter="handleQuery" + /> + </el-form-item> + <el-form-item> + <el-button type="primary" icon="Search" @click="handleQuery">鎼滅储</el-button> + <el-button icon="Refresh" @click="resetQuery">閲嶇疆</el-button> + </el-form-item> + </el-form> + <el-row> + <el-table @row-click="clickRow" ref="refTable" :data="userList" @selection-change="handleSelectionChange" height="260px"> + <el-table-column type="selection" width="55"></el-table-column> + <el-table-column label="鐢ㄦ埛鍚嶇О" prop="userName" :show-overflow-tooltip="true" /> + <el-table-column label="鐢ㄦ埛鏄电О" prop="nickName" :show-overflow-tooltip="true" /> + <el-table-column label="閭" prop="email" :show-overflow-tooltip="true" /> + <el-table-column label="鎵嬫満" prop="phonenumber" :show-overflow-tooltip="true" /> + <el-table-column label="鐘舵��" align="center" prop="status"> + <template #default="scope"> + <dict-tag :options="sys_normal_disable" :value="scope.row.status" /> + </template> + </el-table-column> + <el-table-column label="鍒涘缓鏃堕棿" align="center" prop="createTime" width="180"> + <template #default="scope"> + <span>{{ parseTime(scope.row.createTime) }}</span> + </template> + </el-table-column> + </el-table> + <pagination + v-show="total > 0" + :total="total" + v-model:page="queryParams.pageNum" + v-model:limit="queryParams.pageSize" + @pagination="getList" + /> + </el-row> + <template #footer> + <div class="dialog-footer"> + <el-button type="primary" @click="handleSelectUser">纭� 瀹�</el-button> + <el-button @click="visible = false">鍙� 娑�</el-button> + </div> + </template> + </el-dialog> +</template> + +<script setup name="SelectUser"> +import { authUserSelectAll, unallocatedUserList } from "@/api/system/role" + +const props = defineProps({ + roleId: { + type: [Number, String] + } +}) + +const { proxy } = getCurrentInstance() +const { sys_normal_disable } = proxy.useDict("sys_normal_disable") + +const userList = ref([]) +const visible = ref(false) +const total = ref(0) +const userIds = ref([]) + +const queryParams = reactive({ + pageNum: 1, + pageSize: 10, + roleId: undefined, + userName: undefined, + phonenumber: undefined +}) + +// 鏄剧ず寮规 +function show() { + queryParams.roleId = props.roleId + getList() + visible.value = true +} + +/**閫夋嫨琛� */ +function clickRow(row) { + proxy.$refs["refTable"].toggleRowSelection(row) +} + +// 澶氶�夋閫変腑鏁版嵁 +function handleSelectionChange(selection) { + userIds.value = selection.map(item => item.userId) +} + +// 鏌ヨ琛ㄦ暟鎹� +function getList() { + unallocatedUserList(queryParams).then(res => { + userList.value = res.rows + total.value = res.total + }) +} + +/** 鎼滅储鎸夐挳鎿嶄綔 */ +function handleQuery() { + queryParams.pageNum = 1 + getList() +} + +/** 閲嶇疆鎸夐挳鎿嶄綔 */ +function resetQuery() { + proxy.resetForm("queryRef") + handleQuery() +} + +const emit = defineEmits(["ok"]) +/** 閫夋嫨鎺堟潈鐢ㄦ埛鎿嶄綔 */ +function handleSelectUser() { + const roleId = queryParams.roleId + const uIds = userIds.value.join(",") + if (uIds == "") { + proxy.$modal.msgError("璇烽�夋嫨瑕佸垎閰嶇殑鐢ㄦ埛") + return + } + authUserSelectAll({ roleId: roleId, userIds: uIds }).then(res => { + proxy.$modal.msgSuccess(res.msg) + visible.value = false + emit("ok") + }) +} + +defineExpose({ + show, +}) +</script> diff --git a/src/views/system/user/authRole.vue b/src/views/system/user/authRole.vue new file mode 100644 index 0000000..a7546aa --- /dev/null +++ b/src/views/system/user/authRole.vue @@ -0,0 +1,123 @@ +<template> + <div class="app-container"> + <h4 class="form-header h4">鍩烘湰淇℃伅</h4> + <el-form :model="form" label-width="80px"> + <el-row> + <el-col :span="8" :offset="2"> + <el-form-item label="鐢ㄦ埛鏄电О" prop="nickName"> + <el-input v-model="form.nickName" disabled /> + </el-form-item> + </el-col> + <el-col :span="8" :offset="2"> + <el-form-item label="鐧诲綍璐﹀彿" prop="userName"> + <el-input v-model="form.userName" disabled /> + </el-form-item> + </el-col> + </el-row> + </el-form> + + <h4 class="form-header h4">瑙掕壊淇℃伅</h4> + <el-table v-loading="loading" :row-key="getRowKey" @row-click="clickRow" ref="roleRef" @selection-change="handleSelectionChange" :data="roles.slice((pageNum - 1) * pageSize, pageNum * pageSize)"> + <el-table-column label="搴忓彿" width="55" type="index" align="center"> + <template #default="scope"> + <span>{{ (pageNum - 1) * pageSize + scope.$index + 1 }}</span> + </template> + </el-table-column> + <el-table-column type="selection" :reserve-selection="true" :selectable="checkSelectable" width="55"></el-table-column> + <el-table-column label="瑙掕壊缂栧彿" align="center" prop="roleId" /> + <el-table-column label="瑙掕壊鍚嶇О" align="center" prop="roleName" /> + <el-table-column label="鏉冮檺瀛楃" align="center" prop="roleKey" /> + <el-table-column label="鍒涘缓鏃堕棿" align="center" prop="createTime" width="180"> + <template #default="scope"> + <span>{{ parseTime(scope.row.createTime) }}</span> + </template> + </el-table-column> + </el-table> + + <pagination v-show="total > 0" :total="total" v-model:page="pageNum" v-model:limit="pageSize" /> + + <el-form label-width="100px"> + <div style="text-align: center;margin-left:-120px;margin-top:30px;"> + <el-button type="primary" @click="submitForm()">鎻愪氦</el-button> + <el-button @click="close()">杩斿洖</el-button> + </div> + </el-form> + </div> +</template> + +<script setup name="AuthRole"> +import { getAuthRole, updateAuthRole } from "@/api/system/user" + +const route = useRoute() +const { proxy } = getCurrentInstance() + +const loading = ref(true) +const total = ref(0) +const pageNum = ref(1) +const pageSize = ref(10) +const roleIds = ref([]) +const roles = ref([]) +const form = ref({ + nickName: undefined, + userName: undefined, + userId: undefined +}) + +/** 鍗曞嚮閫変腑琛屾暟鎹� */ +function clickRow(row) { + if (checkSelectable(row)) { + proxy.$refs["roleRef"].toggleRowSelection(row) + } +} + +/** 澶氶�夋閫変腑鏁版嵁 */ +function handleSelectionChange(selection) { + roleIds.value = selection.map(item => item.roleId) +} + +/** 淇濆瓨閫変腑鐨勬暟鎹紪鍙� */ +function getRowKey(row) { + return row.roleId +} + +// 妫�鏌ヨ鑹茬姸鎬� +function checkSelectable(row) { + return row.status === "0" ? true : false +} + +/** 鍏抽棴鎸夐挳 */ +function close() { + const obj = { path: "/system/user" } + proxy.$tab.closeOpenPage(obj) +} + +/** 鎻愪氦鎸夐挳 */ +function submitForm() { + const userId = form.value.userId + const rIds = roleIds.value.join(",") + updateAuthRole({ userId: userId, roleIds: rIds }).then(response => { + proxy.$modal.msgSuccess("鎺堟潈鎴愬姛") + close() + }) +} + +(() => { + const userId = route.params && route.params.userId + if (userId) { + loading.value = true + getAuthRole(userId).then(response => { + form.value = response.user + roles.value = response.roles + total.value = roles.value.length + nextTick(() => { + roles.value.forEach(row => { + if (row.flag) { + proxy.$refs["roleRef"].toggleRowSelection(row) + } + }) + }) + loading.value = false + }) + } +})() +</script> diff --git a/src/views/system/user/index.vue b/src/views/system/user/index.vue new file mode 100644 index 0000000..2cfb05e --- /dev/null +++ b/src/views/system/user/index.vue @@ -0,0 +1,538 @@ +<template> + <div class="app-container"> + <el-row :gutter="20"> + <splitpanes :horizontal="appStore.device === 'mobile'" class="default-theme"> + <!--閮ㄩ棬鏁版嵁--> + <pane size="16"> + <el-col> + <div class="head-container"> + <el-input v-model="deptName" placeholder="璇疯緭鍏ラ儴闂ㄥ悕绉�" clearable prefix-icon="Search" style="margin-bottom: 20px" /> + </div> + <div class="head-container"> + <el-tree :data="deptOptions" :props="{ label: 'label', children: 'children' }" :expand-on-click-node="false" :filter-node-method="filterNode" ref="deptTreeRef" node-key="id" highlight-current default-expand-all @node-click="handleNodeClick" /> + </div> + </el-col> + </pane> + <!--鐢ㄦ埛鏁版嵁--> + <pane size="84"> + <el-col> + <el-form :model="queryParams" ref="queryRef" :inline="true" v-show="showSearch" label-width="68px"> + <el-form-item label="鐢ㄦ埛鍚嶇О" prop="userName"> + <el-input v-model="queryParams.userName" placeholder="璇疯緭鍏ョ敤鎴峰悕绉�" clearable style="width: 240px" @keyup.enter="handleQuery" /> + </el-form-item> + <el-form-item label="鎵嬫満鍙风爜" prop="phonenumber"> + <el-input v-model="queryParams.phonenumber" placeholder="璇疯緭鍏ユ墜鏈哄彿鐮�" clearable style="width: 240px" @keyup.enter="handleQuery" /> + </el-form-item> + <el-form-item label="鐘舵��" prop="status"> + <el-select v-model="queryParams.status" placeholder="鐢ㄦ埛鐘舵��" clearable style="width: 240px"> + <el-option v-for="dict in sys_normal_disable" :key="dict.value" :label="dict.label" :value="dict.value" /> + </el-select> + </el-form-item> + <el-form-item label="鍒涘缓鏃堕棿" style="width: 308px"> + <el-date-picker v-model="dateRange" value-format="YYYY-MM-DD" type="daterange" range-separator="-" start-placeholder="寮�濮嬫棩鏈�" end-placeholder="缁撴潫鏃ユ湡"></el-date-picker> + </el-form-item> + <el-form-item> + <el-button type="primary" icon="Search" @click="handleQuery">鎼滅储</el-button> + <el-button icon="Refresh" @click="resetQuery">閲嶇疆</el-button> + </el-form-item> + </el-form> + + <el-row :gutter="10" class="mb8"> + <el-col :span="1.5"> + <el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['system:user:add']">鏂板</el-button> + </el-col> + <el-col :span="1.5"> + <el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate" v-hasPermi="['system:user:edit']">淇敼</el-button> + </el-col> + <el-col :span="1.5"> + <el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete" v-hasPermi="['system:user:remove']">鍒犻櫎</el-button> + </el-col> + <el-col :span="1.5"> + <el-button type="info" plain icon="Upload" @click="handleImport" v-hasPermi="['system:user:import']">瀵煎叆</el-button> + </el-col> + <el-col :span="1.5"> + <el-button type="warning" plain icon="Download" @click="handleExport" v-hasPermi="['system:user:export']">瀵煎嚭</el-button> + </el-col> + <right-toolbar v-model:showSearch="showSearch" @queryTable="getList" :columns="columns"></right-toolbar> + </el-row> + + <el-table v-loading="loading" :data="userList" @selection-change="handleSelectionChange"> + <el-table-column type="selection" width="50" align="center" /> + <el-table-column label="鐢ㄦ埛缂栧彿" align="center" key="userId" prop="userId" v-if="columns[0].visible" /> + <el-table-column label="鐢ㄦ埛鍚嶇О" align="center" key="userName" prop="userName" v-if="columns[1].visible" :show-overflow-tooltip="true" /> + <el-table-column label="鐢ㄦ埛鏄电О" align="center" key="nickName" prop="nickName" v-if="columns[2].visible" :show-overflow-tooltip="true" /> + <el-table-column label="閮ㄩ棬" align="center" key="deptName" prop="dept.deptName" v-if="columns[3].visible" :show-overflow-tooltip="true" /> + <el-table-column label="鎵嬫満鍙风爜" align="center" key="phonenumber" prop="phonenumber" v-if="columns[4].visible" width="120" /> + <el-table-column label="鐘舵��" align="center" key="status" v-if="columns[5].visible"> + <template #default="scope"> + <el-switch + v-model="scope.row.status" + active-value="0" + inactive-value="1" + @change="handleStatusChange(scope.row)" + ></el-switch> + </template> + </el-table-column> + <el-table-column label="鍒涘缓鏃堕棿" align="center" prop="createTime" v-if="columns[6].visible" width="160"> + <template #default="scope"> + <span>{{ parseTime(scope.row.createTime) }}</span> + </template> + </el-table-column> + <el-table-column label="鎿嶄綔" align="center" width="150" class-name="small-padding fixed-width"> + <template #default="scope"> + <el-tooltip content="淇敼" placement="top" v-if="scope.row.userId !== 1"> + <el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['system:user:edit']"></el-button> + </el-tooltip> + <el-tooltip content="鍒犻櫎" placement="top" v-if="scope.row.userId !== 1"> + <el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['system:user:remove']"></el-button> + </el-tooltip> + <el-tooltip content="閲嶇疆瀵嗙爜" placement="top" v-if="scope.row.userId !== 1"> + <el-button link type="primary" icon="Key" @click="handleResetPwd(scope.row)" v-hasPermi="['system:user:resetPwd']"></el-button> + </el-tooltip> + <el-tooltip content="鍒嗛厤瑙掕壊" placement="top" v-if="scope.row.userId !== 1"> + <el-button link type="primary" icon="CircleCheck" @click="handleAuthRole(scope.row)" v-hasPermi="['system:user:edit']"></el-button> + </el-tooltip> + </template> + </el-table-column> + </el-table> + <pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" /> + </el-col> + </pane> + </splitpanes> + </el-row> + + <!-- 娣诲姞鎴栦慨鏀圭敤鎴烽厤缃璇濇 --> + <el-dialog :title="title" v-model="open" width="600px" append-to-body> + <el-form :model="form" :rules="rules" ref="userRef" label-width="80px"> + <el-row> + <el-col :span="12"> + <el-form-item label="鐢ㄦ埛鏄电О" prop="nickName"> + <el-input v-model="form.nickName" placeholder="璇疯緭鍏ョ敤鎴锋樀绉�" maxlength="30" /> + </el-form-item> + </el-col> + <el-col :span="12"> + <el-form-item label="褰掑睘閮ㄩ棬" prop="deptId"> + <el-tree-select v-model="form.deptId" :data="enabledDeptOptions" :props="{ value: 'id', label: 'label', children: 'children' }" value-key="id" placeholder="璇烽�夋嫨褰掑睘閮ㄩ棬" check-strictly /> + </el-form-item> + </el-col> + </el-row> + <el-row> + <el-col :span="12"> + <el-form-item label="鎵嬫満鍙风爜" prop="phonenumber"> + <el-input v-model="form.phonenumber" placeholder="璇疯緭鍏ユ墜鏈哄彿鐮�" maxlength="11" /> + </el-form-item> + </el-col> + <el-col :span="12"> + <el-form-item label="閭" prop="email"> + <el-input v-model="form.email" placeholder="璇疯緭鍏ラ偖绠�" maxlength="50" /> + </el-form-item> + </el-col> + </el-row> + <el-row> + <el-col :span="12"> + <el-form-item v-if="form.userId == undefined" label="鐢ㄦ埛鍚嶇О" prop="userName"> + <el-input v-model="form.userName" placeholder="璇疯緭鍏ョ敤鎴峰悕绉�" maxlength="30" /> + </el-form-item> + </el-col> + <el-col :span="12"> + <el-form-item v-if="form.userId == undefined" label="鐢ㄦ埛瀵嗙爜" prop="password"> + <el-input v-model="form.password" placeholder="璇疯緭鍏ョ敤鎴峰瘑鐮�" type="password" maxlength="20" show-password /> + </el-form-item> + </el-col> + </el-row> + <el-row> + <el-col :span="12"> + <el-form-item label="鐢ㄦ埛鎬у埆"> + <el-select v-model="form.sex" placeholder="璇烽�夋嫨"> + <el-option v-for="dict in sys_user_sex" :key="dict.value" :label="dict.label" :value="dict.value"></el-option> + </el-select> + </el-form-item> + </el-col> + <el-col :span="12"> + <el-form-item label="鐘舵��"> + <el-radio-group v-model="form.status"> + <el-radio v-for="dict in sys_normal_disable" :key="dict.value" :value="dict.value">{{ dict.label }}</el-radio> + </el-radio-group> + </el-form-item> + </el-col> + </el-row> + <el-row> + <el-col :span="12"> + <el-form-item label="宀椾綅"> + <el-select v-model="form.postIds" multiple placeholder="璇烽�夋嫨"> + <el-option v-for="item in postOptions" :key="item.postId" :label="item.postName" :value="item.postId" :disabled="item.status == 1"></el-option> + </el-select> + </el-form-item> + </el-col> + <el-col :span="12"> + <el-form-item label="瑙掕壊"> + <el-select v-model="form.roleIds" multiple placeholder="璇烽�夋嫨"> + <el-option v-for="item in roleOptions" :key="item.roleId" :label="item.roleName" :value="item.roleId" :disabled="item.status == 1"></el-option> + </el-select> + </el-form-item> + </el-col> + </el-row> + <el-row> + <el-col :span="24"> + <el-form-item label="澶囨敞"> + <el-input v-model="form.remark" type="textarea" placeholder="璇疯緭鍏ュ唴瀹�"></el-input> + </el-form-item> + </el-col> + </el-row> + </el-form> + <template #footer> + <div class="dialog-footer"> + <el-button type="primary" @click="submitForm">纭� 瀹�</el-button> + <el-button @click="cancel">鍙� 娑�</el-button> + </div> + </template> + </el-dialog> + + <!-- 鐢ㄦ埛瀵煎叆瀵硅瘽妗� --> + <el-dialog :title="upload.title" v-model="upload.open" width="400px" append-to-body> + <el-upload ref="uploadRef" :limit="1" accept=".xlsx, .xls" :headers="upload.headers" :action="upload.url + '?updateSupport=' + upload.updateSupport" :disabled="upload.isUploading" :on-progress="handleFileUploadProgress" :on-success="handleFileSuccess" :auto-upload="false" drag> + <el-icon class="el-icon--upload"><upload-filled /></el-icon> + <div class="el-upload__text">灏嗘枃浠舵嫋鍒版澶勶紝鎴�<em>鐐瑰嚮涓婁紶</em></div> + <template #tip> + <div class="el-upload__tip text-center"> + <div class="el-upload__tip"> + <el-checkbox v-model="upload.updateSupport" />鏄惁鏇存柊宸茬粡瀛樺湪鐨勭敤鎴锋暟鎹� + </div> + <span>浠呭厑璁稿鍏ls銆亁lsx鏍煎紡鏂囦欢銆�</span> + <el-link type="primary" :underline="false" style="font-size: 12px; vertical-align: baseline" @click="importTemplate">涓嬭浇妯℃澘</el-link> + </div> + </template> + </el-upload> + <template #footer> + <div class="dialog-footer"> + <el-button type="primary" @click="submitFileForm">纭� 瀹�</el-button> + <el-button @click="upload.open = false">鍙� 娑�</el-button> + </div> + </template> + </el-dialog> + </div> +</template> + +<script setup name="User"> +import { getToken } from "@/utils/auth" +import useAppStore from '@/store/modules/app' +import { changeUserStatus, listUser, resetUserPwd, delUser, getUser, updateUser, addUser, deptTreeSelect } from "@/api/system/user" +import { Splitpanes, Pane } from "splitpanes" +import "splitpanes/dist/splitpanes.css" + +const router = useRouter() +const appStore = useAppStore() +const { proxy } = getCurrentInstance() +const { sys_normal_disable, sys_user_sex } = proxy.useDict("sys_normal_disable", "sys_user_sex") + +const userList = 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 deptName = ref("") +const deptOptions = ref(undefined) +const enabledDeptOptions = ref(undefined) +const initPassword = ref(undefined) +const postOptions = ref([]) +const roleOptions = ref([]) +/*** 鐢ㄦ埛瀵煎叆鍙傛暟 */ +const upload = reactive({ + // 鏄惁鏄剧ず寮瑰嚭灞傦紙鐢ㄦ埛瀵煎叆锛� + open: false, + // 寮瑰嚭灞傛爣棰橈紙鐢ㄦ埛瀵煎叆锛� + title: "", + // 鏄惁绂佺敤涓婁紶 + isUploading: false, + // 鏄惁鏇存柊宸茬粡瀛樺湪鐨勭敤鎴锋暟鎹� + updateSupport: 0, + // 璁剧疆涓婁紶鐨勮姹傚ご閮� + headers: { Authorization: "Bearer " + getToken() }, + // 涓婁紶鐨勫湴鍧� + url: import.meta.env.VITE_APP_BASE_API + "/system/user/importData" +}) +// 鍒楁樉闅愪俊鎭� +const columns = ref([ + { key: 0, label: `鐢ㄦ埛缂栧彿`, visible: true }, + { key: 1, label: `鐢ㄦ埛鍚嶇О`, visible: true }, + { key: 2, label: `鐢ㄦ埛鏄电О`, visible: true }, + { key: 3, label: `閮ㄩ棬`, visible: true }, + { key: 4, label: `鎵嬫満鍙风爜`, visible: true }, + { key: 5, label: `鐘舵�乣, visible: true }, + { key: 6, label: `鍒涘缓鏃堕棿`, visible: true } +]) + +const data = reactive({ + form: {}, + queryParams: { + pageNum: 1, + pageSize: 10, + userName: undefined, + phonenumber: undefined, + status: undefined, + deptId: undefined + }, + rules: { + userName: [{ required: true, message: "鐢ㄦ埛鍚嶇О涓嶈兘涓虹┖", trigger: "blur" }, { min: 2, max: 20, message: "鐢ㄦ埛鍚嶇О闀垮害蹇呴』浠嬩簬 2 鍜� 20 涔嬮棿", trigger: "blur" }], + nickName: [{ required: true, message: "鐢ㄦ埛鏄电О涓嶈兘涓虹┖", trigger: "blur" }], + password: [{ required: true, message: "鐢ㄦ埛瀵嗙爜涓嶈兘涓虹┖", trigger: "blur" }, { min: 5, max: 20, message: "鐢ㄦ埛瀵嗙爜闀垮害蹇呴』浠嬩簬 5 鍜� 20 涔嬮棿", trigger: "blur" }, { pattern: /^[^<>"'|\\]+$/, message: "涓嶈兘鍖呭惈闈炴硶瀛楃锛�< > \" ' \\\ |", trigger: "blur" }], + email: [{ type: "email", message: "璇疯緭鍏ユ纭殑閭鍦板潃", trigger: ["blur", "change"] }], + phonenumber: [{ pattern: /^1[3|4|5|6|7|8|9][0-9]\d{8}$/, message: "璇疯緭鍏ユ纭殑鎵嬫満鍙风爜", trigger: "blur" }] + } +}) + +const { queryParams, form, rules } = toRefs(data) + +/** 閫氳繃鏉′欢杩囨护鑺傜偣 */ +const filterNode = (value, data) => { + if (!value) return true + return data.label.indexOf(value) !== -1 +} + +/** 鏍规嵁鍚嶇О绛涢�夐儴闂ㄦ爲 */ +watch(deptName, val => { + proxy.$refs["deptTreeRef"].filter(val) +}) + +/** 鏌ヨ鐢ㄦ埛鍒楄〃 */ +function getList() { + loading.value = true + listUser(proxy.addDateRange(queryParams.value, dateRange.value)).then(res => { + loading.value = false + userList.value = res.rows + total.value = res.total + }) +} + +/** 鏌ヨ閮ㄩ棬涓嬫媺鏍戠粨鏋� */ +function getDeptTree() { + deptTreeSelect().then(response => { + deptOptions.value = response.data + enabledDeptOptions.value = filterDisabledDept(JSON.parse(JSON.stringify(response.data))) + }) +} + +/** 杩囨护绂佺敤鐨勯儴闂� */ +function filterDisabledDept(deptList) { + return deptList.filter(dept => { + if (dept.disabled) { + return false + } + if (dept.children && dept.children.length) { + dept.children = filterDisabledDept(dept.children) + } + return true + }) +} + +/** 鑺傜偣鍗曞嚮浜嬩欢 */ +function handleNodeClick(data) { + queryParams.value.deptId = data.id + handleQuery() +} + +/** 鎼滅储鎸夐挳鎿嶄綔 */ +function handleQuery() { + queryParams.value.pageNum = 1 + getList() +} + +/** 閲嶇疆鎸夐挳鎿嶄綔 */ +function resetQuery() { + dateRange.value = [] + proxy.resetForm("queryRef") + queryParams.value.deptId = undefined + proxy.$refs.deptTreeRef.setCurrentKey(null) + handleQuery() +} + +/** 鍒犻櫎鎸夐挳鎿嶄綔 */ +function handleDelete(row) { + const userIds = row.userId || ids.value + proxy.$modal.confirm('鏄惁纭鍒犻櫎鐢ㄦ埛缂栧彿涓�"' + userIds + '"鐨勬暟鎹」锛�').then(function () { + return delUser(userIds) + }).then(() => { + getList() + proxy.$modal.msgSuccess("鍒犻櫎鎴愬姛") + }).catch(() => {}) +} + +/** 瀵煎嚭鎸夐挳鎿嶄綔 */ +function handleExport() { + proxy.download("system/user/export", { + ...queryParams.value, + },`user_${new Date().getTime()}.xlsx`) +} + +/** 鐢ㄦ埛鐘舵�佷慨鏀� */ +function handleStatusChange(row) { + let text = row.status === "0" ? "鍚敤" : "鍋滅敤" + proxy.$modal.confirm('纭瑕�"' + text + '""' + row.userName + '"鐢ㄦ埛鍚�?').then(function () { + return changeUserStatus(row.userId, row.status) + }).then(() => { + proxy.$modal.msgSuccess(text + "鎴愬姛") + }).catch(function () { + row.status = row.status === "0" ? "1" : "0" + }) +} + +/** 鏇村鎿嶄綔 */ +function handleCommand(command, row) { + switch (command) { + case "handleResetPwd": + handleResetPwd(row) + break + case "handleAuthRole": + handleAuthRole(row) + break + default: + break + } +} + +/** 璺宠浆瑙掕壊鍒嗛厤 */ +function handleAuthRole(row) { + const userId = row.userId + router.push("/system/user-auth/role/" + userId) +} + +/** 閲嶇疆瀵嗙爜鎸夐挳鎿嶄綔 */ +function handleResetPwd(row) { + proxy.$prompt('璇疯緭鍏�"' + row.userName + '"鐨勬柊瀵嗙爜', "鎻愮ず", { + confirmButtonText: "纭畾", + cancelButtonText: "鍙栨秷", + closeOnClickModal: false, + inputPattern: /^.{5,20}$/, + inputErrorMessage: "鐢ㄦ埛瀵嗙爜闀垮害蹇呴』浠嬩簬 5 鍜� 20 涔嬮棿", + inputValidator: (value) => { + if (/<|>|"|'|\||\\/.test(value)) { + return "涓嶈兘鍖呭惈闈炴硶瀛楃锛�< > \" ' \\\ |" + } + }, + }).then(({ value }) => { + resetUserPwd(row.userId, value).then(response => { + proxy.$modal.msgSuccess("淇敼鎴愬姛锛屾柊瀵嗙爜鏄細" + value) + }) + }).catch(() => {}) +} + +/** 閫夋嫨鏉℃暟 */ +function handleSelectionChange(selection) { + ids.value = selection.map(item => item.userId) + single.value = selection.length != 1 + multiple.value = !selection.length +} + +/** 瀵煎叆鎸夐挳鎿嶄綔 */ +function handleImport() { + upload.title = "鐢ㄦ埛瀵煎叆" + upload.open = true +} + +/** 涓嬭浇妯℃澘鎿嶄綔 */ +function importTemplate() { + proxy.download("system/user/importTemplate", { + }, `user_template_${new Date().getTime()}.xlsx`) +} + +/**鏂囦欢涓婁紶涓鐞� */ +const handleFileUploadProgress = (event, file, fileList) => { + upload.isUploading = true +} + +/** 鏂囦欢涓婁紶鎴愬姛澶勭悊 */ +const handleFileSuccess = (response, file, fileList) => { + upload.open = false + upload.isUploading = false + proxy.$refs["uploadRef"].handleRemove(file) + proxy.$alert("<div style='overflow: auto;overflow-x: hidden;max-height: 70vh;padding: 10px 20px 0;'>" + response.msg + "</div>", "瀵煎叆缁撴灉", { dangerouslyUseHTMLString: true }) + getList() +} + +/** 鎻愪氦涓婁紶鏂囦欢 */ +function submitFileForm() { + proxy.$refs["uploadRef"].submit() +} + +/** 閲嶇疆鎿嶄綔琛ㄥ崟 */ +function reset() { + form.value = { + userId: undefined, + deptId: undefined, + userName: undefined, + nickName: undefined, + password: undefined, + phonenumber: undefined, + email: undefined, + sex: undefined, + status: "0", + remark: undefined, + postIds: [], + roleIds: [] + } + proxy.resetForm("userRef") +} + +/** 鍙栨秷鎸夐挳 */ +function cancel() { + open.value = false + reset() +} + +/** 鏂板鎸夐挳鎿嶄綔 */ +function handleAdd() { + reset() + getUser().then(response => { + postOptions.value = response.posts + roleOptions.value = response.roles + open.value = true + title.value = "娣诲姞鐢ㄦ埛" + form.value.password = initPassword.value + }) +} + +/** 淇敼鎸夐挳鎿嶄綔 */ +function handleUpdate(row) { + reset() + const userId = row.userId || ids.value + getUser(userId).then(response => { + form.value = response.data + postOptions.value = response.posts + roleOptions.value = response.roles + form.value.postIds = response.postIds + form.value.roleIds = response.roleIds + open.value = true + title.value = "淇敼鐢ㄦ埛" + form.password = "" + }) +} + +/** 鎻愪氦鎸夐挳 */ +function submitForm() { + proxy.$refs["userRef"].validate(valid => { + if (valid) { + if (form.value.userId != undefined) { + updateUser(form.value).then(response => { + proxy.$modal.msgSuccess("淇敼鎴愬姛") + open.value = false + getList() + }) + } else { + addUser(form.value).then(response => { + proxy.$modal.msgSuccess("鏂板鎴愬姛") + open.value = false + getList() + }) + } + } + }) +} + +getDeptTree() +getList() +</script> diff --git a/src/views/system/user/profile/index.vue b/src/views/system/user/profile/index.vue new file mode 100644 index 0000000..719a028 --- /dev/null +++ b/src/views/system/user/profile/index.vue @@ -0,0 +1,87 @@ +<template> + <div class="app-container"> + <el-row :gutter="20"> + <el-col :span="6" :xs="24"> + <el-card class="box-card"> + <template v-slot:header> + <div class="clearfix"> + <span>涓汉淇℃伅</span> + </div> + </template> + <div> + <div class="text-center"> + <userAvatar /> + </div> + <ul class="list-group list-group-striped"> + <li class="list-group-item"> + <svg-icon icon-class="user" />鐢ㄦ埛鍚嶇О + <div class="pull-right">{{ state.user.userName }}</div> + </li> + <li class="list-group-item"> + <svg-icon icon-class="phone" />鎵嬫満鍙风爜 + <div class="pull-right">{{ state.user.phonenumber }}</div> + </li> + <li class="list-group-item"> + <svg-icon icon-class="email" />鐢ㄦ埛閭 + <div class="pull-right">{{ state.user.email }}</div> + </li> + <li class="list-group-item"> + <svg-icon icon-class="tree" />鎵�灞為儴闂� + <div class="pull-right" v-if="state.user.dept">{{ state.user.dept.deptName }} / {{ state.postGroup }}</div> + </li> + <li class="list-group-item"> + <svg-icon icon-class="peoples" />鎵�灞炶鑹� + <div class="pull-right">{{ state.roleGroup }}</div> + </li> + <li class="list-group-item"> + <svg-icon icon-class="date" />鍒涘缓鏃ユ湡 + <div class="pull-right">{{ state.user.createTime }}</div> + </li> + </ul> + </div> + </el-card> + </el-col> + <el-col :span="18" :xs="24"> + <el-card> + <template v-slot:header> + <div class="clearfix"> + <span>鍩烘湰璧勬枡</span> + </div> + </template> + <el-tabs v-model="activeTab"> + <el-tab-pane label="鍩烘湰璧勬枡" name="userinfo"> + <userInfo :user="state.user" /> + </el-tab-pane> + <el-tab-pane label="淇敼瀵嗙爜" name="resetPwd"> + <resetPwd /> + </el-tab-pane> + </el-tabs> + </el-card> + </el-col> + </el-row> + </div> +</template> + +<script setup name="Profile"> +import userAvatar from "./userAvatar" +import userInfo from "./userInfo" +import resetPwd from "./resetPwd" +import { getUserProfile } from "@/api/system/user" + +const activeTab = ref("userinfo") +const state = reactive({ + user: {}, + roleGroup: {}, + postGroup: {} +}) + +function getUser() { + getUserProfile().then(response => { + state.user = response.data + state.roleGroup = response.roleGroup + state.postGroup = response.postGroup + }) +} + +getUser() +</script> diff --git a/src/views/system/user/profile/resetPwd.vue b/src/views/system/user/profile/resetPwd.vue new file mode 100644 index 0000000..73c6b18 --- /dev/null +++ b/src/views/system/user/profile/resetPwd.vue @@ -0,0 +1,59 @@ +<template> + <el-form ref="pwdRef" :model="user" :rules="rules" label-width="80px"> + <el-form-item label="鏃у瘑鐮�" prop="oldPassword"> + <el-input v-model="user.oldPassword" placeholder="璇疯緭鍏ユ棫瀵嗙爜" type="password" show-password /> + </el-form-item> + <el-form-item label="鏂板瘑鐮�" prop="newPassword"> + <el-input v-model="user.newPassword" placeholder="璇疯緭鍏ユ柊瀵嗙爜" type="password" show-password /> + </el-form-item> + <el-form-item label="纭瀵嗙爜" prop="confirmPassword"> + <el-input v-model="user.confirmPassword" placeholder="璇风‘璁ゆ柊瀵嗙爜" type="password" show-password/> + </el-form-item> + <el-form-item> + <el-button type="primary" @click="submit">淇濆瓨</el-button> + <el-button type="danger" @click="close">鍏抽棴</el-button> + </el-form-item> + </el-form> +</template> + +<script setup> +import { updateUserPwd } from "@/api/system/user" + +const { proxy } = getCurrentInstance() + +const user = reactive({ + oldPassword: undefined, + newPassword: undefined, + confirmPassword: undefined +}) + +const equalToPassword = (rule, value, callback) => { + if (user.newPassword !== value) { + callback(new Error("涓ゆ杈撳叆鐨勫瘑鐮佷笉涓�鑷�")) + } else { + callback() + } +} + +const rules = ref({ + oldPassword: [{ required: true, message: "鏃у瘑鐮佷笉鑳戒负绌�", trigger: "blur" }], + newPassword: [{ required: true, message: "鏂板瘑鐮佷笉鑳戒负绌�", trigger: "blur" }, { min: 6, max: 20, message: "闀垮害鍦� 6 鍒� 20 涓瓧绗�", trigger: "blur" }, { pattern: /^[^<>"'|\\]+$/, message: "涓嶈兘鍖呭惈闈炴硶瀛楃锛�< > \" ' \\\ |", trigger: "blur" }], + confirmPassword: [{ required: true, message: "纭瀵嗙爜涓嶈兘涓虹┖", trigger: "blur" }, { required: true, validator: equalToPassword, trigger: "blur" }] +}) + +/** 鎻愪氦鎸夐挳 */ +function submit() { + proxy.$refs.pwdRef.validate(valid => { + if (valid) { + updateUserPwd(user.oldPassword, user.newPassword).then(response => { + proxy.$modal.msgSuccess("淇敼鎴愬姛") + }) + } + }) +} + +/** 鍏抽棴鎸夐挳 */ +function close() { + proxy.$tab.closePage() +} +</script> diff --git a/src/views/system/user/profile/userAvatar.vue b/src/views/system/user/profile/userAvatar.vue new file mode 100644 index 0000000..64d2c94 --- /dev/null +++ b/src/views/system/user/profile/userAvatar.vue @@ -0,0 +1,180 @@ +<template> + <div class="user-info-head" @click="editCropper()"> + <img :src="options.img" title="鐐瑰嚮涓婁紶澶村儚" class="img-circle img-lg" /> + <el-dialog :title="title" v-model="open" width="800px" append-to-body @opened="modalOpened" @close="closeDialog"> + <el-row> + <el-col :xs="24" :md="12" :style="{ height: '350px' }"> + <vue-cropper + ref="cropper" + :img="options.img" + :info="true" + :autoCrop="options.autoCrop" + :autoCropWidth="options.autoCropWidth" + :autoCropHeight="options.autoCropHeight" + :fixedBox="options.fixedBox" + :outputType="options.outputType" + @realTime="realTime" + v-if="visible" + /> + </el-col> + <el-col :xs="24" :md="12" :style="{ height: '350px' }"> + <div class="avatar-upload-preview"> + <img :src="options.previews.url" :style="options.previews.img" /> + </div> + </el-col> + </el-row> + <br /> + <el-row> + <el-col :lg="2" :md="2"> + <el-upload + action="#" + :http-request="requestUpload" + :show-file-list="false" + :before-upload="beforeUpload" + > + <el-button> + 閫夋嫨 + <el-icon class="el-icon--right"><Upload /></el-icon> + </el-button> + </el-upload> + </el-col> + <el-col :lg="{ span: 1, offset: 2 }" :md="2"> + <el-button icon="Plus" @click="changeScale(1)"></el-button> + </el-col> + <el-col :lg="{ span: 1, offset: 1 }" :md="2"> + <el-button icon="Minus" @click="changeScale(-1)"></el-button> + </el-col> + <el-col :lg="{ span: 1, offset: 1 }" :md="2"> + <el-button icon="RefreshLeft" @click="rotateLeft()"></el-button> + </el-col> + <el-col :lg="{ span: 1, offset: 1 }" :md="2"> + <el-button icon="RefreshRight" @click="rotateRight()"></el-button> + </el-col> + <el-col :lg="{ span: 2, offset: 6 }" :md="2"> + <el-button type="primary" @click="uploadImg()">鎻� 浜�</el-button> + </el-col> + </el-row> + </el-dialog> + </div> +</template> + +<script setup> +import "vue-cropper/dist/index.css" +import { VueCropper } from "vue-cropper" +import { uploadAvatar } from "@/api/system/user" +import useUserStore from "@/store/modules/user" + +const userStore = useUserStore() +const { proxy } = getCurrentInstance() + +const open = ref(false) +const visible = ref(false) +const title = ref("淇敼澶村儚") + +//鍥剧墖瑁佸壀鏁版嵁 +const options = reactive({ + img: userStore.avatar, // 瑁佸壀鍥剧墖鐨勫湴鍧� + autoCrop: true, // 鏄惁榛樿鐢熸垚鎴浘妗� + autoCropWidth: 200, // 榛樿鐢熸垚鎴浘妗嗗搴� + autoCropHeight: 200, // 榛樿鐢熸垚鎴浘妗嗛珮搴� + fixedBox: true, // 鍥哄畾鎴浘妗嗗ぇ灏� 涓嶅厑璁告敼鍙� + outputType: "png", // 榛樿鐢熸垚鎴浘涓篜NG鏍煎紡 + filename: 'avatar', // 鏂囦欢鍚嶇О + previews: {} //棰勮鏁版嵁 +}) + +/** 缂栬緫澶村儚 */ +function editCropper() { + open.value = true +} + +/** 鎵撳紑寮瑰嚭灞傜粨鏉熸椂鐨勫洖璋� */ +function modalOpened() { + visible.value = true +} + +/** 瑕嗙洊榛樿涓婁紶琛屼负 */ +function requestUpload() {} + +/** 鍚戝乏鏃嬭浆 */ +function rotateLeft() { + proxy.$refs.cropper.rotateLeft() +} + +/** 鍚戝彸鏃嬭浆 */ +function rotateRight() { + proxy.$refs.cropper.rotateRight() +} + +/** 鍥剧墖缂╂斁 */ +function changeScale(num) { + num = num || 1 + proxy.$refs.cropper.changeScale(num) +} + +/** 涓婁紶棰勫鐞� */ +function beforeUpload(file) { + if (file.type.indexOf("image/") == -1) { + proxy.$modal.msgError("鏂囦欢鏍煎紡閿欒锛岃涓婁紶鍥剧墖绫诲瀷,濡傦細JPG锛孭NG鍚庣紑鐨勬枃浠躲��") + } else { + const reader = new FileReader() + reader.readAsDataURL(file) + reader.onload = () => { + options.img = reader.result + options.filename = file.name + } + } +} + +/** 涓婁紶鍥剧墖 */ +function uploadImg() { + proxy.$refs.cropper.getCropBlob(data => { + let formData = new FormData() + formData.append("avatarfile", data, options.filename) + uploadAvatar(formData).then(response => { + open.value = false + options.img = import.meta.env.VITE_APP_BASE_API + response.imgUrl + userStore.avatar = options.img + proxy.$modal.msgSuccess("淇敼鎴愬姛") + visible.value = false + }) + }) +} + +/** 瀹炴椂棰勮 */ +function realTime(data) { + options.previews = data +} + +/** 鍏抽棴绐楀彛 */ +function closeDialog() { + options.img = userStore.avatar + options.visible = false +} +</script> + +<style lang='scss' scoped> +.user-info-head { + position: relative; + display: inline-block; + height: 120px; +} + +.user-info-head:hover:after { + content: "+"; + position: absolute; + left: 0; + right: 0; + top: 0; + bottom: 0; + color: #eee; + background: rgba(0, 0, 0, 0.5); + font-size: 24px; + font-style: normal; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + cursor: pointer; + line-height: 110px; + border-radius: 50%; +} +</style> \ No newline at end of file diff --git a/src/views/system/user/profile/userInfo.vue b/src/views/system/user/profile/userInfo.vue new file mode 100644 index 0000000..5099ffa --- /dev/null +++ b/src/views/system/user/profile/userInfo.vue @@ -0,0 +1,67 @@ +<template> + <el-form ref="userRef" :model="form" :rules="rules" label-width="80px"> + <el-form-item label="鐢ㄦ埛鏄电О" prop="nickName"> + <el-input v-model="form.nickName" maxlength="30" /> + </el-form-item> + <el-form-item label="鎵嬫満鍙风爜" prop="phonenumber"> + <el-input v-model="form.phonenumber" maxlength="11" /> + </el-form-item> + <el-form-item label="閭" prop="email"> + <el-input v-model="form.email" maxlength="50" /> + </el-form-item> + <el-form-item label="鎬у埆"> + <el-radio-group v-model="form.sex"> + <el-radio value="0">鐢�</el-radio> + <el-radio value="1">濂�</el-radio> + </el-radio-group> + </el-form-item> + <el-form-item> + <el-button type="primary" @click="submit">淇濆瓨</el-button> + <el-button type="danger" @click="close">鍏抽棴</el-button> + </el-form-item> + </el-form> +</template> + +<script setup> +import { updateUserProfile } from "@/api/system/user" + +const props = defineProps({ + user: { + type: Object + } +}) + +const { proxy } = getCurrentInstance() + +const form = ref({}) +const rules = ref({ + nickName: [{ required: true, message: "鐢ㄦ埛鏄电О涓嶈兘涓虹┖", trigger: "blur" }], + email: [{ required: true, message: "閭鍦板潃涓嶈兘涓虹┖", trigger: "blur" }, { type: "email", message: "璇疯緭鍏ユ纭殑閭鍦板潃", trigger: ["blur", "change"] }], + phonenumber: [{ required: true, message: "鎵嬫満鍙风爜涓嶈兘涓虹┖", trigger: "blur" }, { pattern: /^1[3|4|5|6|7|8|9][0-9]\d{8}$/, message: "璇疯緭鍏ユ纭殑鎵嬫満鍙风爜", trigger: "blur" }], +}) + +/** 鎻愪氦鎸夐挳 */ +function submit() { + proxy.$refs.userRef.validate(valid => { + if (valid) { + updateUserProfile(form.value).then(response => { + proxy.$modal.msgSuccess("淇敼鎴愬姛") + props.user.phonenumber = form.value.phonenumber + props.user.email = form.value.email + }) + } + }) +} + +/** 鍏抽棴鎸夐挳 */ +function close() { + proxy.$tab.closePage() +} + +// 鍥炴樉褰撳墠鐧诲綍鐢ㄦ埛淇℃伅 +watch(() => props.user, user => { + if (user) { + form.value = { nickName: user.nickName, phonenumber: user.phonenumber, email: user.email, sex: user.sex } + } +},{ immediate: true }) +</script> diff --git a/src/views/tool/build/CodeTypeDialog.vue b/src/views/tool/build/CodeTypeDialog.vue new file mode 100644 index 0000000..de0beb7 --- /dev/null +++ b/src/views/tool/build/CodeTypeDialog.vue @@ -0,0 +1,71 @@ +<template> + <el-dialog v-model="open" width="500px" title="閫夋嫨鐢熸垚绫诲瀷" @open="onOpen" @close="onClose"> + <el-form ref="codeTypeForm" :model="formData" :rules="rules" label-width="100px"> + <el-form-item label="鐢熸垚绫诲瀷" prop="type"> + <el-radio-group v-model="formData.type"> + <el-radio-button v-for="(item, index) in typeOptions" :key="index" :label="item.value"> + {{ item.label }} + </el-radio-button> + </el-radio-group> + </el-form-item> + <el-form-item v-if="showFileName" label="鏂囦欢鍚�" prop="fileName"> + <el-input v-model="formData.fileName" placeholder="璇疯緭鍏ユ枃浠跺悕" clearable /> + </el-form-item> + </el-form> + + <template #footer> + <el-button @click="onClose">鍙栨秷</el-button> + <el-button type="primary" @click="handelConfirm">纭畾</el-button> + </template> + </el-dialog> +</template> + +<script setup> +const open = defineModel() +const props = defineProps({ + showFileName: Boolean +}) +const emit = defineEmits(['confirm']) +const formData = ref({ + fileName: undefined, + type: 'file' +}) +const codeTypeForm = ref() +const rules = { + fileName: [{ + required: true, + message: '璇疯緭鍏ユ枃浠跺悕', + trigger: 'blur' + }], + type: [{ + required: true, + message: '鐢熸垚绫诲瀷涓嶈兘涓虹┖', + trigger: 'change' + }] +} +const typeOptions = ref([ + { + label: '椤甸潰', + value: 'file' + }, + { + label: '寮圭獥', + value: 'dialog' + } +]) +function onOpen() { + if (props.showFileName) { + formData.value.fileName = `${+new Date()}.vue` + } +} +function onClose() { + open.value = false +} +function handelConfirm() { + codeTypeForm.value.validate(valid => { + if (!valid) return + emit('confirm', { ...formData.value }) + onClose() + }) +} +</script> \ No newline at end of file diff --git a/src/views/tool/build/DraggableItem.vue b/src/views/tool/build/DraggableItem.vue new file mode 100644 index 0000000..9ae2354 --- /dev/null +++ b/src/views/tool/build/DraggableItem.vue @@ -0,0 +1,68 @@ +<template> + <el-col :span="element.span" :class="className" @click.stop="activeItem(element)"> + <el-form-item :label="element.label" :label-width="element.labelWidth ? element.labelWidth + 'px' : null" + :required="element.required" v-if="element.layout === 'colFormItem'"> + <render :key="element.tag" :conf="element" v-model="element.defaultValue" /> + </el-form-item> + <el-row :gutter="element.gutter" :class="element.class" @click.stop="activeItem(element)" v-else> + <span class="component-name"> {{ element.componentName }} </span> + <draggable group="componentsGroup" :animation="340" :list="element.children" class="drag-wrapper" item-key="label" + ref="draggableItemRef" :component-data="getComponentData()"> + <template #item="scoped"> + <draggable-item :key="scoped.element.renderKey" :drawing-list="element.children" :element="scoped.element" + :index="index" :active-id="activeId" :form-conf="formConf" @activeItem="activeItem(scoped.element)" + @copyItem="copyItem(scoped.element, element.children)" + @deleteItem="deleteItem(scoped.index, element.children)" /> + </template> + </draggable> + </el-row> + <span class="drawing-item-copy" title="澶嶅埗" @click.stop="copyItem(element)"> + <el-icon><CopyDocument /></el-icon> + </span> + <span class="drawing-item-delete" title="鍒犻櫎" @click.stop="deleteItem(index)"> + <el-icon><Delete /></el-icon> + </span> + </el-col> +</template> +<script setup name="DraggableItem"> +import draggable from "vuedraggable/dist/vuedraggable.common" +import render from '@/utils/generator/render' + +const props = defineProps({ + element: Object, + index: Number, + drawingList: Array, + activeId: { + type: [String, Number] + }, + formConf: Object +}) +const className = ref('') +const draggableItemRef = ref(null) +const emits = defineEmits(['activeItem', 'copyItem', 'deleteItem']) + +function activeItem(item) { + emits('activeItem', item) +} +function copyItem(item, parent) { + emits('copyItem', item, parent ?? props.drawingList) +} +function deleteItem(item, parent) { + emits('deleteItem', item, parent ?? props.drawingList) +} + +function getComponentData() { + return { + gutter: props.element.gutter, + justify: props.element.justify, + align: props.element.align + } +} + +watch(() => props.activeId, (val) => { + className.value = (props.element.layout === 'rowFormItem' ? 'drawing-row-item' : 'drawing-item') + (val === props.element.formId ? ' active-from-item' : '') + if (props.formConf.unFocusedComponentBorder) { + className.value += ' unfocus-bordered' + } +}, { immediate: true }) +</script> \ No newline at end of file diff --git a/src/views/tool/build/IconsDialog.vue b/src/views/tool/build/IconsDialog.vue new file mode 100644 index 0000000..98d9c13 --- /dev/null +++ b/src/views/tool/build/IconsDialog.vue @@ -0,0 +1,115 @@ +<template> + <div class="icon-dialog"> + <el-dialog v-model="value" width="980px" :close-on-click-modal="false" :modal-append-to-body="false" @open="onOpen" + @close="onClose"> + <template #header="{ close, titleId, titleClass }"> + 閫夋嫨鍥炬爣 + <el-input v-model="key" size="small" :style="{ width: '260px' }" placeholder="璇疯緭鍏ュ浘鏍囧悕绉�" prefix-icon="Search" + clearable /> + </template> + <ul class="icon-ul"> + <li v-for="icon in iconList" :key="icon" :class="active === icon ? 'active-item' : ''" @click="onSelect(icon)"> + <div> + <el-icon :size="30"> + <component :is="icon" /> + </el-icon> + <div>{{ icon }}</div> + </div> + </li> + </ul> + </el-dialog> + </div> +</template> +<script setup> +import * as ElementPlusIconsVue from '@element-plus/icons-vue' +import { watch } from 'vue' + +const iconList = ref([]) +const originList = [] +const key = ref('') +const active = ref('') +const emit = defineEmits(['select']) +const value = defineModel() +for (const [key] of Object.entries(ElementPlusIconsVue)) { + iconList.value.push(key) + originList.push(key) +} + +function onOpen() { } +function onClose() { } +function onSelect(icon) { + active.value = icon + emit('select', icon) + value.value = false +} + +watch(key, (val) => { + if (val) { + iconList.value = originList.filter(name => name.indexOf(val) > -1) + } else { + iconList.value = originList + } +}) +</script> +<style lang="scss" scoped> +.icon-ul { + margin: 0; + padding: 0; + font-size: 0; + + li { + list-style-type: none; + text-align: center; + font-size: 14px; + display: inline-flex; + width: 16.66%; + box-sizing: border-box; + height: 108px; + padding: 6px 6px 6px 6px; + cursor: pointer; + overflow: hidden; + align-items: center; + justify-content: center; + + &:hover { + background: #f2f2f2; + } + + &.active-item { + background: #e1f3fb; + color: #7a6df0 + } + + i { + font-size: 30px; + line-height: 50px; + margin-bottom: 10px; + } + } +} + +.icon-dialog { + :deep() { + .el-dialog { + border-radius: 8px; + margin-bottom: 0; + margin-top: 4vh !important; + display: flex; + flex-direction: column; + max-height: 92vh; + overflow: hidden; + box-sizing: border-box; + + .el-dialog__header { + padding-top: 14px; + } + + .el-dialog__body { + margin: 0 20px 20px 20px; + padding: 0; + overflow: auto; + } + } + } +} +</style> diff --git a/src/views/tool/build/RightPanel.vue b/src/views/tool/build/RightPanel.vue new file mode 100644 index 0000000..5fe80fb --- /dev/null +++ b/src/views/tool/build/RightPanel.vue @@ -0,0 +1,906 @@ +<template> + <div class="right-board"> + <el-tabs v-model="currentTab" stretch class="center-tabs"> + <el-tab-pane label="缁勪欢灞炴��" name="field" /> + <el-tab-pane label="琛ㄥ崟灞炴��" name="form" /> + </el-tabs> + <div class="field-box"> + <a class="document-link" target="_blank" :href="documentLink" title="鏌ョ湅缁勪欢鏂囨。"> + <el-icon> + <Link /> + </el-icon> + </a> + <el-scrollbar class="right-scrollbar"> + <!-- 缁勪欢灞炴�� --> + <el-form v-show="currentTab === 'field' && showField" size="default" label-width="90px" label-position="top" + style=""> + <el-form-item v-if="activeData.changeTag" label="缁勪欢绫诲瀷"> + <el-select v-model="activeData.tagIcon" placeholder="璇烽�夋嫨缁勪欢绫诲瀷" :style="{ width: '100%' }" @change="tagChange"> + <el-option-group v-for="group in tagList" :key="group.label" :label="group.label"> + <el-option v-for="item in group.options" :key="item.label" :label="item.label" :value="item.tagIcon"> + <svg-icon class="node-icon" :icon-class="item.tagIcon" style="margin-right: 10px;" /> + <span> {{ item.label }}</span> + </el-option> + </el-option-group> + </el-select> + </el-form-item> + <el-form-item v-if="activeData.vModel !== undefined" label="瀛楁鍚�"> + <el-input v-model="activeData.vModel" placeholder="璇疯緭鍏ュ瓧娈靛悕锛坴-model锛�" /> + </el-form-item> + <el-form-item v-if="activeData.componentName !== undefined" label="缁勪欢鍚�"> + {{ activeData.componentName }} + </el-form-item> + <el-form-item v-if="activeData.label !== undefined" label="鏍囬"> + <el-input v-model="activeData.label" placeholder="璇疯緭鍏ユ爣棰�" /> + </el-form-item> + <el-form-item v-if="activeData.placeholder !== undefined" label="鍗犱綅鎻愮ず"> + <el-input v-model="activeData.placeholder" placeholder="璇疯緭鍏ュ崰浣嶆彁绀�" /> + </el-form-item> + <el-form-item v-if="activeData['start-placeholder'] !== undefined" label="寮�濮嬪崰浣�"> + <el-input v-model="activeData['start-placeholder']" placeholder="璇疯緭鍏ュ崰浣嶆彁绀�" /> + </el-form-item> + <el-form-item v-if="activeData['end-placeholder'] !== undefined" label="缁撴潫鍗犱綅"> + <el-input v-model="activeData['end-placeholder']" placeholder="璇疯緭鍏ュ崰浣嶆彁绀�" /> + </el-form-item> + <el-form-item v-if="activeData.span !== undefined" label="琛ㄥ崟鏍呮牸"> + <el-slider v-model="activeData.span" :max="24" :min="1" :marks="{ 12: '' }" @change="spanChange" /> + </el-form-item> + <el-form-item v-if="activeData.layout === 'rowFormItem'" label="鏍呮牸闂撮殧"> + <el-input-number v-model="activeData.gutter" :min="0" placeholder="鏍呮牸闂撮殧" /> + </el-form-item> + + <el-form-item v-if="activeData.justify !== undefined" label="姘村钩鎺掑垪"> + <el-select v-model="activeData.justify" placeholder="璇烽�夋嫨姘村钩鎺掑垪" :style="{ width: '100%' }"> + <el-option v-for="(item, index) in justifyOptions" :key="index" :label="item.label" :value="item.value" /> + </el-select> + </el-form-item> + <el-form-item v-if="activeData.align !== undefined" label="鍨傜洿鎺掑垪"> + <el-radio-group v-model="activeData.align"> + <el-radio-button label="top" /> + <el-radio-button label="middle" /> + <el-radio-button label="bottom" /> + </el-radio-group> + </el-form-item> + <el-form-item v-if="activeData.labelWidth !== undefined" label="鏍囩瀹藉害"> + <el-input v-model.number="activeData.labelWidth" type="number" placeholder="璇疯緭鍏ユ爣绛惧搴�" /> + </el-form-item> + <el-form-item v-if="activeData.style && activeData.style.width !== undefined" label="缁勪欢瀹藉害"> + <el-input v-model="activeData.style.width" placeholder="璇疯緭鍏ョ粍浠跺搴�" clearable /> + </el-form-item> + <el-form-item v-if="activeData.vModel !== undefined" label="榛樿鍊�"> + <el-input :value="setDefaultValue(activeData.defaultValue)" placeholder="璇疯緭鍏ラ粯璁ゅ��" + @input="onDefaultValueInput" /> + </el-form-item> + <el-form-item v-if="activeData.tag === 'el-checkbox-group'" label="鑷冲皯搴旈��"> + <el-input-number :value="activeData.min" :min="0" placeholder="鑷冲皯搴旈��" + @input="$set(activeData, 'min', $event ? $event : undefined)" /> + </el-form-item> + <el-form-item v-if="activeData.tag === 'el-checkbox-group'" label="鏈�澶氬彲閫�"> + <el-input-number :value="activeData.max" :min="0" placeholder="鏈�澶氬彲閫�" + @input="$set(activeData, 'max', $event ? $event : undefined)" /> + </el-form-item> + <el-form-item v-if="activeData.prepend !== undefined" label="鍓嶇紑"> + <el-input v-model="activeData.prepend" placeholder="璇疯緭鍏ュ墠缂�" /> + </el-form-item> + <el-form-item v-if="activeData.append !== undefined" label="鍚庣紑"> + <el-input v-model="activeData.append" placeholder="璇疯緭鍏ュ悗缂�" /> + </el-form-item> + <el-form-item v-if="activeData['prefix-icon'] !== undefined" label="鍓嶅浘鏍�"> + <el-input v-model="activeData['prefix-icon']" placeholder="璇疯緭鍏ュ墠鍥炬爣鍚嶇О"> + <template #append> + <el-button icon="Pointer" @click="openIconsDialog('prefix-icon')"> + 閫夋嫨 + </el-button> + </template> + </el-input> + </el-form-item> + <el-form-item v-if="activeData['suffix-icon'] !== undefined" label="鍚庡浘鏍�"> + <el-input v-model="activeData['suffix-icon']" placeholder="璇疯緭鍏ュ悗鍥炬爣鍚嶇О"> + <template #append> + <el-button icon="Pointer" @click="openIconsDialog('suffix-icon')"> + 閫夋嫨 + </el-button> + </template> + </el-input> + </el-form-item> + <el-form-item v-if="activeData.tag === 'el-cascader'" label="閫夐」鍒嗛殧绗�"> + <el-input v-model="activeData.separator" placeholder="璇疯緭鍏ラ�夐」鍒嗛殧绗�" /> + </el-form-item> + <el-form-item v-if="activeData.autosize !== undefined" label="鏈�灏忚鏁�"> + <el-input-number v-model="activeData.autosize.minRows" :min="1" placeholder="鏈�灏忚鏁�" /> + </el-form-item> + <el-form-item v-if="activeData.autosize !== undefined" label="鏈�澶ц鏁�"> + <el-input-number v-model="activeData.autosize.maxRows" :min="1" placeholder="鏈�澶ц鏁�" /> + </el-form-item> + <el-form-item v-if="activeData.min !== undefined" label="鏈�灏忓��"> + <el-input-number v-model="activeData.min" placeholder="鏈�灏忓��" /> + </el-form-item> + <el-form-item v-if="activeData.max !== undefined" label="鏈�澶у��"> + <el-input-number v-model="activeData.max" placeholder="鏈�澶у��" /> + </el-form-item> + <el-form-item v-if="activeData.step !== undefined" label="姝ラ暱"> + <el-input-number v-model="activeData.step" placeholder="姝ユ暟" /> + </el-form-item> + <el-form-item v-if="activeData.tag === 'el-input-number'" label="绮惧害"> + <el-input-number v-model="activeData.precision" :min="0" placeholder="绮惧害" /> + </el-form-item> + <el-form-item v-if="activeData.tag === 'el-input-number'" label="鎸夐挳浣嶇疆"> + <el-radio-group v-model="activeData['controls-position']"> + <el-radio-button label=""> + 榛樿 + </el-radio-button> + <el-radio-button label="right"> + 鍙充晶 + </el-radio-button> + </el-radio-group> + </el-form-item> + <el-form-item v-if="activeData.maxlength !== undefined" label="鏈�澶氳緭鍏�"> + <el-input v-model="activeData.maxlength" placeholder="璇疯緭鍏ュ瓧绗﹂暱搴�"> + <template slot="append"> + 涓瓧绗� + </template> + </el-input> + </el-form-item> + <el-form-item v-if="activeData['active-text'] !== undefined" label="寮�鍚彁绀�"> + <el-input v-model="activeData['active-text']" placeholder="璇疯緭鍏ュ紑鍚彁绀�" /> + </el-form-item> + <el-form-item v-if="activeData['inactive-text'] !== undefined" label="鍏抽棴鎻愮ず"> + <el-input v-model="activeData['inactive-text']" placeholder="璇疯緭鍏ュ叧闂彁绀�" /> + </el-form-item> + <el-form-item v-if="activeData['active-value'] !== undefined" label="寮�鍚��"> + <el-input :value="setDefaultValue(activeData['active-value'])" placeholder="璇疯緭鍏ュ紑鍚��" + @input="onSwitchValueInput($event, 'active-value')" /> + </el-form-item> + <el-form-item v-if="activeData['inactive-value'] !== undefined" label="鍏抽棴鍊�"> + <el-input :value="setDefaultValue(activeData['inactive-value'])" placeholder="璇疯緭鍏ュ叧闂��" + @input="onSwitchValueInput($event, 'inactive-value')" /> + </el-form-item> + <el-form-item v-if="activeData.type !== undefined && 'el-date-picker' === activeData.tag" label="鏃堕棿绫诲瀷"> + <el-select v-model="activeData.type" placeholder="璇烽�夋嫨鏃堕棿绫诲瀷" :style="{ width: '100%' }" + @change="dateTypeChange"> + <el-option v-for="(item, index) in dateOptions" :key="index" :label="item.label" :value="item.value" /> + </el-select> + </el-form-item> + <el-form-item v-if="activeData.name !== undefined" label="鏂囦欢瀛楁鍚�"> + <el-input v-model="activeData.name" placeholder="璇疯緭鍏ヤ笂浼犳枃浠跺瓧娈靛悕" /> + </el-form-item> + <el-form-item v-if="activeData.accept !== undefined" label="鏂囦欢绫诲瀷"> + <el-select v-model="activeData.accept" placeholder="璇烽�夋嫨鏂囦欢绫诲瀷" :style="{ width: '100%' }" clearable> + <el-option label="鍥剧墖" value="image/*" /> + <el-option label="瑙嗛" value="video/*" /> + <el-option label="闊抽" value="audio/*" /> + <el-option label="excel" value=".xls,.xlsx" /> + <el-option label="word" value=".doc,.docx" /> + <el-option label="pdf" value=".pdf" /> + <el-option label="txt" value=".txt" /> + </el-select> + </el-form-item> + <el-form-item v-if="activeData.fileSize !== undefined" label="鏂囦欢澶у皬"> + <el-input v-model.number="activeData.fileSize" placeholder="璇疯緭鍏ユ枃浠跺ぇ灏�"> + <el-select slot="append" v-model="activeData.sizeUnit" :style="{ width: '66px' }"> + <el-option label="KB" value="KB" /> + <el-option label="MB" value="MB" /> + <el-option label="GB" value="GB" /> + </el-select> + </el-input> + </el-form-item> + <el-form-item v-if="activeData.action !== undefined" label="涓婁紶鍦板潃"> + <el-input v-model="activeData.action" placeholder="璇疯緭鍏ヤ笂浼犲湴鍧�" clearable /> + </el-form-item> + <el-form-item v-if="activeData['list-type'] !== undefined" label="鍒楄〃绫诲瀷"> + <el-radio-group v-model="activeData['list-type']" size="small"> + <el-radio-button label="text"> + text + </el-radio-button> + <el-radio-button label="picture"> + picture + </el-radio-button> + <el-radio-button label="picture-card"> + picture-card + </el-radio-button> + </el-radio-group> + </el-form-item> + <el-form-item v-if="activeData.buttonText !== undefined" v-show="'picture-card' !== activeData['list-type']" + label="鎸夐挳鏂囧瓧"> + <el-input v-model="activeData.buttonText" placeholder="璇疯緭鍏ユ寜閽枃瀛�" /> + </el-form-item> + <el-form-item v-if="activeData['range-separator'] !== undefined" label="鍒嗛殧绗�"> + <el-input v-model="activeData['range-separator']" placeholder="璇疯緭鍏ュ垎闅旂" /> + </el-form-item> + <el-form-item v-if="activeData['picker-options'] !== undefined" label="鏃堕棿娈�"> + <el-input v-model="activeData['picker-options'].selectableRange" placeholder="璇疯緭鍏ユ椂闂存" /> + </el-form-item> + <el-form-item v-if="activeData.format !== undefined" label="鏃堕棿鏍煎紡"> + <el-input :value="activeData.format" placeholder="璇疯緭鍏ユ椂闂存牸寮�" @input="setTimeValue($event)" /> + </el-form-item> + <template v-if="['el-checkbox-group', 'el-radio-group', 'el-select'].indexOf(activeData.tag) > -1"> + <el-divider>閫夐」</el-divider> + <draggable :list="activeData.options" :animation="340" group="selectItem" handle=".option-drag" + item-key="label"> + <template #item="{ element, index }"> + <div :key="index" class="select-item"> + <div class="select-line-icon option-drag"> + <i class="el-icon-s-operation" /> + </div> + <el-input v-model="element.label" placeholder="閫夐」鍚�" size="small" /> + <el-input placeholder="閫夐」鍊�" size="small" :value="element.value" + @input="setOptionValue(element, $event)" /> + <div class="close-btn select-line-icon" @click="activeData.options.splice(index, 1)"> + <el-icon> + <Remove /> + </el-icon> + </div> + </div> + </template> + </draggable> + <div> + <el-button icon="CirclePlus" style="margin-left: 8px; margin-top: 10px;" text bg type="primary" + @click="addSelectItem"> + 娣诲姞閫夐」 + </el-button> + </div> + <el-divider /> + </template> + + <template v-if="['el-cascader'].indexOf(activeData.tag) > -1"> + <el-divider>閫夐」</el-divider> + <el-form-item label="鏁版嵁绫诲瀷"> + <el-radio-group v-model="activeData.dataType" size="small"> + <el-radio-button label="dynamic"> + 鍔ㄦ�佹暟鎹� + </el-radio-button> + <el-radio-button label="static"> + 闈欐�佹暟鎹� + </el-radio-button> + </el-radio-group> + </el-form-item> + + <template v-if="activeData.dataType === 'dynamic'"> + <el-form-item label="鏍囩閿悕"> + <el-input v-model="activeData.labelKey" placeholder="璇疯緭鍏ユ爣绛鹃敭鍚�" /> + </el-form-item> + <el-form-item label="鍊奸敭鍚�"> + <el-input v-model="activeData.valueKey" placeholder="璇疯緭鍏ュ�奸敭鍚�" /> + </el-form-item> + <el-form-item label="瀛愮骇閿悕"> + <el-input v-model="activeData.childrenKey" placeholder="璇疯緭鍏ュ瓙绾ч敭鍚�" /> + </el-form-item> + </template> + + <el-tree v-if="activeData.dataType === 'static'" draggable :data="activeData.options" node-key="id" + :expand-on-click-node="false" :render-content="renderContent" /> + <div v-if="activeData.dataType === 'static'"> + <el-button icon="CirclePlus" style="margin-left: 0; margin-top: 10px;" type="primary" text bg + @click="addTreeItem"> + 娣诲姞鐖剁骇 + </el-button> + </div> + <el-divider /> + </template> + + <el-form-item v-if="activeData.optionType !== undefined" label="閫夐」鏍峰紡"> + <el-radio-group v-model="activeData.optionType"> + <el-radio-button label="default"> + 榛樿 + </el-radio-button> + <el-radio-button label="button"> + 鎸夐挳 + </el-radio-button> + </el-radio-group> + </el-form-item> + <el-form-item v-if="activeData['active-color'] !== undefined" label="寮�鍚鑹�"> + <el-color-picker v-model="activeData['active-color']" /> + </el-form-item> + <el-form-item v-if="activeData['inactive-color'] !== undefined" label="鍏抽棴棰滆壊"> + <el-color-picker v-model="activeData['inactive-color']" /> + </el-form-item> + + <el-form-item v-if="activeData['allow-half'] !== undefined" label="鍏佽鍗婇��"> + <el-switch v-model="activeData['allow-half']" /> + </el-form-item> + <el-form-item v-if="activeData['show-text'] !== undefined" label="杈呭姪鏂囧瓧"> + <el-switch v-model="activeData['show-text']" @change="rateTextChange" /> + </el-form-item> + <el-form-item v-if="activeData['show-score'] !== undefined" label="鏄剧ず鍒嗘暟"> + <el-switch v-model="activeData['show-score']" @change="rateScoreChange" /> + </el-form-item> + <el-form-item v-if="activeData['show-stops'] !== undefined" label="鏄剧ず闂存柇鐐�"> + <el-switch v-model="activeData['show-stops']" /> + </el-form-item> + <el-form-item v-if="activeData.range !== undefined" label="鑼冨洿閫夋嫨"> + <el-switch v-model="activeData.range" @change="rangeChange" /> + </el-form-item> + <el-form-item v-if="activeData.border !== undefined && activeData.optionType === 'default'" label="鏄惁甯﹁竟妗�"> + <el-switch v-model="activeData.border" /> + </el-form-item> + <el-form-item v-if="activeData.tag === 'el-color-picker'" label="棰滆壊鏍煎紡"> + <el-select v-model="activeData['color-format']" placeholder="璇烽�夋嫨棰滆壊鏍煎紡" :style="{ width: '100%' }" + @change="colorFormatChange"> + <el-option v-for="(item, index) in colorFormatOptions" :key="index" :label="item.label" + :value="item.value" /> + </el-select> + </el-form-item> + <el-form-item v-if="activeData.size !== undefined && + (activeData.optionType === 'button' || + activeData.border || + activeData.tag === 'el-color-picker')" label="閫夐」灏哄"> + <el-radio-group v-model="activeData.size"> + <el-radio-button label="large"> + 杈冨ぇ + </el-radio-button> + <el-radio-button label="default"> + 榛樿 + </el-radio-button> + <el-radio-button label="small"> + 杈冨皬 + </el-radio-button> + </el-radio-group> + </el-form-item> + <el-form-item v-if="activeData['show-word-limit'] !== undefined" label="杈撳叆缁熻"> + <el-switch v-model="activeData['show-word-limit']" /> + </el-form-item> + <el-form-item v-if="activeData.tag === 'el-input-number'" label="涓ユ牸姝ユ暟"> + <el-switch v-model="activeData['step-strictly']" /> + </el-form-item> + <el-form-item v-if="activeData.tag === 'el-cascader'" label="鏄惁澶氶��"> + <el-switch v-model="activeData.props.props.multiple" /> + </el-form-item> + <el-form-item v-if="activeData.tag === 'el-cascader'" label="灞曠ず鍏ㄨ矾寰�"> + <el-switch v-model="activeData['show-all-levels']" /> + </el-form-item> + <el-form-item v-if="activeData.tag === 'el-cascader'" label="鍙惁绛涢��"> + <el-switch v-model="activeData.filterable" /> + </el-form-item> + <el-form-item v-if="activeData.clearable !== undefined" label="鑳藉惁娓呯┖"> + <el-switch v-model="activeData.clearable" /> + </el-form-item> + <el-form-item v-if="activeData.showTip !== undefined" label="鏄剧ず鎻愮ず"> + <el-switch v-model="activeData.showTip" /> + </el-form-item> + <el-form-item v-if="activeData.multiple !== undefined" label="澶氶�夋枃浠�"> + <el-switch v-model="activeData.multiple" /> + </el-form-item> + <el-form-item v-if="activeData['auto-upload'] !== undefined" label="鑷姩涓婁紶"> + <el-switch v-model="activeData['auto-upload']" /> + </el-form-item> + <el-form-item v-if="activeData.readonly !== undefined" label="鏄惁鍙"> + <el-switch v-model="activeData.readonly" /> + </el-form-item> + <el-form-item v-if="activeData.disabled !== undefined" label="鏄惁绂佺敤"> + <el-switch v-model="activeData.disabled" /> + </el-form-item> + <el-form-item v-if="activeData.tag === 'el-select'" label="鏄惁鍙悳绱�"> + <el-switch v-model="activeData.filterable" /> + </el-form-item> + <el-form-item v-if="activeData.tag === 'el-select'" label="鏄惁澶氶��"> + <el-switch v-model="activeData.multiple" @change="multipleChange" /> + </el-form-item> + <el-form-item v-if="activeData.required !== undefined" label="鏄惁蹇呭~"> + <el-switch v-model="activeData.required" /> + </el-form-item> + + <template v-if="activeData.layoutTree"> + <el-divider>甯冨眬缁撴瀯鏍�</el-divider> + <el-tree :data="[activeData]" :props="layoutTreeProps" node-key="renderKey" default-expand-all draggable> + <template #default="{ node, data }"> + <span class="node-label"> + <svg-icon class="node-icon" :icon-class="data.tagIcon" style="margin-right: 5px;" /> + {{ node.label }} + </span> + </template> + </el-tree> + </template> + + <template v-if="activeData.layout === 'colFormItem'"> + <el-divider>姝e垯鏍¢獙</el-divider> + <div v-for="(item, index) in activeData.regList" :key="index" class="reg-item"> + <span class="close-btn" @click="activeData.regList.splice(index, 1)"> + <el-icon> + <Close /> + </el-icon> + </span> + <el-form-item label="琛ㄨ揪寮�"> + <el-input v-model="item.pattern" placeholder="璇疯緭鍏ユ鍒�" /> + </el-form-item> + <el-form-item label="閿欒鎻愮ず" style="margin-bottom:0"> + <el-input v-model="item.message" placeholder="璇疯緭鍏ラ敊璇彁绀�" /> + </el-form-item> + </div> + <div> + <el-button icon="CirclePlus" style="margin-left: 0; margin-top: 10px;" type="primary" text bg + @click="addReg"> + 娣诲姞瑙勫垯 + </el-button> + </div> + </template> + </el-form> + <!-- 琛ㄥ崟灞炴�� --> + <el-form v-show="currentTab === 'form'" label-width="90px" label-position="top"> + <el-form-item label="琛ㄥ崟鍚�"> + <el-input v-model="formConf.formRef" placeholder="璇疯緭鍏ヨ〃鍗曞悕锛坮ef锛�" /> + </el-form-item> + <el-form-item label="琛ㄥ崟妯″瀷"> + <el-input v-model="formConf.formModel" placeholder="璇疯緭鍏ユ暟鎹ā鍨�" /> + </el-form-item> + <el-form-item label="鏍¢獙妯″瀷"> + <el-input v-model="formConf.formRules" placeholder="璇疯緭鍏ユ牎楠屾ā鍨�" /> + </el-form-item> + <el-form-item label="琛ㄥ崟灏哄"> + <el-radio-group v-model="formConf.size"> + <el-radio-button label="large" value="杈冨ぇ" /> + <el-radio-button label="default" value="榛樿" /> + <el-radio-button label="small" value="杈冨皬" /> + </el-radio-group> + </el-form-item> + <el-form-item label="鏍囩瀵归綈"> + <el-radio-group v-model="formConf.labelPosition"> + <el-radio-button label="left" value="宸﹀榻�" /> + <el-radio-button label="right" value="鍙冲榻�" /> + <el-radio-button label="top" value="椤堕儴瀵归綈" /> + </el-radio-group> + </el-form-item> + <el-form-item label="鏍囩瀹藉害"> + <el-input-number v-model="formConf.labelWidth" placeholder="鏍囩瀹藉害" /> + </el-form-item> + <el-form-item label="鏍呮牸闂撮殧"> + <el-input-number v-model="formConf.gutter" :min="0" placeholder="鏍呮牸闂撮殧" /> + </el-form-item> + <el-form-item label="绂佺敤琛ㄥ崟"> + <el-switch v-model="formConf.disabled" /> + </el-form-item> + <el-form-item label="琛ㄥ崟鎸夐挳"> + <el-switch v-model="formConf.formBtns" /> + </el-form-item> + <el-form-item label="鏄剧ず鏈�変腑缁勪欢杈规"> + <el-switch v-model="formConf.unFocusedComponentBorder" /> + </el-form-item> + </el-form> + </el-scrollbar> + </div> + <icons-dialog v-model="iconsVisible" :current="activeData[currentIconModel]" @select="setIcon" /> + <treeNode-dialog v-model="dialogVisible" @commit="addNode" /> + + </div> +</template> + +<script setup> +import draggable from "vuedraggable/dist/vuedraggable.common" +import { isNumberStr } from '@/utils/index' +import IconsDialog from './IconsDialog' +import TreeNodeDialog from './TreeNodeDialog' +import { inputComponents, selectComponents } from '@/utils/generator/config' + +const { proxy } = getCurrentInstance() +const dateTimeFormat = { + date: 'YYYY-MM-DD', + week: 'YYYY 绗� ww 鍛�', + month: 'YYYY-MM', + year: 'YYYY', + datetime: 'YYYY-MM-DD HH:mm:ss', + daterange: 'YYYY-MM-DD', + monthrange: 'YYYY-MM', + datetimerange: 'YYYY-MM-DD HH:mm:ss' +} +const props = defineProps({ + showField: Boolean, + activeData: Object, + formConf: Object +}) + +const data = reactive({ + currentTab: 'field', + currentNode: null, + dialogVisible: false, + iconsVisible: false, + currentIconModel: null, + dateTypeOptions: [ + { + label: '鏃�(date)', + value: 'date' + }, + { + label: '鍛�(week)', + value: 'week' + }, + { + label: '鏈�(month)', + value: 'month' + }, + { + label: '骞�(year)', + value: 'year' + }, + { + label: '鏃ユ湡鏃堕棿(datetime)', + value: 'datetime' + } + ], + dateRangeTypeOptions: [ + { + label: '鏃ユ湡鑼冨洿(daterange)', + value: 'daterange' + }, + { + label: '鏈堣寖鍥�(monthrange)', + value: 'monthrange' + }, + { + label: '鏃ユ湡鏃堕棿鑼冨洿(datetimerange)', + value: 'datetimerange' + } + ], + colorFormatOptions: [ + { + label: 'hex', + value: 'hex' + }, + { + label: 'rgb', + value: 'rgb' + }, + { + label: 'rgba', + value: 'rgba' + }, + { + label: 'hsv', + value: 'hsv' + }, + { + label: 'hsl', + value: 'hsl' + } + ], + justifyOptions: [ + { + label: 'start', + value: 'start' + }, + { + label: 'end', + value: 'end' + }, + { + label: 'center', + value: 'center' + }, + { + label: 'space-around', + value: 'space-around' + }, + { + label: 'space-between', + value: 'space-between' + } + ], + layoutTreeProps: { + label(data, node) { + return data.componentName || `${data.label}: ${data.vModel}` + } + } +}) + +const { currentTab, currentNode, dialogVisible, iconsVisible, currentIconModel, dateTypeOptions, dateRangeTypeOptions, colorFormatOptions, justifyOptions, layoutTreeProps } = toRefs(data) + +const documentLink = computed(() => props.activeData.document || 'https://element-plus.org/zh-CN/guide/installation') + +const dateOptions = computed(() => { + if (props.activeData.type !== undefined && props.activeData.tag === 'el-date-picker') { + if (props.activeData['start-placeholder'] === undefined) { + return dateTypeOptions.value + } + return dateRangeTypeOptions.value + } + return [] +}) + +const tagList = ref([ + { + label: '杈撳叆鍨嬬粍浠�', + options: inputComponents + }, + { + label: '閫夋嫨鍨嬬粍浠�', + options: selectComponents + } +]) + +const emit = defineEmits(['tag-change']) + +function addReg() { + props.activeData.regList.push({ + pattern: '', + message: '' + }) +} +function addSelectItem() { + props.activeData.options.push({ + label: '', + value: '' + }) +} + +function addTreeItem() { + ++proxy.idGlobal + dialogVisible.value = true + currentNode.value = props.activeData.options +} + +function renderContent(h, { node, data, store }) { + return h('div', { + class: "custom-tree-node" + }, [ + h('span', node.label), + h('span', { + class: "node-operation" + }, [ + h(resolveComponent('el-link'), { + type: "primary", + icon: "Plus", + underline: false, + onClick: () => { + append(data) + + } + }), + h(resolveComponent('el-link'), { + type: "danger", + icon: "Delete", + underline: false, + style: "margin-left: 5px;", + onClick: () => { + remove(node, data) + } + }) + ]) + ]) +} +function append(data) { + if (!data.children) { + data.children = [] + } + dialogVisible.value = true + currentNode.value = data.children +} +function remove(node, data) { + const { parent } = node + const children = parent.data.children || parent.data + const index = children.findIndex(d => d.id === data.id) + children.splice(index, 1) +} +function addNode(data) { + currentNode.value.push(data) +} + +function setOptionValue(item, val) { + item.value = isNumberStr(val) ? +val : val +} +function setDefaultValue(val) { + if (Array.isArray(val)) { + return val.join(',') + } + if (['string', 'number'].indexOf(val) > -1) { + return val + } + if (typeof val === 'boolean') { + return `${val}` + } + return val +} + +function onDefaultValueInput(str) { + if (Array.isArray(props.activeData.defaultValue)) { + // 鏁扮粍 + props.activeData.defaultValue = str.split(',').map(val => (isNumberStr(val) ? +val : val)) + } else if (['true', 'false'].indexOf(str) > -1) { + // 甯冨皵 + props.activeData.defaultValue = JSON.parse(str) + } else { + // 瀛楃涓插拰鏁板瓧 + props.activeData.defaultValue = isNumberStr(str) ? +str : str + } +} + +function onSwitchValueInput(val, name) { + if (['true', 'false'].indexOf(val) > -1) { + props.activeData[name] = JSON.parse(val) + } else { + props.activeData[name] = isNumberStr(val) ? +val : val + } +} + +function setTimeValue(val, type) { + const valueFormat = type === 'week' ? dateTimeFormat.date : val + props.activeData.defaultValue = null + props.activeData['value-format'] = valueFormat + props.activeData.format = val +} + +function spanChange(val) { + props.formConf.span = val +} + +function multipleChange(val) { + props.activeData.defaultValue = val ? [] : '' +} + +function dateTypeChange(val) { + setTimeValue(dateTimeFormat[val], val) +} + +function rangeChange(val) { + props.activeData.defaultValue = val ? [props.activeData.min, props.activeData.max] : props.activeData.min +} + +function rateTextChange(val) { + if (val) props.activeData['show-score'] = false +} + +function rateScoreChange(val) { + if (val) props.activeData['show-text'] = false +} + +function colorFormatChange(val) { + props.activeData.defaultValue = null + props.activeData['show-alpha'] = val.indexOf('a') > -1 + props.activeData.renderKey = +new Date() // 鏇存柊renderKey,閲嶆柊娓叉煋璇ョ粍浠� +} + +function openIconsDialog(model) { + iconsVisible.value = true + currentIconModel.value = model +} + +function setIcon(val) { + props.activeData[currentIconModel.value] = val +} + +function tagChange(tagIcon) { + let target = inputComponents.find(item => item.tagIcon === tagIcon) + if (!target) target = selectComponents.find(item => item.tagIcon === tagIcon) + emit('tag-change', target) +} +</script> + +<style lang="scss" scoped> +.right-board { + width: 350px; + position: absolute; + right: 0; + top: 0; + padding-top: 3px; + + &:deep() { + .el-tabs__header { + margin: 0; + } + + .el-input-group__append .el-button { + display: inline-flex; + } + } + + .field-box { + position: relative; + height: calc(100vh - 50px - 40px - 42px); + box-sizing: border-box; + overflow: hidden; + } + + .el-scrollbar { + height: 100%; + + &:deep() { + .el-scrollbar__view { + padding: 30px 20px; + } + + } + } +} + +.reg-item { + padding: 12px 6px; + background: var(--el-border-color-extra-light); + position: relative; + border-radius: 4px; + + .close-btn { + position: absolute; + right: -6px; + top: -6px; + display: flex; + align-items: center; + justify-content: center; + width: 16px; + height: 16px; + line-height: 16px; + background: rgba(0, 0, 0, .2); + border-radius: 50%; + color: #fff; + z-index: 1; + cursor: pointer; + font-size: 12px; + } +} + +.select-item { + display: flex; + border: 1px dashed #fff; + box-sizing: border-box; + + & .close-btn { + cursor: pointer; + color: #f56c6c; + } + + & .el-input+.el-input { + margin-left: 4px; + } +} + +.select-item+.select-item { + margin-top: 4px; +} + +.select-item.sortable-chosen { + border: 1px dashed #409eff; +} + +.select-line-icon { + line-height: 32px; + font-size: 22px; + padding: 0 4px; + color: #777; +} + +.option-drag { + cursor: move; +} + +.time-range { + .el-date-editor { + width: 227px; + } + + :deep() { + .el-icon-time { + display: none; + } + } +} + +.document-link { + position: absolute; + display: flex; + width: 26px; + height: 26px; + top: 0; + left: 0; + cursor: pointer; + background: #409eff; + z-index: 1; + border-radius: 0 0 6px 0; + justify-content: center; + align-items: center; + color: #fff; + font-size: 18px; +} + +.node-label { + font-size: 14px; +} + +.node-icon { + color: #bebfc3; +} + +.custom-tree-node { + flex: 1; + display: flex; + align-items: center; + justify-content: space-between; + font-size: 14px; + padding-right: 8px; +} +</style> \ No newline at end of file diff --git a/src/views/tool/build/TreeNodeDialog.vue b/src/views/tool/build/TreeNodeDialog.vue new file mode 100644 index 0000000..372d3af --- /dev/null +++ b/src/views/tool/build/TreeNodeDialog.vue @@ -0,0 +1,93 @@ +<template> + <div> + <el-dialog title="娣诲姞閫夐」" v-model="open" width="800px" :close-on-click-modal="false" :modal-append-to-body="false" + @open="onOpen" @close="onClose"> + <el-form ref="treeNodeForm" :model="formData" :rules="rules" label-width="100px"> + <el-col :span="24"> + <el-form-item label="閫夐」鍚�" prop="label"> + <el-input v-model="formData.label" placeholder="璇疯緭鍏ラ�夐」鍚�" clearable /> + </el-form-item> + </el-col> + <el-col :span="24"> + <el-form-item label="閫夐」鍊�" prop="value"> + <el-input v-model="formData.value" placeholder="璇疯緭鍏ラ�夐」鍊�" clearable> + <template #append> + <el-select v-model="dataType" :style="{ width: '100px' }"> + <el-option v-for="(item, index) in dataTypeOptions" :key="index" :label="item.label" :value="item.value" + :disabled="item.disabled" /> + </el-select> + </template> + + </el-input> + </el-form-item> + </el-col> + </el-form> + <template #footer> + <div class="dialog-footer"> + <el-button type="primary" @click="handelConfirm">纭� 瀹�</el-button> + <el-button @click="onClose">鍙� 娑�</el-button> + </div> + </template> + </el-dialog> + </div> +</template> +<script setup> +const open = defineModel() +const emit = defineEmits(['confirm']) +const formData = ref({ + label: undefined, + value: undefined +}) +const rules = { + label: [ + { + required: true, + message: '璇疯緭鍏ラ�夐」鍚�', + trigger: 'blur' + } + ], + value: [ + { + required: true, + message: '璇疯緭鍏ラ�夐」鍊�', + trigger: 'blur' + } + ] +} +const dataType = ref('string') +const dataTypeOptions = ref([ + { + label: '瀛楃涓�', + value: 'string' + }, + { + label: '鏁板瓧', + value: 'number' + } +]) +const id = ref(100) +const treeNodeForm = ref() + +function onOpen() { + formData.value = { + label: undefined, + value: undefined + } +} + +function onClose() { + open.value = false +} + +function handelConfirm() { + treeNodeForm.value.validate(valid => { + if (!valid) return + if (dataType.value === 'number') { + formData.value.value = parseFloat(formData.value.value) + } + formData.value.id = id.value++ + emit('commit', formData.value) + onClose() + }) +} +</script> diff --git a/src/views/tool/build/index.vue b/src/views/tool/build/index.vue new file mode 100644 index 0000000..e630740 --- /dev/null +++ b/src/views/tool/build/index.vue @@ -0,0 +1,653 @@ +<template> + <div class="container"> + <div class="left-board"> + <div class="logo-wrapper"> + <div class="logo"> + <img :src="logo" alt="logo"> Form Generator + </div> + </div> + <el-scrollbar class="left-scrollbar"> + <div class="components-list"> + <div class="components-title"> + <svg-icon icon-class="component" />杈撳叆鍨嬬粍浠� + </div> + <draggable class="components-draggable" :list="inputComponents" + :group="{ name: 'componentsGroup', pull: 'clone', put: false }" :clone="cloneComponent" + draggable=".components-item" :sort="false" @end="onEnd" item-key="label"> + <template #item="{ element, index }"> + <div :key="index" class="components-item" @click="addComponent(element)"> + <div class="components-body"> + <svg-icon :icon-class="element.tagIcon" /> + {{ element.label }} + </div> + </div> + </template> + </draggable> + <div class="components-title"> + <svg-icon icon-class="component" />閫夋嫨鍨嬬粍浠� + </div> + <draggable class="components-draggable" :list="selectComponents" + :group="{ name: 'componentsGroup', pull: 'clone', put: false }" :clone="cloneComponent" + draggable=".components-item" :sort="false" @end="onEnd" item-key="label"> + <template #item="{ element, index }"> + <div :key="index" class="components-item" @click="addComponent(element)"> + <div class="components-body"> + <svg-icon :icon-class="element.tagIcon" /> + {{ element.label }} + </div> + </div> + </template> + </draggable> + <div class="components-title"> + <svg-icon icon-class="component" /> 甯冨眬鍨嬬粍浠� + </div> + <draggable class="components-draggable" :list="layoutComponents" + :group="{ name: 'componentsGroup', pull: 'clone', put: false }" :clone="cloneComponent" + draggable=".components-item" :sort="false" @end="onEnd" item-key="label"> + <template #item="{ element, index }"> + <div :key="index" class="components-item" @click="addComponent(element)"> + <div class="components-body"> + <svg-icon :icon-class="element.tagIcon" /> + {{ element.label }} + </div> + </div> + </template> + </draggable> + </div> + </el-scrollbar> + </div> + <div class="center-board"> + <div class="action-bar"> + <el-button icon="Download" type="primary" text @click="download"> + 瀵煎嚭vue鏂囦欢 + </el-button> + <el-button class="copy-btn-main" icon="DocumentCopy" type="primary" text @click="copy"> + 澶嶅埗浠g爜 + </el-button> + <el-button class="delete-btn" icon="Delete" text @click="empty" type="danger"> + 娓呯┖ + </el-button> + </div> + <el-scrollbar class="center-scrollbar"> + <el-row class="center-board-row" :gutter="formConf.gutter"> + <el-form :size="formConf.size" :label-position="formConf.labelPosition" :disabled="formConf.disabled" + :label-width="formConf.labelWidth + 'px'"> + <draggable class="drawing-board" :list="drawingList" :animation="340" group="componentsGroup" + item-key="label"> + <template #item="{ element, index }"> + <draggable-item :key="element.renderKey" :drawing-list="drawingList" :element="element" :index="index" + :active-id="activeId" :form-conf="formConf" @activeItem="activeFormItem" @copyItem="drawingItemCopy" + @deleteItem="drawingItemDelete" /> + </template> + </draggable> + <div v-show="!drawingList.length" class="empty-info"> + 浠庡乏渚ф嫋鍏ユ垨鐐归�夌粍浠惰繘琛岃〃鍗曡璁� + </div> + </el-form> + </el-row> + </el-scrollbar> + </div> + <right-panel :active-data="activeData" :form-conf="formConf" :show-field="!!drawingList.length" + @tag-change="tagChange" /> + + <code-type-dialog v-model="dialogVisible" title="閫夋嫨鐢熸垚绫诲瀷" :showFileName="showFileName" @confirm="generate" /> + <input id="copyNode" type="hidden"> + </div> +</template> + +<script setup> +import draggable from "vuedraggable/dist/vuedraggable.common" +import ClipboardJS from 'clipboard' +import beautifier from 'js-beautify' +import logo from '@/assets/logo/logo.png' +import { inputComponents, selectComponents, layoutComponents, formConf as formConfData } from '@/utils/generator/config' +import { beautifierConf } from '@/utils/index' +import drawingDefalut from '@/utils/generator/drawingDefalut' +import { makeUpHtml, vueTemplate, vueScript, cssStyle } from '@/utils/generator/html' +import { makeUpJs } from '@/utils/generator/js' +import { makeUpCss } from '@/utils/generator/css' +import Download from '@/plugins/download' +import { ElNotification } from 'element-plus' +import DraggableItem from './DraggableItem' +import RightPanel from './RightPanel' +import CodeTypeDialog from './CodeTypeDialog' +import { onMounted, watch } from 'vue' + +const drawingList = ref(drawingDefalut) +const { proxy } = getCurrentInstance() +const dialogVisible = ref(false) +const showFileName = ref(false) +const operationType = ref('') +const idGlobal = ref(100) +const activeData = ref(drawingDefalut[0]) +const activeId = ref(drawingDefalut[0].formId) +const generateConf = ref(null) +const formData = ref({}) +const formConf = ref(formConfData) +let oldActiveId +let tempActiveData + +function activeFormItem(element) { + activeData.value = element + activeId.value = element.formId +} +function copy() { + dialogVisible.value = true + showFileName.value = false + operationType.value = 'copy' +} +function download() { + dialogVisible.value = true + showFileName.value = true + operationType.value = 'download' +} +function empty() { + proxy.$modal.confirm('纭畾瑕佹竻绌烘墍鏈夌粍浠跺悧锛�', '鎻愮ず', { type: 'warning' }).then(() => { + idGlobal.value = 100 + drawingList.value = [] + } + ) +} + +function onEnd(obj, a) { + if (obj.from !== obj.to) { + activeData.value = tempActiveData + activeId.value = idGlobal.value + } +} + +function addComponent(item) { + const clone = cloneComponent(item) + drawingList.value.push(clone) + activeFormItem(clone) +} + +function cloneComponent(origin) { + const clone = JSON.parse(JSON.stringify(origin)) + clone.formId = ++idGlobal.value + clone.span = formConf.value.span + clone.renderKey = +new Date() // 鏀瑰彉renderKey鍚庡彲浠ュ疄鐜板己鍒舵洿鏂扮粍浠� + if (!clone.layout) clone.layout = 'colFormItem' + if (clone.layout === 'colFormItem') { + clone.vModel = `field${idGlobal.value}` + clone.placeholder !== undefined && (clone.placeholder += clone.label) + tempActiveData = clone + } else if (clone.layout === 'rowFormItem') { + delete clone.label + clone.componentName = `row${idGlobal.value}` + clone.gutter = formConf.value.gutter + tempActiveData = clone + } + return tempActiveData +} + +function drawingItemCopy(item, parent) { + let clone = JSON.parse(JSON.stringify(item)) + clone = createIdAndKey(clone) + parent.push(clone) + activeFormItem(clone) +} + + +function createIdAndKey(item) { + item.formId = ++idGlobal.value + item.renderKey = +new Date() + if (item.layout === 'colFormItem') { + item.vModel = `field${idGlobal.value}` + } else if (item.layout === 'rowFormItem') { + item.componentName = `row${idGlobal.value}` + } + if (Array.isArray(item.children)) { + item.children = item.children.map(childItem => createIdAndKey(childItem)) + } + return item +} + +function drawingItemDelete(index, parent) { + parent.splice(index, 1) + nextTick(() => { + const len = drawingList.value.length + if (len) { + activeFormItem(drawingList.value[len - 1]) + } + }) +} + +function tagChange(newTag) { + newTag = cloneComponent(newTag) + newTag.vModel = activeData.value.vModel + newTag.formId = activeId.value + newTag.span = activeData.value.span + delete activeData.value.tag + delete activeData.value.tagIcon + delete activeData.value.document + Object.keys(newTag).forEach(key => { + if (activeData.value[key] !== undefined + && typeof activeData.value[key] === typeof newTag[key]) { + newTag[key] = activeData.value[key] + } + }) + activeData.value = newTag + updateDrawingList(newTag, drawingList.value) +} + + +function updateDrawingList(newTag, list) { + const index = list.findIndex(item => item.formId === activeId.value) + if (index > -1) { + list.splice(index, 1, newTag) + } else { + list.forEach(item => { + if (Array.isArray(item.children)) updateDrawingList(newTag, item.children) + }) + } +} +function generate(data) { + generateConf.value = data + nextTick(() => { + switch (operationType.value) { + case 'copy': + execCopy(data) + break + case 'download': + execDownload(data) + break + default: + break + } + }) +} + +function execDownload(data) { + const codeStr = generateCode() + const blob = new Blob([codeStr], { type: 'text/plain;charset=utf-8' }) + Download.saveAs(blob, data.fileName) +} + +function execCopy(data) { + document.getElementById('copyNode').click() +} +function AssembleFormData() { + formData.value = { fields: JSON.parse(JSON.stringify(drawingList.value)), ...formConf.value } +} +function generateCode() { + const { type } = generateConf.value + AssembleFormData() + const script = vueScript(makeUpJs(formData.value, type)) + const html = vueTemplate(makeUpHtml(formData.value, type)) + const css = cssStyle(makeUpCss(formData.value)) + return beautifier.html(html + script + css, beautifierConf.html) +} +watch(() => activeData.value.label, (val, oldVal) => { + if ( + activeData.value.placeholder === undefined + || !activeData.value.tag + || oldActiveId !== activeId.value + ) { + return + } + activeData.value.placeholder = activeData.value.placeholder.replace(oldVal, '') + val +}) +watch(activeId, (val) => { + oldActiveId = val +}, { immediate: true }) + +onMounted(() => { + const clipboard = new ClipboardJS('#copyNode', { + text: trigger => { + const codeStr = generateCode() + ElNotification({ title: '鎴愬姛', message: '浠g爜宸插鍒跺埌鍓垏鏉匡紝鍙矘璐淬��', type: 'success' }) + return codeStr + } + }) + clipboard.on('error', e => { + proxy.$modal.msgError('浠g爜澶嶅埗澶辫触') + }) +}) +</script> + +<style lang='scss'> +$lighterBlue: #409EFF; + +.container { + position: relative; + width: 100%; + background-color: var(--el-bg-color-overlay); + height: calc(100vh - 50px - 40px); + overflow: hidden; + + .left-board { + width: 260px; + position: absolute; + left: 0; + top: 0; + height: calc(100vh - 50px - 40px); + + .logo-wrapper { + position: relative; + height: 42px; + border-bottom: 1px solid var(--el-border-color-extra-light); + box-sizing: border-box; + + .logo { + position: absolute; + left: 12px; + top: 6px; + line-height: 30px; + color: #00afff; + font-weight: 600; + font-size: 17px; + white-space: nowrap; + + >img { + width: 30px; + height: 30px; + vertical-align: top; + } + + .github { + display: inline-block; + vertical-align: sub; + margin-left: 15px; + + >img { + height: 22px; + } + } + } + } + + .left-scrollbar { + .el-scrollbar__wrap { + box-sizing: border-box; + overflow-x: hidden !important; + margin-bottom: 0 !important; + + .components-list { + padding: 8px; + box-sizing: border-box; + height: 100%; + + .components-title { + font-size: 14px; + // color: #222; + margin: 6px 2px; + + .svg-icon { + // color: #666; + font-size: 18px; + margin-right: 5px; + } + } + + .components-draggable { + padding-bottom: 20px; + + .components-item { + display: inline-block; + width: 48%; + margin: 1%; + transition: transform 0ms !important; + + .components-body { + padding: 8px 10px; + background: var(--el-border-color-extra-light); + font-size: 12px; + cursor: move; + border: 1px dashed var(--el-border-color-extra-light); + border-radius: 3px; + + .svg-icon { + // color: #777; + font-size: 15px; + margin-right: 5px; + } + + &:hover { + border: 1px dashed #787be8; + color: #787be8; + + .svg-icon { + color: #787be8; + } + } + } + } + } + + + } + } + } + } + + .center-board { + height: calc(100vh - 50px - 40px); + width: auto; + margin: 0 350px 0 260px; + box-sizing: border-box; + + .action-bar { + position: relative; + height: 42px; + padding: 0 15px; + box-sizing: border-box; + ; + border: 1px solid var(--el-border-color-extra-light); + border-top: none; + border-left: none; + display: flex; + align-items: center; + justify-content: flex-end; + + u .delete-btn { + color: #F56C6C; + } + } + + .center-scrollbar { + height: calc(100vh - 50px - 40px - 42px); + overflow: hidden; + border-left: 1px solid var(--el-border-color-extra-light); + border-right: 1px solid var(--el-border-color-extra-light); + box-sizing: border-box; + + .el-scrollbar__view { + overflow-x: hidden; + } + + .center-board-row { + padding: 12px 12px 15px 12px; + box-sizing: border-box; + + &>.el-form { + // 69 = 12+15+42 + height: calc(100vh - 50px - 40px - 69px); + flex: 1; + + .drawing-board { + height: 100%; + position: relative; + + .components-body { + padding: 0; + margin: 0; + font-size: 0; + } + + .sortable-ghost { + position: relative; + display: block; + overflow: hidden; + + &::before { + content: " "; + position: absolute; + left: 0; + right: 0; + top: 0; + height: 3px; + background: rgb(89, 89, 223); + z-index: 2; + } + } + + .components-item.sortable-ghost { + width: 100%; + height: 60px; + background: var(--el-border-color-extra-light); + } + + .active-from-item { + &>.el-form-item { + background: var(--el-border-color-extra-light); + border-radius: 6px; + } + + &>.drawing-item-copy, + &>.drawing-item-delete { + display: initial; + } + + &>.component-name { + color: $lighterBlue; + } + + .el-input__wrapper { + box-shadow: 0 0 0 1px var(--el-input-hover-border-color) inset; + } + } + + .el-form-item { + margin-bottom: 15px; + } + } + + .drawing-item { + position: relative; + cursor: move; + + &.unfocus-bordered:not(.activeFromItem)>div:first-child { + border: 1px dashed #ccc; + } + + .el-form-item { + padding: 12px 10px; + } + } + + .drawing-row-item { + position: relative; + cursor: move; + box-sizing: border-box; + border: 1px dashed #ccc; + border-radius: 3px; + padding: 0 2px; + margin-bottom: 15px; + + .drawing-row-item { + margin-bottom: 2px; + } + + .el-col { + margin-top: 22px; + } + + .el-form-item { + margin-bottom: 0; + } + + .drag-wrapper { + min-height: 80px; + flex: 1; + display: flex; + flex-wrap: wrap; + } + + &.active-from-item { + border: 1px dashed $lighterBlue; + } + + .component-name { + position: absolute; + top: 0; + left: 0; + font-size: 12px; + color: #bbb; + display: inline-block; + padding: 0 6px; + } + } + + .drawing-item, + .drawing-row-item { + &:hover { + &>.el-form-item { + background: var(--el-border-color-extra-light); + border-radius: 6px; + } + + &>.drawing-item-copy, + &>.drawing-item-delete { + display: initial; + } + } + + &>.drawing-item-copy, + &>.drawing-item-delete { + display: none; + position: absolute; + top: -10px; + width: 22px; + height: 22px; + line-height: 22px; + text-align: center; + border-radius: 50%; + font-size: 12px; + border: 1px solid; + cursor: pointer; + z-index: 1; + } + + &>.drawing-item-copy { + right: 56px; + border-color: $lighterBlue; + color: $lighterBlue; + background: #fff; + + &:hover { + background: $lighterBlue; + color: #fff; + } + } + + &>.drawing-item-delete { + right: 24px; + border-color: #F56C6C; + color: #F56C6C; + background: #fff; + + &:hover { + background: #F56C6C; + color: #fff; + } + } + } + + .empty-info { + position: absolute; + top: 46%; + left: 0; + right: 0; + text-align: center; + font-size: 18px; + color: #ccb1ea; + letter-spacing: 4px; + } + + } + } + } + } +} +</style> diff --git a/src/views/tool/gen/basicInfoForm.vue b/src/views/tool/gen/basicInfoForm.vue new file mode 100644 index 0000000..92c3ca9 --- /dev/null +++ b/src/views/tool/gen/basicInfoForm.vue @@ -0,0 +1,48 @@ +<template> + <el-form ref="basicInfoForm" :model="info" :rules="rules" label-width="150px"> + <el-row> + <el-col :span="12"> + <el-form-item label="琛ㄥ悕绉�" prop="tableName"> + <el-input placeholder="璇疯緭鍏ヤ粨搴撳悕绉�" v-model="info.tableName" /> + </el-form-item> + </el-col> + <el-col :span="12"> + <el-form-item label="琛ㄦ弿杩�" prop="tableComment"> + <el-input placeholder="璇疯緭鍏�" v-model="info.tableComment" /> + </el-form-item> + </el-col> + <el-col :span="12"> + <el-form-item label="瀹炰綋绫诲悕绉�" prop="className"> + <el-input placeholder="璇疯緭鍏�" v-model="info.className" /> + </el-form-item> + </el-col> + <el-col :span="12"> + <el-form-item label="浣滆��" prop="functionAuthor"> + <el-input placeholder="璇疯緭鍏�" v-model="info.functionAuthor" /> + </el-form-item> + </el-col> + <el-col :span="24"> + <el-form-item label="澶囨敞" prop="remark"> + <el-input type="textarea" :rows="3" v-model="info.remark"></el-input> + </el-form-item> + </el-col> + </el-row> + </el-form> +</template> + +<script setup> +defineProps({ + info: { + type: Object, + default: null + } +}) + +// 琛ㄥ崟鏍¢獙 +const rules = ref({ + tableName: [{ required: true, message: "璇疯緭鍏ヨ〃鍚嶇О", trigger: "blur" }], + tableComment: [{ required: true, message: "璇疯緭鍏ヨ〃鎻忚堪", trigger: "blur" }], + className: [{ required: true, message: "璇疯緭鍏ュ疄浣撶被鍚嶇О", trigger: "blur" }], + functionAuthor: [{ required: true, message: "璇疯緭鍏ヤ綔鑰�", trigger: "blur" }] +}) +</script> diff --git a/src/views/tool/gen/createTable.vue b/src/views/tool/gen/createTable.vue new file mode 100644 index 0000000..ef6f8f3 --- /dev/null +++ b/src/views/tool/gen/createTable.vue @@ -0,0 +1,46 @@ +<template> + <!-- 鍒涘缓琛� --> + <el-dialog title="鍒涘缓琛�" v-model="visible" width="800px" top="5vh" append-to-body> + <span>鍒涘缓琛ㄨ鍙�(鏀寔澶氫釜寤鸿〃璇彞)锛�</span> + <el-input type="textarea" :rows="10" placeholder="璇疯緭鍏ユ枃鏈�" v-model="content"></el-input> + <template #footer> + <div class="dialog-footer"> + <el-button type="primary" @click="handleImportTable">纭� 瀹�</el-button> + <el-button @click="visible = false">鍙� 娑�</el-button> + </div> + </template> + </el-dialog> +</template> + +<script setup> +import { createTable } from "@/api/tool/gen" + +const visible = ref(false) +const content = ref("") +const { proxy } = getCurrentInstance() +const emit = defineEmits(["ok"]) + +/** 鏄剧ず寮规 */ +function show() { + visible.value = true +} + +/** 瀵煎叆鎸夐挳鎿嶄綔 */ +function handleImportTable() { + if (content.value === "") { + proxy.$modal.msgError("璇疯緭鍏ュ缓琛ㄨ鍙�") + return + } + createTable({ sql: content.value }).then(res => { + proxy.$modal.msgSuccess(res.msg) + if (res.code === 200) { + visible.value = false + emit("ok") + } + }) +} + +defineExpose({ + show, +}) +</script> diff --git a/src/views/tool/gen/editTable.vue b/src/views/tool/gen/editTable.vue new file mode 100644 index 0000000..7cfaebb --- /dev/null +++ b/src/views/tool/gen/editTable.vue @@ -0,0 +1,211 @@ +<template> + <el-card> + <el-tabs v-model="activeName"> + <el-tab-pane label="鍩烘湰淇℃伅" name="basic"> + <basic-info-form ref="basicInfo" :info="info" /> + </el-tab-pane> + <el-tab-pane label="瀛楁淇℃伅" name="columnInfo"> + <el-table ref="dragTable" :data="columns" row-key="columnId" :max-height="tableHeight"> + <el-table-column label="搴忓彿" type="index" min-width="5%" class-name="allowDrag"/> + <el-table-column label="瀛楁鍒楀悕" prop="columnName" min-width="10%" :show-overflow-tooltip="true" class-name="allowDrag"/> + <el-table-column label="瀛楁鎻忚堪" min-width="10%"> + <template #default="scope"> + <el-input v-model="scope.row.columnComment"></el-input> + </template> + </el-table-column> + <el-table-column + label="鐗╃悊绫诲瀷" + prop="columnType" + min-width="10%" + :show-overflow-tooltip="true" + /> + <el-table-column label="Java绫诲瀷" min-width="11%"> + <template #default="scope"> + <el-select v-model="scope.row.javaType"> + <el-option label="Long" value="Long" /> + <el-option label="String" value="String" /> + <el-option label="Integer" value="Integer" /> + <el-option label="Double" value="Double" /> + <el-option label="BigDecimal" value="BigDecimal" /> + <el-option label="Date" value="Date" /> + <el-option label="Boolean" value="Boolean" /> + </el-select> + </template> + </el-table-column> + <el-table-column label="java灞炴��" min-width="10%"> + <template #default="scope"> + <el-input v-model="scope.row.javaField"></el-input> + </template> + </el-table-column> + + <el-table-column label="鎻掑叆" min-width="5%"> + <template #default="scope"> + <el-checkbox true-label="1" false-label="0" v-model="scope.row.isInsert"></el-checkbox> + </template> + </el-table-column> + <el-table-column label="缂栬緫" min-width="5%"> + <template #default="scope"> + <el-checkbox true-label="1" false-label="0" v-model="scope.row.isEdit"></el-checkbox> + </template> + </el-table-column> + <el-table-column label="鍒楄〃" min-width="5%"> + <template #default="scope"> + <el-checkbox true-label="1" false-label="0" v-model="scope.row.isList"></el-checkbox> + </template> + </el-table-column> + <el-table-column label="鏌ヨ" min-width="5%"> + <template #default="scope"> + <el-checkbox true-label="1" false-label="0" v-model="scope.row.isQuery"></el-checkbox> + </template> + </el-table-column> + <el-table-column label="鏌ヨ鏂瑰紡" min-width="10%"> + <template #default="scope"> + <el-select v-model="scope.row.queryType"> + <el-option label="=" value="EQ" /> + <el-option label="!=" value="NE" /> + <el-option label=">" value="GT" /> + <el-option label=">=" value="GTE" /> + <el-option label="<" value="LT" /> + <el-option label="<=" value="LTE" /> + <el-option label="LIKE" value="LIKE" /> + <el-option label="BETWEEN" value="BETWEEN" /> + </el-select> + </template> + </el-table-column> + <el-table-column label="蹇呭~" min-width="5%"> + <template #default="scope"> + <el-checkbox true-label="1" false-label="0" v-model="scope.row.isRequired"></el-checkbox> + </template> + </el-table-column> + <el-table-column label="鏄剧ず绫诲瀷" min-width="12%"> + <template #default="scope"> + <el-select v-model="scope.row.htmlType"> + <el-option label="鏂囨湰妗�" value="input" /> + <el-option label="鏂囨湰鍩�" value="textarea" /> + <el-option label="涓嬫媺妗�" value="select" /> + <el-option label="鍗曢�夋" value="radio" /> + <el-option label="澶嶉�夋" value="checkbox" /> + <el-option label="鏃ユ湡鎺т欢" value="datetime" /> + <el-option label="鍥剧墖涓婁紶" value="imageUpload" /> + <el-option label="鏂囦欢涓婁紶" value="fileUpload" /> + <el-option label="瀵屾枃鏈帶浠�" value="editor" /> + </el-select> + </template> + </el-table-column> + <el-table-column label="瀛楀吀绫诲瀷" min-width="12%"> + <template #default="scope"> + <el-select v-model="scope.row.dictType" clearable filterable placeholder="璇烽�夋嫨"> + <el-option + v-for="dict in dictOptions" + :key="dict.dictType" + :label="dict.dictName" + :value="dict.dictType"> + <span style="float: left">{{ dict.dictName }}</span> + <span style="float: right; color: #8492a6; font-size: 13px">{{ dict.dictType }}</span> + </el-option> + </el-select> + </template> + </el-table-column> + </el-table> + </el-tab-pane> + <el-tab-pane label="鐢熸垚淇℃伅" name="genInfo"> + <gen-info-form ref="genInfo" :info="info" :tables="tables" /> + </el-tab-pane> + </el-tabs> + <el-form label-width="100px"> + <div style="text-align: center;margin-left:-100px;margin-top:10px;"> + <el-button type="primary" @click="submitForm()">鎻愪氦</el-button> + <el-button @click="close()">杩斿洖</el-button> + </div> + </el-form> + </el-card> +</template> + +<script setup name="GenEdit"> +import { getGenTable, updateGenTable } from "@/api/tool/gen" +import { optionselect as getDictOptionselect } from "@/api/system/dict/type" +import basicInfoForm from "./basicInfoForm" +import genInfoForm from "./genInfoForm" +import Sortable from 'sortablejs' + +const route = useRoute() +const { proxy } = getCurrentInstance() + +const activeName = ref("columnInfo") +const tableHeight = ref(document.documentElement.scrollHeight - 245 + "px") +const tables = ref([]) +const columns = ref([]) +const dictOptions = ref([]) +const info = ref({}) + +/** 鎻愪氦鎸夐挳 */ +function submitForm() { + const basicForm = proxy.$refs.basicInfo.$refs.basicInfoForm + const genForm = proxy.$refs.genInfo.$refs.genInfoForm + Promise.all([basicForm, genForm].map(getFormPromise)).then(res => { + const validateResult = res.every(item => !!item) + if (validateResult) { + const genTable = Object.assign({}, info.value) + genTable.columns = columns.value + genTable.params = { + treeCode: info.value.treeCode, + treeName: info.value.treeName, + treeParentCode: info.value.treeParentCode, + parentMenuId: info.value.parentMenuId + } + updateGenTable(genTable).then(res => { + proxy.$modal.msgSuccess(res.msg) + if (res.code === 200) { + close() + } + }) + } else { + proxy.$modal.msgError("琛ㄥ崟鏍¢獙鏈�氳繃锛岃閲嶆柊妫�鏌ユ彁浜ゅ唴瀹�") + } + }) +} + +function getFormPromise(form) { + return new Promise(resolve => { + form.validate(res => { + resolve(res) + }) + }) +} + +function close() { + const obj = { path: "/tool/gen", query: { t: Date.now(), pageNum: route.query.pageNum } } + proxy.$tab.closeOpenPage(obj) +} + +(() => { + const tableId = route.params && route.params.tableId + if (tableId) { + // 鑾峰彇琛ㄨ缁嗕俊鎭� + getGenTable(tableId).then(res => { + columns.value = res.data.rows + info.value = res.data.info + tables.value = res.data.tables + }) + /** 鏌ヨ瀛楀吀涓嬫媺鍒楄〃 */ + getDictOptionselect().then(response => { + dictOptions.value = response.data + }) + } +})() + +// 鎷栧姩鎺掑簭 +onMounted(() => { + const element = document.querySelector('.el-table__body > tbody') + Sortable.create(element, { + handle: ".allowDrag", + onEnd: (evt) => { + const targetRow = columns.value.splice(evt.oldIndex, 1)[0] + columns.value.splice(evt.newIndex, 0, targetRow) + for (const index in columns.value) { + columns.value[index].sort = parseInt(index) + 1 + } + } + }) +}) +</script> diff --git a/src/views/tool/gen/genInfoForm.vue b/src/views/tool/gen/genInfoForm.vue new file mode 100644 index 0000000..75046e8 --- /dev/null +++ b/src/views/tool/gen/genInfoForm.vue @@ -0,0 +1,305 @@ +<template> + <el-form ref="genInfoForm" :model="info" :rules="rules" label-width="150px"> + <el-row> + <el-col :span="12"> + <el-form-item prop="tplCategory"> + <template #label>鐢熸垚妯℃澘</template> + <el-select v-model="info.tplCategory" @change="tplSelectChange"> + <el-option label="鍗曡〃锛堝鍒犳敼鏌ワ級" value="crud" /> + <el-option label="鏍戣〃锛堝鍒犳敼鏌ワ級" value="tree" /> + <el-option label="涓诲瓙琛紙澧炲垹鏀规煡锛�" value="sub" /> + </el-select> + </el-form-item> + </el-col> + + <el-col :span="12"> + <el-form-item prop="tplWebType"> + <template #label>鍓嶇绫诲瀷</template> + <el-select v-model="info.tplWebType"> + <el-option label="Vue2 Element UI 妯$増" value="element-ui" /> + <el-option label="Vue3 Element Plus 妯$増" value="element-plus" /> + </el-select> + </el-form-item> + </el-col> + + <el-col :span="12"> + <el-form-item prop="packageName"> + <template #label> + 鐢熸垚鍖呰矾寰� + <el-tooltip content="鐢熸垚鍦ㄥ摢涓猨ava鍖呬笅锛屼緥濡� com.ruoyi.system" placement="top"> + <el-icon><question-filled /></el-icon> + </el-tooltip> + </template> + <el-input v-model="info.packageName" /> + </el-form-item> + </el-col> + + <el-col :span="12"> + <el-form-item prop="moduleName"> + <template #label> + 鐢熸垚妯″潡鍚� + <el-tooltip content="鍙悊瑙d负瀛愮郴缁熷悕锛屼緥濡� system" placement="top"> + <el-icon><question-filled /></el-icon> + </el-tooltip> + </template> + <el-input v-model="info.moduleName" /> + </el-form-item> + </el-col> + + <el-col :span="12"> + <el-form-item prop="businessName"> + <template #label> + 鐢熸垚涓氬姟鍚� + <el-tooltip content="鍙悊瑙d负鍔熻兘鑻辨枃鍚嶏紝渚嬪 user" placement="top"> + <el-icon><question-filled /></el-icon> + </el-tooltip> + </template> + <el-input v-model="info.businessName" /> + </el-form-item> + </el-col> + + <el-col :span="12"> + <el-form-item prop="functionName"> + <template #label> + 鐢熸垚鍔熻兘鍚� + <el-tooltip content="鐢ㄤ綔绫绘弿杩帮紝渚嬪 鐢ㄦ埛" placement="top"> + <el-icon><question-filled /></el-icon> + </el-tooltip> + </template> + <el-input v-model="info.functionName" /> + </el-form-item> + </el-col> + + <el-col :span="12"> + <el-form-item prop="genType"> + <template #label> + 鐢熸垚浠g爜鏂瑰紡 + <el-tooltip content="榛樿涓簔ip鍘嬬缉鍖呬笅杞斤紝涔熷彲浠ヨ嚜瀹氫箟鐢熸垚璺緞" placement="top"> + <el-icon><question-filled /></el-icon> + </el-tooltip> + </template> + <el-radio v-model="info.genType" value="0">zip鍘嬬缉鍖�</el-radio> + <el-radio v-model="info.genType" value="1">鑷畾涔夎矾寰�</el-radio> + </el-form-item> + </el-col> + + <el-col :span="12"> + <el-form-item> + <template #label> + 涓婄骇鑿滃崟 + <el-tooltip content="鍒嗛厤鍒版寚瀹氳彍鍗曚笅锛屼緥濡� 绯荤粺绠$悊" placement="top"> + <el-icon><question-filled /></el-icon> + </el-tooltip> + </template> + <el-tree-select + v-model="info.parentMenuId" + :data="menuOptions" + :props="{ value: 'menuId', label: 'menuName', children: 'children' }" + value-key="menuId" + placeholder="璇烽�夋嫨绯荤粺鑿滃崟" + check-strictly + /> + </el-form-item> + </el-col> + + <el-col :span="24" v-if="info.genType == '1'"> + <el-form-item prop="genPath"> + <template #label> + 鑷畾涔夎矾寰� + <el-tooltip content="濉啓纾佺洏缁濆璺緞锛岃嫢涓嶅~鍐欙紝鍒欑敓鎴愬埌褰撳墠Web椤圭洰涓�" placement="top"> + <el-icon><question-filled /></el-icon> + </el-tooltip> + </template> + <el-input v-model="info.genPath"> + <template #append> + <el-dropdown> + <el-button type="primary"> + 鏈�杩戣矾寰勫揩閫熼�夋嫨 + <i class="el-icon-arrow-down el-icon--right"></i> + </el-button> + <template #dropdown> + <el-dropdown-menu> + <el-dropdown-item @click="info.genPath = '/'">鎭㈠榛樿鐨勭敓鎴愬熀纭�璺緞</el-dropdown-item> + </el-dropdown-menu> + </template> + </el-dropdown> + </template> + </el-input> + </el-form-item> + </el-col> + </el-row> + + <template v-if="info.tplCategory == 'tree'"> + <h4 class="form-header">鍏朵粬淇℃伅</h4> + <el-row v-show="info.tplCategory == 'tree'"> + <el-col :span="12"> + <el-form-item> + <template #label> + 鏍戠紪鐮佸瓧娈� + <el-tooltip content="鏍戞樉绀虹殑缂栫爜瀛楁鍚嶏紝 濡傦細dept_id" placement="top"> + <el-icon><question-filled /></el-icon> + </el-tooltip> + </template> + <el-select v-model="info.treeCode" placeholder="璇烽�夋嫨"> + <el-option + v-for="(column, index) in info.columns" + :key="index" + :label="column.columnName + '锛�' + column.columnComment" + :value="column.columnName" + ></el-option> + </el-select> + </el-form-item> + </el-col> + <el-col :span="12"> + <el-form-item> + <template #label> + 鏍戠埗缂栫爜瀛楁 + <el-tooltip content="鏍戞樉绀虹殑鐖剁紪鐮佸瓧娈靛悕锛� 濡傦細parent_Id" placement="top"> + <el-icon><question-filled /></el-icon> + </el-tooltip> + </template> + <el-select v-model="info.treeParentCode" placeholder="璇烽�夋嫨"> + <el-option + v-for="(column, index) in info.columns" + :key="index" + :label="column.columnName + '锛�' + column.columnComment" + :value="column.columnName" + ></el-option> + </el-select> + </el-form-item> + </el-col> + <el-col :span="12"> + <el-form-item> + <template #label> + 鏍戝悕绉板瓧娈� + <el-tooltip content="鏍戣妭鐐圭殑鏄剧ず鍚嶇О瀛楁鍚嶏紝 濡傦細dept_name" placement="top"> + <el-icon><question-filled /></el-icon> + </el-tooltip> + </template> + <el-select v-model="info.treeName" placeholder="璇烽�夋嫨"> + <el-option + v-for="(column, index) in info.columns" + :key="index" + :label="column.columnName + '锛�' + column.columnComment" + :value="column.columnName" + ></el-option> + </el-select> + </el-form-item> + </el-col> + </el-row> + </template> + + <template v-if="info.tplCategory == 'sub'"> + <h4 class="form-header">鍏宠仈淇℃伅</h4> + <el-row> + <el-col :span="12"> + <el-form-item> + <template #label> + 鍏宠仈瀛愯〃鐨勮〃鍚� + <el-tooltip content="鍏宠仈瀛愯〃鐨勮〃鍚嶏紝 濡傦細sys_user" placement="top"> + <el-icon><question-filled /></el-icon> + </el-tooltip> + </template> + <el-select v-model="info.subTableName" placeholder="璇烽�夋嫨" @change="subSelectChange"> + <el-option + v-for="(table, index) in tables" + :key="index" + :label="table.tableName + '锛�' + table.tableComment" + :value="table.tableName" + ></el-option> + </el-select> + </el-form-item> + </el-col> + <el-col :span="12"> + <el-form-item> + <template #label> + 瀛愯〃鍏宠仈鐨勫閿悕 + <el-tooltip content="瀛愯〃鍏宠仈鐨勫閿悕锛� 濡傦細user_id" placement="top"> + <el-icon><question-filled /></el-icon> + </el-tooltip> + </template> + <el-select v-model="info.subTableFkName" placeholder="璇烽�夋嫨"> + <el-option + v-for="(column, index) in subColumns" + :key="index" + :label="column.columnName + '锛�' + column.columnComment" + :value="column.columnName" + ></el-option> + </el-select> + </el-form-item> + </el-col> + </el-row> + </template> + + </el-form> +</template> + +<script setup> +import { listMenu } from "@/api/system/menu" + +const subColumns = ref([]) +const menuOptions = ref([]) +const { proxy } = getCurrentInstance() + +const props = defineProps({ + info: { + type: Object, + default: null + }, + tables: { + type: Array, + default: null + } +}) + +// 琛ㄥ崟鏍¢獙 +const rules = ref({ + tplCategory: [{ required: true, message: "璇烽�夋嫨鐢熸垚妯℃澘", trigger: "blur" }], + packageName: [{ required: true, message: "璇疯緭鍏ョ敓鎴愬寘璺緞", trigger: "blur" }], + moduleName: [{ required: true, message: "璇疯緭鍏ョ敓鎴愭ā鍧楀悕", trigger: "blur" }], + businessName: [{ required: true, message: "璇疯緭鍏ョ敓鎴愪笟鍔″悕", trigger: "blur" }], + functionName: [{ required: true, message: "璇疯緭鍏ョ敓鎴愬姛鑳藉悕", trigger: "blur" }] +}) + +function subSelectChange(value) { + props.info.subTableFkName = "" +} + +function tplSelectChange(value) { + if (value !== "sub") { + props.info.subTableName = "" + props.info.subTableFkName = "" + } +} + +function setSubTableColumns(value) { + for (var item in props.tables) { + const name = props.tables[item].tableName + if (value === name) { + subColumns.value = props.tables[item].columns + break + } + } +} + +/** 鏌ヨ鑿滃崟涓嬫媺鏍戠粨鏋� */ +function getMenuTreeselect() { + listMenu().then(response => { + menuOptions.value = proxy.handleTree(response.data, "menuId") + }) +} + +onMounted(() => { + getMenuTreeselect() +}) + +watch(() => props.info.subTableName, val => { + setSubTableColumns(val) +}) + +watch(() => props.info.tplWebType, val => { + if (val === '') { + props.info.tplWebType = "element-plus" + } +}) +</script> diff --git a/src/views/tool/gen/importTable.vue b/src/views/tool/gen/importTable.vue new file mode 100644 index 0000000..b28eacb --- /dev/null +++ b/src/views/tool/gen/importTable.vue @@ -0,0 +1,126 @@ +<template> + <!-- 瀵煎叆琛� --> + <el-dialog title="瀵煎叆琛�" v-model="visible" width="800px" top="5vh" append-to-body> + <el-form :model="queryParams" ref="queryRef" :inline="true"> + <el-form-item label="琛ㄥ悕绉�" prop="tableName"> + <el-input + v-model="queryParams.tableName" + placeholder="璇疯緭鍏ヨ〃鍚嶇О" + clearable + style="width: 180px" + @keyup.enter="handleQuery" + /> + </el-form-item> + <el-form-item label="琛ㄦ弿杩�" prop="tableComment"> + <el-input + v-model="queryParams.tableComment" + placeholder="璇疯緭鍏ヨ〃鎻忚堪" + clearable + style="width: 180px" + @keyup.enter="handleQuery" + /> + </el-form-item> + <el-form-item> + <el-button type="primary" icon="Search" @click="handleQuery">鎼滅储</el-button> + <el-button icon="Refresh" @click="resetQuery">閲嶇疆</el-button> + </el-form-item> + </el-form> + <el-row> + <el-table @row-click="clickRow" ref="table" :data="dbTableList" @selection-change="handleSelectionChange" height="260px"> + <el-table-column type="selection" width="55"></el-table-column> + <el-table-column prop="tableName" label="琛ㄥ悕绉�" :show-overflow-tooltip="true"></el-table-column> + <el-table-column prop="tableComment" label="琛ㄦ弿杩�" :show-overflow-tooltip="true"></el-table-column> + <el-table-column prop="createTime" label="鍒涘缓鏃堕棿"></el-table-column> + <el-table-column prop="updateTime" label="鏇存柊鏃堕棿"></el-table-column> + </el-table> + <pagination + v-show="total>0" + :total="total" + v-model:page="queryParams.pageNum" + v-model:limit="queryParams.pageSize" + @pagination="getList" + /> + </el-row> + <template #footer> + <div class="dialog-footer"> + <el-button type="primary" @click="handleImportTable">纭� 瀹�</el-button> + <el-button @click="visible = false">鍙� 娑�</el-button> + </div> + </template> + </el-dialog> +</template> + +<script setup> +import { listDbTable, importTable } from "@/api/tool/gen" + +const total = ref(0) +const visible = ref(false) +const tables = ref([]) +const dbTableList = ref([]) +const { proxy } = getCurrentInstance() + +const queryParams = reactive({ + pageNum: 1, + pageSize: 10, + tableName: undefined, + tableComment: undefined +}) + +const emit = defineEmits(["ok"]) + +/** 鏌ヨ鍙傛暟鍒楄〃 */ +function show() { + getList() + visible.value = true +} + +/** 鍗曞嚮閫夋嫨琛� */ +function clickRow(row) { + proxy.$refs.table.toggleRowSelection(row) +} + +/** 澶氶�夋閫変腑鏁版嵁 */ +function handleSelectionChange(selection) { + tables.value = selection.map(item => item.tableName) +} + +/** 鏌ヨ琛ㄦ暟鎹� */ +function getList() { + listDbTable(queryParams).then(res => { + dbTableList.value = res.rows + total.value = res.total + }) +} + +/** 鎼滅储鎸夐挳鎿嶄綔 */ +function handleQuery() { + queryParams.pageNum = 1 + getList() +} + +/** 閲嶇疆鎸夐挳鎿嶄綔 */ +function resetQuery() { + proxy.resetForm("queryRef") + handleQuery() +} + +/** 瀵煎叆鎸夐挳鎿嶄綔 */ +function handleImportTable() { + const tableNames = tables.value.join(",") + if (tableNames == "") { + proxy.$modal.msgError("璇烽�夋嫨瑕佸鍏ョ殑琛�") + return + } + importTable({ tables: tableNames }).then(res => { + proxy.$modal.msgSuccess(res.msg) + if (res.code === 200) { + visible.value = false + emit("ok") + } + }) +} + +defineExpose({ + show, +}) +</script> diff --git a/src/views/tool/gen/index.vue b/src/views/tool/gen/index.vue new file mode 100644 index 0000000..c2a0877 --- /dev/null +++ b/src/views/tool/gen/index.vue @@ -0,0 +1,308 @@ +<template> + <div class="app-container"> + <el-form :model="queryParams" ref="queryRef" :inline="true" v-show="showSearch"> + <el-form-item label="琛ㄥ悕绉�" prop="tableName"> + <el-input + v-model="queryParams.tableName" + placeholder="璇疯緭鍏ヨ〃鍚嶇О" + clearable + style="width: 200px" + @keyup.enter="handleQuery" + /> + </el-form-item> + <el-form-item label="琛ㄦ弿杩�" prop="tableComment"> + <el-input + v-model="queryParams.tableComment" + placeholder="璇疯緭鍏ヨ〃鎻忚堪" + clearable + style="width: 200px" + @keyup.enter="handleQuery" + /> + </el-form-item> + <el-form-item label="鍒涘缓鏃堕棿" style="width: 308px"> + <el-date-picker + v-model="dateRange" + value-format="YYYY-MM-DD" + type="daterange" + range-separator="-" + start-placeholder="寮�濮嬫棩鏈�" + end-placeholder="缁撴潫鏃ユ湡" + ></el-date-picker> + </el-form-item> + <el-form-item> + <el-button type="primary" icon="Search" @click="handleQuery">鎼滅储</el-button> + <el-button icon="Refresh" @click="resetQuery">閲嶇疆</el-button> + </el-form-item> + </el-form> + + <el-row :gutter="10" class="mb8"> + <el-col :span="1.5"> + <el-button + type="primary" + plain + icon="Download" + :disabled="multiple" + @click="handleGenTable" + v-hasPermi="['tool:gen:code']" + >鐢熸垚</el-button> + </el-col> + <el-col :span="1.5"> + <el-button + type="primary" + plain + icon="Plus" + @click="openCreateTable" + v-hasRole="['admin']" + >鍒涘缓</el-button> + </el-col> + <el-col :span="1.5"> + <el-button + type="info" + plain + icon="Upload" + @click="openImportTable" + v-hasPermi="['tool:gen:import']" + >瀵煎叆</el-button> + </el-col> + <el-col :span="1.5"> + <el-button + type="success" + plain + icon="Edit" + :disabled="single" + @click="handleEditTable" + v-hasPermi="['tool:gen:edit']" + >淇敼</el-button> + </el-col> + <el-col :span="1.5"> + <el-button + type="danger" + plain + icon="Delete" + :disabled="multiple" + @click="handleDelete" + v-hasPermi="['tool:gen:remove']" + >鍒犻櫎</el-button> + </el-col> + <right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar> + </el-row> + + <el-table ref="genRef" v-loading="loading" :data="tableList" @selection-change="handleSelectionChange" :default-sort="defaultSort" @sort-change="handleSortChange"> + <el-table-column type="selection" align="center" width="55"></el-table-column> + <el-table-column label="搴忓彿" type="index" width="50" align="center"> + <template #default="scope"> + <span>{{(queryParams.pageNum - 1) * queryParams.pageSize + scope.$index + 1}}</span> + </template> + </el-table-column> + <el-table-column label="琛ㄥ悕绉�" align="center" prop="tableName" :show-overflow-tooltip="true" /> + <el-table-column label="琛ㄦ弿杩�" align="center" prop="tableComment" :show-overflow-tooltip="true" /> + <el-table-column label="瀹炰綋" align="center" prop="className" :show-overflow-tooltip="true" /> + <el-table-column label="鍒涘缓鏃堕棿" align="center" prop="createTime" width="160" sortable="custom" :sort-orders="['descending', 'ascending']" /> + <el-table-column label="鏇存柊鏃堕棿" align="center" prop="updateTime" width="160" sortable="custom" :sort-orders="['descending', 'ascending']" /> + <el-table-column label="鎿嶄綔" align="center" width="330" class-name="small-padding fixed-width"> + <template #default="scope"> + <el-tooltip content="棰勮" placement="top"> + <el-button link type="primary" icon="View" @click="handlePreview(scope.row)" v-hasPermi="['tool:gen:preview']"></el-button> + </el-tooltip> + <el-tooltip content="缂栬緫" placement="top"> + <el-button link type="primary" icon="Edit" @click="handleEditTable(scope.row)" v-hasPermi="['tool:gen:edit']"></el-button> + </el-tooltip> + <el-tooltip content="鍒犻櫎" placement="top"> + <el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['tool:gen:remove']"></el-button> + </el-tooltip> + <el-tooltip content="鍚屾" placement="top"> + <el-button link type="primary" icon="Refresh" @click="handleSynchDb(scope.row)" v-hasPermi="['tool:gen:edit']"></el-button> + </el-tooltip> + <el-tooltip content="鐢熸垚浠g爜" placement="top"> + <el-button link type="primary" icon="Download" @click="handleGenTable(scope.row)" v-hasPermi="['tool:gen:code']"></el-button> + </el-tooltip> + </template> + </el-table-column> + </el-table> + <pagination + v-show="total>0" + :total="total" + v-model:page="queryParams.pageNum" + v-model:limit="queryParams.pageSize" + @pagination="getList" + /> + <!-- 棰勮鐣岄潰 --> + <el-dialog :title="preview.title" v-model="preview.open" width="80%" top="5vh" append-to-body class="scrollbar"> + <el-tabs v-model="preview.activeName"> + <el-tab-pane + v-for="(value, key) in preview.data" + :label="key.substring(key.lastIndexOf('/')+1,key.indexOf('.vm'))" + :name="key.substring(key.lastIndexOf('/')+1,key.indexOf('.vm'))" + :key="value" + > + <el-link :underline="false" icon="DocumentCopy" v-copyText="value" v-copyText:callback="copyTextSuccess" style="float:right"> 澶嶅埗</el-link> + <pre>{{ value }}</pre> + </el-tab-pane> + </el-tabs> + </el-dialog> + <import-table ref="importRef" @ok="handleQuery" /> + <create-table ref="createRef" @ok="handleQuery" /> + </div> +</template> + +<script setup name="Gen"> +import { listTable, previewTable, delTable, genCode, synchDb } from "@/api/tool/gen" +import router from "@/router" +import importTable from "./importTable" +import createTable from "./createTable" + +const route = useRoute() +const { proxy } = getCurrentInstance() + +const tableList = ref([]) +const loading = ref(true) +const showSearch = ref(true) +const ids = ref([]) +const single = ref(true) +const multiple = ref(true) +const total = ref(0) +const tableNames = ref([]) +const dateRange = ref([]) +const uniqueId = ref("") +const defaultSort = ref({ prop: "createTime", order: "descending" }) + +const data = reactive({ + queryParams: { + pageNum: 1, + pageSize: 10, + tableName: undefined, + tableComment: undefined, + orderByColumn: defaultSort.value.prop, + isAsc: defaultSort.value.order + }, + preview: { + open: false, + title: "浠g爜棰勮", + data: {}, + activeName: "domain.java" + } +}) + +const { queryParams, preview } = toRefs(data) + +onActivated(() => { + const time = route.query.t + if (time != null && time != uniqueId.value) { + uniqueId.value = time + queryParams.value.pageNum = Number(route.query.pageNum) + dateRange.value = [] + proxy.resetForm("queryForm") + getList() + } +}) + +/** 鏌ヨ琛ㄩ泦鍚� */ +function getList() { + loading.value = true + listTable(proxy.addDateRange(queryParams.value, dateRange.value)).then(response => { + tableList.value = response.rows + total.value = response.total + loading.value = false + }) +} + +/** 鎼滅储鎸夐挳鎿嶄綔 */ +function handleQuery() { + queryParams.value.pageNum = 1 + getList() +} + +/** 鐢熸垚浠g爜鎿嶄綔 */ +function handleGenTable(row) { + const tbNames = row.tableName || tableNames.value + if (tbNames == "") { + proxy.$modal.msgError("璇烽�夋嫨瑕佺敓鎴愮殑鏁版嵁") + return + } + if (row.genType === "1") { + genCode(row.tableName).then(response => { + proxy.$modal.msgSuccess("鎴愬姛鐢熸垚鍒拌嚜瀹氫箟璺緞锛�" + row.genPath) + }) + } else { + proxy.$download.zip("/tool/gen/batchGenCode?tables=" + tbNames, "ruoyi.zip") + } +} + +/** 鍚屾鏁版嵁搴撴搷浣� */ +function handleSynchDb(row) { + const tableName = row.tableName + proxy.$modal.confirm('纭瑕佸己鍒跺悓姝�"' + tableName + '"琛ㄧ粨鏋勫悧锛�').then(function () { + return synchDb(tableName) + }).then(() => { + proxy.$modal.msgSuccess("鍚屾鎴愬姛") + }).catch(() => {}) +} + +/** 鎵撳紑瀵煎叆琛ㄥ脊绐� */ +function openImportTable() { + proxy.$refs["importRef"].show() +} + +/** 鎵撳紑鍒涘缓琛ㄥ脊绐� */ +function openCreateTable() { + proxy.$refs["createRef"].show() +} + +/** 閲嶇疆鎸夐挳鎿嶄綔 */ +function resetQuery() { + dateRange.value = [] + proxy.resetForm("queryRef") + queryParams.value.pageNum = 1 + proxy.$refs["genRef"].sort(defaultSort.value.prop, defaultSort.value.order) +} + +/** 棰勮鎸夐挳 */ +function handlePreview(row) { + previewTable(row.tableId).then(response => { + preview.value.data = response.data + preview.value.open = true + preview.value.activeName = "domain.java" + }) +} + +/** 澶嶅埗浠g爜鎴愬姛 */ +function copyTextSuccess() { + proxy.$modal.msgSuccess("澶嶅埗鎴愬姛") +} + +// 澶氶�夋閫変腑鏁版嵁 +function handleSelectionChange(selection) { + ids.value = selection.map(item => item.tableId) + tableNames.value = selection.map(item => item.tableName) + single.value = selection.length != 1 + multiple.value = !selection.length +} + +/** 鎺掑簭瑙﹀彂浜嬩欢 */ +function handleSortChange(column, prop, order) { + queryParams.value.orderByColumn = column.prop + queryParams.value.isAsc = column.order + getList() +} + +/** 淇敼鎸夐挳鎿嶄綔 */ +function handleEditTable(row) { + const tableId = row.tableId || ids.value[0] + const tableName = row.tableName || tableNames.value[0] + const params = { pageNum: queryParams.value.pageNum } + proxy.$tab.openPage("淇敼[" + tableName + "]鐢熸垚閰嶇疆", '/tool/gen-edit/index/' + tableId, params) +} + +/** 鍒犻櫎鎸夐挳鎿嶄綔 */ +function handleDelete(row) { + const tableIds = row.tableId || ids.value + proxy.$modal.confirm('鏄惁纭鍒犻櫎琛ㄧ紪鍙蜂负"' + tableIds + '"鐨勬暟鎹」锛�').then(function () { + return delTable(tableIds) + }).then(() => { + getList() + proxy.$modal.msgSuccess("鍒犻櫎鎴愬姛") + }).catch(() => {}) +} + +getList() +</script> diff --git a/src/views/tool/swagger/index.vue b/src/views/tool/swagger/index.vue new file mode 100644 index 0000000..a44fa71 --- /dev/null +++ b/src/views/tool/swagger/index.vue @@ -0,0 +1,9 @@ +<template> + <i-frame v-model:src="url"></i-frame> +</template> + +<script setup> +import iFrame from '@/components/iFrame' + +const url = ref(import.meta.env.VITE_APP_BASE_API + "/swagger-ui/index.html") +</script> diff --git a/vite.config.js b/vite.config.js new file mode 100644 index 0000000..d3d9b6d --- /dev/null +++ b/vite.config.js @@ -0,0 +1,79 @@ +import { defineConfig, loadEnv } from 'vite' +import path from 'path' +import createVitePlugins from './vite/plugins' + +const baseUrl = 'http://localhost:8080' // 鍚庣鎺ュ彛 + +// https://vitejs.dev/config/ +export default defineConfig(({ mode, command }) => { + const env = loadEnv(mode, process.cwd()) + const { VITE_APP_ENV } = env + return { + // 閮ㄧ讲鐢熶骇鐜鍜屽紑鍙戠幆澧冧笅鐨刄RL銆� + // 榛樿鎯呭喌涓嬶紝vite 浼氬亣璁句綘鐨勫簲鐢ㄦ槸琚儴缃插湪涓�涓煙鍚嶇殑鏍硅矾寰勪笂 + // 渚嬪 https://www.ruoyi.vip/銆傚鏋滃簲鐢ㄨ閮ㄧ讲鍦ㄤ竴涓瓙璺緞涓婏紝浣犲氨闇�瑕佺敤杩欎釜閫夐」鎸囧畾杩欎釜瀛愯矾寰勩�備緥濡傦紝濡傛灉浣犵殑搴旂敤琚儴缃插湪 https://www.ruoyi.vip/admin/锛屽垯璁剧疆 baseUrl 涓� /admin/銆� + base: VITE_APP_ENV === 'production' ? '/' : '/', + plugins: createVitePlugins(env, command === 'build'), + resolve: { + // https://cn.vitejs.dev/config/#resolve-alias + alias: { + // 璁剧疆璺緞 + '~': path.resolve(__dirname, './'), + // 璁剧疆鍒悕 + '@': path.resolve(__dirname, './src') + }, + // https://cn.vitejs.dev/config/#resolve-extensions + extensions: ['.mjs', '.js', '.ts', '.jsx', '.tsx', '.json', '.vue'] + }, + // 鎵撳寘閰嶇疆 + build: { + // https://vite.dev/config/build-options.html + sourcemap: command === 'build' ? false : 'inline', + outDir: 'dist', + assetsDir: 'assets', + chunkSizeWarningLimit: 2000, + rollupOptions: { + output: { + chunkFileNames: 'static/js/[name]-[hash].js', + entryFileNames: 'static/js/[name]-[hash].js', + assetFileNames: 'static/[ext]/[name]-[hash].[ext]' + } + } + }, + // vite 鐩稿叧閰嶇疆 + server: { + port: 80, + host: true, + open: true, + proxy: { + // https://cn.vitejs.dev/config/#server-proxy + '/dev-api': { + target: baseUrl, + changeOrigin: true, + rewrite: (p) => p.replace(/^\/dev-api/, '') + }, + // springdoc proxy + '^/v3/api-docs/(.*)': { + target: baseUrl, + changeOrigin: true, + } + } + }, + css: { + postcss: { + plugins: [ + { + postcssPlugin: 'internal:charset-removal', + AtRule: { + charset: (atRule) => { + if (atRule.name === 'charset') { + atRule.remove() + } + } + } + } + ] + } + } + } +}) diff --git a/vite/plugins/auto-import.js b/vite/plugins/auto-import.js new file mode 100644 index 0000000..a5d3576 --- /dev/null +++ b/vite/plugins/auto-import.js @@ -0,0 +1,12 @@ +import autoImport from 'unplugin-auto-import/vite' + +export default function createAutoImport() { + return autoImport({ + imports: [ + 'vue', + 'vue-router', + 'pinia' + ], + dts: false + }) +} diff --git a/vite/plugins/compression.js b/vite/plugins/compression.js new file mode 100644 index 0000000..e90aaec --- /dev/null +++ b/vite/plugins/compression.js @@ -0,0 +1,28 @@ +import compression from 'vite-plugin-compression' + +export default function createCompression(env) { + const { VITE_BUILD_COMPRESS } = env + const plugin = [] + if (VITE_BUILD_COMPRESS) { + const compressList = VITE_BUILD_COMPRESS.split(',') + if (compressList.includes('gzip')) { + // http://doc.ruoyi.vip/ruoyi-vue/other/faq.html#浣跨敤gzip瑙e帇缂╅潤鎬佹枃浠� + plugin.push( + compression({ + ext: '.gz', + deleteOriginFile: false + }) + ) + } + if (compressList.includes('brotli')) { + plugin.push( + compression({ + ext: '.br', + algorithm: 'brotliCompress', + deleteOriginFile: false + }) + ) + } + } + return plugin +} diff --git a/vite/plugins/index.js b/vite/plugins/index.js new file mode 100644 index 0000000..10e17c3 --- /dev/null +++ b/vite/plugins/index.js @@ -0,0 +1,15 @@ +import vue from '@vitejs/plugin-vue' + +import createAutoImport from './auto-import' +import createSvgIcon from './svg-icon' +import createCompression from './compression' +import createSetupExtend from './setup-extend' + +export default function createVitePlugins(viteEnv, isBuild = false) { + const vitePlugins = [vue()] + vitePlugins.push(createAutoImport()) + vitePlugins.push(createSetupExtend()) + vitePlugins.push(createSvgIcon(isBuild)) + isBuild && vitePlugins.push(...createCompression(viteEnv)) + return vitePlugins +} diff --git a/vite/plugins/setup-extend.js b/vite/plugins/setup-extend.js new file mode 100644 index 0000000..ed8342e --- /dev/null +++ b/vite/plugins/setup-extend.js @@ -0,0 +1,5 @@ +import setupExtend from 'unplugin-vue-setup-extend-plus/vite' + +export default function createSetupExtend() { + return setupExtend({}) +} diff --git a/vite/plugins/svg-icon.js b/vite/plugins/svg-icon.js new file mode 100644 index 0000000..30a4140 --- /dev/null +++ b/vite/plugins/svg-icon.js @@ -0,0 +1,10 @@ +import { createSvgIconsPlugin } from 'vite-plugin-svg-icons' +import path from 'path' + +export default function createSvgIcon(isBuild) { + return createSvgIconsPlugin({ + iconDirs: [path.resolve(process.cwd(), 'src/assets/icons/svg')], + symbolId: 'icon-[dir]-[name]', + svgoOptions: isBuild + }) +} -- Gitblit v1.9.3