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.

482 lines
14 KiB

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