user.js 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267
  1. import { defineStore } from 'pinia'
  2. import { computed, ref } from 'vue'
  3. import { AUTH_API, TOKEN_KEY, REFRESH_TOKEN_KEY, USER_INFO_KEY, PAGE_PATHS } from '@/config/api'
  4. import { request, parseResponseJson } from '@/utils/request'
  5. import { buildMiniEncryptedLoginBody } from '@/utils/loginRsa'
  6. export const useUserStore = defineStore('user', () => {
  7. const token = ref('')
  8. const refreshToken = ref('')
  9. const userInfo = ref(null)
  10. const isLoading = ref(false)
  11. const menuAll = ref(false)
  12. const menuCodes = ref([])
  13. function normalizeUserInfo(raw) {
  14. if (!raw || typeof raw !== 'object') return raw || null
  15. const avatarUrl =
  16. raw.avatarUrl != null && String(raw.avatarUrl).trim()
  17. ? String(raw.avatarUrl).trim()
  18. : raw.avatar_url != null && String(raw.avatar_url).trim()
  19. ? String(raw.avatar_url).trim()
  20. : ''
  21. return {
  22. ...raw,
  23. avatarUrl
  24. }
  25. }
  26. const isLoggedIn = computed(() => !!token.value && !!userInfo.value)
  27. const userRole = computed(() => userInfo.value?.role || '')
  28. /** 登录成功默认进入工作台 Tab */
  29. const homePath = computed(() => PAGE_PATHS.workbench)
  30. function checkLoginStatus() {
  31. const savedToken = uni.getStorageSync(TOKEN_KEY)
  32. const savedUserInfo = uni.getStorageSync(USER_INFO_KEY)
  33. const savedRefreshToken = uni.getStorageSync(REFRESH_TOKEN_KEY)
  34. const normalizedToken =
  35. typeof savedToken === 'string' ? savedToken.trim() : savedToken ? String(savedToken).trim() : ''
  36. let normalizedUserInfo = null
  37. if (typeof savedUserInfo === 'string') {
  38. try {
  39. normalizedUserInfo = normalizeUserInfo(JSON.parse(savedUserInfo))
  40. } catch (error) {
  41. normalizedUserInfo = null
  42. }
  43. } else if (savedUserInfo && typeof savedUserInfo === 'object') {
  44. normalizedUserInfo = normalizeUserInfo(savedUserInfo)
  45. }
  46. if (normalizedToken && normalizedUserInfo) {
  47. token.value = normalizedToken
  48. refreshToken.value = typeof savedRefreshToken === 'string' ? savedRefreshToken : ''
  49. userInfo.value = normalizedUserInfo
  50. return
  51. }
  52. token.value = ''
  53. refreshToken.value = ''
  54. userInfo.value = null
  55. }
  56. function setLoginInfo(data) {
  57. token.value = data.token || ''
  58. refreshToken.value = data.refreshToken || ''
  59. userInfo.value = normalizeUserInfo(data.userInfo || null)
  60. uni.setStorageSync(TOKEN_KEY, token.value)
  61. uni.setStorageSync(REFRESH_TOKEN_KEY, refreshToken.value)
  62. uni.setStorageSync(USER_INFO_KEY, userInfo.value)
  63. }
  64. async function fetchMenuCodes() {
  65. if (!token.value || !String(token.value).trim()) {
  66. menuAll.value = false
  67. menuCodes.value = []
  68. return
  69. }
  70. try {
  71. const res = await request({
  72. url: AUTH_API.MENU_CODES,
  73. method: 'GET'
  74. })
  75. const result = parseResponseJson(res)
  76. if (!result || result.code !== 200 || !result.data) {
  77. menuAll.value = false
  78. menuCodes.value = []
  79. return
  80. }
  81. const data = result.data
  82. menuAll.value = !!(data.all || data.isSuperAdmin)
  83. menuCodes.value = Array.isArray(data.menuCodes)
  84. ? data.menuCodes.map((c) => String(c || '').trim()).filter(Boolean)
  85. : []
  86. } catch {
  87. menuAll.value = false
  88. menuCodes.value = []
  89. }
  90. }
  91. async function login(params) {
  92. isLoading.value = true
  93. try {
  94. const encryptedBody = await buildMiniEncryptedLoginBody(params.username, params.password)
  95. const res = await request({
  96. url: AUTH_API.LOGIN,
  97. method: 'POST',
  98. data: encryptedBody
  99. })
  100. const result = parseResponseJson(res)
  101. if (result && result.code === 200 && result.data) {
  102. setLoginInfo({
  103. token: result.data.token,
  104. refreshToken: result.data.refreshToken,
  105. userInfo: result.data.userInfo
  106. })
  107. await fetchMenuCodes()
  108. return true
  109. }
  110. uni.showToast({ title: (result && result.message) || '登录失败', icon: 'none' })
  111. return false
  112. } catch (error) {
  113. uni.showToast({
  114. title: (error && error.message) || '网络请求失败',
  115. icon: 'none'
  116. })
  117. return false
  118. } finally {
  119. isLoading.value = false
  120. }
  121. }
  122. /** 手机号快捷登录;loading 由登录页控制,此处不改 isLoading */
  123. async function wechatLogin(params) {
  124. try {
  125. const res = await request({
  126. url: AUTH_API.WECHAT_LOGIN,
  127. method: 'POST',
  128. data: {
  129. loginCode: params.loginCode,
  130. phoneCode: params.phoneCode
  131. }
  132. })
  133. const result = parseResponseJson(res)
  134. if (result && result.code === 200 && result.data) {
  135. setLoginInfo({
  136. token: result.data.token,
  137. refreshToken: result.data.refreshToken,
  138. userInfo: result.data.userInfo
  139. })
  140. await fetchMenuCodes()
  141. return true
  142. }
  143. uni.showToast({ title: (result && result.message) || '登录失败', icon: 'none' })
  144. return false
  145. } catch (e) {
  146. uni.showToast({ title: (e && e.message) || '登录失败', icon: 'none' })
  147. return false
  148. }
  149. }
  150. /** 网页扫码:通知已扫;不写本地登录态 */
  151. async function webQrScanned(sessionId) {
  152. const res = await request({
  153. url: AUTH_API.WEB_QR_SCANNED,
  154. method: 'POST',
  155. data: { sessionId }
  156. })
  157. const result = parseResponseJson(res)
  158. return !!(result && result.code === 200)
  159. }
  160. /** 网页扫码:手机号授权确认;不写本地登录态、不跳转工作台 */
  161. async function webQrConfirm(params) {
  162. const res = await request({
  163. url: AUTH_API.WEB_QR_CONFIRM,
  164. method: 'POST',
  165. data: {
  166. sessionId: params.sessionId,
  167. loginCode: params.loginCode,
  168. phoneCode: params.phoneCode
  169. }
  170. })
  171. const result = parseResponseJson(res)
  172. if (result && result.code === 200) {
  173. return {
  174. ok: true,
  175. message: (result.data && result.data.message) || '电脑端即将登录,可关闭本页'
  176. }
  177. }
  178. return { ok: false, message: (result && result.message) || '授权失败' }
  179. }
  180. function clearLoginInfo() {
  181. token.value = ''
  182. refreshToken.value = ''
  183. userInfo.value = null
  184. uni.removeStorageSync(TOKEN_KEY)
  185. uni.removeStorageSync(REFRESH_TOKEN_KEY)
  186. uni.removeStorageSync(USER_INFO_KEY)
  187. menuAll.value = false
  188. menuCodes.value = []
  189. }
  190. async function logout() {
  191. try {
  192. await request({
  193. url: AUTH_API.LOGOUT,
  194. method: 'POST'
  195. })
  196. } catch (error) {
  197. // ignore
  198. } finally {
  199. clearLoginInfo()
  200. uni.reLaunch({ url: PAGE_PATHS.login })
  201. }
  202. }
  203. function navigateToHome() {
  204. uni.reLaunch({ url: homePath.value })
  205. }
  206. function updateUserInfo(info) {
  207. if (!userInfo.value || !info) return
  208. userInfo.value = normalizeUserInfo({ ...userInfo.value, ...info })
  209. uni.setStorageSync(USER_INFO_KEY, userInfo.value)
  210. }
  211. async function refreshUserInfo() {
  212. if (!token.value || !String(token.value).trim()) return
  213. try {
  214. const res = await request({
  215. url: AUTH_API.CURRENT_USER,
  216. method: 'GET'
  217. })
  218. const result = parseResponseJson(res)
  219. if (result.code !== 200 || !result.data) return
  220. userInfo.value = normalizeUserInfo(result.data)
  221. uni.setStorageSync(USER_INFO_KEY, userInfo.value)
  222. } catch {
  223. // ignore
  224. }
  225. }
  226. return {
  227. token,
  228. refreshToken,
  229. userInfo,
  230. isLoading,
  231. menuAll,
  232. menuCodes,
  233. isLoggedIn,
  234. userRole,
  235. homePath,
  236. checkLoginStatus,
  237. login,
  238. wechatLogin,
  239. webQrScanned,
  240. webQrConfirm,
  241. logout,
  242. clearLoginInfo,
  243. navigateToHome,
  244. updateUserInfo,
  245. refreshUserInfo,
  246. fetchMenuCodes
  247. }
  248. })