You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

408 lines
11 KiB

3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
2 weeks ago
2 weeks ago
2 weeks ago
3 months ago
3 weeks ago
3 weeks ago
3 weeks ago
  1. <script setup>
  2. // 导航栏在这
  3. import { computed, ref } from 'vue'
  4. import { useRoute, useRouter } from 'vue-router'
  5. import { ElMessage } from 'element-plus'
  6. import dmmn from '../assets/link.png'
  7. import ChangePassword from '@/components/changePassword.vue'
  8. import { useAdminStore } from '@/store'
  9. import { storeToRefs } from 'pinia'
  10. import { filterMenu, getRoutePath } from "@/utils/menuUtils.js";
  11. import SettingsIcon from '@/assets/blue.png';
  12. // 存储接口返回的菜单数据
  13. const menuList = ref([])
  14. // 获取仓库实例
  15. const adminStore = useAdminStore()
  16. // 解构状态(保持响应式) 获得 adminData(用户信息) 和 menuTree(菜单树)
  17. const { adminData, menuTree } = storeToRefs(adminStore)
  18. // 筛选权限菜单 ,menuTree 是组件通信拿的
  19. menuList.value = filterMenu(menuTree.value)
  20. console.log("menuList", menuList.value)
  21. // 获取当前路由
  22. const route = useRoute()
  23. // 通用函数:从菜单树中递归找出最匹配的 index
  24. function findBestMatch(menuList, path) {
  25. let bestMatch = ''
  26. function traverse(menus) {
  27. for (const item of menus) {
  28. const itemPath = getRoutePath(item)
  29. // 如果当前菜单的 path 是当前路径的前缀,可能是候选项
  30. if (path.startsWith(itemPath) && itemPath.length > bestMatch.length) {
  31. bestMatch = itemPath
  32. }
  33. if (item.children && item.children.length > 0) {
  34. traverse(item.children)
  35. }
  36. }
  37. }
  38. traverse(menuList)
  39. return bestMatch || path // fallback 到当前路径
  40. }
  41. // 获取菜单图标(require动态导入)
  42. // 响应式高亮菜单
  43. const activeMenu = computed(() => {
  44. return findBestMatch(menuList.value, route.path)
  45. })
  46. const router = useRouter()
  47. const imgrule1 = dmmn
  48. const messageVisible = ref(false)
  49. // 查看个人信息弹出框
  50. const openMessage = function () {
  51. messageVisible.value = true
  52. }
  53. // 关闭个人信息
  54. const closeMessage = function () {
  55. messageVisible.value = false
  56. }
  57. const message = function () {
  58. openMessage()
  59. }
  60. // 显示修改密码弹窗
  61. const showPasswordDialog = ref(false)
  62. const pwdRef = ref()
  63. //打开修改密码弹窗
  64. const openChangePassword = () => {
  65. showPasswordDialog.value = true
  66. }
  67. //关闭后清空密码表单
  68. function onPwdDialogClosed() {
  69. // 调用子组件暴露的 resetFields
  70. pwdRef.value?.resetFields()
  71. }
  72. function logout() {
  73. const machineId = localStorage.getItem('machineId')
  74. localStorage.removeItem('token')
  75. adminStore.clearState()
  76. router.push('/login?machineId=' + machineId)
  77. ElMessage.success('退出成功')
  78. }
  79. </script>
  80. <template>
  81. <div class="main-container">
  82. <!-- 背景毛玻璃层作为内容容器 -->
  83. <div class="background-glass">
  84. <!-- 侧边栏 -->
  85. <div class="sidebar-container">
  86. <el-aside class="sidebar-layout">
  87. <div class="logo">
  88. <img src="../assets/新logo.png" alt="logo" style="width: 9vh; height: 9vh" />
  89. </div>
  90. <div class="menu-scroll-container">
  91. <el-menu :router="true" :default-active="activeMenu" style="min-height: 80vh;border:none;">
  92. <!-- 递归渲染菜单层级 -->
  93. <template v-for="menu in menuList" :key="menu.id">
  94. <!-- 有子菜单的父级菜单menuType=2 且存在children -->
  95. <el-sub-menu v-if="menu.children && menu.children.length > 0" :index="menu.id.toString()">
  96. <template #title>
  97. <img
  98. :src="`/src/assets/SvgIcons/${menu.menuName}.svg`"
  99. :alt="`${menu.menuName}图标`"
  100. style="width: 4vh; height: 4vh; margin-right: 4px;"
  101. >
  102. <span>{{ menu.menuName }}</span>
  103. </template>
  104. <!-- 子菜单 -->
  105. <template v-for="child in menu.children" :key="child.id">
  106. <!-- 子菜单为叶子节点无children -->
  107. <el-menu-item v-if="!child.children || child.children.length === 0" :index="getRoutePath(child)">
  108. <el-icon style="margin-right: 4px;">
  109. <Folder />
  110. </el-icon>
  111. <span>{{ child.menuName }}</span>
  112. </el-menu-item>
  113. <!-- 子菜单有下级 -->
  114. <el-sub-menu v-else :index="child.id.toString()">
  115. <template #title>
  116. <el-icon style="margin-right: 4px;">
  117. <Folder />
  118. </el-icon>
  119. <span>{{ child.menuName }}</span>
  120. </template>
  121. <!-- 递归 下一级-->
  122. <template v-for="grandChild in child.children" :key="grandChild.id">
  123. <el-menu-item :index="getRoutePath(grandChild)">
  124. <el-icon style="margin-right: 4px;">
  125. <Folder />
  126. </el-icon>
  127. <span>{{ grandChild.menuName }}</span>
  128. </el-menu-item>
  129. </template>
  130. </el-sub-menu>
  131. </template>
  132. </el-sub-menu>
  133. <!-- 无子菜单的一级菜单 -->
  134. <el-menu-item v-else :index="getRoutePath(menu)">
  135. <img
  136. :src="`@/assets/SvgIcons/${menu.menuName}.svg`"
  137. :alt="`${menu.menuName}图标`"
  138. style="width: 4vh; height: 4vh; margin-right: 4px;"
  139. >
  140. <span>{{ menu.menuName }}</span>
  141. </el-menu-item>
  142. </template>
  143. </el-menu>
  144. </div>
  145. <!-- 底部固定的设置中心 -->
  146. <div class="settings-container">
  147. <el-dropdown placement="top-start">
  148. <span class="el-dropdown-link">
  149. <img src="@/assets/SvgIcons/设置.svg" alt="设置" style="width: 4vh; height: 4vh" />
  150. <span>设置中心</span>
  151. <el-icon class="arrow-icon">
  152. <ArrowUp />
  153. </el-icon>
  154. </span>
  155. <template #dropdown>
  156. <el-dropdown-menu>
  157. <el-dropdown-item @click="message()">查看个人信息</el-dropdown-item>
  158. <el-dropdown-item @click="openChangePassword">修改密码</el-dropdown-item>
  159. <el-dropdown-item @click="logout">退出登录</el-dropdown-item>
  160. </el-dropdown-menu>
  161. </template>
  162. </el-dropdown>
  163. </div>
  164. </el-aside>
  165. </div>
  166. <!-- 右侧内容区域 -->
  167. <div class="content-container">
  168. <!-- 头部
  169. <el-header class="header">
  170. </el-header> -->
  171. <!-- 主内容区域 -->
  172. <div class="main-area">
  173. <el-main>
  174. <router-view></router-view>
  175. </el-main>
  176. </div>
  177. </div>
  178. </div>
  179. <!-- 查看个人信息 -->
  180. <el-dialog v-model="messageVisible" title="查看个人信息" width="500px">
  181. <el-form :model="adminData">
  182. <el-form-item label="用户姓名" label-width="100px" label-position="left">
  183. <span class="message-font">{{ adminData.adminName }}</span>
  184. </el-form-item>
  185. <el-form-item label="精网号" label-width="100px" label-position="left">
  186. <span class="message-font">{{ adminData.account }}</span>
  187. </el-form-item>
  188. <el-form-item label="地区" label-width="100px" label-position="left">
  189. <span class="message-font">{{ adminData.markets }}</span>
  190. </el-form-item>
  191. <el-form-item label="注册时间" label-width="100px" label-position="left">
  192. <span class="message-font">{{ adminData.createTime }}</span>
  193. </el-form-item>
  194. </el-form>
  195. <template #footer>
  196. <div>
  197. <el-button text @click="closeMessage()">关闭</el-button>
  198. </div>
  199. </template>
  200. </el-dialog>
  201. <!-- 自定义密码修改弹窗组件 -->
  202. <el-dialog v-model="showPasswordDialog" :center="true" width="470px" @closed="onPwdDialogClosed">
  203. <ChangePassword ref="pwdRef" @confirm="showPasswordDialog = false" />
  204. </el-dialog>
  205. </div>
  206. </template>
  207. <style scoped>
  208. /* 主容器,设置背景图并居中 */
  209. .main-container {
  210. position: fixed;
  211. top: 0;
  212. left: 0;
  213. right: 0;
  214. bottom: 0;
  215. background-image: url('@/assets/backgroundBlue.png');
  216. background-size: cover;
  217. background-position: center center;
  218. background-repeat: no-repeat;
  219. overflow: hidden;
  220. }
  221. /* 背景毛玻璃层(作为内容容器) */
  222. .background-glass {
  223. position: absolute;
  224. top: 1vh;
  225. left: 1vh;
  226. right: 1vh;
  227. bottom: 1vh;
  228. background-image: url('@/assets/半透明background.png');
  229. background-size: cover;
  230. z-index: 1;
  231. display: flex;
  232. flex-direction: row;
  233. padding: 10px;
  234. border-radius: 12px;
  235. }
  236. /* 侧边栏容器 */
  237. .sidebar-container {
  238. flex-shrink: 0;
  239. }
  240. .logo {
  241. display: flex;
  242. align-items: center;
  243. justify-content: center;
  244. height: 12vh;
  245. }
  246. /* 中间可滚动菜单容器 */
  247. .menu-scroll-container {
  248. flex: 1;
  249. overflow-y: auto;
  250. padding: 10px 0;
  251. }
  252. /* 底部设置中心样式 */
  253. .settings-container {
  254. padding: 10px 0 10px 20px; /* 上,右, 下,左 */
  255. display: flex;
  256. align-items: center; /* 垂直居中 */
  257. }
  258. /* 调整下拉菜单的样式,确保它向上弹出 */
  259. .el-dropdown-link:focus {
  260. /* 移除底部的异常效果 */
  261. outline: none;
  262. text-decoration: none;
  263. }
  264. .el-dropdown-link {
  265. display: flex;
  266. align-items: center;
  267. cursor: pointer;
  268. gap:10px; /* 图标和文字左右间距 */
  269. }
  270. .sidebar-layout {
  271. width: 15vw;
  272. height: 100%;
  273. background: #E7F4FD; /* 浅蓝色背景 */
  274. /* backdrop-filter: blur(5px); 毛玻璃效果 --消耗性能 */
  275. box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1); /* 添加阴影增强层次感 */
  276. border-radius: 12px;
  277. display: flex;
  278. flex-direction: column;
  279. position: relative;
  280. transition: all 0.3s ease;
  281. }
  282. /* 内容区域容器 */
  283. .content-container {
  284. flex: 1;
  285. display: flex;
  286. flex-direction: column;
  287. margin-left: 5px;
  288. gap: 5px;
  289. height: 100%;
  290. overflow: hidden;
  291. }
  292. /* 主内容区域容器 */
  293. .main-area {
  294. flex: 1;
  295. background: #E7F4FD;
  296. /* 半透明浅色背景 */
  297. /* backdrop-filter: blur(5px); */
  298. /* 毛玻璃效果 */
  299. border-radius: 12px;
  300. box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1);
  301. /* 添加阴影增强层次感 */
  302. overflow: hidden;
  303. display: flex;
  304. flex-direction: column;
  305. }
  306. /* 主内容区域样式 */
  307. .el-main {
  308. height: 100%;
  309. padding: 20px;
  310. background: transparent;
  311. overflow-y: hidden;
  312. /* 应用自定义滚动条 */
  313. }
  314. /* 确保el-menu撑满容器 */
  315. .sidebar-layout .el-menu {
  316. width: 100%;
  317. }
  318. /* 侧边栏菜单样式 适配浅色背景 */
  319. .el-menu {
  320. background: transparent !important;
  321. }
  322. /* 工作台,金币管理,现金管理 */
  323. ::v-deep(.el-sub-menu__title:hover),
  324. ::v-deep(.el-menu-item:hover) {
  325. background: #E5EBFE;
  326. }
  327. /* 子菜单展开时和背景同色 */
  328. ::v-deep(.el-sub-menu__title),
  329. ::v-deep(.el-menu-item) {
  330. background: #E7F4FD;
  331. }
  332. .message-font {
  333. /* 个人信息字体样式 */
  334. font-size: 16px;
  335. font-weight: bold;
  336. }
  337. /* 确保全局el-container适应容器 */
  338. :deep(.el-container) {
  339. /* vue3的深度选择器,用于覆盖element-plus的默认样式 */
  340. min-height: 100%;
  341. width: 100%;
  342. background: transparent;
  343. }
  344. /* 为侧边栏和主内容区域添加滚动条样式 */
  345. .menu-scroll-container,
  346. .el-main {
  347. scrollbar-width: thin;
  348. /* Firefox */
  349. scrollbar-color: rgba(0, 0, 0, 0.3) rgba(255, 255, 255, 0.2);
  350. /* Firefox滑块和轨道颜色 */
  351. }
  352. </style>