Showing
48 changed files
with
2994 additions
and
87 deletions
.env.development
0 → 100644
.env.production
0 → 100644
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]` | ... | ... |
build/external/globConfig/const.ts
0 → 100644
build/external/globConfig/getGlobConfigName.ts
renamed from
build/getConfigFileName.ts
... | ... | @@ -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, ''); | ... | ... |
build/external/globConfig/index.ts
0 → 100644
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 | + | ... | ... |
build/external/globConfig/useGlobSetting.ts
0 → 100644
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 | +}; | ... | ... |
build/external/utils.ts
0 → 100644
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 | +} | ... | ... |
build/external/vite/html.ts
0 → 100644
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 | +} | ... | ... |
build/external/vite/proxy.ts
0 → 100644
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 | ... | ... |
src/api/external/sys/model/jwtModel.ts
0 → 100644
src/api/external/sys/model/userModel.ts
0 → 100644
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 | +} | ... | ... |
src/api/external/sys/user.ts
0 → 100644
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 | +}; | ... | ... |
src/enums/external/cacheEnum.ts
0 → 100644
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 | +} | ... | ... |
src/enums/external/httpEnum.ts
0 → 100644
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 | +} | ... | ... |
src/enums/external/pageEnum.ts
0 → 100644
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 | +}; | ... | ... |
src/enums/external/roleEnum.ts
0 → 100644
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 | +} | ... | ... |
src/hooks/external/setting/index.ts
0 → 100644
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 | +}; | ... | ... |
src/hooks/external/useMessage.ts
0 → 100644
src/settings/external/encryptionSetting.ts
0 → 100644
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(); | ... | ... |
src/settings/external/projectSetting.ts
0 → 100644
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; | ... | ... |
src/store/external/module/user.ts
0 → 100644
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 | +} | ... | ... |
src/utils/external/auth/index.ts
0 → 100644
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 | +} | ... | ... |
src/utils/external/cache/index.ts
0 → 100644
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; | ... | ... |
src/utils/external/cache/memory.ts
0 → 100644
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 | +} | ... | ... |
src/utils/external/cache/persistent.ts
0 → 100644
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(); | ... | ... |
src/utils/external/cache/storageCache.ts
0 → 100644
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 | +}; | ... | ... |
src/utils/external/cipher.ts
0 → 100644
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 | +} | ... | ... |
src/utils/external/env/index.ts
0 → 100644
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 | +} | ... | ... |
src/utils/external/http/axios/Axios.ts
0 → 100644
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 | +} | ... | ... |
src/utils/external/http/axios/axiosCancel.ts
0 → 100644
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 | +} | ... | ... |
src/utils/external/http/axios/checkStatus.ts
0 → 100644
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 | +} | ... | ... |
src/utils/external/http/axios/helper.ts
0 → 100644
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 | +} | ... | ... |
src/utils/external/http/axios/index.ts
0 → 100644
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 | +}); | ... | ... |
src/utils/external/index.ts
0 → 100644
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 | +}; | ... | ... |
src/utils/external/is.ts
0 → 100644
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 | } | ... | ... |
types/external/axios.d.ts
0 → 100644
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 | +} | ... | ... |
types/external/config.d.ts
0 → 100644
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 | +} | ... | ... |
types/external/global.d.ts
0 → 100644
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 | +} | ... | ... |
types/external/index.d.ts
0 → 100644
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>; | ... | ... |
types/external/store.d.ts
0 → 100644
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 | +} | ... | ... |
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 | +) | ... | ... |