diff --git a/.env b/.env index 09344c1..f5327e8 100644 --- a/.env +++ b/.env @@ -1,5 +1,17 @@ # 平台本地运行端口号 -VITE_PORT = 8848 +VITE_PORT=8202 + +# 预发布环境路由历史模式(Hash模式传"hash"、HTML5模式传"h5"、Hash模式带base参数传"hash,base参数"、HTML5模式带base参数传"h5,base参数") +VITE_ROUTER_HISTORY="hash" + +# 跨域代理地址 +VITE_APP_URL="http://localhost:8801" + +# 是否在打包时使用cdn替换本地库 替换 true 不替换 false +VITE_CDN=true + +# 是否启用gzip压缩或brotli压缩(分两种情况,删除原始文件和不删除原始文件) +# 压缩时不删除原始文件的配置:gzip、brotli、both(同时开启 gzip 与 brotli 压缩)、none(不开启压缩,默认) +# 压缩时删除原始文件的配置:gzip-clear、brotli-clear、both-clear(同时开启 gzip 与 brotli 压缩)、none(不开启压缩,默认) +VITE_COMPRESSION="none" -# 是否隐藏首页 隐藏 true 不隐藏 false (勿删除,VITE_HIDE_HOME只需在.env文件配置) -VITE_HIDE_HOME = false diff --git a/.env.development b/.env.development index 90d1146..f5327e8 100644 --- a/.env.development +++ b/.env.development @@ -1,8 +1,17 @@ # 平台本地运行端口号 -VITE_PORT = 8848 +VITE_PORT=8202 -# 开发环境读取配置文件路径 -VITE_PUBLIC_PATH = / +# 预发布环境路由历史模式(Hash模式传"hash"、HTML5模式传"h5"、Hash模式带base参数传"hash,base参数"、HTML5模式带base参数传"h5,base参数") +VITE_ROUTER_HISTORY="hash" + +# 跨域代理地址 +VITE_APP_URL="http://localhost:8801" + +# 是否在打包时使用cdn替换本地库 替换 true 不替换 false +VITE_CDN=true + +# 是否启用gzip压缩或brotli压缩(分两种情况,删除原始文件和不删除原始文件) +# 压缩时不删除原始文件的配置:gzip、brotli、both(同时开启 gzip 与 brotli 压缩)、none(不开启压缩,默认) +# 压缩时删除原始文件的配置:gzip-clear、brotli-clear、both-clear(同时开启 gzip 与 brotli 压缩)、none(不开启压缩,默认) +VITE_COMPRESSION="none" -# 开发环境路由历史模式(Hash模式传"hash"、HTML5模式传"h5"、Hash模式带base参数传"hash,base参数"、HTML5模式带base参数传"h5,base参数") -VITE_ROUTER_HISTORY = "hash" diff --git a/.env.production b/.env.production index 84e6086..f5327e8 100644 --- a/.env.production +++ b/.env.production @@ -1,13 +1,17 @@ -# 线上环境平台打包路径 -VITE_PUBLIC_PATH = / +# 平台本地运行端口号 +VITE_PORT=8202 -# 线上环境路由历史模式(Hash模式传"hash"、HTML5模式传"h5"、Hash模式带base参数传"hash,base参数"、HTML5模式带base参数传"h5,base参数") -VITE_ROUTER_HISTORY = "hash" +# 预发布环境路由历史模式(Hash模式传"hash"、HTML5模式传"h5"、Hash模式带base参数传"hash,base参数"、HTML5模式带base参数传"h5,base参数") +VITE_ROUTER_HISTORY="hash" + +# 跨域代理地址 +VITE_APP_URL="http://localhost:8801" # 是否在打包时使用cdn替换本地库 替换 true 不替换 false -VITE_CDN = false +VITE_CDN=true # 是否启用gzip压缩或brotli压缩(分两种情况,删除原始文件和不删除原始文件) # 压缩时不删除原始文件的配置:gzip、brotli、both(同时开启 gzip 与 brotli 压缩)、none(不开启压缩,默认) # 压缩时删除原始文件的配置:gzip-clear、brotli-clear、both-clear(同时开启 gzip 与 brotli 压缩)、none(不开启压缩,默认) -VITE_COMPRESSION = "none" \ No newline at end of file +VITE_COMPRESSION="none" + diff --git a/.env.staging b/.env.staging index 65b57e3..f5327e8 100644 --- a/.env.staging +++ b/.env.staging @@ -1,16 +1,17 @@ -# 预发布也需要生产环境的行为 -# https://cn.vitejs.dev/guide/env-and-mode.html#modes -# NODE_ENV = development - -VITE_PUBLIC_PATH = / +# 平台本地运行端口号 +VITE_PORT=8202 # 预发布环境路由历史模式(Hash模式传"hash"、HTML5模式传"h5"、Hash模式带base参数传"hash,base参数"、HTML5模式带base参数传"h5,base参数") -VITE_ROUTER_HISTORY = "hash" +VITE_ROUTER_HISTORY="hash" + +# 跨域代理地址 +VITE_APP_URL="http://localhost:8801" # 是否在打包时使用cdn替换本地库 替换 true 不替换 false -VITE_CDN = true +VITE_CDN=true # 是否启用gzip压缩或brotli压缩(分两种情况,删除原始文件和不删除原始文件) # 压缩时不删除原始文件的配置:gzip、brotli、both(同时开启 gzip 与 brotli 压缩)、none(不开启压缩,默认) # 压缩时删除原始文件的配置:gzip-clear、brotli-clear、both-clear(同时开启 gzip 与 brotli 压缩)、none(不开启压缩,默认) -VITE_COMPRESSION = "none" +VITE_COMPRESSION="none" + diff --git a/.nvmrc b/.nvmrc new file mode 100644 index 0000000..67d2ffe --- /dev/null +++ b/.nvmrc @@ -0,0 +1 @@ +v20.13.1 \ No newline at end of file diff --git a/build/buildEnv.ts b/build/buildEnv.ts new file mode 100644 index 0000000..6bd7be3 --- /dev/null +++ b/build/buildEnv.ts @@ -0,0 +1,52 @@ +import { pathResolve } from './utils'; +import type { BuildOptions } from 'vite'; + +export const buildEnvironment = () => { + const environment: BuildOptions = { + target: 'es2015', + assetsInlineLimit: 20000, + // 构建输出的目录,默认值为"dist" + outDir: 'dist', + // 用于指定使用的代码压缩工具。在这里,minify 被设置为 'terser',表示使用 Terser 进行代码压缩。默认值terser + // esbuild 打包更快,但是不能去除 console.log,terser打包慢,但能去除 console.log + minify: 'terser', + // 用于配置 Terser 的选项 + terserOptions: { + // 用于配置压缩选项 + compress: { + drop_console: true, // 是否删除代码中的 console 语句, 默认值false + drop_debugger: true, // 是否删除代码中的 debugger 语句, 默认值false + }, + }, + // 禁用 gzip 压缩大小报告,可略微减少打包时间 + reportCompressedSize: false, + // 用于指定是否生成源映射文件。源映射文件可以帮助调试和定位源代码中的错误。当设置为false时,构建过程不会生成源映射文件 + sourcemap: false, + // 用于配置 CommonJS 模块的选项 + commonjsOptions: { + // 用于指定是否忽略 CommonJS 模块中的 try-catch 语句。当设置为false时,构建过程会保留 CommonJS 模块中的 try-catch 语句 + ignoreTryCatch: false, + }, + // 规定触发警告的 chunk 大小, 当某个代码分块的大小超过该限制时,Vite 会发出警告 + chunkSizeWarningLimit: 2000, + rollupOptions: { + input: { + index: pathResolve('../index.html', import.meta.url), + }, + // 静态资源分类打包 + output: { + chunkFileNames: 'static/js/[name]-[hash].js', + entryFileNames: 'static/js/[name]-[hash].js', + assetFileNames: 'static/[ext]/[name]-[hash].[ext]', + manualChunks: id => { + // 如果是包含在包中则打包成 vendor + if (id.includes('node_modules')) { + return 'vendor'; + } + }, + }, + }, + }; + + return environment; +}; diff --git a/build/cdn.ts b/build/cdn.ts index 9e4bfe0..9284a32 100644 --- a/build/cdn.ts +++ b/build/cdn.ts @@ -1,4 +1,4 @@ -import { Plugin as importToCDN } from "vite-plugin-cdn-import"; +import { Plugin as importToCDN } from 'vite-plugin-cdn-import'; /** * @description 打包时采用`cdn`模式,仅限外网使用(默认不采用,如果需要采用cdn模式,请在 .env.production 文件,将 VITE_CDN 设置成true) @@ -6,55 +6,56 @@ import { Plugin as importToCDN } from "vite-plugin-cdn-import"; * 注意:上面提到的仅限外网使用也不是完全肯定的,如果你们公司内网部署的有相关js、css文件,也可以将下面配置对应改一下,整一套内网版cdn */ export const cdn = importToCDN({ - //(prodUrl解释: name: 对应下面modules的name,version: 自动读取本地package.json中dependencies依赖中对应包的版本号,path: 对应下面modules的path,当然也可写完整路径,会替换prodUrl) - prodUrl: "https://cdn.bootcdn.net/ajax/libs/{name}/{version}/{path}", - modules: [ - { - name: "vue", - var: "Vue", - path: "vue.global.prod.min.js" - }, - { - name: "vue-router", - var: "VueRouter", - path: "vue-router.global.min.js" - }, - { - name: "vue-i18n", - var: "VueI18n", - path: "vue-i18n.runtime.global.prod.min.js" - }, - // 项目中没有直接安装vue-demi,但是pinia用到了,所以需要在引入pinia前引入vue-demi(https://github.com/vuejs/pinia/blob/v2/packages/pinia/package.json#L77) - { - name: "vue-demi", - var: "VueDemi", - path: "index.iife.min.js" - }, - { - name: "pinia", - var: "Pinia", - path: "pinia.iife.min.js" - }, - { - name: "element-plus", - var: "ElementPlus", - path: "index.full.min.js", - css: "index.min.css" - }, - { - name: "axios", - var: "axios", - path: "axios.min.js" - }, - { - name: "dayjs", - var: "dayjs", - path: "dayjs.min.js" - }, - { - name: "echarts", - var: "echarts", - path: "echarts.min.js" - } - ] + //(prodUrl解释: name: 对应下面modules的name,version: 自动读取本地package.json中dependencies依赖中对应包的版本号,path: 对应下面modules的path,当然也可写完整路径,会替换prodUrl) + // prodUrl: 'https://cdn.bootcdn.net/ajax/libs/{name}/{version}/{path}', + prodUrl: 'https://unpkg.com/{name}@{version}/{path}', + modules: [ + { + name: 'vue', + var: 'Vue', + path: 'dist/vue.global.js', + }, + { + name: 'vue-router', + var: 'VueRouter', + path: 'dist/vue-router.global.js', + }, + { + name: 'vue-i18n', + var: 'VueI18n', + path: 'dist/vue-i18n.global.js', + }, + // 项目中没有直接安装vue-demi,但是pinia用到了,所以需要在引入pinia前引入vue-demi(https://github.com/vuejs/pinia/blob/v2/packages/pinia/package.json#L77) + { + name: 'vue-demi', + var: 'VueDemi', + path: 'lib/index.iife.js', + }, + { + name: 'pinia', + var: 'Pinia', + path: 'dist/pinia.iife.js', + }, + { + name: 'element-plus', + var: 'ElementPlus', + path: 'dist/index.full.js', + css: 'dist/index.css', + }, + { + name: 'axios', + var: 'axios', + path: 'dist/axios.min.js', + }, + { + name: 'dayjs', + var: 'dayjs', + path: 'dayjs.min.js', + }, + { + name: 'echarts', + var: 'echarts', + path: 'dist/echarts.js', + }, + ], }); diff --git a/build/compress.ts b/build/compress.ts index 6178986..323a702 100644 --- a/build/compress.ts +++ b/build/compress.ts @@ -1,63 +1,57 @@ -import type { Plugin } from "vite"; -import { isArray } from "@pureadmin/utils"; -import compressPlugin from "vite-plugin-compression"; +import type { Plugin } from 'vite'; +import { isArray } from '@pureadmin/utils'; +import compressPlugin from 'vite-plugin-compression'; -export const configCompressPlugin = ( - compress: ViteCompression -): Plugin | Plugin[] => { - if (compress === "none") return null; +export const configCompressPlugin = (compress: ViteCompression): Plugin | Plugin[] => { + if (compress === 'none') return null; - const gz = { - // 生成的压缩包后缀 - ext: ".gz", - // 体积大于threshold才会被压缩 - threshold: 0, - // 默认压缩.js|mjs|json|css|html后缀文件,设置成true,压缩全部文件 - filter: () => true, - // 压缩后是否删除原始文件 - deleteOriginFile: false - }; - const br = { - ext: ".br", - algorithm: "brotliCompress", - threshold: 0, - filter: () => true, - deleteOriginFile: false - }; + const gz = { + // 生成的压缩包后缀 + ext: '.gz', + // 体积大于threshold才会被压缩 + threshold: 0, + // 默认压缩.js|mjs|json|css|html后缀文件,设置成true,压缩全部文件 + filter: () => true, + // 压缩后是否删除原始文件 + deleteOriginFile: false, + }; + const br = { + ext: '.br', + algorithm: 'brotliCompress', + threshold: 0, + filter: () => true, + deleteOriginFile: false, + }; - const codeList = [ - { k: "gzip", v: gz }, - { k: "brotli", v: br }, - { k: "both", v: [gz, br] } - ]; + const codeList = [ + { k: 'gzip', v: gz }, + { k: 'brotli', v: br }, + { k: 'both', v: [gz, br] }, + ]; - const plugins: Plugin[] = []; + const plugins: Plugin[] = []; - codeList.forEach(item => { - if (compress.includes(item.k)) { - if (compress.includes("clear")) { - if (isArray(item.v)) { - item.v.forEach(vItem => { - plugins.push( - compressPlugin(Object.assign(vItem, { deleteOriginFile: true })) - ); - }); - } else { - plugins.push( - compressPlugin(Object.assign(item.v, { deleteOriginFile: true })) - ); - } - } else { - if (isArray(item.v)) { - item.v.forEach(vItem => { - plugins.push(compressPlugin(vItem)); - }); - } else { - plugins.push(compressPlugin(item.v)); - } - } - } - }); + codeList.forEach(item => { + if (compress.includes(item.k)) { + if (compress.includes('clear')) { + if (isArray(item.v)) { + item.v.forEach(vItem => { + plugins.push(compressPlugin(Object.assign(vItem, { deleteOriginFile: true }))); + }); + } else { + plugins.push(compressPlugin(Object.assign(item.v, { deleteOriginFile: true }))); + } + } else { + if (isArray(item.v)) { + item.v.forEach(vItem => { + plugins.push(compressPlugin(vItem)); + }); + } else { + plugins.push(compressPlugin(item.v)); + } + } + } + }); - return plugins; + return plugins; }; diff --git a/build/info.ts b/build/info.ts index 43140ab..5e1e40a 100644 --- a/build/info.ts +++ b/build/info.ts @@ -1,51 +1,51 @@ -import type { Plugin } from "vite"; -import { getPackageSize } from "./utils"; -import dayjs, { type Dayjs } from "dayjs"; -import duration from "dayjs/plugin/duration"; -import gradientString from "gradient-string"; -import boxen, { type Options as BoxenOptions } from "boxen"; +import type { Plugin } from 'vite'; +import { getPackageSize } from './utils'; +import dayjs, { type Dayjs } from 'dayjs'; +import duration from 'dayjs/plugin/duration'; +import gradientString from 'gradient-string'; +import boxen, { type Options as BoxenOptions } from 'boxen'; dayjs.extend(duration); -const welcomeMessage = gradientString("cyan", "magenta").multiline( - `您好! 欢迎使用 bunny-admin 后台管理 +const welcomeMessage = gradientString('cyan', 'magenta').multiline( + `您好! 欢迎使用 bunny-admin 后台管理 项目访问地址如下: -http://localhost:8848/` +http://localhost:8802/`, ); const boxenOptions: BoxenOptions = { - padding: 0.5, - borderColor: "cyan", - borderStyle: "round" + padding: 0.5, + borderColor: 'cyan', + borderStyle: 'round', }; export function viteBuildInfo(): Plugin { - let config: { command: string }; - let startTime: Dayjs; - let endTime: Dayjs; - let outDir: string; - return { - name: "vite:buildInfo", - configResolved(resolvedConfig) { - config = resolvedConfig; - outDir = resolvedConfig.build?.outDir ?? "dist"; - }, - buildStart() { - console.log(boxen(welcomeMessage, boxenOptions)); - if (config.command === "build") { - startTime = dayjs(new Date()); - } - }, - closeBundle() { - if (config.command === "build") { - endTime = dayjs(new Date()); - getPackageSize({ - folder: outDir, - callback: (size: string) => { - console.log(boxen(gradientString("cyan", "magenta").multiline(`🎉 恭喜打包完成(总用时${dayjs.duration(endTime.diff(startTime)).format("mm分ss秒")},打包后的大小为${size})`), boxenOptions)); - } - }); - } - } - }; + let config: { command: string }; + let startTime: Dayjs; + let endTime: Dayjs; + let outDir: string; + return { + name: 'vite:buildInfo', + configResolved(resolvedConfig) { + config = resolvedConfig; + outDir = resolvedConfig.build?.outDir ?? 'dist'; + }, + buildStart() { + console.log(boxen(welcomeMessage, boxenOptions)); + if (config.command === 'build') { + startTime = dayjs(new Date()); + } + }, + closeBundle() { + if (config.command === 'build') { + endTime = dayjs(new Date()); + getPackageSize({ + folder: outDir, + callback: (size: string) => { + console.log(boxen(gradientString('cyan', 'magenta').multiline(`🎉 恭喜打包完成(总用时${dayjs.duration(endTime.diff(startTime)).format('mm分ss秒')},打包后的大小为${size})`), boxenOptions)); + }, + }); + } + }, + }; } diff --git a/build/server.ts b/build/server.ts new file mode 100644 index 0000000..3f32a91 --- /dev/null +++ b/build/server.ts @@ -0,0 +1,29 @@ +import { loadEnv, type ServerOptions } from 'vite'; +import { root, wrapperEnv } from './utils'; + +export const serverOptions = (mode: string) => { + const { VITE_PORT, VITE_APP_URL } = wrapperEnv(loadEnv(mode, root)); + + const options: ServerOptions = { + port: VITE_PORT, // ? 端口号 + host: '0.0.0.0', + proxy: { + '/api': { + target: VITE_APP_URL, + changeOrigin: true, + rewrite: (path: string) => path.replace(/^\/api/, '/api'), + }, + '/mock': { + target: VITE_APP_URL, + changeOrigin: true, + rewrite: (path: string) => path.replace(/^\/mock/, '/mock'), + }, + }, + // 预热文件以提前转换和缓存结果,降低启动期间的初始页面加载时长并防止转换瀑布 + warmup: { + clientFiles: ['./index.html', './src/{views,components}/*'], + }, + }; + + return options; +}; diff --git a/build/utils.ts b/build/utils.ts index 3adf897..67ea689 100644 --- a/build/utils.ts +++ b/build/utils.ts @@ -34,7 +34,6 @@ const alias: Record = { '@': pathResolve('../src'), '@build': pathResolve(), }; - /** 平台的名称、版本、运行所需的`node`和`pnpm`版本、依赖、最后构建时间的类型提示 */ const __APP_INFO__ = { pkg: { name, version, engines, dependencies, devDependencies }, @@ -43,14 +42,12 @@ const __APP_INFO__ = { /** 处理环境变量 */ const wrapperEnv = (envConf: Recordable): ViteEnv => { - // 默认值 + // TODO 默认值 const ret: ViteEnv = { VITE_PORT: 8848, - VITE_PUBLIC_PATH: '', - VITE_ROUTER_HISTORY: '', - VITE_CDN: false, - VITE_HIDE_HOME: 'false', - VITE_COMPRESSION: 'none', + VITE_APP_URL: '', + VITE_COMPRESSION: 'gzip', + VITE_CDN: true, }; for (const envName of Object.keys(envConf)) { @@ -67,6 +64,7 @@ const wrapperEnv = (envConf: Recordable): ViteEnv => { process.env[envName] = JSON.stringify(realName); } } + return ret; }; diff --git a/package.json b/package.json index 7967a46..1985035 100644 --- a/package.json +++ b/package.json @@ -90,6 +90,7 @@ "responsive-storage": "^2.2.0", "sortablejs": "^1.15.2", "swiper": "^11.1.1", + "terser": "^5.31.0", "typeit": "^8.8.3", "v-contextmenu": "^3.2.0", "v3-infinite-loading": "^1.3.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 46918e1..49a6463 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -131,6 +131,9 @@ importers: swiper: specifier: ^11.1.1 version: 11.1.3 + terser: + specifier: ^5.31.0 + version: 5.31.0 typeit: specifier: ^8.8.3 version: 8.8.3 @@ -257,10 +260,10 @@ importers: version: 7.10.0(eslint@9.3.0)(typescript@5.4.5) '@vitejs/plugin-vue': specifier: ^5.0.4 - version: 5.0.4(vite@5.2.11(@types/node@20.12.12)(sass@1.77.2))(vue@3.4.27(typescript@5.4.5)) + version: 5.0.4(vite@5.2.11(@types/node@20.12.12)(sass@1.77.2)(terser@5.31.0))(vue@3.4.27(typescript@5.4.5)) '@vitejs/plugin-vue-jsx': specifier: ^3.1.0 - version: 3.1.0(vite@5.2.11(@types/node@20.12.12)(sass@1.77.2))(vue@3.4.27(typescript@5.4.5)) + version: 3.1.0(vite@5.2.11(@types/node@20.12.12)(sass@1.77.2)(terser@5.31.0))(vue@3.4.27(typescript@5.4.5)) autoprefixer: specifier: ^10.4.19 version: 10.4.19(postcss@8.4.38) @@ -356,13 +359,13 @@ importers: version: 5.4.5 vite: specifier: ^5.2.11 - version: 5.2.11(@types/node@20.12.12)(sass@1.77.2) + version: 5.2.11(@types/node@20.12.12)(sass@1.77.2)(terser@5.31.0) vite-plugin-cdn-import: specifier: ^0.3.5 version: 0.3.5(rollup@4.18.0) vite-plugin-compression: specifier: ^0.5.1 - version: 0.5.1(vite@5.2.11(@types/node@20.12.12)(sass@1.77.2)) + version: 0.5.1(vite@5.2.11(@types/node@20.12.12)(sass@1.77.2)(terser@5.31.0)) vite-plugin-fake-server: specifier: ^2.1.1 version: 2.1.1 @@ -1250,6 +1253,9 @@ packages: resolution: {integrity: sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==} engines: {node: '>=6.0.0'} + '@jridgewell/source-map@0.3.6': + resolution: {integrity: sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==} + '@jridgewell/sourcemap-codec@1.4.15': resolution: {integrity: sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==} @@ -2289,6 +2295,9 @@ packages: resolution: {integrity: sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==} engines: {node: '>=18'} + commander@2.20.3: + resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==} + commander@4.1.1: resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==} engines: {node: '>= 6'} @@ -5395,6 +5404,11 @@ packages: resolution: {integrity: sha512-un0FmiRUQNr5PJqy9kP7c40F5BOfpGlYTrxonDChEZB7pzZxRNp/bt+ymiy9/npwXya9KH99nJ/GXFIiUkYGFQ==} engines: {node: '>=8'} + terser@5.31.0: + resolution: {integrity: sha512-Q1JFAoUKE5IMfI4Z/lkE/E6+SwgzO+x4tq4v1AyBLRj8VSYvRO6A/rQrPg1yud4g0En9EKI1TvFRF2tQFcoUkg==} + engines: {node: '>=10'} + hasBin: true + test-exclude@6.0.0: resolution: {integrity: sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==} engines: {node: '>=8'} @@ -6376,7 +6390,7 @@ snapshots: lodash.merge: 4.6.2 lodash.uniq: 4.5.0 resolve-from: 5.0.0 - ts-node: 10.9.2(@types/node@20.5.1)(typescript@5.4.5) + ts-node: 10.9.2(@types/node@20.12.12)(typescript@5.4.5) typescript: 5.4.5 transitivePeerDependencies: - '@swc/core' @@ -6954,6 +6968,11 @@ snapshots: '@jridgewell/set-array@1.2.1': {} + '@jridgewell/source-map@0.3.6': + dependencies: + '@jridgewell/gen-mapping': 0.3.5 + '@jridgewell/trace-mapping': 0.3.25 + '@jridgewell/sourcemap-codec@1.4.15': {} '@jridgewell/trace-mapping@0.3.25': @@ -7394,19 +7413,19 @@ snapshots: '@uppy/utils': 4.1.3 nanoid: 3.3.7 - '@vitejs/plugin-vue-jsx@3.1.0(vite@5.2.11(@types/node@20.12.12)(sass@1.77.2))(vue@3.4.27(typescript@5.4.5))': + '@vitejs/plugin-vue-jsx@3.1.0(vite@5.2.11(@types/node@20.12.12)(sass@1.77.2)(terser@5.31.0))(vue@3.4.27(typescript@5.4.5))': dependencies: '@babel/core': 7.24.6 '@babel/plugin-transform-typescript': 7.24.6(@babel/core@7.24.6) '@vue/babel-plugin-jsx': 1.2.2(@babel/core@7.24.6) - vite: 5.2.11(@types/node@20.12.12)(sass@1.77.2) + vite: 5.2.11(@types/node@20.12.12)(sass@1.77.2)(terser@5.31.0) vue: 3.4.27(typescript@5.4.5) transitivePeerDependencies: - supports-color - '@vitejs/plugin-vue@5.0.4(vite@5.2.11(@types/node@20.12.12)(sass@1.77.2))(vue@3.4.27(typescript@5.4.5))': + '@vitejs/plugin-vue@5.0.4(vite@5.2.11(@types/node@20.12.12)(sass@1.77.2)(terser@5.31.0))(vue@3.4.27(typescript@5.4.5))': dependencies: - vite: 5.2.11(@types/node@20.12.12)(sass@1.77.2) + vite: 5.2.11(@types/node@20.12.12)(sass@1.77.2)(terser@5.31.0) vue: 3.4.27(typescript@5.4.5) '@volar/language-core@1.11.1': @@ -8180,6 +8199,8 @@ snapshots: commander@12.1.0: {} + commander@2.20.3: {} + commander@4.1.1: {} commander@7.2.0: {} @@ -8281,7 +8302,7 @@ snapshots: dependencies: '@types/node': 20.5.1 cosmiconfig: 8.3.6(typescript@5.4.5) - ts-node: 10.9.2(@types/node@20.5.1)(typescript@5.4.5) + ts-node: 10.9.2(@types/node@20.12.12)(typescript@5.4.5) typescript: 5.4.5 cosmiconfig-typescript-loader@5.0.0(@types/node@20.12.12)(cosmiconfig@9.0.0(typescript@5.4.5))(typescript@5.4.5): @@ -9699,7 +9720,7 @@ snapshots: slash: 3.0.0 strip-json-comments: 3.1.1 optionalDependencies: - ts-node: 10.9.2(@types/node@20.5.1)(typescript@5.4.5) + ts-node: 10.9.2(@types/node@20.12.12)(typescript@5.4.5) transitivePeerDependencies: - bufferutil - canvas @@ -10786,7 +10807,7 @@ snapshots: yaml: 2.4.2 optionalDependencies: postcss: 8.4.38 - ts-node: 10.9.2(@types/node@20.5.1)(typescript@5.4.5) + ts-node: 10.9.2(@types/node@20.12.12)(typescript@5.4.5) postcss-media-query-parser@0.2.3: {} @@ -11721,6 +11742,13 @@ snapshots: ansi-escapes: 4.3.2 supports-hyperlinks: 2.3.0 + terser@5.31.0: + dependencies: + '@jridgewell/source-map': 0.3.6 + acorn: 8.11.3 + commander: 2.20.3 + source-map-support: 0.5.21 + test-exclude@6.0.0: dependencies: '@istanbuljs/schema': 0.1.3 @@ -11796,14 +11824,14 @@ snapshots: ts-interface-checker@0.1.13: {} - ts-node@10.9.2(@types/node@20.5.1)(typescript@5.4.5): + ts-node@10.9.2(@types/node@20.12.12)(typescript@5.4.5): dependencies: '@cspotcode/source-map-support': 0.8.1 '@tsconfig/node10': 1.0.11 '@tsconfig/node12': 1.0.11 '@tsconfig/node14': 1.0.3 '@tsconfig/node16': 1.0.4 - '@types/node': 20.5.1 + '@types/node': 20.12.12 acorn: 8.11.3 acorn-walk: 8.3.2 arg: 4.1.3 @@ -11964,12 +11992,12 @@ snapshots: transitivePeerDependencies: - rollup - vite-plugin-compression@0.5.1(vite@5.2.11(@types/node@20.12.12)(sass@1.77.2)): + vite-plugin-compression@0.5.1(vite@5.2.11(@types/node@20.12.12)(sass@1.77.2)(terser@5.31.0)): dependencies: chalk: 4.1.2 debug: 4.3.4 fs-extra: 10.1.0 - vite: 5.2.11(@types/node@20.12.12)(sass@1.77.2) + vite: 5.2.11(@types/node@20.12.12)(sass@1.77.2)(terser@5.31.0) transitivePeerDependencies: - supports-color @@ -11990,7 +12018,7 @@ snapshots: svgo: 3.3.2 vue: 3.4.27(typescript@5.4.5) - vite@5.2.11(@types/node@20.12.12)(sass@1.77.2): + vite@5.2.11(@types/node@20.12.12)(sass@1.77.2)(terser@5.31.0): dependencies: esbuild: 0.20.2 postcss: 8.4.38 @@ -11999,6 +12027,7 @@ snapshots: '@types/node': 20.12.12 fsevents: 2.3.3 sass: 1.77.2 + terser: 5.31.0 vue-demi@0.14.7(vue@3.4.27(typescript@5.4.5)): dependencies: diff --git a/src/config/index.ts b/src/config/index.ts index c81d1c4..01689db 100644 --- a/src/config/index.ts +++ b/src/config/index.ts @@ -1,52 +1,51 @@ -import axios from "axios"; -import type { App } from "vue"; +import axios from 'axios'; +import type { App } from 'vue'; let config: object = {}; -const { VITE_PUBLIC_PATH } = import.meta.env; const setConfig = (cfg?: unknown) => { - config = Object.assign(config, cfg); + config = Object.assign(config, cfg); }; const getConfig = (key?: string): PlatformConfigs => { - if (typeof key === "string") { - const arr = key.split("."); - if (arr && arr.length) { - let data = config; - arr.forEach(v => { - if (data && typeof data[v] !== "undefined") { - data = data[v]; - } else { - data = null; - } - }); - return data; - } - } - return config; + if (typeof key === 'string') { + const arr = key.split('.'); + if (arr && arr.length) { + let data = config; + arr.forEach(v => { + if (data && typeof data[v] !== 'undefined') { + data = data[v]; + } else { + data = null; + } + }); + return data; + } + } + return config; }; /** 获取项目动态全局配置 */ export const getPlatformConfig = async (app: App): Promise => { - app.config.globalProperties.$config = getConfig(); - return axios({ - method: "get", - url: `${VITE_PUBLIC_PATH}platform-config.json` - }) - .then(({ data: config }) => { - let $config = app.config.globalProperties.$config; - // 自动注入系统配置 - if (app && $config && typeof config === "object") { - $config = Object.assign($config, config); - app.config.globalProperties.$config = $config; - // 设置全局配置 - setConfig($config); - } - return $config; - }) - .catch(() => { - throw "请在public文件夹下添加platform-config.json配置文件"; - }); + app.config.globalProperties.$config = getConfig(); + return axios({ + method: 'get', + url: `platform-config.json`, + }) + .then(({ data: config }) => { + let $config = app.config.globalProperties.$config; + // 自动注入系统配置 + if (app && $config && typeof config === 'object') { + $config = Object.assign($config, config); + app.config.globalProperties.$config = $config; + // 设置全局配置 + setConfig($config); + } + return $config; + }) + .catch(() => { + throw '请在public文件夹下添加platform-config.json配置文件'; + }); }; /** 本地响应式存储的命名空间 */ diff --git a/types/global.d.ts b/types/global.d.ts index 56b1d0e..6463c01 100644 --- a/types/global.d.ts +++ b/types/global.d.ts @@ -1,203 +1,201 @@ -import type { ECharts } from "echarts"; -import type { TableColumns } from "@pureadmin/table"; +import type { ECharts } from 'echarts'; +import type { TableColumns } from '@pureadmin/table'; /** * 全局类型声明,无需引入直接在 `.vue` 、`.ts` 、`.tsx` 文件使用即可获得类型提示 */ declare global { - /** - * 平台的名称、版本、运行所需的`node`和`pnpm`版本、依赖、最后构建时间的类型提示 - */ - const __APP_INFO__: { - pkg: { - name: string; - version: string; - engines: { - node: string; - pnpm: string; - }; - dependencies: Recordable; - devDependencies: Recordable; - }; - lastBuildTime: string; - }; + /** + * 平台的名称、版本、运行所需的`node`和`pnpm`版本、依赖、最后构建时间的类型提示 + */ + const __APP_INFO__: { + pkg: { + name: string; + version: string; + engines: { + node: string; + pnpm: string; + }; + dependencies: Recordable; + devDependencies: Recordable; + }; + lastBuildTime: string; + }; - /** - * Window 的类型提示 - */ - interface Window { - // Global vue app instance - __APP__: App; - webkitCancelAnimationFrame: (handle: number) => void; - mozCancelAnimationFrame: (handle: number) => void; - oCancelAnimationFrame: (handle: number) => void; - msCancelAnimationFrame: (handle: number) => void; - webkitRequestAnimationFrame: (callback: FrameRequestCallback) => number; - mozRequestAnimationFrame: (callback: FrameRequestCallback) => number; - oRequestAnimationFrame: (callback: FrameRequestCallback) => number; - msRequestAnimationFrame: (callback: FrameRequestCallback) => number; - } + /** + * Window 的类型提示 + */ + interface Window { + // Global vue app instance + __APP__: App; + webkitCancelAnimationFrame: (handle: number) => void; + mozCancelAnimationFrame: (handle: number) => void; + oCancelAnimationFrame: (handle: number) => void; + msCancelAnimationFrame: (handle: number) => void; + webkitRequestAnimationFrame: (callback: FrameRequestCallback) => number; + mozRequestAnimationFrame: (callback: FrameRequestCallback) => number; + oRequestAnimationFrame: (callback: FrameRequestCallback) => number; + msRequestAnimationFrame: (callback: FrameRequestCallback) => number; + } - /** - * Document 的类型提示 - */ - interface Document { - webkitFullscreenElement?: Element; - mozFullScreenElement?: Element; - msFullscreenElement?: Element; - } + /** + * Document 的类型提示 + */ + interface Document { + webkitFullscreenElement?: Element; + mozFullScreenElement?: Element; + msFullscreenElement?: Element; + } - /** - * 打包压缩格式的类型声明 - */ - type ViteCompression = "none" | "gzip" | "brotli" | "both" | "gzip-clear" | "brotli-clear" | "both-clear"; + /** + * 打包压缩格式的类型声明 + */ + type ViteCompression = 'none' | 'gzip' | 'brotli' | 'both' | 'gzip-clear' | 'brotli-clear' | 'both-clear'; - /** - * 全局自定义环境变量的类型声明 - * @see {@link https://pure-admin.github.io/pure-admin-doc/pages/config/#%E5%85%B7%E4%BD%93%E9%85%8D%E7%BD%AE} - */ - interface ViteEnv { - VITE_PORT: number; - VITE_PUBLIC_PATH: string; - VITE_ROUTER_HISTORY: string; - VITE_CDN: boolean; - VITE_HIDE_HOME: string; - VITE_COMPRESSION: ViteCompression; - } + /** + * TODO 全局自定义环境变量的类型声明 + * @see {@link https://pure-admin.github.io/pure-admin-doc/pages/config/#%E5%85%B7%E4%BD%93%E9%85%8D%E7%BD%AE} + */ + interface ViteEnv { + VITE_PORT: number; // 启动端口号 + VITE_APP_URL: string; // 跨域代理地址 + VITE_COMPRESSION: ViteCompression; + VITE_CDN: boolean; // 是否启用CDN + } - /** - * 继承 `@pureadmin/table` 的 `TableColumns` ,方便全局直接调用 - */ - interface TableColumnList extends Array {} + /** + * 继承 `@pureadmin/table` 的 `TableColumns` ,方便全局直接调用 + */ + interface TableColumnList extends Array {} - /** - * 对应 `public/platform-config.json` 文件的类型声明 - * @see {@link https://pure-admin.github.io/pure-admin-doc/pages/config/#platform-config-json} - */ - interface PlatformConfigs { - Version?: string; - Title?: string; - Copyright?: string; - FixedHeader?: boolean; - HiddenSideBar?: boolean; - MultiTagsCache?: boolean; - MaxTagsLevel?: number; - KeepAlive?: boolean; - Locale?: string; - Layout?: string; - Theme?: string; - DarkMode?: boolean; - OverallStyle?: string; - Grey?: boolean; - Weak?: boolean; - HideTabs?: boolean; - HideFooter?: boolean; - Stretch?: boolean | number; - SidebarStatus?: boolean; - EpThemeColor?: string; - ShowLogo?: boolean; - ShowModel?: string; - MenuArrowIconNoTransition?: boolean; - CachingAsyncRoutes?: boolean; - TooltipEffect?: Effect; - ResponsiveStorageNameSpace?: string; - MenuSearchHistory?: number; - MapConfigure?: { - amapKey?: string; - options: { - resizeEnable?: boolean; - center?: number[]; - zoom?: number; - }; - }; - } + /** + * 对应 `public/platform-config.json` 文件的类型声明 + * @see {@link https://pure-admin.github.io/pure-admin-doc/pages/config/#platform-config-json} + */ + interface PlatformConfigs { + Version?: string; + Title?: string; + Copyright?: string; + FixedHeader?: boolean; + HiddenSideBar?: boolean; + MultiTagsCache?: boolean; + MaxTagsLevel?: number; + KeepAlive?: boolean; + Locale?: string; + Layout?: string; + Theme?: string; + DarkMode?: boolean; + OverallStyle?: string; + Grey?: boolean; + Weak?: boolean; + HideTabs?: boolean; + HideFooter?: boolean; + Stretch?: boolean | number; + SidebarStatus?: boolean; + EpThemeColor?: string; + ShowLogo?: boolean; + ShowModel?: string; + MenuArrowIconNoTransition?: boolean; + CachingAsyncRoutes?: boolean; + TooltipEffect?: Effect; + ResponsiveStorageNameSpace?: string; + MenuSearchHistory?: number; + MapConfigure?: { + amapKey?: string; + options: { + resizeEnable?: boolean; + center?: number[]; + zoom?: number; + }; + }; + } - /** - * 与 `PlatformConfigs` 类型不同,这里是缓存到浏览器本地存储的类型声明 - * @see {@link https://pure-admin.github.io/pure-admin-doc/pages/config/#platform-config-json} - */ - interface StorageConfigs { - version?: string; - title?: string; - fixedHeader?: boolean; - hiddenSideBar?: boolean; - multiTagsCache?: boolean; - keepAlive?: boolean; - locale?: string; - layout?: string; - theme?: string; - darkMode?: boolean; - grey?: boolean; - weak?: boolean; - hideTabs?: boolean; - hideFooter?: boolean; - sidebarStatus?: boolean; - epThemeColor?: string; - themeColor?: string; - overallStyle?: string; - showLogo?: boolean; - showModel?: string; - menuSearchHistory?: number; - mapConfigure?: { - amapKey?: string; - options: { - resizeEnable?: boolean; - center?: number[]; - zoom?: number; - }; - }; - username?: string; - } + /** + * 与 `PlatformConfigs` 类型不同,这里是缓存到浏览器本地存储的类型声明 + * @see {@link https://pure-admin.github.io/pure-admin-doc/pages/config/#platform-config-json} + */ + interface StorageConfigs { + version?: string; + title?: string; + fixedHeader?: boolean; + hiddenSideBar?: boolean; + multiTagsCache?: boolean; + keepAlive?: boolean; + locale?: string; + layout?: string; + theme?: string; + darkMode?: boolean; + grey?: boolean; + weak?: boolean; + hideTabs?: boolean; + hideFooter?: boolean; + sidebarStatus?: boolean; + epThemeColor?: string; + themeColor?: string; + overallStyle?: string; + showLogo?: boolean; + showModel?: string; + menuSearchHistory?: number; + mapConfigure?: { + amapKey?: string; + options: { + resizeEnable?: boolean; + center?: number[]; + zoom?: number; + }; + }; + username?: string; + } - /** - * `responsive-storage` 本地响应式 `storage` 的类型声明 - */ - interface ResponsiveStorage { - locale: { - locale?: string; - }; - layout: { - layout?: string; - theme?: string; - darkMode?: boolean; - sidebarStatus?: boolean; - epThemeColor?: string; - themeColor?: string; - overallStyle?: string; - }; - configure: { - grey?: boolean; - weak?: boolean; - hideTabs?: boolean; - hideFooter?: boolean; - showLogo?: boolean; - showModel?: string; - multiTagsCache?: boolean; - stretch?: boolean | number; - }; - tags?: Array; - } + /** + * `responsive-storage` 本地响应式 `storage` 的类型声明 + */ + interface ResponsiveStorage { + locale: { + locale?: string; + }; + layout: { + layout?: string; + theme?: string; + darkMode?: boolean; + sidebarStatus?: boolean; + epThemeColor?: string; + themeColor?: string; + overallStyle?: string; + }; + configure: { + grey?: boolean; + weak?: boolean; + hideTabs?: boolean; + hideFooter?: boolean; + showLogo?: boolean; + showModel?: string; + multiTagsCache?: boolean; + stretch?: boolean | number; + }; + tags?: Array; + } - /** - * 平台里所有组件实例都能访问到的全局属性对象的类型声明 - */ - interface GlobalPropertiesApi { - $echarts: ECharts; - $storage: ResponsiveStorage; - $config: PlatformConfigs; - } + /** + * 平台里所有组件实例都能访问到的全局属性对象的类型声明 + */ + interface GlobalPropertiesApi { + $echarts: ECharts; + $storage: ResponsiveStorage; + $config: PlatformConfigs; + } - /** - * 扩展 `Element` - */ - interface Element { - // v-ripple 作用于 src/directives/ripple/index.ts 文件 - _ripple?: { - enabled?: boolean; - centered?: boolean; - class?: string; - circle?: boolean; - touched?: boolean; - }; - } + /** + * 扩展 `Element` + */ + interface Element { + // v-ripple 作用于 src/directives/ripple/index.ts 文件 + _ripple?: { + enabled?: boolean; + centered?: boolean; + class?: string; + circle?: boolean; + touched?: boolean; + }; + } } diff --git a/vite.config.ts b/vite.config.ts index 95fac64..88b3d02 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -1,52 +1,28 @@ import { getPluginsList } from './build/plugins'; -import { include, exclude } from './build/optimize'; -import { type UserConfigExport, type ConfigEnv, loadEnv } from 'vite'; -import { root, alias, wrapperEnv, pathResolve, __APP_INFO__ } from './build/utils'; +import { exclude, include } from './build/optimize'; +import { type ConfigEnv, loadEnv, type UserConfigExport } from 'vite'; +import { __APP_INFO__, alias, root, wrapperEnv } from './build/utils'; +import { buildEnvironment } from './build/buildEnv'; +import { serverOptions } from './build/server'; // Vite中文文档:https://cn.vitejs.dev/guide/build#browser-compatibility +// Vite中文文档:https://cn.vitejs.dev/guide/build#browser-compatibility export default ({ mode }: ConfigEnv): UserConfigExport => { - const { VITE_CDN, VITE_PORT, VITE_COMPRESSION, VITE_PUBLIC_PATH } = wrapperEnv(loadEnv(mode, root)); + const { VITE_CDN, VITE_COMPRESSION } = wrapperEnv(loadEnv(mode, root)); return { - base: VITE_PUBLIC_PATH, root, - resolve: { - alias, - }, + resolve: { alias }, // 服务端渲染 - server: { - // 端口号 - port: VITE_PORT, - host: '0.0.0.0', - // 本地跨域代理 https://cn.vitejs.dev/config/server-options.html#server-proxy - proxy: {}, - // 预热文件以提前转换和缓存结果,降低启动期间的初始页面加载时长并防止转换瀑布 - warmup: { - clientFiles: ['./index.html', './src/{views,components}/*'], - }, - }, + server: serverOptions(mode), + // 插件相关 plugins: getPluginsList(VITE_CDN, VITE_COMPRESSION), - // https://cn.vitejs.dev/config/dep-optimization-options.html#dep-optimization-options - optimizeDeps: { - include, - exclude, - }, - build: { - // https://cn.vitejs.dev/guide/build.html#browser-compatibility - target: 'es2015', - sourcemap: false, - // 消除打包大小超过500kb警告 - chunkSizeWarningLimit: 4000, - rollupOptions: { - input: { - index: pathResolve('./index.html', import.meta.url), - }, - // 静态资源分类打包 - output: { - chunkFileNames: 'static/js/[name]-[hash].js', - entryFileNames: 'static/js/[name]-[hash].js', - assetFileNames: 'static/[ext]/[name]-[hash].[ext]', - }, - }, + optimizeDeps: { include, exclude }, + esbuild: { + pure: ['console.log', 'debugger'], + jsxFactory: 'h', + jsxFragment: 'Fragment', + jsxInject: "import { h } from 'vue';", }, + build: buildEnvironment(), define: { __INTLIFY_PROD_DEVTOOLS__: false, __APP_INFO__: JSON.stringify(__APP_INFO__),