From e38c0656952f70552170fc4ee72420b0c741919e Mon Sep 17 00:00:00 2001
From: zhangwencui <1064582902@qq.com>
Date: 星期四, 18 六月 2026 10:04:09 +0800
Subject: [PATCH] 样式修改
---
src/assets/styles/index.scss | 129 +-
src/views/customerService/feedbackRegistration/components/formDia.vue | 2
src/assets/styles/sidebar.scss | 112 +
src/layout/components/Navbar.vue | 261 ++--
src/assets/styles/element-ui.scss | 202 +-
src/layout/index.vue | 156 ++
src/settings.js | 2
src/layout/components/TagsView/index.vue | 729 +++++++------
src/views/customerService/components/viewDia.vue | 2
src/assets/styles/variables.module.scss | 22
src/store/modules/settings.js | 2
src/assets/styles/ruoyi.scss | 578 +++++-----
src/assets/image.png | 0
src/views/login.vue | 2
src/layout/components/Sidebar/Logo.vue | 240 ++--
src/components/TopNav/index.vue | 433 ++++----
src/layout/components/AppMain.vue | 2
src/layout/components/Sidebar/index.vue | 149 +-
src/layout/components/Settings/index.vue | 2
src/layout/components/Sidebar/SidebarItem.vue | 7
20 files changed, 1,613 insertions(+), 1,419 deletions(-)
diff --git a/src/assets/image.png b/src/assets/image.png
new file mode 100644
index 0000000..b4f609f
--- /dev/null
+++ b/src/assets/image.png
Binary files differ
diff --git a/src/assets/styles/element-ui.scss b/src/assets/styles/element-ui.scss
index 10c7c78..7e772a7 100644
--- a/src/assets/styles/element-ui.scss
+++ b/src/assets/styles/element-ui.scss
@@ -1,52 +1,52 @@
-// cover some element-ui styles
-
-.el-breadcrumb__inner,
-.el-breadcrumb__inner a {
- font-weight: 400 !important;
-}
-
-.el-upload {
- input[type="file"] {
- display: none !important;
- }
-}
-
-.el-upload__input {
- display: none;
-}
-
-.cell {
- .el-tag {
- margin-right: 0px;
- }
-}
-
-.small-padding {
- .cell {
- padding-left: 5px;
- padding-right: 5px;
- }
-}
-
-.fixed-width {
- .el-button--mini {
- padding: 7px 10px;
- width: 60px;
- }
-}
-
-.status-col {
- .cell {
- padding: 0 10px;
- text-align: center;
-
- .el-tag {
- margin-right: 0px;
- }
- }
-}
-
-// to fixed https://github.com/ElemeFE/element/issues/2461
+// cover some element-ui styles
+
+.el-breadcrumb__inner,
+.el-breadcrumb__inner a {
+ font-weight: 400 !important;
+}
+
+.el-upload {
+ input[type="file"] {
+ display: none !important;
+ }
+}
+
+.el-upload__input {
+ display: none;
+}
+
+.cell {
+ .el-tag {
+ margin-right: 0px;
+ }
+}
+
+.small-padding {
+ .cell {
+ padding-left: 5px;
+ padding-right: 5px;
+ }
+}
+
+.fixed-width {
+ .el-button--mini {
+ padding: 7px 10px;
+ width: 60px;
+ }
+}
+
+.status-col {
+ .cell {
+ padding: 0 10px;
+ text-align: center;
+
+ .el-tag {
+ margin-right: 0px;
+ }
+ }
+}
+
+// to fixed https://github.com/ElemeFE/element/issues/2461
.el-dialog {
transform: none;
left: 0;
@@ -99,64 +99,64 @@
.el-message-box__content {
padding: 24px 24px 0;
}
-.el-message-box__container {
- justify-content: center;
-}
-.el-message-box__btns {
- text-align: center;
- padding: 16px;
- display: flex;
- flex-direction: row-reverse;
- justify-content: center;
- align-items: center;
- .el-button--primary {
- margin-right: 12px;
- }
-}
+.el-message-box__container {
+ justify-content: center;
+}
+.el-message-box__btns {
+ text-align: center;
+ padding: 16px;
+ display: flex;
+ flex-direction: row-reverse;
+ justify-content: center;
+ align-items: center;
+ .el-button--primary {
+ margin-right: 12px;
+ }
+}
.el-table__expanded-cell {
padding: 0 !important;
.el-table__header-wrapper {
background-color: var(--surface-soft) !important;
}
}
-
-// refine element ui upload
-.upload-container {
- .el-upload {
- width: 100%;
-
- .el-upload-dragger {
- width: 100%;
- height: 200px;
- }
- }
-}
-
-// dropdown
-.el-dropdown-menu {
- a {
- display: block;
- }
-}
-
-// fix date-picker ui bug in filter-item
-.el-range-editor.el-input__inner {
- display: inline-flex !important;
-}
-
-// to fix el-date-picker css style
-.el-range-separator {
- box-sizing: content-box;
-}
-
-.el-menu--collapse
- > div
- > .el-submenu
- > .el-submenu__title
- .el-submenu__icon-arrow {
- display: none;
-}
-
+
+// refine element ui upload
+.upload-container {
+ .el-upload {
+ width: 100%;
+
+ .el-upload-dragger {
+ width: 100%;
+ height: 200px;
+ }
+ }
+}
+
+// dropdown
+.el-dropdown-menu {
+ a {
+ display: block;
+ }
+}
+
+// fix date-picker ui bug in filter-item
+.el-range-editor.el-input__inner {
+ display: inline-flex !important;
+}
+
+// to fix el-date-picker css style
+.el-range-separator {
+ box-sizing: content-box;
+}
+
+.el-menu--collapse
+ > div
+ > .el-submenu
+ > .el-submenu__title
+ .el-submenu__icon-arrow {
+ display: none;
+}
+
.el-dropdown .el-dropdown-link {
color: var(--el-color-primary) !important;
}
diff --git a/src/assets/styles/index.scss b/src/assets/styles/index.scss
index 90c8527..c44943f 100644
--- a/src/assets/styles/index.scss
+++ b/src/assets/styles/index.scss
@@ -1,24 +1,22 @@
-@import './variables.module.scss';
-@import './mixin.scss';
-@import './transition.scss';
-@import './element-ui.scss';
-@import './sidebar.scss';
-@import './btn.scss';
-@import './ruoyi.scss';
+@import "./variables.module.scss";
+@import "./mixin.scss";
+@import "./transition.scss";
+@import "./element-ui.scss";
+@import "./sidebar.scss";
+@import "./btn.scss";
+@import "./ruoyi.scss";
-body {
- height: 100%;
- margin: 0;
+body {
+ height: 100%;
+ margin: 0;
-moz-osx-font-smoothing: grayscale;
-webkit-font-smoothing: antialiased;
- text-rendering: optimizeLegibility;
- font-family: "Segoe UI", "PingFang SC", "Microsoft YaHei", sans-serif;
- background:
- radial-gradient(circle at 9% -6%, rgba(59, 130, 246, 0.14), transparent 36%),
- radial-gradient(circle at 88% -8%, rgba(56, 189, 248, 0.12), transparent 30%),
- linear-gradient(165deg, #f3f7fc 0%, #eef5ff 54%, #f8fbff 100%);
- color: var(--text-primary);
-}
+ text-rendering: optimizeLegibility;
+ font-family: "Segoe UI", "PingFang SC", "Microsoft YaHei", sans-serif;
+ background: radial-gradient(circle at 9% -6%, rgba(59, 130, 246, 0.14), transparent 36%), radial-gradient(circle at 88% -8%, rgba(56, 189, 248, 0.12), transparent 30%),
+ linear-gradient(165deg, #f3f7fc 0%, #eef5ff 54%, #f8fbff 100%);
+ color: var(--text-primary);
+}
label {
font-weight: 600;
@@ -34,11 +32,11 @@
height: 100%;
}
-html,
-body,
-#app {
- background-color: var(--app-bg);
-}
+html,
+body,
+#app {
+ background-color: var(--app-bg);
+}
*,
*:before,
@@ -110,17 +108,17 @@
}
}
-aside {
- background: rgba(255, 255, 255, 0.84);
- padding: 8px 24px;
- margin-bottom: 20px;
- border-radius: var(--radius-md);
- border: 1px solid var(--surface-border);
+aside {
+ background: rgba(255, 255, 255, 0.84);
+ padding: 8px 24px;
+ margin-bottom: 20px;
+ border-radius: var(--radius-md);
+ border: 1px solid var(--surface-border);
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: var(--text-secondary);
+ color: var(--text-secondary);
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
@@ -136,34 +134,57 @@
//main-container鍏ㄥ眬鏍峰紡
.app-container {
- padding: 20px 24px 24px;
+ --radius-lg: 0px;
+ --radius-md: 0px;
+ --radius-sm: 0px;
+ --radius-xs: 0px;
+ --el-border-radius-base: 0px;
+ --el-border-radius-small: 0px;
+ --el-border-radius-round: 0px;
+ --el-border-radius-circle: 0px;
+
+ padding: 20px;
+ background-color: var(--surface-base, #ffffff);
+ border-radius: 0;
+ box-shadow: 0 1px 4px rgba(0, 21, 41, 0.08);
+ border: 1px solid var(--surface-border, #e2e8f0);
+ min-height: calc(100vh - var(--topbar-height, 64px) - var(--content-gap, 16px) - 20px);
+ margin-bottom: 10px;
+
+ @media (max-width: 768px) {
+ padding: 12px;
+ border-radius: 0;
+ }
}
-.search_form {
+.search_form {
display: flex;
align-items: center;
justify-content: space-between;
- .search_title {
+ margin-bottom: 16px;
+ padding-bottom: 16px;
+ border-bottom: 1px dashed var(--surface-border, #e2e8f0);
+ .search_title {
font-size: 14px;
font-weight: 600;
letter-spacing: 0.04em;
- color: var(--text-secondary);
- }
-}
-.table_list {
- background: var(--panel-mask);
- border: 1px solid var(--surface-border);
- border-radius: var(--radius-md);
- box-shadow: var(--shadow-sm);
- backdrop-filter: blur(12px);
- padding: 18px;
-}
+ color: var(--text-secondary);
+ }
+}
+.table_list {
+ background: transparent;
+ border: none;
+ border-radius: 0;
+ box-shadow: none;
+ backdrop-filter: none;
+ padding: 0;
+}
.components-container {
margin: 30px 50px;
position: relative;
}
.text-center {
- text-align: center
+ text-align: center;
}
.sub-navbar {
@@ -190,15 +211,15 @@
}
}
-.link-type,
-.link-type:focus {
- color: var(--accent-light);
- cursor: pointer;
-
- &:hover {
- color: #2563eb;
- }
-}
+.link-type,
+.link-type:focus {
+ color: var(--accent-light);
+ cursor: pointer;
+
+ &:hover {
+ color: #2563eb;
+ }
+}
.filter-container {
padding-bottom: 10px;
diff --git a/src/assets/styles/ruoyi.scss b/src/assets/styles/ruoyi.scss
index d20ba26..3170a9c 100644
--- a/src/assets/styles/ruoyi.scss
+++ b/src/assets/styles/ruoyi.scss
@@ -1,289 +1,289 @@
-/**
- * 閫氱敤css鏍峰紡甯冨眬澶勭悊
- * Copyright (c) 2019 ruoyi
- */
-
-/** 鍩虹閫氱敤 **/
-.pt5 {
- padding-top: 5px;
-}
-.pr5 {
- padding-right: 5px;
-}
-.pb5 {
- padding-bottom: 5px;
-}
-.mt5 {
- margin-top: 5px;
-}
-.mr5 {
- margin-right: 5px;
-}
-.mb5 {
- margin-bottom: 5px;
-}
-.mb8 {
- margin-bottom: 8px;
-}
-.ml5 {
- margin-left: 5px;
-}
-.mt10 {
- margin-top: 10px;
-}
-.mr10 {
- margin-right: 10px;
-}
-.mb10 {
- margin-bottom: 10px;
-}
-.ml10 {
- margin-left: 10px;
-}
-.mt20 {
- margin-top: 20px;
-}
-.mr20 {
- margin-right: 20px;
-}
-.mb20 {
- margin-bottom: 20px;
-}
-.ml20 {
- margin-left: 20px;
-}
-
-.h1, .h2, .h3, .h4, .h5, .h6, h1, h2, h3, h4, h5, h6 {
- font-family: inherit;
- font-weight: 500;
- line-height: 1.1;
- color: inherit;
-}
-
-.el-form .el-form-item__label {
- font-weight: 700;
-}
-.el-dialog:not(.is-fullscreen) {
- margin-top: 6vh !important;
-}
-
-.el-dialog.scrollbar .el-dialog__body {
- overflow: auto;
- overflow-x: hidden;
- max-height: 70vh;
-}
-
-.el-table {
- .el-table__header-wrapper, .el-table__fixed-header-wrapper {
- th {
- word-break: break-word;
- background-color: #F0F1F5 !important;
- color: #515a6e;
- height: 40px !important;
- font-size: 13px;
- }
- }
- .el-table__body-wrapper {
- .el-button [class*="el-icon-"] + span {
- margin-left: 1px;
- }
- }
-}
-
-/** 琛ㄥ崟甯冨眬 **/
-.form-header {
- font-size:15px;
- color:#6379bb;
- border-bottom:1px solid #ddd;
- margin:8px 10px 25px 10px;
- padding-bottom:5px
-}
-
-/** 琛ㄦ牸甯冨眬 **/
-.pagination-container {
- display: flex;
- justify-content: flex-end;
- margin-top: 20px;
- background-color: transparent !important;
-}
-
-/* 寮圭獥涓殑鍒嗛〉鍣� */
-.el-dialog .pagination-container {
- position: static !important;
- margin: 10px 0 0 0;
- padding: 0 !important;
-
- .el-pagination {
- position: static;
- }
-}
-
-/* 绉诲姩绔�傞厤 */
-@media (max-width: 768px) {
- .pagination-container {
- .el-pagination {
- > .el-pagination__jump {
- display: none !important;
- }
- > .el-pagination__sizes {
- display: none !important;
- }
- }
- }
-}
-
-/* tree border */
-.tree-border {
- margin-top: 5px;
- border: 1px solid var(--el-border-color-light, #e5e6e7);
- background: var(--el-bg-color, #FFFFFF) none;
- border-radius:4px;
- width: 100%;
-}
-
-.el-table .fixed-width .el-button--small {
- padding-left: 0;
- padding-right: 0;
- width: inherit;
-}
-
-/** 琛ㄦ牸鏇村鎿嶄綔涓嬫媺鏍峰紡 */
-.el-table .el-dropdown-link {
- cursor: pointer;
- color: #2C51D9;
- margin-left: 10px;
-}
-
-.el-table .el-dropdown, .el-icon-arrow-down {
- font-size: 12px;
-}
-
-.el-tree-node__content > .el-checkbox {
- margin-right: 8px;
-}
-
-.list-group-striped > .list-group-item {
- border-left: 0;
- border-right: 0;
- border-radius: 0;
- padding-left: 0;
- padding-right: 0;
-}
-
-.list-group {
- padding-left: 0px;
- list-style: none;
-}
-
-.list-group-item {
- border-bottom: 1px solid #e7eaec;
- border-top: 1px solid #e7eaec;
- margin-bottom: -1px;
- padding: 11px 0px;
- font-size: 13px;
-}
-
-.pull-right {
- float: right !important;
-}
-
-.el-card__header {
- padding: 14px 15px 7px !important;
- min-height: 40px;
-}
-
-.el-card__body {
- padding: 15px 20px 20px 20px !important;
-}
-
-.card-box {
- margin-bottom: 10px;
-}
-
-/* button color */
-.el-button--cyan.is-active,
-.el-button--cyan:active {
- background: #20B2AA;
- border-color: #20B2AA;
- color: #FFFFFF;
-}
-
-.el-button--cyan:focus,
-.el-button--cyan:hover {
- background: #48D1CC;
- border-color: #48D1CC;
- color: #FFFFFF;
-}
-
-.el-button--cyan {
- background-color: #20B2AA;
- border-color: #20B2AA;
- color: #FFFFFF;
-}
-
-/* text color */
-.text-navy {
- color: #1ab394;
-}
-
-.text-primary {
- color: inherit;
-}
-
-.text-success {
- color: #1c84c6;
-}
-
-.text-info {
- color: #23c6c8;
-}
-
-.text-warning {
- color: #f8ac59;
-}
-
-.text-danger {
- color: #ed5565;
-}
-
-.text-muted {
- color: #888888;
-}
-
-/* image */
-.img-circle {
- border-radius: 50%;
-}
-
-.img-lg {
- width: 120px;
- height: 120px;
-}
-
-.avatar-upload-preview {
- position: absolute;
- top: 50%;
- transform: translate(50%, -50%);
- width: 200px;
- height: 200px;
- border-radius: 50%;
- box-shadow: 0 0 4px #ccc;
- overflow: hidden;
-}
-
-/* 鎷栨嫿鍒楁牱寮� */
-.sortable-ghost{
- opacity: .8;
- color: #fff!important;
- background: #42b983!important;
-}
-
-/* 琛ㄦ牸鍙充晶宸ュ叿鏍忔牱寮� */
-.top-right-btn {
- margin-left: auto;
-}
-
-/* 鍒嗗壊闈㈡澘鏍峰紡 */
-.splitpanes.default-theme .splitpanes__pane {
- background-color: var(--splitpanes-default-bg) !important;
-}
+/**
+ * 閫氱敤css鏍峰紡甯冨眬澶勭悊
+ * Copyright (c) 2019 ruoyi
+ */
+
+/** 鍩虹閫氱敤 **/
+.pt5 {
+ padding-top: 5px;
+}
+.pr5 {
+ padding-right: 5px;
+}
+.pb5 {
+ padding-bottom: 5px;
+}
+.mt5 {
+ margin-top: 5px;
+}
+.mr5 {
+ margin-right: 5px;
+}
+.mb5 {
+ margin-bottom: 5px;
+}
+.mb8 {
+ margin-bottom: 8px;
+}
+.ml5 {
+ margin-left: 5px;
+}
+.mt10 {
+ margin-top: 10px;
+}
+.mr10 {
+ margin-right: 10px;
+}
+.mb10 {
+ margin-bottom: 10px;
+}
+.ml10 {
+ margin-left: 10px;
+}
+.mt20 {
+ margin-top: 20px;
+}
+.mr20 {
+ margin-right: 20px;
+}
+.mb20 {
+ margin-bottom: 20px;
+}
+.ml20 {
+ margin-left: 20px;
+}
+
+.h1, .h2, .h3, .h4, .h5, .h6, h1, h2, h3, h4, h5, h6 {
+ font-family: inherit;
+ font-weight: 500;
+ line-height: 1.1;
+ color: inherit;
+}
+
+.el-form .el-form-item__label {
+ font-weight: 700;
+}
+.el-dialog:not(.is-fullscreen) {
+ margin-top: 6vh !important;
+}
+
+.el-dialog.scrollbar .el-dialog__body {
+ overflow: auto;
+ overflow-x: hidden;
+ max-height: 70vh;
+}
+
+.el-table {
+ .el-table__header-wrapper, .el-table__fixed-header-wrapper {
+ th {
+ word-break: break-word;
+ background-color: #F0F1F5 !important;
+ color: #515a6e;
+ height: 40px !important;
+ font-size: 13px;
+ }
+ }
+ .el-table__body-wrapper {
+ .el-button [class*="el-icon-"] + span {
+ margin-left: 1px;
+ }
+ }
+}
+
+/** 琛ㄥ崟甯冨眬 **/
+.form-header {
+ font-size:15px;
+ color:#6379bb;
+ border-bottom:1px solid #ddd;
+ margin:8px 10px 25px 10px;
+ padding-bottom:5px
+}
+
+/** 琛ㄦ牸甯冨眬 **/
+.pagination-container {
+ display: flex;
+ justify-content: flex-end;
+ margin-top: 20px;
+ background-color: transparent !important;
+}
+
+/* 寮圭獥涓殑鍒嗛〉鍣� */
+.el-dialog .pagination-container {
+ position: static !important;
+ margin: 10px 0 0 0;
+ padding: 0 !important;
+
+ .el-pagination {
+ position: static;
+ }
+}
+
+/* 绉诲姩绔�傞厤 */
+@media (max-width: 768px) {
+ .pagination-container {
+ .el-pagination {
+ > .el-pagination__jump {
+ display: none !important;
+ }
+ > .el-pagination__sizes {
+ display: none !important;
+ }
+ }
+ }
+}
+
+/* tree border */
+.tree-border {
+ margin-top: 5px;
+ border: 1px solid var(--el-border-color-light, #e5e6e7);
+ background: var(--el-bg-color, #FFFFFF) none;
+ border-radius:4px;
+ width: 100%;
+}
+
+.el-table .fixed-width .el-button--small {
+ padding-left: 0;
+ padding-right: 0;
+ width: inherit;
+}
+
+/** 琛ㄦ牸鏇村鎿嶄綔涓嬫媺鏍峰紡 */
+.el-table .el-dropdown-link {
+ cursor: pointer;
+ color: #2C51D9;
+ margin-left: 10px;
+}
+
+.el-table .el-dropdown, .el-icon-arrow-down {
+ font-size: 12px;
+}
+
+.el-tree-node__content > .el-checkbox {
+ margin-right: 8px;
+}
+
+.list-group-striped > .list-group-item {
+ border-left: 0;
+ border-right: 0;
+ border-radius: 0;
+ padding-left: 0;
+ padding-right: 0;
+}
+
+.list-group {
+ padding-left: 0px;
+ list-style: none;
+}
+
+.list-group-item {
+ border-bottom: 1px solid #e7eaec;
+ border-top: 1px solid #e7eaec;
+ margin-bottom: -1px;
+ padding: 11px 0px;
+ font-size: 13px;
+}
+
+.pull-right {
+ float: right !important;
+}
+
+.el-card__header {
+ padding: 14px 15px 7px !important;
+ min-height: 40px;
+}
+
+.el-card__body {
+ padding: 15px 20px 20px 20px !important;
+}
+
+.card-box {
+ margin-bottom: 10px;
+}
+
+/* button color */
+.el-button--cyan.is-active,
+.el-button--cyan:active {
+ background: #20B2AA;
+ border-color: #20B2AA;
+ color: #FFFFFF;
+}
+
+.el-button--cyan:focus,
+.el-button--cyan:hover {
+ background: #48D1CC;
+ border-color: #48D1CC;
+ color: #FFFFFF;
+}
+
+.el-button--cyan {
+ background-color: #20B2AA;
+ border-color: #20B2AA;
+ color: #FFFFFF;
+}
+
+/* text color */
+.text-navy {
+ color: #1ab394;
+}
+
+.text-primary {
+ color: inherit;
+}
+
+.text-success {
+ color: #1c84c6;
+}
+
+.text-info {
+ color: #23c6c8;
+}
+
+.text-warning {
+ color: #f8ac59;
+}
+
+.text-danger {
+ color: #ed5565;
+}
+
+.text-muted {
+ color: #888888;
+}
+
+/* image */
+.img-circle {
+ border-radius: 50%;
+}
+
+.img-lg {
+ width: 120px;
+ height: 120px;
+}
+
+.avatar-upload-preview {
+ position: absolute;
+ top: 50%;
+ transform: translate(50%, -50%);
+ width: 200px;
+ height: 200px;
+ border-radius: 50%;
+ box-shadow: 0 0 4px #ccc;
+ overflow: hidden;
+}
+
+/* 鎷栨嫿鍒楁牱寮� */
+.sortable-ghost{
+ opacity: .8;
+ color: #fff!important;
+ background: #42b983!important;
+}
+
+/* 琛ㄦ牸鍙充晶宸ュ叿鏍忔牱寮� */
+.top-right-btn {
+ margin-left: auto;
+}
+
+/* 鍒嗗壊闈㈡澘鏍峰紡 */
+.splitpanes.default-theme .splitpanes__pane {
+ background-color: var(--splitpanes-default-bg) !important;
+}
diff --git a/src/assets/styles/sidebar.scss b/src/assets/styles/sidebar.scss
index 677396c..d75e1f2 100644
--- a/src/assets/styles/sidebar.scss
+++ b/src/assets/styles/sidebar.scss
@@ -17,14 +17,14 @@
height: 100vh;
position: fixed;
top: 0;
+ padding-top: var(--topbar-height);
left: 0;
z-index: 1001;
overflow: hidden;
- padding: 0;
font-size: 0;
- background: var(--sidebar-bg);
+ background: var(--surface-base);
border-right: 1px solid var(--surface-border);
- box-shadow: var(--shadow-md);
+ box-shadow: none;
> * {
position: relative;
@@ -68,7 +68,7 @@
border: none !important;
height: 100%;
width: 100% !important;
- padding: 12px 0;
+ padding: 8px 0;
background: transparent !important;
}
@@ -88,24 +88,26 @@
.submenu-title-noDropdown,
.el-sub-menu__title,
.el-menu-item {
- width: calc(100% - 16px) !important;
- margin: 4px 8px !important;
+ width: 100% !important;
+ margin: 0 !important;
height: 44px;
line-height: 44px;
- border-radius: var(--radius-md);
- padding-left: 16px !important;
- padding-right: 36px !important; // 棰勭暀绠ご浣嶇疆
+ border-radius: 0;
+ padding-left: 18px !important;
+ padding-right: 18px !important;
box-sizing: border-box;
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
- color: var(--sidebar-text);
+ color: var(--el-color-primary);
background: transparent;
border: none !important;
position: relative;
+ font-size: 14px;
+ border-bottom: 1px dashed rgba(var(--el-color-primary-rgb), 0.35) !important;
.svg-icon {
margin-right: 12px;
- width: 18px;
- height: 18px;
+ width: 16px;
+ height: 16px;
vertical-align: middle;
flex-shrink: 0;
transition: transform 0.3s ease;
@@ -115,11 +117,12 @@
.el-sub-menu {
&.is-opened {
> .el-sub-menu__title {
- color: var(--menu-active-text) !important;
+ color: var(--el-color-primary) !important;
+ font-weight: 600;
.el-sub-menu__icon-arrow {
transform: rotate(180deg) !important;
- color: var(--menu-active-text) !important;
+ color: var(--el-color-primary) !important;
}
}
}
@@ -134,7 +137,7 @@
font-size: 12px !important;
display: inline-block !important;
transition: transform 0.3s ease !important;
- color: var(--sidebar-text) !important;
+ color: var(--el-color-primary) !important;
z-index: 2;
}
}
@@ -142,37 +145,52 @@
.submenu-title-noDropdown:hover,
.el-sub-menu__title:hover,
.el-menu-item:hover {
- background: var(--menu-hover) !important;
- color: var(--menu-active-text) !important;
+ background-color: rgba(var(--el-color-primary-rgb), 0.06) !important;
+ color: var(--el-color-primary) !important;
.svg-icon {
- transform: scale(1.1);
+ color: var(--el-color-primary) !important;
+ }
+
+ .el-sub-menu__icon-arrow {
+ color: var(--el-color-primary) !important;
}
}
.el-menu-item.is-active,
.el-sub-menu.is-active > .el-sub-menu__title {
- color: var(--menu-active-text) !important;
- background: var(--menu-active-bg) !important;
- box-shadow: var(--menu-active-glow);
+ color: var(--el-color-primary) !important;
+ background: rgba(var(--el-color-primary-rgb), 0.06) !important;
+ box-shadow: none;
font-weight: 600;
.svg-icon {
- color: var(--menu-active-text) !important;
+ color: var(--el-color-primary) !important;
}
}
& .nest-menu .el-sub-menu > .el-sub-menu__title,
& .el-sub-menu .el-menu-item {
- height: 40px;
- line-height: 40px;
- margin: 2px 8px !important;
- padding-left: 44px !important; // 澧炲姞瀛愯彍鍗曠缉杩�
- border-radius: var(--radius-sm);
- font-size: 13px;
+ height: 44px;
+ line-height: 44px;
+ margin: 0 !important;
+ padding-left: 38px !important;
+ border-radius: 0;
+ font-size: 14px;
+ background-color: transparent !important;
+ border-bottom: 1px dashed rgba(var(--el-color-primary-rgb), 0.3) !important;
&.is-active {
- background: var(--menu-active-bg) !important;
+ background-color: rgba(var(--el-color-primary-rgb), 0.06) !important;
+ }
+
+ &:hover {
+ background-color: rgba(var(--el-color-primary-rgb), 0.06) !important;
+ color: var(--el-color-primary) !important;
+
+ .svg-icon {
+ color: var(--el-color-primary) !important;
+ }
}
}
}
@@ -189,6 +207,7 @@
justify-content: center;
margin: 4px 8px !important;
width: calc(var(--sidebar-collapsed-width) - 16px) !important;
+ border-bottom: none !important;
.svg-icon {
margin-right: 0;
@@ -218,6 +237,8 @@
.sidebar-container {
transition: transform 0.25s;
width: var(--sidebar-width) !important;
+ padding-top: 0;
+ z-index: 1004;
}
&.hideSidebar {
@@ -247,40 +268,41 @@
.nest-menu .el-sub-menu > .el-sub-menu__title,
.el-menu-item {
min-width: 0 !important;
- margin: 4px 10px;
- width: calc(100% - 20px);
+ margin: 0;
+ width: 100%;
height: 44px;
line-height: 44px;
- padding-left: 16px !important;
- padding-right: 16px !important;
+ padding-left: 18px !important;
+ padding-right: 18px !important;
box-sizing: border-box;
- border-radius: var(--radius-md);
- color: var(--sidebar-text);
+ border-radius: 0;
+ color: var(--el-color-primary);
background: transparent;
transition: all 0.2s ease;
+ border-bottom: 1px dashed rgba(var(--el-color-primary-rgb), 0.35);
&:hover {
- background: var(--menu-hover) !important;
- color: #fff !important;
+ background: rgba(var(--el-color-primary-rgb), 0.06) !important;
+ color: var(--el-color-primary) !important;
}
&.is-active {
- background: var(--menu-active-bg) !important;
- color: var(--menu-active-text) !important;
+ background: rgba(var(--el-color-primary-rgb), 0.06) !important;
+ color: var(--el-color-primary) !important;
font-weight: 600;
- box-shadow: var(--menu-active-glow);
+ box-shadow: none;
}
}
> .el-menu--popup {
max-height: 100vh;
overflow: hidden;
- padding: 8px;
- border-radius: var(--radius-lg);
+ padding: 8px 0;
+ border-radius: 0;
border: 1px solid var(--surface-border);
- box-shadow: var(--shadow-menu);
- background: var(--sidebar-bg);
- backdrop-filter: blur(16px);
+ box-shadow: var(--shadow-sm);
+ background: var(--surface-base);
+ backdrop-filter: none;
> .el-menu {
max-height: calc(100vh - 16px);
diff --git a/src/assets/styles/variables.module.scss b/src/assets/styles/variables.module.scss
index 70b3af3..625d3b0 100644
--- a/src/assets/styles/variables.module.scss
+++ b/src/assets/styles/variables.module.scss
@@ -73,20 +73,20 @@
--content-radius: 10px;
--layout-header-z: 20;
- --el-color-primary: #374d77;
- --el-color-primary-rgb: 37, 89, 163;
+ --el-color-primary: #008c8c;
+ --el-color-primary-rgb: 0, 140, 140;
--el-color-success: #14b8a6;
--el-color-warning: #f59e0b;
--el-color-danger: #ef4444;
- --sidebar-bg: #1e293b;
- --sidebar-text: #94a3b8;
- --sidebar-muted: #64748b;
- --menu-hover: rgba(255, 255, 255, 0.05);
- --menu-active-bg: #3b82f6;
- --menu-active-text: #ffffff;
- --menu-surface: #1e293b;
- --menu-active-glow: 0 4px 12px rgba(59, 130, 246, 0.3);
+ --sidebar-bg: #304156;
+ --sidebar-text: #bfcbd9;
+ --sidebar-muted: #8b9bb4;
+ --menu-hover: #263445;
+ --menu-active-bg: transparent;
+ --menu-active-text: #409eff;
+ --menu-surface: #304156;
+ --menu-active-glow: none;
--app-bg: #f8fafc;
--app-bg-accent: #f1f5f9;
@@ -122,7 +122,7 @@
--tags-item-hover: #f1f5f9;
--tags-close-hover: rgba(239, 68, 68, 0.1);
- --accent-primary: #374d77;
+ --accent-primary: #008c8c;
--accent-light: #3b82f6;
--accent-lighter: #60a5fa;
diff --git a/src/components/TopNav/index.vue b/src/components/TopNav/index.vue
index 56e2803..27de555 100644
--- a/src/components/TopNav/index.vue
+++ b/src/components/TopNav/index.vue
@@ -1,217 +1,216 @@
-<template>
- <el-menu
- :default-active="activeMenu"
- mode="horizontal"
- @select="handleSelect"
- :ellipsis="false"
- >
- <template v-for="(item, index) in topMenus">
- <el-menu-item :style="{'--theme': theme}" :index="item.path" :key="index" v-if="index < visibleNumber">
- <svg-icon
- v-if="item.meta && item.meta.icon && item.meta.icon !== '#'"
- :icon-class="item.meta.icon"/>
- {{ item.meta.title }}
- </el-menu-item>
- </template>
-
- <!-- 椤堕儴鑿滃崟瓒呭嚭鏁伴噺鎶樺彔 -->
- <el-sub-menu :style="{'--theme': theme}" index="more" v-if="topMenus.length > visibleNumber">
- <template #title>鏇村鑿滃崟</template>
- <template v-for="(item, index) in topMenus">
- <el-menu-item
- :index="item.path"
- :key="index"
- v-if="index >= visibleNumber">
- <svg-icon
- v-if="item.meta && item.meta.icon && item.meta.icon !== '#'"
- :icon-class="item.meta.icon"/>
- {{ item.meta.title }}
- </el-menu-item>
- </template>
- </el-sub-menu>
- </el-menu>
-</template>
-
-<script setup>
-import { constantRoutes } from "@/router"
-import { isHttp } from '@/utils/validate'
-import useAppStore from '@/store/modules/app'
-import useSettingsStore from '@/store/modules/settings'
-import usePermissionStore from '@/store/modules/permission'
-
-// 椤堕儴鏍忓垵濮嬫暟
-const visibleNumber = ref(null)
-// 褰撳墠婵�娲昏彍鍗曠殑 index
-const currentIndex = ref(null)
-// 闅愯棌渚ц竟鏍忚矾鐢�
-const hideList = ['/index', '/user/profile']
-
-const appStore = useAppStore()
-const settingsStore = useSettingsStore()
-const permissionStore = usePermissionStore()
-const route = useRoute()
-const router = useRouter()
-
-// 涓婚棰滆壊
-const theme = computed(() => settingsStore.theme)
-// 鎵�鏈夌殑璺敱淇℃伅
-const routers = computed(() => permissionStore.topbarRouters)
-
-// 椤堕儴鏄剧ず鑿滃崟
-const topMenus = computed(() => {
- let topMenus = []
- routers.value.map((menu) => {
- if (menu.hidden !== true) {
- // 鍏煎椤堕儴鏍忎竴绾ц彍鍗曞唴閮ㄨ烦杞�
- if (menu.path === '/' && menu.children) {
- topMenus.push(menu.children[0])
- } else {
- topMenus.push(menu)
- }
- }
- })
- return topMenus
-})
-
-// 璁剧疆瀛愯矾鐢�
-const childrenMenus = computed(() => {
- let childrenMenus = []
- routers.value.map((router) => {
- for (let item in router.children) {
- if (router.children[item].parentPath === undefined) {
- if(router.path === "/") {
- router.children[item].path = "/" + router.children[item].path
- } else {
- if(!isHttp(router.children[item].path)) {
- router.children[item].path = router.path + "/" + router.children[item].path
- }
- }
- router.children[item].parentPath = router.path
- }
- childrenMenus.push(router.children[item])
- }
- })
- return constantRoutes.concat(childrenMenus)
-})
-
-// 榛樿婵�娲荤殑鑿滃崟
-const activeMenu = computed(() => {
- const path = route.path
- let activePath = path
- if (path !== undefined && path.lastIndexOf("/") > 0 && hideList.indexOf(path) === -1) {
- const tmpPath = path.substring(1, path.length)
- if (!route.meta.link) {
- activePath = "/" + tmpPath.substring(0, tmpPath.indexOf("/"))
- appStore.toggleSideBarHide(false)
- }
- } else if(!route.children) {
- activePath = path
- appStore.toggleSideBarHide(true)
- }
- activeRoutes(activePath)
- return activePath
-})
-
-function setVisibleNumber() {
- const width = document.body.getBoundingClientRect().width / 3
- visibleNumber.value = parseInt(width / 85)
-}
-
-function handleSelect(key, keyPath) {
- currentIndex.value = key
- const route = routers.value.find(item => item.path === key)
- if (isHttp(key)) {
- // http(s):// 璺緞鏂扮獥鍙f墦寮�
- window.open(key, "_blank")
- } else if (!route || !route.children) {
- // 娌℃湁瀛愯矾鐢辫矾寰勫唴閮ㄦ墦寮�
- const routeMenu = childrenMenus.value.find(item => item.path === key)
- if (routeMenu && routeMenu.query) {
- let query = JSON.parse(routeMenu.query)
- router.push({ path: key, query: query })
- } else {
- router.push({ path: key })
- }
- appStore.toggleSideBarHide(true)
- } else {
- // 鏄剧ず宸︿晶鑱斿姩鑿滃崟
- activeRoutes(key)
- appStore.toggleSideBarHide(false)
- }
-}
-
-function activeRoutes(key) {
- let routes = []
- if (childrenMenus.value && childrenMenus.value.length > 0) {
- childrenMenus.value.map((item) => {
- if (key == item.parentPath || (key == "index" && "" == item.path)) {
- routes.push(item)
- }
- })
- }
- if(routes.length > 0) {
- permissionStore.setSidebarRouters(routes)
- } else {
- appStore.toggleSideBarHide(true)
- }
- return routes
-}
-
-onMounted(() => {
- window.addEventListener('resize', setVisibleNumber)
-})
-
-onBeforeUnmount(() => {
- window.removeEventListener('resize', setVisibleNumber)
-})
-
-onMounted(() => {
- setVisibleNumber()
-})
-</script>
-
-<style lang="scss">
-.topmenu-container.el-menu--horizontal > .el-menu-item {
- float: left;
- height: 50px !important;
- line-height: 50px !important;
- color: #999093 !important;
- padding: 0 5px !important;
- margin: 0 10px !important;
-}
-
-.topmenu-container.el-menu--horizontal > .el-menu-item.is-active, .el-menu--horizontal > .el-sub-menu.is-active .el-submenu__title {
- border-bottom: 2px solid #{'var(--theme)'} !important;
- color: #303133;
-}
-
-/* sub-menu item */
-.topmenu-container.el-menu--horizontal > .el-sub-menu .el-sub-menu__title {
- float: left;
- height: 50px !important;
- line-height: 50px !important;
- color: #999093 !important;
- padding: 0 5px !important;
- margin: 0 10px !important;
-}
-
-/* 鑳屾櫙鑹查殣钘� */
-.topmenu-container.el-menu--horizontal>.el-menu-item:not(.is-disabled):focus, .topmenu-container.el-menu--horizontal>.el-menu-item:not(.is-disabled):hover, .topmenu-container.el-menu--horizontal>.el-submenu .el-submenu__title:hover {
- background-color: #ffffff;
-}
-
-/* 鍥炬爣鍙抽棿璺� */
-.topmenu-container .svg-icon {
- margin-right: 4px;
-}
-
-/* topmenu more arrow */
-.topmenu-container .el-sub-menu .el-sub-menu__icon-arrow {
- position: static;
- vertical-align: middle;
- margin-left: 8px;
- margin-top: 0px;
-}
-
-
-</style>
+<template>
+ <div class="top-nav">
+ <button v-show="showArrows"
+ class="nav-arrow nav-arrow--left"
+ type="button"
+ :disabled="!canScrollLeft"
+ @click="scrollLeft">
+ <el-icon :size="18">
+ <ArrowLeft />
+ </el-icon>
+ </button>
+ <div ref="scrollWrapRef"
+ class="top-nav__scroll"
+ @scroll.passive="updateScrollState">
+ <el-menu class="top-nav-menu"
+ :default-active="activeMenu"
+ mode="horizontal"
+ :unique-opened="true"
+ :ellipsis="false"
+ :active-text-color="theme">
+ <sidebar-item v-for="(routeItem, index) in topbarRouters"
+ :key="routeItem.path + index"
+ :item="routeItem"
+ :base-path="routeItem.path" />
+ </el-menu>
+ </div>
+ <button v-show="showArrows"
+ class="nav-arrow nav-arrow--right"
+ type="button"
+ :disabled="!canScrollRight"
+ @click="scrollRight">
+ <el-icon :size="18">
+ <ArrowRight />
+ </el-icon>
+ </button>
+ </div>
+</template>
+
+<script setup>
+ import { ArrowLeft, ArrowRight } from "@element-plus/icons-vue";
+ import useSettingsStore from "@/store/modules/settings";
+ import usePermissionStore from "@/store/modules/permission";
+ import SidebarItem from "@/layout/components/Sidebar/SidebarItem.vue";
+
+ const settingsStore = useSettingsStore();
+ const permissionStore = usePermissionStore();
+ const route = useRoute();
+
+ // 涓婚棰滆壊
+ const theme = computed(() => settingsStore.theme);
+ const topbarRouters = computed(() => permissionStore.topbarRouters);
+ const scrollWrapRef = ref(null);
+ const canScrollLeft = ref(false);
+ const canScrollRight = ref(false);
+ const showArrows = computed(() => canScrollLeft.value || canScrollRight.value);
+
+ // 榛樿婵�娲荤殑鑿滃崟
+ const activeMenu = computed(() => {
+ const { meta, path } = route;
+ if (meta?.activeMenu) return meta.activeMenu;
+ return path;
+ });
+
+ function updateScrollState() {
+ const el = scrollWrapRef.value;
+ if (!el) return;
+ const maxScrollLeft = el.scrollWidth - el.clientWidth;
+ canScrollLeft.value = el.scrollLeft > 0;
+ canScrollRight.value = el.scrollLeft < maxScrollLeft - 1;
+ }
+
+ function scrollByStep(direction) {
+ const el = scrollWrapRef.value;
+ if (!el) return;
+ const step = Math.max(240, Math.floor(el.clientWidth * 0.6));
+ el.scrollBy({ left: direction * step, behavior: "smooth" });
+ requestAnimationFrame(() => updateScrollState());
+ }
+
+ function scrollLeft() {
+ scrollByStep(-1);
+ }
+
+ function scrollRight() {
+ scrollByStep(1);
+ }
+
+ let resizeRaf = 0;
+ function handleResize() {
+ if (resizeRaf) cancelAnimationFrame(resizeRaf);
+ resizeRaf = requestAnimationFrame(() => {
+ updateScrollState();
+ });
+ }
+
+ onMounted(() => {
+ updateScrollState();
+ window.addEventListener("resize", handleResize, { passive: true });
+ });
+
+ onBeforeUnmount(() => {
+ window.removeEventListener("resize", handleResize);
+ if (resizeRaf) cancelAnimationFrame(resizeRaf);
+ });
+</script>
+
+<style lang="scss" scoped>
+ .top-nav {
+ position: relative;
+ height: var(--topbar-height);
+ display: flex;
+ align-items: center;
+ width: 100%;
+ min-width: 0;
+ }
+
+ .top-nav__scroll {
+ flex: 1;
+ min-width: 0;
+ height: 100%;
+ overflow-x: auto;
+ overflow-y: hidden;
+ scrollbar-width: none;
+ -ms-overflow-style: none;
+
+ &::-webkit-scrollbar {
+ display: none;
+ }
+ }
+
+ .nav-arrow {
+ width: 34px;
+ height: 34px;
+ flex: 0 0 34px;
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ border-radius: 999px;
+ border: 1px solid var(--surface-border);
+ background: var(--surface-base);
+ color: var(--text-secondary);
+ cursor: pointer;
+ transition: 0.2s ease;
+ margin: 0 8px;
+
+ &:hover:not(:disabled) {
+ color: var(--el-color-primary);
+ border-color: rgba(var(--el-color-primary-rgb), 0.35);
+ background: rgba(var(--el-color-primary-rgb), 0.06);
+ }
+
+ &:disabled {
+ opacity: 0.45;
+ cursor: not-allowed;
+ }
+ }
+
+ .top-nav-menu {
+ width: max-content;
+ min-width: 100%;
+ height: 100%;
+ border-bottom: none;
+ background: transparent;
+ }
+
+ :deep(.top-nav-menu.el-menu--horizontal) {
+ display: flex;
+ align-items: stretch;
+ flex-wrap: nowrap;
+ height: 100%;
+ width: max-content;
+ min-width: 100%;
+ }
+
+ :deep(.top-nav-menu.el-menu--horizontal > .el-menu-item),
+ :deep(.top-nav-menu.el-menu--horizontal > .el-sub-menu > .el-sub-menu__title) {
+ height: var(--topbar-height);
+ line-height: var(--topbar-height);
+ padding: 0 12px;
+ color: var(--text-secondary);
+ border-bottom: 2px solid transparent;
+ transition: background 0.2s ease, color 0.2s ease, border-color 0.2s ease;
+ }
+
+ :deep(
+ .top-nav-menu.el-menu--horizontal > .el-menu-item:not(.is-disabled):hover
+ ),
+ :deep(
+ .top-nav-menu.el-menu--horizontal > .el-sub-menu > .el-sub-menu__title:hover
+ ) {
+ background: rgba(var(--el-color-primary-rgb), 0.06);
+ color: var(--el-color-primary);
+ }
+
+ :deep(.top-nav-menu.el-menu--horizontal > .el-menu-item.is-active),
+ :deep(
+ .top-nav-menu.el-menu--horizontal
+ > .el-sub-menu.is-active
+ > .el-sub-menu__title
+ ) {
+ background: rgba(var(--el-color-primary-rgb), 0.06);
+ color: var(--el-color-primary);
+ font-weight: 600;
+ border-bottom-color: var(--el-color-primary);
+ }
+
+ :deep(.top-nav-menu.el-menu--horizontal > .el-menu-item .svg-icon),
+ :deep(
+ .top-nav-menu.el-menu--horizontal
+ > .el-sub-menu
+ > .el-sub-menu__title
+ .svg-icon
+ ) {
+ margin-right: 8px;
+ }
+</style>
diff --git a/src/layout/components/AppMain.vue b/src/layout/components/AppMain.vue
index 82aa810..a9e4b4b 100644
--- a/src/layout/components/AppMain.vue
+++ b/src/layout/components/AppMain.vue
@@ -49,7 +49,7 @@
width: 100%;
height: 100%;
padding: var(--content-gap);
- padding-top: 0;
+ /* padding-top: 0; */
}
.fixed-header + .app-main {
diff --git a/src/layout/components/Navbar.vue b/src/layout/components/Navbar.vue
index 15de4e1..771c3f3 100644
--- a/src/layout/components/Navbar.vue
+++ b/src/layout/components/Navbar.vue
@@ -1,29 +1,33 @@
<template>
<div class="navbar">
- <div class="left-menu">
- <hamburger id="hamburger-container"
- :is-active="appStore.sidebar.opened"
- class="hamburger-container"
- @toggleClick="toggleSideBar" />
- <breadcrumb v-if="!settingsStore.topNav"
- id="breadcrumb-container"
- class="breadcrumb-container" />
+ <div class="navbar-left">
+ <div class="navbar-logo"
+ v-if="appStore.device !== 'mobile'"
+ :style="{ width: logoWidth }">
+ <logo v-if="settingsStore.sidebarLogo"
+ :collapse="logoCollapse" />
+ </div>
+ <div v-if="settingsStore.topNav && appStore.device !== 'mobile'"
+ class="top-nav-wrapper">
+ <top-nav />
+ </div>
+ <div v-else
+ class="left-menu-spacer"></div>
</div>
<div class="right-menu">
- <div class="search-wrapper">
- <el-icon class="search-icon"
- @click="openHeaderSearch">
- <Search />
- </el-icon>
- <el-input v-model="topSearchKeyword"
- placeholder="蹇�熸悳绱�..."
- clearable
- @keyup.enter="openHeaderSearch" />
+ <div class="action-icons">
+ <!-- 鎼滅储鍥炬爣 -->
+ <div class="right-menu-item hover-effect action-icon-btn"
+ @click="openHeaderSearch">
+ <el-icon :size="18">
+ <Search />
+ </el-icon>
+ </div>
<header-search ref="headerSearchRef"
:keyword="topSearchKeyword"
- class="search-popup-trigger" />
- </div>
- <div class="action-icons">
+ class="search-popup-trigger"
+ style="display: none;" />
+ <!-- 閫氱煡鍥炬爣 -->
<el-popover v-model:visible="notificationVisible"
:width="500"
placement="bottom-end"
@@ -31,7 +35,7 @@
:popper-options="{ modifiers: [{ name: 'offset', options: { offset: [0, 10] } }] }"
popper-class="notification-popover">
<template #reference>
- <div class="notification-container right-menu-item hover-effect">
+ <div class="notification-container right-menu-item hover-effect action-icon-btn">
<el-badge :value="unreadCount"
:hidden="unreadCount === 0"
class="notification-badge">
@@ -44,7 +48,8 @@
<NotificationCenter @unreadCountChange="handleUnreadCountChange"
ref="notificationCenterRef" />
</el-popover>
- <div class="right-menu-item hover-effect screenfull-container">
+ <!-- 鍏ㄥ睆鍥炬爣 -->
+ <div class="right-menu-item hover-effect screenfull-container action-icon-btn">
<screenfull />
</div>
</div>
@@ -104,13 +109,21 @@
</template>
<script setup>
+ import { ref, computed, watch, onMounted, onUnmounted, nextTick } from "vue";
import { ElMessageBox } from "element-plus";
- import { Bell, Search } from "@element-plus/icons-vue";
- import Breadcrumb from "@/components/Breadcrumb";
- import Hamburger from "@/components/Hamburger";
+ import {
+ Bell,
+ Search,
+ User,
+ Setting,
+ SwitchButton,
+ CaretBottom,
+ } from "@element-plus/icons-vue";
import Screenfull from "@/components/Screenfull";
import HeaderSearch from "@/components/HeaderSearch";
import NotificationCenter from "./NotificationCenter/index.vue";
+ import Logo from "./Sidebar/Logo.vue";
+ import TopNav from "@/components/TopNav/index.vue";
import useAppStore from "@/store/modules/app";
import useUserStore from "@/store/modules/user";
import useSettingsStore from "@/store/modules/settings";
@@ -119,15 +132,27 @@
const userStore = useUserStore();
const settingsStore = useSettingsStore();
+ const isTopNavLayout = computed(
+ () => settingsStore.topNav && appStore.device !== "mobile"
+ );
+ const logoWidth = computed(() => {
+ if (isTopNavLayout.value) {
+ return "var(--sidebar-width)";
+ }
+ return appStore.sidebar.opened
+ ? "var(--sidebar-width)"
+ : "var(--sidebar-collapsed-width)";
+ });
+ const logoCollapse = computed(() => {
+ if (isTopNavLayout.value) return false;
+ return !appStore.sidebar.opened;
+ });
+
const topSearchKeyword = ref("");
const headerSearchRef = ref(null);
const notificationVisible = ref(false);
const notificationCenterRef = ref(null);
const unreadCount = ref(0);
-
- function toggleSideBar() {
- appStore.toggleSideBar();
- }
function openHeaderSearch() {
headerSearchRef.value?.open(topSearchKeyword.value);
@@ -205,134 +230,91 @@
display: flex;
align-items: center;
justify-content: space-between;
- padding: 0 24px;
- background: var(--navbar-bg);
- border-bottom: 1px solid rgba(255, 255, 255, 0.08);
- backdrop-filter: blur(12px);
+ padding: 0 24px 0 0;
+ background: #fff;
+ border-bottom: 1px solid #d8dce5;
+ box-shadow: 0 1px 4px rgba(0, 21, 41, 0.08);
z-index: var(--layout-header-z);
}
- .left-menu {
+ .navbar-left {
display: flex;
align-items: center;
- gap: 12px;
- }
-
- .hamburger-container {
- height: 32px;
- width: 32px;
- border-radius: var(--radius-sm);
- display: flex;
- align-items: center;
- justify-content: center;
- color: var(--navbar-text);
- cursor: pointer;
- transition: all 0.2s ease;
-
- &:hover {
- background: var(--navbar-hover);
- color: #fff;
- }
- }
-
- .breadcrumb-container {
+ height: 100%;
+ flex: 1;
min-width: 0;
+ }
- :deep(.el-breadcrumb__inner) {
- color: var(--navbar-text) !important;
- opacity: 0.85;
+ .navbar-logo {
+ height: 100%;
+ display: flex;
+ align-items: center;
+ transition: width 0.25s ease;
+ overflow: hidden;
+ flex-shrink: 0;
+ background-color: #fff; // 寮哄埗璁句负鐧借壊鑳屾櫙
+ // border-right: 1px solid #d8dce5; // 澧炲姞鍙充晶杈规浠ュ尯鍒嗗鑸爮涓讳綋
+ }
- &:hover {
- color: #fff !important;
- opacity: 1;
- }
+ .top-nav-wrapper {
+ display: flex;
+ align-items: center;
+ height: 100%;
+ min-width: 0;
+ flex: 1;
+ padding: 0 12px;
+ overflow: hidden;
+ }
- a {
- color: inherit !important;
- font-weight: 500 !important;
- }
- }
-
- :deep(.no-redirect) {
- color: #fff !important;
- font-weight: 600 !important;
- opacity: 1;
- }
-
- :deep(.el-breadcrumb__separator) {
- color: var(--navbar-text);
- opacity: 0.5;
- }
+ .left-menu-spacer {
+ flex: 1;
+ min-width: 0;
}
.right-menu {
display: flex;
align-items: center;
- gap: 20px; // 澧炲姞澶х粍涔嬮棿鐨勯棿璺�
-
- .search-wrapper {
- display: flex;
- align-items: center;
- gap: 8px;
- height: 34px;
- padding: 0 12px;
- background: var(--navbar-hover);
- border: 1px solid var(--surface-border);
- border-radius: 17px;
- width: 240px; // 鎼滅储妗嗘洿鍔犵簿鑷村皬宸�
- transition: all 0.3s ease;
-
- &:focus-within {
- width: 300px;
- background: rgba(255, 255, 255, 0.1);
- border-color: var(--accent-primary);
- box-shadow: 0 0 0 2px rgba(var(--el-color-primary-rgb), 0.2);
- }
-
- .search-icon {
- color: var(--sidebar-text);
- font-size: 16px;
- cursor: pointer;
- }
-
- :deep(.el-input__wrapper) {
- background: transparent;
- box-shadow: none !important;
- padding: 0;
- }
-
- :deep(.el-input__inner) {
- color: var(--navbar-text);
- font-size: 13px;
- height: 32px;
-
- &::placeholder {
- color: var(--sidebar-text);
- }
- }
- }
+ gap: 16px; // 璋冩暣缁勪箣闂寸殑闂磋窛
.action-icons {
display: flex;
align-items: center;
- gap: 4px;
+ gap: 12px; // 鍥炬爣涔嬮棿鐨勯棿璺�
padding-right: 16px;
- border-right: 1px solid var(--surface-border); // 澧炲姞鍨傜洿鍒嗗壊绾�
+ border-right: 1px solid var(--surface-border);
- .right-menu-item {
- padding: 0 8px;
- height: 34px;
+ .action-icon-btn {
+ width: 36px;
+ height: 36px;
+ padding: 0;
display: flex;
align-items: center;
- color: var(--navbar-text);
- border-radius: var(--radius-sm);
+ justify-content: center;
+ color: var(--text-secondary);
+ border-radius: 50%; // 鍦嗗舰鑳屾櫙
+ background: rgba(0, 0, 0, 0.04); // 娴呮祬鐨勫渾褰㈡搴曡壊
transition: all 0.2s ease;
cursor: pointer;
&:hover {
- background: var(--navbar-hover);
- color: var(--menu-active-text);
+ background: rgba(0, 0, 0, 0.08); // 鎮仠鍔犳繁
+ color: var(--el-color-primary);
}
+
+ :deep(.svg-icon) {
+ width: 18px !important;
+ height: 18px !important;
+ }
+
+ :deep(.el-icon) {
+ font-size: 18px !important;
+ }
+ }
+
+ .notification-container {
+ display: flex;
+ align-items: center;
+ justify-content: center;
}
}
@@ -348,14 +330,12 @@
height: 38px;
padding: 4px 10px 4px 6px;
border-radius: 999px;
- border: 1px solid rgba(var(--el-color-primary-rgb), 0.32);
- background: rgba(0, 0, 0, 0.16);
+ border: 1px solid transparent;
+ background: transparent;
transition: 0.2s ease;
&:hover {
- background: rgba(0, 0, 0, 0.24);
- border-color: rgba(var(--el-color-primary-rgb), 0.58);
- box-shadow: 0 0 0 2px rgba(var(--el-color-primary-rgb), 0.18);
+ background: rgba(0, 0, 0, 0.05);
}
}
@@ -368,8 +348,8 @@
.user-name {
font-size: 13px;
- font-weight: 700;
- color: var(--navbar-text);
+ font-weight: 500;
+ color: var(--text-primary);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
@@ -386,8 +366,7 @@
width: 30px;
height: 30px;
border-radius: 999px;
- border: 2px solid rgba(var(--el-color-primary-rgb), 0.28);
- box-shadow: 0 8px 18px rgba(0, 0, 0, 0.18);
+ border: 1px solid rgba(0, 0, 0, 0.1);
object-fit: cover;
transition: 0.2s ease;
}
@@ -403,12 +382,12 @@
width: 9px;
height: 9px;
background: #10b981;
- border: 2px solid var(--navbar-bg);
+ border: 2px solid #fff;
border-radius: 999px;
}
.caret-icon {
- color: var(--navbar-text);
+ color: var(--text-secondary);
opacity: 0.76;
font-size: 12px;
transition: 0.2s ease;
diff --git a/src/layout/components/Settings/index.vue b/src/layout/components/Settings/index.vue
index c478c78..69398f2 100644
--- a/src/layout/components/Settings/index.vue
+++ b/src/layout/components/Settings/index.vue
@@ -153,7 +153,7 @@
const sideTheme = ref(settingsStore.sideTheme);
const storeSettings = computed(() => settingsStore);
const predefineColors = ref([
- "#374D77",
+ "#008C8C",
"#81D8D0",
"#E85827",
"#008C8C",
diff --git a/src/layout/components/Sidebar/Logo.vue b/src/layout/components/Sidebar/Logo.vue
index 632a0df..569f843 100644
--- a/src/layout/components/Sidebar/Logo.vue
+++ b/src/layout/components/Sidebar/Logo.vue
@@ -1,145 +1,163 @@
<template>
- <div class="sidebar-logo-container" :class="{ collapse }">
+ <div class="sidebar-logo-container"
+ :class="{ collapse }">
<transition name="sidebarLogoFade">
- <router-link style="display: flex;" v-if="collapse" key="collapse" class="sidebar-logo-link" to="/">
- <img :src="faviconUrl" class="sidebar-logo sidebar-favicon" alt="绔欑偣鍥炬爣" />
+ <router-link style="display: flex;"
+ v-if="collapse"
+ key="collapse"
+ class="sidebar-logo-link"
+ to="/">
+ <img :src="faviconUrl"
+ class="sidebar-logo sidebar-favicon"
+ alt="绔欑偣鍥炬爣" />
</router-link>
- <router-link v-else key="expand" class="sidebar-logo-link" :style="expandLogoLinkStyle" to="/">
- <img v-if="logoUrl" :src="logoUrl" class="sidebar-logo" @error="handleImageError" alt="鍏徃Logo" />
- <h1 v-if="!logoUrl" class="sidebar-title">{{ title }}</h1>
+ <router-link v-else
+ key="expand"
+ class="sidebar-logo-link"
+ :style="expandLogoLinkStyle"
+ to="/">
+ <img v-if="logoUrl"
+ :src="logoUrl"
+ class="sidebar-logo"
+ @error="handleImageError"
+ alt="鍏徃Logo" />
+ <h1 v-if="!logoUrl"
+ class="sidebar-title">{{ title }}</h1>
</router-link>
</transition>
</div>
</template>
<script setup>
-import { ref, computed, onMounted, watch } from 'vue'
-import useUserStore from '@/store/modules/user'
-import defaultLogo from '@/assets/logo/logo.png'
+ import { ref, computed, onMounted, watch } from "vue";
+ import useUserStore from "@/store/modules/user";
+ import defaultLogo from "@/assets/logo/鑺杞欢锛堟睙鑻忥級鏈夐檺鍏徃.png";
-defineProps({
- collapse: {
- type: Boolean,
- required: true
- }
-})
+ defineProps({
+ collapse: {
+ type: Boolean,
+ required: true,
+ },
+ });
-const title = import.meta.env.VITE_APP_TITLE
-const userStore = useUserStore()
-const baseUrl = import.meta.env.BASE_URL || '/'
-const faviconUrl = `${baseUrl.replace(/\/?$/, '/') }favicon.ico`.replace(/([^:]\/)\/+/g, '$1')
+ const title = import.meta.env.VITE_APP_TITLE;
+ const userStore = useUserStore();
+ const baseUrl = import.meta.env.BASE_URL || "/";
+ const faviconUrl = `${baseUrl.replace(/\/?$/, "/")}favicon.ico`.replace(
+ /([^:]\/)\/+/g,
+ "$1"
+ );
-const cleanFactoryName = computed(() => {
- if (!userStore.currentFactoryName) return ''
- return userStore.currentFactoryName.trim()
-})
+ const cleanFactoryName = computed(() => {
+ if (!userStore.currentFactoryName) return "";
+ return userStore.currentFactoryName.trim();
+ });
-const logoUrl = ref('')
+ const logoUrl = ref("");
-const expandLogoLinkStyle = computed(() => {
- if (!logoUrl.value) {
- return { '--logo-bg-image': 'none' }
- }
- const escaped = String(logoUrl.value).replace(/"/g, '\\"')
- return { '--logo-bg-image': `url("${escaped}")` }
-})
-
-const updateLogoUrl = () => {
- if (!cleanFactoryName.value) {
- logoUrl.value = defaultLogo
- return
- }
-
- try {
- const dynamicLogo = import.meta.glob('/src/assets/logo/*.png', { eager: true })
- const logoPath = `/src/assets/logo/${cleanFactoryName.value}.png`
-
- if (dynamicLogo[logoPath]) {
- logoUrl.value = dynamicLogo[logoPath].default
- } else {
- logoUrl.value = defaultLogo
+ const expandLogoLinkStyle = computed(() => {
+ if (!logoUrl.value) {
+ return { "--logo-bg-image": "none" };
}
- } catch (error) {
- console.error('鍔犺浇宸ュ巶 Logo 澶辫触:', error)
- logoUrl.value = defaultLogo
- }
-}
+ const escaped = String(logoUrl.value).replace(/"/g, '\\"');
+ return { "--logo-bg-image": `url("${escaped}")` };
+ });
-onMounted(() => {
- updateLogoUrl()
- watch(() => userStore.currentFactoryName, updateLogoUrl)
-})
+ const updateLogoUrl = () => {
+ if (!cleanFactoryName.value) {
+ logoUrl.value = defaultLogo;
+ return;
+ }
-const handleImageError = () => {
- logoUrl.value = defaultLogo
-}
+ try {
+ const dynamicLogo = import.meta.glob("/src/assets/logo/*.png", {
+ eager: true,
+ });
+ const logoPath = `/src/assets/logo/${cleanFactoryName.value}.png`;
+
+ if (dynamicLogo[logoPath]) {
+ logoUrl.value = dynamicLogo[logoPath].default;
+ } else {
+ logoUrl.value = defaultLogo;
+ }
+ } catch (error) {
+ console.error("鍔犺浇宸ュ巶 Logo 澶辫触:", error);
+ logoUrl.value = defaultLogo;
+ }
+ };
+
+ onMounted(() => {
+ updateLogoUrl();
+ watch(() => userStore.currentFactoryName, updateLogoUrl);
+ });
+
+ const handleImageError = () => {
+ logoUrl.value = defaultLogo;
+ };
</script>
<style lang="scss" scoped>
-@import '@/assets/styles/variables.module.scss';
+ @import "@/assets/styles/variables.module.scss";
-.sidebarLogoFade-enter-active {
- transition: opacity 1.5s;
-}
-
-.sidebarLogoFade-enter,
-.sidebarLogoFade-leave-to {
- opacity: 0;
-}
-
-.sidebar-logo-container {
- position: relative;
- width: 100% !important;
- height: 64px !important;
- line-height: 64px;
- background: transparent;
- border-bottom: 1px solid rgba(255, 255, 255, 0.08);
- text-align: left;
- overflow: hidden;
- transition: all 0.3s ease;
-
- .sidebar-logo-link {
- height: 100%;
- width: 100%;
- display: flex;
- align-items: center;
- justify-content: flex-start; // 榛樿灞曞紑鏃堕潬宸�
- padding: 0 16px;
+ .sidebarLogoFade-enter-active {
+ transition: opacity 1.5s;
}
- .sidebar-logo {
- height:100%;
- width:100%;
- width: auto;
- max-width: 100%;
- vertical-align: middle;
- object-fit: contain;
- display: block;
+ .sidebarLogoFade-enter,
+ .sidebarLogoFade-leave-to {
+ opacity: 0;
}
- .sidebar-title {
- display: inline-block;
- margin: 0;
- color: #fff;
- font-weight: 600;
- line-height: 64px;
- font-size: 16px;
- vertical-align: middle;
- margin-left: 12px;
- white-space: nowrap;
- }
+ .sidebar-logo-container {
+ position: relative;
+ width: 100% !important;
+ height: 100% !important;
+ line-height: var(--topbar-height, 64px);
+ background: transparent;
+ text-align: left;
+ overflow: hidden;
+ transition: all 0.3s ease;
- &.collapse {
.sidebar-logo-link {
- padding: 0;
- justify-content: center; // 鎶樺彔鏃剁粷瀵瑰眳涓�
+ height: 100%;
+ width: 100%;
+ display: flex;
+ align-items: center;
+ justify-content: center; // 濮嬬粓灞呬腑
+ padding: 0; // 绉婚櫎涓よ竟闂磋窛锛岃鍥剧墖鑳芥拺婊�
}
.sidebar-logo {
- width: 32px;
- height: 32px;
+ width: 100%; // 瀹藉害濮嬬粓鍜屼晶杈规爮涓�鑷�
+ height: auto; // 楂樺害鑷�傚簲
+ vertical-align: middle;
+ object-fit: contain;
+ display: block;
+ }
+
+ .sidebar-title {
+ display: inline-block;
margin: 0;
+ color: #333; // 鐧借壊鑳屾櫙涓嬮渶瑕佹繁鑹插瓧浣�
+ font-weight: 600;
+ line-height: var(--topbar-height, 64px);
+ font-size: 16px;
+ vertical-align: middle;
+ margin-left: 12px;
+ white-space: nowrap;
+ }
+
+ &.collapse {
+ .sidebar-logo-link {
+ padding: 0;
+ justify-content: center; // 鎶樺彔鏃剁粷瀵瑰眳涓�
+ }
+
+ .sidebar-logo {
+ width: 32px;
+ height: 32px;
+ margin: 0;
+ }
}
}
-}
</style>
diff --git a/src/layout/components/Sidebar/SidebarItem.vue b/src/layout/components/Sidebar/SidebarItem.vue
index e770ee2..09b3a39 100644
--- a/src/layout/components/Sidebar/SidebarItem.vue
+++ b/src/layout/components/Sidebar/SidebarItem.vue
@@ -85,6 +85,13 @@
if (isExternal(props.basePath)) {
return props.basePath
}
+ if (String(routePath || '').startsWith('/')) {
+ if (routeQuery) {
+ let query = JSON.parse(routeQuery)
+ return { path: getNormalPath(routePath), query: query }
+ }
+ return getNormalPath(routePath)
+ }
if (routeQuery) {
let query = JSON.parse(routeQuery)
return { path: getNormalPath(props.basePath + '/' + routePath), query: query }
diff --git a/src/layout/components/Sidebar/index.vue b/src/layout/components/Sidebar/index.vue
index 8f9fb89..6490960 100644
--- a/src/layout/components/Sidebar/index.vue
+++ b/src/layout/components/Sidebar/index.vue
@@ -1,101 +1,100 @@
<template>
- <div :class="{ 'has-logo': showLogo }" class="sidebar-container">
- <logo v-if="showLogo" :collapse="isCollapse" />
+ <div :class="{ 'has-logo': showLogo && appStore.device === 'mobile' }"
+ class="sidebar-container">
+ <logo v-if="showLogo && appStore.device === 'mobile'"
+ :collapse="false" />
<el-scrollbar wrap-class="scrollbar-wrapper">
- <el-menu
- :default-active="activeMenu"
- :collapse="isCollapse"
- :background-color="getMenuBackground"
- :text-color="getMenuTextColor"
- :unique-opened="true"
- :active-text-color="theme"
- :collapse-transition="false"
- mode="vertical"
- :class="sideTheme"
- >
- <sidebar-item
- v-for="(route, index) in sidebarRouters"
- :key="route.path + index"
- :item="route"
- :base-path="route.path"
- />
+ <el-menu :default-active="activeMenu"
+ :collapse="isCollapse"
+ :background-color="getMenuBackground"
+ :text-color="getMenuTextColor"
+ :unique-opened="true"
+ :active-text-color="theme"
+ :collapse-transition="false"
+ mode="vertical"
+ :class="sideTheme">
+ <sidebar-item v-for="(route, index) in sidebarRouters"
+ :key="route.path + index"
+ :item="route"
+ :base-path="route.path" />
</el-menu>
</el-scrollbar>
</div>
</template>
<script setup>
-import Logo from "./Logo";
-import SidebarItem from "./SidebarItem";
-import useAppStore from "@/store/modules/app";
-import useSettingsStore from "@/store/modules/settings";
-import usePermissionStore from "@/store/modules/permission";
+ import Logo from "./Logo";
+ import SidebarItem from "./SidebarItem";
+ import useAppStore from "@/store/modules/app";
+ import useSettingsStore from "@/store/modules/settings";
+ import usePermissionStore from "@/store/modules/permission";
-const route = useRoute();
-const appStore = useAppStore();
-const settingsStore = useSettingsStore();
-const permissionStore = usePermissionStore();
+ const route = useRoute();
+ const appStore = useAppStore();
+ const settingsStore = useSettingsStore();
+ const permissionStore = usePermissionStore();
-const sidebarRouters = computed(() => permissionStore.sidebarRouters);
-const showLogo = computed(() => settingsStore.sidebarLogo);
-const sideTheme = computed(() => settingsStore.sideTheme);
-const theme = computed(() => settingsStore.theme);
-const isCollapse = computed(() => !appStore.sidebar.opened);
+ const sidebarRouters = computed(() => permissionStore.sidebarRouters);
+ const showLogo = computed(() => settingsStore.sidebarLogo);
+ const sideTheme = computed(() => settingsStore.sideTheme);
+ const theme = computed(() => settingsStore.theme);
+ const isCollapse = computed(() => !appStore.sidebar.opened);
-const getMenuBackground = computed(() => "var(--sidebar-bg)");
+ const getMenuBackground = computed(() => "var(--sidebar-bg)");
-const getMenuTextColor = computed(() => "var(--sidebar-text)");
+ const getMenuTextColor = computed(() => "var(--sidebar-text)");
-const activeMenu = computed(() => {
- const { meta, path } = route;
- if (meta.activeMenu) return meta.activeMenu;
- return path;
-});
+ const activeMenu = computed(() => {
+ const { meta, path } = route;
+ if (meta.activeMenu) return meta.activeMenu;
+ return path;
+ });
</script>
<style lang="scss" scoped>
-.sidebar-container {
- background: transparent;
- border-radius: 0;
- overflow: hidden;
-
- .scrollbar-wrapper {
+ .sidebar-container {
background: transparent;
- }
+ border-radius: 0;
+ overflow: hidden;
+ display: flex;
+ flex-direction: column;
- .el-menu {
- border: none !important;
- height: 100%;
- width: 100% !important;
- background: transparent !important;
-
- :deep(.el-menu-item),
- :deep(.el-sub-menu__title) {
- margin-bottom: 4px;
- border-radius: var(--radius-md);
- color: var(--sidebar-text);
- font-size: 14px;
- transition: all 0.2s ease;
- display: flex;
- align-items: center;
-
- &:hover {
- background: var(--menu-hover) !important;
- color: #fff !important;
- }
+ .scrollbar-wrapper {
+ background: transparent;
}
- :deep(.el-menu-item.is-active) {
- background: var(--menu-active-bg) !important;
- color: var(--menu-active-text) !important;
- font-weight: 600;
- box-shadow: var(--menu-active-glow);
+ .el-menu {
+ border: none !important;
+ height: 100%;
+ width: 100% !important;
+ background: transparent !important;
- .svg-icon {
+ :deep(.el-menu-item),
+ :deep(.el-sub-menu__title) {
+ margin-bottom: 4px;
+ border-radius: var(--radius-md);
+ color: var(--sidebar-text);
+ font-size: 14px;
+ transition: all 0.2s ease;
+ display: flex;
+ align-items: center;
+
+ &:hover {
+ background: var(--menu-hover) !important;
+ color: #fff !important;
+ }
+ }
+
+ :deep(.el-menu-item.is-active) {
+ background: var(--menu-active-bg) !important;
color: var(--menu-active-text) !important;
+ font-weight: 600;
+ box-shadow: var(--menu-active-glow);
+
+ .svg-icon {
+ color: var(--menu-active-text) !important;
+ }
}
}
-
}
-}
</style>
diff --git a/src/layout/components/TagsView/index.vue b/src/layout/components/TagsView/index.vue
index 9b8e525..7b2345c 100644
--- a/src/layout/components/TagsView/index.vue
+++ b/src/layout/components/TagsView/index.vue
@@ -1,411 +1,462 @@
<template>
- <div id="tags-view-container" class="tags-view-container">
- <scroll-pane ref="scrollPaneRef" class="tags-view-wrapper" @scroll="handleScroll">
- <router-link
- v-for="tag in visitedViews"
- :key="tag.path"
- :data-path="tag.path"
- :class="isActive(tag) ? 'active' : ''"
- :to="{ path: tag.path, query: tag.query, fullPath: tag.fullPath }"
- class="tags-view-item"
- :style="activeStyle(tag)"
- @click.middle="!isAffix(tag) ? closeSelectedTag(tag) : ''"
- @contextmenu.prevent="openMenu(tag, $event)"
- >
- {{ tag.title }}
- <span v-if="!isAffix(tag)" class="tags-view-close" @click.prevent.stop="closeSelectedTag(tag)">
- <close class="el-icon-close" />
+ <div id="tags-view-container"
+ class="tags-view-container">
+ <scroll-pane ref="scrollPaneRef"
+ class="tags-view-wrapper"
+ @scroll="handleScroll">
+ <router-link v-for="tag in visitedViews"
+ :key="tag.path"
+ :data-path="tag.path"
+ :class="isActive(tag) ? 'active' : ''"
+ :to="{ path: tag.path, query: tag.query, fullPath: tag.fullPath }"
+ class="tags-view-item"
+ :style="activeStyle(tag)"
+ @click.middle="!isAffix(tag) ? closeSelectedTag(tag) : ''"
+ @contextmenu.prevent="openMenu(tag, $event)">
+ <span class="tags-view-item-title">{{ tag.title }}</span>
+ <span v-if="!isAffix(tag)"
+ class="tags-view-close"
+ @click.prevent.stop="closeSelectedTag(tag)">
+ <el-icon class="el-icon-close">
+ <Close />
+ </el-icon>
</span>
</router-link>
</scroll-pane>
- <ul v-show="visible" :style="{ left: left + 'px', top: top + 'px' }" class="contextmenu">
+ <ul v-show="visible"
+ :style="{ left: left + 'px', top: top + 'px' }"
+ class="contextmenu">
<li @click="refreshSelectedTag(selectedTag)">
- <refresh-right style="width: 1em; height: 1em;" /> 鍒锋柊椤甸潰
+ <el-icon>
+ <RefreshRight />
+ </el-icon> 鍒锋柊椤甸潰
</li>
- <li v-if="!isAffix(selectedTag)" @click="closeSelectedTag(selectedTag)">
- <close style="width: 1em; height: 1em;" /> 鍏抽棴褰撳墠
+ <li v-if="!isAffix(selectedTag)"
+ @click="closeSelectedTag(selectedTag)">
+ <el-icon>
+ <Close />
+ </el-icon> 鍏抽棴褰撳墠
</li>
<li @click="closeOthersTags">
- <circle-close style="width: 1em; height: 1em;" /> 鍏抽棴鍏朵粬
+ <el-icon>
+ <CircleClose />
+ </el-icon> 鍏抽棴鍏朵粬
</li>
- <li v-if="!isFirstView()" @click="closeLeftTags">
- <back style="width: 1em; height: 1em;" /> 鍏抽棴宸︿晶
+ <li v-if="!isFirstView()"
+ @click="closeLeftTags">
+ <el-icon>
+ <Back />
+ </el-icon> 鍏抽棴宸︿晶
</li>
- <li v-if="!isLastView()" @click="closeRightTags">
- <right style="width: 1em; height: 1em;" /> 鍏抽棴鍙充晶
+ <li v-if="!isLastView()"
+ @click="closeRightTags">
+ <el-icon>
+ <Right />
+ </el-icon> 鍏抽棴鍙充晶
</li>
<li @click="closeAllTags(selectedTag)">
- <circle-close style="width: 1em; height: 1em;" /> 鍏ㄩ儴鍏抽棴
+ <el-icon>
+ <CircleClose />
+ </el-icon> 鍏ㄩ儴鍏抽棴
</li>
</ul>
</div>
</template>
<script setup>
-import ScrollPane from './ScrollPane'
-import { getNormalPath } from '@/utils/ruoyi'
-import useTagsViewStore from '@/store/modules/tagsView'
-import useSettingsStore from '@/store/modules/settings'
-import usePermissionStore from '@/store/modules/permission'
+ import ScrollPane from "./ScrollPane";
+ import { getNormalPath } from "@/utils/ruoyi";
+ import useTagsViewStore from "@/store/modules/tagsView";
+ import useSettingsStore from "@/store/modules/settings";
+ import usePermissionStore from "@/store/modules/permission";
+ import {
+ Close,
+ RefreshRight,
+ CircleClose,
+ Back,
+ Right,
+ } from "@element-plus/icons-vue";
-const visible = ref(false)
-const top = ref(0)
-const left = ref(0)
-const selectedTag = ref({})
-const affixTags = ref([])
-const scrollPaneRef = ref(null)
+ const visible = ref(false);
+ const top = ref(0);
+ const left = ref(0);
+ const selectedTag = ref({});
+ const affixTags = ref([]);
+ const scrollPaneRef = ref(null);
-const { proxy } = getCurrentInstance()
-const route = useRoute()
-const router = useRouter()
+ const { proxy } = getCurrentInstance();
+ const route = useRoute();
+ const router = useRouter();
-const visitedViews = computed(() => useTagsViewStore().visitedViews)
-const routes = computed(() => usePermissionStore().routes)
-const theme = computed(() => useSettingsStore().theme)
+ const visitedViews = computed(() => useTagsViewStore().visitedViews);
+ const routes = computed(() => usePermissionStore().routes);
+ const theme = computed(() => useSettingsStore().theme);
-watch(route, () => {
- addTags()
- moveToCurrentTag()
-})
+ watch(route, () => {
+ addTags();
+ moveToCurrentTag();
+ });
-watch(visible, (value) => {
- if (value) {
- document.body.addEventListener('click', closeMenu)
- } else {
- document.body.removeEventListener('click', closeMenu)
- }
-})
-
-onMounted(() => {
- initTags()
- addTags()
-})
-
-function isActive(r) {
- return r.path === route.path
-}
-
-function activeStyle(tag) {
- if (!isActive(tag)) return {}
- return {
- "background-color": theme.value,
- "border-color": theme.value
- }
-}
-
-function isAffix(tag) {
- return tag.meta && tag.meta.affix
-}
-
-function isFirstView() {
- try {
- return selectedTag.value.fullPath === '/index' || selectedTag.value.fullPath === visitedViews.value[1].fullPath
- } catch (err) {
- return false
- }
-}
-
-function isLastView() {
- try {
- return selectedTag.value.fullPath === visitedViews.value[visitedViews.value.length - 1].fullPath
- } catch (err) {
- return false
- }
-}
-
-function filterAffixTags(routes, basePath = '') {
- let tags = []
- routes.forEach(route => {
- if (route.meta && route.meta.affix) {
- const tagPath = getNormalPath(basePath + '/' + route.path)
- tags.push({
- fullPath: tagPath,
- path: tagPath,
- name: route.name,
- meta: { ...route.meta }
- })
+ watch(visible, value => {
+ if (value) {
+ document.body.addEventListener("click", closeMenu);
+ } else {
+ document.body.removeEventListener("click", closeMenu);
}
- if (route.children) {
- const tempTags = filterAffixTags(route.children, route.path)
- if (tempTags.length >= 1) {
- tags = [...tags, ...tempTags]
+ });
+
+ onMounted(() => {
+ initTags();
+ addTags();
+ });
+
+ function isActive(r) {
+ return r.path === route.path;
+ }
+
+ function activeStyle(tag) {
+ if (!isActive(tag)) return {};
+ return {
+ "background-color": theme.value,
+ "border-color": theme.value,
+ };
+ }
+
+ function isAffix(tag) {
+ return tag.meta && tag.meta.affix;
+ }
+
+ function isFirstView() {
+ try {
+ return (
+ selectedTag.value.fullPath === "/index" ||
+ selectedTag.value.fullPath === visitedViews.value[1].fullPath
+ );
+ } catch (err) {
+ return false;
+ }
+ }
+
+ function isLastView() {
+ try {
+ return (
+ selectedTag.value.fullPath ===
+ visitedViews.value[visitedViews.value.length - 1].fullPath
+ );
+ } catch (err) {
+ return false;
+ }
+ }
+
+ function filterAffixTags(routes, basePath = "") {
+ let tags = [];
+ routes.forEach(route => {
+ if (route.meta && route.meta.affix) {
+ const tagPath = getNormalPath(basePath + "/" + route.path);
+ tags.push({
+ fullPath: tagPath,
+ path: tagPath,
+ name: route.name,
+ meta: { ...route.meta },
+ });
}
- }
- })
- return tags
-}
-
-function initTags() {
- const res = filterAffixTags(routes.value)
- affixTags.value = res
- for (const tag of res) {
- // Must have tag name
- if (tag.name) {
- useTagsViewStore().addVisitedView(tag)
- }
- }
-}
-
-function addTags() {
- const { name } = route
- if (name) {
- useTagsViewStore().addView(route)
- }
-}
-
-function moveToCurrentTag() {
- nextTick(() => {
- for (const r of visitedViews.value) {
- if (r.path === route.path) {
- scrollPaneRef.value.moveToTarget(r)
- // when query is different then update
- if (r.fullPath !== route.fullPath) {
- useTagsViewStore().updateVisitedView(route)
+ if (route.children) {
+ const tempTags = filterAffixTags(route.children, route.path);
+ if (tempTags.length >= 1) {
+ tags = [...tags, ...tempTags];
}
}
- }
- })
-}
-
-function refreshSelectedTag(view) {
- proxy.$tab.refreshPage(view)
- if (route.meta.link) {
- useTagsViewStore().delIframeView(route)
+ });
+ return tags;
}
-}
-function closeSelectedTag(view) {
- proxy.$tab.closePage(view).then(({ visitedViews }) => {
- if (isActive(view)) {
- toLastView(visitedViews, view)
+ function initTags() {
+ const res = filterAffixTags(routes.value);
+ affixTags.value = res;
+ for (const tag of res) {
+ // Must have tag name
+ if (tag.name) {
+ useTagsViewStore().addVisitedView(tag);
+ }
}
- })
-}
+ }
-function closeRightTags() {
- proxy.$tab.closeRightPage(selectedTag.value).then(visitedViews => {
- if (!visitedViews.find(i => i.fullPath === route.fullPath)) {
- toLastView(visitedViews)
+ function addTags() {
+ const { name } = route;
+ if (name) {
+ useTagsViewStore().addView(route);
}
- })
-}
+ }
-function closeLeftTags() {
- proxy.$tab.closeLeftPage(selectedTag.value).then(visitedViews => {
- if (!visitedViews.find(i => i.fullPath === route.fullPath)) {
- toLastView(visitedViews)
+ function moveToCurrentTag() {
+ nextTick(() => {
+ for (const r of visitedViews.value) {
+ if (r.path === route.path) {
+ scrollPaneRef.value.moveToTarget(r);
+ // when query is different then update
+ if (r.fullPath !== route.fullPath) {
+ useTagsViewStore().updateVisitedView(route);
+ }
+ }
+ }
+ });
+ }
+
+ function refreshSelectedTag(view) {
+ proxy.$tab.refreshPage(view);
+ if (route.meta.link) {
+ useTagsViewStore().delIframeView(route);
}
- })
-}
+ }
-function closeOthersTags() {
- router.push(selectedTag.value).catch(() => { })
- proxy.$tab.closeOtherPage(selectedTag.value).then(() => {
- moveToCurrentTag()
- })
-}
+ function closeSelectedTag(view) {
+ proxy.$tab.closePage(view).then(({ visitedViews }) => {
+ if (isActive(view)) {
+ toLastView(visitedViews, view);
+ }
+ });
+ }
-function closeAllTags(view) {
- proxy.$tab.closeAllPage().then(({ visitedViews }) => {
- if (affixTags.value.some(tag => tag.path === route.path)) {
- return
- }
- toLastView(visitedViews, view)
- })
-}
+ function closeRightTags() {
+ proxy.$tab.closeRightPage(selectedTag.value).then(visitedViews => {
+ if (!visitedViews.find(i => i.fullPath === route.fullPath)) {
+ toLastView(visitedViews);
+ }
+ });
+ }
-function toLastView(visitedViews, view) {
- const latestView = visitedViews.slice(-1)[0]
- if (latestView) {
- router.push(latestView.fullPath)
- } else {
- // now the default is to redirect to the home page if there is no tags-view,
- // you can adjust it according to your needs.
- if (view.name === 'Dashboard') {
- // to reload home page
- router.replace({ path: '/redirect' + view.fullPath })
+ function closeLeftTags() {
+ proxy.$tab.closeLeftPage(selectedTag.value).then(visitedViews => {
+ if (!visitedViews.find(i => i.fullPath === route.fullPath)) {
+ toLastView(visitedViews);
+ }
+ });
+ }
+
+ function closeOthersTags() {
+ router.push(selectedTag.value).catch(() => {});
+ proxy.$tab.closeOtherPage(selectedTag.value).then(() => {
+ moveToCurrentTag();
+ });
+ }
+
+ function closeAllTags(view) {
+ proxy.$tab.closeAllPage().then(({ visitedViews }) => {
+ if (affixTags.value.some(tag => tag.path === route.path)) {
+ return;
+ }
+ toLastView(visitedViews, view);
+ });
+ }
+
+ function toLastView(visitedViews, view) {
+ const latestView = visitedViews.slice(-1)[0];
+ if (latestView) {
+ router.push(latestView.fullPath);
} else {
- router.push('/')
+ // now the default is to redirect to the home page if there is no tags-view,
+ // you can adjust it according to your needs.
+ if (view.name === "Dashboard") {
+ // to reload home page
+ router.replace({ path: "/redirect" + view.fullPath });
+ } else {
+ router.push("/");
+ }
}
}
-}
-function openMenu(tag, e) {
- const menuMinWidth = 105
- const offsetLeft = proxy.$el.getBoundingClientRect().left // container margin left
- const offsetWidth = proxy.$el.offsetWidth // container width
- const maxLeft = offsetWidth - menuMinWidth // left boundary
- const l = e.clientX - offsetLeft + 15 // 15: margin right
+ function openMenu(tag, e) {
+ const menuMinWidth = 105;
+ const offsetLeft = proxy.$el.getBoundingClientRect().left; // container margin left
+ const offsetWidth = proxy.$el.offsetWidth; // container width
+ const maxLeft = offsetWidth - menuMinWidth; // left boundary
+ const l = e.clientX - offsetLeft + 15; // 15: margin right
- if (l > maxLeft) {
- left.value = maxLeft
- } else {
- left.value = l
+ if (l > maxLeft) {
+ left.value = maxLeft;
+ } else {
+ left.value = l;
+ }
+
+ top.value = e.clientY;
+ visible.value = true;
+ selectedTag.value = tag;
}
- top.value = e.clientY
- visible.value = true
- selectedTag.value = tag
-}
+ function closeMenu() {
+ visible.value = false;
+ }
-function closeMenu() {
- visible.value = false
-}
-
-function handleScroll() {
- closeMenu()
-}
+ function handleScroll() {
+ closeMenu();
+ }
</script>
<style lang="scss" scoped>
-.tags-view-container {
- height: 42px; // 澧炲姞瀹瑰櫒楂樺害锛屾彁渚涙洿澶氬懠鍚告劅
- width: 100%;
- background: var(--tags-bg);
- border-bottom: 1px solid var(--surface-border);
- padding: 0 16px;
- display: flex;
- align-items: center;
+ .tags-view-container {
+ height: 34px;
+ width: 100%;
+ background: #fff;
+ border-bottom: 1px solid #d8dce5;
+ box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.12), 0 0 3px 0 rgba(0, 0, 0, 0.04);
+ padding: 0;
+ display: flex;
+ align-items: center;
+ position: relative;
+ z-index: 10;
- .tags-view-wrapper {
- flex: 1;
- overflow: hidden;
- white-space: nowrap;
+ .tags-view-wrapper {
+ flex: 1;
+ overflow: hidden;
+ white-space: nowrap;
- .tags-view-item {
- display: inline-flex;
- align-items: center;
- position: relative;
- cursor: pointer;
- height: 30px; // 澧炲姞椤电楂樺害
- line-height: 30px;
- color: var(--tags-item-text);
- background: var(--tags-item-bg);
- border: 1px solid var(--tags-item-border);
- border-radius: 6px; // 绋嶅井鍦嗘鼎涓�鐐�
- padding: 0 12px;
- font-size: 12px;
- margin-right: 6px;
- transition: all 0.2s ease;
- gap: 6px;
- text-decoration: none;
+ .tags-view-item {
+ display: inline-flex;
+ align-items: center;
+ position: relative;
+ cursor: pointer;
+ height: 26px;
+ line-height: 26px;
+ color: #495060;
+ background-color: #fff;
+ border: 1px solid #d8dce5;
+ border-radius: 2px;
+ padding: 0 8px;
+ font-size: 12px;
+ margin-right: 5px;
+ margin-top: 4px;
+ margin-left: 5px;
+ transition: all 0.3s ease;
+ text-decoration: none;
- &:hover {
- background: var(--tags-item-hover);
- color: var(--accent-primary);
- border-color: var(--accent-primary);
- }
-
- &.active {
- background: var(--accent-primary);
- color: #fff;
- border-color: var(--accent-primary);
- box-shadow: 0 2px 8px rgba(37, 99, 235, 0.15);
- font-weight: 500;
-
- &::before {
- content: "";
- background: #fff;
- display: inline-block;
- width: 6px;
- height: 6px;
- border-radius: 50%;
+ .tags-view-item-title {
margin-right: 2px;
+ display: inline-block;
+ vertical-align: middle;
+ }
+
+ &:hover {
+ background-color: #f4f4f5;
+ }
+
+ &.active {
+ background-color: var(--el-color-primary, #42b983);
+ color: #fff;
+ border-color: var(--el-color-primary, #42b983);
+ box-shadow: none;
+ font-weight: 400;
+
+ &::before {
+ content: "";
+ background-color: #fff;
+ display: inline-block;
+ width: 8px;
+ height: 8px;
+ border-radius: 50%;
+ margin-right: 4px;
+ }
+
+ .tags-view-close {
+ color: #fff;
+ display: inline-block;
+ vertical-align: middle;
+ &:hover {
+ background-color: rgba(255, 255, 255, 0.3);
+ }
+ }
+ }
+ }
+ }
+
+ .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, 0.3);
+
+ li {
+ margin: 0;
+ padding: 7px 16px;
+ cursor: pointer;
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ transition: all 0.2s ease;
+ white-space: nowrap;
+
+ .el-icon {
+ font-size: 14px;
+ }
+
+ &:hover {
+ background: #eee;
}
}
}
}
-
- .contextmenu {
- margin: 0;
- background: #fff;
- z-index: 3000;
- position: absolute;
- list-style-type: none;
- padding: 4px 0;
- border-radius: var(--radius-md);
- font-size: 12px;
- color: var(--text-secondary);
- box-shadow: var(--shadow-md);
- border: 1px solid var(--surface-border);
-
- li {
- margin: 0;
- padding: 8px 16px;
- cursor: pointer;
- display: flex;
- align-items: center;
- gap: 8px;
- transition: all 0.2s ease;
- white-space: nowrap;
-
- svg {
- width: 14px;
- height: 14px;
- color: inherit;
- }
-
- &:hover {
- background: #f1f5f9;
- color: var(--accent-primary);
- }
- }
- }
-}
</style>
<style lang="scss">
-//reset element css of el-icon-close
-.tags-view-wrapper {
- .el-scrollbar__view {
- display: flex;
- align-items: center;
- }
-
- .tags-view-item {
- .tags-view-close {
- display: inline-flex;
+ //reset element css of el-icon-close
+ .tags-view-wrapper {
+ .el-scrollbar__view {
+ display: flex;
align-items: center;
- justify-content: center;
- width: 12px;
- height: 12px;
- line-height: 1;
- align-self: center;
- transform: translateY(1px);
+ height: 34px;
}
- .el-icon-close {
- display: inline-flex;
- align-items: center;
- justify-content: center;
- width: 12px;
- height: 12px;
- line-height: 1;
- vertical-align: initial !important;
- border-radius: 50%;
- text-align: center;
- transition: all .3s cubic-bezier(.645, .045, .355, 1);
- transform-origin: 100% 50%;
- align-self: center;
-
- &:before {
- transform: scale(.6);
+ .tags-view-item {
+ .tags-view-close {
display: inline-flex;
align-items: center;
justify-content: center;
+ width: 14px;
+ height: 14px;
+ line-height: 1;
+ align-self: center;
+ transform: translateY(0px);
+ margin-left: 2px;
}
- svg {
- display: block;
- width: 10px;
- height: 10px;
- }
+ .el-icon-close {
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ width: 14px;
+ height: 14px;
+ line-height: 1;
+ vertical-align: middle;
+ border-radius: 50%;
+ text-align: center;
+ transition: all 0.3s cubic-bezier(0.645, 0.045, 0.355, 1);
+ transform-origin: 100% 50%;
+ align-self: center;
- &:hover {
- background-color: var(--tags-close-hover, #b4bccc);
- color: #fff;
+ &:before {
+ transform: scale(0.8);
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ }
+
+ svg {
+ display: block;
+ width: 10px;
+ height: 10px;
+ }
+
+ &:hover {
+ background-color: var(--tags-close-hover, #b4bccc);
+ color: #fff;
+ }
}
}
}
-}
</style>
diff --git a/src/layout/index.vue b/src/layout/index.vue
index 20f1232..4d39c3f 100644
--- a/src/layout/index.vue
+++ b/src/layout/index.vue
@@ -5,13 +5,28 @@
<div v-if="device === 'mobile' && sidebar.opened"
class="drawer-bg"
@click="handleClickOutside" />
- <sidebar v-if="!sidebar.hide"
+ <div :class="{ 'fixed-header': fixedHeader }"
+ class="top-header">
+ <navbar @setLayout="setLayout" />
+ </div>
+ <sidebar v-if="!sidebar.hide && (!settingsStore.topNav || device === 'mobile')"
class="sidebar-container" />
<div :class="{ hasTagsView: showTagsView, sidebarHide: sidebar.hide }"
class="main-container main-layout">
- <div :class="{ 'fixed-header': fixedHeader, 'with-tags': showTagsView }">
- <navbar @setLayout="setLayout" />
+ <div v-if="showTagsView"
+ class="tags-view-wrapper"
+ :class="{ 'fixed-tags-view': fixedHeader }">
<tags-view />
+ </div>
+ <div class="breadcrumb-wrapper"
+ :class="{ 'fixed-breadcrumb': fixedHeader }">
+ <hamburger v-if="!sidebar.hide && (!settingsStore.topNav || device === 'mobile')"
+ id="hamburger-container"
+ :is-active="sidebar.opened"
+ class="hamburger-container"
+ @toggleClick="toggleSideBar" />
+ <breadcrumb id="breadcrumb-container"
+ class="breadcrumb-container" />
</div>
<app-main />
<settings ref="settingRef" />
@@ -25,6 +40,8 @@
import { useRoute } from "vue-router";
import Sidebar from "./components/Sidebar/index.vue";
import { AppMain, Navbar, Settings, TagsView } from "./components";
+ import Breadcrumb from "@/components/Breadcrumb";
+ import Hamburger from "@/components/Hamburger";
import AIChatSidebar from "@/components/AIChatSidebar/index.vue";
import defaultSettings from "@/settings";
@@ -34,17 +51,16 @@
import useTagsViewStore from "@/store/modules/tagsView";
const settingsStore = useSettingsStore();
+ const appStore = useAppStore();
const tagsViewStore = useTagsViewStore();
const userStore = useUserStore();
const route = useRoute();
const theme = computed(() => settingsStore.theme);
const sideTheme = computed(() => settingsStore.sideTheme);
- const sidebar = computed(() => useAppStore().sidebar);
- const device = computed(() => useAppStore().device);
+ const sidebar = computed(() => appStore.sidebar);
+ const device = computed(() => appStore.device);
const needTagsView = computed(() => settingsStore.tagsView);
- const showTagsView = computed(
- () => needTagsView.value && tagsViewStore.visitedViews.length > 1
- );
+ const showTagsView = computed(() => needTagsView.value);
const fixedHeader = computed(() => settingsStore.fixedHeader);
const aiEnabled = computed(() => Number(userStore.aiEnabled) === 1);
const showGlobalAiChat = computed(() => {
@@ -68,22 +84,35 @@
() => device.value,
() => {
if (device.value === "mobile" && sidebar.value.opened) {
- useAppStore().closeSideBar({ withoutAnimation: false });
+ appStore.closeSideBar({ withoutAnimation: false });
}
}
);
watchEffect(() => {
if (width.value - 1 < WIDTH) {
- useAppStore().toggleDevice("mobile");
- useAppStore().closeSideBar({ withoutAnimation: true });
+ appStore.toggleDevice("mobile");
+ appStore.closeSideBar({ withoutAnimation: true });
} else {
- useAppStore().toggleDevice("desktop");
+ appStore.toggleDevice("desktop");
+ }
+ });
+
+ watchEffect(() => {
+ if (settingsStore.topNav && device.value !== "mobile") {
+ appStore.toggleSideBarHide(true);
+ appStore.closeSideBar({ withoutAnimation: true });
+ } else {
+ appStore.toggleSideBarHide(false);
}
});
function handleClickOutside() {
- useAppStore().closeSideBar({ withoutAnimation: false });
+ appStore.closeSideBar({ withoutAnimation: false });
+ }
+
+ function toggleSideBar() {
+ appStore.toggleSideBar();
}
const settingRef = ref(null);
@@ -113,8 +142,18 @@
width: 100%;
top: 0;
height: 100%;
- position: absolute;
- z-index: 999;
+ position: fixed;
+ z-index: 1003;
+ }
+
+ .top-header {
+ width: 100%;
+ z-index: 1002;
+ }
+
+ .top-header.fixed-header {
+ position: sticky;
+ top: 0;
}
.main-layout {
@@ -123,26 +162,86 @@
transition: margin-left 0.25s ease;
display: flex;
flex-direction: column;
- padding: 0;
}
- .fixed-header {
- position: sticky;
- top: 0;
- z-index: var(--layout-header-z);
+ .breadcrumb-wrapper {
width: 100%;
- padding: 0;
- background: #fff;
+ padding: 10px 24px;
+ background: transparent;
display: flex;
- flex-direction: column;
- gap: 2px; // 鍦� Navbar 鍜� TagsView 涔嬮棿澧炲姞鏋佸皬鐨勭┖闅�
+ align-items: center;
+ gap: 12px;
}
- .fixed-header.with-tags {
- padding-bottom: 4px; // 搴曢儴鐣欏嚭涓�鐐硅窛绂伙紝涓嶈鐩存帴璐寸潃涓讳綋鍐呭
+ .hamburger-container {
+ height: 32px;
+ width: 32px;
+ border-radius: var(--radius-sm);
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ color: var(--text-secondary);
+ cursor: pointer;
+ transition: all 0.2s ease;
+ flex-shrink: 0;
+
+ &:hover {
+ background: rgba(0, 0, 0, 0.05);
+ color: var(--el-color-primary);
+ }
}
- .hideSidebar .fixed-header {
+ .breadcrumb-container {
+ min-width: 0;
+
+ :deep(.el-breadcrumb__inner) {
+ color: var(--text-secondary) !important;
+ opacity: 0.85;
+
+ &:hover {
+ color: var(--el-color-primary) !important;
+ opacity: 1;
+ }
+
+ a {
+ color: inherit !important;
+ font-weight: 500 !important;
+ }
+ }
+
+ :deep(.no-redirect) {
+ color: var(--text-primary) !important;
+ font-weight: 600 !important;
+ opacity: 1;
+ }
+
+ :deep(.el-breadcrumb__separator) {
+ color: var(--text-tertiary);
+ opacity: 0.5;
+ }
+ }
+
+ .fixed-breadcrumb {
+ position: sticky;
+ top: var(--topbar-height);
+ z-index: calc(var(--layout-header-z) - 1);
+ background: var(--app-bg);
+ }
+
+ .hasTagsView {
+ .fixed-breadcrumb {
+ top: calc(var(--topbar-height) + var(--tagsbar-height));
+ }
+ }
+
+ .tags-view-wrapper.fixed-tags-view {
+ position: sticky;
+ top: var(--topbar-height);
+ z-index: calc(var(--layout-header-z) - 1);
+ background: var(--app-bg);
+ }
+
+ .hideSidebar .breadcrumb-wrapper {
width: 100%;
}
@@ -150,9 +249,8 @@
margin-left: var(--sidebar-collapsed-width);
}
- .mobile .fixed-header {
+ .mobile .top-header.fixed-header {
width: 100%;
- padding: 8px 10px 0;
}
.mobile .main-layout,
diff --git a/src/settings.js b/src/settings.js
index 276b2f6..678de16 100644
--- a/src/settings.js
+++ b/src/settings.js
@@ -15,7 +15,7 @@
/**
* 鏄惁鏄剧ず椤堕儴瀵艰埅
*/
- topNav: false,
+ topNav: true,
/**
* 鏄惁鏄剧ず tagsView
diff --git a/src/store/modules/settings.js b/src/store/modules/settings.js
index 226fcac..cbaa912 100644
--- a/src/store/modules/settings.js
+++ b/src/store/modules/settings.js
@@ -17,7 +17,7 @@
const useSettingsStore = defineStore("settings", () => {
const title = ref("");
- const theme = ref(storageSetting.theme || "#374D77");
+ const theme = ref(storageSetting.theme || "#008C8C");
const sideThemeValue = ref(storageSetting.sideTheme || sideTheme);
const showSettingsValue = ref(showSettings);
const topNavValue = ref(storageSetting.topNav === undefined ? topNav : storageSetting.topNav);
diff --git a/src/views/customerService/components/viewDia.vue b/src/views/customerService/components/viewDia.vue
index 92833de..262151a 100644
--- a/src/views/customerService/components/viewDia.vue
+++ b/src/views/customerService/components/viewDia.vue
@@ -154,7 +154,7 @@
transform: translateY(-50%);
width: 4px;
height: 1rem;
- background-color: #374d77;
+ background-color: #008c8c;
border-radius: 2px;
}
</style>
diff --git a/src/views/customerService/feedbackRegistration/components/formDia.vue b/src/views/customerService/feedbackRegistration/components/formDia.vue
index 0db27c1..a26d065 100644
--- a/src/views/customerService/feedbackRegistration/components/formDia.vue
+++ b/src/views/customerService/feedbackRegistration/components/formDia.vue
@@ -522,7 +522,7 @@
transform: translateY(-50%);
width: 4px;
height: 1rem;
- background-color: #374d77; /* Element 榛樿绾㈣壊 */
+ background-color: #008c8c; /* Element 榛樿绾㈣壊 */
border-radius: 2px;
}
</style>
diff --git a/src/views/login.vue b/src/views/login.vue
index d8c9f22..07a6697 100644
--- a/src/views/login.vue
+++ b/src/views/login.vue
@@ -463,7 +463,7 @@
<style scoped lang="scss">
.login-page {
- --accent: var(--accent-primary, var(--el-color-primary, #374d77));
+ --accent: var(--accent-primary, var(--el-color-primary, #008c8c));
--accent-rgb: var(--el-color-primary-rgb, 22, 116, 88);
--text: #0f172a;
--muted: #64748b;
--
Gitblit v1.9.3