cloud-web-corejs 1.0.54-dev.767 → 1.0.54-dev.768

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/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "cloud-web-corejs",
3
3
  "private": false,
4
- "version": "1.0.54-dev.767",
4
+ "version": "1.0.54-dev.768",
5
5
  "scripts": {
6
6
  "dev": "vue-cli-service serve",
7
7
  "lint": "eslint --ext .js,.vue src",
@@ -40,6 +40,17 @@ import LuckyExcel from "luckyexcel";
40
40
  //导入库export.js 这个文件是es6的,不能在普通的HTML文件直接引入js文件(虽然都是js文件,但是有区别,具体请百度es6与es5)!需要把es6转es5才可以直接引入使用!
41
41
  import { exportExcel, generateExcelFile } from "./export";
42
42
  import { saveAs } from "file-saver";
43
+ import {
44
+ getPublicAssetUrl,
45
+ loadStyles,
46
+ } from "@base/utils/externalAssets";
47
+
48
+ const luckysheetStyleUrls = [
49
+ "luckysheet/plugins/css/pluginsCss.css",
50
+ "luckysheet/plugins/plugins.css",
51
+ "luckysheet/css/luckysheet.css",
52
+ "luckysheet/assets/iconfont/iconfont.css",
53
+ ].map(getPublicAssetUrl);
43
54
 
44
55
  export default {
45
56
  name: "luckysheet",
@@ -62,12 +73,23 @@ export default {
62
73
  };
63
74
  },
64
75
  beforeDestroy() {
65
- if (luckysheet) {
76
+ if (window.luckysheet) {
66
77
  this.exitEditMode();
67
- luckysheet.destroy();
78
+ window.luckysheet.destroy();
68
79
  }
69
80
  },
70
- mounted() {
81
+ async mounted() {
82
+ try {
83
+ await loadStyles(luckysheetStyleUrls);
84
+ } catch (error) {
85
+ console.error("Luckysheet 样式加载失败", error);
86
+ return;
87
+ }
88
+ if (this._isDestroyed) return;
89
+ if (!window.luckysheet) {
90
+ console.error("luckysheet未初始化");
91
+ return;
92
+ }
71
93
  // In some cases, you need to use $nextTick
72
94
  // this.$nextTick(() => {
73
95
  let config = this.options.config || {};
package/src/index.js CHANGED
@@ -1,11 +1,22 @@
1
1
  /**version-1.0*/
2
2
  import "./public-path"; // qiankun publicPath 修正,必须保持第一行
3
- import Vue from "vue";
4
- import Cookies from "js-cookie";
5
- import "normalize.css/normalize.css"; // a modern alternative to CSS resets
6
- import Element from "element-ui";
7
- import "@/styles/element-variables.scss";
8
- import "@/styles/index.scss"; // global css
3
+ import Vue from "vue";
4
+ import Cookies from "js-cookie";
5
+ import "normalize.css/normalize.css"; // a modern alternative to CSS resets
6
+ import Button from "element-ui/lib/button";
7
+ import Checkbox from "element-ui/lib/checkbox";
8
+ import Dialog from "element-ui/lib/dialog";
9
+ import Form from "element-ui/lib/form";
10
+ import FormItem from "element-ui/lib/form-item";
11
+ import Input from "element-ui/lib/input";
12
+ import TabPane from "element-ui/lib/tab-pane";
13
+ import Tabs from "element-ui/lib/tabs";
14
+ import Tooltip from "element-ui/lib/tooltip";
15
+ import Loading from "element-ui/lib/loading";
16
+ import Message from "element-ui/lib/message";
17
+ import MessageBox from "element-ui/lib/message-box";
18
+ import "@/styles/element-variables.scss";
19
+ import "@/styles/index.scss"; // 登录页与业务页共用同一套全局样式
9
20
 
10
21
  import App from "@base/App";
11
22
  import store from "@base/store";
@@ -13,189 +24,13 @@ import router, { resetRouter } from "@base/router";
13
24
  import settings from "@/settings";
14
25
  import { setToken as setAuthToken } from "@base/utils/auth";
15
26
 
16
- import "@/icons"; // icon
17
- import "@base/permission"; // permission control
27
+ import "@/icons"; // icon
28
+ import "@base/permission"; // permission control
29
+ import "@base/resources/js/base/common.js"; // request 序列化在登录接口阶段即依赖
18
30
 
19
- import * as filters from "@base/filters"; // global filters
20
-
21
- // main.js 文件添加以下代码
22
-
23
- import BaseKeepAlive from "@base/utils/keepAlive.js";
24
-
25
- Vue.component("BaseKeepAlive", BaseKeepAlive);
26
-
27
- import VXETable from "vxe-table";
28
- import "vxe-table/lib/style.css";
29
-
30
- // 引入扩展插件
31
- import "@base/components/table/plugins/extend-cell-area/vxe-table-extend-cell-area.es6.min.js";
32
- import "@base/components/table/plugins/extend-cell-area/vxe-table-extend-cell-area.min.css";
33
-
34
- /* // 设置授权信息
35
- VXETable.setup({
36
- // showAuthLog: true, // 是否在控制台显示授权信息,专业版支持关闭
37
- // authId: 'gbeumewkoyt2rhf9', // 获取授权后在官网登录后进入“用户中心”查看
38
- authId: 'gbeumewkoyt2rhft', // 获取授权后在官网登录后进入“用户中心”查看
39
- // onAuth (e) {
40
- // // 打印授权状态
41
- // console.log(e)
42
- // }
43
- }) */
44
-
45
- import Vab from "@base/utils/vab";
46
-
47
- Vue.use(Vab);
48
-
49
- Vue.use(VXETable);
50
-
51
- /* import tablePluns from "@/mixins/table/index.js"
52
- VXETable.use(tablePluns) */
53
-
54
- import "@base/components/table/index.js";
55
-
56
- import drag from "@base/directive/el-drag-dialog/index.js";
57
-
58
- Vue.use(drag.install);
59
-
60
- import elDialogCenter from "@base/directive/el-dialog-center/index.js";
61
-
62
- Vue.use(elDialogCenter.install);
63
-
64
- import ErroreBox from "@base/components/errorMsg/index.js";
65
-
66
- Vue.use(ErroreBox);
67
-
68
- import ExcelExport from "@base/components/excelExport/index.js";
69
-
70
- Vue.use(ExcelExport);
71
-
72
- import ExcelImport from "@base/components/excelImport/index.js";
73
-
74
- Vue.use(ExcelImport);
75
-
76
- import ConfirmDialog from "@base/components/confirmDialog/index.js";
77
-
78
- Vue.use(ConfirmDialog);
79
-
80
- import baseAlert from "@base/components/baseAlert/index.js";
81
-
82
- Vue.use(baseAlert);
83
-
84
- import FormDialog from "@base/components/formDialog/index.js";
85
-
86
- Vue.use(FormDialog);
87
-
88
- import permission from "@base/directive/permission";
89
-
90
- Vue.use(permission);
91
-
92
- import VabUpload from "@base/components/VabUpload/index.js";
93
-
94
- Vue.use(VabUpload);
95
-
96
- Vue.component("baseUpload", () =>
97
- import("@base/components/VabUpload/view.vue")
98
- );
99
-
100
- import baseAttachment from "@base/components/baseAttachment/install";
101
-
102
- Vue.use(baseAttachment);
103
- Vue.component("attachmentShowList", () =>
104
- import("@base/components/baseAttachment/showList")
105
- );
106
-
107
- import "@base/resources/js/base/common.js";
108
-
109
- import vxeFilter from "@base/components/table/vxeFilter/index.js";
110
-
111
- Vue.use(vxeFilter);
112
-
113
- import baseTableExport from "@base/components/excelExport/button.vue";
114
-
115
- Vue.component(baseTableExport.name, baseTableExport);
116
-
117
- /* Vue.component('extendedProperties', () => import('@base/components/extendedProperties/view.vue')); */
118
-
119
- import LimitNumber from "@base/directive/LimitNumber/index.js";
120
-
121
- Vue.use(LimitNumber);
122
-
123
- import baseInputNumber from "@base/components/baseInputNumber/index.vue";
124
-
125
- Vue.component(baseInputNumber.name, baseInputNumber);
126
-
127
- import elReadonly from "@base/directive/el-readonly/index.js";
128
-
129
- Vue.use(elReadonly);
130
-
131
- /*import cnPrint from "@base/components/cnPrint/index.js"
132
-
133
- Vue.use(cnPrint);*/
134
-
135
- /* Vue.component('oplogTable', () => import('@base/components/oplogTable/index.vue')); */
136
- import oplogTable from "@base/components/oplogTable/index.vue";
137
-
138
- Vue.component(oplogTable.name, oplogTable);
139
-
140
- import baseInputBatch from "@base/components/baseInputBatch/index.vue";
141
-
142
- Vue.component(baseInputBatch.name, baseInputBatch);
143
-
144
- import baseArea from "@base/components/baseArea/index.vue";
145
-
146
- Vue.component(baseArea.name, baseArea);
147
-
148
- Vue.component("baseInputExport", () =>
149
- import("@base/components/baseInputExport/index.vue")
150
- );
151
-
152
- Vue.component("advancedSearchDialog", () =>
153
- import("@base/components/advancedSearchDialog/index.vue")
154
- );
155
-
156
- import baseTabs from "@base/components/baseTabs/index.vue";
157
-
158
- Vue.component(baseTabs.name, baseTabs);
159
-
160
- import baseTabPane from "@base/components/baseTabs/baseTabPane.vue";
161
-
162
- Vue.component(baseTabPane.name, baseTabPane);
163
-
164
- import vb_tabs from "@base/components/vb-tabs/index.vue";
165
- import vb_tab_pane from "@base/components/vb-tabs/tab-pane.vue";
166
- import xTabs from "@base/components/vb-tabs/x-tabs.vue";
167
-
168
- Vue.component(vb_tabs.name, vb_tabs);
169
- Vue.component(vb_tab_pane.name, vb_tab_pane);
170
- Vue.component(xTabs.name, xTabs);
171
-
172
- Vue.component("status-tag", () =>
173
- import("@base/components/statusTag/index.vue")
174
- );
175
-
176
- Vue.component("CodeEditor", () =>
177
- import("@base/components/code-editor/index.vue")
178
- );
179
-
180
- Vue.component("scriptDescriptionButton", () =>
181
- import("@base/components/scriptDescription/button")
182
- );
183
-
184
- Vue.component("scriptTestButton", () =>
185
- import("@base/components/scriptTest/button")
186
- );
187
-
188
- import baseTextarea from "@base/components/base-textarea/index.vue";
189
-
190
- Vue.component(baseTextarea.name, baseTextarea);
191
-
192
- import jsonImport from "@base/components/jsonImport/index.js";
193
-
194
- Vue.use(jsonImport);
195
-
196
- import langImport from "@base/components/langImport/index.js";
197
-
198
- Vue.use(langImport);
31
+ import installLoginVab from "@base/utils/loginVab";
32
+
33
+ Vue.use(installLoginVab);
199
34
 
200
35
  //vform
201
36
  // import '@base/components/xform/utils/directive'
@@ -215,17 +50,27 @@ if (process.env.NODE_ENV === "production") {
215
50
  mockXHR() */
216
51
  }
217
52
 
218
- Vue.use(Element, {
219
- size: Cookies.get("size") || "medium", // set element-ui default size
220
- // locale: enLang // 如果使用中文,无需设置,请删除
221
- });
222
-
223
- // register global utility filters
224
- Object.keys(filters).forEach((key) => {
225
- Vue.filter(key, filters[key]);
226
- });
227
-
228
- Vue.config.productionTip = false;
53
+ [
54
+ Button,
55
+ Checkbox,
56
+ Dialog,
57
+ Form,
58
+ FormItem,
59
+ Input,
60
+ TabPane,
61
+ Tabs,
62
+ Tooltip,
63
+ ].forEach((component) => Vue.use(component));
64
+ Vue.use(Loading.directive);
65
+ Vue.prototype.$ELEMENT = { size: Cookies.get("size") || "medium" };
66
+ Vue.prototype.$loading = Loading.service;
67
+ Vue.prototype.$message = Message;
68
+ Vue.prototype.$msgbox = MessageBox;
69
+ Vue.prototype.$alert = MessageBox.alert;
70
+ Vue.prototype.$confirm = MessageBox.confirm;
71
+ Vue.prototype.$prompt = MessageBox.prompt;
72
+
73
+ Vue.config.productionTip = false;
229
74
 
230
75
  let instance = null;
231
76
 
package/src/permission.js CHANGED
@@ -1,15 +1,17 @@
1
1
  /**version-1.0*/
2
2
  import router from "@base/router";
3
3
  import store from "./store";
4
- import {
5
- Message
6
- } from "element-ui";
4
+ import Message from "element-ui/lib/message";
7
5
  import NProgress from "nprogress"; // progress bar
8
6
  import "nprogress/nprogress.css"; // progress bar style
9
7
  import {
10
8
  getToken
11
9
  } from "./utils/auth"; // get token from cookie
12
- import settingConfig from "@/settings";
10
+ import settingConfig from "@/settings";
11
+ import {
12
+ ensureBusinessFeatures,
13
+ preloadBusinessFeatures,
14
+ } from "@base/bootstrap/business";
13
15
 
14
16
  import {
15
17
  saveAccessMenuLog
@@ -20,23 +22,63 @@ NProgress.configure({
20
22
  showSpinner: false,
21
23
  }); // NProgress Configuration
22
24
 
23
- const whiteList = [
25
+ const whiteList = [
24
26
  "/login",
25
27
  "/auth-redirect",
26
28
  "/report/vform/out_render",
27
29
  "/report/xform/out_render",
28
- ]; // no redirect whitelist
30
+ ]; // no redirect whitelist
31
+
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
+ }
29
51
 
30
52
  // qiankun 保活:子应用容器被隐藏期间暂停一切路由响应(配合包入口 update 生命周期)
31
- router.beforeEach((to, from, next) => {
32
- if (store.state.micro && store.state.micro.routerPaused) {
53
+ router.beforeEach((to, from, next) => {
54
+ if (store.state.micro && store.state.micro.routerPaused) {
33
55
  next(false);
34
56
  return;
35
57
  }
36
- next();
37
- });
38
-
39
- router.beforeEach((to, from, next) => {
58
+ next();
59
+ });
60
+
61
+ router.beforeEach((to, from, next) => {
62
+ if (!needsBusinessFeatures(to)) {
63
+ next();
64
+ return;
65
+ }
66
+ 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) => {
40
82
  // start progress bar
41
83
  NProgress.start();
42
84
 
@@ -116,9 +158,23 @@ router.beforeEach((to, from, next) => {
116
158
  })
117
159
 
118
160
 
119
- });
120
-
121
- router.onError((error) => {
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
+
172
+ ensureBusinessFeatures()
173
+ .then(() => next())
174
+ .catch((error) => handleBusinessFeatureError(error, next));
175
+ });
176
+
177
+ router.onError((error) => {
122
178
  // finish progress bar
123
179
  console.error(error);
124
180
  NProgress.done();
@@ -1,7 +1,6 @@
1
- import { constantRoutes } from "@base/router";
2
- import { asyncRoutes, sidebarRoutes } from "@base/router/modules/system";
3
- import Layout from "@/layout";
4
- import { getRouters } from "../../api/menu";
1
+ import { constantRoutes } from "@base/router";
2
+ import { asyncRoutes, sidebarRoutes } from "@base/router/modules/system";
3
+ import { getRouters } from "../../api/menu";
5
4
  import router from "@base/router";
6
5
  import storeConfig from "../config/index";
7
6
  import settings from "@/settings";
@@ -10,12 +9,12 @@ import {
10
9
  resolveOwner,
11
10
  dispatchMenus,
12
11
  } 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";
12
+
13
+ const Layout = () => import(/* webpackChunkName: "layout" */ "@/layout");
14
+ const MenuFallback = () =>
15
+ import(
16
+ /* webpackChunkName: "menu-fallback" */ "@base/views/user/menuFallback/index.vue"
17
+ );
19
18
 
20
19
  // qiankun 宿主态(仅 settings.microMainEnabled 的主应用登录后填充)
21
20
  let hostRegistryApps = [];
@@ -230,34 +229,37 @@ const actions = {
230
229
  },
231
230
  // qiankun 宿主编排:拉注册表 → 按归属切分 → 注册子应用,再走常规路由生成。
232
231
  // 非宿主(未开 microMainEnabled / 自身被 qiankun 挂载)直接透传。
233
- setupHostAndRoutes({ dispatch, commit, rootState }, rows = []) {
232
+ setupHostAndRoutes({ dispatch, commit, rootState }, rows = []) {
234
233
  let isHost =
235
234
  settings.microMainEnabled === true && !window.__POWERED_BY_QIANKUN__;
236
- if (!isHost) {
237
- return dispatch("setSidebarRoute", rows);
238
- }
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
- });
260
- },
235
+ if (!isHost) {
236
+ return dispatch("setSidebarRoute", rows);
237
+ }
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
+ );
262
+ },
261
263
  // qiankun 子应用模式:主应用经 props 下发菜单,替代 getRouters 自拉
262
264
  generateRoutesFromProps({ dispatch, rootState }, menus = []) {
263
265
  // 调试覆盖装载时允许 URL 直达未配菜单的页面(handleMenuRoutes 追加兜底路由)
@@ -1,5 +1,3 @@
1
- import variables from '@/styles/element-variables.scss'
2
-
3
1
  const state = {
4
2
  tagsView: true,
5
3
  }
@@ -1,5 +1,4 @@
1
1
  import store from "../index.js";
2
- import _ from "lodash";
3
2
  import { isRepeatable, viewKey } from "@base/utils/repeatOpen";
4
3
 
5
4
  let state, mutations, actions;
@@ -201,7 +200,7 @@ actions = {
201
200
  let newView0 = { ...(view || window.$vueRoot.$route) };
202
201
  delete newView0.matched;
203
202
  newView0.meta.title = title;
204
- let newView = _.cloneDeep(newView0);
203
+ let newView = JSON.parse(JSON.stringify(newView0));
205
204
 
206
205
  commit("UPDATE_VISITED_VIEW", newView);
207
206
  },
@@ -10,7 +10,6 @@ import {
10
10
  } from "../../api/user";
11
11
  import { getToken, setToken, clearToken } from "../../utils/auth";
12
12
  import router, { resetRouter } from "@base/router";
13
- import { unmountAllMicroApps } from "@base/microRouter";
14
13
  import { getDispermissions } from "../../api/menu";
15
14
  import settingConfig from "@/settings";
16
15
  import storeConfig from "../config/index";
@@ -330,14 +329,21 @@ actions = {
330
329
  configUtil.clearToken();
331
330
  // qiankun 宿主:登出必须真正卸载保活中的子应用,
332
331
  // 否则换账号登录后切回子应用会复活上一个用户的实例(旧角色/旧路由/旧页面数据)
333
- unmountAllMicroApps();
334
- configUtil.resetRouter();
335
- sessionStorage.removeItem("easyweb");
336
-
337
- dispatch("tagsView/delAllViews", null, {
338
- root: true,
339
- });
340
- resolve();
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");
341
+
342
+ dispatch("tagsView/delAllViews", null, {
343
+ root: true,
344
+ });
345
+ resolve();
346
+ });
341
347
  });
342
348
  },
343
349
 
@@ -0,0 +1,62 @@
1
+ const assetPromises = Object.create(null);
2
+
3
+ export function getPublicAssetUrl(path) {
4
+ const baseUrl = (process.env.BASE_URL || "/").replace(/\/?$/, "/");
5
+ return baseUrl + String(path || "").replace(/^\//, "");
6
+ }
7
+
8
+ function findAsset(tagName, attribute, url) {
9
+ return Array.from(document.getElementsByTagName(tagName)).find(
10
+ (element) => element.getAttribute(attribute) === url
11
+ );
12
+ }
13
+
14
+ export function loadStyle(href, attributes = {}) {
15
+ const key = "style:" + href;
16
+ if (assetPromises[key]) return assetPromises[key];
17
+ const existing = findAsset("link", "href", href);
18
+ if (existing) return Promise.resolve(existing);
19
+
20
+ assetPromises[key] = new Promise((resolve, reject) => {
21
+ const link = document.createElement("link");
22
+ link.rel = "stylesheet";
23
+ link.href = href;
24
+ Object.keys(attributes).forEach((name) => {
25
+ link.setAttribute(name, attributes[name]);
26
+ });
27
+ link.onload = () => resolve(link);
28
+ link.onerror = () => {
29
+ delete assetPromises[key];
30
+ link.remove();
31
+ reject(new Error("样式加载失败:" + href));
32
+ };
33
+ document.head.appendChild(link);
34
+ });
35
+ return assetPromises[key];
36
+ }
37
+
38
+ export function loadStyles(hrefs) {
39
+ return Promise.all(hrefs.map((href) => loadStyle(href)));
40
+ }
41
+
42
+ export function loadScript(src) {
43
+ const key = "script:" + src;
44
+ if (assetPromises[key]) return assetPromises[key];
45
+ const existing = findAsset("script", "src", src);
46
+ if (existing) return Promise.resolve(existing);
47
+
48
+ assetPromises[key] = new Promise((resolve, reject) => {
49
+ const script = document.createElement("script");
50
+ script.src = src;
51
+ script.async = true;
52
+ script.onload = () => resolve(script);
53
+ script.onerror = () => {
54
+ delete assetPromises[key];
55
+ script.remove();
56
+ reject(new Error("脚本加载失败:" + src));
57
+ };
58
+ document.head.appendChild(script);
59
+ });
60
+ return assetPromises[key];
61
+ }
62
+
@@ -0,0 +1,68 @@
1
+ // API 模块沿用 USER_PREFIX 等全局常量;完整 Vab 延迟加载后,登录阶段需先恢复
2
+ // 这项原本由 vab.js 模块初始化完成的副作用。
3
+ import "@base/utils/prefixGlobals";
4
+ import Loading from "element-ui/lib/loading";
5
+ import MessageBox from "element-ui/lib/message-box";
6
+ import request from "@base/utils/request";
7
+
8
+ /**
9
+ * 登录阶段需要的最小 Vab 能力。完整 Vab 会连带 lodash、moment、XEUtils、
10
+ * 图片预览等后台依赖,改为进入业务路由后再覆盖安装。
11
+ */
12
+ export default function installLoginVab(Vue) {
13
+ Vue.prototype.$baseEventBus = new Vue();
14
+ const $http = async function (option = {}) {
15
+ if (!option.loadingTarget && this.$el) {
16
+ option.loadingTarget = this.$el;
17
+ }
18
+ if (!option.vueTarget) {
19
+ option.vueTarget = this;
20
+ }
21
+ return option.sync === false ? await request(option) : request(option);
22
+ };
23
+ Object.assign($http, request);
24
+ $http.prototype = request.prototype;
25
+ Vue.prototype.$http = $http;
26
+
27
+ Vue.prototype.$baseLoading = function (option = {}) {
28
+ const opts = {
29
+ lock: true,
30
+ background: "hsla(0,0%,100%,.8)",
31
+ target: this.$el,
32
+ ...option,
33
+ };
34
+ if (option.index) {
35
+ opts.spinner = "vab-loading-type" + option.index;
36
+ }
37
+ return Loading.service(opts);
38
+ };
39
+
40
+ Vue.prototype.$baseAlert = function (content, title, callback) {
41
+ return MessageBox.alert(content, title || "提示", {
42
+ confirmButtonText: "确定",
43
+ dangerouslyUseHTMLString: true,
44
+ target: this.$el,
45
+ confirmButtonClass: "icondui",
46
+ customClass: "dialog-style",
47
+ callback,
48
+ });
49
+ };
50
+
51
+ Vue.prototype.$t1 = function (path, values) {
52
+ const root = this.$root || window.$vueRoot;
53
+ if (!root || !root._i18n) return path;
54
+ const i18n = root.$i18n;
55
+ if (i18n.locale === "zh" || !i18n.te(path)) {
56
+ return i18n._t(path, i18n.locale, { zh: { [path]: path } }, null, values);
57
+ }
58
+ return i18n.t(path, values) || path;
59
+ };
60
+
61
+ Vue.prototype.$t2 = function (defaultValue, path, values) {
62
+ const root = this.$root || window.$vueRoot;
63
+ if (!root || !root._i18n) return defaultValue;
64
+ const i18n = root.$i18n;
65
+ const key = path || defaultValue;
66
+ return i18n.te(key) ? i18n.t(key, values) : defaultValue;
67
+ };
68
+ }
@@ -1,8 +1,22 @@
1
1
  import settingConfig from "@/settings";
2
+ import { loadScript } from "@base/utils/externalAssets";
2
3
 
3
- export function initPddLog() {
4
- if (!settingConfig.pddLogEnabled || !window.PDD_OPEN_init) return
5
- pddIntfHandle();
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
+ }
6
20
  }
7
21
 
8
22
  function getPageCode(callback) {
@@ -76,7 +90,7 @@ function handleOrderLog(pati) {
76
90
  async function pddIntfHandle() {
77
91
  getPageCode(async (pageCode) => {
78
92
  if (!pageCode) return
79
- PDD_OPEN_init({
93
+ window.PDD_OPEN_init({
80
94
  code: pageCode
81
95
  // 对于获取 code 接口或未登录态,可不传 code:PDD_OPEN_init({}, function () { ... })
82
96
  }, function () {
@@ -0,0 +1,11 @@
1
+ import prefixConfig from "@base/prefixConfig.js";
2
+
3
+ export function installPrefixGlobals(target) {
4
+ if (target) {
5
+ Object.assign(target, prefixConfig);
6
+ }
7
+ }
8
+
9
+ if (typeof window !== "undefined") {
10
+ installPrefixGlobals(window);
11
+ }
@@ -1,6 +1,6 @@
1
- import { encode } from "js-base64";
2
- import settingConfig from "@/settings";
3
- import QRcode from "qrcode";
1
+ import { encode } from "js-base64";
2
+ import settingConfig from "@/settings";
3
+ import { preloadBusinessFeatures } from "@base/bootstrap/business";
4
4
 
5
5
  function getUrlParams(url) {
6
6
  let result = {};
@@ -234,15 +234,19 @@ export default {
234
234
  reqData = { username: username, smsCode: smsCode, mode: "sms" };
235
235
  }
236
236
 
237
- if (settingConfig.i18nEnabled) {
237
+ if (settingConfig.i18nEnabled) {
238
238
  //登录传语种到后台
239
239
  let language = this.$i18n?.locale;
240
240
  if (language && language != "zh") {
241
241
  reqData.language = language;
242
- }
243
- }
244
-
245
- this.$store
242
+ }
243
+ }
244
+
245
+ // 登录接口、部署前缀、用户信息和菜单请求期间并行下载业务模块,
246
+ // 路由守卫会复用同一个 Promise,不增加登录结果等待链路。
247
+ preloadBusinessFeatures();
248
+
249
+ this.$store
246
250
  .dispatch("user/login2", reqData)
247
251
  .then((res) => {
248
252
  if (res.type == "success") {
@@ -451,8 +455,10 @@ export default {
451
455
  }
452
456
  },
453
457
  mobileLogin() {},
454
- handleRedirectUrl(redirect) {
455
- redirect = redirect || this.$route.query.redirect;
458
+ handleRedirectUrl(redirect) {
459
+ // SSO、扫码等非账号密码入口也在跳转前启动预加载。
460
+ preloadBusinessFeatures();
461
+ redirect = redirect || this.$route.query.redirect;
456
462
  let path = null;
457
463
  if (redirect && redirect.startsWith("/outLinkView")) {
458
464
  path = this.addParamToUrl(redirect, "hasToken", "true");
@@ -640,12 +646,11 @@ export default {
640
646
  });
641
647
  },
642
648
  // 生成二维码
643
- createQR(content, options) {
644
- return new Promise((resolve, reject) => {
645
- QRcode.toDataURL(content, options).then((url) => {
646
- resolve(url);
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);
649
654
  },
650
655
  changeTab() {
651
656
  // this.initQR();