user.ts 15 KB

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