Commit 99ac309fa9eed36f8444b8bbab7b18275c5aa5aa
1 parent
1d3007f0
wip: support vite
Showing
100 changed files
with
397 additions
and
1129 deletions
Too many changes to show.
To preserve performance only 100 of 105 files are displayed.
.env.production
build/script/changelog.ts deleted
100644 → 0
1 | -// #!/usr/bin/env node | |
2 | - | |
3 | -import { sh } from 'tasksfile'; | |
4 | -import { errorConsole, successConsole } from '../utils'; | |
5 | - | |
6 | -export const runChangeLog = async () => { | |
7 | - try { | |
8 | - let cmd = `conventional-changelog -p custom-config -i CHANGELOG.md -s -r 0 `; | |
9 | - | |
10 | - await sh(cmd, { | |
11 | - async: true, | |
12 | - nopipe: true, | |
13 | - }); | |
14 | - await sh('prettier --write **/CHANGELOG.md ', { | |
15 | - async: true, | |
16 | - nopipe: true, | |
17 | - }); | |
18 | - successConsole('CHANGE_LOG.md generated successfully!'); | |
19 | - } catch (error) { | |
20 | - errorConsole('CHANGE_LOG.md generated error\n' + error); | |
21 | - | |
22 | - process.exit(1); | |
23 | - } | |
24 | -}; | |
25 | - | |
26 | -runChangeLog(); |
build/script/postBuild.ts
... | ... | @@ -3,7 +3,6 @@ |
3 | 3 | import { argv } from 'yargs'; |
4 | 4 | import { runBuildConfig } from './buildConf'; |
5 | 5 | import { errorConsole, successConsole } from '../utils'; |
6 | -import { startGzipStyle } from '../vite/plugin/gzip/compress'; | |
7 | 6 | |
8 | 7 | export const runBuild = async () => { |
9 | 8 | try { |
... | ... | @@ -13,8 +12,6 @@ export const runBuild = async () => { |
13 | 12 | if (!argvList.includes('no-conf')) { |
14 | 13 | await runBuildConfig(); |
15 | 14 | } |
16 | - // await runUpdateHtml(); | |
17 | - await startGzipStyle(); | |
18 | 15 | successConsole('Vite Build successfully!'); |
19 | 16 | } catch (error) { |
20 | 17 | errorConsole('Vite Build Error\n' + error); | ... | ... |
build/utils.ts
... | ... | @@ -63,11 +63,11 @@ export function getIPAddress() { |
63 | 63 | return ''; |
64 | 64 | } |
65 | 65 | |
66 | -export function isDevFn(mode: 'development' | 'production'): boolean { | |
66 | +export function isDevFn(mode: string): boolean { | |
67 | 67 | return mode === 'development'; |
68 | 68 | } |
69 | 69 | |
70 | -export function isProdFn(mode: 'development' | 'production'): boolean { | |
70 | +export function isProdFn(mode: string): boolean { | |
71 | 71 | return mode === 'production'; |
72 | 72 | } |
73 | 73 | |
... | ... | @@ -85,13 +85,6 @@ export function isBuildGzip(): boolean { |
85 | 85 | return process.env.VITE_BUILD_GZIP === 'true'; |
86 | 86 | } |
87 | 87 | |
88 | -/** | |
89 | - * Whether to generate package site | |
90 | - */ | |
91 | -export function isSiteMode(): boolean { | |
92 | - return process.env.SITE === 'true'; | |
93 | -} | |
94 | - | |
95 | 88 | export interface ViteEnv { |
96 | 89 | VITE_PORT: number; |
97 | 90 | VITE_USE_MOCK: boolean; |
... | ... | @@ -99,10 +92,12 @@ export interface ViteEnv { |
99 | 92 | VITE_PUBLIC_PATH: string; |
100 | 93 | VITE_PROXY: [string, string][]; |
101 | 94 | VITE_GLOB_APP_TITLE: string; |
95 | + VITE_GLOB_APP_SHORT_NAME: string; | |
102 | 96 | VITE_USE_CDN: boolean; |
103 | 97 | VITE_DROP_CONSOLE: boolean; |
104 | 98 | VITE_BUILD_GZIP: boolean; |
105 | 99 | VITE_DYNAMIC_IMPORT: boolean; |
100 | + VITE_LEGACY: boolean; | |
106 | 101 | } |
107 | 102 | |
108 | 103 | // Read all environment variable configuration files to process.env | ... | ... |
build/vite/hm.ts deleted
100644 → 0
1 | -// Baidu statistics code for site deployment | |
2 | -// Only open in build:site | |
3 | -export const hmScript = `<script> | |
4 | -var _hmt = _hmt || []; | |
5 | -(function() { | |
6 | - var hm = document.createElement("script"); | |
7 | - hm.src = "https://hm.baidu.com/hm.js?384d6046e02f6ac4ea075357bd0e9b43"; | |
8 | - var s = document.getElementsByTagName("script")[0]; | |
9 | - s.parentNode.insertBefore(hm, s); | |
10 | -})(); | |
11 | -</script> | |
12 | -`; |
build/vite/plugin/gzip.ts
0 → 100644
1 | +import gzipPlugin from 'rollup-plugin-gzip'; | |
2 | +import { isBuildGzip } from '../../utils'; | |
3 | +import { Plugin } from 'vite'; | |
4 | +export function configGzipPlugin(isBuild: boolean): Plugin | Plugin[] { | |
5 | + const useGzip = isBuild && isBuildGzip; | |
6 | + | |
7 | + if (useGzip) { | |
8 | + return gzipPlugin(); | |
9 | + } | |
10 | + | |
11 | + return []; | |
12 | +} | ... | ... |
build/vite/plugin/gzip/compress.ts deleted
100644 → 0
1 | -import { gzip } from 'zlib'; | |
2 | -import { readFileSync, writeFileSync } from 'fs'; | |
3 | -import { GzipPluginOptions } from './types'; | |
4 | -import { readAllFile, getCwdPath, isBuildGzip, isSiteMode } from '../../../utils'; | |
5 | - | |
6 | -export function startGzip( | |
7 | - fileContent: string | Buffer, | |
8 | - options: GzipPluginOptions = {} | |
9 | -): Promise<Buffer> { | |
10 | - return new Promise((resolve, reject) => { | |
11 | - gzip(fileContent, options.gzipOptions || {}, (err, result) => { | |
12 | - if (err) { | |
13 | - reject(err); | |
14 | - } else { | |
15 | - resolve(result); | |
16 | - } | |
17 | - }); | |
18 | - }); | |
19 | -} | |
20 | - | |
21 | -// 手动压缩css | |
22 | -export async function startGzipStyle() { | |
23 | - if (isBuildGzip() || isSiteMode()) { | |
24 | - const outDir = 'dist'; | |
25 | - const assets = '_assets'; | |
26 | - const allCssFile = readAllFile(getCwdPath(outDir, assets), /\.(css)$/); | |
27 | - for (const path of allCssFile) { | |
28 | - const source = readFileSync(path); | |
29 | - const content = await startGzip(source); | |
30 | - const ds = path.split('/'); | |
31 | - const fileName = ds[ds.length - 1]; | |
32 | - writeFileSync(getCwdPath(outDir, assets, `${fileName}.gz`), content); | |
33 | - } | |
34 | - } | |
35 | -} |
build/vite/plugin/gzip/index.ts deleted
100644 → 0
1 | -// 修改自https://github.com/kryops/rollup-plugin-gzip | |
2 | -// 因为rollup-plugin-gzip不支持vite | |
3 | -// vite对css打包独立的。所以不能在打包的时候顺带打包css | |
4 | -// TODO rc.9会支持 configurBuild 配置项。到时候重新修改 | |
5 | - | |
6 | -import { readFile, writeFile } from 'fs'; | |
7 | -import { basename } from 'path'; | |
8 | -import { promisify } from 'util'; | |
9 | -import { gzip } from 'zlib'; | |
10 | - | |
11 | -import { OutputAsset, OutputChunk, OutputOptions, Plugin } from 'rollup'; | |
12 | -import { GzipPluginOptions } from './types'; | |
13 | - | |
14 | -const isFunction = (arg: unknown): arg is (...args: any[]) => any => typeof arg === 'function'; | |
15 | -const isRegExp = (arg: unknown): arg is RegExp => | |
16 | - Object.prototype.toString.call(arg) === '[object RegExp]'; | |
17 | - | |
18 | -export type StringMappingOption = (originalString: string) => string; | |
19 | -export type CustomCompressionOption = ( | |
20 | - content: string | Buffer | |
21 | -) => string | Buffer | Promise<string | Buffer>; | |
22 | - | |
23 | -const readFilePromise = promisify(readFile); | |
24 | -const writeFilePromise = promisify(writeFile); | |
25 | - | |
26 | -// functionality partially copied from rollup | |
27 | - | |
28 | -/** | |
29 | - * copied from https://github.com/rollup/rollup/blob/master/src/rollup/index.ts#L450 | |
30 | - */ | |
31 | -function isOutputChunk(file: OutputAsset | OutputChunk): file is OutputChunk { | |
32 | - return typeof (file as OutputChunk).code === 'string'; | |
33 | -} | |
34 | - | |
35 | -/** | |
36 | - * Gets the string/buffer content from a file object. | |
37 | - * Important for adding source map comments | |
38 | - * | |
39 | - * Copied partially from rollup.writeOutputFile | |
40 | - * https://github.com/rollup/rollup/blob/master/src/rollup/index.ts#L454 | |
41 | - */ | |
42 | -function getOutputFileContent( | |
43 | - outputFileName: string, | |
44 | - outputFile: OutputAsset | OutputChunk, | |
45 | - outputOptions: OutputOptions | |
46 | -): string | Buffer { | |
47 | - if (isOutputChunk(outputFile)) { | |
48 | - let source: string | Buffer; | |
49 | - source = outputFile.code; | |
50 | - if (outputOptions.sourcemap && outputFile.map) { | |
51 | - const url = | |
52 | - outputOptions.sourcemap === 'inline' | |
53 | - ? outputFile.map.toUrl() | |
54 | - : `${basename(outputFileName)}.map`; | |
55 | - | |
56 | - // https://github.com/rollup/rollup/blob/master/src/utils/sourceMappingURL.ts#L1 | |
57 | - source += `//# source` + `MappingURL=${url}\n`; | |
58 | - } | |
59 | - return source; | |
60 | - } else { | |
61 | - return typeof outputFile.source === 'string' | |
62 | - ? outputFile.source | |
63 | - : // just to be sure, as it is typed string | Uint8Array in rollup 2.0.0 | |
64 | - Buffer.from(outputFile.source); | |
65 | - } | |
66 | -} | |
67 | - | |
68 | -// actual plugin code | |
69 | - | |
70 | -function gzipPlugin(options: GzipPluginOptions = {}): Plugin { | |
71 | - // check for old options | |
72 | - if ('algorithm' in options) { | |
73 | - console.warn( | |
74 | - '[rollup-plugin-gzip] The "algorithm" option is not supported any more! ' + | |
75 | - 'Use "customCompression" instead to specify a different compression algorithm.' | |
76 | - ); | |
77 | - } | |
78 | - if ('options' in options) { | |
79 | - console.warn('[rollup-plugin-gzip] The "options" option was renamed to "gzipOptions"!'); | |
80 | - } | |
81 | - if ('additional' in options) { | |
82 | - console.warn('[rollup-plugin-gzip] The "additional" option was renamed to "additionalFiles"!'); | |
83 | - } | |
84 | - if ('delay' in options) { | |
85 | - console.warn('[rollup-plugin-gzip] The "delay" option was renamed to "additionalFilesDelay"!'); | |
86 | - } | |
87 | - | |
88 | - const compressGzip: CustomCompressionOption = (fileContent) => { | |
89 | - return new Promise((resolve, reject) => { | |
90 | - gzip(fileContent, options.gzipOptions || {}, (err, result) => { | |
91 | - if (err) { | |
92 | - reject(err); | |
93 | - } else { | |
94 | - resolve(result); | |
95 | - } | |
96 | - }); | |
97 | - }); | |
98 | - }; | |
99 | - | |
100 | - const doCompress = options.customCompression || compressGzip; | |
101 | - | |
102 | - const mapFileName: StringMappingOption = isFunction(options.fileName) | |
103 | - ? (options.fileName as StringMappingOption) | |
104 | - : (fileName: string) => fileName + (options.fileName || '.gz'); | |
105 | - | |
106 | - const plugin: Plugin = { | |
107 | - name: 'gzip', | |
108 | - | |
109 | - generateBundle(outputOptions, bundle) { | |
110 | - return Promise.all( | |
111 | - Object.keys(bundle) | |
112 | - .map((fileName) => { | |
113 | - const fileEntry = bundle[fileName]; | |
114 | - | |
115 | - // file name filter option check | |
116 | - | |
117 | - const fileNameFilter = options.filter || /\.(js|mjs|json|css|html)$/; | |
118 | - | |
119 | - if (isRegExp(fileNameFilter) && !fileName.match(fileNameFilter)) { | |
120 | - return Promise.resolve(); | |
121 | - } | |
122 | - | |
123 | - if ( | |
124 | - isFunction(fileNameFilter) && | |
125 | - !(fileNameFilter as (x: string) => boolean)(fileName) | |
126 | - ) { | |
127 | - return Promise.resolve(); | |
128 | - } | |
129 | - | |
130 | - const fileContent = getOutputFileContent(fileName, fileEntry, outputOptions); | |
131 | - | |
132 | - // minSize option check | |
133 | - if (options.minSize && options.minSize > fileContent.length) { | |
134 | - return Promise.resolve(); | |
135 | - } | |
136 | - | |
137 | - return Promise.resolve(doCompress(fileContent)) | |
138 | - .then((compressedContent) => { | |
139 | - const compressedFileName = mapFileName(fileName); | |
140 | - bundle[compressedFileName] = { | |
141 | - type: 'asset', // Rollup >= 1.21 | |
142 | - name: compressedFileName, | |
143 | - fileName: compressedFileName, | |
144 | - isAsset: true, // Rollup < 1.21 | |
145 | - source: compressedContent, | |
146 | - }; | |
147 | - }) | |
148 | - .catch((err: any) => { | |
149 | - console.error(err); | |
150 | - return Promise.reject('[rollup-plugin-gzip] Error compressing file ' + fileName); | |
151 | - }); | |
152 | - }) | |
153 | - .concat([ | |
154 | - (() => { | |
155 | - if (!options.additionalFiles || !options.additionalFiles.length) | |
156 | - return Promise.resolve(); | |
157 | - | |
158 | - const compressAdditionalFiles = () => | |
159 | - Promise.all( | |
160 | - options.additionalFiles!.map((filePath) => | |
161 | - readFilePromise(filePath) | |
162 | - .then((fileContent) => doCompress(fileContent)) | |
163 | - .then((compressedContent) => { | |
164 | - return writeFilePromise(mapFileName(filePath), compressedContent); | |
165 | - }) | |
166 | - .catch(() => { | |
167 | - return Promise.reject( | |
168 | - '[rollup-plugin-gzip] Error compressing additional file ' + | |
169 | - filePath + | |
170 | - '. Please check the spelling of your configured additionalFiles. ' + | |
171 | - 'You might also have to increase the value of the additionalFilesDelay option.' | |
172 | - ); | |
173 | - }) | |
174 | - ) | |
175 | - ) as Promise<any>; | |
176 | - | |
177 | - // additional files can be processed outside of rollup after a delay | |
178 | - // for older plugins or plugins that write to disk (curcumventing rollup) without awaiting | |
179 | - const additionalFilesDelay = options.additionalFilesDelay || 0; | |
180 | - | |
181 | - if (additionalFilesDelay) { | |
182 | - setTimeout(compressAdditionalFiles, additionalFilesDelay); | |
183 | - return Promise.resolve(); | |
184 | - } else { | |
185 | - return compressAdditionalFiles(); | |
186 | - } | |
187 | - })(), | |
188 | - ]) | |
189 | - ) as Promise<any>; | |
190 | - }, | |
191 | - }; | |
192 | - | |
193 | - return plugin; | |
194 | -} | |
195 | - | |
196 | -export default gzipPlugin; |
build/vite/plugin/gzip/types.ts deleted
100644 → 0
1 | -import type { ZlibOptions } from 'zlib'; | |
2 | - | |
3 | -export type StringMappingOption = (originalString: string) => string; | |
4 | -export type CustomCompressionOption = ( | |
5 | - content: string | Buffer | |
6 | -) => string | Buffer | Promise<string | Buffer>; | |
7 | - | |
8 | -export interface GzipPluginOptions { | |
9 | - /** | |
10 | - * Control which of the output files to compress | |
11 | - * | |
12 | - * Defaults to `/\.(js|mjs|json|css|html)$/` | |
13 | - */ | |
14 | - filter?: RegExp | ((fileName: string) => boolean); | |
15 | - | |
16 | - /** | |
17 | - * GZIP compression options, see https://nodejs.org/api/zlib.html#zlib_class_options | |
18 | - */ | |
19 | - gzipOptions?: ZlibOptions; | |
20 | - | |
21 | - /** | |
22 | - * Specified the minimum size in Bytes for a file to get compressed. | |
23 | - * Files that are smaller than this threshold will not be compressed. | |
24 | - * This does not apply to the files specified through `additionalFiles`! | |
25 | - */ | |
26 | - minSize?: number; | |
27 | - | |
28 | - /** | |
29 | - * This option allows you to compress additional files outside of the main rollup bundling process. | |
30 | - * The processing is delayed to make sure the files are written on disk; the delay is controlled | |
31 | - * through `additionalFilesDelay`. | |
32 | - */ | |
33 | - additionalFiles?: string[]; | |
34 | - | |
35 | - /** | |
36 | - * This options sets a delay (ms) before the plugin compresses the files specified through `additionalFiles`. | |
37 | - * Increase this value if your artifacts take a long time to generate. | |
38 | - * | |
39 | - * Defaults to `2000` | |
40 | - */ | |
41 | - additionalFilesDelay?: number; | |
42 | - | |
43 | - /** | |
44 | - * Set a custom compression algorithm. The function can either return the compressed contents synchronously, | |
45 | - * or otherwise return a promise for asynchronous processing. | |
46 | - */ | |
47 | - customCompression?: CustomCompressionOption; | |
48 | - | |
49 | - /** | |
50 | - * Set a custom file name convention for the compressed files. Can be a suffix string or a function | |
51 | - * returning the file name. | |
52 | - * | |
53 | - * Defaults to `.gz` | |
54 | - */ | |
55 | - fileName?: string | StringMappingOption; | |
56 | -} |
build/vite/plugin/html.ts
1 | 1 | import type { Plugin } from 'vite'; |
2 | -import ViteHtmlPlugin from 'vite-plugin-html'; | |
3 | -import { isProdFn, isSiteMode, ViteEnv } from '../../utils'; | |
2 | +import html from 'vite-plugin-html'; | |
3 | +import { ViteEnv } from '../../utils'; | |
4 | 4 | |
5 | -import { hmScript } from '../hm'; | |
6 | 5 | // @ts-ignore |
7 | 6 | import pkg from '../../../package.json'; |
8 | 7 | import { GLOB_CONFIG_FILE_NAME } from '../../constant'; |
9 | 8 | |
10 | -export function setupHtmlPlugin( | |
11 | - plugins: Plugin[], | |
12 | - env: ViteEnv, | |
13 | - mode: 'development' | 'production' | |
14 | -) { | |
9 | +export function configHtmlPlugin(env: ViteEnv, isBuild: boolean) { | |
15 | 10 | const { VITE_GLOB_APP_TITLE, VITE_PUBLIC_PATH } = env; |
16 | - | |
17 | - const htmlPlugin = ViteHtmlPlugin({ | |
18 | - // html title | |
19 | - title: VITE_GLOB_APP_TITLE, | |
20 | - minify: isProdFn(mode), | |
21 | - options: { | |
22 | - publicPath: VITE_PUBLIC_PATH, | |
23 | - // Package and insert additional configuration files | |
24 | - injectConfig: isProdFn(mode) | |
25 | - ? `<script src='${VITE_PUBLIC_PATH || './'}${GLOB_CONFIG_FILE_NAME}?v=${ | |
26 | - pkg.version | |
27 | - }-${new Date().getTime()}'></script>` | |
28 | - : '', | |
29 | - // Insert Baidu statistics code | |
30 | - hmScript: isSiteMode() ? hmScript : '', | |
31 | - title: VITE_GLOB_APP_TITLE, | |
11 | + const htmlPlugin: Plugin[] = html({ | |
12 | + minify: isBuild, | |
13 | + inject: { | |
14 | + injectData: { | |
15 | + title: VITE_GLOB_APP_TITLE, | |
16 | + }, | |
17 | + tags: isBuild | |
18 | + ? [ | |
19 | + { | |
20 | + tag: 'script', | |
21 | + attrs: { | |
22 | + src: `${VITE_PUBLIC_PATH || './'}${GLOB_CONFIG_FILE_NAME}?v=${ | |
23 | + pkg.version | |
24 | + }-${new Date().getTime()}`, | |
25 | + }, | |
26 | + }, | |
27 | + ] | |
28 | + : [], | |
32 | 29 | }, |
33 | 30 | }); |
34 | - | |
35 | - plugins.push(htmlPlugin); | |
36 | - return plugins; | |
31 | + return htmlPlugin; | |
37 | 32 | } | ... | ... |
build/vite/plugin/importContext.ts
0 → 100644
1 | +import dynamicImport from 'vite-plugin-import-context'; | |
2 | +import type { ViteEnv } from '../../utils'; | |
3 | +import type { Plugin } from 'vite'; | |
4 | + | |
5 | +export function configDynamicImport(env: ViteEnv) { | |
6 | + const { VITE_DYNAMIC_IMPORT } = env; | |
7 | + const dynamicImportPlugin: Plugin = dynamicImport({ | |
8 | + include: ['**/*.ts'], | |
9 | + autoImportRoute: VITE_DYNAMIC_IMPORT, | |
10 | + }); | |
11 | + return dynamicImportPlugin; | |
12 | +} | ... | ... |
build/vite/plugin/index.ts
1 | -import type { Plugin as VitePlugin } from 'vite'; | |
2 | -import type { Plugin as rollupPlugin } from 'rollup'; | |
1 | +import type { Plugin } from 'vite'; | |
3 | 2 | |
4 | 3 | import PurgeIcons from 'vite-plugin-purge-icons'; |
5 | 4 | |
6 | 5 | import visualizer from 'rollup-plugin-visualizer'; |
7 | -import gzipPlugin from './gzip/index'; | |
8 | 6 | |
9 | 7 | // @ts-ignore |
10 | 8 | import pkg from '../../../package.json'; |
11 | -import { isSiteMode, ViteEnv, isReportMode, isBuildGzip } from '../../utils'; | |
12 | -import { setupHtmlPlugin } from './html'; | |
13 | -import { setupPwaPlugin } from './pwa'; | |
14 | -import { setupMockPlugin } from './mock'; | |
9 | +import { ViteEnv, isReportMode } from '../../utils'; | |
10 | +import { configHtmlPlugin } from './html'; | |
11 | +import { configPwaConfig } from './pwa'; | |
12 | +import { configMockPlugin } from './mock'; | |
13 | +import { configDynamicImport } from './importContext'; | |
14 | +import { configGzipPlugin } from './gzip'; | |
15 | 15 | |
16 | 16 | // gen vite plugins |
17 | -export function createVitePlugins(viteEnv: ViteEnv, mode: 'development' | 'production') { | |
18 | - const vitePlugins: VitePlugin[] = []; | |
17 | +export function createVitePlugins(viteEnv: ViteEnv, isBuild: boolean, mode: string) { | |
18 | + const vitePlugins: (Plugin | Plugin[])[] = []; | |
19 | 19 | |
20 | 20 | // vite-plugin-html |
21 | - setupHtmlPlugin(vitePlugins, viteEnv, mode); | |
21 | + vitePlugins.push(configHtmlPlugin(viteEnv, isBuild)); | |
22 | + | |
22 | 23 | // vite-plugin-pwa |
23 | - setupPwaPlugin(vitePlugins, viteEnv, mode); | |
24 | + vitePlugins.push(configPwaConfig(viteEnv, isBuild)); | |
25 | + | |
24 | 26 | // vite-plugin-mock |
25 | - setupMockPlugin(vitePlugins, viteEnv, mode); | |
27 | + vitePlugins.push(configMockPlugin(viteEnv, isBuild)); | |
28 | + | |
29 | + // vite-plugin-import-context | |
30 | + vitePlugins.push(configDynamicImport(viteEnv)); | |
26 | 31 | |
27 | 32 | // vite-plugin-purge-icons |
28 | 33 | vitePlugins.push(PurgeIcons()); |
29 | 34 | |
30 | - return vitePlugins; | |
31 | -} | |
32 | - | |
33 | -// gen rollup plugins | |
34 | -export function createRollupPlugin() { | |
35 | - const rollupPlugins: rollupPlugin[] = []; | |
35 | + // rollup-plugin-gzip | |
36 | + vitePlugins.push(configGzipPlugin(isBuild)); | |
36 | 37 | |
38 | + // rollup-plugin-visualizer | |
37 | 39 | if (isReportMode()) { |
38 | - // rollup-plugin-visualizer | |
39 | - rollupPlugins.push(visualizer({ filename: './build/.cache/stats.html', open: true }) as Plugin); | |
40 | - } | |
41 | - if (isBuildGzip() || isSiteMode()) { | |
42 | - // rollup-plugin-gizp | |
43 | - rollupPlugins.push(gzipPlugin()); | |
40 | + vitePlugins.push(visualizer({ filename: './build/.cache/stats.html', open: true }) as Plugin); | |
44 | 41 | } |
45 | 42 | |
46 | - return rollupPlugins; | |
43 | + return vitePlugins; | |
47 | 44 | } | ... | ... |
build/vite/plugin/mock.ts
1 | -import { createMockServer } from 'vite-plugin-mock'; | |
2 | -import type { Plugin } from 'vite'; | |
3 | -import { isDevFn, ViteEnv } from '../../utils'; | |
1 | +import { viteMockServe } from 'vite-plugin-mock'; | |
2 | +import { ViteEnv } from '../../utils'; | |
4 | 3 | |
5 | -export function setupMockPlugin( | |
6 | - plugins: Plugin[], | |
7 | - env: ViteEnv, | |
8 | - mode: 'development' | 'production' | |
9 | -) { | |
4 | +export function configMockPlugin(env: ViteEnv, isBuild: boolean) { | |
10 | 5 | const { VITE_USE_MOCK } = env; |
11 | 6 | |
12 | - const useMock = isDevFn(mode) && VITE_USE_MOCK; | |
7 | + const useMock = !isBuild && VITE_USE_MOCK; | |
13 | 8 | |
14 | 9 | if (useMock) { |
15 | - const mockPlugin = createMockServer({ | |
10 | + const mockPlugin = viteMockServe({ | |
16 | 11 | ignore: /^\_/, |
17 | 12 | mockPath: 'mock', |
18 | 13 | showTime: true, |
19 | 14 | localEnabled: useMock, |
20 | 15 | }); |
21 | - plugins.push(mockPlugin); | |
16 | + return mockPlugin; | |
22 | 17 | } |
23 | - return plugins; | |
18 | + return []; | |
24 | 19 | } | ... | ... |
build/vite/plugin/pwa.ts
1 | 1 | import { VitePWA } from 'vite-plugin-pwa'; |
2 | -import type { Plugin } from 'vite'; | |
2 | + | |
3 | 3 | import { ViteEnv } from '../../utils'; |
4 | 4 | |
5 | -export function setupPwaPlugin( | |
6 | - plugins: Plugin[], | |
7 | - env: ViteEnv, | |
8 | - // @ts-ignore | |
9 | - mode: 'development' | 'production' | |
10 | -) { | |
11 | - const { VITE_USE_PWA } = env; | |
5 | +export function configPwaConfig(env: ViteEnv, isBulid: boolean) { | |
6 | + const { VITE_USE_PWA, VITE_GLOB_APP_TITLE, VITE_GLOB_APP_SHORT_NAME } = env; | |
12 | 7 | |
13 | - const pwaPlugin = VitePWA({ | |
14 | - manifest: { | |
15 | - name: 'Vben Admin', | |
16 | - short_name: 'vben_admin', | |
17 | - icons: [ | |
18 | - { | |
19 | - src: './resource/img/pwa-192x192.png', | |
20 | - sizes: '192x192', | |
21 | - type: 'image/png', | |
22 | - }, | |
23 | - { | |
24 | - src: './resource/img/pwa-512x512.png', | |
25 | - sizes: '512x512', | |
26 | - type: 'image/png', | |
27 | - }, | |
28 | - ], | |
29 | - }, | |
30 | - }); | |
31 | - if (VITE_USE_PWA) { | |
32 | - plugins.push(pwaPlugin); | |
8 | + if (VITE_USE_PWA && isBulid) { | |
9 | + // vite-plugin-pwa | |
10 | + const pwaPlugin = VitePWA({ | |
11 | + manifest: { | |
12 | + name: VITE_GLOB_APP_TITLE, | |
13 | + short_name: VITE_GLOB_APP_SHORT_NAME, | |
14 | + icons: [ | |
15 | + { | |
16 | + src: './resource/img/pwa-192x192.png', | |
17 | + sizes: '192x192', | |
18 | + type: 'image/png', | |
19 | + }, | |
20 | + { | |
21 | + src: './resource/img/pwa-512x512.png', | |
22 | + sizes: '512x512', | |
23 | + type: 'image/png', | |
24 | + }, | |
25 | + ], | |
26 | + }, | |
27 | + }); | |
28 | + return pwaPlugin; | |
33 | 29 | } |
34 | - return plugins; | |
30 | + return []; | |
35 | 31 | } | ... | ... |
build/vite/plugin/transform/dynamic-import/index.ts deleted
100644 → 0
1 | -// Used to import all files under `src/views` | |
2 | -// The built-in dynamic import of vite cannot meet the needs of importing all files under views | |
3 | -// Special usage ,Only for this project | |
4 | -import glob from 'glob'; | |
5 | -import { Transform } from 'vite/dist/node/transform.js'; | |
6 | - | |
7 | -function getPath(path: string) { | |
8 | - const lastIndex = path.lastIndexOf('.'); | |
9 | - if (lastIndex !== -1) { | |
10 | - path = path.substring(0, lastIndex); | |
11 | - } | |
12 | - return path.replace('src/views', ''); | |
13 | -} | |
14 | - | |
15 | -const dynamicImportTransform = function (enableDynamicImport: boolean): Transform { | |
16 | - return { | |
17 | - test({ path }) { | |
18 | - // Only convert the file | |
19 | - return ( | |
20 | - path.includes('/src/router/helper/dynamicImport.ts') || | |
21 | - path.includes(`\\src\\router\\helper\\dynamicImport.ts`) | |
22 | - ); | |
23 | - }, | |
24 | - transform({ code }) { | |
25 | - if (!enableDynamicImport) { | |
26 | - return code; | |
27 | - } | |
28 | - | |
29 | - // Only convert the dir | |
30 | - try { | |
31 | - const files = glob.sync('src/views/**/**.{vue,tsx}', { cwd: process.cwd() }); | |
32 | - | |
33 | - return ` | |
34 | - export default function (id) { | |
35 | - switch (id) { | |
36 | - ${files | |
37 | - .map((p) => | |
38 | - ` case '${getPath(p)}': return () => import('${p | |
39 | - .replace('src/views', '/@/views') | |
40 | - .replace(/\/\//g, '/')}');`.replace('.tsx', '') | |
41 | - ) | |
42 | - .join('\n ')} | |
43 | - default: return Promise.reject(new Error("Unknown variable dynamic import: " + id)); | |
44 | - } | |
45 | - }\n\n | |
46 | - `; | |
47 | - } catch (error) { | |
48 | - console.error(error); | |
49 | - return code; | |
50 | - } | |
51 | - }, | |
52 | - }; | |
53 | -}; | |
54 | -export default dynamicImportTransform; |
build/vite/plugin/transform/globby/index.ts deleted
100644 → 0
1 | -// Modified from | |
2 | -// https://github.com/luxueyan/vite-transform-globby-import/blob/master/src/index.ts | |
3 | - | |
4 | -// TODO Deleting files requires re-running the project | |
5 | -import { join } from 'path'; | |
6 | -import { lstatSync } from 'fs'; | |
7 | -import glob from 'glob'; | |
8 | -import globrex from 'globrex'; | |
9 | -import dotProp from 'dot-prop'; | |
10 | -import { createResolver, Resolver } from 'vite/dist/node/resolver.js'; | |
11 | -import { Transform } from 'vite/dist/node/transform.js'; | |
12 | - | |
13 | -const modulesDir: string = join(process.cwd(), '/node_modules/'); | |
14 | - | |
15 | -interface SharedConfig { | |
16 | - root?: string; | |
17 | - alias?: Record<string, string>; | |
18 | - resolvers?: Resolver[]; | |
19 | - | |
20 | - includes?: string[]; | |
21 | -} | |
22 | - | |
23 | -function template(template: string) { | |
24 | - return (data: { [x: string]: any }) => { | |
25 | - return template.replace(/#([^#]+)#/g, (_, g1) => data[g1] || g1); | |
26 | - }; | |
27 | -} | |
28 | - | |
29 | -// TODO support hmr | |
30 | -function hmr(isBuild = false) { | |
31 | - if (isBuild) return ''; | |
32 | - return ` | |
33 | - if (import.meta.hot) { | |
34 | - import.meta.hot.accept(); | |
35 | - }`; | |
36 | -} | |
37 | - | |
38 | -// handle includes | |
39 | -function fileInclude(includes: string | string[] | undefined, filePath: string) { | |
40 | - return !includes || !Array.isArray(includes) | |
41 | - ? true | |
42 | - : includes.some((item) => filePath.startsWith(item)); | |
43 | -} | |
44 | - | |
45 | -// Bare exporter | |
46 | -function compareString(modify: any, data: string[][]) { | |
47 | - return modify ? '\n' + data.map((v) => `${v[0]}._path = ${v[1]}`).join('\n') : ''; | |
48 | -} | |
49 | - | |
50 | -function varTemplate(data: string[][], name: string) { | |
51 | - //prepare deep data (for locales) | |
52 | - let deepData: Record<string, object | string> = {}; | |
53 | - let hasDeepData = false; | |
54 | - | |
55 | - //data modify | |
56 | - data.map((v) => { | |
57 | - //check for has deep data | |
58 | - if (v[0].includes('/')) { | |
59 | - hasDeepData = true; | |
60 | - } | |
61 | - | |
62 | - // lastKey is a data | |
63 | - let pathValue = v[0].replace(/\//g, '.').split('.'); | |
64 | - // let scopeKey = ''; | |
65 | - // const len=pathValue.length | |
66 | - // const scope=pathValue[len-2] | |
67 | - let lastKey: string | undefined = pathValue.pop(); | |
68 | - | |
69 | - let deepValue: Record<any, any> = {}; | |
70 | - if (lastKey) { | |
71 | - // Solve the problem of files with the same name in different folders | |
72 | - const lastKeyList = lastKey.replace('_' + pathValue[0], '').split('_'); | |
73 | - const key = lastKeyList.pop(); | |
74 | - if (key) { | |
75 | - deepValue[key] = lastKey; | |
76 | - } | |
77 | - } | |
78 | - // Set Deep Value | |
79 | - deepValue = Object.assign(deepValue, dotProp.get(deepData, pathValue.join('.'))); | |
80 | - dotProp.set(deepData, pathValue.join('.'), deepValue); | |
81 | - }); | |
82 | - | |
83 | - if (hasDeepData) { | |
84 | - return `const ${name} = ` + JSON.stringify(deepData).replace(/\"|\'/g, ''); | |
85 | - } | |
86 | - | |
87 | - return `const ${name} = { ${data.map((v) => v[0]).join(',')} }`; | |
88 | -} | |
89 | - | |
90 | -const globTransform = function (config: SharedConfig): Transform { | |
91 | - const resolver = createResolver( | |
92 | - config.root || process.cwd(), | |
93 | - config.resolvers || [], | |
94 | - config.alias || {} | |
95 | - ); | |
96 | - const { includes } = config; | |
97 | - const cache = new Map(); | |
98 | - const urlMap = new Map(); | |
99 | - return { | |
100 | - test({ path }) { | |
101 | - const filePath = path.replace('\u0000', ''); // why some path startsWith '\u0000'? | |
102 | - | |
103 | - try { | |
104 | - return ( | |
105 | - !filePath.startsWith(modulesDir) && | |
106 | - /\.(vue|js|jsx|ts|tsx)$/.test(filePath) && | |
107 | - fileInclude(includes, filePath) && | |
108 | - lstatSync(filePath).isFile() | |
109 | - ); | |
110 | - } catch { | |
111 | - return false; | |
112 | - } | |
113 | - }, | |
114 | - transform({ code, path, isBuild }) { | |
115 | - let result = cache.get(path); | |
116 | - if (!result) { | |
117 | - const reg = /import\s+([\w\s{}*]+)\s+from\s+(['"])globby(\?locale)?(\?path)?!([^'"]+)\2/g; | |
118 | - const match = code.match(reg); | |
119 | - if (!match) return code; | |
120 | - const lastImport = urlMap.get(path); | |
121 | - if (lastImport && match) { | |
122 | - code = code.replace(lastImport, match[0]); | |
123 | - } | |
124 | - result = code.replace( | |
125 | - reg, | |
126 | - ( | |
127 | - _, | |
128 | - // variable to export | |
129 | - exportName, | |
130 | - // bare export or not | |
131 | - bareExporter, | |
132 | - // is locale import | |
133 | - isLocale, | |
134 | - // inject _path attr | |
135 | - injectPath, | |
136 | - // path export | |
137 | - globPath | |
138 | - ) => { | |
139 | - const filePath = path.replace('\u0000', ''); // why some path startsWith '\u0000'? | |
140 | - // resolve path | |
141 | - | |
142 | - const resolvedFilePath = globPath.startsWith('.') | |
143 | - ? resolver.resolveRelativeRequest(filePath, globPath) | |
144 | - : { pathname: resolver.requestToFile(globPath) }; | |
145 | - | |
146 | - const files = glob.sync(resolvedFilePath.pathname, { dot: true }); | |
147 | - | |
148 | - let templateStr = 'import #name# from #file#'; // import default | |
149 | - let name = exportName; | |
150 | - const m = exportName.match(/\{\s*(\w+)(\s+as\s+(\w+))?\s*\}/); // import module | |
151 | - const m2 = exportName.match(/\*\s+as\s+(\w+)/); // import * as all module | |
152 | - if (m) { | |
153 | - templateStr = `import { ${m[1]} as #name# } from #file#`; | |
154 | - name = m[3] || m[1]; | |
155 | - } else if (m2) { | |
156 | - templateStr = 'import * as #name# from #file#'; | |
157 | - name = m2[1]; | |
158 | - } | |
159 | - | |
160 | - const templateRender = template(templateStr); | |
161 | - | |
162 | - const groups: Array<string>[] = []; | |
163 | - const replaceFiles = files.map((f, i) => { | |
164 | - const filePath = resolver.fileToRequest(f); | |
165 | - const file = bareExporter + filePath + bareExporter; | |
166 | - | |
167 | - if (isLocale) { | |
168 | - const globrexRes = globrex(globPath, { extended: true, globstar: true }); | |
169 | - | |
170 | - // Get segments for files like an en/system ch/modules for: | |
171 | - // ['en', 'system'] ['ch', 'modules'] | |
172 | - | |
173 | - // TODO The window system and mac system path are inconsistent? | |
174 | - const fileNameWithAlias = filePath.replace(/^(\/src\/)/, '/@/'); | |
175 | - const matchedGroups = globrexRes.regex.exec(fileNameWithAlias); | |
176 | - | |
177 | - if (matchedGroups && matchedGroups.length) { | |
178 | - const matchedSegments = matchedGroups[1]; //first everytime "Full Match" | |
179 | - const matchList = matchedSegments.split('/').filter(Boolean); | |
180 | - const lang = matchList.shift(); | |
181 | - const scope = matchList.pop(); | |
182 | - | |
183 | - // Solve the problem of files with the same name in different folders | |
184 | - const scopeKey = scope ? `${scope}_` : ''; | |
185 | - const fileName = matchedGroups[2]; | |
186 | - const name = scopeKey + fileName + '_' + lang; | |
187 | - | |
188 | - //send deep way like an (en/modules/system/dashboard) into groups | |
189 | - groups.push([matchedSegments + name, file]); | |
190 | - return templateRender({ | |
191 | - name, | |
192 | - file, | |
193 | - }); | |
194 | - } | |
195 | - } else { | |
196 | - groups.push([name + i, file]); | |
197 | - return templateRender({ name: name + i, file }); | |
198 | - } | |
199 | - }); | |
200 | - // save in memory used result | |
201 | - const filesJoined = replaceFiles.join('\n'); | |
202 | - | |
203 | - urlMap.set(path, filesJoined); | |
204 | - | |
205 | - // console.log('======================'); | |
206 | - // console.log(filesJoined, varTemplate(groups, name)); | |
207 | - // console.log('======================'); | |
208 | - return [ | |
209 | - filesJoined, | |
210 | - compareString(injectPath, groups), | |
211 | - varTemplate(groups, name), | |
212 | - '', | |
213 | - ].join('\n'); | |
214 | - } | |
215 | - ); | |
216 | - if (isBuild) cache.set(path, result); | |
217 | - } | |
218 | - return `${result}${hmr(isBuild)}`; | |
219 | - }, | |
220 | - }; | |
221 | -}; | |
222 | -export default globTransform; |
build/vite/proxy.ts
1 | +import type { ServerOptions } from 'http-proxy'; | |
2 | + | |
1 | 3 | type ProxyItem = [string, string]; |
2 | 4 | |
3 | 5 | type ProxyList = ProxyItem[]; |
4 | 6 | |
5 | -type ProxyTargetList = Record< | |
6 | - string, | |
7 | - { | |
8 | - target: string; | |
9 | - changeOrigin: boolean; | |
10 | - rewrite: (path: string) => any; | |
11 | - secure?: boolean; | |
12 | - } | |
13 | ->; | |
7 | +type ProxyTargetList = Record<string, ServerOptions & { rewrite: (path: string) => string }>; | |
14 | 8 | |
15 | 9 | const httpsRE = /^https:\/\//; |
16 | 10 | |
... | ... | @@ -23,9 +17,11 @@ export function createProxy(list: ProxyList = []) { |
23 | 17 | for (const [prefix, target] of list) { |
24 | 18 | const isHttps = httpsRE.test(target); |
25 | 19 | |
20 | + // https://github.com/http-party/node-http-proxy#options | |
26 | 21 | ret[prefix] = { |
27 | 22 | target: target, |
28 | 23 | changeOrigin: true, |
24 | + ws: true, | |
29 | 25 | rewrite: (path) => path.replace(new RegExp(`^${prefix}`), ''), |
30 | 26 | // https is require secure=false |
31 | 27 | ...(isHttps ? { secure: false } : {}), | ... | ... |
index.html
1 | 1 | <!DOCTYPE html> |
2 | 2 | <html lang="en"> |
3 | 3 | <head> |
4 | - <%= viteHtmlPluginOptions.hmScript %> | |
5 | 4 | <meta charset="UTF-8" /> |
6 | 5 | <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" /> |
7 | 6 | <meta name="renderer" content="webkit" /> |
... | ... | @@ -10,9 +9,8 @@ |
10 | 9 | content="width=device-width,initial-scale=1.0,minimum-scale=1.0,maximum-scale=1.0,user-scalable=0" |
11 | 10 | /> |
12 | 11 | |
13 | - <title></title> | |
12 | + <title><%= title %></title> | |
14 | 13 | <link rel="icon" href="/favicon.ico" /> |
15 | - <%= viteHtmlPluginOptions.injectConfig %> | |
16 | 14 | </head> |
17 | 15 | <body> |
18 | 16 | <div id="app"> |
... | ... | @@ -137,15 +135,11 @@ |
137 | 135 | </style> |
138 | 136 | <div class="app-loading"> |
139 | 137 | <div class="app-loading-wrap"> |
140 | - <img | |
141 | - src="<%= viteHtmlPluginOptions.publicPath %>resource/img/logo.png" | |
142 | - class="app-loading-logo" | |
143 | - alt="Logo" | |
144 | - /> | |
138 | + <img src="/resource/img/logo.png" class="app-loading-logo" alt="Logo" /> | |
145 | 139 | <div class="app-loading-dots"> |
146 | 140 | <span class="dot dot-spin"><i></i><i></i><i></i><i></i></span> |
147 | 141 | </div> |
148 | - <div class="app-loading-title"><%= viteHtmlPluginOptions.title %></div> | |
142 | + <div class="app-loading-title"><%= title %></div> | |
149 | 143 | </div> |
150 | 144 | </div> |
151 | 145 | </div> | ... | ... |
package.json
1 | 1 | { |
2 | 2 | "name": "vben-admin", |
3 | - "version": "2.0.0-rc.15", | |
3 | + "version": "2.0.0-rc.16", | |
4 | 4 | "scripts": { |
5 | 5 | "bootstrap": "yarn install", |
6 | - "serve": "cross-env vite --mode=development", | |
7 | - "build": "cross-env vite build --mode=production && esno ./build/script/postBuild.ts", | |
8 | - "build:site": "cross-env SITE=true npm run build ", | |
6 | + "serve": "cross-env vite ", | |
7 | + "build": "cross-env vite build && esno ./build/script/postBuild.ts", | |
9 | 8 | "build:no-cache": "yarn clean:cache && npm run build", |
10 | - "typecheck": "vuedx-typecheck .", | |
11 | 9 | "report": "cross-env REPORT=true npm run build ", |
12 | 10 | "preview": "npm run build && esno ./build/script/preview.ts", |
13 | 11 | "preview:dist": "esno ./build/script/preview.ts", |
14 | - "log": "esno ./build/script/changelog.ts", | |
12 | + "log": "conventional-changelog -p custom-config -i CHANGELOG.md -s -r 0", | |
15 | 13 | "clean:cache": "rimraf node_modules/.cache/ && rimraf node_modules/.vite_opt_cache", |
16 | 14 | "clean:lib": "npx rimraf node_modules", |
17 | - "ls-lint": "npx ls-lint", | |
18 | 15 | "lint:eslint": "eslint --fix --ext \"src/**/*.{vue,less,css,scss}\"", |
19 | 16 | "lint:prettier": "prettier --write --loglevel warn \"src/**/*.{js,json,tsx,css,less,scss,vue,html,md}\"", |
20 | 17 | "lint:stylelint": "stylelint --fix \"**/*.{vue,less,postcss,css,scss}\" --cache --cache-location node_modules/.cache/stylelint/", |
... | ... | @@ -22,7 +19,7 @@ |
22 | 19 | }, |
23 | 20 | "dependencies": { |
24 | 21 | "@iconify/iconify": "^2.0.0-rc.5", |
25 | - "@vueuse/core": "^4.0.2", | |
22 | + "@vueuse/core": "^4.0.5", | |
26 | 23 | "ant-design-vue": "^2.0.0-rc.8", |
27 | 24 | "apexcharts": "^3.23.1", |
28 | 25 | "axios": "^0.21.1", |
... | ... | @@ -34,26 +31,27 @@ |
34 | 31 | "nprogress": "^0.2.0", |
35 | 32 | "path-to-regexp": "^6.2.0", |
36 | 33 | "qrcode": "^1.4.4", |
37 | - "sortablejs": "^1.12.0", | |
34 | + "sortablejs": "^1.13.0", | |
38 | 35 | "vditor": "^3.7.5", |
39 | 36 | "vue": "^3.0.5", |
40 | - "vue-i18n": "9.0.0-beta.14", | |
37 | + "vue-i18n": "^9.0.0-rc.1", | |
41 | 38 | "vue-router": "^4.0.2", |
42 | 39 | "vue-types": "^3.0.1", |
43 | 40 | "vuex": "^4.0.0-rc.2", |
44 | 41 | "vuex-module-decorators": "^1.0.1", |
45 | - "xlsx": "^0.16.9", | |
46 | 42 | "zxcvbn": "^4.4.2" |
47 | 43 | }, |
48 | 44 | "devDependencies": { |
45 | + "@babel/core": "^7.12.10", | |
49 | 46 | "@commitlint/cli": "^11.0.0", |
50 | 47 | "@commitlint/config-conventional": "^11.0.0", |
51 | - "@iconify/json": "^1.1.282", | |
48 | + "@iconify/json": "^1.1.283", | |
52 | 49 | "@ls-lint/ls-lint": "^1.9.2", |
53 | - "@purge-icons/generated": "^0.4.1", | |
50 | + "@purge-icons/generated": "^0.5.0", | |
54 | 51 | "@types/echarts": "^4.9.3", |
55 | 52 | "@types/fs-extra": "^9.0.6", |
56 | 53 | "@types/globrex": "^0.1.0", |
54 | + "@types/http-proxy": "^1.17.4", | |
57 | 55 | "@types/koa-static": "^4.0.1", |
58 | 56 | "@types/lodash-es": "^4.17.4", |
59 | 57 | "@types/mockjs": "^1.0.3", |
... | ... | @@ -65,10 +63,13 @@ |
65 | 63 | "@types/zxcvbn": "^4.4.0", |
66 | 64 | "@typescript-eslint/eslint-plugin": "^4.12.0", |
67 | 65 | "@typescript-eslint/parser": "^4.12.0", |
66 | + "@vitejs/plugin-legacy": "^1.1.0", | |
67 | + "@vitejs/plugin-vue": "^1.0.4", | |
68 | + "@vitejs/plugin-vue-jsx": "^1.0.1", | |
68 | 69 | "@vue/compiler-sfc": "^3.0.5", |
69 | 70 | "@vuedx/typecheck": "^0.4.1", |
70 | 71 | "@vuedx/typescript-plugin-vue": "^0.4.1", |
71 | - "autoprefixer": "^9.8.6", | |
72 | + "autoprefixer": "^10.2.1", | |
72 | 73 | "commitizen": "^4.2.2", |
73 | 74 | "conventional-changelog-cli": "^2.1.1", |
74 | 75 | "conventional-changelog-custom-config": "^0.3.1", |
... | ... | @@ -82,14 +83,15 @@ |
82 | 83 | "esno": "^0.4.0", |
83 | 84 | "fs-extra": "^9.0.1", |
84 | 85 | "globrex": "^0.1.2", |
85 | - "husky": "^4.3.6", | |
86 | + "husky": "^4.3.7", | |
86 | 87 | "koa-static": "^5.0.0", |
87 | 88 | "less": "^4.0.0", |
88 | 89 | "lint-staged": "^10.5.3", |
89 | 90 | "portfinder": "^1.0.28", |
90 | - "postcss-import": "^12.0.1", | |
91 | + "postcss-import": "^14.0.0", | |
91 | 92 | "prettier": "^2.2.1", |
92 | 93 | "rimraf": "^3.0.2", |
94 | + "rollup-plugin-gzip": "^2.5.0", | |
93 | 95 | "rollup-plugin-visualizer": "^4.1.2", |
94 | 96 | "stylelint": "^13.8.0", |
95 | 97 | "stylelint-config-prettier": "^8.0.2", |
... | ... | @@ -98,11 +100,12 @@ |
98 | 100 | "tasksfile": "^5.1.1", |
99 | 101 | "ts-node": "^9.1.0", |
100 | 102 | "typescript": "^4.1.3", |
101 | - "vite": "^1.0.0-rc.13", | |
102 | - "vite-plugin-html": "^1.0.0-beta.2", | |
103 | - "vite-plugin-mock": "^1.0.9", | |
104 | - "vite-plugin-purge-icons": "^0.4.5", | |
105 | - "vite-plugin-pwa": "^0.2.1", | |
103 | + "vite": "^2.0.0-beta.15", | |
104 | + "vite-plugin-html": "^2.0.0-beta.5", | |
105 | + "vite-plugin-import-context": "^1.0.0-rc.1", | |
106 | + "vite-plugin-mock": "^2.0.0-beta.1", | |
107 | + "vite-plugin-purge-icons": "^0.5.0", | |
108 | + "vite-plugin-pwa": "^0.3.2", | |
106 | 109 | "vue-eslint-parser": "^7.3.0", |
107 | 110 | "yargs": "^16.2.0" |
108 | 111 | }, | ... | ... |
src/App.vue
... | ... | @@ -13,7 +13,7 @@ |
13 | 13 | import { initAppConfigStore } from '/@/setup/App'; |
14 | 14 | |
15 | 15 | import { useLockPage } from '/@/hooks/web/useLockPage'; |
16 | - import { useLocale } from '/@/hooks/web/useLocale'; | |
16 | + import { useLocale } from '/@/locales/useLocale'; | |
17 | 17 | |
18 | 18 | import { AppProvider } from '/@/components/Application'; |
19 | 19 | |
... | ... | @@ -21,6 +21,9 @@ |
21 | 21 | name: 'App', |
22 | 22 | components: { ConfigProvider, AppProvider }, |
23 | 23 | setup() { |
24 | + const { antConfigLocale, setLocale } = useLocale(); | |
25 | + setLocale(); | |
26 | + | |
24 | 27 | // Initialize vuex internal system configuration |
25 | 28 | initAppConfigStore(); |
26 | 29 | |
... | ... | @@ -28,7 +31,6 @@ |
28 | 31 | const lockEvent = useLockPage(); |
29 | 32 | |
30 | 33 | // support Multi-language |
31 | - const { antConfigLocale } = useLocale(); | |
32 | 34 | |
33 | 35 | return { |
34 | 36 | antConfigLocale, | ... | ... |
src/components/Application/src/AppLocalePicker.vue
... | ... | @@ -22,7 +22,7 @@ |
22 | 22 | import { Dropdown, DropMenu } from '/@/components/Dropdown'; |
23 | 23 | import { GlobalOutlined } from '@ant-design/icons-vue'; |
24 | 24 | |
25 | - import { useLocale } from '/@/hooks/web/useLocale'; | |
25 | + import { useLocale } from '/@/locales/useLocale'; | |
26 | 26 | import { useLocaleSetting } from '/@/hooks/setting/useLocaleSetting'; |
27 | 27 | |
28 | 28 | import { LocaleType } from '/@/locales/types'; |
... | ... | @@ -75,7 +75,6 @@ |
75 | 75 | </script> |
76 | 76 | |
77 | 77 | <style lang="less" scoped> |
78 | - @import (reference) '../../../design/index.less'; | |
79 | 78 | @prefix-cls: ~'@{namespace}-app-locale-picker'; |
80 | 79 | |
81 | 80 | :global(.@{prefix-cls}-overlay) { | ... | ... |
src/components/Application/src/AppLogo.vue
src/components/Application/src/search/AppSearch.vue
src/components/Application/src/search/AppSearchFooter.vue
src/components/Application/src/search/AppSearchModal.vue
... | ... | @@ -123,7 +123,6 @@ |
123 | 123 | }); |
124 | 124 | </script> |
125 | 125 | <style lang="less" scoped> |
126 | - @import (reference) '../../../../design/index.less'; | |
127 | 126 | @prefix-cls: ~'@{namespace}-app-search-modal'; |
128 | 127 | @footer-prefix-cls: ~'@{namespace}-app-search-footer'; |
129 | 128 | .@{prefix-cls} { | ... | ... |
src/components/Basic/src/BasicArrow.vue
src/components/Basic/src/BasicHelp.vue
src/components/Basic/src/BasicTitle.vue
src/components/Container/src/LazyContainer.vue
src/components/Container/src/ScrollContainer.vue
src/components/Container/src/collapse/CollapseContainer.vue
src/components/ContextMenu/src/index.less
src/components/ContextMenu/src/index.tsx
... | ... | @@ -76,9 +76,7 @@ export default defineComponent({ |
76 | 76 | return ( |
77 | 77 | <> |
78 | 78 | <Menu.Item disabled={disabled} class={`${prefixCls}__item`} key={label}> |
79 | - {() => [ | |
80 | - <ItemContent showIcon={props.showIcon} item={item} handler={handleAction} />, | |
81 | - ]} | |
79 | + <ItemContent showIcon={props.showIcon} item={item} handler={handleAction} /> | |
82 | 80 | </Menu.Item> |
83 | 81 | {DividerComp} |
84 | 82 | </> |
... | ... | @@ -109,7 +107,7 @@ export default defineComponent({ |
109 | 107 | ref={wrapRef} |
110 | 108 | style={unref(getStyle)} |
111 | 109 | > |
112 | - {() => renderMenuItem(items)} | |
110 | + {renderMenuItem(items)} | |
113 | 111 | </Menu> |
114 | 112 | ); |
115 | 113 | }; | ... | ... |
src/components/Description/src/index.tsx
... | ... | @@ -114,7 +114,7 @@ export default defineComponent({ |
114 | 114 | const renderDesc = () => { |
115 | 115 | return ( |
116 | 116 | <Descriptions class={`${prefixCls}`} {...(unref(getDescriptionsProps) as any)}> |
117 | - {() => renderItem()} | |
117 | + {renderItem()} | |
118 | 118 | </Descriptions> |
119 | 119 | ); |
120 | 120 | }; | ... | ... |
src/components/Drawer/src/BasicDrawer.vue
src/components/Drawer/src/components/DrawerFooter.vue
src/components/Drawer/src/components/DrawerHeader.vue
src/components/Form/src/BasicForm.vue
src/components/Form/src/components/FormItem.tsx
... | ... | @@ -173,7 +173,8 @@ export default defineComponent({ |
173 | 173 | const characterInx = rules.findIndex((val) => val.max); |
174 | 174 | if (characterInx !== -1 && !rules[characterInx].validator) { |
175 | 175 | rules[characterInx].message = |
176 | - rules[characterInx].message || t('component.form.maxTip', [rules[characterInx].max]); | |
176 | + rules[characterInx].message || | |
177 | + t('component.form.maxTip', [rules[characterInx].max] as Recordable); | |
177 | 178 | } |
178 | 179 | return rules; |
179 | 180 | } |
... | ... | @@ -294,12 +295,10 @@ export default defineComponent({ |
294 | 295 | labelCol={labelCol} |
295 | 296 | wrapperCol={wrapperCol} |
296 | 297 | > |
297 | - {() => ( | |
298 | - <> | |
299 | - {getContent()} | |
300 | - {showSuffix && <span class="suffix">{getSuffix}</span>} | |
301 | - </> | |
302 | - )} | |
298 | + <> | |
299 | + {getContent()} | |
300 | + {showSuffix && <span class="suffix">{getSuffix}</span>} | |
301 | + </> | |
303 | 302 | </Form.Item> |
304 | 303 | ); |
305 | 304 | } |
... | ... | @@ -323,7 +322,7 @@ export default defineComponent({ |
323 | 322 | return ( |
324 | 323 | isIfShow && ( |
325 | 324 | <Col {...realColProps} class={{ hidden: !isShow }}> |
326 | - {() => getContent()} | |
325 | + {getContent()} | |
327 | 326 | </Col> |
328 | 327 | ) |
329 | 328 | ); | ... | ... |
src/components/Icon/src/index.vue
src/components/Markdown/src/index.vue
... | ... | @@ -7,7 +7,7 @@ |
7 | 7 | import 'vditor/dist/index.css'; |
8 | 8 | |
9 | 9 | import { propTypes } from '/@/utils/propTypes'; |
10 | - import { useLocale } from '/@/hooks/web/useLocale'; | |
10 | + import { useLocale } from '/@/locales/useLocale'; | |
11 | 11 | import { useModalContext } from '../../Modal'; |
12 | 12 | |
13 | 13 | type Lang = 'zh_CN' | 'en_US' | 'ja_JP' | 'ko_KR' | undefined; | ... | ... |
src/components/Menu/src/index.less
src/components/Modal/src/components/ModalClose.vue
src/components/Modal/src/index.less
src/components/Preview/index.ts
1 | -export { createImgPreview } from './src/functional'; | |
1 | +// export { createImgPreview } from './src/functional'; | |
2 | 2 | |
3 | -import { createAsyncComponent } from '/@/utils/factory/createAsyncComponent'; | |
4 | -export const ImagePreview = createAsyncComponent(() => import('./src/index.vue')); | |
3 | +export const createImgPreview = () => {}; | |
4 | + | |
5 | +// import { createAsyncComponent } from '/@/utils/factory/createAsyncComponent'; | |
6 | +// export const ImagePreview = createAsyncComponent(() => import('./src/index.vue')); | |
7 | + | |
8 | +export { default as ImagePreview } from './src/index.vue'; | ... | ... |
src/components/Preview/src/index.less
src/components/Preview/src/index.vue
src/components/Scrollbar/src/index.vue
src/components/StrengthMeter/src/index.vue
src/components/Upload/src/FileList.less
src/components/Verify/src/DragVerify.less
src/components/Verify/src/ImgRotate.less
src/components/registerGlobComp.ts
... | ... | @@ -35,25 +35,21 @@ import { |
35 | 35 | Menu, |
36 | 36 | Breadcrumb, |
37 | 37 | } from 'ant-design-vue'; |
38 | -import { getApp } from '/@/setup/App'; | |
38 | +import { App } from 'vue'; | |
39 | 39 | |
40 | 40 | const compList = [Icon, Button, AntButton.Group]; |
41 | 41 | |
42 | 42 | // Fix hmr multiple registered components |
43 | -let registered = false; | |
44 | -export function registerGlobComp() { | |
45 | - if (registered) return; | |
43 | +export function registerGlobComp(app: App) { | |
46 | 44 | compList.forEach((comp: any) => { |
47 | - getApp().component(comp.name, comp); | |
45 | + app.component(comp.name, comp); | |
48 | 46 | }); |
49 | 47 | |
50 | - registered = true; | |
51 | - | |
52 | 48 | // Optional |
53 | 49 | // Why register here: The main reason for registering here is not to increase the size of the first screen code |
54 | 50 | // If you need to customize global components, you can write here |
55 | 51 | // If you don’t need it, you can delete it |
56 | - getApp() | |
52 | + app | |
57 | 53 | .use(Select) |
58 | 54 | .use(Alert) |
59 | 55 | .use(Breadcrumb) | ... | ... |
src/hooks/setting/useLocaleSetting.ts
... | ... | @@ -4,7 +4,7 @@ import { computed, unref } from 'vue'; |
4 | 4 | import { appStore } from '/@/store/modules/app'; |
5 | 5 | |
6 | 6 | import getProjectSetting from '/@/settings/projectSetting'; |
7 | -import { localeList } from '/@/locales'; | |
7 | +import { localeList } from '/@/locales/constant'; | |
8 | 8 | |
9 | 9 | // Get locale configuration |
10 | 10 | const getLocale = computed(() => appStore.getProjectConfig.locale || getProjectSetting.locale); | ... | ... |
src/hooks/web/useI18n.ts
1 | -import { getI18n } from '/@/setup/i18n'; | |
1 | +import { i18n } from '/@/locales/setupI18n'; | |
2 | 2 | |
3 | 3 | export function useI18n(namespace?: string) { |
4 | 4 | function getKey(key: string) { |
... | ... | @@ -16,18 +16,19 @@ export function useI18n(namespace?: string) { |
16 | 16 | }, |
17 | 17 | }; |
18 | 18 | |
19 | - if (!getI18n()) { | |
19 | + if (!i18n) { | |
20 | 20 | return normalFn; |
21 | 21 | } |
22 | 22 | |
23 | - const { t, ...methods } = getI18n().global; | |
23 | + const { t, ...methods } = i18n.global; | |
24 | 24 | |
25 | + const tFn = function (...arg: Parameters<typeof t>) { | |
26 | + if (!arg[0]) return ''; | |
27 | + return t(getKey(arg[0]), ...(arg as Parameters<typeof t>)); | |
28 | + }; | |
25 | 29 | return { |
26 | 30 | ...methods, |
27 | - t: (key: string, ...arg: any): string => { | |
28 | - if (!key) return ''; | |
29 | - return t(getKey(key), ...(arg as Parameters<typeof t>)); | |
30 | - }, | |
31 | + t: tFn, | |
31 | 32 | }; |
32 | 33 | } |
33 | 34 | ... | ... |
src/hooks/web/useMessage.tsx
... | ... | @@ -59,7 +59,7 @@ function createConfirm(options: ModalOptionsEx): ConfirmOptions { |
59 | 59 | icon: getIcon(iconType), |
60 | 60 | ...options, |
61 | 61 | }; |
62 | - return Modal.confirm(opt) as any; | |
62 | + return (Modal.confirm(opt) as unknown) as ConfirmOptions; | |
63 | 63 | } |
64 | 64 | |
65 | 65 | const baseOptions = { | ... | ... |
src/layouts/default/content/index.vue
... | ... | @@ -18,7 +18,7 @@ |
18 | 18 | import { useDesign } from '/@/hooks/web/useDesign'; |
19 | 19 | import { useRootSetting } from '/@/hooks/setting/useRootSetting'; |
20 | 20 | import { useTransitionSetting } from '/@/hooks/setting/useTransitionSetting'; |
21 | - import PageLayout from '/@/layouts/page/index'; | |
21 | + import PageLayout from '/@/layouts/page/index.vue'; | |
22 | 22 | import { useContentViewHeight } from './useContentViewHeight'; |
23 | 23 | import { Loading } from '/@/components/Loading'; |
24 | 24 | |
... | ... | @@ -41,7 +41,6 @@ |
41 | 41 | }); |
42 | 42 | </script> |
43 | 43 | <style lang="less"> |
44 | - @import (reference) '../../../design/index.less'; | |
45 | 44 | @prefix-cls: ~'@{namespace}-layout-content'; |
46 | 45 | |
47 | 46 | .@{prefix-cls} { | ... | ... |
src/layouts/default/footer/index.vue
src/layouts/default/header/MultipleHeader.vue
src/layouts/default/header/components/Breadcrumb.vue
src/layouts/default/header/components/lock/LockModal.vue
src/layouts/default/header/components/notify/NoticeList.vue
src/layouts/default/header/components/notify/index.vue
src/layouts/default/header/components/user-dropdown/index.vue
src/layouts/default/header/index.less
src/layouts/default/index.vue
... | ... | @@ -31,7 +31,6 @@ |
31 | 31 | import { useMenuSetting } from '/@/hooks/setting/useMenuSetting'; |
32 | 32 | import { useDesign } from '/@/hooks/web/useDesign'; |
33 | 33 | |
34 | - import { registerGlobComp } from '/@/components/registerGlobComp'; | |
35 | 34 | import { useAppInject } from '/@/hooks/web/useAppInject'; |
36 | 35 | |
37 | 36 | export default defineComponent({ |
... | ... | @@ -46,11 +45,6 @@ |
46 | 45 | Layout, |
47 | 46 | }, |
48 | 47 | setup() { |
49 | - // ! Only register global components here | |
50 | - // ! Can reduce the size of the first screen code | |
51 | - // default layout It is loaded after login. So it won’t be packaged to the first screen | |
52 | - registerGlobComp(); | |
53 | - | |
54 | 48 | const { prefixCls } = useDesign('default-layout'); |
55 | 49 | |
56 | 50 | const { getIsMobile } = useAppInject(); |
... | ... | @@ -70,7 +64,6 @@ |
70 | 64 | }); |
71 | 65 | </script> |
72 | 66 | <style lang="less"> |
73 | - @import (reference) '../../design/index.less'; | |
74 | 67 | @prefix-cls: ~'@{namespace}-default-layout'; |
75 | 68 | |
76 | 69 | .@{prefix-cls} { | ... | ... |
src/layouts/default/menu/index.less
src/layouts/default/setting/SettingDrawer.tsx
... | ... | @@ -389,26 +389,20 @@ export default defineComponent({ |
389 | 389 | width={330} |
390 | 390 | wrapClassName="setting-drawer" |
391 | 391 | > |
392 | - {{ | |
393 | - default: () => ( | |
394 | - <> | |
395 | - <Divider>{() => t('layout.setting.navMode')}</Divider> | |
396 | - {renderSidebar()} | |
397 | - <Divider>{() => t('layout.setting.headerTheme')}</Divider> | |
398 | - {renderHeaderTheme()} | |
399 | - <Divider>{() => t('layout.setting.sidebarTheme')}</Divider> | |
400 | - {renderSiderTheme()} | |
401 | - <Divider>{() => t('layout.setting.interfaceFunction')}</Divider> | |
402 | - {renderFeatures()} | |
403 | - <Divider>{() => t('layout.setting.interfaceDisplay')}</Divider> | |
404 | - {renderContent()} | |
405 | - <Divider>{() => t('layout.setting.animation')}</Divider> | |
406 | - {renderTransition()} | |
407 | - <Divider /> | |
408 | - <SettingFooter /> | |
409 | - </> | |
410 | - ), | |
411 | - }} | |
392 | + <Divider>{() => t('layout.setting.navMode')}</Divider> | |
393 | + {renderSidebar()} | |
394 | + <Divider>{() => t('layout.setting.headerTheme')}</Divider> | |
395 | + {renderHeaderTheme()} | |
396 | + <Divider>{() => t('layout.setting.sidebarTheme')}</Divider> | |
397 | + {renderSiderTheme()} | |
398 | + <Divider>{() => t('layout.setting.interfaceFunction')}</Divider> | |
399 | + {renderFeatures()} | |
400 | + <Divider>{() => t('layout.setting.interfaceDisplay')}</Divider> | |
401 | + {renderContent()} | |
402 | + <Divider>{() => t('layout.setting.animation')}</Divider> | |
403 | + {renderTransition()} | |
404 | + <Divider /> | |
405 | + <SettingFooter /> | |
412 | 406 | </BasicDrawer> |
413 | 407 | ); |
414 | 408 | }, | ... | ... |
src/layouts/default/setting/components/InputNumberItem.vue
src/layouts/default/setting/components/SelectItem.vue
src/layouts/default/setting/components/SettingFooter.vue
src/layouts/default/setting/components/SwitchItem.vue
src/layouts/default/setting/components/ThemePicker.vue
src/layouts/default/setting/components/TypePicker.vue
src/layouts/default/setting/index.vue
src/layouts/default/sider/DragBar.vue
src/layouts/default/sider/LayoutSider.vue
src/layouts/default/sider/MixSider.vue
src/layouts/default/sider/index.vue
src/layouts/default/tabs/index.less
src/layouts/page/index.tsx deleted
100644 → 0
1 | -import type { DefaultContext } from './transition'; | |
2 | - | |
3 | -import { computed, defineComponent, unref, Transition, KeepAlive } from 'vue'; | |
4 | -import { RouterView } from 'vue-router'; | |
5 | - | |
6 | -import FrameLayout from '/@/layouts/iframe/index.vue'; | |
7 | - | |
8 | -import { useRootSetting } from '/@/hooks/setting/useRootSetting'; | |
9 | - | |
10 | -import { useTransitionSetting } from '/@/hooks/setting/useTransitionSetting'; | |
11 | -import { useCache } from './useCache'; | |
12 | -import { useMultipleTabSetting } from '/@/hooks/setting/useMultipleTabSetting'; | |
13 | -import { getTransitionName } from './transition'; | |
14 | - | |
15 | -export default defineComponent({ | |
16 | - name: 'PageLayout', | |
17 | - setup() { | |
18 | - const { getCaches } = useCache(true); | |
19 | - const { getShowMultipleTab } = useMultipleTabSetting(); | |
20 | - | |
21 | - const { getOpenKeepAlive, getCanEmbedIFramePage } = useRootSetting(); | |
22 | - | |
23 | - const { getBasicTransition, getEnableTransition } = useTransitionSetting(); | |
24 | - | |
25 | - const openCache = computed(() => unref(getOpenKeepAlive) && unref(getShowMultipleTab)); | |
26 | - | |
27 | - return () => { | |
28 | - return ( | |
29 | - <> | |
30 | - <RouterView> | |
31 | - {{ | |
32 | - default: ({ Component, route }: DefaultContext) => { | |
33 | - // No longer show animations that are already in the tab | |
34 | - const cacheTabs = unref(getCaches); | |
35 | - | |
36 | - const name = getTransitionName({ | |
37 | - route, | |
38 | - openCache: unref(openCache), | |
39 | - enableTransition: unref(getEnableTransition), | |
40 | - cacheTabs, | |
41 | - def: unref(getBasicTransition), | |
42 | - }); | |
43 | - | |
44 | - // When the child element is the parentView, adding the key will cause the component to be executed multiple times. When it is not parentView, you need to add a key, because it needs to be compatible with the same route carrying different parameters | |
45 | - const isParentView = Component?.type.parentView; | |
46 | - const componentKey = isParentView ? {} : { key: route.fullPath }; | |
47 | - | |
48 | - const renderComp = () => <Component {...componentKey} />; | |
49 | - | |
50 | - const PageContent = unref(openCache) ? ( | |
51 | - <KeepAlive include={cacheTabs}>{renderComp()}</KeepAlive> | |
52 | - ) : ( | |
53 | - renderComp() | |
54 | - ); | |
55 | - | |
56 | - if (!unref(getEnableTransition)) { | |
57 | - return PageContent; | |
58 | - } | |
59 | - return ( | |
60 | - <Transition name={name} mode="out-in" appear={true}> | |
61 | - {() => PageContent} | |
62 | - </Transition> | |
63 | - ); | |
64 | - }, | |
65 | - }} | |
66 | - </RouterView> | |
67 | - {unref(getCanEmbedIFramePage) && <FrameLayout />} | |
68 | - </> | |
69 | - ); | |
70 | - }; | |
71 | - }, | |
72 | -}); |
src/layouts/page/index.vue
0 → 100644
1 | +<template> | |
2 | + <div> | |
3 | + <router-view> | |
4 | + <template v-slot="{ Component, route }"> | |
5 | + <transition | |
6 | + :name=" | |
7 | + getTransitionName({ | |
8 | + route, | |
9 | + openCache, | |
10 | + enableTransition: getEnableTransition, | |
11 | + cacheTabs: getCaches, | |
12 | + def: getBasicTransition, | |
13 | + }) | |
14 | + " | |
15 | + mode="out-in" | |
16 | + appear | |
17 | + > | |
18 | + <keep-alive v-if="openCache" :include="getCaches"> | |
19 | + <component :is="Component" v-bind="getKey(Component, route)" /> | |
20 | + </keep-alive> | |
21 | + <component v-else :is="Component" v-bind="getKey(Component, route)" /> | |
22 | + </transition> | |
23 | + </template> | |
24 | + </router-view> | |
25 | + <FrameLayout v-if="getCanEmbedIFramePage" /> | |
26 | + </div> | |
27 | +</template> | |
28 | + | |
29 | +<script lang="ts"> | |
30 | + import type { FunctionalComponent } from 'vue'; | |
31 | + import type { RouteLocation } from 'vue-router'; | |
32 | + | |
33 | + import { computed, defineComponent, unref } from 'vue'; | |
34 | + | |
35 | + import FrameLayout from '/@/layouts/iframe/index.vue'; | |
36 | + | |
37 | + import { useRootSetting } from '/@/hooks/setting/useRootSetting'; | |
38 | + | |
39 | + import { useTransitionSetting } from '/@/hooks/setting/useTransitionSetting'; | |
40 | + import { useCache } from './useCache'; | |
41 | + import { useMultipleTabSetting } from '/@/hooks/setting/useMultipleTabSetting'; | |
42 | + import { getTransitionName } from './transition'; | |
43 | + | |
44 | + export default defineComponent({ | |
45 | + name: 'PageLayout', | |
46 | + components: { FrameLayout }, | |
47 | + setup() { | |
48 | + const { getCaches } = useCache(true); | |
49 | + const { getShowMultipleTab } = useMultipleTabSetting(); | |
50 | + | |
51 | + const { getOpenKeepAlive, getCanEmbedIFramePage } = useRootSetting(); | |
52 | + | |
53 | + const { getBasicTransition, getEnableTransition } = useTransitionSetting(); | |
54 | + | |
55 | + const openCache = computed(() => unref(getOpenKeepAlive) && unref(getShowMultipleTab)); | |
56 | + | |
57 | + function getKey(component: FunctionalComponent & { type: Indexable }, route: RouteLocation) { | |
58 | + return !!component?.type.parentView ? {} : { key: route.fullPath }; | |
59 | + } | |
60 | + | |
61 | + return { | |
62 | + getTransitionName, | |
63 | + openCache, | |
64 | + getEnableTransition, | |
65 | + getBasicTransition, | |
66 | + getCaches, | |
67 | + getCanEmbedIFramePage, | |
68 | + getKey, | |
69 | + }; | |
70 | + }, | |
71 | + }); | |
72 | +</script> | ... | ... |
src/locales/index.ts renamed to src/locales/constant.ts
src/locales/getMessage.ts
0 → 100644
src/locales/helper.ts
0 → 100644
1 | +import { set } from 'lodash-es'; | |
2 | + | |
3 | +export function genMessage(langs: Record<string, Record<string, any>>, prefix = 'lang') { | |
4 | + const obj: Recordable = {}; | |
5 | + | |
6 | + Object.keys(langs).forEach((key) => { | |
7 | + const mod = langs[key].default; | |
8 | + let k = key.replace(`./${prefix}/`, '').replace(/^\.\//, ''); | |
9 | + const lastIndex = k.lastIndexOf('.'); | |
10 | + k = k.substring(0, lastIndex); | |
11 | + const keyList = k.split('/'); | |
12 | + const lang = keyList.shift(); | |
13 | + const objKey = keyList.join('.'); | |
14 | + if (lang) { | |
15 | + set(obj, lang, obj[lang] || {}); | |
16 | + set(obj[lang], objKey, mod); | |
17 | + } | |
18 | + }); | |
19 | + return obj; | |
20 | +} | ... | ... |
src/setup/i18n/index.ts renamed to src/locales/setupI18n.ts
1 | -import { App } from 'vue'; | |
1 | +import type { App } from 'vue'; | |
2 | 2 | import type { I18n, I18nOptions } from 'vue-i18n'; |
3 | 3 | |
4 | 4 | import { createI18n } from 'vue-i18n'; |
5 | -import localeMessages from '/@/locales'; | |
6 | -import { useLocale } from '/@/hooks/web/useLocale'; | |
5 | + | |
6 | +import 'moment/dist/locale/zh-cn'; | |
7 | + | |
7 | 8 | import projectSetting from '/@/settings/projectSetting'; |
8 | -const { setupLocale } = useLocale(); | |
9 | + | |
10 | +import messages from './getMessage'; | |
9 | 11 | |
10 | 12 | const { lang, availableLocales, fallback } = projectSetting?.locale; |
13 | + | |
11 | 14 | const localeData: I18nOptions = { |
12 | 15 | legacy: false, |
13 | 16 | locale: lang, |
14 | 17 | fallbackLocale: fallback, |
15 | - messages: localeMessages, | |
18 | + messages, | |
16 | 19 | availableLocales: availableLocales, |
17 | 20 | sync: true, //If you don’t want to inherit locale from global scope, you need to set sync of i18n component option to false. |
18 | 21 | silentTranslationWarn: true, // true - warning off |
19 | 22 | missingWarn: false, |
20 | 23 | silentFallbackWarn: true, |
21 | 24 | }; |
22 | - | |
23 | -let i18n: I18n; | |
25 | +export let i18n: I18n; | |
24 | 26 | |
25 | 27 | // setup i18n instance with glob |
26 | 28 | export function setupI18n(app: App) { |
27 | 29 | i18n = createI18n(localeData) as I18n; |
28 | - setupLocale(); | |
29 | 30 | app.use(i18n); |
30 | 31 | } |
31 | - | |
32 | -export function getI18n(): I18n { | |
33 | - return i18n; | |
34 | -} | ... | ... |
src/hooks/web/useLocale.ts renamed to src/locales/useLocale.ts
... | ... | @@ -2,18 +2,16 @@ |
2 | 2 | * Multi-language related operations |
3 | 3 | */ |
4 | 4 | import type { LocaleType } from '/@/locales/types'; |
5 | +import type { Ref } from 'vue'; | |
5 | 6 | |
6 | 7 | import { unref, ref } from 'vue'; |
7 | - | |
8 | -import { getI18n } from '/@/setup/i18n'; | |
9 | - | |
10 | 8 | import { useLocaleSetting } from '/@/hooks/setting/useLocaleSetting'; |
11 | 9 | |
12 | 10 | import moment from 'moment'; |
13 | 11 | |
14 | 12 | import 'moment/dist/locale/zh-cn'; |
15 | 13 | |
16 | -moment.locale('zh-cn'); | |
14 | +import { i18n } from './setupI18n'; | |
17 | 15 | |
18 | 16 | const antConfigLocaleRef = ref<any>(null); |
19 | 17 | |
... | ... | @@ -23,7 +21,11 @@ export function useLocale() { |
23 | 21 | // Switching the language will change the locale of useI18n |
24 | 22 | // And submit to configuration modification |
25 | 23 | function changeLocale(lang: LocaleType): void { |
26 | - (getI18n().global.locale as any).value = lang; | |
24 | + if (i18n.mode === 'legacy') { | |
25 | + i18n.global.locale = lang; | |
26 | + } else { | |
27 | + ((i18n.global.locale as unknown) as Ref<string>).value = lang; | |
28 | + } | |
27 | 29 | setLocalSetting({ lang }); |
28 | 30 | // i18n.global.setLocaleMessage(locale, messages); |
29 | 31 | |
... | ... | @@ -51,13 +53,13 @@ export function useLocale() { |
51 | 53 | } |
52 | 54 | |
53 | 55 | // initialization |
54 | - function setupLocale() { | |
56 | + function setLocale() { | |
55 | 57 | const lang = unref(getLang); |
56 | 58 | lang && changeLocale(lang); |
57 | 59 | } |
58 | 60 | |
59 | 61 | return { |
60 | - setupLocale, | |
62 | + setLocale, | |
61 | 63 | getLocale, |
62 | 64 | getLang, |
63 | 65 | changeLocale, | ... | ... |
src/main.ts
... | ... | @@ -6,18 +6,19 @@ import { setupStore } from '/@/store'; |
6 | 6 | import { setupAntd } from '/@/setup/ant-design-vue'; |
7 | 7 | import { setupErrorHandle } from '/@/setup/error-handle'; |
8 | 8 | import { setupGlobDirectives } from '/@/directives'; |
9 | -import { setupI18n } from '/@/setup/i18n'; | |
9 | +import { setupI18n } from '/@/locales/setupI18n'; | |
10 | 10 | import { setupProdMockServer } from '../mock/_createProductionServer'; |
11 | -import { setApp } from '/@/setup/App'; | |
11 | + | |
12 | +import { registerGlobComp } from '/@/components/registerGlobComp'; | |
12 | 13 | |
13 | 14 | import { isDevMode, isProdMode, isUseMock } from '/@/utils/env'; |
14 | 15 | |
15 | 16 | import '/@/design/index.less'; |
16 | 17 | |
17 | -import '/@/locales/index'; | |
18 | - | |
19 | 18 | const app = createApp(App); |
20 | 19 | |
20 | +registerGlobComp(app); | |
21 | + | |
21 | 22 | // Configure component library |
22 | 23 | setupAntd(app); |
23 | 24 | |
... | ... | @@ -51,5 +52,3 @@ if (isDevMode()) { |
51 | 52 | if (isProdMode() && isUseMock()) { |
52 | 53 | setupProdMockServer(); |
53 | 54 | } |
54 | -// Used to share app instances in other modules | |
55 | -setApp(app); | ... | ... |
src/router/helper/dynamicImport.ts deleted
100644 → 0
src/router/helper/routeHelper.ts
... | ... | @@ -2,8 +2,8 @@ import type { AppRouteModule, AppRouteRecordRaw } from '/@/router/types'; |
2 | 2 | import type { RouteLocationNormalized, RouteRecordNormalized } from 'vue-router'; |
3 | 3 | |
4 | 4 | import { getParentLayout, LAYOUT } from '/@/router/constant'; |
5 | -import dynamicImport from './dynamicImport'; | |
6 | 5 | import { cloneDeep } from 'lodash-es'; |
6 | +import { warn } from '/@/utils/log'; | |
7 | 7 | |
8 | 8 | export type LayoutMapKey = 'LAYOUT'; |
9 | 9 | |
... | ... | @@ -11,12 +11,20 @@ const LayoutMap = new Map<LayoutMapKey, () => Promise<typeof import('*.vue')>>() |
11 | 11 | |
12 | 12 | // 动态引入 |
13 | 13 | function asyncImportRoute(routes: AppRouteRecordRaw[] | undefined) { |
14 | + // TODO Because xlsx does not support vite2.0 temporarily. So filter the excel example first | |
15 | + const dynamicViewsModules = importContext({ | |
16 | + dir: '/@/views', | |
17 | + deep: true, | |
18 | + regexp: /^(?!.*\/demo\/excel).*\.(tsx?|vue)$/, | |
19 | + dynamicImport: true, | |
20 | + dynamicEnabled: 'autoImportRoute', | |
21 | + }); | |
14 | 22 | if (!routes) return; |
15 | 23 | routes.forEach((item) => { |
16 | 24 | const { component, name } = item; |
17 | 25 | const { children } = item; |
18 | 26 | if (component) { |
19 | - item.component = dynamicImport(component as string); | |
27 | + item.component = dynamicImport(dynamicViewsModules, component as string); | |
20 | 28 | } else if (name) { |
21 | 29 | item.component = getParentLayout(name); |
22 | 30 | } |
... | ... | @@ -24,6 +32,24 @@ function asyncImportRoute(routes: AppRouteRecordRaw[] | undefined) { |
24 | 32 | }); |
25 | 33 | } |
26 | 34 | |
35 | +function dynamicImport(dynamicViewsModules: DynamicImportContextResult, component: string) { | |
36 | + const keys = dynamicViewsModules.keys(); | |
37 | + const matchKeys = keys.filter((key) => { | |
38 | + const k = key.substr(1); | |
39 | + return k.startsWith(component) || k.startsWith(`/${component}`); | |
40 | + }); | |
41 | + if (matchKeys?.length === 1) { | |
42 | + const matchKey = matchKeys[0]; | |
43 | + return dynamicViewsModules(matchKey); | |
44 | + } | |
45 | + if (matchKeys?.length > 1) { | |
46 | + warn( | |
47 | + 'Please do not create `.vue` and `.TSX` files with the same file name in the same hierarchical directory under the views folder. This will cause dynamic introduction failure' | |
48 | + ); | |
49 | + return; | |
50 | + } | |
51 | +} | |
52 | + | |
27 | 53 | // Turn background objects into routing objects |
28 | 54 | export function transformObjToRoute<T = AppRouteModule>(routeList: AppRouteModule[]): T[] { |
29 | 55 | LayoutMap.set('LAYOUT', LAYOUT); | ... | ... |
src/router/menus/index.ts
... | ... | @@ -9,18 +9,18 @@ import router from '/@/router'; |
9 | 9 | import { PermissionModeEnum } from '/@/enums/appEnum'; |
10 | 10 | import { pathToRegexp } from 'path-to-regexp'; |
11 | 11 | |
12 | -import modules from 'globby!/@/router/menus/modules/**/*.@(ts)'; | |
13 | - | |
14 | -const reg = /(((https?:(?:\/\/)?)(?:[-;:&=\+\$,\w]+@)?[A-Za-z0-9.-]+(?::\d+)?|(?:www.|[-;:&=\+\$,\w]+@)[A-Za-z0-9.-]+)((?:\/[\+~%\/.\w-_]*)?\??(?:[-\+=&;%@.\w_]*)#?(?:[\w]*))?)$/; | |
12 | +import modules from 'glob:./modules/**/*.ts'; | |
15 | 13 | |
16 | 14 | const menuModules: MenuModule[] = []; |
17 | 15 | |
18 | 16 | Object.keys(modules).forEach((key) => { |
19 | - const moduleItem = modules[key]; | |
20 | - const menuModule = Array.isArray(moduleItem) ? [...moduleItem] : [moduleItem]; | |
21 | - menuModules.push(...menuModule); | |
17 | + const mod = modules[key].default || {}; | |
18 | + const modList = Array.isArray(mod) ? [...mod] : [mod]; | |
19 | + menuModules.push(...modList); | |
22 | 20 | }); |
23 | 21 | |
22 | +const reg = /(((https?:(?:\/\/)?)(?:[-;:&=\+\$,\w]+@)?[A-Za-z0-9.-]+(?::\d+)?|(?:www.|[-;:&=\+\$,\w]+@)[A-Za-z0-9.-]+)((?:\/[\+~%\/.\w-_]*)?\??(?:[-\+=&;%@.\w_]*)#?(?:[\w]*))?)$/; | |
23 | + | |
24 | 24 | // =========================== |
25 | 25 | // ==========Helper=========== |
26 | 26 | // =========================== | ... | ... |
src/router/routes/index.ts
... | ... | @@ -2,17 +2,18 @@ import type { AppRouteRecordRaw, AppRouteModule } from '/@/router/types'; |
2 | 2 | |
3 | 3 | import { PAGE_NOT_FOUND_ROUTE, REDIRECT_ROUTE } from '../constant'; |
4 | 4 | |
5 | -import modules from 'globby!/@/router/routes/modules/**/*.@(ts)'; | |
6 | 5 | import { mainOutRoutes } from './mainOut'; |
7 | 6 | import { PageEnum } from '/@/enums/pageEnum'; |
8 | - | |
9 | 7 | import { t } from '/@/hooks/web/useI18n'; |
10 | 8 | |
9 | +import modules from 'glob:./modules/**/*.ts'; | |
10 | + | |
11 | 11 | const routeModuleList: AppRouteModule[] = []; |
12 | 12 | |
13 | 13 | Object.keys(modules).forEach((key) => { |
14 | - const mod = Array.isArray(modules[key]) ? [...modules[key]] : [modules[key]]; | |
15 | - routeModuleList.push(...mod); | |
14 | + const mod = modules[key].default || {}; | |
15 | + const modList = Array.isArray(mod) ? [...mod] : [mod]; | |
16 | + routeModuleList.push(...modList); | |
16 | 17 | }); |
17 | 18 | |
18 | 19 | export const asyncRoutes = [PAGE_NOT_FOUND_ROUTE, ...routeModuleList]; | ... | ... |
src/router/routes/modules/demo/feat.ts
... | ... | @@ -172,51 +172,51 @@ const feat: AppRouteModule = { |
172 | 172 | title: t('routes.demo.feat.errorLog'), |
173 | 173 | }, |
174 | 174 | }, |
175 | - { | |
176 | - path: 'excel', | |
177 | - name: 'Excel', | |
178 | - redirect: '/feat/excel/customExport', | |
179 | - component: getParentLayout('Excel'), | |
180 | - meta: { | |
181 | - // icon: 'mdi:microsoft-excel', | |
182 | - title: t('routes.demo.excel.excel'), | |
183 | - }, | |
175 | + // { | |
176 | + // path: 'excel', | |
177 | + // name: 'Excel', | |
178 | + // redirect: '/feat/excel/customExport', | |
179 | + // component: getParentLayout('Excel'), | |
180 | + // meta: { | |
181 | + // // icon: 'mdi:microsoft-excel', | |
182 | + // title: t('routes.demo.excel.excel'), | |
183 | + // }, | |
184 | 184 | |
185 | - children: [ | |
186 | - { | |
187 | - path: 'customExport', | |
188 | - name: 'CustomExport', | |
189 | - component: () => import('/@/views/demo/excel/CustomExport.vue'), | |
190 | - meta: { | |
191 | - title: t('routes.demo.excel.customExport'), | |
192 | - }, | |
193 | - }, | |
194 | - { | |
195 | - path: 'jsonExport', | |
196 | - name: 'JsonExport', | |
197 | - component: () => import('/@/views/demo/excel/JsonExport.vue'), | |
198 | - meta: { | |
199 | - title: t('routes.demo.excel.jsonExport'), | |
200 | - }, | |
201 | - }, | |
202 | - { | |
203 | - path: 'arrayExport', | |
204 | - name: 'ArrayExport', | |
205 | - component: () => import('/@/views/demo/excel/ArrayExport.vue'), | |
206 | - meta: { | |
207 | - title: t('routes.demo.excel.arrayExport'), | |
208 | - }, | |
209 | - }, | |
210 | - { | |
211 | - path: 'importExcel', | |
212 | - name: 'ImportExcel', | |
213 | - component: () => import('/@/views/demo/excel/ImportExcel.vue'), | |
214 | - meta: { | |
215 | - title: t('routes.demo.excel.importExcel'), | |
216 | - }, | |
217 | - }, | |
218 | - ], | |
219 | - }, | |
185 | + // children: [ | |
186 | + // { | |
187 | + // path: 'customExport', | |
188 | + // name: 'CustomExport', | |
189 | + // component: () => import('/@/views/demo/excel/CustomExport.vue'), | |
190 | + // meta: { | |
191 | + // title: t('routes.demo.excel.customExport'), | |
192 | + // }, | |
193 | + // }, | |
194 | + // { | |
195 | + // path: 'jsonExport', | |
196 | + // name: 'JsonExport', | |
197 | + // component: () => import('/@/views/demo/excel/JsonExport.vue'), | |
198 | + // meta: { | |
199 | + // title: t('routes.demo.excel.jsonExport'), | |
200 | + // }, | |
201 | + // }, | |
202 | + // { | |
203 | + // path: 'arrayExport', | |
204 | + // name: 'ArrayExport', | |
205 | + // component: () => import('/@/views/demo/excel/ArrayExport.vue'), | |
206 | + // meta: { | |
207 | + // title: t('routes.demo.excel.arrayExport'), | |
208 | + // }, | |
209 | + // }, | |
210 | + // { | |
211 | + // path: 'importExcel', | |
212 | + // name: 'ImportExcel', | |
213 | + // component: () => import('/@/views/demo/excel/ImportExcel.vue'), | |
214 | + // meta: { | |
215 | + // title: t('routes.demo.excel.importExcel'), | |
216 | + // }, | |
217 | + // }, | |
218 | + // ], | |
219 | + // }, | |
220 | 220 | { |
221 | 221 | path: 'testTab/:id', |
222 | 222 | name: 'TestTab', | ... | ... |
src/settings/projectSetting.ts
... | ... | @@ -172,7 +172,7 @@ const setting: ProjectConfig = { |
172 | 172 | |
173 | 173 | // Whether to cancel the http request that has been sent but not responded when switching the interface. |
174 | 174 | // If it is enabled, I want to overwrite a single interface. Can be set in a separate interface |
175 | - removeAllHttpPending: true, | |
175 | + removeAllHttpPending: false, | |
176 | 176 | }; |
177 | 177 | |
178 | 178 | export default setting; | ... | ... |
src/setup/App.ts
... | ... | @@ -3,7 +3,6 @@ |
3 | 3 | */ |
4 | 4 | |
5 | 5 | import type { ProjectConfig } from '/@/types/config'; |
6 | -import type { App } from 'vue'; | |
7 | 6 | import { computed, ref } from 'vue'; |
8 | 7 | |
9 | 8 | import { ThemeModeEnum } from '/@/enums/appEnum'; |
... | ... | @@ -21,17 +20,6 @@ import { |
21 | 20 | import { appStore } from '/@/store/modules/app'; |
22 | 21 | import { deepMerge } from '/@/utils'; |
23 | 22 | |
24 | -// Used to share global app instances | |
25 | -let app: App; | |
26 | - | |
27 | -export function setApp(_app: App): void { | |
28 | - app = _app; | |
29 | -} | |
30 | - | |
31 | -export function getApp(): App { | |
32 | - return app; | |
33 | -} | |
34 | - | |
35 | 23 | // TODO Theme switching |
36 | 24 | export function useThemeMode(mode: ThemeModeEnum) { |
37 | 25 | const modeRef = ref(mode); | ... | ... |
src/types/vue-app-env.d.ts
... | ... | @@ -23,81 +23,3 @@ declare namespace NodeJS { |
23 | 23 | } |
24 | 24 | |
25 | 25 | declare let process: NodeJS.Process; |
26 | - | |
27 | -declare module '*.bmp' { | |
28 | - const src: string; | |
29 | - export default src; | |
30 | -} | |
31 | - | |
32 | -declare module '*.gif' { | |
33 | - const src: string; | |
34 | - export default src; | |
35 | -} | |
36 | - | |
37 | -declare module '*.jpg' { | |
38 | - const src: string; | |
39 | - export default src; | |
40 | -} | |
41 | - | |
42 | -declare module '*.jpeg' { | |
43 | - const src: string; | |
44 | - export default src; | |
45 | -} | |
46 | - | |
47 | -declare module '*.png' { | |
48 | - const src: string; | |
49 | - export default src; | |
50 | -} | |
51 | - | |
52 | -declare module '*.webp' { | |
53 | - const src: string; | |
54 | - export default src; | |
55 | -} | |
56 | - | |
57 | -declare module '*.svg' { | |
58 | - const src: string; | |
59 | - export default src; | |
60 | -} | |
61 | - | |
62 | -declare module '*.json' { | |
63 | - const content: any | any[]; | |
64 | - export default content; | |
65 | -} | |
66 | - | |
67 | -declare module '*.scss' { | |
68 | - const content: { | |
69 | - readonly [className: string]: string; | |
70 | - }; | |
71 | - export default content; | |
72 | -} | |
73 | -declare module '*.less' { | |
74 | - const content: { | |
75 | - readonly [className: string]: string; | |
76 | - }; | |
77 | - export default content; | |
78 | -} | |
79 | -declare module '*.styl' { | |
80 | - const content: { | |
81 | - readonly [className: string]: string; | |
82 | - }; | |
83 | - export default content; | |
84 | -} | |
85 | -declare module '*.css' { | |
86 | - const content: any; | |
87 | - export default content; | |
88 | -} | |
89 | - | |
90 | -declare module '*.module.css' { | |
91 | - const classes: { readonly [key: string]: string }; | |
92 | - export default classes; | |
93 | -} | |
94 | - | |
95 | -declare module '*.module.scss' { | |
96 | - const classes: { readonly [key: string]: string }; | |
97 | - export default classes; | |
98 | -} | |
99 | - | |
100 | -declare module '*.module.sass' { | |
101 | - const classes: { readonly [key: string]: string }; | |
102 | - export default classes; | |
103 | -} | ... | ... |
src/types/window.d.ts
src/views/dashboard/analysis/components/GrowCard.vue
src/views/dashboard/analysis/index.vue