feat: 添加环境变量枚举

This commit is contained in:
Bunny 2025-02-24 22:45:14 +08:00
parent 5f5f1e3c6f
commit e3c5d7333a
24 changed files with 1610 additions and 108 deletions

21
.dockerignore Normal file
View File

@ -0,0 +1,21 @@
node_modules
.DS_Store
dist
dist-ssr
*.local
.eslintcache
report.html
yarn.lock
npm-debug.log*
.pnpm-error.log*
.pnpm-debug.log
tests/**/coverage/
# Editor directories and files
.idea
*.suo
*.ntvs*
*.njsproj
*.sln
tsconfig.tsbuildinfo

8
.env
View File

@ -11,4 +11,10 @@ VITE_PUBLIC_PATH=/
VITE_APP_URL=http://localhost:8801
# 如果端口被占用会直接退出,而不是尝试下一个端口
VITE_STRICT_PORT=false
VITE_STRICT_PORT=false
# 是否在打包时使用cdn替换本地库 替换 true 不替换 false
VITE_CDN=false
# 是否启用gzip压缩
VITE_COMPRESSION=gzip

View File

@ -8,4 +8,7 @@ VITE_PUBLIC_PATH=/
VITE_APP_URL=http://localhost:8801
# 如果端口被占用会直接退出,而不是尝试下一个端口
VITE_STRICT_PORT=false
VITE_STRICT_PORT=false
# 是否在打包时使用cdn替换本地库 替换 true 不替换 false
VITE_CDN=gzip

View File

@ -1,11 +1,14 @@
# 平台本地运行端口号
VITE_PORT=7000
VITE_PORT=8888
# 开发环境读取配置文件路径
VITE_PUBLIC_PATH=/
# 跨域代理地址
VITE_APP_URL=http://localhost:8801
VITE_APP_URL=http://localhost:8000
# 如果端口被占用会直接退出,而不是尝试下一个端口
VITE_STRICT_PORT=false
VITE_STRICT_PORT=false
# 是否在打包时使用cdn替换本地库 替换 true 不替换 false
VITE_CDN=gzip

View File

@ -2,7 +2,7 @@
export default {
// 超过最大值换行
printWidth: 200,
printWidth: 140,
// 缩进字节数
tabWidth: 1,
// 使用制表符而不是空格缩进行

21
LICENSE Normal file
View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2024 Bunny
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@ -1,4 +1,4 @@
import { BuildOptions } from 'vite';
import type { BuildOptions } from 'vite';
import { pathResolve } from './utils';
export const buildEnv = (): BuildOptions => {
@ -9,7 +9,7 @@ export const buildEnv = (): BuildOptions => {
outDir: 'docker/dist',
// 用于指定使用的代码压缩工具。在这里minify 被设置为 'terser',表示使用 Terser 进行代码压缩。默认值terser
// esbuild 打包更快,但是不能去除 console.logterser打包慢但能去除 console.log
minify: 'terser',
minify: 'terser', // "esbuild"
// 用于配置 Terser 的选项
terserOptions: {
// 用于配置压缩选项
@ -43,7 +43,6 @@ export const buildEnv = (): BuildOptions => {
// 如果是包含在包中则打包成 vendor
if (id.includes('node_modules')) {
return `vendor`;
// return id.toString().split('node_modules/')[1].split('/')[1].toString();
}
},
},

68
build/cdn.ts Normal file
View File

@ -0,0 +1,68 @@
import { Plugin as importToCDN } from 'vite-plugin-cdn-import';
import { wrapperEnv } from './utils';
/**
* @description `cdn`使cdn模式 .env.production VITE_CDN true
* cdnhttps://www.bootcdn.cn当然你也可以选择 https://unpkg.com 或者 https://www.jsdelivr.com
* 使jscss文件cdn
*/
export const cdn = importToCDN({
//prodUrl解释 name: 对应下面modules的nameversion: 自动读取本地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.prod.js',
},
{
name: 'vue-router',
var: 'VueRouter',
path: 'dist/vue-router.global.js',
},
// {
// name: 'vue-i18n',
// var: 'VueI18n',
// path: 'dist/vue-i18n.global.prod.js',
// },
// {
// 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.min.js',
},
],
});
/* 是否使用CDN加速 */
export const useCDN = mode => {
const { VITE_CDN } = wrapperEnv(mode);
return VITE_CDN ? cdn : null;
};

View File

@ -1,6 +1,3 @@
export const define = () => {
return {
}
}
return {};
};

50
build/info.ts Normal file
View File

