user.ts 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399
  1. import type { UserInfo, LoginInfo } from '/#/store';
  2. import type { ErrorMessageMode, SuccessMessageMode } from '/#/axios';
  3. import { defineStore } from 'pinia';
  4. import { store } from '/@/store';
  5. import { RoleEnum } from '/@/enums/roleEnum';
  6. import { PageEnum } from '/@/enums/pageEnum';
  7. import { ROLES_KEY, TOKEN_KEY, USER_INFO_KEY, LOGIN_INFO_KEY, DB_DICT_DATA_KEY, TENANT_ID, OAUTH2_THIRD_LOGIN_TENANT_ID } from '/@/enums/cacheEnum';
  8. import { getAuthCache, setAuthCache, removeAuthCache } from '/@/utils/auth';
  9. import { AutoLoginParams, GetUserInfoModel, LoginParams, ThirdLoginParams } from '/@/api/sys/model/userModel';
  10. import { autoLoginApi, doLogout, getUserInfo, loginApi, phoneLoginApi, thirdLogin } from '/@/api/sys/user';
  11. import { useI18n } from '/@/hooks/web/useI18n';
  12. import { useMessage } from '/@/hooks/web/useMessage';
  13. import { router } from '/@/router';
  14. // import { usePermissionStore } from '/@/store/modules/permission';
  15. // import { RouteRecordRaw } from 'vue-router';
  16. // import { PAGE_NOT_FOUND_ROUTE } from '/@/router/routes/basic';
  17. import { isArray } from '/@/utils/is';
  18. import { useGlobSetting } from '/@/hooks/setting';
  19. import { JDragConfigEnum } from '/@/enums/jeecgEnum';
  20. import { useSso } from '/@/hooks/web/useSso';
  21. import { isOAuth2AppEnv } from '/@/views/sys/login/useLogin';
  22. import { getUrlParam } from '@/utils';
  23. import { loginCipher } from '/@/settings/encryptionSetting';
  24. import { AesEncryption } from '/@/utils/cipher';
  25. interface dictType {
  26. [key: string]: any;
  27. }
  28. interface UserState {
  29. userInfo: Nullable<UserInfo>;
  30. token?: string;
  31. roleList: RoleEnum[];
  32. dictItems?: dictType | null;
  33. sessionTimeout?: boolean;
  34. lastUpdateTime: number;
  35. tenantid?: string | number;
  36. shareTenantId?: Nullable<string | number>;
  37. loginInfo?: Nullable<LoginInfo>;
  38. }
  39. export const useUserStore = defineStore({
  40. id: 'app-user',
  41. state: (): UserState => ({
  42. // 用户信息
  43. userInfo: null,
  44. // token
  45. token: undefined,
  46. // 角色列表
  47. roleList: [],
  48. // 字典
  49. dictItems: null,
  50. // session过期时间
  51. sessionTimeout: false,
  52. // Last fetch time
  53. lastUpdateTime: 0,
  54. //租户id
  55. tenantid: '',
  56. // 分享租户ID
  57. // 用于分享页面所属租户与当前用户登录租户不一致的情况
  58. shareTenantId: null,
  59. //登录返回信息
  60. loginInfo: null,
  61. }),
  62. getters: {
  63. getUserInfo(): UserInfo {
  64. if (this.userInfo == null) {
  65. this.userInfo = getAuthCache<UserInfo>(USER_INFO_KEY) != null ? getAuthCache<UserInfo>(USER_INFO_KEY) : null;
  66. }
  67. return this.userInfo || getAuthCache<UserInfo>(USER_INFO_KEY) || {};
  68. },
  69. getLoginInfo(): LoginInfo {
  70. return this.loginInfo || getAuthCache<LoginInfo>(LOGIN_INFO_KEY) || {};
  71. },
  72. getToken(): string {
  73. return this.token || getAuthCache<string>(TOKEN_KEY);
  74. },
  75. getAllDictItems(): [] {
  76. return this.dictItems || getAuthCache(DB_DICT_DATA_KEY);
  77. },
  78. getRoleList(): RoleEnum[] {
  79. return this.roleList.length > 0 ? this.roleList : getAuthCache<RoleEnum[]>(ROLES_KEY);
  80. },
  81. getSessionTimeout(): boolean {
  82. return !!this.sessionTimeout;
  83. },
  84. getLastUpdateTime(): number {
  85. return this.lastUpdateTime;
  86. },
  87. getTenant(): string | number {
  88. return this.tenantid || getAuthCache<string | number>(TENANT_ID);
  89. },
  90. // 是否有分享租户id
  91. hasShareTenantId(): boolean {
  92. return this.shareTenantId != null && this.shareTenantId !== '';
  93. },
  94. },
  95. actions: {
  96. setToken(info: string | undefined) {
  97. this.token = info ? info : ''; // for null or undefined value
  98. setAuthCache(TOKEN_KEY, info);
  99. },
  100. setRoleList(roleList: RoleEnum[]) {
  101. this.roleList = roleList;
  102. setAuthCache(ROLES_KEY, roleList);
  103. },
  104. setUserInfo(info: UserInfo | null) {
  105. this.userInfo = info;
  106. this.lastUpdateTime = new Date().getTime();
  107. setAuthCache(USER_INFO_KEY, info);
  108. },
  109. setLoginInfo(info: LoginInfo | null) {
  110. this.loginInfo = info;
  111. setAuthCache(LOGIN_INFO_KEY, info);
  112. },
  113. setAllDictItems(dictItems) {
  114. this.dictItems = dictItems;
  115. setAuthCache(DB_DICT_DATA_KEY, dictItems);
  116. },
  117. setAllDictItemsByLocal() {
  118. // 代码逻辑说明: 【QQYUN-8572】表格行选择卡顿问题(customRender中字典引起的)
  119. if (!this.dictItems) {
  120. const allDictItems = getAuthCache(DB_DICT_DATA_KEY);
  121. if (allDictItems) {
  122. this.dictItems = allDictItems;
  123. }
  124. }
  125. },
  126. setTenant(id) {
  127. this.tenantid = id;
  128. setAuthCache(TENANT_ID, id);
  129. },
  130. setShareTenantId(id: NonNullable<typeof this.shareTenantId>) {
  131. this.shareTenantId = id;
  132. },
  133. setSessionTimeout(flag: boolean) {
  134. this.sessionTimeout = flag;
  135. },
  136. resetState() {
  137. this.userInfo = null;
  138. this.dictItems = null;
  139. this.token = '';
  140. this.roleList = [];
  141. this.sessionTimeout = false;
  142. },
  143. /**
  144. * 登录事件
  145. */
  146. async login(
  147. params: LoginParams & {
  148. goHome?: boolean;
  149. mode?: ErrorMessageMode;
  150. }
  151. ): Promise<GetUserInfoModel | null> {
  152. try {
  153. const { goHome = true, mode, ...loginParams } = params;
  154. const encryption = new AesEncryption({ key: loginCipher.key, iv: loginCipher.iv });
  155. loginParams.password = encryption.encryptByAES(loginParams.password);
  156. const data = await loginApi(loginParams, mode);
  157. const { token, userInfo } = data;
  158. // save token
  159. this.setToken(token);
  160. this.setTenant(userInfo.loginTenantId);
  161. return this.afterLoginAction(goHome, data);
  162. } catch (error) {
  163. return Promise.reject(error);
  164. }
  165. },
  166. /**
  167. * 扫码登录事件
  168. */
  169. async qrCodeLogin(token): Promise<GetUserInfoModel | null> {
  170. try {
  171. // save token
  172. this.setToken(token);
  173. return this.afterLoginAction(true, {});
  174. } catch (error) {
  175. return Promise.reject(error);
  176. }
  177. },
  178. /**
  179. * 登录完成处理
  180. * @param goHome
  181. */
  182. async afterLoginAction(goHome?: boolean, data?: any): Promise<any | null> {
  183. if (!this.getToken) return null;
  184. //获取用户信息
  185. const userInfo = await this.getUserInfoAction();
  186. const sessionTimeout = this.sessionTimeout;
  187. if (sessionTimeout) {
  188. this.setSessionTimeout(false);
  189. } else {
  190. // // 构建后台菜单路由
  191. // const permissionStore = usePermissionStore();
  192. // if (!permissionStore.isDynamicAddedRoute) {
  193. // const routes = await permissionStore.buildRoutesAction();
  194. // routes.forEach((route) => {
  195. // router.addRoute(route as unknown as RouteRecordRaw);
  196. // });
  197. // router.addRoute(PAGE_NOT_FOUND_ROUTE as unknown as RouteRecordRaw);
  198. // permissionStore.setDynamicAddedRoute(true);
  199. // }
  200. await this.setLoginInfo({ ...data, isLogin: true });
  201. // 代码逻辑说明: 登录成功后缓存拖拽模块的接口前缀
  202. localStorage.setItem(JDragConfigEnum.DRAG_BASE_URL, useGlobSetting().domainUrl);
  203. // 代码逻辑说明: 修复登录成功后,没有正确重定向的问题
  204. const redirect = router.currentRoute.value?.query?.redirect as string;
  205. // 判断是否有 redirect 重定向地址
  206. // 代码逻辑说明: 【QQYUN-5195】登录之后直接刷新页面导致没有进入创建组织页面------------
  207. if (redirect && goHome) {
  208. // router.options.history.base可替代之前的publicPath
  209. // 当前页面打开
  210. window.open(`${router.options.history.base}${redirect}`, '_self');
  211. return data;
  212. }
  213. // 代码逻辑说明: 【issues/1102】设置单点登录后页面,进入首页提示404,也没有绘制侧边栏 #1102---
  214. const ticket = getUrlParam('ticket');
  215. if (ticket) {
  216. goHome && window.location.replace((userInfo && userInfo.homePath) || PageEnum.BASE_HOME);
  217. } else {
  218. goHome && (await router.replace((userInfo && userInfo.homePath) || PageEnum.BASE_HOME));
  219. }
  220. }
  221. return data;
  222. },
  223. /**
  224. * 手机号登录
  225. * @param params
  226. */
  227. async phoneLogin(
  228. params: LoginParams & {
  229. goHome?: boolean;
  230. mode?: ErrorMessageMode;
  231. }
  232. ): Promise<GetUserInfoModel | null> {
  233. try {
  234. const { goHome = true, mode, ...loginParams } = params;
  235. const data = await phoneLoginApi(loginParams, mode);
  236. // 代码逻辑说明: 【issues/7488】手机号码登录,在请求头中无法获取租户id---
  237. const { token, userInfo } = data;
  238. this.setTenant(userInfo!.loginTenantId);
  239. // save token
  240. this.setToken(token);
  241. return this.afterLoginAction(goHome, data);
  242. } catch (error) {
  243. return Promise.reject(error);
  244. }
  245. },
  246. /**
  247. * 获取用户信息
  248. */
  249. async getUserInfoAction(): Promise<UserInfo | null> {
  250. if (!this.getToken) {
  251. return null;
  252. }
  253. const { userInfo, sysAllDictItems } = await getUserInfo();
  254. if (userInfo) {
  255. const { roles = [] } = userInfo;
  256. if (isArray(roles)) {
  257. const roleList = roles.map((item) => item.value) as RoleEnum[];
  258. this.setRoleList(roleList);
  259. } else {
  260. userInfo.roles = [];
  261. this.setRoleList([]);
  262. }
  263. // 省局项目暂时不需要读取用户的首页配置
  264. userInfo.homePath = PageEnum.BASE_HOME;
  265. this.setUserInfo(userInfo);
  266. }
  267. /**
  268. * 添加字典信息到缓存
  269. * @updateBy:lsq
  270. * @updateDate:2021-09-08
  271. */
  272. if (sysAllDictItems) {
  273. this.setAllDictItems(sysAllDictItems);
  274. }
  275. return userInfo;
  276. },
  277. /**
  278. * 退出登录
  279. */
  280. async logout(goLogin = false) {
  281. if (this.getToken) {
  282. try {
  283. await doLogout();
  284. } catch {
  285. console.log('注销Token失败');
  286. }
  287. }
  288. // let username:any = this.userInfo && this.userInfo.username;
  289. // if(username){
  290. // removeAuthCache(username)
  291. // }
  292. this.setToken('');
  293. setAuthCache(TOKEN_KEY, null);
  294. this.setSessionTimeout(false);
  295. this.setUserInfo(null);
  296. this.setLoginInfo(null);
  297. this.setTenant(null);
  298. // 代码逻辑说明: 【TV360X-23】退出登录后会提示「Token时效,请重新登录」
  299. setTimeout(() => {
  300. this.setAllDictItems(null);
  301. }, 1e3);
  302. // 代码逻辑说明: 退出登录后清除拖拽模块的接口前缀
  303. localStorage.removeItem(JDragConfigEnum.DRAG_BASE_URL);
  304. //如果开启单点登录,则跳转到单点统一登录中心
  305. const openSso = useGlobSetting().openSso;
  306. if (openSso == 'true') {
  307. await useSso().ssoLoginOut();
  308. }
  309. //退出登录的时候需要用的应用id
  310. if (isOAuth2AppEnv()) {
  311. const tenantId = getAuthCache(OAUTH2_THIRD_LOGIN_TENANT_ID);
  312. removeAuthCache(OAUTH2_THIRD_LOGIN_TENANT_ID);
  313. goLogin && (await router.push({ name: 'Login', query: { tenantId: tenantId } }));
  314. } else {
  315. // 代码逻辑说明: 修复登录成功后,没有正确重定向的问题
  316. goLogin &&
  317. (await router.push({
  318. path: PageEnum.BASE_LOGIN,
  319. query: {
  320. // 传入当前的路由,登录成功后跳转到当前路由
  321. redirect: router.currentRoute.value.fullPath,
  322. },
  323. }));
  324. }
  325. },
  326. /**
  327. * 登录事件
  328. */
  329. async ThirdLogin(
  330. params: ThirdLoginParams & {
  331. goHome?: boolean;
  332. mode?: ErrorMessageMode;
  333. }
  334. ): Promise<any | null> {
  335. try {
  336. const { goHome = true, mode, ...ThirdLoginParams } = params;
  337. const data = await thirdLogin(ThirdLoginParams, mode);
  338. // 代码逻辑说明: 【issues/6652】开启租户数据隔离,接入钉钉后登录默认租户为0了---
  339. const { token, userInfo } = data;
  340. this.setTenant(userInfo?.loginTenantId);
  341. // save token
  342. this.setToken(token);
  343. return this.afterLoginAction(goHome, data);
  344. } catch (error) {
  345. return Promise.reject(error);
  346. }
  347. },
  348. /** 用户自动登录,即不需要用户密码即可登录 */
  349. async autoLogin(
  350. params: AutoLoginParams & {
  351. goHome?: boolean;
  352. mode?: ErrorMessageMode;
  353. successMode?: SuccessMessageMode;
  354. }
  355. ) {
  356. const { goHome = true, mode, successMode = 'none', ...loginParams } = params;
  357. // 进行加密
  358. // const encryption = new AesEncryption({ key: loginCipher.key, iv: loginCipher.iv });
  359. // loginParams.password = encryption.encryptByAES(loginParams.password);
  360. // encryption.encryptByAES(loginParams.password);
  361. const data = await autoLoginApi(loginParams, mode, successMode);
  362. const { token } = data;
  363. // save token
  364. this.setToken(token);
  365. // this.setTenant(userInfo.loginTenantId);
  366. await this.afterLoginAction(goHome, data);
  367. return;
  368. },
  369. /**
  370. * 退出询问
  371. */
  372. confirmLoginOut() {
  373. const { createConfirm } = useMessage();
  374. const { t } = useI18n();
  375. createConfirm({
  376. iconType: 'warning',
  377. title: t('sys.app.logoutTip'),
  378. content: t('sys.app.logoutMessage'),
  379. onOk: async () => {
  380. await this.logout(true);
  381. },
  382. });
  383. },
  384. },
  385. });
  386. // Need to be used outside the setup
  387. export function useUserStoreWithOut() {
  388. return useUserStore(store);
  389. }