diff --git a/ruoyi-ui/public/index.html b/ruoyi-ui/public/index.html index 56fd45b36..949de03af 100644 --- a/ruoyi-ui/public/index.html +++ b/ruoyi-ui/public/index.html @@ -1,208 +1,208 @@ -<!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="<%= BASE_URL %>favicon.ico"> - <title><%= webpackConfig.name %></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">正在加载系统资源,请耐心等待</div> - </div> - </div> - </body> -</html> +<!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="<%= BASE_URL %>favicon.ico"> + <title><%= webpackConfig.name %></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">正在加载系统资源,请耐心等待</div> + </div> + </div> + </body> +</html> diff --git a/ruoyi-ui/src/assets/styles/btn.scss b/ruoyi-ui/src/assets/styles/btn.scss index 1ccb57d9c..38b1a3c62 100644 --- a/ruoyi-ui/src/assets/styles/btn.scss +++ b/ruoyi-ui/src/assets/styles/btn.scss @@ -1,99 +1,99 @@ -@import './variables.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; -} +@import './variables.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/ruoyi-ui/src/assets/styles/element-ui.scss b/ruoyi-ui/src/assets/styles/element-ui.scss index 363092a63..0558a43b2 100644 --- a/ruoyi-ui/src/assets/styles/element-ui.scss +++ b/ruoyi-ui/src/assets/styles/element-ui.scss @@ -89,4 +89,21 @@ > .el-submenu__title .el-submenu__icon-arrow { display: none; -} \ No newline at end of file +} + +.el-menu, .el-icon-arrow-down { + transition: none; + -webkit-transition: none; +} + +.el-menu--inline, .el-menu__title, .nest-menu { + transition: none; + -webkit-transition: none; +} + +.el-dialog, .el-dialog__wrapper, .v-modal { + transition: none; + -webkit-transition: none; +} + +//.el-menu- diff --git a/ruoyi-ui/src/assets/styles/index.scss b/ruoyi-ui/src/assets/styles/index.scss index 9f536ae80..8672acbe7 100644 --- a/ruoyi-ui/src/assets/styles/index.scss +++ b/ruoyi-ui/src/assets/styles/index.scss @@ -1,191 +1,191 @@ -@import './variables.scss'; -@import './mixin.scss'; -@import './transition.scss'; -@import './element-ui.scss'; -@import './sidebar.scss'; -@import './btn.scss'; - -body { - height: 100%; - -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; -} - -.pagination-container { - margin-top: 30px; -} - -.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; - } -} - -//refine vue-multiselect plugin -.multiselect { - line-height: 16px; -} - -.multiselect--active { - z-index: 1000 !important; -} +@import './variables.scss'; +@import './mixin.scss'; +//@import './transition.scss'; +@import './element-ui.scss'; +@import './sidebar.scss'; +@import './btn.scss'; + +body { + height: 100%; + -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; +} + +.pagination-container { + margin-top: 30px; +} + +.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; + } +} + +//refine vue-multiselect plugin +.multiselect { + line-height: 16px; +} + +.multiselect--active { + z-index: 1000 !important; +} diff --git a/ruoyi-ui/src/assets/styles/sidebar.scss b/ruoyi-ui/src/assets/styles/sidebar.scss index 43d5f9a98..6de6aaca0 100644 --- a/ruoyi-ui/src/assets/styles/sidebar.scss +++ b/ruoyi-ui/src/assets/styles/sidebar.scss @@ -1,227 +1,228 @@ -#app { - - .main-container { - min-height: 100%; - transition: margin-left .28s; - margin-left: $base-sidebar-width; - position: relative; - } - - .sidebarHide { - margin-left: 0!important; - } - - .sidebar-container { - -webkit-transition: width .28s; - transition: width 0.28s; - width: $base-sidebar-width !important; - background-color: $base-menu-background; - 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: 2px 0 6px rgba(0,21,41,.35); - - // 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, .el-submenu__title { - overflow: hidden !important; - text-overflow: ellipsis !important; - white-space: nowrap !important; - } - - // menu hover - .submenu-title-noDropdown, - .el-submenu__title { - &:hover { - background-color: rgba(0, 0, 0, 0.06) !important; - } - } - - & .theme-dark .is-active > .el-submenu__title { - color: $base-menu-color-active !important; - } - - & .nest-menu .el-submenu>.el-submenu__title, - & .el-submenu .el-menu-item { - min-width: $base-sidebar-width !important; - - &:hover { - background-color: rgba(0, 0, 0, 0.06) !important; - } - } - - & .theme-dark .nest-menu .el-submenu>.el-submenu__title, - & .theme-dark .el-submenu .el-menu-item { - background-color: $base-sub-menu-background !important; - - &:hover { - background-color: $base-sub-menu-hover !important; - } - } - } - - .hideSidebar { - .sidebar-container { - width: 54px !important; - } - - .main-container { - margin-left: 54px; - } - - .submenu-title-noDropdown { - padding: 0 !important; - position: relative; - - .el-tooltip { - padding: 0 !important; - - .svg-icon { - margin-left: 20px; - } - } - } - - .el-submenu { - overflow: hidden; - - &>.el-submenu__title { - padding: 0 !important; - - .svg-icon { - margin-left: 20px; - } - - } - } - - .el-menu--collapse { - .el-submenu { - &>.el-submenu__title { - &>span { - height: 0; - width: 0; - overflow: hidden; - visibility: hidden; - display: inline-block; - } - } - } - } - } - - .el-menu--collapse .el-menu .el-submenu { - 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-submenu>.el-submenu__title, - .el-menu-item { - &:hover { - // you can use $subMenuHover - background-color: rgba(0, 0, 0, 0.06) !important; - } - } - - // the scroll bar appears when the subMenu 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; - } - } -} +#app { + + .main-container { + min-height: 100%; + //transition: margin-left .28s; + margin-left: $base-sidebar-width; + position: relative; + } + + .sidebarHide { + margin-left: 0!important; + } + + .sidebar-container { + //-webkit-transition: width 0.28s; + //transition: width 0.28s; + width: $base-sidebar-width !important; + background-color: $base-menu-background; + 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: 2px 0 6px rgba(0,21,41,.35); + + // 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, .el-submenu__title { + overflow: hidden !important; + text-overflow: ellipsis !important; + white-space: nowrap !important; + } + + // menu hover + .submenu-title-noDropdown, + .el-submenu__title { + &:hover { + background-color: rgba(0, 0, 0, 0.06) !important; + } + } + + & .theme-dark .is-active > .el-submenu__title { + color: $base-menu-color-active !important; + } + + & .nest-menu .el-submenu>.el-submenu__title, + & .el-submenu .el-menu-item { + min-width: $base-sidebar-width !important; + + &:hover { + background-color: rgba(0, 0, 0, 0.06) !important; + } + } + + & .theme-dark .nest-menu .el-submenu>.el-submenu__title, + & .theme-dark .el-submenu .el-menu-item { + background-color: $base-sub-menu-background !important; + + &:hover { + background-color: $base-sub-menu-hover !important; + } + } + } + + .hideSidebar { + .sidebar-container { + width: 54px !important; + } + + .main-container { + margin-left: 54px; + } + + .submenu-title-noDropdown { + padding: 0 !important; + position: relative; + + .el-tooltip { + padding: 0 !important; + + .svg-icon { + margin-left: 20px; + } + } + } + + .el-submenu { + overflow: hidden; + + &>.el-submenu__title { + padding: 0 !important; + + .svg-icon { + margin-left: 20px; + } + + } + } + + .el-menu--collapse { + .el-submenu { + &>.el-submenu__title { + &>span { + height: 0; + width: 0; + overflow: hidden; + visibility: hidden; + display: inline-block; + } + } + } + } + } + + .el-menu--collapse .el-menu .el-submenu { + min-width: $base-sidebar-width !important; + } + + // mobile responsive + .mobile { + .main-container { + margin-left: 0px; + } + + .sidebar-container { + //-webkit-transition: width 1.28s; + //transition: transform 1.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-submenu>.el-submenu__title, + .el-menu-item { + &:hover { + // you can use $subMenuHover + background-color: rgba(0, 0, 0, 0.06) !important; + } + } + + // the scroll bar appears when the subMenu 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/ruoyi-ui/src/assets/styles/variables.scss b/ruoyi-ui/src/assets/styles/variables.scss index 8715dc783..34484d47e 100644 --- a/ruoyi-ui/src/assets/styles/variables.scss +++ b/ruoyi-ui/src/assets/styles/variables.scss @@ -1,54 +1,54 @@ -// base color -$blue:#324157; -$light-blue:#3A71A8; -$red:#C03639; -$pink: #E65D6E; -$green: #30B08F; -$tiffany: #4AB7BD; -$yellow:#FEC171; -$panGreen: #30B08F; - -// 默认菜单主题风格 -$base-menu-color:#bfcbd9; -$base-menu-color-active:#f4f4f5; -$base-menu-background:#304156; -$base-logo-title-color: #ffffff; - -$base-menu-light-color:rgba(0,0,0,.70); -$base-menu-light-background:#ffffff; -$base-logo-light-title-color: #001529; - -$base-sub-menu-background:#1f2d3d; -$base-sub-menu-hover:#001528; - -// 自定义暗色菜单风格 -/** -$base-menu-color:hsla(0,0%,100%,.65); -$base-menu-color-active:#fff; -$base-menu-background:#001529; -$base-logo-title-color: #ffffff; - -$base-menu-light-color:rgba(0,0,0,.70); -$base-menu-light-background:#ffffff; -$base-logo-light-title-color: #001529; - -$base-sub-menu-background:#000c17; -$base-sub-menu-hover:#001528; -*/ - -$base-sidebar-width: 200px; - -// the :export directive is the magic sauce for webpack -// https://www.bluematador.com/blog/how-to-share-variables-between-js-and-sass -:export { - menuColor: $base-menu-color; - menuLightColor: $base-menu-light-color; - menuColorActive: $base-menu-color-active; - menuBackground: $base-menu-background; - menuLightBackground: $base-menu-light-background; - subMenuBackground: $base-sub-menu-background; - subMenuHover: $base-sub-menu-hover; - sideBarWidth: $base-sidebar-width; - logoTitleColor: $base-logo-title-color; - logoLightTitleColor: $base-logo-light-title-color -} +// base color +$blue:#324157; +$light-blue:#3A71A8; +$red:#C03639; +$pink: #E65D6E; +$green: #30B08F; +$tiffany: #4AB7BD; +$yellow:#FEC171; +$panGreen: #30B08F; + +// 默认菜单主题风格 +$base-menu-color:#bfcbd9; +$base-menu-color-active:#f4f4f5; +$base-menu-background:#304156; +$base-logo-title-color: #ffffff; + +$base-menu-light-color:rgba(0,0,0,.70); +$base-menu-light-background:#ffffff; +$base-logo-light-title-color: #001529; + +$base-sub-menu-background:#1f2d3d; +$base-sub-menu-hover:#001528; + +// 自定义暗色菜单风格 +/** +$base-menu-color:hsla(0,0%,100%,.65); +$base-menu-color-active:#fff; +$base-menu-background:#001529; +$base-logo-title-color: #ffffff; + +$base-menu-light-color:rgba(0,0,0,.70); +$base-menu-light-background:#ffffff; +$base-logo-light-title-color: #001529; + +$base-sub-menu-background:#000c17; +$base-sub-menu-hover:#001528; +*/ + +$base-sidebar-width: 200px; + +// the :export directive is the magic sauce for webpack +// https://www.bluematador.com/blog/how-to-share-variables-between-js-and-sass +:export { + menuColor: $base-menu-color; + menuLightColor: $base-menu-light-color; + menuColorActive: $base-menu-color-active; + menuBackground: $base-menu-background; + menuLightBackground: $base-menu-light-background; + subMenuBackground: $base-sub-menu-background; + subMenuHover: $base-sub-menu-hover; + sideBarWidth: $base-sidebar-width; + logoTitleColor: $base-logo-title-color; + logoLightTitleColor: $base-logo-light-title-color +} diff --git a/ruoyi-ui/src/components/HeaderSearch/index.vue b/ruoyi-ui/src/components/HeaderSearch/index.vue index 093118425..04218cb05 100644 --- a/ruoyi-ui/src/components/HeaderSearch/index.vue +++ b/ruoyi-ui/src/components/HeaderSearch/index.vue @@ -1,190 +1,190 @@ -<template> - <div :class="{'show':show}" class="header-search"> - <svg-icon class-name="search-icon" icon-class="search" @click.stop="click" /> - <el-select - ref="headerSearchSelect" - v-model="search" - :remote-method="querySearch" - filterable - default-first-option - remote - placeholder="Search" - class="header-search-select" - @change="change" - > - <el-option v-for="option in options" :key="option.item.path" :value="option.item" :label="option.item.title.join(' > ')" /> - </el-select> - </div> -</template> - -<script> -// fuse is a lightweight fuzzy-search module -// make search results more in line with expectations -import Fuse from 'fuse.js/dist/fuse.min.js' -import path from 'path' - -export default { - name: 'HeaderSearch', - data() { - return { - search: '', - options: [], - searchPool: [], - show: false, - fuse: undefined - } - }, - computed: { - routes() { - return this.$store.getters.permission_routes - } - }, - watch: { - routes() { - this.searchPool = this.generateRoutes(this.routes) - }, - searchPool(list) { - this.initFuse(list) - }, - show(value) { - if (value) { - document.body.addEventListener('click', this.close) - } else { - document.body.removeEventListener('click', this.close) - } - } - }, - mounted() { - this.searchPool = this.generateRoutes(this.routes) - }, - methods: { - click() { - this.show = !this.show - if (this.show) { - this.$refs.headerSearchSelect && this.$refs.headerSearchSelect.focus() - } - }, - close() { - this.$refs.headerSearchSelect && this.$refs.headerSearchSelect.blur() - this.options = [] - this.show = false - }, - change(val) { - const path = val.path; - if(this.ishttp(val.path)) { - // http(s):// 路径新窗口打开 - const pindex = path.indexOf("http"); - window.open(path.substr(pindex, path.length), "_blank"); - } else { - this.$router.push(val.path) - } - this.search = '' - this.options = [] - this.$nextTick(() => { - this.show = false - }) - }, - initFuse(list) { - this.fuse = new Fuse(list, { - shouldSort: true, - threshold: 0.4, - location: 0, - distance: 100, - maxPatternLength: 32, - 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 - generateRoutes(routes, basePath = '/', prefixTitle = []) { - let res = [] - - for (const router of routes) { - // skip hidden router - if (router.hidden) { continue } - - const data = { - path: !this.ishttp(router.path) ? path.resolve(basePath, router.path) : router.path, - title: [...prefixTitle] - } - - if (router.meta && router.meta.title) { - data.title = [...data.title, router.meta.title] - - if (router.redirect !== 'noRedirect') { - // only push the routes with title - // special case: need to exclude parent router without redirect - res.push(data) - } - } - - // recursive child routes - if (router.children) { - const tempRoutes = this.generateRoutes(router.children, data.path, data.title) - if (tempRoutes.length >= 1) { - res = [...res, ...tempRoutes] - } - } - } - return res - }, - querySearch(query) { - if (query !== '') { - this.options = this.fuse.search(query) - } else { - this.options = [] - } - }, - ishttp(url) { - return url.indexOf('http://') !== -1 || url.indexOf('https://') !== -1 - } - } -} -</script> - -<style lang="scss" scoped> -.header-search { - font-size: 0 !important; - - .search-icon { - cursor: pointer; - font-size: 18px; - vertical-align: middle; - } - - .header-search-select { - font-size: 18px; - transition: width 0.2s; - width: 0; - overflow: hidden; - background: transparent; - border-radius: 0; - display: inline-block; - vertical-align: middle; - - ::v-deep .el-input__inner { - border-radius: 0; - border: 0; - padding-left: 0; - padding-right: 0; - box-shadow: none !important; - border-bottom: 1px solid #d9d9d9; - vertical-align: middle; - } - } - - &.show { - .header-search-select { - width: 210px; - margin-left: 10px; - } - } -} -</style> +<template> + <div :class="{'show':show}" class="header-search"> + <svg-icon class-name="search-icon" icon-class="search" @click.stop="click" /> + <el-select + ref="headerSearchSelect" + v-model="search" + :remote-method="querySearch" + filterable + default-first-option + remote + placeholder="Search" + class="header-search-select" + @change="change" + > + <el-option v-for="option in options" :key="option.item.path" :value="option.item" :label="option.item.title.join(' > ')" /> + </el-select> + </div> +</template> + +<script> +// fuse is a lightweight fuzzy-search module +// make search results more in line with expectations +import Fuse from 'fuse.js/dist/fuse.min.js' +import path from 'path' + +export default { + name: 'HeaderSearch', + data() { + return { + search: '', + options: [], + searchPool: [], + show: false, + fuse: undefined + } + }, + computed: { + routes() { + return this.$store.getters.permission_routes + } + }, + watch: { + routes() { + this.searchPool = this.generateRoutes(this.routes) + }, + searchPool(list) { + this.initFuse(list) + }, + show(value) { + if (value) { + document.body.addEventListener('click', this.close) + } else { + document.body.removeEventListener('click', this.close) + } + } + }, + mounted() { + this.searchPool = this.generateRoutes(this.routes) + }, + methods: { + click() { + this.show = !this.show + if (this.show) { + this.$refs.headerSearchSelect && this.$refs.headerSearchSelect.focus() + } + }, + close() { + this.$refs.headerSearchSelect && this.$refs.headerSearchSelect.blur() + this.options = [] + this.show = false + }, + change(val) { + const path = val.path; + if(this.ishttp(val.path)) { + // http(s):// 路径新窗口打开 + const pindex = path.indexOf("http"); + window.open(path.substr(pindex, path.length), "_blank"); + } else { + this.$router.push(val.path) + } + this.search = '' + this.options = [] + this.$nextTick(() => { + this.show = false + }) + }, + initFuse(list) { + this.fuse = new Fuse(list, { + shouldSort: true, + threshold: 0.4, + location: 0, + distance: 100, + maxPatternLength: 32, + 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 + generateRoutes(routes, basePath = '/', prefixTitle = []) { + let res = [] + + for (const router of routes) { + // skip hidden router + if (router.hidden) { continue } + + const data = { + path: !this.ishttp(router.path) ? path.resolve(basePath, router.path) : router.path, + title: [...prefixTitle] + } + + if (router.meta && router.meta.title) { + data.title = [...data.title, router.meta.title] + + if (router.redirect !== 'noRedirect') { + // only push the routes with title + // special case: need to exclude parent router without redirect + res.push(data) + } + } + + // recursive child routes + if (router.children) { + const tempRoutes = this.generateRoutes(router.children, data.path, data.title) + if (tempRoutes.length >= 1) { + res = [...res, ...tempRoutes] + } + } + } + return res + }, + querySearch(query) { + if (query !== '') { + this.options = this.fuse.search(query) + } else { + this.options = [] + } + }, + ishttp(url) { + return url.indexOf('http://') !== -1 || url.indexOf('https://') !== -1 + } + } +} +</script> + +<style lang="scss" scoped> +.header-search { + font-size: 0 !important; + + .search-icon { + cursor: pointer; + font-size: 18px; + vertical-align: middle; + } + + .header-search-select { + font-size: 18px; + /*transition: width 0.2s;*/ + width: 0; + overflow: hidden; + background: transparent; + border-radius: 0; + display: inline-block; + vertical-align: middle; + + ::v-deep .el-input__inner { + border-radius: 0; + border: 0; + padding-left: 0; + padding-right: 0; + box-shadow: none !important; + border-bottom: 1px solid #d9d9d9; + vertical-align: middle; + } + } + + &.show { + .header-search-select { + width: 210px; + margin-left: 10px; + } + } +} +</style> diff --git a/ruoyi-ui/src/components/ImagePreview/index.vue b/ruoyi-ui/src/components/ImagePreview/index.vue index 743d8d51d..87ae7a0b0 100644 --- a/ruoyi-ui/src/components/ImagePreview/index.vue +++ b/ruoyi-ui/src/components/ImagePreview/index.vue @@ -65,7 +65,7 @@ export default { background-color: #ebeef5; box-shadow: 0 0 5px 1px #ccc; ::v-deep .el-image__inner { - transition: all 0.3s; + /*transition: all 0.3s;*/ cursor: pointer; &:hover { transform: scale(1.2); diff --git a/ruoyi-ui/src/components/PanThumb/index.vue b/ruoyi-ui/src/components/PanThumb/index.vue index 796b01b89..c90f4f858 100644 --- a/ruoyi-ui/src/components/PanThumb/index.vue +++ b/ruoyi-ui/src/components/PanThumb/index.vue @@ -1,142 +1,142 @@ -<template> - <div :style="{zIndex:zIndex,height:height,width:width}" class="pan-item"> - <div class="pan-info"> - <div class="pan-info-roles-container"> - <slot /> - </div> - </div> - <!-- eslint-disable-next-line --> - <div :style="{backgroundImage: `url(${image})`}" class="pan-thumb"></div> - </div> -</template> - -<script> -export default { - name: 'PanThumb', - props: { - image: { - type: String, - required: true - }, - zIndex: { - type: Number, - default: 1 - }, - width: { - type: String, - default: '150px' - }, - height: { - type: String, - default: '150px' - } - } -} -</script> - -<style scoped> -.pan-item { - width: 200px; - height: 200px; - border-radius: 50%; - display: inline-block; - position: relative; - cursor: default; - box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2); -} - -.pan-info-roles-container { - padding: 20px; - text-align: center; -} - -.pan-thumb { - width: 100%; - height: 100%; - background-position: center center; - background-size: cover; - border-radius: 50%; - overflow: hidden; - position: absolute; - transform-origin: 95% 40%; - transition: all 0.3s ease-in-out; -} - -/* .pan-thumb:after { - content: ''; - width: 8px; - height: 8px; - position: absolute; - border-radius: 50%; - top: 40%; - left: 95%; - margin: -4px 0 0 -4px; - background: radial-gradient(ellipse at center, rgba(14, 14, 14, 1) 0%, rgba(125, 126, 125, 1) 100%); - box-shadow: 0 0 1px rgba(255, 255, 255, 0.9); -} */ - -.pan-info { - position: absolute; - width: inherit; - height: inherit; - border-radius: 50%; - overflow: hidden; - box-shadow: inset 0 0 0 5px rgba(0, 0, 0, 0.05); -} - -.pan-info h3 { - color: #fff; - text-transform: uppercase; - position: relative; - letter-spacing: 2px; - font-size: 18px; - margin: 0 60px; - padding: 22px 0 0 0; - height: 85px; - font-family: 'Open Sans', Arial, sans-serif; - text-shadow: 0 0 1px #fff, 0 1px 2px rgba(0, 0, 0, 0.3); -} - -.pan-info p { - color: #fff; - padding: 10px 5px; - font-style: italic; - margin: 0 30px; - font-size: 12px; - border-top: 1px solid rgba(255, 255, 255, 0.5); -} - -.pan-info p a { - display: block; - color: #333; - width: 80px; - height: 80px; - background: rgba(255, 255, 255, 0.3); - border-radius: 50%; - color: #fff; - font-style: normal; - font-weight: 700; - text-transform: uppercase; - font-size: 9px; - letter-spacing: 1px; - padding-top: 24px; - margin: 7px auto 0; - font-family: 'Open Sans', Arial, sans-serif; - opacity: 0; - transition: transform 0.3s ease-in-out 0.2s, opacity 0.3s ease-in-out 0.2s, background 0.2s linear 0s; - transform: translateX(60px) rotate(90deg); -} - -.pan-info p a:hover { - background: rgba(255, 255, 255, 0.5); -} - -.pan-item:hover .pan-thumb { - transform: rotate(-110deg); -} - -.pan-item:hover .pan-info p a { - opacity: 1; - transform: translateX(0px) rotate(0deg); -} -</style> +<template> + <div :style="{zIndex:zIndex,height:height,width:width}" class="pan-item"> + <div class="pan-info"> + <div class="pan-info-roles-container"> + <slot /> + </div> + </div> + <!-- eslint-disable-next-line --> + <div :style="{backgroundImage: `url(${image})`}" class="pan-thumb"></div> + </div> +</template> + +<script> +export default { + name: 'PanThumb', + props: { + image: { + type: String, + required: true + }, + zIndex: { + type: Number, + default: 1 + }, + width: { + type: String, + default: '150px' + }, + height: { + type: String, + default: '150px' + } + } +} +</script> + +<style scoped> +.pan-item { + width: 200px; + height: 200px; + border-radius: 50%; + display: inline-block; + position: relative; + cursor: default; + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2); +} + +.pan-info-roles-container { + padding: 20px; + text-align: center; +} + +.pan-thumb { + width: 100%; + height: 100%; + background-position: center center; + background-size: cover; + border-radius: 50%; + overflow: hidden; + position: absolute; + transform-origin: 95% 40%; + /*transition: all 0.3s ease-in-out;*/ +} + +/* .pan-thumb:after { + content: ''; + width: 8px; + height: 8px; + position: absolute; + border-radius: 50%; + top: 40%; + left: 95%; + margin: -4px 0 0 -4px; + background: radial-gradient(ellipse at center, rgba(14, 14, 14, 1) 0%, rgba(125, 126, 125, 1) 100%); + box-shadow: 0 0 1px rgba(255, 255, 255, 0.9); +} */ + +.pan-info { + position: absolute; + width: inherit; + height: inherit; + border-radius: 50%; + overflow: hidden; + box-shadow: inset 0 0 0 5px rgba(0, 0, 0, 0.05); +} + +.pan-info h3 { + color: #fff; + text-transform: uppercase; + position: relative; + letter-spacing: 2px; + font-size: 18px; + margin: 0 60px; + padding: 22px 0 0 0; + height: 85px; + font-family: 'Open Sans', Arial, sans-serif; + text-shadow: 0 0 1px #fff, 0 1px 2px rgba(0, 0, 0, 0.3); +} + +.pan-info p { + color: #fff; + padding: 10px 5px; + font-style: italic; + margin: 0 30px; + font-size: 12px; + border-top: 1px solid rgba(255, 255, 255, 0.5); +} + +.pan-info p a { + display: block; + color: #333; + width: 80px; + height: 80px; + background: rgba(255, 255, 255, 0.3); + border-radius: 50%; + color: #fff; + font-style: normal; + font-weight: 700; + text-transform: uppercase; + font-size: 9px; + letter-spacing: 1px; + padding-top: 24px; + margin: 7px auto 0; + font-family: 'Open Sans', Arial, sans-serif; + opacity: 0; + /*transition: transform 0.3s ease-in-out 0.2s, opacity 0.3s ease-in-out 0.2s, background 0.2s linear 0s;*/ + transform: translateX(60px) rotate(90deg); +} + +.pan-info p a:hover { + background: rgba(255, 255, 255, 0.5); +} + +.pan-item:hover .pan-thumb { + transform: rotate(-110deg); +} + +.pan-item:hover .pan-info p a { + opacity: 1; + transform: translateX(0px) rotate(0deg); +} +</style> diff --git a/ruoyi-ui/src/components/RightPanel/index.vue b/ruoyi-ui/src/components/RightPanel/index.vue index 1534b9bee..7609aa7da 100644 --- a/ruoyi-ui/src/components/RightPanel/index.vue +++ b/ruoyi-ui/src/components/RightPanel/index.vue @@ -1,149 +1,149 @@ -<template> - <div ref="rightPanel" :class="{show:show}" class="rightPanel-container"> - <div class="rightPanel-background" /> - <div class="rightPanel"> - <div class="rightPanel-items"> - <slot /> - </div> - </div> - </div> -</template> - -<script> -import { addClass, removeClass } from '@/utils' - -export default { - name: 'RightPanel', - props: { - clickNotClose: { - default: false, - type: Boolean - }, - buttonTop: { - default: 250, - type: Number - } - }, - computed: { - show: { - get() { - return this.$store.state.settings.showSettings - }, - set(val) { - this.$store.dispatch('settings/changeSetting', { - key: 'showSettings', - value: val - }) - } - }, - theme() { - return this.$store.state.settings.theme - }, - }, - watch: { - show(value) { - if (value && !this.clickNotClose) { - this.addEventClick() - } - if (value) { - addClass(document.body, 'showRightPanel') - } else { - removeClass(document.body, 'showRightPanel') - } - } - }, - mounted() { - this.insertToBody() - this.addEventClick() - }, - beforeDestroy() { - const elx = this.$refs.rightPanel - elx.remove() - }, - methods: { - addEventClick() { - window.addEventListener('click', this.closeSidebar) - }, - closeSidebar(evt) { - const parent = evt.target.closest('.rightPanel') - if (!parent) { - this.show = false - window.removeEventListener('click', this.closeSidebar) - } - }, - insertToBody() { - const elx = this.$refs.rightPanel - const body = document.querySelector('body') - body.insertBefore(elx, body.firstChild) - } - } -} -</script> - -<style> -.showRightPanel { - overflow: hidden; - position: relative; - width: calc(100% - 15px); -} -</style> - -<style lang="scss" scoped> -.rightPanel-background { - position: fixed; - top: 0; - left: 0; - opacity: 0; - transition: opacity .3s cubic-bezier(.7, .3, .1, 1); - background: rgba(0, 0, 0, .2); - z-index: -1; -} - -.rightPanel { - width: 100%; - max-width: 260px; - height: 100vh; - position: fixed; - top: 0; - right: 0; - box-shadow: 0px 0px 15px 0px rgba(0, 0, 0, .05); - transition: all .25s cubic-bezier(.7, .3, .1, 1); - transform: translate(100%); - background: #fff; - z-index: 40000; -} - -.show { - transition: all .3s cubic-bezier(.7, .3, .1, 1); - - .rightPanel-background { - z-index: 20000; - opacity: 1; - width: 100%; - height: 100%; - } - - .rightPanel { - transform: translate(0); - } -} - -.handle-button { - width: 48px; - height: 48px; - position: absolute; - left: -48px; - text-align: center; - font-size: 24px; - border-radius: 6px 0 0 6px !important; - z-index: 0; - pointer-events: auto; - cursor: pointer; - color: #fff; - line-height: 48px; - i { - font-size: 24px; - line-height: 48px; - } -} -</style> +<template> + <div ref="rightPanel" :class="{show:show}" class="rightPanel-container"> + <div class="rightPanel-background" /> + <div class="rightPanel"> + <div class="rightPanel-items"> + <slot /> + </div> + </div> + </div> +</template> + +<script> +import { addClass, removeClass } from '@/utils' + +export default { + name: 'RightPanel', + props: { + clickNotClose: { + default: false, + type: Boolean + }, + buttonTop: { + default: 250, + type: Number + } + }, + computed: { + show: { + get() { + return this.$store.state.settings.showSettings + }, + set(val) { + this.$store.dispatch('settings/changeSetting', { + key: 'showSettings', + value: val + }) + } + }, + theme() { + return this.$store.state.settings.theme + }, + }, + watch: { + show(value) { + if (value && !this.clickNotClose) { + this.addEventClick() + } + if (value) { + addClass(document.body, 'showRightPanel') + } else { + removeClass(document.body, 'showRightPanel') + } + } + }, + mounted() { + this.insertToBody() + this.addEventClick() + }, + beforeDestroy() { + const elx = this.$refs.rightPanel + elx.remove() + }, + methods: { + addEventClick() { + window.addEventListener('click', this.closeSidebar) + }, + closeSidebar(evt) { + const parent = evt.target.closest('.rightPanel') + if (!parent) { + this.show = false + window.removeEventListener('click', this.closeSidebar) + } + }, + insertToBody() { + const elx = this.$refs.rightPanel + const body = document.querySelector('body') + body.insertBefore(elx, body.firstChild) + } + } +} +</script> + +<style> +.showRightPanel { + overflow: hidden; + position: relative; + width: calc(100% - 15px); +} +</style> + +<style lang="scss" scoped> +.rightPanel-background { + position: fixed; + top: 0; + left: 0; + opacity: 0; + /*transition: opacity .3s cubic-bezier(.7, .3, .1, 1);*/ + background: rgba(0, 0, 0, .2); + z-index: -1; +} + +.rightPanel { + width: 100%; + max-width: 260px; + height: 100vh; + position: fixed; + top: 0; + right: 0; + box-shadow: 0px 0px 15px 0px rgba(0, 0, 0, .05); + /*transition: all .25s cubic-bezier(.7, .3, .1, 1);*/ + transform: translate(100%); + background: #fff; + z-index: 40000; +} + +.show { + /*transition: all .3s cubic-bezier(.7, .3, .1, 1);*/ + + .rightPanel-background { + z-index: 20000; + opacity: 1; + width: 100%; + height: 100%; + } + + .rightPanel { + transform: translate(0); + } +} + +.handle-button { + width: 48px; + height: 48px; + position: absolute; + left: -48px; + text-align: center; + font-size: 24px; + border-radius: 6px 0 0 6px !important; + z-index: 0; + pointer-events: auto; + cursor: pointer; + color: #fff; + line-height: 48px; + i { + font-size: 24px; + line-height: 48px; + } +} +</style> diff --git a/ruoyi-ui/src/layout/components/Navbar.vue b/ruoyi-ui/src/layout/components/Navbar.vue index 67a53ab67..4d24516ec 100644 --- a/ruoyi-ui/src/layout/components/Navbar.vue +++ b/ruoyi-ui/src/layout/components/Navbar.vue @@ -1,200 +1,200 @@ -<template> - <div class="navbar"> - <hamburger id="hamburger-container" :is-active="sidebar.opened" class="hamburger-container" @toggleClick="toggleSideBar" /> - - <breadcrumb id="breadcrumb-container" class="breadcrumb-container" v-if="!topNav"/> - <top-nav id="topmenu-container" class="topmenu-container" v-if="topNav"/> - - <div class="right-menu"> - <template v-if="device!=='mobile'"> - <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"> - <size-select id="size-select" class="right-menu-item hover-effect" /> - </el-tooltip> - - </template> - - <el-dropdown class="avatar-container right-menu-item hover-effect" trigger="click"> - <div class="avatar-wrapper"> - <img :src="avatar" class="user-avatar"> - <i class="el-icon-caret-bottom" /> - </div> - <el-dropdown-menu slot="dropdown"> - <router-link to="/user/profile"> - <el-dropdown-item>个人中心</el-dropdown-item> - </router-link> - <el-dropdown-item @click.native="setting = true"> - <span>布局设置</span> - </el-dropdown-item> - <el-dropdown-item divided @click.native="logout"> - <span>退出登录</span> - </el-dropdown-item> - </el-dropdown-menu> - </el-dropdown> - </div> - </div> -</template> - -<script> -import { mapGetters } from 'vuex' -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 Search from '@/components/HeaderSearch' -import RuoYiGit from '@/components/RuoYi/Git' -import RuoYiDoc from '@/components/RuoYi/Doc' - -export default { - components: { - Breadcrumb, - TopNav, - Hamburger, - Screenfull, - SizeSelect, - Search, - RuoYiGit, - RuoYiDoc - }, - computed: { - ...mapGetters([ - 'sidebar', - 'avatar', - 'device' - ]), - setting: { - get() { - return this.$store.state.settings.showSettings - }, - set(val) { - this.$store.dispatch('settings/changeSetting', { - key: 'showSettings', - value: val - }) - } - }, - topNav: { - get() { - return this.$store.state.settings.topNav - } - } - }, - methods: { - toggleSideBar() { - this.$store.dispatch('app/toggleSideBar') - }, - async logout() { - this.$confirm('确定注销并退出系统吗?', '提示', { - confirmButtonText: '确定', - cancelButtonText: '取消', - type: 'warning' - }).then(() => { - this.$store.dispatch('LogOut').then(() => { - location.href = '/index'; - }) - }).catch(() => {}); - } - } -} -</script> - -<style lang="scss" scoped> -.navbar { - height: 50px; - overflow: hidden; - position: relative; - background: #fff; - box-shadow: 0 1px 4px rgba(0,21,41,.08); - - .hamburger-container { - line-height: 46px; - height: 100%; - float: left; - cursor: pointer; - transition: background .3s; - -webkit-tap-highlight-color:transparent; - - &:hover { - background: rgba(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; - - &: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 .3s; - - &:hover { - background: rgba(0, 0, 0, .025) - } - } - } - - .avatar-container { - margin-right: 30px; - - .avatar-wrapper { - margin-top: 5px; - position: relative; - - .user-avatar { - cursor: pointer; - width: 40px; - height: 40px; - border-radius: 10px; - } - - .el-icon-caret-bottom { - cursor: pointer; - position: absolute; - right: -20px; - top: 25px; - font-size: 12px; - } - } - } - } -} -</style> +<template> + <div class="navbar"> + <hamburger id="hamburger-container" :is-active="sidebar.opened" class="hamburger-container" @toggleClick="toggleSideBar" /> + + <breadcrumb id="breadcrumb-container" class="breadcrumb-container" v-if="!topNav"/> + <top-nav id="topmenu-container" class="topmenu-container" v-if="topNav"/> + + <div class="right-menu"> + <template v-if="device!=='mobile'"> + <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"> + <size-select id="size-select" class="right-menu-item hover-effect" /> + </el-tooltip> + + </template> + + <el-dropdown class="avatar-container right-menu-item hover-effect" trigger="click"> + <div class="avatar-wrapper"> + <img :src="avatar" class="user-avatar"> + <i class="el-icon-caret-bottom" /> + </div> + <el-dropdown-menu slot="dropdown"> + <router-link to="/user/profile"> + <el-dropdown-item>个人中心</el-dropdown-item> + </router-link> + <el-dropdown-item @click.native="setting = true"> + <span>布局设置</span> + </el-dropdown-item> + <el-dropdown-item divided @click.native="logout"> + <span>退出登录</span> + </el-dropdown-item> + </el-dropdown-menu> + </el-dropdown> + </div> + </div> +</template> + +<script> +import { mapGetters } from 'vuex' +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 Search from '@/components/HeaderSearch' +import RuoYiGit from '@/components/RuoYi/Git' +import RuoYiDoc from '@/components/RuoYi/Doc' + +export default { + components: { + Breadcrumb, + TopNav, + Hamburger, + Screenfull, + SizeSelect, + Search, + RuoYiGit, + RuoYiDoc + }, + computed: { + ...mapGetters([ + 'sidebar', + 'avatar', + 'device' + ]), + setting: { + get() { + return this.$store.state.settings.showSettings + }, + set(val) { + this.$store.dispatch('settings/changeSetting', { + key: 'showSettings', + value: val + }) + } + }, + topNav: { + get() { + return this.$store.state.settings.topNav + } + } + }, + methods: { + toggleSideBar() { + this.$store.dispatch('app/toggleSideBar') + }, + async logout() { + this.$confirm('确定注销并退出系统吗?', '提示', { + confirmButtonText: '确定', + cancelButtonText: '取消', + type: 'warning' + }).then(() => { + this.$store.dispatch('LogOut').then(() => { + location.href = '/index'; + }) + }).catch(() => {}); + } + } +} +</script> + +<style lang="scss" scoped> +.navbar { + height: 50px; + overflow: hidden; + position: relative; + background: #fff; + box-shadow: 0 1px 4px rgba(0,21,41,.08); + + .hamburger-container { + line-height: 46px; + height: 100%; + float: left; + cursor: pointer; + /*transition: background .3s;*/ + -webkit-tap-highlight-color:transparent; + + &:hover { + background: rgba(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; + + &: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, .025) + } + } + } + + .avatar-container { + margin-right: 30px; + + .avatar-wrapper { + margin-top: 5px; + position: relative; + + .user-avatar { + cursor: pointer; + width: 40px; + height: 40px; + border-radius: 10px; + } + + .el-icon-caret-bottom { + cursor: pointer; + position: absolute; + right: -20px; + top: 25px; + font-size: 12px; + } + } + } + } +} +</style> diff --git a/ruoyi-ui/src/layout/components/Sidebar/Logo.vue b/ruoyi-ui/src/layout/components/Sidebar/Logo.vue index c8401c519..b8368c235 100644 --- a/ruoyi-ui/src/layout/components/Sidebar/Logo.vue +++ b/ruoyi-ui/src/layout/components/Sidebar/Logo.vue @@ -1,93 +1,93 @@ -<template> - <div class="sidebar-logo-container" :class="{'collapse':collapse}" :style="{ backgroundColor: sideTheme === 'theme-dark' ? variables.menuBackground : variables.menuLightBackground }"> - <transition name="sidebarLogoFade"> - <router-link v-if="collapse" key="collapse" class="sidebar-logo-link" to="/"> - <img v-if="logo" :src="logo" class="sidebar-logo" /> - <h1 v-else class="sidebar-title" :style="{ color: sideTheme === 'theme-dark' ? variables.logoTitleColor : variables.logoLightTitleColor }">{{ title }} </h1> - </router-link> - <router-link v-else key="expand" class="sidebar-logo-link" to="/"> - <img v-if="logo" :src="logo" class="sidebar-logo" /> - <h1 class="sidebar-title" :style="{ color: sideTheme === 'theme-dark' ? variables.logoTitleColor : variables.logoLightTitleColor }">{{ title }} </h1> - </router-link> - </transition> - </div> -</template> - -<script> -import logoImg from '@/assets/logo/logo.png' -import variables from '@/assets/styles/variables.scss' - -export default { - name: 'SidebarLogo', - props: { - collapse: { - type: Boolean, - required: true - } - }, - computed: { - variables() { - return variables; - }, - sideTheme() { - return this.$store.state.settings.sideTheme - } - }, - data() { - return { - title: '若依管理系统', - logo: logoImg - } - } -} -</script> - -<style lang="scss" scoped> -.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: #2b2f3a; - 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: #fff; - 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> +<template> + <div class="sidebar-logo-container" :class="{'collapse':collapse}" :style="{ backgroundColor: sideTheme === 'theme-dark' ? variables.menuBackground : variables.menuLightBackground }"> +<!-- <transition name="sidebarLogoFade">--> + <router-link v-if="collapse" key="collapse" class="sidebar-logo-link" to="/"> + <img v-if="logo" :src="logo" class="sidebar-logo" /> + <h1 v-else class="sidebar-title" :style="{ color: sideTheme === 'theme-dark' ? variables.logoTitleColor : variables.logoLightTitleColor }">{{ title }} </h1> + </router-link> + <router-link v-else key="expand" class="sidebar-logo-link" to="/"> + <img v-if="logo" :src="logo" class="sidebar-logo" /> + <h1 class="sidebar-title" :style="{ color: sideTheme === 'theme-dark' ? variables.logoTitleColor : variables.logoLightTitleColor }">{{ title }} </h1> + </router-link> +<!-- </transition>--> + </div> +</template> + +<script> +import logoImg from '@/assets/logo/logo.png' +import variables from '@/assets/styles/variables.scss' + +export default { + name: 'SidebarLogo', + props: { + collapse: { + type: Boolean, + required: true + } + }, + computed: { + variables() { + return variables; + }, + sideTheme() { + return this.$store.state.settings.sideTheme + } + }, + data() { + return { + title: '若依管理系统', + logo: logoImg + } + } +} +</script> + +<style lang="scss" scoped> +.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: #2b2f3a; + 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: #fff; + 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> diff --git a/ruoyi-ui/src/layout/components/TagsView/index.vue b/ruoyi-ui/src/layout/components/TagsView/index.vue index 2381d2e09..696f429b5 100644 --- a/ruoyi-ui/src/layout/components/TagsView/index.vue +++ b/ruoyi-ui/src/layout/components/TagsView/index.vue @@ -1,326 +1,326 @@ -<template> - <div id="tags-view-container" class="tags-view-container"> - <scroll-pane ref="scrollPane" class="tags-view-wrapper" @scroll="handleScroll"> - <router-link - v-for="tag in visitedViews" - ref="tag" - :key="tag.path" - :class="isActive(tag)?'active':''" - :to="{ path: tag.path, query: tag.query, fullPath: tag.fullPath }" - tag="span" - class="tags-view-item" - :style="activeStyle(tag)" - @click.middle.native="!isAffix(tag)?closeSelectedTag(tag):''" - @contextmenu.prevent.native="openMenu(tag,$event)" - > - {{ tag.title }} - <span v-if="!isAffix(tag)" class="el-icon-close" @click.prevent.stop="closeSelectedTag(tag)" /> - </router-link> - </scroll-pane> - <ul v-show="visible" :style="{left:left+'px',top:top+'px'}" class="contextmenu"> - <li @click="refreshSelectedTag(selectedTag)"><i class="el-icon-refresh-right"></i> 刷新页面</li> - <li v-if="!isAffix(selectedTag)" @click="closeSelectedTag(selectedTag)"><i class="el-icon-close"></i> 关闭当前</li> - <li @click="closeOthersTags"><i class="el-icon-circle-close"></i> 关闭其他</li> - <li v-if="!isFirstView()" @click="closeLeftTags"><i class="el-icon-back"></i> 关闭左侧</li> - <li v-if="!isLastView()" @click="closeRightTags"><i class="el-icon-right"></i> 关闭右侧</li> - <li @click="closeAllTags(selectedTag)"><i class="el-icon-circle-close"></i> 全部关闭</li> - </ul> - </div> -</template> - -<script> -import ScrollPane from './ScrollPane' -import path from 'path' - -export default { - components: { ScrollPane }, - data() { - return { - visible: false, - top: 0, - left: 0, - selectedTag: {}, - affixTags: [] - } - }, - computed: { - visitedViews() { - return this.$store.state.tagsView.visitedViews - }, - routes() { - return this.$store.state.permission.routes - }, - theme() { - return this.$store.state.settings.theme; - } - }, - watch: { - $route() { - this.addTags() - this.moveToCurrentTag() - }, - visible(value) { - if (value) { - document.body.addEventListener('click', this.closeMenu) - } else { - document.body.removeEventListener('click', this.closeMenu) - } - } - }, - mounted() { - this.initTags() - this.addTags() - }, - methods: { - isActive(route) { - return route.path === this.$route.path - }, - activeStyle(tag) { - if (!this.isActive(tag)) return {}; - return { - "background-color": this.theme, - "border-color": this.theme - }; - }, - isAffix(tag) { - return tag.meta && tag.meta.affix - }, - isFirstView() { - try { - return this.selectedTag.fullPath === this.visitedViews[1].fullPath || this.selectedTag.fullPath === '/index' - } catch (err) { - return false - } - }, - isLastView() { - try { - return this.selectedTag.fullPath === this.visitedViews[this.visitedViews.length - 1].fullPath - } catch (err) { - return false - } - }, - filterAffixTags(routes, basePath = '/') { - let tags = [] - routes.forEach(route => { - if (route.meta && route.meta.affix) { - const tagPath = path.resolve(basePath, route.path) - tags.push({ - fullPath: tagPath, - path: tagPath, - name: route.name, - meta: { ...route.meta } - }) - } - if (route.children) { - const tempTags = this.filterAffixTags(route.children, route.path) - if (tempTags.length >= 1) { - tags = [...tags, ...tempTags] - } - } - }) - return tags - }, - initTags() { - const affixTags = this.affixTags = this.filterAffixTags(this.routes) - for (const tag of affixTags) { - // Must have tag name - if (tag.name) { - this.$store.dispatch('tagsView/addVisitedView', tag) - } - } - }, - addTags() { - const { name } = this.$route - if (name) { - this.$store.dispatch('tagsView/addView', this.$route) - } - return false - }, - moveToCurrentTag() { - const tags = this.$refs.tag - this.$nextTick(() => { - for (const tag of tags) { - if (tag.to.path === this.$route.path) { - this.$refs.scrollPane.moveToTarget(tag) - // when query is different then update - if (tag.to.fullPath !== this.$route.fullPath) { - this.$store.dispatch('tagsView/updateVisitedView', this.$route) - } - break - } - } - }) - }, - refreshSelectedTag(view) { - this.$tab.refreshPage(view); - }, - closeSelectedTag(view) { - this.$tab.closePage(view).then(({ visitedViews }) => { - if (this.isActive(view)) { - this.toLastView(visitedViews, view) - } - }) - }, - closeRightTags() { - this.$tab.closeRightPage(this.selectedTag).then(visitedViews => { - if (!visitedViews.find(i => i.fullPath === this.$route.fullPath)) { - this.toLastView(visitedViews) - } - }) - }, - closeLeftTags() { - this.$tab.closeLeftPage(this.selectedTag).then(visitedViews => { - if (!visitedViews.find(i => i.fullPath === this.$route.fullPath)) { - this.toLastView(visitedViews) - } - }) - }, - closeOthersTags() { - this.$router.push(this.selectedTag).catch(()=>{}); - this.$tab.closeOtherPage(this.selectedTag).then(() => { - this.moveToCurrentTag() - }) - }, - closeAllTags(view) { - this.$tab.closeAllPage().then(({ visitedViews }) => { - if (this.affixTags.some(tag => tag.path === this.$route.path)) { - return - } - this.toLastView(visitedViews, view) - }) - }, - toLastView(visitedViews, view) { - const latestView = visitedViews.slice(-1)[0] - if (latestView) { - this.$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 - this.$router.replace({ path: '/redirect' + view.fullPath }) - } else { - this.$router.push('/') - } - } - }, - openMenu(tag, e) { - const menuMinWidth = 105 - const offsetLeft = this.$el.getBoundingClientRect().left // container margin left - const offsetWidth = this.$el.offsetWidth // container width - const maxLeft = offsetWidth - menuMinWidth // left boundary - const left = e.clientX - offsetLeft + 15 // 15: margin right - - if (left > maxLeft) { - this.left = maxLeft - } else { - this.left = left - } - - this.top = e.clientY - this.visible = true - this.selectedTag = tag - }, - closeMenu() { - this.visible = false - }, - handleScroll() { - this.closeMenu() - } - } -} -</script> - -<style lang="scss" scoped> -.tags-view-container { - height: 34px; - width: 100%; - background: #fff; - border-bottom: 1px solid #d8dce5; - box-shadow: 0 1px 3px 0 rgba(0, 0, 0, .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 #d8dce5; - color: #495060; - background: #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: 2px; - } - } - } - } - .contextmenu { - margin: 0; - background: #fff; - z-index: 3000; - position: absolute; - list-style-type: none; - padding: 5px 0; - border-radius: 4px; - font-size: 12px; - font-weight: 400; - color: #333; - box-shadow: 2px 2px 3px 0 rgba(0, 0, 0, .3); - li { - margin: 0; - padding: 7px 16px; - cursor: pointer; - &:hover { - background: #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: #b4bccc; - color: #fff; - } - } - } -} -</style> +<template> + <div id="tags-view-container" class="tags-view-container"> + <scroll-pane ref="scrollPane" class="tags-view-wrapper" @scroll="handleScroll"> + <router-link + v-for="tag in visitedViews" + ref="tag" + :key="tag.path" + :class="isActive(tag)?'active':''" + :to="{ path: tag.path, query: tag.query, fullPath: tag.fullPath }" + tag="span" + class="tags-view-item" + :style="activeStyle(tag)" + @click.middle.native="!isAffix(tag)?closeSelectedTag(tag):''" + @contextmenu.prevent.native="openMenu(tag,$event)" + > + {{ tag.title }} + <span v-if="!isAffix(tag)" class="el-icon-close" @click.prevent.stop="closeSelectedTag(tag)" /> + </router-link> + </scroll-pane> + <ul v-show="visible" :style="{left:left+'px',top:top+'px'}" class="contextmenu"> + <li @click="refreshSelectedTag(selectedTag)"><i class="el-icon-refresh-right"></i> 刷新页面</li> + <li v-if="!isAffix(selectedTag)" @click="closeSelectedTag(selectedTag)"><i class="el-icon-close"></i> 关闭当前</li> + <li @click="closeOthersTags"><i class="el-icon-circle-close"></i> 关闭其他</li> + <li v-if="!isFirstView()" @click="closeLeftTags"><i class="el-icon-back"></i> 关闭左侧</li> + <li v-if="!isLastView()" @click="closeRightTags"><i class="el-icon-right"></i> 关闭右侧</li> + <li @click="closeAllTags(selectedTag)"><i class="el-icon-circle-close"></i> 全部关闭</li> + </ul> + </div> +</template> + +<script> +import ScrollPane from './ScrollPane' +import path from 'path' + +export default { + components: { ScrollPane }, + data() { + return { + visible: false, + top: 0, + left: 0, + selectedTag: {}, + affixTags: [] + } + }, + computed: { + visitedViews() { + return this.$store.state.tagsView.visitedViews + }, + routes() { + return this.$store.state.permission.routes + }, + theme() { + return this.$store.state.settings.theme; + } + }, + watch: { + $route() { + this.addTags() + this.moveToCurrentTag() + }, + visible(value) { + if (value) { + document.body.addEventListener('click', this.closeMenu) + } else { + document.body.removeEventListener('click', this.closeMenu) + } + } + }, + mounted() { + this.initTags() + this.addTags() + }, + methods: { + isActive(route) { + return route.path === this.$route.path + }, + activeStyle(tag) { + if (!this.isActive(tag)) return {}; + return { + "background-color": this.theme, + "border-color": this.theme + }; + }, + isAffix(tag) { + return tag.meta && tag.meta.affix + }, + isFirstView() { + try { + return this.selectedTag.fullPath === this.visitedViews[1].fullPath || this.selectedTag.fullPath === '/index' + } catch (err) { + return false + } + }, + isLastView() { + try { + return this.selectedTag.fullPath === this.visitedViews[this.visitedViews.length - 1].fullPath + } catch (err) { + return false + } + }, + filterAffixTags(routes, basePath = '/') { + let tags = [] + routes.forEach(route => { + if (route.meta && route.meta.affix) { + const tagPath = path.resolve(basePath, route.path) + tags.push({ + fullPath: tagPath, + path: tagPath, + name: route.name, + meta: { ...route.meta } + }) + } + if (route.children) { + const tempTags = this.filterAffixTags(route.children, route.path) + if (tempTags.length >= 1) { + tags = [...tags, ...tempTags] + } + } + }) + return tags + }, + initTags() { + const affixTags = this.affixTags = this.filterAffixTags(this.routes) + for (const tag of affixTags) { + // Must have tag name + if (tag.name) { + this.$store.dispatch('tagsView/addVisitedView', tag) + } + } + }, + addTags() { + const { name } = this.$route + if (name) { + this.$store.dispatch('tagsView/addView', this.$route) + } + return false + }, + moveToCurrentTag() { + const tags = this.$refs.tag + this.$nextTick(() => { + for (const tag of tags) { + if (tag.to.path === this.$route.path) { + this.$refs.scrollPane.moveToTarget(tag) + // when query is different then update + if (tag.to.fullPath !== this.$route.fullPath) { + this.$store.dispatch('tagsView/updateVisitedView', this.$route) + } + break + } + } + }) + }, + refreshSelectedTag(view) { + this.$tab.refreshPage(view); + }, + closeSelectedTag(view) { + this.$tab.closePage(view).then(({ visitedViews }) => { + if (this.isActive(view)) { + this.toLastView(visitedViews, view) + } + }) + }, + closeRightTags() { + this.$tab.closeRightPage(this.selectedTag).then(visitedViews => { + if (!visitedViews.find(i => i.fullPath === this.$route.fullPath)) { + this.toLastView(visitedViews) + } + }) + }, + closeLeftTags() { + this.$tab.closeLeftPage(this.selectedTag).then(visitedViews => { + if (!visitedViews.find(i => i.fullPath === this.$route.fullPath)) { + this.toLastView(visitedViews) + } + }) + }, + closeOthersTags() { + this.$router.push(this.selectedTag).catch(()=>{}); + this.$tab.closeOtherPage(this.selectedTag).then(() => { + this.moveToCurrentTag() + }) + }, + closeAllTags(view) { + this.$tab.closeAllPage().then(({ visitedViews }) => { + if (this.affixTags.some(tag => tag.path === this.$route.path)) { + return + } + this.toLastView(visitedViews, view) + }) + }, + toLastView(visitedViews, view) { + const latestView = visitedViews.slice(-1)[0] + if (latestView) { + this.$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 + this.$router.replace({ path: '/redirect' + view.fullPath }) + } else { + this.$router.push('/') + } + } + }, + openMenu(tag, e) { + const menuMinWidth = 105 + const offsetLeft = this.$el.getBoundingClientRect().left // container margin left + const offsetWidth = this.$el.offsetWidth // container width + const maxLeft = offsetWidth - menuMinWidth // left boundary + const left = e.clientX - offsetLeft + 15 // 15: margin right + + if (left > maxLeft) { + this.left = maxLeft + } else { + this.left = left + } + + this.top = e.clientY + this.visible = true + this.selectedTag = tag + }, + closeMenu() { + this.visible = false + }, + handleScroll() { + this.closeMenu() + } + } +} +</script> + +<style lang="scss" scoped> +.tags-view-container { + height: 34px; + width: 100%; + background: #fff; + border-bottom: 1px solid #d8dce5; + box-shadow: 0 1px 3px 0 rgba(0, 0, 0, .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 #d8dce5; + color: #495060; + background: #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: 2px; + } + } + } + } + .contextmenu { + margin: 0; + background: #fff; + z-index: 3000; + position: absolute; + list-style-type: none; + padding: 5px 0; + border-radius: 4px; + font-size: 12px; + font-weight: 400; + color: #333; + box-shadow: 2px 2px 3px 0 rgba(0, 0, 0, .3); + li { + margin: 0; + padding: 7px 16px; + cursor: pointer; + &:hover { + background: #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: #b4bccc; + color: #fff; + } + } + } +} +</style> diff --git a/ruoyi-ui/src/layout/index.vue b/ruoyi-ui/src/layout/index.vue index 9540aa4b3..8c10e0bc7 100644 --- a/ruoyi-ui/src/layout/index.vue +++ b/ruoyi-ui/src/layout/index.vue @@ -1,111 +1,111 @@ -<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 /> - <tags-view v-if="needTagsView" /> - </div> - <app-main /> - <right-panel> - <settings /> - </right-panel> - </div> - </div> -</template> - -<script> -import RightPanel from '@/components/RightPanel' -import { AppMain, Navbar, Settings, Sidebar, TagsView } from './components' -import ResizeMixin from './mixin/ResizeHandler' -import { mapState } from 'vuex' -import variables from '@/assets/styles/variables.scss' - -export default { - name: 'Layout', - components: { - AppMain, - Navbar, - RightPanel, - Settings, - Sidebar, - TagsView - }, - mixins: [ResizeMixin], - computed: { - ...mapState({ - theme: state => state.settings.theme, - sideTheme: state => state.settings.sideTheme, - sidebar: state => state.app.sidebar, - device: state => state.app.device, - needTagsView: state => state.settings.tagsView, - fixedHeader: state => state.settings.fixedHeader - }), - classObj() { - return { - hideSidebar: !this.sidebar.opened, - openSidebar: this.sidebar.opened, - withoutAnimation: this.sidebar.withoutAnimation, - mobile: this.device === 'mobile' - } - }, - variables() { - return variables; - } - }, - methods: { - handleClickOutside() { - this.$store.dispatch('app/closeSideBar', { withoutAnimation: false }) - } - } -} -</script> - -<style lang="scss" scoped> - @import "~@/assets/styles/mixin.scss"; - @import "~@/assets/styles/variables.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: calc(100%); - } - - .mobile .fixed-header { - width: 100%; - } -</style> +<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 /> + <tags-view v-if="needTagsView" /> + </div> + <app-main /> + <right-panel> + <settings /> + </right-panel> + </div> + </div> +</template> + +<script> +import RightPanel from '@/components/RightPanel' +import { AppMain, Navbar, Settings, Sidebar, TagsView } from './components' +import ResizeMixin from './mixin/ResizeHandler' +import { mapState } from 'vuex' +import variables from '@/assets/styles/variables.scss' + +export default { + name: 'Layout', + components: { + AppMain, + Navbar, + RightPanel, + Settings, + Sidebar, + TagsView + }, + mixins: [ResizeMixin], + computed: { + ...mapState({ + theme: state => state.settings.theme, + sideTheme: state => state.settings.sideTheme, + sidebar: state => state.app.sidebar, + device: state => state.app.device, + needTagsView: state => state.settings.tagsView, + fixedHeader: state => state.settings.fixedHeader + }), + classObj() { + return { + hideSidebar: !this.sidebar.opened, + openSidebar: this.sidebar.opened, + withoutAnimation: this.sidebar.withoutAnimation, + mobile: this.device === 'mobile' + } + }, + variables() { + return variables; + } + }, + methods: { + handleClickOutside() { + this.$store.dispatch('app/closeSideBar', { withoutAnimation: false }) + } + } +} +</script> + +<style lang="scss" scoped> + @import "~@/assets/styles/mixin.scss"; + @import "~@/assets/styles/variables.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: calc(100%); + } + + .mobile .fixed-header { + width: 100%; + } +</style> diff --git a/ruoyi-ui/src/main.js b/ruoyi-ui/src/main.js index ebd94b9d8..be9810f44 100644 --- a/ruoyi-ui/src/main.js +++ b/ruoyi-ui/src/main.js @@ -1,86 +1,87 @@ -import Vue from 'vue' - -import Cookies from 'js-cookie' - -import Element from 'element-ui' -import './assets/styles/element-variables.scss' - -import '@/assets/styles/index.scss' // global css -import '@/assets/styles/ruoyi.scss' // ruoyi 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' - -import './assets/icons' // icon -import './permission' // permission control -import { getDicts } from "@/api/system/dict/data"; -import { getConfigKey } from "@/api/system/config"; -import { parseTime, resetForm, addDateRange, selectDictLabel, selectDictLabels, handleTree } 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' -// 头部标签组件 -import VueMeta from 'vue-meta' -// 字典数据组件 -import DictData from '@/components/DictData' - -// 全局方法挂载 -Vue.prototype.getDicts = getDicts -Vue.prototype.getConfigKey = getConfigKey -Vue.prototype.parseTime = parseTime -Vue.prototype.resetForm = resetForm -Vue.prototype.addDateRange = addDateRange -Vue.prototype.selectDictLabel = selectDictLabel -Vue.prototype.selectDictLabels = selectDictLabels -Vue.prototype.download = download -Vue.prototype.handleTree = handleTree - -// 全局组件挂载 -Vue.component('DictTag', DictTag) -Vue.component('Pagination', Pagination) -Vue.component('RightToolbar', RightToolbar) -Vue.component('Editor', Editor) -Vue.component('FileUpload', FileUpload) -Vue.component('ImageUpload', ImageUpload) -Vue.component('ImagePreview', ImagePreview) - -Vue.use(directive) -Vue.use(plugins) -Vue.use(VueMeta) -DictData.install() - -/** - * If you don't want to use mock-server - * you want to use MockJs for mock api - * you can execute: mockXHR() - * - * Currently MockJs will be used in the production environment, - * please remove it before going online! ! ! - */ - -Vue.use(Element, { - size: Cookies.get('size') || 'medium' // set element-ui default size -}) - -Vue.config.productionTip = false - -new Vue({ - el: '#app', - router, - store, - render: h => h(App) -}) +import Vue from 'vue' + +import Cookies from 'js-cookie' + +import Element from 'element-ui' +import './assets/styles/element-variables.scss' + +import '@/assets/styles/index.scss' // global css +import '@/assets/styles/ruoyi.scss' // ruoyi 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' + +import './assets/icons' // icon +import './permission' // permission control +import { getDicts } from "@/api/system/dict/data"; +import { getConfigKey } from "@/api/system/config"; +import { parseTime, resetForm, addDateRange, selectDictLabel, selectDictLabels, handleTree } 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' +// 头部标签组件 +import VueMeta from 'vue-meta' +// 字典数据组件 +import DictData from '@/components/DictData' + +// 全局方法挂载 +Vue.prototype.getDicts = getDicts +Vue.prototype.getConfigKey = getConfigKey +Vue.prototype.parseTime = parseTime +Vue.prototype.resetForm = resetForm +Vue.prototype.addDateRange = addDateRange +Vue.prototype.selectDictLabel = selectDictLabel +Vue.prototype.selectDictLabels = selectDictLabels +Vue.prototype.download = download +Vue.prototype.handleTree = handleTree + +// 全局组件挂载 +Vue.component('DictTag', DictTag) +Vue.component('Pagination', Pagination) +Vue.component('RightToolbar', RightToolbar) +Vue.component('Editor', Editor) +Vue.component('FileUpload', FileUpload) +Vue.component('ImageUpload', ImageUpload) +Vue.component('ImagePreview', ImagePreview) + +Vue.use(directive) +Vue.use(plugins) +Vue.use(VueMeta) +DictData.install() + +/** + * If you don't want to use mock-server + * you want to use MockJs for mock api + * you can execute: mockXHR() + * + * Currently MockJs will be used in the production environment, + * please remove it before going online! ! ! + */ + +Vue.use(Element, { + // size: Cookies.get('size') || 'medium' // set element-ui default size + size: 'medium' +}) + +Vue.config.productionTip = false + +new Vue({ + el: '#app', + router, + store, + render: h => h(App) +}) diff --git a/ruoyi-ui/src/settings.js b/ruoyi-ui/src/settings.js index ae65fd0a6..8852458cb 100644 --- a/ruoyi-ui/src/settings.js +++ b/ruoyi-ui/src/settings.js @@ -12,7 +12,7 @@ module.exports = { /** * 是否显示顶部导航 */ - topNav: true, + topNav: false, /** * 是否显示 tagsView diff --git a/ruoyi-ui/src/views/dashboard/PanelGroup.vue b/ruoyi-ui/src/views/dashboard/PanelGroup.vue index 8d3a73bca..42e3d3d64 100644 --- a/ruoyi-ui/src/views/dashboard/PanelGroup.vue +++ b/ruoyi-ui/src/views/dashboard/PanelGroup.vue @@ -1,181 +1,181 @@ -<template> - <el-row :gutter="40" class="panel-group"> - <el-col :xs="12" :sm="12" :lg="6" class="card-panel-col"> - <div class="card-panel" @click="handleSetLineChartData('newVisitis')"> - <div class="card-panel-icon-wrapper icon-people"> - <svg-icon icon-class="peoples" class-name="card-panel-icon" /> - </div> - <div class="card-panel-description"> - <div class="card-panel-text"> - 访客 - </div> - <count-to :start-val="0" :end-val="102400" :duration="2600" class="card-panel-num" /> - </div> - </div> - </el-col> - <el-col :xs="12" :sm="12" :lg="6" class="card-panel-col"> - <div class="card-panel" @click="handleSetLineChartData('messages')"> - <div class="card-panel-icon-wrapper icon-message"> - <svg-icon icon-class="message" class-name="card-panel-icon" /> - </div> - <div class="card-panel-description"> - <div class="card-panel-text"> - 消息 - </div> - <count-to :start-val="0" :end-val="81212" :duration="3000" class="card-panel-num" /> - </div> - </div> - </el-col> - <el-col :xs="12" :sm="12" :lg="6" class="card-panel-col"> - <div class="card-panel" @click="handleSetLineChartData('purchases')"> - <div class="card-panel-icon-wrapper icon-money"> - <svg-icon icon-class="money" class-name="card-panel-icon" /> - </div> - <div class="card-panel-description"> - <div class="card-panel-text"> - 金额 - </div> - <count-to :start-val="0" :end-val="9280" :duration="3200" class="card-panel-num" /> - </div> - </div> - </el-col> - <el-col :xs="12" :sm="12" :lg="6" class="card-panel-col"> - <div class="card-panel" @click="handleSetLineChartData('shoppings')"> - <div class="card-panel-icon-wrapper icon-shopping"> - <svg-icon icon-class="shopping" class-name="card-panel-icon" /> - </div> - <div class="card-panel-description"> - <div class="card-panel-text"> - 订单 - </div> - <count-to :start-val="0" :end-val="13600" :duration="3600" class="card-panel-num" /> - </div> - </div> - </el-col> - </el-row> -</template> - -<script> -import CountTo from 'vue-count-to' - -export default { - components: { - CountTo - }, - methods: { - handleSetLineChartData(type) { - this.$emit('handleSetLineChartData', type) - } - } -} -</script> - -<style lang="scss" scoped> -.panel-group { - margin-top: 18px; - - .card-panel-col { - margin-bottom: 32px; - } - - .card-panel { - height: 108px; - cursor: pointer; - font-size: 12px; - position: relative; - overflow: hidden; - color: #666; - background: #fff; - box-shadow: 4px 4px 40px rgba(0, 0, 0, .05); - border-color: rgba(0, 0, 0, .05); - - &:hover { - .card-panel-icon-wrapper { - color: #fff; - } - - .icon-people { - background: #40c9c6; - } - - .icon-message { - background: #36a3f7; - } - - .icon-money { - background: #f4516c; - } - - .icon-shopping { - background: #34bfa3 - } - } - - .icon-people { - color: #40c9c6; - } - - .icon-message { - color: #36a3f7; - } - - .icon-money { - color: #f4516c; - } - - .icon-shopping { - color: #34bfa3 - } - - .card-panel-icon-wrapper { - float: left; - margin: 14px 0 0 14px; - padding: 16px; - transition: all 0.38s ease-out; - border-radius: 6px; - } - - .card-panel-icon { - float: left; - font-size: 48px; - } - - .card-panel-description { - float: right; - font-weight: bold; - margin: 26px; - margin-left: 0px; - - .card-panel-text { - line-height: 18px; - color: rgba(0, 0, 0, 0.45); - font-size: 16px; - margin-bottom: 12px; - } - - .card-panel-num { - font-size: 20px; - } - } - } -} - -@media (max-width:550px) { - .card-panel-description { - display: none; - } - - .card-panel-icon-wrapper { - float: none !important; - width: 100%; - height: 100%; - margin: 0 !important; - - .svg-icon { - display: block; - margin: 14px auto !important; - float: none !important; - } - } -} -</style> +<template> + <el-row :gutter="40" class="panel-group"> + <el-col :xs="12" :sm="12" :lg="6" class="card-panel-col"> + <div class="card-panel" @click="handleSetLineChartData('newVisitis')"> + <div class="card-panel-icon-wrapper icon-people"> + <svg-icon icon-class="peoples" class-name="card-panel-icon" /> + </div> + <div class="card-panel-description"> + <div class="card-panel-text"> + 访客 + </div> + <count-to :start-val="0" :end-val="102400" :duration="2600" class="card-panel-num" /> + </div> + </div> + </el-col> + <el-col :xs="12" :sm="12" :lg="6" class="card-panel-col"> + <div class="card-panel" @click="handleSetLineChartData('messages')"> + <div class="card-panel-icon-wrapper icon-message"> + <svg-icon icon-class="message" class-name="card-panel-icon" /> + </div> + <div class="card-panel-description"> + <div class="card-panel-text"> + 消息 + </div> + <count-to :start-val="0" :end-val="81212" :duration="3000" class="card-panel-num" /> + </div> + </div> + </el-col> + <el-col :xs="12" :sm="12" :lg="6" class="card-panel-col"> + <div class="card-panel" @click="handleSetLineChartData('purchases')"> + <div class="card-panel-icon-wrapper icon-money"> + <svg-icon icon-class="money" class-name="card-panel-icon" /> + </div> + <div class="card-panel-description"> + <div class="card-panel-text"> + 金额 + </div> + <count-to :start-val="0" :end-val="9280" :duration="3200" class="card-panel-num" /> + </div> + </div> + </el-col> + <el-col :xs="12" :sm="12" :lg="6" class="card-panel-col"> + <div class="card-panel" @click="handleSetLineChartData('shoppings')"> + <div class="card-panel-icon-wrapper icon-shopping"> + <svg-icon icon-class="shopping" class-name="card-panel-icon" /> + </div> + <div class="card-panel-description"> + <div class="card-panel-text"> + 订单 + </div> + <count-to :start-val="0" :end-val="13600" :duration="3600" class="card-panel-num" /> + </div> + </div> + </el-col> + </el-row> +</template> + +<script> +import CountTo from 'vue-count-to' + +export default { + components: { + CountTo + }, + methods: { + handleSetLineChartData(type) { + this.$emit('handleSetLineChartData', type) + } + } +} +</script> + +<style lang="scss" scoped> +.panel-group { + margin-top: 18px; + + .card-panel-col { + margin-bottom: 32px; + } + + .card-panel { + height: 108px; + cursor: pointer; + font-size: 12px; + position: relative; + overflow: hidden; + color: #666; + background: #fff; + box-shadow: 4px 4px 40px rgba(0, 0, 0, .05); + border-color: rgba(0, 0, 0, .05); + + &:hover { + .card-panel-icon-wrapper { + color: #fff; + } + + .icon-people { + background: #40c9c6; + } + + .icon-message { + background: #36a3f7; + } + + .icon-money { + background: #f4516c; + } + + .icon-shopping { + background: #34bfa3 + } + } + + .icon-people { + color: #40c9c6; + } + + .icon-message { + color: #36a3f7; + } + + .icon-money { + color: #f4516c; + } + + .icon-shopping { + color: #34bfa3 + } + + .card-panel-icon-wrapper { + float: left; + margin: 14px 0 0 14px; + padding: 16px; + /*transition: all 0.38s ease-out;*/ + border-radius: 6px; + } + + .card-panel-icon { + float: left; + font-size: 48px; + } + + .card-panel-description { + float: right; + font-weight: bold; + margin: 26px; + margin-left: 0px; + + .card-panel-text { + line-height: 18px; + color: rgba(0, 0, 0, 0.45); + font-size: 16px; + margin-bottom: 12px; + } + + .card-panel-num { + font-size: 20px; + } + } + } +} + +@media (max-width:550px) { + .card-panel-description { + display: none; + } + + .card-panel-icon-wrapper { + float: none !important; + width: 100%; + height: 100%; + margin: 0 !important; + + .svg-icon { + display: block; + margin: 14px auto !important; + float: none !important; + } + } +} +</style>