@ -0,0 +1,50 @@
import { wrapperEnv } from './utils';
import dayjs, { type Dayjs } from 'dayjs';
import gradientString from 'gradient-string';
import duration from 'dayjs/plugin/duration';
import boxen, { type Options as BoxenOptions } from 'boxen';
dayjs.extend(duration);
const boxenOptions: BoxenOptions = {
padding: 0.94,
borderColor: 'cyan',
borderStyle: 'round',
textAlignment: 'left',
};
/* 输出日志信息 */
const printLogMessage = (VITE_PORT: number) => {
return gradientString('cyan', 'magenta').multiline(
`保存成功!服务器重新启动
访
http://localhost:${VITE_PORT}`,
);
};
export const viteConsoleLog = mode => {
const { VITE_PORT } = wrapperEnv(mode);
let config: { command: string };
let startTime: Dayjs;
let endTime: Dayjs;
return {
name: 'vite:buildInfo',
configResolved(resolvedConfig) {
config = resolvedConfig;
},
buildStart() {
console.log(boxen(printLogMessage(VITE_PORT), boxenOptions));
if (config.command === 'build') {
startTime = dayjs(new Date());
}
},
closeBundle() {
if (config.command === 'build') {
endTime = dayjs(new Date());
const format = dayjs.duration(endTime.diff(startTime)).format('mm分ss秒');
console.log(boxen(gradientString('cyan', 'magenta').multiline(`🎉 恭喜打包完成(总用时${format}`), boxenOptions));
}
},
};
};

View File

@ -1,10 +1,12 @@
import vue from "@vitejs/plugin-vue";
import {PluginOption} from "vite";
import vue from '@vitejs/plugin-vue';
import type { PluginOption } from 'vite';
import vueJsx from '@vitejs/plugin-vue-jsx';
import Inspector from 'vite-plugin-vue-inspector';
import { compressPack, report } from './utils';
import removeConsole from 'vite-plugin-remove-console';
import { useCDN } from './cdn';
import { viteConsoleLog } from './info';
export const plugins = (): PluginOption[] => {
return [
vue()
]
}
export const plugins = (mode): PluginOption[] => {
return [vue(), vueJsx(), Inspector(), report(), removeConsole(), useCDN(mode), viteConsoleLog(mode), compressPack(mode)];
};

View File

