Commit 310dafe855278d7fbe7ec9aa60d284c49ffd8350

Authored by ww
1 parent a7f18193

chore: 迁移iot平台http请求相关模块等

Showing 48 changed files with 2994 additions and 87 deletions
  1 +# Proxy
  2 +VITE_GLOB_PROXY = [["/api", "http://222.180.200.114:48080/api"]]
  3 +
  4 +# 内容安全协议
  5 +VITE_GLOB_CONTENT_SECURITY_POLICY = false
... ...
  1 +# Proxy
  2 +VITE_GLOB_PROXY = [["/api", "http://222.180.200.114:48080/api"]]
  3 +
  4 +# 内容安全协议
  5 +VITE_GLOB_CONTENT_SECURITY_POLICY = true
... ...
1 1 import path from 'path'
  2 +import { BuildOptions } from 'vite'
2 3 export const OUTPUT_DIR = 'dist'
3 4
4 5 // monaco-editor 路径
... ... @@ -11,12 +12,12 @@ export const chunkSizeWarningLimit = 2000
11 12 export const brotliSize = false
12 13
13 14 // 分包
14   -export const rollupOptions = {
  15 +export const rollupOptions: BuildOptions['rollupOptions'] = {
15 16 output: {
16 17 chunkFileNames: 'static/js/[name]-[hash].js',
17 18 entryFileNames: 'static/js/[name]-[hash].js',
18 19 assetFileNames: (chunkInfo) => {
19   - if(['.png', '.jpg', '.jpeg'].includes(path.extname(chunkInfo.name))) {
  20 + if (['.png', '.jpg', '.jpeg'].includes(path.extname(chunkInfo.name!))) {
20 21 return `static/[ext]/[name].[ext]`
21 22 }
22 23 return `static/[ext]/[name]-[hash].[ext]`
... ...
  1 +/**
  2 + * The name of the configuration file entered in the production environment
  3 + */
  4 +export const GLOB_CONFIG_FILE_NAME = '_app.config.js';
  5 +
... ...
... ... @@ -2,7 +2,7 @@
2 2 * Get the configuration file variable name
3 3 * @param env
4 4 */
5   -export const getConfigFileName = (env: Record<string, any>) => {
  5 +export const getGlobalConfigName = (env: Record<string, any>) => {
6 6 return `__PRODUCTION__${env.VITE_GLOB_APP_SHORT_NAME || '__APP'}__CONF__`
7 7 .toUpperCase()
8 8 .replace(/\s/g, '');
... ...
  1 +import { getRootPath } from "../utils";
  2 +import { writeFileSync } from "fs";
  3 +import { Plugin } from "vite";
  4 +import { GLOB_CONFIG_FILE_NAME } from "./const";
  5 +import { getGlobalConfigName } from "./getGlobConfigName";
  6 +
  7 +export function GenerateBuildConfig(viteEnv: Record<string, any>) {
  8 + return {
  9 + name: 'vite-plugin-generate-global-config',
  10 + apply: 'build',
  11 + enforce: 'post',
  12 + closeBundle() {
  13 +
  14 + function createConfig({ config, configName, configFileName }: { configName: string, config: Record<string, any>, configFileName?: string } = { configName: '', config: {} }) {
  15 + try {
  16 + const windowConf = `window.${configName}`;
  17 + // Ensure that the variable will not be modified
  18 + const configStr = `${windowConf}=${JSON.stringify(config)};
  19 + Object.freeze(${windowConf});
  20 + Object.defineProperty(window, "${configName}", {
  21 + configurable: false,
  22 + writable: false,
  23 + });
  24 + `.replace(/\s/g, '');
  25 +
  26 + writeFileSync(getRootPath(`dist/${configFileName}`), configStr, { encoding: 'utf-8' })
  27 + } catch (error) {
  28 +
  29 + }
  30 + }
  31 +
  32 + const configName = getGlobalConfigName(viteEnv as ImportMetaEnv)
  33 +
  34 + createConfig({ config: viteEnv, configFileName: GLOB_CONFIG_FILE_NAME, configName })
  35 +
  36 + }
  37 + } as Plugin
  38 +}
  39 +
... ...
  1 +import { getGlobalConfigName } from "./getGlobConfigName";
  2 +
  3 +
  4 +export function getAppEnvConfig() {
  5 + const ENV_NAME = getGlobalConfigName(import.meta.env);
  6 +
  7 + const ENV = (import.meta.env.DEV
  8 + ? // Get the global configuration (the configuration will be extracted independently when packaging)
  9 + (import.meta.env as unknown as GlobEnvConfig)
  10 + : window[ENV_NAME as any]) as unknown as GlobEnvConfig;
  11 + const {
  12 + VITE_GLOB_APP_TITLE,
  13 + VITE_GLOB_APP_SHORT_NAME,
  14 + VITE_GLOB_CONTENT_SECURITY_POLICY,
  15 + } = ENV;
  16 +
  17 + if (!/^[a-zA-Z\_]*$/.test(VITE_GLOB_APP_SHORT_NAME)) {
  18 + console.warn(
  19 + `VITE_GLOB_APP_SHORT_NAME Variables can only be characters/underscores, please modify in the environment variables and re-running.`
  20 + );
  21 + }
  22 +
  23 + return {
  24 + VITE_GLOB_APP_TITLE,
  25 + VITE_GLOB_APP_SHORT_NAME,
  26 + VITE_GLOB_CONTENT_SECURITY_POLICY,
  27 + };
  28 +}
  29 +
  30 +export const useGlobSetting = (): Readonly<GlobConfig> => {
  31 + const {
  32 + VITE_GLOB_APP_TITLE,
  33 + VITE_GLOB_APP_SHORT_NAME,
  34 + VITE_GLOB_CONTENT_SECURITY_POLICY,
  35 + } = getAppEnvConfig();
  36 +
  37 + if (!/[a-zA-Z\_]*/.test(VITE_GLOB_APP_SHORT_NAME)) {
  38 + console.warn(
  39 + `VITE_GLOB_APP_SHORT_NAME Variables can only be characters/underscores, please modify in the environment variables and re-running.`
  40 + );
  41 + }
  42 +
  43 + // Take global configuration
  44 + const glob: Readonly<GlobConfig> = {
  45 + title: VITE_GLOB_APP_TITLE,
  46 + securityPolicy: VITE_GLOB_CONTENT_SECURITY_POLICY,
  47 + };
  48 +
  49 + return glob as Readonly<GlobConfig>;
  50 +};
... ...
  1 +import { resolve } from "path"
  2 +
  3 +export function parseEnv(env: ImportMetaEnv) {
  4 + const res: Record<string, any> = {}
  5 +
  6 + for (const envName of Object.keys(env)) {
  7 + let value = env[envName]
  8 +
  9 + // parese to boolean
  10 + value = value === 'true' ? true : value === 'false' ? false : value
  11 +
  12 + if (envName === 'VITE_GLOB_PROXY') {
  13 + try {
  14 + value = JSON.parse(value)
  15 + } catch (error) {
  16 + value = ''
  17 + }
  18 + }
  19 +
  20 + res[envName] = value
  21 +
  22 + if (typeof value === 'string') {
  23 + process.env[envName] = value
  24 + } else if (typeof value === 'object') {
  25 + process.env[envName] === JSON.stringify(value)
  26 + }
  27 +
  28 + }
  29 +
  30 + return res
  31 +}
  32 +
  33 +export function getEnvConfig(match = 'VITE_GLOB_',) {
  34 +
  35 +}
  36 +
  37 +export function getRootPath(...dir: string[]) {
  38 + return resolve(process.cwd(), ...dir);
  39 +}
... ...
  1 +import { createHtmlPlugin } from 'vite-plugin-html'
  2 +
  3 +const GLOB_CONFIG_FILE_NAME = '_app.config.js'
  4 +
  5 +export function configHtmlPlugin(env: ImportMetaEnv, isBuild: boolean) {
  6 + const { VITE_GLOB_APP_TITLE, VITE_GLOB_CONTENT_SECURITY_POLICY, VITE_GLOB_PUBLIC_PATH } = env
  7 + const getAppConfigSrc = () => {
  8 + const path = VITE_GLOB_PUBLIC_PATH.endsWith('/') ? VITE_GLOB_PUBLIC_PATH : `${VITE_GLOB_PUBLIC_PATH}`
  9 + return `${path || '/'}${GLOB_CONFIG_FILE_NAME}?v=${Date.now()}`
  10 + }
  11 +
  12 + const htmlPlugin = createHtmlPlugin({
  13 + minify: isBuild,
  14 + inject: {
  15 + data: {
  16 + title: VITE_GLOB_APP_TITLE,
  17 + contentSecurityPolicy: VITE_GLOB_CONTENT_SECURITY_POLICY ? `<meta http-equiv="Content-Security-Policy" content="upgrade-insecure-requests" />` : ''
  18 + },
  19 + tags: isBuild ? [
  20 + {
  21 + tag: 'script',
  22 + attrs: { src: getAppConfigSrc() }
  23 + }
  24 + ] : []
  25 + }
  26 + })
  27 +
  28 + return htmlPlugin
  29 +}
... ...
  1 +import { ProxyOptions } from "vite"
  2 +
  3 +export const createProxy = (viteEnv: ImportMetaEnv) => {
  4 + const { VITE_GLOB_PROXY } = viteEnv
  5 + const httpsReg = /^https:\/\//;
  6 + const res: Record<string, ProxyOptions> = {}
  7 +
  8 + for (const [prefix, target] of VITE_GLOB_PROXY) {
  9 + const isHttps = httpsReg.test(prefix)
  10 +
  11 + res[prefix] = {
  12 + target,
  13 + changeOrigin: true,
  14 + ws: true,
  15 + rewrite: (path) => path.replace(new RegExp(`^${prefix}`), ''),
  16 + ...(isHttps ? { secure: false } : {})
  17 + }
  18 + }
  19 +
  20 + return res
  21 +}
... ...
... ... @@ -31,10 +31,12 @@
31 31 "gsap": "^3.11.3",
32 32 "highlight.js": "^11.5.0",
33 33 "html2canvas": "^1.4.1",
  34 + "jwt-decode": "^3.1.2",
34 35 "keymaster": "^1.6.2",
35 36 "monaco-editor": "^0.33.0",
36 37 "naive-ui": "2.34.3",
37 38 "pinia": "^2.0.13",
  39 + "qs": "^6.11.0",
38 40 "screenfull": "^6.0.1",
39 41 "three": "^0.145.0",
40 42 "vue": "^3.2.31",
... ... @@ -49,6 +51,7 @@
49 51 "@commitlint/cli": "^17.0.2",
50 52 "@commitlint/config-conventional": "^17.0.2",
51 53 "@types/node": "^16.11.26",
  54 + "@types/qs": "^6.9.7",
52 55 "@types/three": "^0.144.0",
53 56 "@typescript-eslint/eslint-plugin": "^5.18.0",
54 57 "@typescript-eslint/parser": "^5.18.0",
... ... @@ -76,6 +79,7 @@
76 79 "typescript": "4.6.3",
77 80 "vite": "2.9.9",
78 81 "vite-plugin-compression": "^0.5.1",
  82 + "vite-plugin-html": "^3.2.0",
79 83 "vite-plugin-importer": "^0.2.5",
80 84 "vite-plugin-mock": "^2.9.6",
81 85 "vite-plugin-monaco-editor": "^1.1.0",
... ...
... ... @@ -10,6 +10,7 @@ specifiers:
10 10 '@types/keymaster': ^1.6.30
11 11 '@types/lodash': ^4.14.184
12 12 '@types/node': ^16.11.26
  13 + '@types/qs': ^6.9.7
13 14 '@types/three': ^0.144.0
14 15 '@typescript-eslint/eslint-plugin': ^5.18.0
15 16 '@typescript-eslint/parser': ^5.18.0
... ... @@ -39,6 +40,7 @@ specifiers:
39 40 highlight.js: ^11.5.0
40 41 html2canvas: ^1.4.1
41 42 husky: ^8.0.1
  43 + jwt-decode: ^3.1.2
42 44 keymaster: ^1.6.2
43 45 lodash: ~4.17.21
44 46 mockjs: ^1.1.0
... ... @@ -47,6 +49,7 @@ specifiers:
47 49 pinia: ^2.0.13
48 50 plop: ^3.0.5
49 51 prettier: ^2.6.2
  52 + qs: ^6.11.0
50 53 sass: ^1.49.11
51 54 sass-loader: ^12.6.0
52 55 screenfull: ^6.0.1
... ... @@ -54,6 +57,7 @@ specifiers:
54 57 typescript: 4.6.3
55 58 vite: 2.9.9
56 59 vite-plugin-compression: ^0.5.1
  60 + vite-plugin-html: ^3.2.0
57 61 vite-plugin-importer: ^0.2.5
58 62 vite-plugin-mock: ^2.9.6
59 63 vite-plugin-monaco-editor: ^1.1.0
... ... @@ -85,10 +89,12 @@ dependencies:
85 89 gsap: 3.11.3
86 90 highlight.js: 11.5.1
87 91 html2canvas: 1.4.1
  92 + jwt-decode: 3.1.2
88 93 keymaster: 1.6.2
89 94 monaco-editor: 0.33.0
90 95 naive-ui: 2.34.3_vue@3.2.37
91 96 pinia: 2.0.14_ub5l46u3nefphax5x2tezui4oq
  97 + qs: 6.11.0
92 98 screenfull: 6.0.1
93 99 three: 0.145.0
94 100 vue: 3.2.37
... ... @@ -103,6 +109,7 @@ devDependencies:
103 109 '@commitlint/cli': 17.0.2
104 110 '@commitlint/config-conventional': 17.0.2
105 111 '@types/node': 16.11.40
  112 + '@types/qs': 6.9.7
106 113 '@types/three': 0.144.0
107 114 '@typescript-eslint/eslint-plugin': 5.28.0_evi7yu7wunhzwb24olrfvzynny
108 115 '@typescript-eslint/parser': 5.28.0_sfmgizikprcxt7r54j7cnzjamu
... ... @@ -130,6 +137,7 @@ devDependencies:
130 137 typescript: 4.6.3
131 138 vite: 2.9.9_sass@1.52.3
132 139 vite-plugin-compression: 0.5.1_vite@2.9.9
  140 + vite-plugin-html: 3.2.0_vite@2.9.9
133 141 vite-plugin-importer: 0.2.5
134 142 vite-plugin-mock: 2.9.6_mockjs@1.1.0+vite@2.9.9
135 143 vite-plugin-monaco-editor: 1.1.0_monaco-editor@0.33.0
... ... @@ -745,6 +753,13 @@ packages:
745 753 engines: {node: '>=6.0.0'}
746 754 dev: true
747 755
  756 + /@jridgewell/source-map/0.3.2:
  757 + resolution: {integrity: sha512-m7O9o2uR8k2ObDysZYzdfhb08VuEml5oWGiosa1VdaPZ/A6QyPkAJuwN0Q1lhULOf6B7MtQmHENS743hWtCrgw==}
  758 + dependencies:
  759 + '@jridgewell/gen-mapping': 0.3.1
  760 + '@jridgewell/trace-mapping': 0.3.13
  761 + dev: true
  762 +
748 763 /@jridgewell/sourcemap-codec/1.4.13:
749 764 resolution: {integrity: sha512-GryiOJmNcWbovBxTfZSF71V/mXbgcV3MewDe3kIMCLyIh5e7SKAeUZs+rMnJ8jkMolZ/4/VsdBmMrw3l+VdZ3w==}
750 765 dev: true
... ... @@ -928,6 +943,10 @@ packages:
928 943 resolution: {integrity: sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==}
929 944 dev: true
930 945
  946 + /@types/qs/6.9.7:
  947 + resolution: {integrity: sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==}
  948 + dev: true
  949 +
931 950 /@types/resolve/1.17.1:
932 951 resolution: {integrity: sha512-yy7HuzQhj0dhGpD8RLXSZWEkLsV9ibvxvi6EiJ3bkqLAO1RGo0WbkWQiwpRlSFymTJRz0d3k5LM3kkx8ArDbLw==}
933 952 dependencies:
... ... @@ -1237,7 +1256,6 @@ packages:
1237 1256 dependencies:
1238 1257 '@vue/reactivity': 3.2.37
1239 1258 '@vue/shared': 3.2.37
1240   - dev: false
1241 1259
1242 1260 /@vue/runtime-dom/3.2.37:
1243 1261 resolution: {integrity: sha512-HimKdh9BepShW6YozwRKAYjYQWg9mQn63RGEiSswMbW+ssIht1MILYlVGkAGGQbkhSh31PCdoUcfiu4apXJoPw==}
... ... @@ -1245,7 +1263,6 @@ packages:
1245 1263 '@vue/runtime-core': 3.2.37
1246 1264 '@vue/shared': 3.2.37
1247 1265 csstype: 2.6.20
1248   - dev: false
1249 1266
1250 1267 /@vue/server-renderer/3.2.37_vue@3.2.37:
1251 1268 resolution: {integrity: sha512-kLITEJvaYgZQ2h47hIzPh2K3jG8c1zCVbp/o/bzQOyvzaKiCquKS7AaioPI28GNxIsE/zSx+EwWYsNxDCX95MA==}
... ... @@ -1255,7 +1272,6 @@ packages:
1255 1272 '@vue/compiler-ssr': 3.2.37
1256 1273 '@vue/shared': 3.2.37
1257 1274 vue: 3.2.37
1258   - dev: false
1259 1275
1260 1276 /@vue/shared/3.2.37:
1261 1277 resolution: {integrity: sha512-4rSJemR2NQIo9Klm1vabqWjD8rs/ZaJSzMxkMNeJS6lHiUjjUeYFbooN19NgFjztubEKh3WlZUeOLVdbbUWHsw==}
... ... @@ -1449,6 +1465,10 @@ packages:
1449 1465 resolution: {integrity: sha512-p4DO/JXwjs8klJyJL8Q2oM4ks5fUTze/h5k10oPPKMiLe1fj3G1QMzPHNmN1Py4ycOk7WlO2DcGXv1qiESJCZA==}
1450 1466 dev: false
1451 1467
  1468 + /async/3.2.4:
  1469 + resolution: {integrity: sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==}
  1470 + dev: true
  1471 +
1452 1472 /asynckit/0.4.0:
1453 1473 resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==}
1454 1474 dev: false
... ... @@ -1520,6 +1540,12 @@ packages:
1520 1540 concat-map: 0.0.1
1521 1541 dev: true
1522 1542
  1543 + /brace-expansion/2.0.1:
  1544 + resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==}
  1545 + dependencies:
  1546 + balanced-match: 1.0.2
  1547 + dev: true
  1548 +
1523 1549 /braces/3.0.2:
1524 1550 resolution: {integrity: sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==}
1525 1551 engines: {node: '>=8'}
... ... @@ -1539,6 +1565,10 @@ packages:
1539 1565 picocolors: 1.0.0
1540 1566 dev: true
1541 1567
  1568 + /buffer-from/1.1.2:
  1569 + resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==}
  1570 + dev: true
  1571 +
1542 1572 /buffer/5.7.1:
1543 1573 resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==}
1544 1574 dependencies:
... ... @@ -1563,7 +1593,6 @@ packages:
1563 1593 dependencies:
1564 1594 function-bind: 1.1.1
1565 1595 get-intrinsic: 1.1.2
1566   - dev: true
1567 1596
1568 1597 /callsites/3.1.0:
1569 1598 resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==}
... ... @@ -1672,6 +1701,13 @@ packages:
1672 1701 fsevents: 2.3.2
1673 1702 dev: true
1674 1703
  1704 + /clean-css/5.3.2:
  1705 + resolution: {integrity: sha512-JVJbM+f3d3Q704rF4bqQ5UUyTtuJ0JRKNbTKVEeujCCBoMdkEi+V+e8oktO9qGQNSvHrFTM6JZRXrUvGR1czww==}
  1706 + engines: {node: '>= 10.0'}
  1707 + dependencies:
  1708 + source-map: 0.6.1
  1709 + dev: true
  1710 +
1675 1711 /clean-stack/2.2.0:
1676 1712 resolution: {integrity: sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==}
1677 1713 engines: {node: '>=6'}
... ... @@ -1748,6 +1784,10 @@ packages:
1748 1784 color-string: 1.9.1
1749 1785 dev: false
1750 1786
  1787 + /colorette/2.0.19:
  1788 + resolution: {integrity: sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ==}
  1789 + dev: true
  1790 +
1751 1791 /combined-stream/1.0.8:
1752 1792 resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==}
1753 1793 engines: {node: '>= 0.8'}
... ... @@ -1755,6 +1795,15 @@ packages:
1755 1795 delayed-stream: 1.0.0
1756 1796 dev: false
1757 1797
  1798 + /commander/2.20.3:
  1799 + resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==}
  1800 + dev: true
  1801 +
  1802 + /commander/8.3.0:
  1803 + resolution: {integrity: sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==}
  1804 + engines: {node: '>= 12'}
  1805 + dev: true
  1806 +
