user.ts 15 KB

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