user.js 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128
  1. import { defineStore } from 'pinia'
  2. import { ref, computed } from 'vue'
  3. import { getToken, setToken, removeToken, setUserInfo, removeUserInfo, getUserInfo } from '@/utils/storage'
  4. import { login as loginApi, logout as logoutApi, getUserInfoApi } from '@/api/auth'
  5. /**
  6. * 用户状态 Store
  7. * 管理登录状态、用户信息、Token、权限路由列表
  8. */
  9. export const useUserStore = defineStore('user', () => {
  10. // 状态
  11. const token = ref(getToken() || '')
  12. const userInfo = ref(getUserInfo() || {})
  13. // 计算属性
  14. const isLoggedIn = computed(() => !!token.value)
  15. const username = computed(() => userInfo.value?.username || '')
  16. const nickname = computed(() => userInfo.value?.nickname || userInfo.value?.username || '')
  17. const avatar = computed(() => userInfo.value?.avatar || '/default-avatar.png')
  18. /**
  19. * 管理员角色:1=超级管理员,2=设备管理员,3=圣手管理
  20. * 超级管理员拥有所有菜单权限
  21. */
  22. const adminRole = computed(() => userInfo.value?.adminRole || null)
  23. /**
  24. * 权限路由路径列表(JSON字符串解析后的数组)
  25. * 超级管理员(adminRole=1)此值为 null,前端视为拥有全部权限
  26. * 其他角色为可访问的路由路径数组,如 ['/dashboard', '/device']
  27. */
  28. const permissions = computed(() => {
  29. const raw = userInfo.value?.permissions
  30. if (raw === null || raw === undefined) return null // 超级管理员:全部权限
  31. if (typeof raw === 'string') {
  32. try {
  33. return JSON.parse(raw).map(normalizePermissionPath).filter(Boolean)
  34. } catch {
  35. return []
  36. }
  37. }
  38. if (Array.isArray(raw)) return raw.map(normalizePermissionPath).filter(Boolean)
  39. return []
  40. })
  41. /**
  42. * 判断是否有指定路由的权限
  43. * @param {string} path 路由 path
  44. */
  45. function hasPermission(path) {
  46. if (adminRole.value === 1) return true // 超级管理员全部通过
  47. if (permissions.value === null) return true // null 也视为全部权限
  48. return permissions.value.includes(normalizePermissionPath(path))
  49. }
  50. /**
  51. * 登录
  52. * @param {Object} loginData 登录参数 { username, password }
  53. */
  54. async function login(loginData) {
  55. const res = await loginApi(loginData)
  56. const { token: newToken, ...info } = res.data
  57. // 保存 Token 到内存和 localStorage
  58. token.value = newToken
  59. setToken(newToken)
  60. // 保存用户基本信息(含 adminRole 和 permissions)
  61. userInfo.value = info
  62. setUserInfo(info)
  63. return res
  64. }
  65. /**
  66. * 退出登录
  67. * 清除本地状态并调用后端退出接口
  68. */
  69. async function logout() {
  70. try {
  71. await logoutApi()
  72. } catch (e) {
  73. // 退出接口失败不影响本地清除
  74. }
  75. clearUser()
  76. }
  77. /**
  78. * 获取最新用户信息(从后端刷新,含权限路由)
  79. */
  80. async function fetchUserInfo() {
  81. const res = await getUserInfoApi()
  82. userInfo.value = res.data
  83. setUserInfo(res.data)
  84. return res.data
  85. }
  86. /**
  87. * 清除用户状态(退出登录时调用)
  88. */
  89. function clearUser() {
  90. token.value = ''
  91. userInfo.value = {}
  92. removeToken()
  93. removeUserInfo()
  94. }
  95. return {
  96. token,
  97. userInfo,
  98. isLoggedIn,
  99. username,
  100. nickname,
  101. avatar,
  102. adminRole,
  103. permissions,
  104. hasPermission,
  105. login,
  106. logout,
  107. fetchUserInfo,
  108. clearUser
  109. }
  110. })
  111. function normalizePermissionPath(path) {
  112. if (!path || typeof path !== 'string') return ''
  113. const cleanPath = path.split('?')[0].split('#')[0].trim()
  114. if (cleanPath === '/admin') return '/system/admin'
  115. if (cleanPath === '/log') return '/system/log'
  116. return cleanPath
  117. }