1758 1807 /commander/9.3.0:
1759 1808 resolution: {integrity: sha512-hv95iU5uXPbK83mjrJKuZyFM/LBAoCV/XhVGkS5Je6tl7sxr6A0ITMw5WoRV46/UaJ46Nllm3Xt7IaJhXTIkzw==}
1760 1809 engines: {node: ^12.20.0 || >=14}
... ... @@ -1783,6 +1832,11 @@ packages:
1783 1832 resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==}
1784 1833 dev: true
1785 1834
  1835 + /connect-history-api-fallback/1.6.0:
  1836 + resolution: {integrity: sha512-e54B99q/OUoH64zYYRf3HBP5z24G38h5D3qXu23JGRoigpX5Ss4r9ZnDk3g0Z8uQC2x2lPaJ+UlWBc1ZWBWdLg==}
  1837 + engines: {node: '>=0.8'}
  1838 + dev: true
  1839 +
1786 1840 /connect/3.7.0:
1787 1841 resolution: {integrity: sha512-ZqRXc+tZukToSNmh5C2iWMSoV3X1YUcPbqEM4DkEG5tNQXrQUZCNVGGv3IuicnkMtPfGf3Xtp8WCXs295iQ1pQ==}
1788 1842 engines: {node: '>= 0.10.0'}
... ... @@ -1795,6 +1849,10 @@ packages:
1795 1849 - supports-color
1796 1850 dev: true
1797 1851
  1852 + /consola/2.15.3:
  1853 + resolution: {integrity: sha512-9vAdYbHj6x2fLKC4+oPH0kFzY/orMZyG2Aj+kNylHxKGJ/Ed4dpNyAQYwJOdqO4zdM7XpVHmyejQDcQHrnuXbw==}
  1854 + dev: true
  1855 +
1798 1856 /constant-case/3.0.4:
1799 1857 resolution: {integrity: sha512-I2hSBi7Vvs7BEuJDr5dDHfzb/Ruj3FyvFyh7KLilAjNQw3Be+xgqUBA2W6scVEcL0hL1dwPRtIqEPVUCKkSsyQ==}
1800 1858 dependencies:
... ... @@ -1832,8 +1890,8 @@ packages:
1832 1890 engines: {node: '>=10'}
1833 1891 hasBin: true
1834 1892 dependencies:
1835   - is-text-path: 1.0.1
1836 1893 JSONStream: 1.3.5
  1894 + is-text-path: 1.0.1
1837 1895 lodash: 4.17.21
1838 1896 meow: 8.1.2
1839 1897 split2: 3.2.2
... ... @@ -1904,6 +1962,21 @@ packages:
1904 1962 csstype: 3.0.11
1905 1963 dev: false
1906 1964
  1965 + /css-select/4.3.0:
  1966 + resolution: {integrity: sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ==}
  1967 + dependencies:
  1968 + boolbase: 1.0.0
  1969 + css-what: 6.1.0
  1970 + domhandler: 4.3.1
  1971 + domutils: 2.8.0
  1972 + nth-check: 2.1.1
  1973 + dev: true
  1974 +
  1975 + /css-what/6.1.0:
  1976 + resolution: {integrity: sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==}
  1977 + engines: {node: '>= 6'}
  1978 + dev: true
  1979 +
1907 1980 /cssesc/3.0.0:
1908 1981 resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==}
1909 1982 engines: {node: '>=4'}
... ... @@ -1912,7 +1985,6 @@ packages:
1912 1985
1913 1986 /csstype/2.6.20:
1914 1987 resolution: {integrity: sha512-/WwNkdXfckNgw6S5R125rrW8ez139lBHWouiBvX8dfMFtcn6V81REDqnH7+CRpRipfYlyU1CmOnOxrmGcFOjeA==}
1915   - dev: false
1916 1988
1917 1989 /csstype/3.0.11:
1918 1990 resolution: {integrity: sha512-sa6P2wJ+CAbgyy4KFssIb/JNMLxFvKF1pCYCSXS8ZMuqZnMsrxqI2E5sPyoTpxoPU/gVZMzr2zjOfg8GIZOMsw==}
... ... @@ -2112,6 +2184,16 @@ packages:
2112 2184 is-obj: 2.0.0
2113 2185 dev: true
2114 2186
  2187 + /dotenv-expand/8.0.3:
  2188 + resolution: {integrity: sha512-SErOMvge0ZUyWd5B0NXMQlDkN+8r+HhVUsxgOO7IoPDOdDRD2JjExpN6y3KnFR66jsJMwSn1pqIivhU5rcJiNg==}
  2189 + engines: {node: '>=12'}
  2190 + dev: true
  2191 +
  2192 + /dotenv/16.0.3:
  2193 + resolution: {integrity: sha512-7GO6HghkA5fYG9TYnNxi14/7K9f5occMlp3zXAuSxn7CKCxt9xbNWG7yF8hTCSUchlfWSe3uLmlPfigevRItzQ==}
  2194 + engines: {node: '>=12'}
  2195 + dev: true
  2196 +
2115 2197 /echarts-liquidfill/3.1.0_echarts@5.3.3:
2116 2198 resolution: {integrity: sha512-5Dlqs/jTsdTUAsd+K5LPLLTgrbbNORUSBQyk8PSy1Mg2zgHDWm83FmvA4s0ooNepCJojFYRITTQ4GU1UUSKYLw==}
2117 2199 peerDependencies:
... ... @@ -2137,12 +2219,19 @@ packages:
2137 2219 dependencies:
2138 2220 tslib: 2.3.0
2139 2221 zrender: 5.3.2
2140   - dev: true
2141 2222
2142 2223 /ee-first/1.1.1:
2143 2224 resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==}
2144 2225 dev: true
2145 2226
  2227 + /ejs/3.1.8:
  2228 + resolution: {integrity: sha512-/sXZeMlhS0ArkfX2Aw780gJzXSMPnKjtspYZv+f3NiKLlubezAHDU5+9xz6gd3/NhG3txQCo6xlglmTS+oTGEQ==}
  2229 + engines: {node: '>=0.10.0'}
  2230 + hasBin: true
  2231 + dependencies:
  2232 + jake: 10.8.5
  2233 + dev: true
  2234 +
2146 2235 /electron-to-chromium/1.4.155:
2147 2236 resolution: {integrity: sha512-niPzKBSYPG06gxLKO0c2kEmgdRMTtIbNrBlvD31Ld8Q57b/K0218U4j8u/OOt25XE1eFOn47FcmQVdx9R1qqxA==}
2148 2237 dev: true
... ... @@ -2775,6 +2864,12 @@ packages:
2775 2864 flat-cache: 3.0.4
2776 2865 dev: true
2777 2866
  2867 + /filelist/1.0.4:
  2868 + resolution: {integrity: sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==}
  2869 + dependencies:
  2870 + minimatch: 5.1.6
  2871 + dev: true
  2872 +
2778 2873 /fill-range/7.0.1:
2779 2874 resolution: {integrity: sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==}
2780 2875 engines: {node: '>=8'}
... ... @@ -2912,7 +3007,6 @@ packages:
2912 3007
2913 3008 /function-bind/1.1.1:
2914 3009 resolution: {integrity: sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==}
2915   - dev: true
2916 3010
2917 3011 /function.prototype.name/1.1.5:
2918 3012 resolution: {integrity: sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA==}
... ... @@ -2948,7 +3042,6 @@ packages:
2948 3042 function-bind: 1.1.1
2949 3043 has: 1.0.3
2950 3044 has-symbols: 1.0.3
2951   - dev: true
2952 3045
2953 3046 /get-stream/6.0.1:
2954 3047 resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==}
... ... @@ -3111,7 +3204,6 @@ packages:
3111 3204 /has-symbols/1.0.3:
3112 3205 resolution: {integrity: sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==}
3113 3206 engines: {node: '>= 0.4'}
3114   - dev: true
3115 3207
3116 3208 /has-tostringtag/1.0.0:
3117 3209 resolution: {integrity: sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==}
... ... @@ -3125,12 +3217,16 @@ packages:
3125 3217 engines: {node: '>= 0.4.0'}
3126 3218 dependencies:
3127 3219 function-bind: 1.1.1
3128   - dev: true
3129 3220
3130 3221 /hash-sum/2.0.0:
3131 3222 resolution: {integrity: sha512-WdZTbAByD+pHfl/g9QSsBIIwy8IT+EsPiKDs0KNX+zSHhdDLFKdZu0BQHljvO+0QI/BasbMSUa8wYNCZTvhslg==}
3132 3223 dev: true
3133 3224
  3225 + /he/1.2.0:
  3226 + resolution: {integrity: sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==}
  3227 + hasBin: true
  3228 + dev: true
  3229 +
3134 3230 /header-case/2.0.4:
3135 3231 resolution: {integrity: sha512-H/vuk5TEEVZwrR0lp2zed9OCo1uAILMlx0JEMgC26rzyJJ3N1v6XkwHHXJQdR2doSjcGPM6OKPYoJgf0plJ11Q==}
3136 3232 dependencies:
... ... @@ -3161,6 +3257,20 @@ packages:
3161 3257 lru-cache: 6.0.0
3162 3258 dev: true
3163 3259
  3260 + /html-minifier-terser/6.1.0:
  3261 + resolution: {integrity: sha512-YXxSlJBZTP7RS3tWnQw74ooKa6L9b9i9QYXY21eUEvhZ3u9XLfv6OnFsQq6RxkhHygsaUMvYsZRV5rU/OVNZxw==}
  3262 + engines: {node: '>=12'}
  3263 + hasBin: true
  3264 + dependencies:
  3265 + camel-case: 4.1.2
  3266 + clean-css: 5.3.2
  3267 + commander: 8.3.0
  3268 + he: 1.2.0
  3269 + param-case: 3.0.4
  3270 + relateurl: 0.2.7
  3271 + terser: 5.16.4
  3272 + dev: true
  3273 +
3164 3274 /html-tags/3.2.0:
3165 3275 resolution: {integrity: sha512-vy7ClnArOZwCnqZgvv+ddgHgJiAFXe3Ge9ML5/mBctVJoUoYPCdxVucOywjDARn6CVoh3dRSFdPHy2sX80L0Wg==}
3166 3276 engines: {node: '>=8'}
... ... @@ -3517,6 +3627,17 @@ packages:
3517 3627 engines: {node: '>=0.10.0'}
3518 3628 dev: true
3519 3629
  3630 + /jake/10.8.5:
  3631 + resolution: {integrity: sha512-sVpxYeuAhWt0OTWITwT98oyV0GsXyMlXCF+3L1SuafBVUIr/uILGRB+NqwkzhgXKvoJpDIpQvqkUALgdmQsQxw==}
  3632 + engines: {node: '>=10'}
  3633 + hasBin: true
  3634 + dependencies:
  3635 + async: 3.2.4
  3636 + chalk: 4.1.2
  3637 + filelist: 1.0.4
  3638 + minimatch: 3.1.2
  3639 + dev: true
  3640 +
3520 3641 /js-stringify/1.0.2:
3521 3642 resolution: {integrity: sha512-rtS5ATOo2Q5k1G+DADISilDA6lv79zIiwFd6CcjuIxGKLFm5C+RLImRscVap9k55i+MOZwgliw+NejvkLuGD5g==}
3522 3643 dev: true
... ... @@ -3591,6 +3712,10 @@ packages:
3591 3712 promise: 7.3.1
3592 3713 dev: true
3593 3714
  3715 + /jwt-decode/3.1.2:
  3716 + resolution: {integrity: sha512-UfpWE/VZn0iP50d8cz9NrZLM9lSWhcJ+0Gt/nm4by88UL+J1SiKN8/5dkjMmbEzwL2CAe+67GsegCbIKtbp75A==}
  3717 + dev: false
  3718 +
3594 3719 /keymaster/1.6.2:
3595 3720 resolution: {integrity: sha512-OvA/AALN8IDKKkTk2Z+bDrzs/SQao4lo/QPbwSdDvm+frxfiYiYCSn1aHFUypJY3SruAO1y/c771agBmTXqUtg==}
3596 3721 dev: false
... ... @@ -3790,6 +3915,13 @@ packages:
3790 3915 brace-expansion: 1.1.11
3791 3916 dev: true
3792 3917
  3918 + /minimatch/5.1.6:
  3919 + resolution: {integrity: sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==}
  3920 + engines: {node: '>=10'}
  3921 + dependencies:
  3922 + brace-expansion: 2.0.1
  3923 + dev: true
  3924 +
3793 3925 /minimist-options/4.1.0:
3794 3926 resolution: {integrity: sha512-Q4r8ghd80yhO/0j1O3B2BjweX3fiHg9cdOwjJd2J76Q135c+NDxGCqdYKQ1SKBuFfgWbAUzBfvYjPUEeNgqN1A==}
3795 3927 engines: {node: '>= 6'}
... ... @@ -3818,7 +3950,6 @@ packages:
3818 3950
3819 3951 /monaco-editor/0.33.0:
3820 3952 resolution: {integrity: sha512-VcRWPSLIUEgQJQIE0pVT8FcGBIgFoxz7jtqctE+IiCxWugD0DwgyQBcZBhdSrdMC84eumoqMZsGl2GTreOzwqw==}
3821   - dev: false
3822 3953
3823 3954 /ms/2.0.0:
3824 3955 resolution: {integrity: sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==}
... ... @@ -3882,6 +4013,13 @@ packages:
3882 4013 tslib: 2.4.0
3883 4014 dev: true
3884 4015
  4016 + /node-html-parser/5.4.2:
  4017 + resolution: {integrity: sha512-RaBPP3+51hPne/OolXxcz89iYvQvKOydaqoePpOgXcrOKZhjVIzmpKZz+Hd/RBO2/zN2q6CNJhQzucVz+u3Jyw==}
  4018 + dependencies:
  4019 + css-select: 4.3.0
  4020 + he: 1.2.0
  4021 + dev: true
  4022 +
3885 4023 /node-plop/0.31.0:
3886 4024 resolution: {integrity: sha512-aKLPxiBoFTNUovvtK8j/Whc4PZREkYx6htw2HJPiU8wYquXmN8pkd9B3xlFo6AJ4ZlzFsQSf/NXR5xET8EqRYw==}
3887 4025 engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
... ... @@ -3949,7 +4087,6 @@ packages:
3949 4087
3950 4088 /object-inspect/1.12.2:
3951 4089 resolution: {integrity: sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ==}
3952   - dev: true
3953 4090
3954 4091 /object-keys/1.1.1:
3955 4092 resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==}
... ... @@ -4228,6 +4365,10 @@ packages:
4228 4365 engines: {node: '>=8'}
4229 4366 dev: true
4230 4367
  4368 + /pathe/0.2.0:
  4369 + resolution: {integrity: sha512-sTitTPYnn23esFR3RlqYBWn4c45WGeLcsKzQiUpXJAyfcWkolvlYpV8FLo7JishK946oQwMFUCHXQ9AjGPKExw==}
  4370 + dev: true
  4371 +
