Background.vue 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322
  1. <template>
  2. <el-container class="admin-layout">
  3. <el-aside :width="collapsed ? '64px' : '240px'" class="aside-container">
  4. <div class="logo-section">
  5. <router-link to="/bg" class="logo-link">
  6. <img class="logo-img" src="@/assets/img/logo.png" alt="logo">
  7. <transition name="logo-fade">
  8. <h1 v-show="!collapsed" class="logo-text">用户后台</h1>
  9. </transition>
  10. </router-link>
  11. </div>
  12. <el-menu
  13. :default-active="$route.path"
  14. router
  15. class="admin-menu"
  16. background-color="#0d131a"
  17. text-color="rgba(255, 255, 255, 0.65)"
  18. active-text-color="#ffffff"
  19. :unique-opened="true"
  20. :collapse="collapsed"
  21. :collapse-transition="false"
  22. >
  23. <el-submenu v-for="item in menuList" :key="item.path" :index="item.path">
  24. <template slot="title">
  25. <i :class="[item.icon, 'menu-icon']" />
  26. <span class="menu-title-text">{{ item.title }}</span>
  27. </template>
  28. <el-menu-item v-for="child in item.children" :key="child.path" :index="child.path">
  29. <i :class="[child.icon, 'menu-icon-child']" />
  30. <span slot="title" class="menu-title-text">{{ child.title }}</span>
  31. </el-menu-item>
  32. </el-submenu>
  33. </el-menu>
  34. </el-aside>
  35. <el-container class="content-container">
  36. <el-header class="admin-header">
  37. <div class="header-left">
  38. <el-button
  39. class="toggle-btn"
  40. :icon="collapsed ? 'el-icon-s-unfold' : 'el-icon-s-fold'"
  41. @click="collapsed = !collapsed"
  42. />
  43. </div>
  44. <div class="header-right">
  45. <div class="icon-item-wrapper" @click="goToMessage">
  46. <el-badge :value="msgCount" :max="99" :hidden="msgCount <= 0" class="badge-item">
  47. <i class="el-icon-message-solid custom-icon" />
  48. </el-badge>
  49. <span class="icon-label">消息</span>
  50. </div>
  51. <el-dropdown trigger="hover" class="user-dropdown">
  52. <div class="user-info">
  53. <el-avatar
  54. :size="28"
  55. :src="user && user.avatarUrl ? user.avatarUrl : ''"
  56. icon="el-icon-user-solid"
  57. class="mgr-8"
  58. />
  59. <span class="username">{{ user ? user.username : '管理员' }}</span>
  60. <i class="el-icon-arrow-down el-icon--right" />
  61. </div>
  62. <el-dropdown-menu slot="dropdown">
  63. <el-dropdown-item icon="el-icon-s-home" @click.native="backToHome">
  64. 回到前台
  65. </el-dropdown-item>
  66. <el-dropdown-item icon="el-icon-switch-button" divided @click.native="goToLogout">
  67. 退出登录
  68. </el-dropdown-item>
  69. </el-dropdown-menu>
  70. </el-dropdown>
  71. </div>
  72. </el-header>
  73. <el-main class="admin-main">
  74. <div class="main-content-card">
  75. <router-view />
  76. </div>
  77. </el-main>
  78. </el-container>
  79. </el-container>
  80. </template>
  81. <script>
  82. import store from '@/store'
  83. import { userMixin } from 'assets/js/mixin'
  84. import { getAuthedUser } from '@/utils/auth'
  85. import { getUnreadCount } from '@/api/user'
  86. export default {
  87. name: 'Background',
  88. mixins: [userMixin],
  89. data() {
  90. return {
  91. collapsed: false,
  92. user: null,
  93. menuList: [],
  94. msgCount: 0
  95. }
  96. },
  97. created() {
  98. this.user = getAuthedUser()
  99. if (this.user !== null) {
  100. getUnreadCount().then(resp => {
  101. if (resp.code === 0) {
  102. this.msgCount = resp.data.total
  103. }
  104. })
  105. }
  106. this.initMenus()
  107. },
  108. methods: {
  109. initMenus() {
  110. const routes = store.getters.addRoutes || []
  111. routes.forEach(route => {
  112. if (route.path === '/bg') {
  113. if (route.children) this.menuList.push(...route.children)
  114. } else if (route.path.startsWith('/bg/')) {
  115. this.menuList.push(route)
  116. }
  117. })
  118. },
  119. backToHome() {
  120. this.$router.push('/')
  121. },
  122. goToMessage() {
  123. this.$router.push('/bg/account/message')
  124. }
  125. }
  126. }
  127. </script>
  128. <style scoped>
  129. .admin-layout {
  130. height: 100vh;
  131. width: 100%;
  132. }
  133. /* ==================== 🌸 现代化侧边栏重塑 ==================== */
  134. .aside-container {
  135. background-color: #0d131a; /* 更高级的黑青色,远离沉闷死黑 */
  136. transition: width 0.25s cubic-bezier(0.3, 0.1, 0.3, 1);
  137. overflow: hidden;
  138. box-shadow: 4px 0 24px rgba(0, 0, 0, 0.15);
  139. display: flex;
  140. flex-direction: column;
  141. }
  142. .admin-menu {
  143. border-right: none;
  144. flex: 1;
  145. padding: 8px 0; /* 让菜单顶部有呼吸感 */
  146. }
  147. /* Logo 区域重绘 */
  148. .logo-section {
  149. height: 64px;
  150. display: flex;
  151. align-items: center;
  152. padding: 0 16px;
  153. background-color: #0d131a;
  154. border-bottom: 1px solid rgba(255, 255, 255, 0.04); /* 极细精致分界线 */
  155. box-sizing: border-box;
  156. }
  157. .logo-link {
  158. display: flex;
  159. align-items: center;
  160. text-decoration: none;
  161. width: 100%;
  162. padding-left: 4px;
  163. }
  164. .logo-img {
  165. height: 28px;
  166. width: 28px;
  167. object-fit: contain;
  168. filter: drop-shadow(0 2px 8px rgba(24, 144, 255, 0.3)); /* 让 Logo 微发光 */
  169. }
  170. .logo-text {
  171. color: #ffffff;
  172. font-size: 16px;
  173. margin-left: 12px;
  174. font-weight: 600;
  175. white-space: nowrap;
  176. letter-spacing: 0.5px;
  177. }
  178. /* Logo 丝滑文字动效 */
  179. .logo-fade-enter-active {
  180. transition: all 0.3s ease-out;
  181. }
  182. .logo-fade-leave-active {
  183. transition: all 0.1s ease-in;
  184. }
  185. .logo-fade-enter, .logo-fade-leave-to {
  186. opacity: 0;
  187. transform: translateX(-10px);
  188. }
  189. /* ==================== 🛠️ Element UI 菜单高级覆构 ==================== */
  190. /* 1. 基础间距与弧度微调 */
  191. ::v-deep .el-submenu,
  192. ::v-deep .el-menu-item {
  193. padding: 0 12px !important; /* 给左右空出间距,打造胶囊浮动感 */
  194. margin: 4px 0;
  195. }
  196. ::v-deep .el-submenu__title,
  197. ::v-deep .el-menu-item {
  198. height: 44px !important;
  199. line-height: 44px !important;
  200. border-radius: 6px !important; /* 统一小圆角 */
  201. margin: 2px 0;
  202. }
  203. /* 图标与文本精致感调整 */
  204. .menu-icon {
  205. font-size: 16px;
  206. margin-right: 12px !important;
  207. transition: transform 0.25s ease;
  208. }
  209. .menu-icon-child {
  210. font-size: 14px;
  211. margin-right: 12px !important;
  212. opacity: 0.7;
  213. }
  214. .menu-title-text {
  215. font-size: 14px;
  216. letter-spacing: 0.3px;
  217. }
  218. /* 2. 悬浮态(Hover)美化 */
  219. ::v-deep .el-submenu__title:hover,
  220. ::v-deep .el-menu-item:hover {
  221. color: #ffffff !important;
  222. background-color: rgba(255, 255, 255, 0.05) !important; /* 极其柔和的半透明浅白 */
  223. }
  224. ::v-deep .el-submenu__title:hover .menu-icon {
  225. transform: scale(1.08); /* 悬浮时图标微放大交互 */
  226. }
  227. /* 3. 子菜单展开后的深色背景(层级化) */
  228. ::v-deep .el-menu--inline {
  229. background-color: #06090d !important; /* 子菜单背景明显更深,营造内陷嵌套感 */
  230. border-radius: 6px;
  231. padding: 4px 0;
  232. margin-top: 2px;
  233. }
  234. /* 4. 核心:高难度现代高亮激活态(Floating Capsule & Side Indicator) */
  235. ::v-deep .el-menu-item.is-active {
  236. background-color: #1890ff !important; /* 主题蓝渐变基础色 */
  237. background: linear-gradient(135deg, #1890ff 0%, #0076e4 100%) !important; /* 微渐变极具质感 */
  238. color: #ffffff !important;
  239. font-weight: 500;
  240. box-shadow: 0 4px 12px rgba(24, 144, 255, 0.25); /* 胶囊发光悬浮感 */
  241. }
  242. ::v-deep .el-menu-item.is-active .menu-icon-child {
  243. opacity: 1;
  244. color: #ffffff !important;
  245. }
  246. /* 当子菜单项激活时,父级标题的亮白突出 */
  247. ::v-deep .el-submenu.is-active > .el-submenu__title {
  248. color: #ffffff !important;
  249. }
  250. ::v-deep .el-submenu.is-active > .el-submenu__title i {
  251. color: #1890ff !important; /* 父级图标变蓝 */
  252. }
  253. /* 5. 折叠状态(Collapse)特殊处理 */
  254. .admin-menu:not(.el-menu--collapse) {
  255. width: 240px;
  256. }
  257. ::v-deep .el-menu--collapse {
  258. width: 64px;
  259. }
  260. ::v-deep .el-menu--collapse .el-submenu,
  261. ::v-deep .el-menu--collapse .el-menu-item {
  262. padding: 0 8px !important; /* 折叠后更紧凑 */
  263. }
  264. ::v-deep .el-menu--collapse .menu-icon {
  265. margin-right: 0 !important;
  266. }
  267. /* ==================== 顶部和常规组件保持优秀样式 ==================== */
  268. .admin-header {
  269. background-color: #fff;
  270. padding: 0 20px;
  271. height: 64px !important;
  272. display: flex;
  273. justify-content: space-between;
  274. align-items: center;
  275. box-shadow: 0 1px 4px rgba(0, 21, 41, 0.05);
  276. z-index: 10;
  277. }
  278. .toggle-btn {
  279. font-size: 20px;
  280. border: none;
  281. background: transparent;
  282. color: #333;
  283. padding: 8px;
  284. cursor: pointer;
  285. }
  286. .toggle-btn:hover { color: #1890ff; }
  287. .header-right { display: flex; align-items: center; gap: 8px; }
  288. .user-info { display: flex; align-items: center; padding: 0 12px; height: 64px; cursor: pointer; transition: all 0.3s; }
  289. .user-info:hover { background-color: rgba(0, 0, 0, 0.025); }
  290. .username { font-size: 14px; font-weight: 500; color: rgba(0, 0, 0, 0.65); }
  291. .mgr-8 { margin-right: 8px; }
  292. .admin-main { background-color: #f4f7f9; padding: 24px; }
  293. .main-content-card { background: #fff; min-height: calc(100vh - 112px); border-radius: 8px; padding: 24px; box-shadow: 0 1px 3px rgba(0,0,0,0.02); }
  294. .icon-item-wrapper { display: flex; align-items: center; justify-content: center; gap: 6px; height: 64px; padding: 0 16px; cursor: pointer; transition: all 0.3s; border-radius: 4px; }
  295. .icon-item-wrapper:hover { background-color: rgba(0, 0, 0, 0.025); }
  296. .icon-item-wrapper:hover .custom-icon, .icon-item-wrapper:hover .icon-label { color: #1890ff; }
  297. .custom-icon { font-size: 18px; color: rgba(0, 0, 0, 0.65); transition: color 0.3s; }
  298. .icon-label { font-size: 14px; font-weight: 500; color: rgba(0, 0, 0, 0.65); transition: color 0.3s; }
  299. .badge-item ::v-deep .el-badge__content { font-family: sans-serif; border: 1px solid #fff; top: 2px; right: 2px; }
  300. </style>