@infly/libs 2.0.26 → 2.0.37

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.
Files changed (39) hide show
  1. package/bin/cli.js +29 -21
  2. package/build/build-dist/index.js +258 -61
  3. package/build/webpack5/remove-legacy-assets-plugin.js +84 -0
  4. package/build/webpack5/webpack.base.js +228 -20
  5. package/build/webpack5/webpack.base.test.js +59 -0
  6. package/index.js +10 -0
  7. package/module/Permission.js +121 -55
  8. package/module/REST.js +136 -26
  9. package/module/Router.js +15 -0
  10. package/module/Uts.js +380 -331
  11. package/module/cjs/deep-merge.cjs +38 -0
  12. package/module/cjs/page-config.cjs +119 -0
  13. package/module/cjs/request-url-rules.cjs +55 -0
  14. package/package.json +11 -10
  15. package/script/build/command.js +48 -0
  16. package/script/build/env.js +28 -0
  17. package/script/build/git.js +252 -0
  18. package/script/build/preview.js +75 -0
  19. package/script/build/webhook.js +118 -0
  20. package/script/git-automation/check-packages.js +11 -8
  21. package/script/git-automation/git-utils.js +67 -0
  22. package/script/git-automation/index.js +378 -106
  23. package/script/index.js +8 -8
  24. package/script/pts/cloud-scenes.mjs +65 -0
  25. package/script/pts/cloud.js +151 -0
  26. package/script/pts/generate-cloud-params.mjs +210 -0
  27. package/script/pts/generate-cloud-params.test.mjs +67 -0
  28. package/script/webhook/webhook.js +75 -4
  29. package/store/modules/user.js +111 -9
  30. package/tools/auto-export.js +56 -0
  31. package/tools/file-export.js +32 -9
  32. package/tools/file-process.js +3 -0
  33. package/tools/project-preview.js +110 -97
  34. package/dataInit/commonTypeMap.js +0 -31
  35. package/dataInit/marketingActivitiesMap.js +0 -214
  36. package/dataInit/orderMap.js +0 -13
  37. package/dataInit/personalMap.js +0 -19
  38. package/dataInit/settlementMap.js +0 -17
  39. package/types/unused.index.d.ts +0 -71
@@ -2,12 +2,22 @@
2
2
 
3
3
  const path = require("path");
4
4
  const InlineRuntimePlugin = require("./inline-runtime-plugin");
5
+ const RemoveLegacyAssetsPlugin = require("./remove-legacy-assets-plugin");
5
6
 
6
7
  // 环境变量与常量配置
7
8
  const ENV = {
8
- IS_PROD: ["production", "staging"].includes(process.env.NODE_ENV)
9
+ IS_PROD: ["production", "staging"].includes(process.env.NODE_ENV),
10
+ IS_STAGING: process.env.ENV === "staging",
11
+ IS_DEV: process.env.ENV === "development"
9
12
  };
10
13
 
14
+ // 强制禁用 modern mode(staging 环境)
15
+ if (ENV.IS_STAGING) {
16
+ process.env.VUE_CLI_MODERN_MODE = "false";
17
+ process.env.VUE_CLI_MODERN_BUILD = "false";
18
+ console.log("\n\x1b[36m⚡ Building for STAGING with Modern Bundle only (仅现代浏览器)...\x1b[0m");
19
+ }
20
+
11
21
  /**
12
22
  * 解析项目路径(兼容monorepo)
13
23
  * @param {string} dir 相对路径
@@ -33,7 +43,8 @@ function createNodePolyfills() {
33
43
  /**
34
44
  * CSS/SCSS配置
35
45
  */