4231 4372 /picocolors/1.0.0:
4232 4373 resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==}
4233 4374
... ... @@ -4410,6 +4551,13 @@ packages:
4410 4551 engines: {node: '>=0.6.0', teleport: '>=0.2.0'}
4411 4552 dev: true
4412 4553
  4554 + /qs/6.11.0:
  4555 + resolution: {integrity: sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==}
  4556 + engines: {node: '>=0.6'}
  4557 + dependencies:
  4558 + side-channel: 1.0.4
  4559 + dev: false
  4560 +
4413 4561 /queue-microtask/1.2.3:
4414 4562 resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==}
4415 4563 dev: true
... ... @@ -4487,6 +4635,11 @@ packages:
4487 4635 engines: {node: '>=8'}
4488 4636 dev: true
4489 4637
  4638 + /relateurl/0.2.7:
  4639 + resolution: {integrity: sha512-G08Dxvm4iDN3MLM0EsP62EDV9IuhXPR6blNz6Utcp7zyV3tr4HVNINt6MpaRWbxoOHT3Q7YN2P+jaHX8vUbgog==}
  4640 + engines: {node: '>= 0.10'}
  4641 + dev: true
  4642 +
4490 4643 /request-light/0.5.8:
4491 4644 resolution: {integrity: sha512-3Zjgh+8b5fhRJBQZoy+zbVKpAQGLyka0MPgW3zruTF4dFFJ8Fqcfu9YsAvi/rvdcaTeWG3MkbZv4WKxAn/84Lg==}
4492 4645 dev: true
... ... @@ -4686,7 +4839,6 @@ packages:
4686 4839 call-bind: 1.0.2
4687 4840 get-intrinsic: 1.1.2
4688 4841 object-inspect: 1.12.2
4689   - dev: true
4690 4842
4691 4843 /signal-exit/3.0.7:
4692 4844 resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==}
... ... @@ -4723,6 +4875,13 @@ packages:
4723 4875 resolution: {integrity: sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==}
4724 4876 engines: {node: '>=0.10.0'}
4725 4877
  4878 + /source-map-support/0.5.21:
  4879 + resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==}
  4880 + dependencies:
  4881 + buffer-from: 1.1.2
  4882 + source-map: 0.6.1
  4883 + dev: true
  4884 +
4726 4885 /source-map/0.6.1:
4727 4886 resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==}
4728 4887 engines: {node: '>=0.10.0'}
... ... @@ -4853,6 +5012,17 @@ packages:
4853 5012 resolution: {integrity: sha512-ovssysQTa+luh7A5Weu3Rta6FJlFBBbInjOh722LIt6klpU2/HtdUbszju/G4devcvk8PGt7FCLv5wftu3THUA==}
4854 5013 dev: true
4855 5014
  5015 + /terser/5.16.4:
  5016 + resolution: {integrity: sha512-5yEGuZ3DZradbogeYQ1NaGz7rXVBDWujWlx1PT8efXO6Txn+eWbfKqB2bTDVmFXmePFkoLU6XI8UektMIEA0ug==}
  5017 + engines: {node: '>=10'}
  5018 + hasBin: true
  5019 + dependencies:
  5020 + '@jridgewell/source-map': 0.3.2
  5021 + acorn: 8.7.1
  5022 + commander: 2.20.3
  5023 + source-map-support: 0.5.21
  5024 + dev: true
  5025 +
4856 5026 /text-extensions/1.9.0:
4857 5027 resolution: {integrity: sha512-wiBrwC1EhBelW12Zy26JeOUkQ5mRu+5o8rpsJk5+2t+Y5vE7e842qtZDQ2g1NpX/29HdyFeJ4nSIhI47ENSxlQ==}
4858 5028 engines: {node: '>=0.10'}
... ... @@ -4965,7 +5135,6 @@ packages:
4965 5135
4966 5136 /tslib/2.3.0:
4967 5137 resolution: {integrity: sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==}
4968   - dev: true
4969 5138
4970 5139 /tslib/2.4.0:
4971 5140 resolution: {integrity: sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==}
... ... @@ -5017,7 +5186,6 @@ packages:
5017 5186 resolution: {integrity: sha512-yNIatDa5iaofVozS/uQJEl3JRWLKKGJKh6Yaiv0GLGSuhpFJe7P3SbHZ8/yjAHRQwKRoA6YZqlfjXWmVzoVSMw==}
5018 5187 engines: {node: '>=4.2.0'}
5019 5188 hasBin: true
5020   - dev: true
5021 5189
5022 5190 /typescript/4.7.3:
5023 5191 resolution: {integrity: sha512-WOkT3XYvrpXx4vMMqlD+8R8R37fZkjyLGlxavMc4iB8lrl8L0DeTcHbYgw/v0N/z9wAFsgBhcsF0ruoySS22mA==}
... ... @@ -5137,6 +5305,26 @@ packages:
5137 5305 - supports-color
5138 5306 dev: true
5139 5307
  5308 + /vite-plugin-html/3.2.0_vite@2.9.9:
  5309 + resolution: {integrity: sha512-2VLCeDiHmV/BqqNn5h2V+4280KRgQzCFN47cst3WiNK848klESPQnzuC3okH5XHtgwHH/6s1Ho/YV6yIO0pgoQ==}
  5310 + peerDependencies:
  5311 + vite: '>=2.0.0'
  5312 + dependencies:
  5313 + '@rollup/pluginutils': 4.2.1
  5314 + colorette: 2.0.19
  5315 + connect-history-api-fallback: 1.6.0
  5316 + consola: 2.15.3
  5317 + dotenv: 16.0.3
  5318 + dotenv-expand: 8.0.3
  5319 + ejs: 3.1.8
  5320 + fast-glob: 3.2.11
  5321 + fs-extra: 10.1.0
  5322 + html-minifier-terser: 6.1.0
  5323 + node-html-parser: 5.4.2
  5324 + pathe: 0.2.0
  5325 + vite: 2.9.9_sass@1.52.3
  5326 + dev: true
  5327 +
5140 5328 /vite-plugin-importer/0.2.5:
5141 5329 resolution: {integrity: sha512-6OtqJmVwnfw8+B4OIh7pIdXs+jLkN7g5PIqmZdpgrMYjIFMiZrcMB1zlyUQSTokKGC90KwXviO/lq1hcUBUG3Q==}
5142 5330 dependencies:
... ... @@ -5437,7 +5625,6 @@ packages:
5437 5625 '@vue/runtime-dom': 3.2.37
5438 5626 '@vue/server-renderer': 3.2.37_vue@3.2.37
5439 5627 '@vue/shared': 3.2.37
5440   - dev: false
5441 5628
5442 5629 /vue3-lazyload/0.2.5-beta_2yymnzrok6eda47acnj2yjm3ae:
5443 5630 resolution: {integrity: sha512-GVhJfL9Hcu+AvWsYmUwODivvt+gzpT0ztgAzZaUduoiTaGCv/qzhr0VwAQXfjGF3XFYFyOJsHlAi3/WE0P8XTQ==}
... ... @@ -5603,4 +5790,3 @@ packages:
5603 5790 resolution: {integrity: sha512-8IiYdfwHj2rx0UeIGZGGU4WEVSDEdeVCaIg/fomejg1Xu6OifAL1GVzIPHg2D+MyUkbNgPWji90t0a8IDk+39w==}
5604 5791 dependencies:
5605 5792 tslib: 2.3.0
5606   - dev: true
... ...
  1 +export interface JwtModel {
  2 + userId: string;
  3 + username: string;
  4 + tenantCode: string;
  5 + tenantName: string;
  6 + exp: number;
  7 + role: string[];
  8 +}
... ...
  1 +/**
  2 + * @description: Login interface parameters
  3 + */
  4 +export interface LoginParams {
  5 + username: string;
  6 + password: string;
  7 +}
  8 +export interface SmsLoginParams {
  9 + phoneNumber: string;
  10 + code: string;
  11 +}
  12 +export interface RefreshTokenParams {
  13 + refreshToken: string;
  14 +}
  15 +
  16 +export interface RoleInfo {
  17 + roleName: string;
  18 + value: string;
  19 +}
  20 +
  21 +/**
  22 + * @description: Login interface return value
  23 + */
  24 +export interface LoginResultModel {
  25 + token: string;
  26 + refreshToken: string;
  27 +}
  28 +
  29 +/**
  30 + * @description: Get user information return value
  31 + */
  32 +export interface GetUserInfoModel {
  33 + roles: RoleInfo[];
  34 + // 用户id
  35 + userId: string | number;
  36 + // 用户名
  37 + username: string;
  38 + // 真实名字
  39 + realName: string;
  40 + // 头像
  41 + avatar: string;
  42 + // 介绍
  43 + desc?: string;
  44 +}
  45 +export interface UserInfoModel {
  46 + userId: string | number;
  47 + username: string;
  48 + realName: string;
  49 + avatar: string;
  50 + tenantCode: string;
  51 + tenantName: string;
  52 + roles: string[];
  53 + plainRoles?: PlainRoleInfo[];
  54 +}
  55 +
  56 +export interface PlainRoleInfo {
  57 + roleName: string;
  58 + roleId: string;
  59 +}
... ...
  1 +import { defHttp } from '@/utils/external/http/axios';
  2 +import {
  3 + LoginParams,
  4 + LoginResultModel,
  5 + GetUserInfoModel,
  6 + UserInfoModel,
  7 + RefreshTokenParams,
  8 + SmsLoginParams,
  9 +} from './model/userModel';
  10 +
  11 +import type { ErrorMessageMode } from '/#/external/axios';
  12 +
  13 +enum Api {
  14 + Login = '/auth/login',
  15 + SmsCodeLogin = '/auth/code/login',
  16 + Logout = '/logout',
  17 + GetUserInfo = '/user',
  18 + GetMyInfo = '/user/me/info',
  19 + GetPermCode = '/role/me/permissions',
  20 + RefreshToken = '/auth/token',
  21 + SendLoginSmsCode = '/noauth/send_login_code/',
  22 + ResetCode = '/noauth/reset_code/',
  23 + ResetPassword = '/noauth/reset/',
  24 +}
  25 +
  26 +/**
  27 + * @description: user login api
  28 + */
  29 +export function loginApi(params: LoginParams, mode: ErrorMessageMode = 'modal') {
  30 + return defHttp.post<LoginResultModel>(
  31 + {
  32 + url: Api.Login,
  33 + params,
  34 + },
  35 + {
  36 + errorMessageMode: mode,
  37 + joinPrefix: false,
  38 + }
  39 + );
  40 +}
  41 +export function getMyInfo() {
  42 + return defHttp.get<UserInfoModel>({ url: Api.GetMyInfo });
  43 +}
  44 +/**
  45 + * @description: getUserInfo
  46 + */
  47 +export function getUserInfo() {
  48 + return defHttp.get<GetUserInfoModel>({ url: Api.GetUserInfo });
  49 +}
  50 +
  51 +export function getPermCode() {
  52 + return defHttp.get<string[]>({ url: Api.GetPermCode });
  53 +}
  54 +export async function SendLoginSmsCode(phoneNumber: string) {
  55 + return await defHttp.post<boolean>({ url: Api.SendLoginSmsCode + phoneNumber });
  56 +}
  57 +export function doLogout() {
  58 + // return defHttp.get({ url: Api.Logout });
  59 +}
  60 +export function doRefreshToken(params: RefreshTokenParams) {
  61 + return defHttp.post<LoginResultModel>(
  62 + {
  63 + url: Api.RefreshToken,
  64 + params,
  65 + },
  66 + {
  67 + joinPrefix: false,
  68 + }
  69 + );
  70 +}
  71 +export function smsCodeLoginApi(params: SmsLoginParams, mode: ErrorMessageMode = 'modal') {
  72 + return defHttp.post<LoginResultModel>(
  73 + {
  74 + url: Api.SmsCodeLogin,
  75 + params,
  76 + },
  77 + {
  78 + errorMessageMode: mode,
  79 + }
  80 + );
  81 +}
  82 +
  83 +export const getUserToken = (id: string) => {
  84 + return defHttp.get<Record<'token' | 'refreshToken', string>>({
  85 + url: `/third/login/id/${id}`,
  86 + });
  87 +};
... ...
  1 +// token key
  2 +export const TOKEN_KEY = 'TOKEN__';
  3 +
  4 +export const JWT_TOKEN_KEY = 'JWT_TOKEN';
  5 +
  6 +export const REFRESH_TOKEN_KEY = 'REFRESH_TOKEN';
  7 +
  8 +export const LOCALE_KEY = 'LOCALE__';
  9 +
  10 +// user info key
  11 +export const USER_INFO_KEY = 'USER__INFO__';
  12 +
  13 +// role info key
  14 +export const ROLES_KEY = 'ROLES__KEY';
  15 +
  16 +// project config key
  17 +export const PROJ_CFG_KEY = 'PROJ__CFG__KEY__';
  18 +
  19 +// lock info
  20 +export const LOCK_INFO_KEY = 'LOCK__INFO__KEY__';
  21 +
  22 +export const MULTIPLE_TABS_KEY = 'MULTIPLE_TABS__KEY__';
  23 +
  24 +export const APP_DARK_MODE_KEY_ = '__APP__DARK__MODE__';
  25 +
  26 +// base global local key
  27 +export const APP_LOCAL_CACHE_KEY = 'COMMON__LOCAL__KEY__';
  28 +
  29 +// base global session key
  30 +export const APP_SESSION_CACHE_KEY = 'COMMON__SESSION__KEY__';
  31 +
  32 +export const PLATFORM = 'PLATFORM';
  33 +
  34 +export const MENU_LIST = 'MENU_LIST';
  35 +export enum CacheTypeEnum {
  36 + SESSION,
  37 + LOCAL,
  38 +}
... ...
  1 +/**
  2 + * @description: Request result set
  3 + */
  4 +export enum ResultEnum {
  5 + SUCCESS = 0,
  6 + ERROR = 1,
  7 + TIMEOUT = 401,
  8 + TYPE = 'success',
  9 +}
  10 +
  11 +/**
  12 + * @description: request method
  13 + */
  14 +export enum RequestEnum {
  15 + GET = 'GET',
  16 + POST = 'POST',
  17 + PUT = 'PUT',
  18 + DELETE = 'DELETE',
  19 +}
  20 +
  21 +/**
  22 + * @description: contentTyp
  23 + */
  24 +export enum ContentTypeEnum {
  25 + // json
  26 + JSON = 'application/json;charset=UTF-8',
  27 + // form-data qs
  28 + FORM_URLENCODED = 'application/x-www-form-urlencoded;charset=UTF-8',
  29 + // form-data upload
  30 + FORM_DATA = 'multipart/form-data;charset=UTF-8',
  31 +}
... ...
  1 +export const PageEnum = {
  2 + SYSTEM_PASSWORD: '/system/changePassword',
  3 + // basic login path
  4 + BASE_LOGIN: '/login',
  5 + // basic home path
  6 + // BASE_HOME: isDolang.value == '/dashboard/workbench' ? '/dashboard/workbench' : isDolang.value,
  7 + BASE_HOME: '/dashboard/workbench',
  8 + // error page path
  9 + ERROR_PAGE: '/exception',
  10 + // error log page path
  11 + ERROR_LOG_PAGE: '/error-log/list',
  12 + //消息配置
  13 + MESSAGE_CONFIG: '/message/config',
  14 + //设备配置
  15 + DEVICE_PROFILE: '/product/profiles',
  16 + DEVICE_LIST: '/device/list',
  17 +};
... ...
  1 +export enum RoleEnum {
  2 + SYS_ADMIN = 'SYS_ADMIN',
  3 + PLATFORM_ADMIN = 'PLATFORM_ADMIN',
  4 + TENANT_ADMIN = 'TENANT_ADMIN',
  5 + CUSTOMER_USER = 'CUSTOMER_USER',
  6 +}
  7 +
  8 +export function isAdmin(role: string) {
  9 + if (role === RoleEnum.SYS_ADMIN || role === RoleEnum.PLATFORM_ADMIN) {
  10 + return true;
  11 + } else if (role === RoleEnum.TENANT_ADMIN || role === RoleEnum.CUSTOMER_USER) {
  12 + return false;
  13 + }
  14 +}
  15 +// 按钮级权限控制,只针对客户
  16 +export function authBtn(role: string): boolean {
  17 + return role !== RoleEnum.CUSTOMER_USER
  18 +}
... ...
  1 +import type { GlobConfig } from '/#/external/config';
  2 +import { getAppEnvConfig } from '@/utils/external/env';
  3 +
  4 +export const useGlobSetting = (): Readonly<GlobConfig> => {
  5 + const {
  6 + VITE_GLOB_APP_TITLE,
  7 + VITE_GLOB_API_URL,
  8 + VITE_GLOB_APP_SHORT_NAME,
  9 + VITE_GLOB_API_URL_PREFIX,
  10 + VITE_GLOB_UPLOAD_URL,
  11 + VITE_GLOB_CONFIGURATION,
  12 + VITE_GLOB_WEB_SOCKET,
  13 + VITE_GLOB_CONTENT_SECURITY_POLICY,
  14 + VITE_GLOB_ALARM_NOTIFY_POLLING_INTERVAL_TIME,
  15 + VITE_GLOB_ALARM_NOTIFY_DURATION,
  16 + } = getAppEnvConfig();
  17 +
  18 + if (!/[a-zA-Z_]*/.test(VITE_GLOB_APP_SHORT_NAME)) {
  19 + console.warn(
  20 + `VITE_GLOB_APP_SHORT_NAME Variables can only be characters/underscores, please modify in the environment variables and re-running.`
  21 + );
  22 + }
  23 +
  24 + // Take global configuration
  25 + const glob: Readonly<GlobConfig> = {
  26 + title: VITE_GLOB_APP_TITLE,
  27 + apiUrl: VITE_GLOB_API_URL,
  28 + shortName: VITE_GLOB_APP_SHORT_NAME,
  29 + urlPrefix: VITE_GLOB_API_URL_PREFIX,
  30 + uploadUrl: VITE_GLOB_UPLOAD_URL,
  31 + configurationPrefix: VITE_GLOB_CONFIGURATION,
  32 + socketUrl: VITE_GLOB_WEB_SOCKET,
  33 + securityPolicy: VITE_GLOB_CONTENT_SECURITY_POLICY,
  34 + alarmNotifyDuration: VITE_GLOB_ALARM_NOTIFY_DURATION,
  35 + alarmPollingInterval: VITE_GLOB_ALARM_NOTIFY_POLLING_INTERVAL_TIME,
  36 + };
  37 + return glob as Readonly<GlobConfig>;
  38 +};