@ -1,6 +1,7 @@
import { type ServerOptions } from 'vite';
import type { ServerOptions } from 'vite';
import { wrapperEnv } from './utils';
/* 开发服务配置 */
export const server = mode => {
const { VITE_PORT, VITE_APP_URL, VITE_STRICT_PORT } = wrapperEnv(mode);

View File

@ -1,6 +1,8 @@
import { dirname, resolve } from 'node:path';
import { fileURLToPath } from 'node:url';
import { loadEnv } from 'vite';
import { visualizer } from 'rollup-plugin-visualizer';
import viteCompression from 'vite-plugin-compression';
export const root: string = process.cwd();
@ -31,7 +33,20 @@ export const pathResolve = (dir = '.', metaUrl = import.meta.url) => {
* @param prefix
* @link https://cn.vite.dev/config/#using-environment-variables-in-config
*/
export const wrapperEnv = (mode, prefix = '') => {
export const wrapperEnv = (mode, prefix = ''): ViteEnv => {
const env = loadEnv(mode, root, prefix);
return env;
};
/* 打包分析 */
export const report = () => {
const lifecycle = process.env.npm_lifecycle_event;
return lifecycle === 'report' ? visualizer({ open: true, brotliSize: true, filename: 'report.html' }) : (null as any);
};
/* 启用gzip压缩 */
export const compressPack = mode => {
const { VITE_COMPRESSION } = wrapperEnv(mode);
return VITE_COMPRESSION == 'gzip' ? viteCompression({ threshold: 1024000 }) : null;
};

31
docker/Dockerfile Normal file
View File

@ -0,0 +1,31 @@
# 使用官方的 Nginx 镜像作为基础镜像
FROM nginx:1.27.3
# 删除默认的 Nginx 配置文件
RUN rm /etc/nginx/conf.d/default.conf
# 将自定义的 Nginx 配置文件复制到容器中
COPY nginx.conf /etc/nginx/conf.d/default.conf
#COPY bunny-web.site.csr /etc/nginx/bunny-web.site.csr
#COPY bunny-web.site.key /etc/nginx/bunny-web.site.key
#COPY bunny-web.site_bundle.crt /etc/nginx/bunny-web.site_bundle.crt
#COPY bunny-web.site_bundle.pem /etc/nginx/bunny-web.site_bundle.pem
# 设置时区,构建镜像时执行的命令
RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
RUN echo "Asia/Shanghai" > /etc/timezone
# 创建一个目录来存放前端项目文件
WORKDIR /usr/share/nginx/html
# 将前端项目打包文件复制到 Nginx 的默认静态文件目录
COPY dist/ /usr/share/nginx/html
# 复制到nginx目录下
COPY dist/ /etc/nginx/html
# 暴露 Nginx 的默认端口
EXPOSE 80
#EXPOSE 443
# 自动启动 Nginx
CMD ["nginx", "-g", "daemon off;"]

32
docker/nginx.conf Normal file
View File

@ -0,0 +1,32 @@
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
server {
listen 80 ;
listen [::]:80;
server_name localhost;
client_max_body_size 5M; # 最大文件上传设置
location / {
root /etc/nginx/html;
index index.html index.htm;
try_files $uri /index.html;
}
# 后端跨域请求
location ~/api/ {
proxy_pass http://172.17.0.1:8000;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
error_page 404 404.html;
location = /50x.html {
root html;
}
}

View File

@ -5,12 +5,17 @@
"type": "module",
"scripts": {
"dev": "vite --host",
"build": "rimraf dist && NODE_OPTIONS=--max-old-space-size=8192 vite build && generate-version-file",
"preview": "vite preview"
"build": "vite build",
"preview": "vite preview",
"report": "rimraf dist && vite build"
},
"dependencies": {
"@typescript-eslint/eslint-plugin": "^8.24.1",
"@typescript-eslint/parser": "^8.24.1",
"@vitejs/plugin-vue-jsx": "^4.1.1",
"animate.css": "^4.1.1",
"axios": "^1.7.9",
"boxen": "^8.0.1",
"dayjs": "^1.11.13",
"echarts": "^5.5.1",
"eslint": "^9.9.1",
@ -18,13 +23,19 @@
"eslint-define-config": "^2.1.0",
"eslint-plugin-prettier": "^5.2.1",
"eslint-plugin-vue": "^9.27.0",
"gradient-string": "^3.0.0",
"js-cookie": "^3.0.5",
"list": "^2.0.19",
"pinia": "^2.3.1",
"pinia-plugin-persistedstate": "^3.2.3",
"prettier": "^3.3.3",
"rimraf": "^5.0.10",
"rollup-plugin-visualizer": "^5.14.0",
"sass": "^1.77.8",
"terser": "^5.39.0",
"vite-plugin-cdn-import": "^1.0.1",
"vite-plugin-remove-console": "^2.2.0",
"vite-plugin-vue-inspector": "^5.3.1",
"vue": "^3.5.13",
"vue-router": "^4.4.3",
"vue-types": "^6.0.0"
@ -34,6 +45,25 @@
"@vue/tsconfig": "^0.7.0",
"typescript": "~5.7.2",
"vite": "^6.1.0",
"vite-plugin-compression": "^0.5.1",
"vue-tsc": "^2.2.0"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0",
"pnpm": ">=8.6.10"
},
"pnpm": {
"allowedDeprecatedVersions": {
"sourcemap-codec": "*",
"domexception": "*",
"w3c-hr-time": "*",
"stable": "*",
"abab": "*"
},
"peerDependencyRules": {
"allowedVersions": {
"eslint": "9"
}
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -1,30 +1,32 @@
<script setup lang="ts">
import HelloWorld from './components/HelloWorld.vue'
<script lang="ts" setup>
import HelloWorld from './components/HelloWorld.vue';
</script>
<template>
<div>
<a href="https://vite.dev" target="_blank">
<img src="/vite.svg" class="logo" alt="Vite logo" />
</a>
<a href="https://vuejs.org/" target="_blank">
<img src="./assets/vue.svg" class="logo vue" alt="Vue logo" />
</a>
</div>
<HelloWorld msg="Vite + Vue" />
<div>
<a href="https://vite.dev" target="_blank">
<img alt="Vite logo" class="logo" src="/vite.svg" />
</a>
<a href="https://vuejs.org/" target="_blank">
<img alt="Vue logo" class="logo vue" src="./assets/vue.svg" />
</a>
</div>
<HelloWorld msg="Vite + Vue" />
</template>
<style scoped>
.logo {
height: 6em;
padding: 1.5em;
will-change: filter;
transition: filter 300ms;
height: 6em;
padding: 1.5em;
will-change: filter;
transition: filter 300ms;
}
.logo:hover {
filter: drop-shadow(0 0 2em #646cffaa);
filter: drop-shadow(0 0 2em #646cffaa);
}
.logo.vue:hover {
filter: drop-shadow(0 0 2em #42b883aa);
filter: drop-shadow(0 0 2em #42b883aa);
}
</style>

View File

@ -1,5 +1,5 @@
import { createApp } from 'vue'
import './style.css'
import App from './App.vue'
import { createApp } from 'vue';
import './style.css';
import App from './App.vue';
createApp(App).mount('#app')
createApp(App).mount('#app');

View File

@ -1,79 +1,84 @@
:root {
font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif;
line-height: 1.5;
font-weight: 400;
font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif;
line-height: 1.5;
font-weight: 400;
color-scheme: light dark;
color: rgba(255, 255, 255, 0.87);
background-color: #242424;
color-scheme: light dark;
color: rgba(255, 255, 255, 0.87);
background-color: #242424;
font-synthesis: none;
text-rendering: optimizeLegibility;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
font-synthesis: none;
text-rendering: optimizeLegibility;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
a {
font-weight: 500;
color: #646cff;
text-decoration: inherit;
font-weight: 500;
color: #646cff;
text-decoration: inherit;
}
a:hover {
color: #535bf2;
color: #535bf2;
}
body {
margin: 0;
display: flex;
place-items: center;
min-width: 320px;
min-height: 100vh;
margin: 0;
display: flex;
place-items: center;
min-width: 320px;
min-height: 100vh;
}
h1 {
font-size: 3.2em;
line-height: 1.1;
font-size: 3.2em;
line-height: 1.1;
}
button {
border-radius: 8px;
border: 1px solid transparent;
padding: 0.6em 1.2em;
font-size: 1em;
font-weight: 500;
font-family: inherit;
background-color: #1a1a1a;
cursor: pointer;
transition: border-color 0.25s;
border-radius: 8px;
border: 1px solid transparent;
padding: 0.6em 1.2em;
font-size: 1em;
font-weight: 500;
font-family: inherit;
background-color: #1a1a1a;
cursor: pointer;
transition: border-color 0.25s;
}
button:hover {
border-color: #646cff;
border-color: #646cff;
}
button:focus,
button:focus-visible {
outline: 4px auto -webkit-focus-ring-color;
outline: 4px auto -webkit-focus-ring-color;
}
.card {
padding: 2em;
padding: 2em;
}
#app {
max-width: 1280px;
margin: 0 auto;
padding: 2rem;
text-align: center;
max-width: 1280px;
margin: 0 auto;
padding: 2rem;
text-align: center;
}
@media (prefers-color-scheme: light) {
:root {
color: #213547;
background-color: #ffffff;
}
a:hover {
color: #747bff;
}
button {
background-color: #f9f9f9;
}
:root {
color: #213547;
background-color: #ffffff;
}
a:hover {
color: #747bff;
}
button {
background-color: #f9f9f9;
}
}

12
src/types/global.d.ts vendored Normal file
View File

@ -0,0 +1,12 @@
declare global {
/* 环境便配置 */
interface ViteEnv {
VITE_APP_TITLE: string;
VITE_PORT: number;
VITE_PUBLIC_PATH: string;
VITE_APP_URL: string;
VITE_STRICT_PORT: boolean;
VITE_CDN: boolean;
VITE_COMPRESSION: string;
}
}

View File

@ -1,7 +1,24 @@
{
"files": [],
"references": [
{ "path": "./tsconfig.app.json" },
{ "path": "./tsconfig.node.json" }
{
"path": "./tsconfig.app.json"
},
{
"path": "./tsconfig.node.json"
}
],
"include": [
"mock/*.ts",
"src/**/*.ts",
"src/**/*.tsx",
"src/**/*.vue",
"src/types/*.d.ts",
"vite.config.ts"
],
"exclude": [
"dist",
"**/*.js",
"node_modules"
]
}

View File

@ -9,14 +9,13 @@ import { exclude, include } from './build/optimize';
// https://vite.dev/config/
export default defineConfig(({ command, mode, isSsrBuild, isPreview }) => {
// 加载前面带有 VITE 的环境变量
const env = wrapperEnv(mode, 'VITE');
return {
root,
base: env.VITE_PUBLIC_PATH,
define: define(),
plugins: plugins(),
plugins: plugins(mode),
resolve: resolve(),
esbuild: {
jsxFactory: 'h',