user.ts 15 KB

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