... ...
  1 +import { useMessage, useNotification, useDialog } from 'naive-ui'
  2 +
  3 +export function useMEssage() {
  4 + return {
  5 + createMessage: useMessage,
  6 + createNotification: useNotification,
  7 + createDialog: useDialog
  8 + }
  9 +}
... ...
  1 +import { isDevMode } from '@/utils/external/env';
  2 +
  3 +// System default cache time, in seconds
  4 +export const DEFAULT_CACHE_TIME = 60 * 60 * 24 * 7;
  5 +
  6 +// aes encryption key
  7 +export const cacheCipher = {
  8 + key: '_11111000001111@',
  9 + iv: '@11111000001111_',
  10 +};
  11 +
  12 +// Whether the system cache is encrypted using aes
  13 +export const enableStorageEncryption = !isDevMode();
... ...
  1 +import type { ProjectConfig } from '/#/external/config';
  2 +import { CacheTypeEnum } from '@/enums/external/cacheEnum';
  3 +
  4 +
  5 +// ! You need to clear the browser cache after the change
  6 +const setting: Partial<ProjectConfig> = {
  7 + // Whether to show the configuration button
  8 + showSettingButton: true,
  9 +
  10 + // Whether to show the theme switch button
  11 + showDarkModeToggle: true,
  12 +
  13 + // Website gray mode, open for possible mourning dates
  14 + grayMode: false,
  15 +
  16 + // Color Weakness Mode
  17 + colorWeak: false,
  18 +
  19 + // Whether to cancel the menu, the top, the multi-tab page display, for possible embedded in other systems
  20 + fullContent: false,
  21 +
  22 +
  23 + // Whether to display the logo
  24 + showLogo: true,
  25 +
  26 + // Whether to show footer
  27 + showFooter: false,
  28 +
  29 + // Whether to enable KeepAlive cache is best to close during development, otherwise the cache needs to be cleared every time
  30 + openKeepAlive: true,
  31 +
  32 + // Automatic screen lock time, 0 does not lock the screen. Unit minute default 0
  33 + lockTime: 0,
  34 +
  35 + // Whether to show breadcrumbs
  36 + showBreadCrumb: false,
  37 +
  38 + // Whether to show the breadcrumb icon
  39 + showBreadCrumbIcon: false,
  40 +
  41 + // Use error-handler-plugin
  42 + useErrorHandle: false,
  43 +
  44 + // Whether to open back to top
  45 + useOpenBackTop: true,
  46 +
  47 + // Is it possible to embed iframe pages
  48 + canEmbedIFramePage: true,
  49 +
  50 + // Whether to delete unclosed messages and notify when switching the interface
  51 + closeMessageOnSwitch: true,
  52 +
  53 + // Whether to cancel the http request that has been sent but not responded when switching the interface.
  54 + // If it is enabled, I want to overwrite a single interface. Can be set in a separate interface
  55 + removeAllHttpPending: false,
  56 +};
  57 +
  58 +export default setting;
... ...
  1 +import type { UserInfo, UserUpdateInfo } from '/#/external/store';
  2 +import type { ErrorMessageMode } from '/#/external/axios';
  3 +import { defineStore } from 'pinia';
  4 +import { pinia as store } from '@/store';
  5 +import { RoleEnum } from '@/enums/external/roleEnum';
  6 +import { PageEnum } from '@/enums/external/pageEnum';
  7 +import { JWT_TOKEN_KEY, REFRESH_TOKEN_KEY, ROLES_KEY, USER_INFO_KEY } from '@/enums/external/cacheEnum';
  8 +import { getAuthCache, setAuthCache } from '@/utils/external/auth';
  9 +import {
  10 + LoginParams,
  11 + LoginResultModel,
  12 + RefreshTokenParams,
  13 + SmsLoginParams,
  14 +} from '@/api/external/sys/model/userModel';
  15 +import { doRefreshToken, loginApi } from '@/api/external/sys/user';
  16 +import router from '@/router';
  17 +import { createLocalStorage } from '@/utils/external/cache';
  18 +import { useI18n } from 'vue-i18n';
  19 +import { useDialog } from 'naive-ui';
  20 +
  21 +interface UserState {
  22 + platInfo: any;
  23 + enterPriseInfo: any;
  24 + userInfo: Nullable<UserInfo>;
  25 + userUpdateInfo?: Nullable<UserUpdateInfo>;
  26 + token?: string;
  27 + roleList: RoleEnum[];
  28 + sessionTimeout?: boolean;
  29 + lastUpdateTime: number;
  30 + jwtToken?: string;
  31 + refreshToken?: string;
  32 + outTarget?: string;
  33 +}
  34 +
  35 +const storage = createLocalStorage();
  36 +export const useUserStore = defineStore({
  37 + id: 'app-user',
  38 + state: (): UserState => ({
  39 + //平台信息
  40 + platInfo: storage.get('platformInfo') || null,
  41 + enterPriseInfo: storage.get('enterPriseInfo') || null,
  42 +
  43 + // user info
  44 + userInfo: null,
  45 + userUpdateInfo: null,
  46 + // token
  47 + jwtToken: undefined,
  48 + //refresh Token
  49 + refreshToken: undefined,
  50 + // roleList
  51 + roleList: [],
  52 + // Whether the login expired
  53 + sessionTimeout: false,
  54 + // Last fetch time
  55 + lastUpdateTime: 0,
  56 + }),
  57 +
  58 + getters: {
  59 + getPlatInfo(): any {
  60 + return this.platInfo;
  61 + },
  62 + getUserInfo(): UserInfo {
  63 + return this.userInfo || getAuthCache<UserInfo>(USER_INFO_KEY) || {};
  64 + },
  65 +
  66 + getUserUpdateInfo(): UserUpdateInfo {
  67 + return this.userUpdateInfo || {};
  68 + },
  69 + getJwtToken(): string {
  70 + return this.jwtToken || getAuthCache<string>(JWT_TOKEN_KEY);
  71 + },
  72 + getRefreshToken(): string {
  73 + return this.refreshToken || getAuthCache<string>(REFRESH_TOKEN_KEY);
  74 + },
  75 + getRoleList(): RoleEnum[] {
  76 + return this.roleList.length > 0 ? this.roleList : getAuthCache<RoleEnum[]>(ROLES_KEY);
  77 + },
  78 + getSessionTimeout(): boolean {
  79 + return !!this.sessionTimeout;
  80 + },
  81 + getLastUpdateTime(): number {
  82 + return this.lastUpdateTime;
  83 + },
  84 + },
  85 + actions: {
  86 + setPlatInfo(platInfo: any) {
  87 + this.platInfo = platInfo;
  88 + },
  89 + setEnterPriseInfo(enterPriseInfo: any) {
  90 + this.enterPriseInfo = enterPriseInfo;
  91 + },
  92 +
  93 + storeToken(jwtToken: string, refreshToken: string) {
  94 + this.jwtToken = jwtToken;
  95 + this.refreshToken = refreshToken;
  96 + setAuthCache(JWT_TOKEN_KEY, jwtToken);
  97 + setAuthCache(REFRESH_TOKEN_KEY, refreshToken);
  98 + },
  99 + setToken(info: string | undefined) {
  100 + this.jwtToken = info;
  101 + setAuthCache(JWT_TOKEN_KEY, info);
  102 + },
  103 + setRoleList(roleList: RoleEnum[]) {
  104 + this.roleList = roleList;
  105 + setAuthCache(ROLES_KEY, roleList);
  106 + },
  107 + setUserInfo(info: UserInfo | null) {
  108 + this.userInfo = info;
  109 + this.lastUpdateTime = new Date().getTime();
  110 + setAuthCache(USER_INFO_KEY, info);
  111 + },
  112 + setUserUpdateInfo(info: UserUpdateInfo) {
  113 + this.userUpdateInfo = info;
  114 + },
  115 + setSessionTimeout(flag: boolean) {
  116 + this.sessionTimeout = flag;
  117 + },
  118 + resetState() {
  119 + this.userInfo = null;
  120 + this.token = '';
  121 + this.roleList = [];
  122 + this.sessionTimeout = false;
  123 + },
  124 + /**
  125 + * @description: login
  126 + */
  127 + async login(
  128 + params: LoginParams & {
  129 + goHome?: boolean;
  130 + mode?: ErrorMessageMode;
  131 + }
  132 + ) {
  133 + try {
  134 + const { goHome = true, mode, ...loginParams } = params;
  135 + const data = await loginApi(loginParams, mode);
  136 + } catch (error) {
  137 + return Promise.reject(error);
  138 + }
  139 + },
  140 + async process(data: LoginResultModel, goHome?: boolean) {
  141 +
  142 + },
  143 + async smsCodelogin(
  144 + params: SmsLoginParams & {
  145 + goHome?: boolean;
  146 + mode?: ErrorMessageMode;
  147 + }
  148 + ) {
  149 +
  150 + },
  151 + async getMyUserInfoAction() {
  152 +
  153 + },
  154 + /**
  155 + * @description: logout
  156 + */
  157 + async logout(goLogin = false) {
  158 + this.setToken(undefined);
  159 + this.setSessionTimeout(false);
  160 + setAuthCache(REFRESH_TOKEN_KEY, undefined);
  161 + this.setUserInfo(null);
  162 + goLogin && router.push(PageEnum.BASE_LOGIN);
  163 + window.localStorage.clear();
  164 + window.localStorage.removeItem('updateUserInfo');
  165 + },
  166 +
  167 + async doRefresh() {
  168 + try {
  169 + const req = { refreshToken: this.refreshToken } as RefreshTokenParams;
  170 + const data = await doRefreshToken(req);
  171 + const { token, refreshToken } = data;
  172 + this.storeToken(token, refreshToken);
  173 + } catch (error) {
  174 + this.logout();
  175 + }
  176 + },
  177 +
  178 + /**
  179 + * @description: Confirm before logging out
  180 + */
  181 + confirmLoginOut() {
  182 + const { warning } = useDialog();
  183 + const { t } = useI18n();
  184 + warning({
  185 + type: 'warning',
  186 + title: '温馨提醒',
  187 + content: '是否确认退出系统?',
  188 + onPositiveClick: async () => {
  189 + await this.logout(true);
  190 + },
  191 + });
  192 + },
  193 + },
  194 +});
  195 +
  196 +// Need to be used outside the setup
  197 +export function useUserStoreWithOut() {
  198 + return useUserStore(store);
  199 +}
... ...
  1 +import { Persistent, BasicKeys } from '@/utils/external/cache/persistent';
  2 +import { CacheTypeEnum } from '@/enums/external/cacheEnum';
  3 +import projectSetting from '@/settings/external/projectSetting';
  4 +import { JWT_TOKEN_KEY, REFRESH_TOKEN_KEY } from '@/enums/external/cacheEnum';
  5 +
  6 +const { permissionCacheType } = projectSetting;
  7 +const isLocal = permissionCacheType === CacheTypeEnum.LOCAL;
  8 +
  9 +export function getAuthCache<T>(key: BasicKeys) {
  10 + const fn = isLocal ? Persistent.getLocal : Persistent.getSession;
  11 + return fn(key) as T;
  12 +}
  13 +
  14 +export function setAuthCache(key: BasicKeys, value: any) {
  15 + const fn = isLocal ? Persistent.setLocal : Persistent.setSession;
  16 + return fn(key, value, true);
  17 +}
  18 +
  19 +export function clearAuthCache(immediate = true) {
  20 + const fn = isLocal ? Persistent.clearLocal : Persistent.clearSession;
  21 + return fn(immediate);
  22 +}
  23 +export function getJwtToken() {
  24 + return getAuthCache(JWT_TOKEN_KEY);
  25 +}
  26 +export function getRefreshToken() {
  27 + return getAuthCache(REFRESH_TOKEN_KEY);
  28 +}
... ...
  1 +import { getStorageShortName } from '@/utils/external/env';
  2 +import { createStorage as create, CreateStorageParams } from './storageCache';
  3 +import { enableStorageEncryption, DEFAULT_CACHE_TIME } from '@/settings/external/encryptionSetting';
  4 +
  5 +
  6 +export type Options = Partial<CreateStorageParams>;
  7 +
  8 +const createOptions = (storage: Storage, options: Options = {}): Options => {
  9 + return {
  10 + // No encryption in debug mode
  11 + hasEncrypt: enableStorageEncryption,
  12 + storage,
  13 + prefixKey: getStorageShortName(),
  14 + ...options,
  15 + };
  16 +};
  17 +
  18 +export const WebStorage = create(createOptions(sessionStorage));
  19 +
  20 +export const createStorage = (storage: Storage = sessionStorage, options: Options = {}) => {
  21 + return create(createOptions(storage, options));
  22 +};
  23 +
  24 +export const createSessionStorage = (options: Options = {}) => {
  25 + return createStorage(sessionStorage, { ...options, timeout: DEFAULT_CACHE_TIME });
  26 +};
  27 +
  28 +export const createLocalStorage = (options: Options = {}) => {
  29 + return createStorage(localStorage, { ...options, timeout: DEFAULT_CACHE_TIME });
  30 +};
  31 +
  32 +export default WebStorage;
... ...
  1 +export interface Cache<V = any> {
  2 + value?: V;
  3 + timeoutId?: ReturnType<typeof setTimeout>;
  4 + time?: number;
  5 + alive?: number;
  6 +}
  7 +
  8 +const NOT_ALIVE = 0;
  9 +
  10 +export class Memory<T = any, V = any> {
  11 + private cache: { [key in keyof T]?: Cache<V> } = {};
  12 + private alive: number;
  13 +
  14 + constructor(alive = NOT_ALIVE) {
  15 + // Unit second
  16 + this.alive = alive * 1000;
  17 + }
  18 +
  19 + get getCache() {
  20 + return this.cache;
  21 + }
  22 +
  23 + setCache(cache: { [key in keyof T]?: Cache<V> }) {
  24 + this.cache = cache;
  25 + }
  26 +
  27 + // get<K extends keyof T>(key: K) {
  28 + // const item = this.getItem(key);
  29 + // const time = item?.time;
  30 + // if (!isNullOrUnDef(time) && time < new Date().getTime()) {
  31 + // this.remove(key);
  32 + // }
  33 + // return item?.value ?? undefined;
  34 + // }
  35 +
  36 + get<K extends keyof T>(key: K) {
  37 + return this.cache[key];
  38 + }
  39 +
  40 + set<K extends keyof T>(key: K, value: V, expires?: number) {
  41 + let item = this.get(key);
  42 +
  43 + if (!expires || (expires as number) <= 0) {
  44 + expires = this.alive;
  45 + }
  46 + if (item) {
  47 + if (item.timeoutId) {
  48 + clearTimeout(item.timeoutId);
  49 + item.timeoutId = undefined;
  50 + }
  51 + item.value = value;
  52 + } else {
  53 + item = { value, alive: expires };
  54 + this.cache[key] = item;
  55 + }
  56 +
  57 + if (!expires) {
  58 + return value;
  59 + }
  60 + const now = new Date().getTime();
  61 + item.time = now + this.alive;
  62 + item.timeoutId = setTimeout(
  63 + () => {
  64 + this.remove(key);
  65 + },
  66 + expires > now ? expires - now : expires
  67 + );
  68 +
  69 + return value;
  70 + }
  71 +
  72 + remove<K extends keyof T>(key: K) {
  73 + const item = this.get(key);
  74 + Reflect.deleteProperty(this.cache, key);
  75 + if (item) {
  76 + clearTimeout(item.timeoutId!);
  77 + return item.value;
  78 + }
  79 + }
  80 +
  81 + resetCache(cache: { [K in keyof T]: Cache }) {
  82 + Object.keys(cache).forEach((key) => {
  83 + const k = key as any as keyof T;
  84 + const item = cache[k];
  85 + if (item && item.time) {
  86 + const now = new Date().getTime();
  87 + const expire = item.time;
  88 + if (expire > now) {
  89 + this.set(k, item.value, expire);
  90 + }
  91 + }
  92 + });
  93 + }
  94 +
  95 + clear() {
  96 + Object.keys(this.cache).forEach((key) => {
  97 + const item: Cache<V> = this.cache[key as keyof T]!;
  98 + item.timeoutId && clearTimeout(item.timeoutId);
  99 + });
  100 + this.cache = {};
  101 + }
  102 +}