36
- function cssModule({ additionalData }) {
46
+ function cssModule(cssConfig = {}) {
47
+ const { additionalData = "" } = cssConfig || {};
37
48
  return {
38
49
  loaderOptions: {
39
50
  sass: {
@@ -41,8 +52,13 @@ function cssModule({ additionalData }) {
41
52
  sassOptions: {
42
53
  api: "modern",
43
54
  quietDeps: true,
44
- silenceDeprecations: ["legacy-js-api", "function-units"]
55
+ silenceDeprecations: ["legacy-js-api", "function-units", "import"],
56
+ // 添加以下配置来优化内存使用
57
+ outputStyle: "compressed", // 压缩输出
58
+ sourceMap: false // 禁用 source map(开发环境可启用)
45
59
  },
60
+ // 使用 webpackImporter 来优化模块解析
61
+ webpackImporter: true,
46
62
  additionalData
47
63
  },
48
64
  css: {
@@ -53,23 +69,33 @@ function cssModule({ additionalData }) {
53
69
  }
54
70
  }
55
71
  },
56
- extract: ENV.IS_PROD ? { ignoreOrder: true } : false
72
+ extract: ENV.IS_PROD
73
+ ? {
74
+ ignoreOrder: true,
75
+ // 添加缓存配置
76
+ chunkFilename: "css/[name].[contenthash:8].css"
77
+ }
78
+ : false
57
79
  };
58
80
  }
59
81
 
60
82
  /**
61
83
  * 基础 configureWebpack 配置
62
84
  */
63
- function configureWebpack({ name }) {
64
- return {
85
+ function configureWebpack(extraConfig = {}) {
86
+ const { name, alias } = extraConfig || {};
87
+ const config = {
65
88
  name,
66
89
  resolve: {
67
90
  alias: {
68
91
  vue$: "vue/dist/vue.esm.js",
69
- "@": resolve("src"),
70
- "@postal-benefits-platform": resolve("../postal-benefits-platform/src"),
92
+ ...(alias || {}),
93
+ "@": resolve("src")
71
94
  },
72
- fallback: createNodePolyfills()
95
+ fallback: createNodePolyfills(),
96
+ // 优化模块解析
97
+ extensions: [".js", ".vue", ".json", ".jsx"],
98
+ modules: ["node_modules"]
73
99
  },
74
100
  target: ["web", "es5"],
75
101
  output: {
@@ -84,19 +110,120 @@ function configureWebpack({ name }) {
84
110
  }
85
111
  },
86
112
  performance: { hints: false },
87
- ignoreWarnings: [{ module: /sass-loader/ }]
113
+ ignoreWarnings: [{ module: /sass-loader/ }],
114
+
115
+ // 添加文件系统缓存(Webpack 5 核心优化)
116
+ cache: {
117
+ type: "filesystem",
118
+ cacheDirectory: resolve("node_modules/.cache/webpack"),
119
+ buildDependencies: {
120
+ config: [__filename]
121
+ }
122
+ }
88
123
  };
124
+
125
+ // 开发环境优化
126
+ if (ENV.IS_DEV) {
127
+ config.optimization = {
128
+ removeAvailableModules: false,
129
+ removeEmptyChunks: false,
130
+ splitChunks: false,
131
+ runtimeChunk: true
132
+ };
133
+ }
134
+
135
+ return config;
89
136
  }
90
137
 
91
138
  /**
92
139
  * chainWebpack配置
93
140
  */
94
- function chainWebpack(config) {
141
+ function chainWebpack(config, options = {}) {
142
+ const { htmlPluginOptions = {} } = options || {};
143
+
95
144
  config.plugins.delete("preload");
96
145
  config.plugins.delete("prefetch");
97
146
 
147
+ if (Object.keys(htmlPluginOptions).length) {
148
+ config.plugin("html").tap((args) => {
149
+ const originalOptions = args[0] || {};
150
+ return [{ ...originalOptions, ...htmlPluginOptions }];
151
+ });
152
+ }
153
+
154
+ // 强制禁用 modern mode(staging 环境)
155
+ if (ENV.IS_STAGING) {
156
+ // 删除 @vue/cli-service 的 modern mode 相关插件
157
+ config.plugins.delete("modern-mode-plugin");
158
+
159
+ // 强制修改 HTML 插件配置
160
+ config.plugin("html").tap((args) => {
161
+ args[0] = args[0] || {};
162
+
163
+ // 彻底禁用 modern mode
164
+ args[0].modern = false;
165
+ args[0].modulePreload = false;
166
+ args[0].scriptLoading = "defer";
167
+
168
+ // 删除所有 modern/legacy 相关的钩子和回调
169
+ delete args[0].modernBuild;
170
+ delete args[0].legacyBuild;
171
+ delete args[0].modernManifest;
172
+ delete args[0].legacyManifest;
173
+
174
+ // 关键:重写 templateParameters 来阻止 modern mode 注入
175
+ const originalTemplateParameters = args[0].templateParameters || {};
176
+ args[0].templateParameters = (compilation, assets, assetTags, options) => {
177
+ const params =
178
+ typeof originalTemplateParameters === "function"
179
+ ? originalTemplateParameters(compilation, assets, assetTags, options)
180
+ : originalTemplateParameters;
181
+
182
+ // 移除 modern/legacy 相关的参数
183
+ if (params) {
184
+ delete params.modernBuild;
185
+ delete params.legacyBuild;
186
+ delete params.modernManifest;
187
+ delete params.legacyManifest;
188
+ }
189
+
190
+ return params;
191
+ };
192
+
193
+ return args;
194
+ });
195
+
196
+ // 添加自定义插件来处理 legacy 文件问题
197
+ config.plugin("remove-legacy-assets").use(RemoveLegacyAssetsPlugin);
198
+
199
+ // Babel 配置:只转译现代浏览器
200
+ config.module
201
+ .rule("js")
202
+ .use("babel-loader")
203
+ .tap((options) => ({
204
+ ...options,
205
+ presets: [
206
+ [
207
+ "@babel/preset-env",
208
+ {
209
+ targets: {
210
+ esmodules: true,
211
+ chrome: "60",
212
+ firefox: "60",
213
+ safari: "11",
214
+ edge: "79"
215
+ },
216
+ modules: false
217
+ }
218
+ ]
219
+ ],
220
+ cacheDirectory: true,
221
+ cacheCompression: false
222
+ }));
223
+ }
224
+
98
225
  // 注册 runtime 内联插件
99
- config.plugin("inline-runtime").use(InlineRuntimePlugin)
226
+ config.plugin("inline-runtime").use(InlineRuntimePlugin);
100
227
 
101
228
  // SVG图标
102
229
  config.module.rule("svg").exclude.add(resolve("src/icons")).end();
@@ -110,7 +237,7 @@ function chainWebpack(config) {
110
237
  .options({ symbolId: "icon-[name]" })
111
238
  .end();
112
239
 
113
- // Vue Loader
240
+ // Vue Loader 优化
114
241
  config.module
115
242
  .rule("vue")
116
243
  .use("vue-loader")
@@ -125,6 +252,19 @@ function chainWebpack(config) {
125
252
  }))
126
253
  .end();
127
254
 
255
+ // 优化 Babel Loader 缓存(非 staging 环境)
256
+ if (!ENV.IS_STAGING) {
257
+ config.module
258
+ .rule("js")
259
+ .use("babel-loader")
260
+ .loader("babel-loader")
261
+ .tap((options) => ({
262
+ ...options,
263
+ cacheDirectory: true,
264
+ cacheCompression: false
265
+ }));
266
+ }
267
+
128
268
  // 图片资源
129
269
  config.module
130
270
  .rule("images")
@@ -178,17 +318,63 @@ function chainWebpack(config) {
178
318
  maxInitialRequests: 30,
179
319
  enforceSizeThreshold: 50000,
180
320
  cacheGroups: {
321
+ // 最高优先级:ElementUI 框架(单独分包)
322
+ elementUI: {
323
+ name: "chunk-element-ui",
324
+ test: /[\\/]node_modules[\\/]_?element-ui(.*)/,
325
+ priority: 40,
326
+ chunks: "all",
327
+ reuseExistingChunk: true
328
+ },
329
+ // 高优先级:ECharts 图表库(体积大,单独分包)
330
+ echarts: {
331
+ name: "chunk-echarts",
332
+ test: /[\\/]node_modules[\\/](echarts|zrender|v-charts)(.*)/,
333
+ priority: 35,
334
+ chunks: "async",
335
+ reuseExistingChunk: true
336
+ },
337
+ // Infly UI 组件库
338
+ inflyUI: {
339
+ name: "chunk-infly-ui",
340
+ test: /[\\/]packages[\\/]infly-ui[\\/]/,
341
+ priority: 31,
342
+ chunks: "all",
343
+ reuseExistingChunk: true
344
+ },
345
+ // Infly 工具库(monorepo 共享代码)
346
+ inflyLibs: {
347
+ name: "chunk-infly-libs",
348
+ test: /[\\/]packages[\\/]infly-libs[\\/]/,
349
+ priority: 30,
350
+ chunks: "all",
351
+ reuseExistingChunk: true
352
+ },
353
+ // Vue 全家桶(vue、vue-router、vuex)
354
+ vue: {
355
+ name: "chunk-vue",
356
+ test: /[\\/]node_modules[\\/](vue|vue-router|vuex)(.*)/,
357
+ priority: 25,
358
+ chunks: "all",
359
+ reuseExistingChunk: true
360
+ },
361
+ // 工具库(axios、lodash、moment 等)
362
+ utils: {
363
+ name: "chunk-utils",
364
+ test: /[\\/]node_modules[\\/](axios|nprogress|path-to-regexp|qrcanvas|html2canvas|element-china-area-data|vue-awesome-swiper|normalize\.css)(.*)/,
365
+ priority: 20,
366
+ chunks: "all",
367
+ reuseExistingChunk: true
368
+ },
369
+ // 其他 node_modules 库
181
370
  libs: {
182
371
  name: "chunk-libs",
183
372
  test: /[\\/]node_modules[\\/]/,
184
373
  priority: 10,
185
- chunks: "initial"
186
- },
187
- elementUI: {
188
- name: "chunk-elementUI",
189
- priority: 20,
190
- test: /[\\/]node_modules[\\/]_?element-ui(.*)/
374
+ chunks: "initial",
375
+ reuseExistingChunk: true
191
376
  },
377
+ // 公共组件(被多次引用的组件)
192
378
  commons: {
193
379
  name: "chunk-commons",
194
380
  test: resolve("src/components"),
@@ -220,8 +406,30 @@ function chainWebpack(config) {
220
406
  }
221
407
  }
222
408
 
409
+ /**
410
+ * devServer 优化配置
411
+ */
412
+ function devServer() {
413
+ return {
414
+ hot: true,
415
+ client: {
416
+ logging: "warn", // 减少控制台日志
417
+ overlay: false
418
+ },
419
+ // 优化监听性能
420
+ watchFiles: {
421
+ options: {
422
+ ignored: /node_modules/,
423
+ usePolling: false
424
+ }
425
+ }
426
+ };
427
+ }
428
+
223
429
  module.exports = {
224
430
  cssModule,
225
431
  configureWebpack,
226
- chainWebpack
432
+ chainWebpack,
433
+ devServer,
434
+ projectResolve: resolve
227
435
  };
@@ -0,0 +1,59 @@
1
+ "use strict";
2
+
3
+ const assert = require("node:assert/strict");
4
+ const test = require("node:test");
5
+ const { chainWebpack } = require("./webpack.base");
6
+
7
+ function createChainConfig() {
8
+ let htmlPluginOptions;
9
+ const chain = new Proxy(
10
+ function () {},
11
+ {
12
+ get(_target, property) {
13
+ if (property === "plugin") {
14
+ return (name) => ({
15
+ tap(callback) {
16
+ if (name === "html") {
17
+ [htmlPluginOptions] = callback([htmlPluginOptions || {}]);
18
+ }
19
+ return this;
20
+ },
21
+ use() {
22
+ return this;
23
+ }
24
+ });
25
+ }
26
+
27
+ return chain;
28
+ },
29
+ apply() {
30
+ return chain;
31
+ }
32
+ }
33
+ );
34
+
35
+ return {
36
+ config: chain,
37
+ getHtmlPluginOptions: () => htmlPluginOptions
38
+ };
39
+ }
40
+
41
+ test("chainWebpack 默认不增加 HTML 插件参数", () => {
42
+ const { config, getHtmlPluginOptions } = createChainConfig();
43
+
44
+ chainWebpack(config);
45
+
46
+ assert.equal(getHtmlPluginOptions()?.faviconPath, undefined);
47
+ });
48
+
49
+ test("chainWebpack 合并调用方传入的 HTML 插件参数", () => {
50
+ const { config, getHtmlPluginOptions } = createChainConfig();
51
+
52
+ chainWebpack(config, {
53
+ htmlPluginOptions: {
54
+ faviconPath: "favicon_xzya.ico"
55
+ }
56
+ });
57
+
58
+ assert.equal(getHtmlPluginOptions()?.faviconPath, "favicon_xzya.ico");
59
+ });
package/index.js CHANGED
@@ -1,7 +1,17 @@
1
1
  const buildDist = require("./build/build-dist");
2
2
  const projectPreview = require("./tools/project-preview");
3
+ const path = require("path");
3
4
 
4
5
  function init() {
6
+ // 检查是否在 infly-libs 包自身目录中执行
7
+ const currentDir = process.cwd();
8
+ const packageDir = __dirname;
9
+
10
+ // 如果当前目录就是 infly-libs 包目录,跳过执行
11
+ if (currentDir === packageDir) {
12
+ return;
13
+ }
14
+
5
15
  projectPreview.scriptInit();
6
16
  buildDist.scriptInit();
7
17
  }
@@ -9,35 +9,35 @@ import TokenService from "./TokenService";
9
9
  * @param {Object} config.router 路由实例
10
10
  * @param {Object} config.store Vuex store 实例
11
11
  * @param {Object} config.NProgress NProgress 实例
12
- * @param {Function} config.getToken 获取 token 的函数
13
12
  * @param {Function} config.getUserInfo 获取用户信息的函数
14
13
  * @param {Function} config.resetToken 重置 token 的函数
15
14
  * @param {Function} config.showMessage 显示消息的函数
16
- * @param {Function} config.getTitle 获取页面标题的函数
17
- * * @returns {void}
15
+ * @returns {void}
18
16
  */
19
17
  export function initPermission({
20
- whiteList = ["/login"],
18
+ whiteList = ["/login", "/404", "/403", "/error/401", "/error/404"],
21
19
  getUserInfoAction = "user/getInfo",
22
20
  resetTokenAction = "user/resetToken",
23
21
  refreshTokenAction = "user/refreshToken",
24
22
  pagePathCacheKey, // 页面路径缓存
25
23
  router,
26
24
  store,
27
- defaultSettings = {},
25
+ defaultSettings,
26
+ settings = defaultSettings || {},
28
27
  NProgress,
29
28
  Message,
30
29
  getUserInfo,
31
30
  resetToken,
32
31
  showMessage
33
32
  }) {
34
- const { title: projectTitle } = defaultSettings || {};
33
+ const { title: projectTitle } = settings || {};
35
34
  const { dispatch } = store || {};
35
+ // 转为 Set,将白名单查找从 O(n) 优化为 O(1)
36
+ const whiteSet = new Set(whiteList);
36
37
 
38
+ // afterEach 已统一调用 NProgress.done(),此处无需重复
37
39
  const redirectToLogin = (to, next) => {
38
40
  next(`/login?redirect=${to.path}`);
39
- NProgress.done();
40
- return;
41
41
  };
42
42
 
43
43
  const handleGetUserInfo = () => {
@@ -56,78 +56,144 @@ export function initPermission({
56
56
  }
57
57
  };
58
58
 
59
+ // 降级链:优先调用自定义 showMessage,其次 Message,最后 console
59
60
  const handleShowMessage = (message, type = "error") => {
60
61
  if (Uts.isFunction(showMessage)) {
61
62
  showMessage(message);
62
- }
63
-
64
- if (Uts.isFunction(Message)) {
63
+ } else if (Uts.isFunction(Message)) {
65
64
  Message[type](message);
65
+ } else {
66
+ console.log(message);
66
67
  }
67
-
68
- console.log(message);
69
68
  };
70
69
 
71
70
  const handlePageTitle = (path) => {
72
71
  const title = projectTitle || "后台管理系统";
73
- if (path) {
74
- return `${path} - ${title}`;
75
- }
76
- return `${title}`;
72
+ return path ? `${path} - ${title}` : title;
77
73
  };
78
74
 
79
75
  NProgress.configure({ showSpinner: false });
80
76
 
81
- router.beforeEach(async (to, from, next) => {
82
- NProgress.start();
83
- document.title = handlePageTitle(to.meta.title);
77
+ // 辅助函数:处理 URL token 参数
78
+ const handleUrlTokenParam = async (to, next, accessTokenKey, accessToken) => {
79
+ if (!to.query[accessTokenKey]) return false;
84
80
 
85
- if (!whiteList.includes(to.path) && pagePathCacheKey) {
86
- localStorage.setItem(pagePathCacheKey, window.location.href);
81
+ // 优先从地址栏获取token, 更新存储token值
82
+ if (accessToken && Uts.isFunction(dispatch)) {
83
+ try {
84
+ await dispatch(refreshTokenAction, { token: accessToken });
85
+ } catch (error) {
86
+ console.error("[Token Refresh Error]:", error);
87
+ }
87
88
  }
88
89
 
89
- const Token = TokenService.getToken();
90
- const accessTokenKey = TokenService.getUrlTokenKey();
91
- const accessToken = TokenService.getUrlToken();
90
+ // 用解构 rest 移除 token 参数,避免修改原对象
91
+ const { [accessTokenKey]: _, ...newQuery } = to.query;
92
+ next({ path: to.path, query: newQuery, replace: true });
93
+ return true;
94
+ };
92
95
 
93
- // 处理 URL 中的 accessToken 参数
94
- if (to.query[accessTokenKey]) {
95
- const newQuery = { ...to.query };
96
+ // 辅助函数:检查权限
97
+ const checkPermission = (permission) => {
98
+ if (!permission) return true;
96
99
 
97
- // 优先从地址栏获取token, 更新存储token值
98
- if (accessToken && Uts.isFunction(dispatch)) {
99
- await dispatch(refreshTokenAction, { token: accessToken });
100
- }
100
+ const hasPermission = Uts.getPM(permission);
101
101
 
102
- delete newQuery[accessTokenKey];
103
- NProgress.done();
104
- return next({ path: to.path, query: newQuery, replace: true });
102
+ if (!hasPermission) {
103
+ console.warn(`[权限不足] 需要权限: ${permission}`);
105
104
  }
106
105
 
107
- // 检查白名单路径
108
- if (whiteList.includes(to.path)) {
109
- NProgress.done();
110
- return next();
106
+ return hasPermission;
107
+ };
108
+
109
+ // 辅助函数:处理已认证用户的路由
110
+ const handleAuthenticatedRoute = async (to, next, permission) => {
111
+ // 已登录用户访问登录页
112
+ if (to.path === "/login") {
113
+ const { PMRedirectPath } = (await handleGetUserInfo()) || {};
114
+ const redirectPath = to.query.redirect;
115
+
116
+ if (redirectPath) {
117
+ // 查找 redirect 路由对应的权限
118
+ const matched = router.resolve(redirectPath)?.route;
119
+ const redirectPermission = matched?.meta?.permission;
120
+ // 无需权限 或 有权限,则走 redirect
121
+ if (!redirectPermission || checkPermission(redirectPermission)) {
122
+ return next({ path: redirectPath });
123
+ }
124
+ }
125
+
126
+ // 否则跳到第一个有权限的页面
127
+ return next({ path: PMRedirectPath || "/" });
111
128
  }
112
129
 
113
- if (Token) {
114
- // 如果已登录,但尝试访问登录页,则重定向到首页
115
- if (to.path === "/login") {
116
- NProgress.done();
117
- next({ path: "/" });
118
- } else {
119
- try {
120
- await handleGetUserInfo();
121
- NProgress.done();
122
- next();
123
- } catch (error) {
124
- const { message = "" } = error || {};
125
- // handleShowMessage(message || error || "验证失败, 请重新登录");
126
- await handleResetToken();
127
- redirectToLogin(to, next);
130
+ try {
131
+ // 获取用户信息
132
+ const { PMRedirectPath } = (await handleGetUserInfo()) || {};
133
+
134
+ // 合并权限检查,避免重复调用 checkPermission
135
+ if (permission && !checkPermission(permission)) {
136
+ // 有兜底路径时优先跳转,否则跳 403
137
+ if (PMRedirectPath) {
138
+ return next({ path: PMRedirectPath });
139
+ }
140
+ if (!whiteSet.has(to.path)) {
141
+ return next({ path: "/error/403" });
128
142
  }
129
143
  }
130
- } else {
144
+
145
+ next();
146
+ } catch (error) {
147
+ const { message = "" } = error || {};
148
+ console.error("[认证失败]:", message || error);
149
+
150
+ // 可选:显示错误消息
151
+ // handleShowMessage(message || "验证失败,请重新登录");
152
+
153
+ await handleResetToken();
154
+ redirectToLogin(to, next);
155
+ }
156
+ };
157
+
158
+ router.beforeEach(async (to, from, next) => {
159
+ NProgress.start();
160
+
161
+ try {
162
+ const Token = TokenService.getToken();
163
+ const accessTokenKey = TokenService.getUrlTokenKey();
164
+ const accessToken = TokenService.getUrlToken();
165
+ const { permission } = to.meta || {};
166
+
167
+ // 设置页面标题
168
+ document.title = handlePageTitle(to.meta?.title);
169
+
170
+ // 缓存页面路径(非白名单页面)
171
+ if (pagePathCacheKey && !whiteSet.has(to.path)) {
172
+ localStorage.setItem(pagePathCacheKey, window.location.href);
173
+ }
174
+
175
+ // 1. 处理 URL token 参数
176
+ if (await handleUrlTokenParam(to, next, accessTokenKey, accessToken)) {
177
+ return;
178
+ }
179
+
180
+ // 2. 检查白名单路径
181
+ if (whiteSet.has(to.path)) {
182
+ return next();
183
+ }
184
+
185
+ // 3. 未登录用户,重定向到登录页
186
+ if (!Token) {
187
+ return redirectToLogin(to, next);
188
+ }
189
+
190
+ // 4. 已登录用户的路由处理
191
+ await handleAuthenticatedRoute(to, next, permission);
192
+ } catch (error) {
193
+ console.error("[路由守卫错误]:", error);
194
+
195
+ // 发生未预期的错误时,重定向到登录页
196
+ await handleResetToken();
131
197
  redirectToLogin(to, next);
132
198
  }
133
199
  });