@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.
- package/bin/cli.js +29 -21
- package/build/build-dist/index.js +258 -61
- package/build/webpack5/remove-legacy-assets-plugin.js +84 -0
- package/build/webpack5/webpack.base.js +228 -20
- package/build/webpack5/webpack.base.test.js +59 -0
- package/index.js +10 -0
- package/module/Permission.js +121 -55
- package/module/REST.js +136 -26
- package/module/Router.js +15 -0
- package/module/Uts.js +380 -331
- package/module/cjs/deep-merge.cjs +38 -0
- package/module/cjs/page-config.cjs +119 -0
- package/module/cjs/request-url-rules.cjs +55 -0
- package/package.json +11 -10
- package/script/build/command.js +48 -0
- package/script/build/env.js +28 -0
- package/script/build/git.js +252 -0
- package/script/build/preview.js +75 -0
- package/script/build/webhook.js +118 -0
- package/script/git-automation/check-packages.js +11 -8
- package/script/git-automation/git-utils.js +67 -0
- package/script/git-automation/index.js +378 -106
- package/script/index.js +8 -8
- package/script/pts/cloud-scenes.mjs +65 -0
- package/script/pts/cloud.js +151 -0
- package/script/pts/generate-cloud-params.mjs +210 -0
- package/script/pts/generate-cloud-params.test.mjs +67 -0
- package/script/webhook/webhook.js +75 -4
- package/store/modules/user.js +111 -9
- package/tools/auto-export.js +56 -0
- package/tools/file-export.js +32 -9
- package/tools/file-process.js +3 -0
- package/tools/project-preview.js +110 -97
- package/dataInit/commonTypeMap.js +0 -31
- package/dataInit/marketingActivitiesMap.js +0 -214
- package/dataInit/orderMap.js +0 -13
- package/dataInit/personalMap.js +0 -19
- package/dataInit/settlementMap.js +0 -17
- 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(
|
|
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
|
|
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(
|
|
64
|
-
|
|
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
|
-
|
|
70
|
-
"@
|
|
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
|
}
|
package/module/Permission.js
CHANGED
|
@@ -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
|
-
* @
|
|
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 } =
|
|
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
|
-
|
|
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
|
-
|
|
82
|
-
|
|
83
|
-
|
|
77
|
+
// 辅助函数:处理 URL token 参数
|
|
78
|
+
const handleUrlTokenParam = async (to, next, accessTokenKey, accessToken) => {
|
|
79
|
+
if (!to.query[accessTokenKey]) return false;
|
|
84
80
|
|
|
85
|
-
|
|
86
|
-
|
|
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
|
-
|
|
90
|
-
const accessTokenKey =
|
|
91
|
-
|
|
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
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
+
// 辅助函数:检查权限
|
|
97
|
+
const checkPermission = (permission) => {
|
|
98
|
+
if (!permission) return true;
|
|
96
99
|
|
|
97
|
-
|
|
98
|
-
if (accessToken && Uts.isFunction(dispatch)) {
|
|
99
|
-
await dispatch(refreshTokenAction, { token: accessToken });
|
|
100
|
-
}
|
|
100
|
+
const hasPermission = Uts.getPM(permission);
|
|
101
101
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
return next({ path: to.path, query: newQuery, replace: true });
|
|
102
|
+
if (!hasPermission) {
|
|
103
|
+
console.warn(`[权限不足] 需要权限: ${permission}`);
|
|
105
104
|
}
|
|
106
105
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
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
|
-
|
|
114
|
-
//
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
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
|
-
|
|
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
|
});
|