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.

383 lines
10 KiB

1 month ago
1 month ago
3 weeks ago
1 month ago
  1. <script setup>
  2. // 导航栏在这
  3. import {onMounted, ref} from 'vue'
  4. import {useRoute, useRouter} from 'vue-router'
  5. import {ElMessage} from 'element-plus'
  6. import API from '@/util/http'
  7. import dmmn from '../assets/link.png'
  8. import moment from 'moment'
  9. import ChangePassword from '@/components/changePassword.vue'
  10. // 获取当前路由实例
  11. const route = useRoute()
  12. // 存储接口返回的菜单数据
  13. const menuList = ref([])
  14. /**
  15. * 调用 /menu/tree 接口获取菜单数据
  16. */
  17. const fetchMenuTree = async function() {
  18. try {
  19. const result = await API({ url: '/menu/tree', data: {id: adminData.value.roleId} })
  20. return result.data // 直接返回接口响应数据
  21. } catch (error) {
  22. console.error('菜单数据请求失败:', error)
  23. return { code: 500, msg: '获取菜单失败' }
  24. }
  25. }
  26. function filterMenu(menuList) {
  27. return menuList
  28. .filter(menu => menu.menuType !== 4)
  29. .map(menu => ({
  30. ...menu,
  31. children: menu.children ? filterMenu(menu.children) : []
  32. }))
  33. .sort((a, b) => a.priority - b.priority); // 按 id 升序
  34. }
  35. /**
  36. * 映射菜单名称到路由路径
  37. */
  38. const getRoutePath = (menu) => {
  39. // 路由映射表:key为接口menuName,value为对应路由路径
  40. const routeMap = {
  41. '工作台': '/workspace',
  42. '审核页面': '/audit',
  43. '财务审核': '/audit',
  44. '充值审核': '/rechargeAudit',
  45. '退款审核': '/refundAudit',
  46. '汇率管理': '/rate',
  47. '消耗管理': '/coinConsume',
  48. '消耗页面': '/coinConsume',
  49. '权限管理': '/permissions',
  50. '充值管理': '/coinRecharge',
  51. '充值页面': '/coinRecharge',
  52. '退款管理': '/coinRefund',
  53. '退款页面': '/coinRefund',
  54. '客户账户明细': '/usergold',
  55. };
  56. // 未匹配的菜单默认使用id作为路由(可根据实际需求调整)
  57. return routeMap[menu.menuName] || '/workspace'
  58. }
  59. const router = useRouter()
  60. const imgrule1 = dmmn
  61. const messageVisible = ref(false)
  62. // 这是获取用户信息的接口
  63. const adminData = ref({
  64. name: ''
  65. })
  66. const getAdminData = async function () {
  67. try {
  68. const result = await API({ url: '/admin/userinfo', data: {} })
  69. adminData.value = result
  70. console.log('请求成功', result)
  71. console.log('用户信息', adminData.value)
  72. } catch (error) {
  73. console.log('请求失败', error)
  74. }
  75. }
  76. // 获取地区
  77. const areas = ref([])
  78. const currentArea = ref('全部')
  79. const getAreas = async function () {
  80. try {
  81. const result = await API({ url: '/general/adminMarkets', data: {
  82. account: adminData.value.account,
  83. } })
  84. areas.value = result.data
  85. console.log('请求成功', result)
  86. } catch (error) {
  87. console.log('请求失败', error)
  88. }
  89. }
  90. // 查看个人信息弹出框
  91. const openMessage = function () {
  92. messageVisible.value = true
  93. }
  94. const closeMessage = function () {
  95. messageVisible.value = false
  96. }
  97. const message = function () {
  98. openMessage()
  99. }
  100. // 导出列表数据
  101. const exportList = ref([])
  102. // 导出列表加载状态
  103. const exportListLoading = ref(false)
  104. //根据状态返回对应的标签类型
  105. const getTagType = (state) => {
  106. switch (state) {
  107. case 0:
  108. return 'info';
  109. case 1:
  110. return 'primary';
  111. case 2:
  112. return'success';
  113. case 3:
  114. return 'danger';
  115. default:
  116. return 'info';
  117. }
  118. }
  119. //根据状态返回对应的标签文案
  120. const getTagText = (state) => {
  121. switch (state) {
  122. case 0:
  123. return '待执行';
  124. case 1:
  125. return '执行中';
  126. case 2:
  127. return'执行完成';
  128. case 3:
  129. return '执行出错';
  130. default:
  131. return '未知状态';
  132. }
  133. }
  134. // 下载导出文件
  135. const downloadExportFile = (item) => {
  136. if (item.state === 2) {
  137. const link = document.createElement('a')
  138. link.href = item.url
  139. link.download = item.fileName
  140. link.click()
  141. } else {
  142. ElMessage.warning('文件还在导出中,请稍后再试')
  143. }
  144. }
  145. function logout() {
  146. const machineId = localStorage.getItem('machineId')
  147. localStorage.removeItem('token')
  148. router.push('/login?machineId=' + machineId)
  149. ElMessage.success('退出成功')
  150. }
  151. // 挂载
  152. onMounted(async function () {
  153. // 获取用户信息
  154. await getAdminData()
  155. const menus = await fetchMenuTree()
  156. menuList.value = filterMenu(menus)
  157. })
  158. // 处理地区点击事件,直接在组件内更新当前地区,包老师改的,直接传参
  159. const changeDataByArea = (item) => {
  160. currentArea.value = item
  161. }
  162. // 控制导出列表弹窗显示状态
  163. const exportListVisible = ref(false)
  164. // 显示修改密码弹窗
  165. const showPasswordDialog = ref(false)
  166. // 打开导出列表弹窗
  167. const openExportList = () => {
  168. getExportList()
  169. exportListVisible.value = true
  170. }
  171. //打开修改密码弹窗
  172. const openChangePassword = () => {
  173. showPasswordDialog.value = true
  174. }
  175. // 關閉修改密碼的清空邏輯
  176. const changePasswordRef = ref(null)
  177. const handleClosePasswordDialog = () => {
  178. changePasswordRef.value?.resetFields()
  179. }
  180. </script>
  181. <template>
  182. <div class="common-layout">
  183. <el-container>
  184. <el-aside style="
  185. width: 15%;
  186. min-width: 180px;
  187. position: fixed; /* 固定位置 */
  188. top: 0;
  189. left: 0;
  190. height: 100vh; /* 高度占满视口 */
  191. z-index: 100; /* 确保侧边栏在其他元素之上 */
  192. ">
  193. <div class="logo">
  194. <img src="../assets/新logo.png" alt="logo" style="width: 80px; height: 80px" />
  195. <!-- <div style="font-size: 16px; font-weight: bold; color: black; text-align: center;" ><h1>海外金币管理系统</h1></div> -->
  196. </div>
  197. <el-card style="min-height: 90%;">
  198. <el-menu :router="true" class="el-menu-vertical-demo" :default-active="$route.path">
  199. <!-- 递归渲染菜单层级 -->
  200. <template v-for="menu in menuList" :key="menu.id">
  201. <!-- 有子菜单的父级菜单menuType=2 且存在children -->
  202. <el-sub-menu v-if="menu.children && menu.children.length > 0" :index="menu.id.toString()">
  203. <template #title>
  204. <el-icon>
  205. <Folder />
  206. </el-icon>
  207. <span>{{ menu.menuName }}</span>
  208. </template>
  209. <!-- 子菜单 -->
  210. <template v-for="child in menu.children" :key="child.id">
  211. <!-- 子菜单为叶子节点无children -->
  212. <el-menu-item v-if="!child.children || child.children.length === 0" :index="getRoutePath(child)">
  213. <span>{{ child.menuName }}</span>
  214. </el-menu-item>
  215. <!-- 子菜单有下级 -->
  216. <el-sub-menu v-else :index="child.id.toString()">
  217. <template #title>
  218. <span>{{ child.menuName }}</span>
  219. </template>
  220. <!-- 递归 下一级-->
  221. <template v-for="grandChild in child.children" :key="grandChild.id">
  222. <el-menu-item :index="getRoutePath(grandChild)">
  223. <span>{{ grandChild.menuName }}</span>
  224. </el-menu-item>
  225. </template>
  226. </el-sub-menu>
  227. </template>
  228. </el-sub-menu>
  229. <!-- 无子菜单的一级菜单 -->
  230. <el-menu-item v-else :index="getRoutePath(menu)">
  231. <el-icon>
  232. <Folder />
  233. </el-icon>
  234. <span>{{ menu.menuName }}</span>
  235. </el-menu-item>
  236. </template>
  237. </el-menu>
  238. </el-card>
  239. </el-aside>
  240. <el-container style="margin-left: 15%; min-width: 180px">
  241. <!-- 修改 el-header 样式 -->
  242. <el-header style="
  243. position: fixed;
  244. top: 0;
  245. left: 15%;
  246. right: 0;
  247. z-index: 80;
  248. background: white;
  249. ">
  250. <el-menu class="el-menu-demo" mode="horizontal" :ellipsis="false">
  251. <el-sub-menu index="1" class="admin">
  252. <template #title>
  253. <el-image :src="imgrule1" alt="错误" style="width: 50px; height: 50px" />
  254. <span style="margin-left: 10px">{{ adminData.name }}</span>
  255. </template>
  256. <el-menu-item @click="message()">查看个人信息</el-menu-item>
  257. <el-menu-item @click="openChangePassword">修改密码</el-menu-item>
  258. <el-menu-item @click="logout">退出登录</el-menu-item>
  259. </el-sub-menu>
  260. </el-menu>
  261. </el-header>
  262. <el-main style="margin-top: 60px">
  263. <!-- 60px el-header 的大致高度可根据实际情况调整 -->
  264. <router-view></router-view>
  265. </el-main>
  266. </el-container>
  267. </el-container>
  268. <!-- 查看个人信息 -->
  269. <el-dialog v-model="messageVisible" title="查看个人信息" width="500px">
  270. <el-form :model="adminData">
  271. <el-form-item label="用户姓名" label-width="100px" label-position="left">
  272. <span class="message-font">{{ adminData.adminName }}</span>
  273. </el-form-item>
  274. <el-form-item label="精网号" label-width="100px" label-position="left">
  275. <span class="message-font">{{ adminData.account }}</span>
  276. </el-form-item>
  277. <el-form-item label="地区" label-width="100px" label-position="left">
  278. <span class="message-font">{{ adminData.markets }}</span>
  279. </el-form-item>
  280. <el-form-item label="注册时间" label-width="100px" label-position="left">
  281. <span class="message-font">{{ adminData.createTime }}</span>
  282. </el-form-item>
  283. </el-form>
  284. <template #footer>
  285. <div class="dialog-footer">
  286. <el-button text @click="closeMessage()">关闭</el-button>
  287. </div>
  288. </template>
  289. </el-dialog>
  290. <!-- 自定义密码修改弹窗组件 父組件和子組件通信-->
  291. <el-dialog v-model="showPasswordDialog" :center="true" width="470px" @close="handleClosePasswordDialog">
  292. <ChangePassword ref="changePasswordRef" @confirm="showPasswordDialog = false" />
  293. </el-dialog>
  294. </div>
  295. </template>
  296. <style scoped>
  297. .message-font {
  298. font-size: 16px;
  299. font-weight: bold;
  300. }
  301. .item {
  302. margin-top: 20px;
  303. margin-right: 40px;
  304. }
  305. .admin {
  306. margin-left: auto;
  307. }
  308. .el-aside {
  309. min-height: 100vh;
  310. width: 200px;
  311. }
  312. /* background-color: #BFD8D2; */
  313. .logo {
  314. margin: 20px 0px 20px 20px;
  315. display: flex;
  316. }
  317. .el-menu-demo {
  318. border: none;
  319. /* 去除边框 */
  320. padding: 0;
  321. /* 去除内边距 */
  322. float: right;
  323. /* 将菜单向右浮动 */
  324. }
  325. .el-menu-vertical-demo:not(.el-menu--collapse) {
  326. width: 240px;
  327. min-height: 400px;
  328. border: none;
  329. /* 去除边框 */
  330. }
  331. </style>