... ...
  1 +import type { LockInfo, UserInfo } from '/#/external/store';
  2 +import type { ProjectConfig } from '/#/external/config';
  3 +import type { RouteLocationNormalized } from 'vue-router';
  4 +
  5 +import { createLocalStorage, createSessionStorage } from '@/utils/external/cache';
  6 +import { Memory } from './memory';
  7 +import {
  8 + PLATFORM,
  9 + TOKEN_KEY,
  10 + JWT_TOKEN_KEY,
  11 + REFRESH_TOKEN_KEY,
  12 + USER_INFO_KEY,
  13 + ROLES_KEY,
  14 + LOCK_INFO_KEY,
  15 + PROJ_CFG_KEY,
  16 + APP_LOCAL_CACHE_KEY,
  17 + APP_SESSION_CACHE_KEY,
  18 + MULTIPLE_TABS_KEY,
  19 + MENU_LIST,
  20 +} from '@/enums/external/cacheEnum';
  21 +import { DEFAULT_CACHE_TIME } from '@/settings/external/encryptionSetting';
  22 +import { toRaw } from 'vue';
  23 +import omit from 'lodash/omit';
  24 +import pick from 'lodash/pick';
  25 +
  26 +interface BasicStore {
  27 + [PLATFORM]: Object;
  28 + [TOKEN_KEY]: string | number | null | undefined;
  29 + [JWT_TOKEN_KEY]: string | number | null | undefined;
  30 + [REFRESH_TOKEN_KEY]: string | number | null | undefined;
  31 + [USER_INFO_KEY]: UserInfo;
  32 + [ROLES_KEY]: string[];
  33 + [LOCK_INFO_KEY]: LockInfo;
  34 + [PROJ_CFG_KEY]: ProjectConfig;
  35 + [MULTIPLE_TABS_KEY]: RouteLocationNormalized[];
  36 + [MENU_LIST]: any[];
  37 +}
  38 +
  39 +type LocalStore = BasicStore;
  40 +
  41 +type SessionStore = BasicStore;
  42 +
  43 +export type BasicKeys = keyof BasicStore;
  44 +type LocalKeys = keyof LocalStore;
  45 +type SessionKeys = keyof SessionStore;
  46 +
  47 +const ls = createLocalStorage();
  48 +const ss = createSessionStorage();
  49 +
  50 +const localMemory = new Memory(DEFAULT_CACHE_TIME);
  51 +const sessionMemory = new Memory(DEFAULT_CACHE_TIME);
  52 +
  53 +function initPersistentMemory() {
  54 + const localCache = ls.get(APP_LOCAL_CACHE_KEY);
  55 + const sessionCache = ss.get(APP_SESSION_CACHE_KEY);
  56 + localCache && localMemory.resetCache(localCache);
  57 + sessionCache && sessionMemory.resetCache(sessionCache);
  58 +}
  59 +
  60 +export class Persistent {
  61 + static getLocal<T>(key: LocalKeys) {
  62 + return localMemory.get(key)?.value as Nullable<T>;
  63 + }
  64 +
  65 + static setLocal(key: LocalKeys, value: LocalStore[LocalKeys], immediate = false): void {
  66 + localMemory.set(key, toRaw(value));
  67 + immediate && ls.set(APP_LOCAL_CACHE_KEY, localMemory.getCache);
  68 + }
  69 +
  70 + static removeLocal(key: LocalKeys, immediate = false): void {
  71 + localMemory.remove(key);
  72 + immediate && ls.set(APP_LOCAL_CACHE_KEY, localMemory.getCache);
  73 + }
  74 +
  75 + static clearLocal(immediate = false): void {
  76 + localMemory.clear();
  77 + immediate && ls.clear();
  78 + }
  79 +
  80 + static getSession<T>(key: SessionKeys) {
  81 + return sessionMemory.get(key)?.value as Nullable<T>;
  82 + }
  83 +
  84 + static setSession(key: SessionKeys, value: SessionStore[SessionKeys], immediate = false): void {
  85 + sessionMemory.set(key, toRaw(value));
  86 + immediate && ss.set(APP_SESSION_CACHE_KEY, sessionMemory.getCache);
  87 + }
  88 +
  89 + static removeSession(key: SessionKeys, immediate = false): void {
  90 + sessionMemory.remove(key);
  91 + immediate && ss.set(APP_SESSION_CACHE_KEY, sessionMemory.getCache);
  92 + }
  93 + static clearSession(immediate = false): void {
  94 + sessionMemory.clear();
  95 + immediate && ss.clear();
  96 + }
  97 +
  98 + static clearAll(immediate = false) {
  99 + sessionMemory.clear();
  100 + localMemory.clear();
  101 + if (immediate) {
  102 + ls.clear();
  103 + ss.clear();
  104 + }
  105 + }
  106 +}
  107 +
  108 +window.addEventListener('beforeunload', function () {
  109 + // TOKEN_KEY 在登录或注销时已经写入到storage了,此处为了解决同时打开多个窗口时token不同步的问题
  110 + // LOCK_INFO_KEY 在锁屏和解锁时写入,此处也不应修改
  111 + ls.set(APP_LOCAL_CACHE_KEY, {
  112 + ...omit(localMemory.getCache, LOCK_INFO_KEY),
  113 + ...pick(ls.get(APP_LOCAL_CACHE_KEY), [
  114 + TOKEN_KEY,
  115 + JWT_TOKEN_KEY,
  116 + REFRESH_TOKEN_KEY,
  117 + USER_INFO_KEY,
  118 + LOCK_INFO_KEY,
  119 + ROLES_KEY,
  120 + ]),
  121 + });
  122 + ss.set(APP_SESSION_CACHE_KEY, {
  123 + ...omit(sessionMemory.getCache, LOCK_INFO_KEY),
  124 + ...pick(ss.get(APP_SESSION_CACHE_KEY), [
  125 + TOKEN_KEY,
  126 + JWT_TOKEN_KEY,
  127 + REFRESH_TOKEN_KEY,
  128 + USER_INFO_KEY,
  129 + LOCK_INFO_KEY,
  130 + ROLES_KEY,
  131 + ]),
  132 + });
  133 +});
  134 +
  135 +function storageChange(e: any) {
  136 + const { key, newValue, oldValue } = e;
  137 +
  138 + if (!key) {
  139 + Persistent.clearAll();
  140 + return;
  141 + }
  142 +
  143 + if (!!newValue && !!oldValue) {
  144 + if (APP_LOCAL_CACHE_KEY === key) {
  145 + Persistent.clearLocal();
  146 + }
  147 + if (APP_SESSION_CACHE_KEY === key) {
  148 + Persistent.clearSession();
  149 + }
  150 + }
  151 +}
  152 +
  153 +window.addEventListener('storage', storageChange);
  154 +
  155 +initPersistentMemory();
... ...
  1 +import { cacheCipher } from '@/settings/external/encryptionSetting';
  2 +
  3 +import type { EncryptionParams } from '@/utils/external/cipher';
  4 +
  5 +import { AesEncryption } from '@/utils/external/cipher';
  6 +
  7 +import { isNullOrUnDef } from '@/utils/external/is';
  8 +
  9 +export interface CreateStorageParams extends EncryptionParams {
  10 + prefixKey: string;
  11 + storage: Storage;
  12 + hasEncrypt: boolean;
  13 + timeout?: Nullable<number>;
  14 +}
  15 +export const createStorage = ({
  16 + prefixKey = '',
  17 + storage = sessionStorage,
  18 + key = cacheCipher.key,
  19 + iv = cacheCipher.iv,
  20 + timeout = null,
  21 + hasEncrypt = true,
  22 +}: Partial<CreateStorageParams> = {}) => {
  23 + if (hasEncrypt && [key.length, iv.length].some((item) => item !== 16)) {
  24 + throw new Error('When hasEncrypt is true, the key or iv must be 16 bits!');
  25 + }
  26 +
  27 + const encryption = new AesEncryption({ key, iv });
  28 +
  29 + /**
  30 + *Cache class
  31 + *Construction parameters can be passed into sessionStorage, localStorage,
  32 + * @class Cache
  33 + * @example
  34 + */
  35 + const WebStorage = class WebStorage {
  36 + private storage: Storage;
  37 + private prefixKey?: string;
  38 + private encryption: AesEncryption;
  39 + private hasEncrypt: boolean;
  40 + /**
  41 + *
  42 + * @param {*} storage
  43 + */
  44 + constructor() {
  45 + this.storage = storage;
  46 + this.prefixKey = prefixKey;
  47 + this.encryption = encryption;
  48 + this.hasEncrypt = hasEncrypt;
  49 + }
  50 +
  51 + private getKey(key: string) {
  52 + return `${this.prefixKey}${key}`.toUpperCase();
  53 + }
  54 +
  55 + /**
  56 + *
  57 + * Set cache
  58 + * @param {string} key
  59 + * @param {*} value
  60 + * @expire Expiration time in seconds
  61 + * @memberof Cache
  62 + */
  63 + set(key: string, value: any, expire: number | null = timeout) {
  64 + const stringData = JSON.stringify({
  65 + value,
  66 + time: Date.now(),
  67 + expire: !isNullOrUnDef(expire) ? new Date().getTime() + expire * 1000 : null,
  68 + });
  69 + const stringifyValue = this.hasEncrypt
  70 + ? this.encryption.encryptByAES(stringData)
  71 + : stringData;
  72 + this.storage.setItem(this.getKey(key), stringifyValue);
  73 + }
  74 +
  75 + /**
  76 + *Read cache
  77 + * @param {string} key
  78 + * @memberof Cache
  79 + */
  80 + get(key: string, def: any = null): any {
  81 + const val = this.storage.getItem(this.getKey(key));
  82 + if (!val) return def;
  83 +
  84 + try {
  85 + const decVal = this.hasEncrypt ? this.encryption.decryptByAES(val) : val;
  86 + const data = JSON.parse(decVal);
  87 + const { value, expire } = data;
  88 + if (isNullOrUnDef(expire) || expire >= new Date().getTime()) {
  89 + return value;
  90 + }
  91 + this.remove(key);
  92 + } catch (e) {
  93 + return def;
  94 + }
  95 + }
  96 +
  97 + /**
  98 + * Delete cache based on key
  99 + * @param {string} key
  100 + * @memberof Cache
  101 + */
  102 + remove(key: string) {
  103 + this.storage.removeItem(this.getKey(key));
  104 + }
  105 +
  106 + /**
  107 + * Delete all caches of this instance
  108 + */
  109 + clear(): void {
  110 + this.storage.clear();
  111 + }
  112 + };
  113 + return new WebStorage();
  114 +};
... ...
  1 +import { encrypt, decrypt } from 'crypto-js/aes';
  2 +import { parse } from 'crypto-js/enc-utf8';
  3 +import pkcs7 from 'crypto-js/pad-pkcs7';
  4 +import ECB from 'crypto-js/mode-ecb';
  5 +import md5 from 'crypto-js/md5';
  6 +import UTF8 from 'crypto-js/enc-utf8';
  7 +import Base64 from 'crypto-js/enc-base64';
  8 +
  9 +export interface EncryptionParams {
  10 + key: string;
  11 + iv: string;
  12 +}
  13 +
  14 +export class AesEncryption {
  15 + private key;
  16 + private iv;
  17 +
  18 + constructor(opt: Partial<EncryptionParams> = {}) {
  19 + const { key, iv } = opt;
  20 + if (key) {
  21 + this.key = parse(key);
  22 + }
  23 + if (iv) {
  24 + this.iv = parse(iv);
  25 + }
  26 + }
  27 +
  28 + get getOptions() {
  29 + return {
  30 + mode: ECB,
  31 + padding: pkcs7,
  32 + iv: this.iv,
  33 + };
  34 + }
  35 +
  36 + encryptByAES(cipherText: string) {
  37 + return encrypt(cipherText, this.key!, this.getOptions).toString();
  38 + }
  39 +
  40 + decryptByAES(cipherText: string) {
  41 + return decrypt(cipherText, this.key!, this.getOptions).toString(UTF8);
  42 + }
  43 +}
  44 +
  45 +export function encryptByBase64(cipherText: string) {
  46 + return UTF8.parse(cipherText).toString(Base64);
  47 +}
  48 +
  49 +export function decodeByBase64(cipherText: string) {
  50 + return Base64.parse(cipherText).toString(UTF8);
  51 +}
  52 +
  53 +export function encryptByMd5(password: string) {
  54 + return md5(password).toString();
  55 +}
... ...
  1 +import type { GlobEnvConfig } from '/#/external/config';
  2 +
  3 +import pkg from '../../../../package.json';
  4 +import { getGlobalConfigName } from 'build/external/globConfig/getGlobConfigName';
  5 +
  6 +export function getCommonStoragePrefix() {
  7 + const { VITE_GLOB_APP_SHORT_NAME } = getAppEnvConfig();
  8 + return `${VITE_GLOB_APP_SHORT_NAME}__${getEnv()}`.toUpperCase();
  9 +}
  10 +
  11 +// Generate cache key according to version
  12 +export function getStorageShortName() {
  13 + return `${getCommonStoragePrefix()}${`__${pkg.version}`}__`.toUpperCase();
  14 +}
  15 +
  16 +export function getAppEnvConfig() {
  17 + const ENV_NAME = getGlobalConfigName(import.meta.env);
  18 +
  19 + const ENV = (import.meta.env.DEV
  20 + ? // Get the global configuration (the configuration will be extracted independently when packaging)
  21 + (import.meta.env as unknown as GlobEnvConfig)
  22 + : window[ENV_NAME as any]) as unknown as GlobEnvConfig;
  23 + const {
  24 + VITE_GLOB_APP_TITLE,
  25 + VITE_GLOB_API_URL,
  26 + VITE_GLOB_APP_SHORT_NAME,
  27 + VITE_GLOB_API_URL_PREFIX,
  28 + VITE_GLOB_UPLOAD_URL,
  29 + VITE_GLOB_CONFIGURATION,
  30 + VITE_GLOB_WEB_SOCKET,
  31 + VITE_GLOB_CONTENT_SECURITY_POLICY,
  32 + VITE_GLOB_ALARM_NOTIFY_POLLING_INTERVAL_TIME,
  33 + VITE_GLOB_ALARM_NOTIFY_DURATION,
  34 + } = ENV;
  35 +
  36 + if (!/^[a-zA-Z\_]*$/.test(VITE_GLOB_APP_SHORT_NAME)) {
  37 + console.warn(
  38 + `VITE_GLOB_APP_SHORT_NAME Variables can only be characters/underscores, please modify in the environment variables and re-running.`
  39 + );
  40 + }
  41 +
  42 + return {
  43 + VITE_GLOB_APP_TITLE,
  44 + VITE_GLOB_API_URL,
  45 + VITE_GLOB_APP_SHORT_NAME,
  46 + VITE_GLOB_API_URL_PREFIX,
  47 + VITE_GLOB_UPLOAD_URL,
  48 + VITE_GLOB_CONFIGURATION,
  49 + VITE_GLOB_WEB_SOCKET,
  50 + VITE_GLOB_CONTENT_SECURITY_POLICY,
  51 + VITE_GLOB_ALARM_NOTIFY_POLLING_INTERVAL_TIME,
  52 + VITE_GLOB_ALARM_NOTIFY_DURATION,
  53 + };
  54 +}
  55 +
  56 +/**
  57 + * @description: Development mode
  58 + */
  59 +export const devMode = 'development';
  60 +
  61 +/**
  62 + * @description: Production mode
  63 + */
  64 +export const prodMode = 'production';
  65 +
  66 +/**
  67 + * @description: Get environment variables
  68 + * @returns:
  69 + * @example:
  70 + */
  71 +export function getEnv(): string {
  72 + return import.meta.env.MODE;
  73 +}
  74 +
  75 +/**
  76 + * @description: Is it a development mode
  77 + * @returns:
  78 + * @example:
  79 + */
  80 +export function isDevMode(): boolean {
  81 + return import.meta.env.DEV;
  82 +}
  83 +
  84 +/**
  85 + * @description: Is it a production mode
  86 + * @returns:
  87 + * @example:
  88 + */
  89 +export function isProdMode(): boolean {
  90 + return import.meta.env.PROD;
  91 +}
