cloud-web-corejs 1.0.54-dev.770 → 1.0.54-dev.771

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/permission.js CHANGED
@@ -1,20 +1,18 @@
1
- /**version-1.0*/
2
- import router from "@base/router";
3
- import store from "./store";
4
- import Message from "element-ui/lib/message";
5
- import NProgress from "nprogress"; // progress bar
6
- import "nprogress/nprogress.css"; // progress bar style
7
- import {
8
- getToken
9
- } from "./utils/auth"; // get token from cookie
1
+ /**version-1.0*/
2
+ import router from "@base/router";
3
+ import store from "./store";
4
+ import {
5
+ Message
6
+ } from "element-ui";
7
+ import NProgress from "nprogress"; // progress bar
8
+ import "nprogress/nprogress.css"; // progress bar style
9
+ import {
10
+ getToken
11
+ } from "./utils/auth"; // get token from cookie
10
12
  import settingConfig from "@/settings";
13
+
11
14
  import {
12
- ensureBusinessFeatures,
13
- preloadBusinessFeatures,
14
- } from "@base/bootstrap/business";
15
-
16
- import {
17
- saveAccessMenuLog
15
+ saveAccessMenuLog
18
16
  } from "./api/user";
19
17
  import indexUtil from "./utils/index.js"
20
18
 
@@ -26,30 +24,10 @@ const whiteList = [
26
24
  "/login",
27
25
  "/auth-redirect",
28
26
  "/report/vform/out_render",
29
- "/report/xform/out_render",
27
+ "/report/xform/out_render",
30
28
  ]; // no redirect whitelist
31
29
 
