user.js 3.2 KB

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