... ...
  1 +import type { AxiosRequestConfig, AxiosInstance, AxiosResponse, AxiosError } from 'axios';
  2 +import type { CreateAxiosOptions } from './axiosTransform';
  3 +import axios from 'axios';
  4 +import qs from 'qs';
  5 +import { AxiosCanceler } from './axiosCancel';
  6 +import jwt_decode from 'jwt-decode';
  7 +import { RequestOptions, Result, UploadFileParams } from '/#/external/axios';
  8 +import { JwtModel } from '@/api/external/sys/model/jwtModel';
  9 +import { isFunction } from '@/utils/external/is'
  10 +import { ContentTypeEnum, RequestEnum } from '@/enums/external/httpEnum';
  11 +import omit from 'lodash/omit';
  12 +import cloneDeep from 'lodash/cloneDeep';
  13 +import { useUserStore } from '@/store/external/module/user';
  14 +
  15 +export * from './axiosTransform';
  16 +
  17 +/**
  18 + * @description: axios module
  19 + */
  20 +export class VAxios {
  21 + private axiosInstance: AxiosInstance;
  22 + private readonly options: CreateAxiosOptions;
  23 + private waitingQueue: any[];
  24 + private refreshing = false;
  25 +
  26 + constructor(options: CreateAxiosOptions) {
  27 + this.options = options;
  28 + this.axiosInstance = axios.create(options);
  29 + this.setupInterceptors();
  30 + this.waitingQueue = [];
  31 + }
  32 +
  33 + /**
  34 + * @description: Create axios instance
  35 + */
  36 + private createAxios(config: CreateAxiosOptions): void {
  37 + this.axiosInstance = axios.create(config);
  38 + }
  39 +
  40 + private getTransform() {
  41 + const { transform } = this.options;
  42 + return transform;
  43 + }
  44 +
  45 + getAxios(): AxiosInstance {
  46 + return this.axiosInstance;
  47 + }
  48 +
  49 + /**
  50 + * @description: Reconfigure axios
  51 + */
  52 + configAxios(config: CreateAxiosOptions) {
  53 + if (!this.axiosInstance) {
  54 + return;
  55 + }
  56 + this.createAxios(config);
  57 + }
  58 +
  59 + /**
  60 + * @description: Set general header
  61 + */
  62 + setHeader(headers: any): void {
  63 + if (!this.axiosInstance) {
  64 + return;
  65 + }
  66 + Object.assign(this.axiosInstance.defaults.headers, headers);
  67 + }
  68 + /**
  69 + * JWT 自动刷新
  70 + * @description: 自动刷新token
  71 + */
  72 + private isNeedTokenURL(url: string, arr = ['/auth/login', '/auth/token']) {
  73 + return !arr.some((val) => url.indexOf(val) > -1);
  74 + }
  75 +
  76 + /**
  77 + *
  78 + * @returns
  79 + */
  80 + private refreshTokenBeforeReq(doRefreshTokenApi: () => Promise<unknown>): Promise<unknown> {
  81 + // 创建一个未完成的promise,把改变状态的resolve方法交给请求token结束后执行
  82 + const promise = new Promise((resolve) => {
  83 + // 等待队列放的是一个回调函数,来延迟resolve的执行,以此控制promise状态的改变
  84 + this.waitingQueue.push(() => resolve(null));
  85 + });
  86 + if (!this.refreshing) {
  87 + this.refreshing = true;
  88 + // 模拟请求刷新Token接口,当接口返回数据时执行then方法 TODO 添加catch捕获异常
  89 + doRefreshTokenApi().then(() => {
  90 + this.refreshing = false;
  91 + this.waitingQueue.forEach((cb) => cb());
  92 + this.waitingQueue.length = 0;
  93 + });
  94 + }
  95 + return promise;
  96 + }
  97 + /**
  98 + * @description: Interceptor configuration
  99 + */
  100 + private setupInterceptors() {
  101 + const transform = this.getTransform();
  102 + if (!transform) {
  103 + return;
  104 + }
  105 + const {
  106 + requestInterceptors,
  107 + requestInterceptorsCatch,
  108 + responseInterceptors,
  109 + responseInterceptorsCatch,
  110 + } = transform;
  111 +
  112 + const axiosCanceler = new AxiosCanceler();
  113 +
  114 + // Request interceptor configuration processing
  115 + this.axiosInstance.interceptors.request.use(async (config: AxiosRequestConfig) => {
  116 + // If cancel repeat request is turned on, then cancel repeat request is prohibited
  117 + const userStore = useUserStore();
  118 + if (userStore && userStore.jwtToken) {
  119 + try {
  120 + const res = jwt_decode(userStore.jwtToken) as JwtModel;
  121 + const currentTime = new Date().getTime() / 1000;
  122 + if (currentTime >= res.exp && this.isNeedTokenURL(config.url!)) {
  123 + await this.refreshTokenBeforeReq(userStore.doRefresh);
  124 + }
  125 + } catch (error) {
  126 + userStore.logout();
  127 + }
  128 + }
  129 + const {
  130 + headers: { ignoreCancelToken } = {},
  131 + } = config;
  132 +
  133 + const ignoreCancel =
  134 + ignoreCancelToken !== undefined
  135 + ? ignoreCancelToken
  136 + : this.options.requestOptions?.ignoreCancelToken;
  137 +
  138 + !ignoreCancel && axiosCanceler.addPending(config);
  139 + if (requestInterceptors && isFunction(requestInterceptors)) {
  140 + config = requestInterceptors(config, this.options);
  141 + }
  142 + return config;
  143 + }, undefined);
  144 +
  145 + // Request interceptor error capture
  146 + requestInterceptorsCatch &&
  147 + isFunction(requestInterceptorsCatch) &&
  148 + this.axiosInstance.interceptors.request.use(undefined, requestInterceptorsCatch);
  149 +
  150 + // Response result interceptor processing
  151 + this.axiosInstance.interceptors.response.use((res: AxiosResponse<any>) => {
  152 + res && axiosCanceler.removePending(res.config);
  153 + if (responseInterceptors && isFunction(responseInterceptors)) {
  154 + res = responseInterceptors(res);
  155 + }
  156 + return res;
  157 + }, undefined);
  158 +
  159 + // Response result interceptor error capture
  160 + responseInterceptorsCatch &&
  161 + isFunction(responseInterceptorsCatch) &&
  162 + this.axiosInstance.interceptors.response.use(undefined, responseInterceptorsCatch);
  163 + }
  164 +
  165 + /**
  166 + * @description: File Upload
  167 + */
  168 + uploadFile<T = any>(config: AxiosRequestConfig, params: UploadFileParams) {
  169 + const formData = new window.FormData();
  170 +
  171 + if (params.data) {
  172 + Object.keys(params.data).forEach((key) => {
  173 + if (!params.data) return;
  174 + const value = params.data[key];
  175 + if (Array.isArray(value)) {
  176 + value.forEach((item) => {
  177 + formData.append(`${key}[]`, item);
  178 + });
  179 + return;
  180 + }
  181 +
  182 + formData.append(key, params.data[key]);
  183 + });
  184 + }
  185 + formData.append(params.name || 'file', params.file, params.filename);
  186 + const customParams = omit(params, 'file', 'filename', 'file');
  187 +
  188 + Object.keys(customParams).forEach((key) => {
  189 + formData.append(key, customParams[key]);
  190 + });
  191 +
  192 + return this.axiosInstance.request<T>({
  193 + ...config,
  194 + method: 'POST',
  195 + data: formData,
  196 + headers: {
  197 + 'Content-type': ContentTypeEnum.FORM_DATA,
  198 + ignoreCancelToken: true,
  199 + },
  200 + });
  201 + }
  202 +
  203 + // support form-data
  204 + supportFormData(config: AxiosRequestConfig) {
  205 + const headers = config.headers || this.options.headers;
  206 + const contentType = headers?.['Content-Type'] || headers?.['content-type'];
  207 +
  208 + if (
  209 + contentType !== ContentTypeEnum.FORM_URLENCODED ||
  210 + !Reflect.has(config, 'data') ||
  211 + config.method?.toUpperCase() === RequestEnum.GET
  212 + ) {
  213 + return config;
  214 + }
  215 +
  216 + return {
  217 + ...config,
  218 + data: qs.stringify(config.data, { arrayFormat: 'brackets' }),
  219 + };
  220 + }
  221 +
  222 + get<T = any>(config: AxiosRequestConfig, options?: RequestOptions): Promise<T> {
  223 + return this.request({ ...config, method: 'GET' }, options);
  224 + }
  225 +
  226 + post<T = any>(config: AxiosRequestConfig, options?: RequestOptions): Promise<T> {
  227 + return this.request({ ...config, method: 'POST' }, options);
  228 + }
  229 +
  230 + put<T = any>(config: AxiosRequestConfig, options?: RequestOptions): Promise<T> {
  231 + return this.request({ ...config, method: 'PUT' }, options);
  232 + }
  233 +
  234 + delete<T = any>(config: AxiosRequestConfig, options?: RequestOptions): Promise<T> {
  235 + return this.request({ ...config, method: 'DELETE' }, options);
  236 + }
  237 +
  238 + request<T = any>(config: AxiosRequestConfig, options?: RequestOptions): Promise<T> {
  239 + let conf: CreateAxiosOptions = cloneDeep(config);
  240 + const transform = this.getTransform();
  241 +
  242 + const { requestOptions } = this.options;
  243 +
  244 + const opt: RequestOptions = Object.assign({}, requestOptions, options);
  245 +
  246 + const { beforeRequestHook, requestCatchHook, transformRequestHook } = transform || {};
  247 + if (beforeRequestHook && isFunction(beforeRequestHook)) {
  248 + conf = beforeRequestHook(conf, opt);
  249 + }
  250 + conf.requestOptions = opt;
  251 +
  252 + conf = this.supportFormData(conf);
  253 +
  254 + return new Promise((resolve, reject) => {
  255 + this.axiosInstance
  256 + .request<any, AxiosResponse<Result>>(conf)
  257 + .then((res: AxiosResponse<Result>) => {
  258 + if (transformRequestHook && isFunction(transformRequestHook)) {
  259 + try {
  260 + const ret = transformRequestHook(res, opt);
  261 + resolve(ret);
  262 + } catch (err) {
  263 + reject(err || new Error('request error!'));
  264 + }
  265 + return;
  266 + }
  267 + resolve(res as unknown as Promise<T>);
  268 + })
  269 + .catch((e: Error | AxiosError) => {
  270 + if (requestCatchHook && isFunction(requestCatchHook)) {
  271 + reject(requestCatchHook(e, opt));
  272 + return;
  273 + }
  274 + if (axios.isAxiosError(e)) {
  275 + // rewrite error message from axios in here
  276 + }
  277 + reject(e);
  278 + });
  279 + });
  280 + }
  281 +}
... ...
  1 +import type { AxiosRequestConfig, Canceler } from 'axios';
  2 +import axios from 'axios';
  3 +import { isFunction } from '@/utils/external/is';
  4 +
  5 +// Used to store the identification and cancellation function of each request
  6 +let pendingMap = new Map<string, Canceler>();
  7 +
  8 +export const getPendingUrl = (config: AxiosRequestConfig) => [config.method, config.url].join('&');
  9 +
  10 +export class AxiosCanceler {
  11 + /**
  12 + * Add request
  13 + * @param {Object} config
  14 + */
  15 + addPending(config: AxiosRequestConfig) {
  16 + this.removePending(config);
  17 + const url = getPendingUrl(config);
  18 + config.cancelToken =
  19 + config.cancelToken ||
  20 + new axios.CancelToken((cancel) => {
  21 + if (!pendingMap.has(url)) {
  22 + // If there is no current request in pending, add it
  23 + pendingMap.set(url, cancel);
  24 + }
  25 + });
  26 + }
  27 +
  28 + /**
  29 + * @description: Clear all pending
  30 + */
  31 + removeAllPending() {
  32 + pendingMap.forEach((cancel) => {
  33 + cancel && isFunction(cancel) && cancel();
  34 + });
  35 + pendingMap.clear();
  36 + }
  37 +
  38 + /**
  39 + * Removal request
  40 + * @param {Object} config
  41 + */
  42 + removePending(config: AxiosRequestConfig) {
  43 + const url = getPendingUrl(config);
  44 +
  45 + if (pendingMap.has(url)) {
  46 + // If there is a current request identifier in pending,
  47 + // the current request needs to be cancelled and removed
  48 + const cancel = pendingMap.get(url);
  49 + cancel && cancel(url);
  50 + pendingMap.delete(url);
  51 + }
  52 + }
  53 +
  54 + /**
  55 + * @description: reset
  56 + */
  57 + reset(): void {
  58 + pendingMap = new Map<string, Canceler>();
  59 + }
  60 +}
... ...
  1 +/**
  2 + * Data processing class, can be configured according to the project
  3 + */
  4 +import type { AxiosRequestConfig, AxiosResponse } from 'axios';
  5 +import type { RequestOptions, Result } from '/#/external/axios';
  6 +
  7 +export interface CreateAxiosOptions extends AxiosRequestConfig {
  8 + authenticationScheme?: string;
  9 + urlPrefix?: string;
  10 + transform?: AxiosTransform;
  11 + requestOptions?: RequestOptions;
  12 +}
  13 +
  14 +export abstract class AxiosTransform {
  15 + /**
  16 + * @description: Process configuration before request
  17 + * @description: Process configuration before request
  18 + */
  19 + beforeRequestHook?: (config: AxiosRequestConfig, options: RequestOptions) => AxiosRequestConfig;
  20 +
  21 + /**
  22 + * @description: Request successfully processed
  23 + */
  24 + transformRequestHook?: (res: AxiosResponse<Result>, options: RequestOptions) => any;
  25 +
  26 + /**
  27 + * @description: 请求失败处理
  28 + */
  29 + requestCatchHook?: (e: Error, options: RequestOptions) => Promise<any>;
  30 +
  31 + /**
  32 + * @description: 请求之前的拦截器
  33 + */
  34 + requestInterceptors?: (
  35 + config: AxiosRequestConfig,
  36 + options: CreateAxiosOptions
  37 + ) => AxiosRequestConfig;
  38 +
  39 + /**
  40 + * @description: 请求之后的拦截器
  41 + */
  42 + responseInterceptors?: (res: AxiosResponse<any>) => AxiosResponse<any>;
  43 +
  44 + /**
  45 + * @description: 请求之前的拦截器错误处理
  46 + */
  47 + requestInterceptorsCatch?: (error: Error) => void;
  48 +
  49 + /**
  50 + * @description: 请求之后的拦截器错误处理
  51 + */
  52 + responseInterceptorsCatch?: (error: Error) => void;
  53 +}
... ...
  1 +import type { ErrorMessageMode } from '/#/external/axios';
  2 +import { useUserStoreWithOut } from '@/store/external/module/user';
  3 +import { useI18n } from 'vue-i18n';
  4 +import { useMessage } from 'naive-ui';
  5 +
  6 +const { error } = useMessage()
  7 +export function checkStatus(
  8 + status: number,
  9 + msg: string,
  10 + errorMessageMode: ErrorMessageMode = 'message'
  11 +): void {
  12 + const { t } = useI18n();
  13 + const userStore = useUserStoreWithOut();
  14 + let errMessage = msg;
  15 + switch (status) {
  16 + case 400:
  17 + errMessage = `${msg}`;
  18 + break;
  19 + // 401: Not logged in
  20 + // Jump to the login page if not logged in, and carry the path of the current page
  21 + // Return to the current page after successful login. This step needs to be operated on the login page.
  22 + case 401:
  23 + errMessage = '';
  24 + userStore.logout()
  25 + break;
  26 + case 403:
  27 + errMessage = '未授权';
  28 + break;
  29 + // 404请求不存在
  30 + case 404:
  31 + errMessage = '未找到该资源!';
  32 + break;
  33 + case 405:
  34 + errMessage = '网络请求错误,请求方法未允许!';
  35 + break;
  36 + case 408:
  37 + errMessage = '网络请求超时!';
  38 + break;
  39 + case 500:
  40 + errMessage = '服务器错误,请联系管理员!';
  41 + break;
  42 + case 501:
  43 + errMessage = '网络未实现!';
  44 + break;
  45 + case 502:
  46 + errMessage = '网络错误!';
  47 + break;
  48 + case 503:
  49 + errMessage = '服务不可用,服务器暂时过载或维护!';
  50 + break;
  51 + case 504:
  52 + errMessage = '网络超时!';
  53 + break;
  54 + case 505:
  55 + errMessage = 'http版本不支持该请求!';
  56 + break;
  57 + default:
  58 + }
  59 + if (errMessage) {
  60 + if (errorMessageMode === 'message') {
  61 + error(errMessage);
  62 + }
  63 + }
  64 +}
... ...
  1 +import { isObject, isString } from '@/utils/external/is';
  2 +
  3 +const DATE_TIME_FORMAT = 'YYYY-MM-DD HH:mm';
  4 +
  5 +export function joinTimestamp<T extends boolean>(
  6 + join: boolean,
  7 + restful: T
  8 +): T extends true ? string : object;
  9 +
  10 +export function joinTimestamp(join: boolean, restful = false): string | object {
  11 + if (!join) {
  12 + return restful ? '' : {};
  13 + }
  14 + const now = new Date().getTime();
  15 + if (restful) {
  16 + return `?_t=${now}`;
  17 + }
  18 + return { _t: now };
  19 +}
  20 +
  21 +/**
  22 + * @description: Format request parameter time
  23 + */
  24 +export function formatRequestDate(params: Recordable) {
  25 + if (Object.prototype.toString.call(params) !== '[object Object]') {
  26 + return;
  27 + }
  28 +
  29 + for (const key in params) {
  30 + if (params[key] && params[key]._isAMomentObject) {
  31 + params[key] = params[key].format(DATE_TIME_FORMAT);
  32 + }
  33 + if (isString(key)) {
  34 + const value = params[key];
  35 + if (value) {
  36 + try {
  37 + params[key] = isString(value) ? value.trim() : value;
  38 + } catch (error) {
  39 + throw new Error(error as string);
  40 + }
  41 + }
  42 + }
  43 + if (isObject(params[key])) {
  44 + formatRequestDate(params[key]);
  45 + }
  46 + }
  47 +}