32
- // 这些页面只依赖登录核心包;其余已登录页面和免登录业务页在进入前加载业务能力。
33
- const lightweightRoutes = new Set([
34
- "/login",
35
- "/auth-redirect",
36
- "/404",
37
- ]);
38
- const parallelBusinessRoutes = new Set(["/", "/home"]);
39
-
40
- function needsBusinessFeatures(to) {
41
- return !lightweightRoutes.has(to.path)
42
- && (getToken() || to.meta.checkToken === false);
43
- }
44
-
45
- function handleBusinessFeatureError(error, next) {
46
- console.error("[bootstrap] 业务模块加载失败", error);
47
- Message.error("业务模块加载失败,请刷新后重试");
48
- NProgress.done();
49
- next(false);
50
- }
51
-
52
- // qiankun 保活:子应用容器被隐藏期间暂停一切路由响应(配合包入口 update 生命周期)
30
+ // qiankun 保活:子应用容器被隐藏期间暂停一切路由响应(配合包入口 update 生命周期)
53
31
  router.beforeEach((to, from, next) => {
54
32
  if (store.state.micro && store.state.micro.routerPaused) {
55
33
  next(false);
@@ -59,28 +37,8 @@ router.beforeEach((to, from, next) => {
59
37
  });
60
38
 
61
39
  router.beforeEach((to, from, next) => {
62
- if (!needsBusinessFeatures(to)) {
63
- next();
64
- return;
65
- }
40
+ // start progress bar
66
41
  NProgress.start();
67
-
68
- // 首页首跳先启动下载,再让鉴权守卫并行拉用户信息和菜单;最终在
69
- // beforeResolve 中等待安装完成,确保组件渲染前全局能力已经就绪。
70
- if (parallelBusinessRoutes.has(to.path)) {
71
- preloadBusinessFeatures();
72
- next();
73
- return;
74
- }
75
-
76
- ensureBusinessFeatures()
77
- .then(() => next())
78
- .catch((error) => handleBusinessFeatureError(error, next));
79
- });
80
-
81
- router.beforeEach((to, from, next) => {
82
- // start progress bar
83
- NProgress.start();
84
42
 
85
43
  store.dispatch("user/initLoginConfig").then(() => {
86
44
  const hasToken = getToken();
@@ -157,26 +115,12 @@ router.beforeEach((to, from, next) => {
157
115
  }
158
116
  })
159
117
 
160
-
161
- });
162
-
163
- router.beforeResolve((to, from, next) => {
164
- if (
165
- !needsBusinessFeatures(to)
166
- || !parallelBusinessRoutes.has(to.path)
167
- ) {
168
- next();
169
- return;
170
- }
171
118
 
172
- ensureBusinessFeatures()
173
- .then(() => next())
174
- .catch((error) => handleBusinessFeatureError(error, next));
175
119
  });
176
120
 
177
121
  router.onError((error) => {
178
- // finish progress bar
179
- console.error(error);
122
+ // finish progress bar
123
+ console.error(error);
180
124
  NProgress.done();
181
125
  });
182
126
 
@@ -1,20 +1,21 @@
1
1
  import { constantRoutes } from "@base/router";
2
2
  import { asyncRoutes, sidebarRoutes } from "@base/router/modules/system";
3
+ import Layout from "@/layout";
3
4
  import { getRouters } from "../../api/menu";
4
5
  import router from "@base/router";
5
6
  import storeConfig from "../config/index";
6
7
  import settings from "@/settings";
7
8
  import {
8
9
  resolveComponentPath,
9
- resolveOwner,
10
- dispatchMenus,
11
- } from "@base/utils/menuAdapter";
12
-
13
- const Layout = () => import(/* webpackChunkName: "layout" */ "@/layout");
14
- const MenuFallback = () =>
15
- import(
16
- /* webpackChunkName: "menu-fallback" */ "@base/views/user/menuFallback/index.vue"
17
- );
10
+ resolveOwner,
11
+ dispatchMenus,
12
+ } from "@base/utils/menuAdapter";
13
+ import {
14
+ getAppRegistry,
15
+ registerSubApps,
16
+ buildSubAppRoutes,
17
+ } from "@base/microRouter";
18
+ import MenuFallback from "@base/views/user/menuFallback/index.vue";
18
19
 
19
20
  // qiankun 宿主态(仅 settings.microMainEnabled 的主应用登录后填充)
20
21
  let hostRegistryApps = [];
@@ -235,30 +236,27 @@ const actions = {
235
236
  if (!isHost) {
236
237
  return dispatch("setSidebarRoute", rows);
237
238
  }
238
- return import("@base/microRouter").then(
239
- ({ getAppRegistry, registerSubApps, buildSubAppRoutes }) =>
240
- getAppRegistry().then((apps) => {
241
- // 自排除:自身也在注册表时(子应用开宿主形态互访别家应用)不装载自己,
242
- // 否则会自嵌套且自身菜单被误判为"派发给子应用"而跳过本地路由
243
- let ownAppCode = settings.appCode || "";
244
- hostRegistryApps = (apps || []).filter(
245
- (app) => app.appCode !== ownAppCode
246
- );
247
- commit(
248
- "SET_REGISTRY_APP_CODES",
249
- hostRegistryApps.map((app) => app.appCode)
250
- );
251
- let subAppMenus = dispatchMenus(rows, hostRegistryApps);
252
- hostMicroRoutes = buildSubAppRoutes(hostRegistryApps, subAppMenus);
253
- if (hostRegistryApps.length) {
254
- registerSubApps(hostRegistryApps, {
255
- subAppMenus,
256
- userInfo: buildUserInfoSnapshot(rootState.user),
257
- });
258
- }
259
- return dispatch("setSidebarRoute", rows);
260
- })
261
- );
239
+ return getAppRegistry().then((apps) => {
240
+ // 自排除:自身也在注册表时(子应用开宿主形态互访别家应用)不装载自己,
241
+ // 否则会自嵌套且自身菜单被误判为"派发给子应用"而跳过本地路由
242
+ let ownAppCode = settings.appCode || "";
243
+ hostRegistryApps = (apps || []).filter(
244
+ (app) => app.appCode !== ownAppCode
245
+ );
246
+ commit(
247
+ "SET_REGISTRY_APP_CODES",
248
+ hostRegistryApps.map((app) => app.appCode)
249
+ );
250
+ let subAppMenus = dispatchMenus(rows, hostRegistryApps);
251
+ hostMicroRoutes = buildSubAppRoutes(hostRegistryApps, subAppMenus);
252
+ if (hostRegistryApps.length) {
253
+ registerSubApps(hostRegistryApps, {
254
+ subAppMenus,
255
+ userInfo: buildUserInfoSnapshot(rootState.user),
256
+ });
257
+ }
258
+ return dispatch("setSidebarRoute", rows);
259
+ });
262
260
  },
263
261
  // qiankun 子应用模式:主应用经 props 下发菜单,替代 getRouters 自拉
264
262
  generateRoutesFromProps({ dispatch, rootState }, menus = []) {
@@ -1,3 +1,5 @@
1
+ import variables from '@/styles/element-variables.scss'
2
+
1
3
  const state = {
2
4
  tagsView: true,
3
5
  }
@@ -1,4 +1,5 @@
1
1
  import store from "../index.js";
2
+ import _ from "lodash";
2
3
  import { isRepeatable, viewKey } from "@base/utils/repeatOpen";
3
4
 
4
5
  let state, mutations, actions;
@@ -200,7 +201,7 @@ actions = {
200
201
  let newView0 = { ...(view || window.$vueRoot.$route) };
201
202
  delete newView0.matched;
202
203
  newView0.meta.title = title;
203
- let newView = JSON.parse(JSON.stringify(newView0));
204
+ let newView = _.cloneDeep(newView0);
204
205
 
205
206
  commit("UPDATE_VISITED_VIEW", newView);
206
207
  },
@@ -8,9 +8,10 @@ import {
8
8
  getLoginConfig,
9
9
  getLogicParamValue,
10
10
  } from "../../api/user";
11
- import { getToken, setToken, clearToken } from "../../utils/auth";
12
- import router, { resetRouter } from "@base/router";
13
- import { getDispermissions } from "../../api/menu";
11
+ import { getToken, setToken, clearToken } from "../../utils/auth";
12
+ import router, { resetRouter } from "@base/router";
13
+ import { unmountAllMicroApps } from "@base/microRouter";
14
+ import { getDispermissions } from "../../api/menu";
14
15
  import settingConfig from "@/settings";
15
16
  import storeConfig from "../config/index";
16
17
 
@@ -329,21 +330,14 @@ actions = {
329
330
  configUtil.clearToken();
330
331
  // qiankun 宿主:登出必须真正卸载保活中的子应用,
331
332
  // 否则换账号登录后切回子应用会复活上一个用户的实例(旧角色/旧路由/旧页面数据)
332
- import("@base/microRouter")
333
- .then(({ unmountAllMicroApps }) => unmountAllMicroApps())
334
- .catch((error) => {
335
- // 微前端 chunk 加载失败不能阻断本地登录态清理
336
- console.error("[user] 微应用卸载模块加载失败", error);
337
- })
338
- .then(() => {
339
- configUtil.resetRouter();
340
- sessionStorage.removeItem("easyweb");
333
+ unmountAllMicroApps();
334
+ configUtil.resetRouter();
335
+ sessionStorage.removeItem("easyweb");
341
336
 
342
- dispatch("tagsView/delAllViews", null, {
343
- root: true,
344
- });
345
- resolve();
346
- });
337
+ dispatch("tagsView/delAllViews", null, {
338
+ root: true,
339
+ });
340
+ resolve();
347
341
  });
348
342
  },
349
343
 
@@ -1,22 +1,8 @@
1
1
  import settingConfig from "@/settings";
2
- import { loadScript } from "@base/utils/externalAssets";
3
2
 
4
- const PDD_LOG_SDK_URL
5
- = "https://pfile.pddpic.com/galerie-go/open_sdk/pc.202102201613.js";
6
-
7
- export async function initPddLog() {
8
- if (!settingConfig.pddLogEnabled) return;
9
- if (!window.PDD_OPEN_init) {
10
- try {
11
- await loadScript(PDD_LOG_SDK_URL);
12
- } catch (error) {
13
- console.warn("[pddLog] SDK 加载失败", error);
14
- return;
15
- }
16
- }
17
- if (typeof window.PDD_OPEN_init === "function") {
18
- pddIntfHandle();
19
- }
3
+ export function initPddLog() {
4
+ if (!settingConfig.pddLogEnabled || !window.PDD_OPEN_init) return
5
+ pddIntfHandle();
20
6
  }
21
7
 
22
8
  function getPageCode(callback) {
@@ -90,7 +76,7 @@ function handleOrderLog(pati) {
90
76
  async function pddIntfHandle() {
91
77
  getPageCode(async (pageCode) => {
92
78
  if (!pageCode) return
93
- window.PDD_OPEN_init({
79
+ PDD_OPEN_init({
94
80
  code: pageCode
95
81
  // 对于获取 code 接口或未登录态,可不传 code:PDD_OPEN_init({}, function () { ... })
96
82
  }, function () {
@@ -1,9 +1,9 @@
1
1
  import { encode } from "js-base64";
2
2
  import settingConfig from "@/settings";
3
- import { preloadBusinessFeatures } from "@base/bootstrap/business";
4
-
5
- function getUrlParams(url) {
6
- let result = {};
3
+ import QRcode from "qrcode";
4
+
5
+ function getUrlParams(url) {
6
+ let result = {};
7
7
  if (url) {
8
8
  let urlStr = url.split("?")[1];
9
9
  const urlSearchParams = new URLSearchParams(urlStr);
@@ -89,14 +89,12 @@ export default {
89
89
  immediate: true,
90
90
  },
91
91
  },
92
- beforeDestroy() {
93
- this.clearQrLoginTimer();
94
- this.clearSmsTimer();
95
- // 注意:这里不能关跳转期 loading。组件销毁发生在导航确认时,而首页真正渲染上屏
96
- // 还在其后,遮罩要撑过这段时间,由 closeRedirectLoading 收口
97
- },
98
- created() {
99
- this.init();
92
+ beforeDestroy() {
93
+ this.clearQrLoginTimer();
94
+ this.clearSmsTimer();
95
+ },
96
+ created() {
97
+ this.init();
100
98
  },
101
99
  mounted() {
102
100
  // if (this.loginForm.username === '') {
@@ -242,25 +240,23 @@ export default {
242
240
  }
243
241
  }
244
242
 
245
- // 登录接口、部署前缀、用户信息和菜单请求期间并行下载业务模块,
246
- // 路由守卫会复用同一个 Promise,不增加登录结果等待链路。
247
- preloadBusinessFeatures();
248
-
249
243
  this.$store
250
- .dispatch("user/login2", reqData)
251
- .then((res) => {
252
- if (res.type == "success") {
253
- localStorage.removeItem("currentMenuId");
254
- this.rememberPassword();
255
- window.removeEventListener("keydown", this.keyDown, false);
256
- // 登录成功后的跳转链路(getWebPrefix → 路由守卫拉用户信息/生成动态路由 →
257
- // 首页渲染上屏)耗时较长,换成挂在 body 上的全屏遮罩顶住这段时间,
258
- // 防止登录页被再次操作;由 closeRedirectLoading 统一收口
259
- loadingObj.close();
260
- this.openRedirectLoading();
261
- this.handleRedirectUrl();
262
- } else {
263
- setTimeout(function () {
244
+ .dispatch("user/login2", reqData)
245
+ .then((res) => {
246
+ setTimeout(function () {
247
+ loadingObj.close();
248
+ }, 200);
249
+ if (res.type == "success") {
250
+ localStorage.removeItem("currentMenuId");
251
+ this.rememberPassword();
252
+ /*this.$router.push({
253
+ path: '/'
254
+ });*/
255
+ this.loading = false;
256
+ window.removeEventListener("keydown", this.keyDown, false);
257
+ this.handleRedirectUrl();
258
+ } else {
259
+ setTimeout(function () {
264
260
  loadingObj.close();
265
261
  }, 200);
266
262
  }
@@ -453,41 +449,33 @@ export default {
453
449
  clearInterval(this.smsTimer);
454
450
  this.smsTimer = null;
455
451
  }
456
- },
457
- mobileLogin() {},
452
+ },
453
+ mobileLogin() {},
458
454
  handleRedirectUrl(redirect) {
459
- // SSO、扫码等非账号密码入口也在跳转前启动预加载。
460
- preloadBusinessFeatures();
461
455
  redirect = redirect || this.$route.query.redirect;
462
- let path = null;
463
- if (redirect && redirect.startsWith("/outLinkView")) {
456
+ let path = null;
457
+ if (redirect && redirect.startsWith("/outLinkView")) {
464
458
  path = this.addParamToUrl(redirect, "hasToken", "true");
465
459
  } else {
466
460
  path = "/";
467
461
  }
468
462
  this.getWebPrefix((webPrefix) => {
469
463
  let prefix = webPrefix ? "/" + webPrefix : "";
470
- let ssotoken = this.searchParam.gatewayToken;
471
- let xtoken = this.searchParam.xtoken;
472
- if (prefix && prefix !== window.WEB_PREFIX) {
473
- // 整页跳转,loading 随页面卸载消失
474
- location.href = prefix + "/index.html" + path;
475
- } else if (!!ssotoken || !!xtoken) {
476
- prefix = prefix || window.WEB_PREFIX;
477
- location.href = prefix + "/index.html" + path;
478
- } else {
479
- // 遮罩不在 push 的回调里收口:守卫内的重定向会让原始导航走 onAbort,
480
- // 而真正的导航仍在继续,统一交给 openRedirectLoading 里注册的 afterEach
481
- this.$router.push({
482
- path: path,
483
- });
484
- }
485
- }, () => {
486
- // getWebPrefix 请求失败:恢复登录页可操作,避免 loading 永久卡死
487
- this.closeRedirectLoading(true);
488
- });
489
- /*if (redirect && redirect.startsWith("/outLinkView")) {
490
- let path = this.addParamToUrl(redirect, "hasToken", "true");
464
+ let ssotoken = this.searchParam.gatewayToken;
465
+ let xtoken = this.searchParam.xtoken;
466
+ if (prefix && prefix !== window.WEB_PREFIX) {
467
+ location.href = prefix + "/index.html" + path;
468
+ } else if (!!ssotoken || !!xtoken) {
469
+ prefix = prefix || window.WEB_PREFIX;
470
+ location.href = prefix + "/index.html" + path;
471
+ } else {
472
+ this.$router.push({
473
+ path: path,
474
+ });
475
+ }
476
+ });
477
+ /*if (redirect && redirect.startsWith("/outLinkView")) {
478
+ let path = this.addParamToUrl(redirect, "hasToken", "true");
491
479
  this.$router.push({
492
480
  path: path
493
481
  });
@@ -504,104 +492,23 @@ export default {
504
492
  } else {
505
493
  url +=
506
494
  (url.indexOf("?") === -1 ? "?" : "&") + paramKey + "=" + paramValue;
507
- }
508
- return url;
509
- },
510
- getWebPrefix(callback, errorCallback) {
511
- this.$http({
512
- url: USER_PREFIX + "/auth/getWebPrefix",
513
- method: `post`,
495
+ }
496
+ return url;
497
+ },
498
+ getWebPrefix(callback) {
499
+ this.$http({
500
+ url: USER_PREFIX + "/auth/getWebPrefix",
501
+ method: `post`,
514
502
  data: {},
515
503
  isLoading: true,
516
504
  success: (res) => {
517
- let webPrefix = res.objx;
518
- callback && callback(webPrefix);
519
- },
520
- fail: () => {
521
- errorCallback && errorCallback();
522
- },
523
- error: () => {
524
- errorCallback && errorCallback();
525
- },
526
- });
527
- },
528
- /**
529
- * 打开登录跳转期的全屏遮罩。
530
- * 不能用 fullscreen:true —— element 的全屏 loading 是全局单例(fullscreenLoading),
531
- * request.js 里 isLoading 的请求(getWebPrefix 就是)用的正是这个单例,请求结束会把
532
- * 它一起 close 掉。这里传 target:body + fullscreen:false 拿独立实例,遮罩挂在 body 上
533
- * 不随登录组件销毁而消失,全屏样式手动补。
534
- */
535
- openRedirectLoading() {
536
- this.closeRedirectLoading();
537
- let loading = this.$baseLoading({
538
- target: document.body,
539
- fullscreen: false,
540
- lock: true,
541
- });
542
- let el = loading && loading.$el;
543
- if (el) {
544
- el.style.position = "fixed";
545
- el.style.top = "0";
546
- el.style.left = "0";
547
- el.style.width = "100%";
548
- el.style.height = "100%";
549
- el.style.zIndex = 9999;
550
- }
551
- this.redirectLoadingObj = loading;
552
- // 收口挂在 afterEach 上:路由守卫里的重定向会让原始 push 走 onAbort,但新导航
553
- // 其实还在继续(实测 onAbort 在 +211ms 触发,首页 +577ms 才绘制完成),所以不能
554
- // 用 push 的回调收口。afterEach 只在导航真正确认后触发一次,且带最终目标路由
555
- this.redirectAfterEachOff = this.$router.afterEach((to) => {
556
- if (String((to && to.path) || "").indexOf("/login") >= 0) {
557
- // 兜了一圈又回到登录页(如守卫拉用户信息失败),恢复登录页可操作
558
- this.closeRedirectLoading(true);
559
- } else {
560
- this.closeRedirectLoadingAfterPaint();
561
- }
562
- });
563
- // 兜底:跳转链路既不 complete 也不 abort 时不至于永久锁死
564
- this.redirectLoadingTimer = setTimeout(() => {
565
- this.closeRedirectLoading(true);
566
- }, 20000);
567
- },
568
- /**
569
- * 等目标页真正绘制上屏后再撤遮罩:$nextTick 等 Vue 完成 patch,
570
- * 双 rAF 等浏览器完成这一帧的绘制
571
- */
572
- closeRedirectLoadingAfterPaint() {
573
- this.$nextTick(() => {
574
- requestAnimationFrame(() => {
575
- requestAnimationFrame(() => {
576
- this.closeRedirectLoading();
577
- });
578
- });
579
- });
580
- },
581
- /**
582
- * 登录跳转期 loading 统一收口:上屏完成、跳转中止、getWebPrefix 失败、超时兜底时调用。
583
- * reenable=true 时恢复登录页可操作(跳转失败仍停留在登录页的场景)
584
- */
585
- closeRedirectLoading(reenable) {
586
- if (this.redirectAfterEachOff) {
587
- this.redirectAfterEachOff();
588
- this.redirectAfterEachOff = null;
589
- }
590
- if (this.redirectLoadingTimer) {
591
- clearTimeout(this.redirectLoadingTimer);
592
- this.redirectLoadingTimer = null;
593
- }
594
- if (this.redirectLoadingObj) {
595
- this.redirectLoadingObj.close();
596
- this.redirectLoadingObj = null;
597
- }
598
- if (reenable) {
599
- this.loading = false;
600
- window.addEventListener("keydown", this.keyDown, false);
601
- }
602
- },
603
- handleGatewayTokenLogin() {
604
- let ssotoken = this.searchParam.gatewayToken;
505
+ let webPrefix = res.objx;
506
+ callback && callback(webPrefix);
507
+ },
508
+ });
509
+ },
510
+ handleGatewayTokenLogin() {
511
+ let ssotoken = this.searchParam.gatewayToken;
605
512
  let companycode = this.searchParam.entCode;
606
513
  this.$store
607
514
  .dispatch("user/login2", { ssotoken, companycode })
@@ -643,17 +550,18 @@ export default {
643
550
  this.qrExpired = false;
644
551
  this.qrBase64 = url;
645
552
  this.createQrLoginTimer();
646
- });
647
- },
648
- // 生成二维码
649
- async createQR(content, options) {
650
- const { default: QRCode } = await import(
651
- /* webpackChunkName: "login-qrcode" */ "qrcode"
652
- );
653
- return QRCode.toDataURL(content, options);
654
- },
655
- changeTab() {
656
- // this.initQR();
553
+ });
554
+ },
555
+ // 生成二维码
556
+ createQR(content, options) {
557
+ return new Promise((resolve, reject) => {
558
+ QRcode.toDataURL(content, options).then((url) => {
559
+ resolve(url);
560
+ });
561
+ });
562
+ },
563
+ changeTab() {
564
+ // this.initQR();
657
565
  // return
658
566
  if (this.activeName == "third") {
659
567
  this.initQR();
@@ -1,31 +0,0 @@
1
- let installPromise = null;
2
-
3
- /**
4
- * 登录页只安装认证所需的最小能力。表格、Excel、上传和业务弹框等全局能力
5
- * 在第一次进入业务路由前按需安装,避免进入 /login 时下载整套后台模块。
6
- */
7
- export function ensureBusinessFeatures() {
8
- if (!installPromise) {
9
- installPromise = import(
10
- /* webpackChunkName: "business-core" */ "./businessFeatures"
11
- ).catch((error) => {
12
- // 网络抖动或发布切换导致 chunk 失败时,允许下一次导航重新尝试
13
- installPromise = null;
14
- throw error;
15
- });
16
- }
17
- return installPromise;
18
- }
19
-
20
- /**
21
- * 提前触发业务模块下载和安装,但不阻塞当前交互。
22
- * 登录提交后会与认证、用户信息和菜单请求并行;真正进入业务路由时
23
- * ensureBusinessFeatures 会复用同一个 Promise,失败时仍由路由守卫统一提示。
24
- */
25
- export function preloadBusinessFeatures() {
26
- const promise = ensureBusinessFeatures();
27
- promise.catch((error) => {
28
- console.warn("[bootstrap] 业务模块预加载失败,将在导航时重试", error);
29
- });
30
- return promise;
31
- }