... ...
  1 +// axios配置 可自行根据项目进行更改,只需更改该文件即可,其他文件可以不动
  2 +// The axios configuration can be changed according to the project, just change the file, other files can be left unchanged
  3 +
  4 +import type { AxiosResponse } from 'axios';
  5 +import type { RequestOptions, Result } from '/#/external/axios';
  6 +import type { AxiosTransform, CreateAxiosOptions } from './axiosTransform';
  7 +import { VAxios } from './Axios';
  8 +import { checkStatus } from './checkStatus';
  9 +import { useGlobSetting } from '@/hooks/external/setting';
  10 +import { RequestEnum, ContentTypeEnum } from '@/enums/external/httpEnum';
  11 +import { isString } from '@/utils/external/is';
  12 +import { getJwtToken } from '@/utils/external/auth';
  13 +import { setObjToUrlParams, deepMerge } from '@/utils/external';
  14 +import { joinTimestamp, formatRequestDate } from './helper';
  15 +import { PageEnum } from '@/enums/external/pageEnum';
  16 +import router from '@/router';
  17 +import { useDialog } from 'naive-ui';
  18 +
  19 +const globSetting = useGlobSetting();
  20 +const urlPrefix = globSetting.urlPrefix;
  21 +
  22 +const { success, error } = useDialog()
  23 +/**
  24 + * @description: 数据处理,方便区分多种处理方式
  25 + */
  26 +const transform: AxiosTransform = {
  27 + /**
  28 + * @description: 处理请求数据。如果数据不是预期格式,可直接抛出错误
  29 + */
  30 + transformRequestHook: (res: AxiosResponse<Result>, options: RequestOptions) => {
  31 + const { isReturnNativeResponse } = options;
  32 + // 是否返回原生响应头 比如:需要获取响应头时使用该属性
  33 + if (isReturnNativeResponse) {
  34 + return res;
  35 + }
  36 + return res.data;
  37 + },
  38 +
  39 + // 请求之前处理config
  40 + beforeRequestHook: (config, options) => {
  41 + const { apiUrl, joinPrefix, joinParamsToUrl, formatDate, joinTime = true } = options;
  42 +
  43 + if (joinPrefix) {
  44 + config.url = `${urlPrefix}${config.url}`;
  45 + }
  46 +
  47 + if (apiUrl && isString(apiUrl)) {
  48 + config.url = `${apiUrl}${config.url}`;
  49 + }
  50 + const params = config.params || {};
  51 + const data = config.data || false;
  52 + formatDate && data && !isString(data) && formatRequestDate(data);
  53 + if (config.method?.toUpperCase() === RequestEnum.GET) {
  54 + if (!isString(params)) {
  55 + // 给 get 请求加上时间戳参数,避免从缓存中拿数据。
  56 + config.params = Object.assign(params || {}, joinTimestamp(joinTime, false));
  57 + } else {
  58 + // 兼容restful风格
  59 + config.url = config.url + params + `${joinTimestamp(joinTime, true)}`;
  60 + config.params = undefined;
  61 + }
  62 + } else {
  63 + if (!isString(params)) {
  64 + formatDate && formatRequestDate(params);
  65 + if (Reflect.has(config, 'data') && config.data && Object.keys(config.data).length > 0) {
  66 + config.data = data;
  67 + config.params = params;
  68 + } else {
  69 + // 非GET请求如果没有提供data,则将params视为data
  70 + config.data = params;
  71 + config.params = undefined;
  72 + }
  73 + if (joinParamsToUrl) {
  74 + config.url = setObjToUrlParams(
  75 + config.url as string,
  76 + Object.assign({}, config.params, config.data)
  77 + );
  78 + }
  79 + } else {
  80 + // 兼容restful风格
  81 + config.url = config.url + params;
  82 + config.params = undefined;
  83 + }
  84 + }
  85 + return config;
  86 + },
  87 +
  88 + /**
  89 + * @description: 请求拦截器处理
  90 + */
  91 + requestInterceptors: (config, options) => {
  92 + // 请求之前处理config
  93 + const token = getJwtToken();
  94 + if (token && (config as Recordable)?.requestOptions?.withToken !== false) {
  95 + // jwt token
  96 + config.headers!['X-Authorization'] = options.authenticationScheme
  97 + ? `${options.authenticationScheme} ${token}`
  98 + : token as string;
  99 + }
  100 + return config;
  101 + },
  102 +
  103 + /**
  104 + * @description: 响应拦截器处理
  105 + */
  106 + responseInterceptors: (res: AxiosResponse<any>) => {
  107 + return res;
  108 + },
  109 +
  110 + /**
  111 + * @description: 响应错误处理
  112 + */
  113 + responseInterceptorsCatch: (error: any) => {
  114 +
  115 + const { response, code, message, config } = error || {};
  116 +
  117 + const errorMessageMode = config?.requestOptions?.errorMessageMode || 'none';
  118 + const errorMsgIsObj = typeof response?.data === 'object';
  119 + const msg: string = errorMsgIsObj
  120 + ? response?.data?.message || response?.data?.msg
  121 + : response?.data;
  122 + const err: string = error?.toString?.() ?? '';
  123 + let errMessage = '';
  124 + try {
  125 + if (response?.data?.status == '401' || response?.data?.message == '"Authentication failed"') {
  126 + window.localStorage.clear();
  127 + window.sessionStorage.clear();
  128 + router.push(PageEnum.BASE_HOME);
  129 + }
  130 + if (code === 'ECONNABORTED' && message.indexOf('timeout') !== -1) {
  131 + errMessage = '接口请求超时,请刷新页面重试!'
  132 + }
  133 + if (err?.includes('Network Error')) {
  134 + errMessage = '网络异常,请检查您的网络连接时候正常!'
  135 + }
  136 +
  137 + if (errMessage) {
  138 + if (errorMessageMode === 'modal') {
  139 + error({ title: '错误提示', content: errMessage })
  140 + } else if (errorMessageMode === 'message') {
  141 + error({ title: '错误提示', content: errMessage })
  142 + }
  143 + return Promise.reject(error);
  144 + }
  145 + } catch (error: any) {
  146 + throw new Error(error);
  147 + }
  148 + checkStatus(error?.response?.status, msg, errorMessageMode);
  149 + return Promise.reject(response.data);
  150 + },
  151 +};
  152 +
  153 +function createAxios(opt?: Partial<CreateAxiosOptions>) {
  154 + return new VAxios(
  155 + deepMerge(
  156 + {
  157 + // See https://developer.mozilla.org/en-US/docs/Web/HTTP/Authentication#authentication_schemes
  158 + // authentication schemes,e.g: Bearer
  159 + // authenticationScheme: 'Bearer',
  160 + authenticationScheme: 'Bearer',
  161 + timeout: 10 * 1000,
  162 + // 基础接口地址
  163 + // baseURL: globSetting.apiUrl,
  164 + // 接口可能会有通用的地址部分,可以统一抽取出来
  165 + urlPrefix: urlPrefix,
  166 + headers: { 'Content-Type': ContentTypeEnum.JSON },
  167 + // 如果是form-data格式
  168 + // headers: { 'Content-Type': ContentTypeEnum.FORM_URLENCODED },
  169 + // 数据处理方式
  170 + transform,
  171 + // 配置项,下面的选项都可以在独立的接口请求中覆盖
  172 + requestOptions: {
  173 + // 默认将prefix 添加到url
  174 + joinPrefix: true,
  175 + // 是否返回原生响应头 比如:需要获取响应头时使用该属性
  176 + isReturnNativeResponse: false,
  177 + // 需要对返回数据进行处理
  178 + isTransformResponse: true,
  179 + // post请求的时候添加参数到url
  180 + joinParamsToUrl: false,
  181 + // 格式化提交参数时间
  182 + formatDate: true,
  183 + // 消息提示类型
  184 + errorMessageMode: 'message',
  185 + // 接口地址
  186 + apiUrl: globSetting.apiUrl,
  187 + // 是否加入时间戳
  188 + joinTime: true,
  189 + // 忽略重复请求
  190 + ignoreCancelToken: true,
  191 + // 是否携带token
  192 + withToken: true,
  193 + },
  194 + },
  195 + opt || {}
  196 + )
  197 + );
  198 +}
  199 +export const defHttp = createAxios();
  200 +
  201 +// other api url
  202 +export const otherHttp = createAxios({
  203 + requestOptions: {
  204 + apiUrl: 'xxx',
  205 + },
  206 +});
... ...
  1 +import type { RouteLocationNormalized, RouteRecordNormalized } from 'vue-router';
  2 +import type { App, Component, Plugin } from 'vue';
  3 +
  4 +import { unref } from 'vue';
  5 +import { isObject } from '@/utils/external/is';
  6 +
  7 +export const noop = () => { };
  8 +
  9 +/**
  10 + * @description: Set ui mount node
  11 + */
  12 +export function getPopupContainer(node?: HTMLElement): HTMLElement {
  13 + return (node?.parentNode as HTMLElement) ?? document.body;
  14 +}
  15 +
  16 +/**
  17 + * Add the object as a parameter to the URL
  18 + * @param baseUrl url
  19 + * @param obj
  20 + * @returns {string}
  21 + * eg:
  22 + * let obj = {a: '3', b: '4'}
  23 + * setObjToUrlParams('www.baidu.com', obj)
  24 + * ==>www.baidu.com?a=3&b=4
  25 + */
  26 +export function setObjToUrlParams(baseUrl: string, obj: any): string {
  27 + let parameters = '';
  28 + for (const key in obj) {
  29 + parameters += key + '=' + encodeURIComponent(obj[key]) + '&';
  30 + }
  31 + parameters = parameters.replace(/&$/, '');
  32 + return /\?$/.test(baseUrl) ? baseUrl + parameters : baseUrl.replace(/\/?$/, '?') + parameters;
  33 +}
  34 +
  35 +export function deepMerge<T = any>(src: any = {}, target: any = {}): T {
  36 + let key: string;
  37 + for (key in target) {
  38 + src[key] = isObject(src[key]) ? deepMerge(src[key], target[key]) : (src[key] = target[key]);
  39 + }
  40 + return src;
  41 +}
  42 +
  43 +export function openWindow(
  44 + url: string,
  45 + opt?: { target?: TargetContext | string; noopener?: boolean; noreferrer?: boolean }
  46 +) {
  47 + const { target = '__blank', noopener = true, noreferrer = true } = opt || {};
  48 + const feature: string[] = [];
  49 +
  50 + noopener && feature.push('noopener=yes');
  51 + noreferrer && feature.push('noreferrer=yes');
  52 +
  53 + window.open(url, target, feature.join(','));
  54 +}
  55 +
  56 +// dynamic use hook props
  57 +export function getDynamicProps<T extends Recordable, U>(props: T): Partial<U> {
  58 + const ret: Recordable = {};
  59 +
  60 + Object.keys(props).map((key) => {
  61 + ret[key] = unref((props as Recordable)[key]);
  62 + });
  63 +
  64 + return ret as Partial<U>;
  65 +}
  66 +
  67 +export function getRawRoute(route: RouteLocationNormalized): RouteLocationNormalized {
  68 + if (!route) return route;
  69 + const { matched, ...opt } = route;
  70 + return {
  71 + ...opt,
  72 + matched: (matched
  73 + ? matched.map((item) => ({
  74 + meta: item.meta,
  75 + name: item.name,
  76 + path: item.path,
  77 + }))
  78 + : undefined) as RouteRecordNormalized[],
  79 + };
  80 +}
  81 +
  82 +export const withInstall = <T extends Component>(component: T, alias?: string) => {
  83 + const comp = component as any;
  84 + comp.install = (app: App) => {
  85 + app.component(comp.name || comp.displayName, component);
  86 + if (alias) {
  87 + app.config.globalProperties[alias] = component;
  88 + }
  89 + };
  90 + return component as T & Plugin;
  91 +};
... ...
  1 +const toString = Object.prototype.toString;
  2 +
  3 +export function is(val: unknown, type: string) {
  4 + return toString.call(val) === `[object ${type}]`;
  5 +}
  6 +
  7 +export function isDef<T = unknown>(val?: T): val is T {
  8 + return typeof val !== 'undefined';
  9 +}
  10 +
  11 +export function isUnDef<T = unknown>(val?: T): val is T {
  12 + return !isDef(val);
  13 +}
  14 +
  15 +export function isObject(val: any): val is Record<any, any> {
  16 + return val !== null && is(val, 'Object');
  17 +}
  18 +
  19 +export function isEmpty<T = unknown>(val: T): val is T {
  20 + if (isArray(val) || isString(val)) {
  21 + return val.length === 0;
  22 + }
  23 +
  24 + if (val instanceof Map || val instanceof Set) {
  25 + return val.size === 0;
  26 + }
  27 +
  28 + if (isObject(val)) {
  29 + return Object.keys(val).length === 0;
  30 + }
  31 +
  32 + return false;
  33 +}
  34 +
  35 +export function isDate(val: unknown): val is Date {
  36 + return is(val, 'Date');
  37 +}
  38 +
  39 +export function isNull(val: unknown): val is null {
  40 + return val === null;
  41 +}
  42 +
  43 +export function isNullAndUnDef(val: unknown): val is null | undefined {
  44 + return isUnDef(val) && isNull(val);
  45 +}
  46 +
  47 +export function isNullOrUnDef(val: unknown): val is null | undefined {
  48 + return isUnDef(val) || isNull(val);
  49 +}
  50 +
  51 +export function isNumber(val: unknown): val is number {
  52 + return is(val, 'Number');
  53 +}
  54 +
  55 +export function isPromise<T = any>(val: unknown): val is Promise<T> {
  56 + return is(val, 'Promise') && isObject(val) && isFunction(val.then) && isFunction(val.catch);
  57 +}
  58 +
  59 +export function isString(val: unknown): val is string {
  60 + return is(val, 'String');
  61 +}
  62 +
  63 +export function isFunction(val: unknown): val is Function {
  64 + return typeof val === 'function';
  65 +}
  66 +
  67 +export function isBoolean(val: unknown): val is boolean {
  68 + return is(val, 'Boolean');
  69 +}
  70 +
  71 +export function isRegExp(val: unknown): val is RegExp {
  72 + return is(val, 'RegExp');
  73 +}
  74 +
  75 +export function isArray(val: any): val is Array<any> {
  76 + return val && Array.isArray(val);
  77 +}
  78 +
  79 +export function isWindow(val: any): val is Window {
  80 + return typeof window !== 'undefined' && is(val, 'Window');
  81 +}
  82 +
  83 +export function isElement(val: unknown): val is Element {
  84 + return isObject(val) && !!val.tagName;
  85 +}
  86 +
  87 +export function isMap(val: unknown): val is Map<any, any> {
  88 + return is(val, 'Map');
  89 +}
  90 +
  91 +export const isServer = typeof window === 'undefined';
  92 +
  93 +export const isClient = !isServer;
  94 +
  95 +export function isUrl(path: string): boolean {
  96 + const reg =
  97 + /(((^https?:(?:\/\/)?)(?:[-;:&=\+\$,\w]+@)?[A-Za-z0-9.-]+(?::\d+)?|(?:www.|[-;:&=\+\$,\w]+@)[A-Za-z0-9.-]+)((?:\/[\+~%\/.\w-_]*)?\??(?:[-\+=&;%@.\w_]*)#?(?:[\w]*))?)$/;
  98 + return reg.test(path);
  99 +}
... ...
... ... @@ -20,6 +20,6 @@
20 20 // "strictNullChecks": true, //不允许使用null
21 21 "noImplicitThis": true //不允许往this上面挂属性
22 22 },
23   - "include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue", "types/**/*"],
  23 + "include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue", "types/**/*", "build/**/*"],
24 24 "exclude": ["node_modules", "dist", "**/*.js"]
25 25 }
... ...
  1 +export type ErrorMessageMode = 'none' | 'modal' | 'message' | undefined;
  2 +
  3 +export interface RequestOptions {
  4 + // Splicing request parameters to url
  5 + joinParamsToUrl?: boolean;
  6 + // Format request parameter time
  7 + formatDate?: boolean;
  8 + // Whether to process the request result
  9 + isTransformResponse?: boolean;
  10 + // Whether to return native response headers
  11 + // For example: use this attribute when you need to get the response headers
  12 + isReturnNativeResponse?: boolean;
  13 + // Whether to join url
  14 + joinPrefix?: boolean;
  15 + // Interface address, use the default apiUrl if you leave it blank
  16 + apiUrl?: string;
  17 + // Error message prompt type
  18 + errorMessageMode?: ErrorMessageMode;
  19 + // Whether to add a timestamp
  20 + joinTime?: boolean;
  21 + ignoreCancelToken?: boolean;
  22 + // Whether to send token in header
  23 + withToken?: boolean;
  24 +}
  25 +
  26 +export interface Result<T = any> {
  27 + code: number;
  28 + type: 'success' | 'error' | 'warning';
  29 + message: string;
  30 + result: T;
  31 +}
  32 +
  33 +// multipart/form-data: upload file
  34 +export interface UploadFileParams {
  35 + // Other parameters
  36 + data?: Recordable;
  37 + // File parameter interface field name
  38 + name?: string;
  39 + // file name
  40 + file: File | Blob;
  41 + // file name
  42 + filename?: string;
  43 + [key: string]: any;
  44 +}
  45 +
  46 +export interface PaginationResult<T = Recordable> {
  47 + items: T[];
  48 + total: number;
  49 +}
... ...
  1 +import { MenuTypeEnum, MenuModeEnum, TriggerEnum, MixSidebarTriggerEnum } from '/@/enums/menuEnum';
  2 +import {
  3 + ContentEnum,
  4 + PermissionModeEnum,
  5 + ThemeEnum,
  6 + RouterTransitionEnum,
  7 + SettingButtonPositionEnum,
  8 + SessionTimeoutProcessingEnum,
  9 +} from '/@/enums/appEnum';
  10 +
  11 +import { CacheTypeEnum } from '/@/enums/cacheEnum';
  12 +
  13 +export type LocaleType = 'zh_CN' | 'en' | 'ru' | 'ja' | 'ko';
  14 +
  15 +export interface MenuSetting {
  16 + bgColor: string;
  17 + fixed: boolean;
  18 + collapsed: boolean;
  19 + canDrag: boolean;
  20 + show: boolean;
  21 + hidden: boolean;
  22 + split: boolean;
  23 + menuWidth: number;
  24 + mode: MenuModeEnum;
  25 + type: MenuTypeEnum;
  26 + theme: ThemeEnum;
  27 + topMenuAlign: 'start' | 'center' | 'end';
  28 + trigger: TriggerEnum;
  29 + accordion: boolean;
  30 + closeMixSidebarOnChange: boolean;
  31 + collapsedShowTitle: boolean;
  32 + mixSideTrigger: MixSidebarTriggerEnum;
  33 + mixSideFixed: boolean;
  34 +}
  35 +
  36 +export interface MultiTabsSetting {
  37 + cache: boolean;
  38 + show: boolean;
  39 + showQuick: boolean;
  40 + canDrag: boolean;
  41 + showRedo: boolean;
  42 + showFold: boolean;
  43 +}
  44 +
  45 +export interface HeaderSetting {
  46 + bgColor: string;
  47 + fixed: boolean;
  48 + show: boolean;
  49 + theme: ThemeEnum;
  50 + // Turn on full screen
  51 + showFullScreen: boolean;
  52 + // Whether to show the lock screen
  53 + useLockPage: boolean;
  54 + // Show document button
  55 + showDoc: boolean;
  56 + // Show message center button
  57 + showNotice: boolean;
  58 + showSearch: boolean;
  59 +}
  60 +
  61 +export interface LocaleSetting {
  62 + showPicker: boolean;
  63 + // Current language
  64 + locale: LocaleType;
  65 + // default language
  66 + fallback: LocaleType;
  67 + // available Locales
  68 + availableLocales: LocaleType[];
  69 +}
  70 +
  71 +export interface TransitionSetting {
  72 + // Whether to open the page switching animation
  73 + enable: boolean;
  74 + // Route basic switching animation
  75 + basicTransition: RouterTransitionEnum;
  76 + // Whether to open page switching loading
  77 + openPageLoading: boolean;
  78 + // Whether to open the top progress bar
  79 + openNProgress: boolean;
  80 +}
  81 +
  82 +export interface ProjectConfig {
  83 + // Storage location of permission related information
  84 + permissionCacheType: CacheTypeEnum;
  85 + // Whether to show the configuration button
  86 + showSettingButton: boolean;
  87 + // Whether to show the theme switch button
  88 + showDarkModeToggle: boolean;
  89 + // Configure where the button is displayed
  90 + settingButtonPosition: SettingButtonPositionEnum;
  91 + // Permission mode
  92 + permissionMode: PermissionModeEnum;
  93 + // Session timeout processing
  94 + sessionTimeoutProcessing: SessionTimeoutProcessingEnum;
  95 + // Website gray mode, open for possible mourning dates
  96 + grayMode: boolean;
  97 + // Whether to turn on the color weak mode
  98 + colorWeak: boolean;
  99 + // Theme color
  100 + themeColor: string;
  101 +
  102 + // The main interface is displayed in full screen, the menu is not displayed, and the top
  103 + fullContent: boolean;
  104 + // content width
  105 + contentMode: ContentEnum;
  106 + // Whether to display the logo
  107 + showLogo: boolean;
  108 + // Whether to show the global footer
  109 + showFooter: boolean;
  110 + // menuType: MenuTypeEnum;
  111 + headerSetting: HeaderSetting;
  112 + // menuSetting
  113 + menuSetting: MenuSetting;
  114 + // Multi-tab settings
  115 + multiTabsSetting: MultiTabsSetting;
  116 + // Animation configuration
  117 + transitionSetting: TransitionSetting;
  118 + // pageLayout whether to enable keep-alive
  119 + openKeepAlive: boolean;
  120 + // Lock screen time
  121 + lockTime: number;
  122 + // Show breadcrumbs
  123 + showBreadCrumb: boolean;
  124 + // Show breadcrumb icon
  125 + showBreadCrumbIcon: boolean;
  126 + // Use error-handler-plugin
  127 + useErrorHandle: boolean;
  128 + // Whether to open back to top
  129 + useOpenBackTop: boolean;
  130 + // Is it possible to embed iframe pages
  131 + canEmbedIFramePage: boolean;
  132 + // Whether to delete unclosed messages and notify when switching the interface
  133 + closeMessageOnSwitch: boolean;
  134 + // Whether to cancel the http request that has been sent but not responded when switching the interface.
  135 + removeAllHttpPending: boolean;
  136 +}
  137 +
  138 +export interface GlobConfig {
  139 + // Site title
  140 + title: string;
  141 + // Service interface url
  142 + apiUrl: string;
  143 + // Upload url
  144 + uploadUrl?: string;
  145 + // Service interface url prefix
  146 + urlPrefix?: string;
  147 + // Project abbreviation
  148 + shortName: string;
  149 + // configuration center proxy prefix
  150 + configurationPrefix: string;
  151 + // socket url
  152 + socketUrl: string;
  153 + // alarm notify alarm duration
  154 + alarmNotifyDuration: string;
  155 + // alarm notify polling interval
  156 + alarmPollingInterval: string;
  157 + // upgrade your http policy to https
  158 + securityPolicy: string;
  159 +}
  160 +export interface GlobEnvConfig {
  161 + // Site title
  162 + VITE_GLOB_APP_TITLE: string;
  163 + // Service interface url
  164 + VITE_GLOB_API_URL: string;
  165 + // Service interface url prefix
  166 + VITE_GLOB_API_URL_PREFIX?: string;
  167 + // Project abbreviation
  168 + VITE_GLOB_APP_SHORT_NAME: string;
  169 + // Upload url
  170 + VITE_GLOB_UPLOAD_URL?: string;
  171 + // configuration
  172 + VITE_GLOB_CONFIGURATION: string;
  173 + // socket
  174 + VITE_GLOB_WEB_SOCKET: string;
  175 + // force transform http to https
  176 + VITE_GLOB_CONTENT_SECURITY_POLICY: string;
  177 + // notify polling interval time
  178 + VITE_GLOB_ALARM_NOTIFY_POLLING_INTERVAL_TIME: string;
  179 + // notify duration
  180 + VITE_GLOB_ALARM_NOTIFY_DURATION: string;
  181 +}
... ...
  1 +import type {
  2 + ComponentRenderProxy,
  3 + VNode,
  4 + VNodeChild,
  5 + ComponentPublicInstance,
  6 + FunctionalComponent,
  7 + PropType as VuePropType,
  8 +} from 'vue';
  9 +
  10 +declare global {
  11 + const __APP_INFO__: {
  12 + pkg: {
  13 + name: string;
  14 + version: string;
  15 + dependencies: Recordable<string>;
  16 + devDependencies: Recordable<string>;
  17 + };
  18 + lastBuildTime: string;
  19 + };
  20 + // declare interface Window {
  21 + // // Global vue app instance
  22 + // __APP__: App<Element>;
  23 + // }
  24 +
  25 + // vue
  26 + declare type PropType<T> = VuePropType<T>;
  27 + declare type VueNode = VNodeChild | JSX.Element;
  28 +
  29 + export type Writable<T> = {
  30 + -readonly [P in keyof T]: T[P];
  31 + };
  32 +
  33 + declare type Nullable<T> = T | null;
  34 + declare type NonNullable<T> = T extends null | undefined ? never : T;
  35 + declare type Recordable<T = any> = Record<string, T>;
  36 + declare type ReadonlyRecordable<T = any> = {
  37 + readonly [key: string]: T;
  38 + };
  39 + declare type Indexable<T = any> = {
  40 + [key: string]: T;
  41 + };
  42 + declare type DeepPartial<T> = {
  43 + [P in keyof T]?: DeepPartial<T[P]>;
  44 + };
  45 + declare type TimeoutHandle = ReturnType<typeof setTimeout>;
  46 + declare type IntervalHandle = ReturnType<typeof setInterval>;
  47 +
  48 + declare interface ChangeEvent extends Event {
  49 + target: HTMLInputElement;
  50 + }
  51 +
  52 + declare interface WheelEvent {
  53 + path?: EventTarget[];
  54 + }
  55 + interface ImportMetaEnv extends ViteEnv {
  56 + __: unknown;
  57 + }
  58 +
  59 + declare interface ViteEnv {
  60 + VITE_PORT: number;
  61 + VITE_GLOB_USE_MOCK: boolean;
  62 + VITE_USE_PWA: boolean;
  63 + VITE_GLOB_PUBLIC_PATH: string;
  64 + VITE_PROXY: [string, string][];
  65 + VITE_GLOB_APP_TITLE: string;
  66 + VITE_GLOB_APP_SHORT_NAME: string;
  67 + VITE_USE_CDN: boolean;
  68 + VITE_GLOB_DROP_CONSOLE: boolean;
  69 + VITE_GLOB_BUILD_COMPRESS: 'gzip' | 'brotli' | 'none';
  70 + VITE_GLOB_BUILD_COMPRESS_DELETE_ORIGIN_FILE: boolean;
  71 + VITE_LEGACY: boolean;
  72 + VITE_GLOB_USE_IMAGEMIN: boolean;
  73 + VITE_GENERATE_UI: string;
  74 + VITE_GLOB_CONTENT_SECURITY_POLICY: boolean;
  75 + VITE_GLOB_ALARM_NOTIFY_POLLING_INTERVAL_TIME: number;
  76 + VITE_GLOB_ALARM_NOTIFY_DURATION: number;
  77 + }
  78 +
  79 + declare function parseInt(s: string | number, radix?: number): number;
  80 +
  81 + declare function parseFloat(string: string | number): number;
  82 +
  83 + namespace JSX {
  84 + // tslint:disable no-empty-interface
  85 + type Element = VNode;
  86 + // tslint:disable no-empty-interface
  87 + type ElementClass = ComponentRenderProxy;
  88 + interface ElementAttributesProperty {
  89 + $props: any;
  90 + }
  91 + interface IntrinsicElements {
  92 + [elem: string]: any;
  93 + }
  94 + interface IntrinsicAttributes {
  95 + [elem: string]: any;
  96 + }
  97 + }
  98 +}
  99 +
  100 +declare module 'vue' {
  101 + export type JSXComponent<Props = any> =
  102 + | { new (): ComponentPublicInstance<Props> }
  103 + | FunctionalComponent<Props>;
  104 +}
... ...
  1 +declare interface Fn<T = any, R = T> {
  2 + (...arg: T[]): R;
  3 +}
  4 +
  5 +declare interface PromiseFn<T = any, R = T> {
  6 + (...arg: T[]): Promise<R>;
  7 +}
  8 +
  9 +declare type RefType<T> = T | null;
  10 +
  11 +declare type LabelValueOptions = {
  12 + label: string;
  13 + value: any;
  14 + [key: string]: string | number | boolean;
  15 +}[];
  16 +
  17 +declare type EmitType = (event: string, ...args: any[]) => void;
  18 +
  19 +declare type TargetContext = '_self' | '_blank';
  20 +
  21 +declare interface ComponentElRef<T extends HTMLElement = HTMLDivElement> {
  22 + $el: T;
  23 +}
  24 +
  25 +declare type ComponentRef<T extends HTMLElement = HTMLDivElement> = ComponentElRef<T> | null;
  26 +
  27 +declare type ElRef<T extends HTMLElement = HTMLDivElement> = Nullable<T>;
... ...
  1 +import { ErrorTypeEnum } from '/@/enums/exceptionEnum';
  2 +import { MenuModeEnum, MenuTypeEnum } from '/@/enums/menuEnum';
  3 +import { PlainRoleInfo } from '/@/api/sys/model/userModel';
  4 +
  5 +// Lock screen information
  6 +export interface LockInfo {
  7 + // Password required
  8 + pwd?: string | undefined;
  9 + // Is it locked?
  10 + isLock?: boolean;
  11 +}
  12 +
  13 +// Error-log information
  14 +export interface ErrorLogInfo {
  15 + // Type of error
  16 + type: ErrorTypeEnum;
  17 + // Error file
  18 + file: string;
  19 + // Error name
  20 + name?: string;
  21 + // Error message
  22 + message: string;
  23 + // Error stack
  24 + stack?: string;
  25 + // Error detail
  26 + detail: string;
  27 + // Error url
  28 + url: string;
  29 + // Error time
  30 + time?: string;
  31 +}
  32 +
  33 +export interface UserInfo {
  34 + userId?: string | number;
  35 + username?: string;
  36 + realName?: string;
  37 + avatar?: string;
  38 + homePath?: string;
  39 + tenantCode?: string;
  40 + tenantName?: string;
  41 + roles?: string[];
  42 + plainRoles?: PlainRoleInfo[];
  43 + phoneNumber?: string;
  44 + email?: string;
  45 +}
  46 +
  47 +export interface BeforeMiniState {
  48 + menuCollapsed?: boolean;
  49 + menuSplit?: boolean;
  50 + menuMode?: MenuModeEnum;
  51 + menuType?: MenuTypeEnum;
  52 +}
  53 +
  54 +export interface UserUpdateInfo {
  55 + activateToken?: string;
  56 + avatar?: string;
  57 + createTime?: string;
  58 + creator?: string;
  59 + email?: string;
  60 + enabled?: true;
  61 + hasPassword?: false;
  62 + id?: string;
  63 + level?: 2;
  64 + password?: string;
  65 + phoneNumber?: string;
  66 + realName?: string;
  67 + tenantId?: string;
  68 + updateTime?: string;
  69 + updater?: string;
  70 + username?: string;
  71 +}
... ...
... ... @@ -12,5 +12,3 @@ interface Window {
12 12 // 编辑 JSON 的存储对象
13 13 opener: any
14 14 }
15   -
16   -declare type Recordable<T = any> = Record<string, T>
... ...
1 1 /// <reference types="vite/client" />
2 2
3   -interface ImportMetaEnv {
  3 +
  4 +declare interface GlobEnvConfig {
4 5 // 标题
5 6 VITE_GLOB_APP_TITLE: string;
  7 + // 公共路径
  8 + VITE_GLOB_PUBLIC_PATH: string
  9 + // 代理地址
  10 + VITE_GLOB_PROXY: [string, string][]
  11 + // 内容安全协议
  12 + VITE_GLOB_CONTENT_SECURITY_POLICY: boolean
  13 +
  14 + VITE_GLOB_APP_SHORT_NAME: string
  15 +}
  16 +
  17 +
  18 +declare type GlobConfig = {
  19 +
  20 +}
  21 +
  22 +declare interface ViteEnv extends GlobEnvConfig {
6 23 // 端口
7 24 VITE_DEV_PORT: string;
8 25 // 开发地址
9 26 VITE_DEV_PATH: string
10 27 // 生产地址
11 28 VITE_PRO_PATH: string
12   -}
\ No newline at end of file
  29 +}
  30 +
  31 +interface ImportMetaEnv extends ViteEnv {
  32 +}
  33 +
... ...
1   -import { defineConfig } from 'vite'
  1 +import { defineConfig, loadEnv } from 'vite'
2 2 import vue from '@vitejs/plugin-vue'
3 3 import { resolve } from 'path'
4 4 import { OUTPUT_DIR, brotliSize, chunkSizeWarningLimit, terserOptions, rollupOptions } from './build/constant'
5 5 import viteCompression from 'vite-plugin-compression'
6 6 import { viteMockServe } from 'vite-plugin-mock'
7 7 import monacoEditorPlugin from 'vite-plugin-monaco-editor'
  8 +import { createProxy } from './build/external/vite/proxy'
  9 +import { parseEnv } from './build/external/utils'
  10 +import { GenerateBuildConfig } from './build/external/globConfig'
8 11
9 12 function pathResolve(dir: string) {
10 13 return resolve(process.cwd(), '.', dir)
11 14 }
12 15
13   -export default defineConfig({
14   - base: '/',
15   - // 路径重定向
16   - resolve: {
17   - alias: [
18   - {
19   - find: /\/#\//,
20   - replacement: pathResolve('types')
21   - },
22   - {
23   - find: '@',
24   - replacement: pathResolve('src')
25   - },
26   - {
27   - find: 'vue-i18n',
28   - replacement: 'vue-i18n/dist/vue-i18n.cjs.js', //解决i8n警告
  16 +export default defineConfig(({ mode, command }) => {
  17 +
  18 + const root = process.cwd()
  19 +
  20 + const env = loadEnv(mode, root)
  21 +
  22 + const viteEnv = parseEnv(env)
  23 +
  24 + const isBuild = command === 'build'
  25 +
  26 + return {
  27 + base: '/',
  28 + // 路径重定向
  29 + resolve: {
  30 + alias: [
  31 + {
  32 + find: /\/#\//,
  33 + replacement: pathResolve('types')
  34 + },
  35 + {
  36 + find: '@',
  37 + replacement: pathResolve('src')
  38 + },
  39 + {
  40 + find: 'vue-i18n',
  41 + replacement: 'vue-i18n/dist/vue-i18n.cjs.js', //解决i8n警告
  42 + }
  43 + ],
  44 + dedupe: ['vue']
  45 + },
  46 + // 全局 css 注册
  47 + css: {
  48 + preprocessorOptions: {
  49 + scss: {
  50 + javascriptEnabled: true,
  51 + additionalData: `@import "src/styles/common/style.scss";`
  52 + }
29 53 }
  54 + },
  55 + plugins: [
  56 + vue(),
  57 + monacoEditorPlugin({
  58 + languageWorkers: ['editorWorkerService', 'typescript', 'json', 'html']
  59 + }),
  60 + viteMockServe({
  61 + mockPath: '/src/api/mock',
  62 + // 开发打包开关
  63 + localEnabled: true,
  64 + // 生产打包开关
  65 + prodEnabled: true,
  66 + // 打开后,可以读取 ts 文件模块。 请注意,打开后将无法监视.js 文件
  67 + supportTs: true,
  68 + // 监视文件更改
  69 + watchFiles: true
  70 + }),
  71 + // 压缩
  72 + viteCompression({
  73 + verbose: true,
  74 + disable: false,
  75 + threshold: 10240,
  76 + algorithm: 'gzip',
  77 + ext: '.gz'
  78 + }),
  79 + GenerateBuildConfig(viteEnv)
30 80 ],
31   - dedupe: ['vue']
32   - },
33   - // 全局 css 注册
34   - css: {
35   - preprocessorOptions: {
36   - scss: {
37   - javascriptEnabled: true,
38   - additionalData: `@import "src/styles/common/style.scss";`
39   - }
  81 + build: {
  82 + target: 'es2015',
  83 + outDir: OUTPUT_DIR,
  84 + // minify: 'terser', // 如果需要用terser混淆,可打开这两行
  85 + // terserOptions: terserOptions,
  86 + rollupOptions: rollupOptions,
  87 + brotliSize: brotliSize,
  88 + chunkSizeWarningLimit: chunkSizeWarningLimit
  89 + },
  90 + server: {
  91 + proxy: createProxy(viteEnv)
40 92 }
41   - },
42   - plugins: [
43   - vue(),
44   - monacoEditorPlugin({
45   - languageWorkers: ['editorWorkerService', 'typescript', 'json', 'html']
46   - }),
47   - viteMockServe({
48   - mockPath: '/src/api/mock',
49   - // 开发打包开关
50   - localEnabled: true,
51   - // 生产打包开关
52   - prodEnabled: true,
53   - // 打开后,可以读取 ts 文件模块。 请注意,打开后将无法监视.js 文件
54   - supportTs: true,
55   - // 监视文件更改
56   - watchFiles: true
57   - }),
58   - // 压缩
59   - viteCompression({
60   - verbose: true,
61   - disable: false,
62   - threshold: 10240,
63   - algorithm: 'gzip',
64   - ext: '.gz'
65   - })
66   - ],
67   - build: {
68   - target: 'es2015',
69   - outDir: OUTPUT_DIR,
70   - // minify: 'terser', // 如果需要用terser混淆,可打开这两行
71   - // terserOptions: terserOptions,
72   - rollupOptions: rollupOptions,
73   - brotliSize: brotliSize,
74   - chunkSizeWarningLimit: chunkSizeWarningLimit
75 93 }
76   -})
  94 +}
  95 +)
... ...