├── .env ├── .env.main ├── .env.minor ├── .eslintignore ├── .eslintrc.js ├── .gitignore ├── .husky ├── .gitignore ├── commit-msg └── pre-commit ├── .prettierignore ├── .prettierrc.js ├── .vscode └── extensions.json ├── LICENSE ├── README.md ├── commitlint.config.js ├── index.html ├── package-lock.json ├── package.json ├── public └── favicon.ico ├── src ├── assets │ ├── cdv1.png │ ├── logo.png │ └── style │ │ ├── global.less │ │ └── main.less ├── components │ ├── HelloWorld.vue │ ├── SvgIcon.vue │ ├── VueUse.vue │ └── nprogress │ │ └── index.ts ├── env.d.ts ├── modules │ ├── main │ │ ├── App.vue │ │ ├── assets │ │ │ ├── icons │ │ │ │ ├── collapse-left.svg │ │ │ │ ├── collapse-right.svg │ │ │ │ ├── exit-full-screen.svg │ │ │ │ └── full-screen.svg │ │ │ └── style │ │ │ │ ├── global.less │ │ │ │ └── main.less │ │ ├── components │ │ │ ├── header │ │ │ │ ├── BasicHeader.vue │ │ │ │ ├── components │ │ │ │ │ └── Breadcrumb.vue │ │ │ │ └── index.ts │ │ │ └── menu │ │ │ │ ├── BasicMenu.vue │ │ │ │ └── index.ts │ │ ├── index.html │ │ ├── main.ts │ │ ├── router │ │ │ ├── index.ts │ │ │ └── modules │ │ │ │ ├── async.ts │ │ │ │ └── constant.ts │ │ ├── store │ │ │ ├── index.ts │ │ │ └── user.ts │ │ ├── typings │ │ │ └── global.d.ts │ │ ├── utils │ │ │ └── nav.ts │ │ └── view │ │ │ ├── example │ │ │ ├── HelloWorld.vue │ │ │ ├── Test.vue │ │ │ ├── VirtualTable.vue │ │ │ └── VueUse.vue │ │ │ ├── home │ │ │ └── Home.vue │ │ │ └── layout │ │ │ └── PageLayout.vue │ └── minor │ │ ├── App.vue │ │ ├── index.html │ │ └── main.ts ├── service │ ├── api │ │ ├── interface.ts │ │ ├── mock-data.ts │ │ └── mock.ts │ ├── helper │ │ ├── axios-cancel.ts │ │ ├── check-status.ts │ │ └── enum.ts │ └── index.ts └── utils │ ├── event.ts │ └── is.ts ├── tsconfig.json ├── vite.config.ts └── yarn.lock /.env: -------------------------------------------------------------------------------- 1 | # 根路径 2 | VITE_APP_ROOTPATH = ./ 3 | 4 | # 线上环境接口地址(easymock) 5 | VITE_API_URL = "https://mock.mengxuegu.com/mock/63856c7c9433403d6c06899c/cnhis" 6 | -------------------------------------------------------------------------------- /.env.main: -------------------------------------------------------------------------------- 1 | # app模块 2 | VITE_APP_MODEL = main 3 | 4 | # 根路径 5 | VITE_APP_ROOTPATH = ./src/modules/main/ 6 | 7 | # 线上环境接口地址(easymock) 8 | VITE_API_URL = "https://mock.mengxuegu.com/mock/63856c7c9433403d6c06899c/cnhis" -------------------------------------------------------------------------------- /.env.minor: -------------------------------------------------------------------------------- 1 | # app模块 2 | VITE_APP_MODEL = minor 3 | 4 | # 根路径 5 | VITE_APP_ROOTPATH = ./src/modules/minor/ -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | env: { 4 | browser: true, 5 | node: true, 6 | es2021: true, 7 | }, 8 | parser: "vue-eslint-parser", 9 | extends: [ 10 | "eslint:recommended", 11 | "plugin:vue/vue3-recommended", 12 | "plugin:@typescript-eslint/recommended", 13 | "plugin:prettier/recommended", 14 | "prettier", 15 | ], 16 | parserOptions: { 17 | ecmaVersion: 12, 18 | parser: "@typescript-eslint/parser", 19 | sourceType: "module", 20 | ecmaFeatures: { 21 | jsx: true, 22 | }, 23 | }, 24 | 25 | // eslint-plugin-vue @typescript-eslint/eslint-plugin eslint-plugin-prettier的缩写 26 | plugins: ["vue", "@typescript-eslint", "prettier"], 27 | rules: { 28 | "@typescript-eslint/ban-ts-ignore": "off", 29 | "@typescript-eslint/no-unused-vars": "off", 30 | "@typescript-eslint/explicit-function-return-type": "off", 31 | "@typescript-eslint/no-explicit-any": "off", 32 | "@typescript-eslint/no-var-requires": "off", 33 | "@typescript-eslint/no-empty-function": "off", 34 | "@typescript-eslint/no-use-before-define": "off", 35 | "@typescript-eslint/ban-ts-comment": "off", 36 | "@typescript-eslint/ban-types": "off", 37 | "@typescript-eslint/no-non-null-assertion": "off", 38 | "@typescript-eslint/explicit-module-boundary-types": "off", 39 | "vue/multi-word-component-names": "off", 40 | "no-console": process.env.NODE_ENV === "production" ? "error" : "off", 41 | "no-debugger": process.env.NODE_ENV === "production" ? "error" : "off", 42 | }, 43 | globals: { 44 | defineProps: "readonly", 45 | defineEmits: "readonly", 46 | defineExpose: "readonly", 47 | withDefaults: "readonly", 48 | }, 49 | }; 50 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .DS_Store 3 | dist 4 | dist-ssr 5 | *.local 6 | *.log 7 | -------------------------------------------------------------------------------- /.husky/.gitignore: -------------------------------------------------------------------------------- 1 | _ 2 | -------------------------------------------------------------------------------- /.husky/commit-msg: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | . "$(dirname "$0")/_/husky.sh" 3 | 4 | npx --no-install commitlint --edit "$1" 5 | -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | . "$(dirname "$0")/_/husky.sh" 3 | 4 | npx lint-staged 5 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | # 忽略格式化文件 (根据项目需要自行添加) 2 | node_modules 3 | dist -------------------------------------------------------------------------------- /.prettierrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | arrowParens: "avoid", // 只有一个参数的箭头函数的参数是否带圆括号(默认avoid) 3 | singleQuote: false, // 是否单引号 4 | semi: true, // 声明结尾使用分号(默认true) 5 | printWidth: 100, // 一行的字符数,超过会换行(默认80) 6 | tabWidth: 2, // 每个tab相当于多少个空格(默认2) 7 | useTabs: false, // 是否使用tab进行缩进(默认false) 8 | quoteProps: "as-needed", 9 | bracketSpacing: true, // 对象字面量的大括号间使用空格(默认true) 10 | jsxBracketSameLine: false, // 多行JSX中的>放置在最后一行的结尾,而不是另起一行(默认false) 11 | jsxSingleQuote: false, 12 | htmlWhitespaceSensitivity: "ignore", 13 | overrides: [ 14 | { 15 | files: "*.json", 16 | options: { 17 | printWidth: 200, 18 | }, 19 | }, 20 | ], 21 | }; 22 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": ["johnsoncodehk.volar"] 3 | } 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | ISC License 2 | 3 | Copyright (c) 2004-2010 by Internet Systems Consortium, Inc. ("ISC") 4 | 5 | Copyright (c) 1995-2003 by Internet Software Consortium 6 | 7 | Permission to use, copy, modify, and /or distribute this software for any 8 | purpose with or without fee is hereby granted, provided that the above copyright 9 | notice and this permission notice appear in all copies. 10 | 11 | THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH REGARD 12 | TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. 13 | IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL 14 | DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, 15 | WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING 16 | OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## 前言 2 | 3 | 本项目是一个 vue3+vite+ts 的多页面(MPA)模板,支持单独模块打包,支持本地开发只编译单独模块,同时,也支持多模块一起打包和本地编译。 4 | 5 | ## 脚手架安装 6 | 7 | 本项目支持通过`cdv-cli`脚手架来安装,脚手架目前提供: 8 | 9 | - vue3 单页开发模板 10 | - vue3 多页开发模板 11 | - vue3 移动端 H5 开发模板 12 | - vue3 + taro3 移动多端开发模板 13 | 14 | [cdv-cli 脚手架使用方法点击查看](https://github.com/ruanlin-kylin/cdv-cli) 15 | 16 | ![image](./src/assets/cdv1.png) 17 | 18 | ## 技术栈 19 | 20 | - [vite](https://cn.vitejs.dev/) 尤大团队开发的新一代构建工具,急速启动,快速热载 21 | - [axios](https://www.kancloud.cn/yunye/axios/234845) 这个就不用介绍了吧,使用最广泛的 ajax 封装插件 22 | - [naive-ui](https://www.naiveui.com/zh-CN/dark/docs/introduction) 尤雨溪推荐 UI 库,TypeScript 语法,主题可调,为 vue3 而生 23 | - [vueuse](https://vueuse.org/) 尤雨溪推荐,可以理解为 vue3 的 hooks 库,专为 vue 设计 24 | - [pinia](https://pinia.vuejs.org/) 尤雨溪推荐,替代 vuex4,作者原话 pinia 就是 vuex5 了 25 | 26 | ## 项目说明 27 | 28 | - 执行指令 yarn dev,通过`http://localhost:5238/`访问到最外层的 index.html 文件,然后通过该页面可以进入到 main 模块 和 minor 模块。 29 | 30 | - vite.config.ts 中有个`root`属性,用来配置项目访问入口的根路径,默认应该是'./',即 vite.config.ts 文件所在的路径。 31 | - 使用默认的`root`,执行指令 yarn dev,直接访问`http://localhost:5238`,然后进入相应的模块,你可能感觉路由有点怪异。这可以通过更改`root`的值和路由的`basePath`来修正。以下是默认配置。 32 | 33 | ```javascript 34 | /** 以下是默认的 三个 文件 配置。*/ 35 | // vite.config.ts 36 | export default defineConfig({ 37 | root: env.VITE_APP_ROOTPATH, // VITE_APP_ROOTPATH 在.env 文件中设置 38 | }) 39 | 40 | // .env 41 | VITE_APP_ROOTPATH = ./ 42 | 43 | // main模块 router/index.ts 44 | const router = createRouter({ 45 | history: createWebHistory() 46 | }); 47 | 48 | ``` 49 | 50 | 你可以按下面这样修改,修改完成后,直接访问`http://localhost:5238/`会报错`not find page`,需要拼接 URL 通过`http://localhost:5238/main/`、`http://localhost:5238/minor/`来分别访问 main 和 minor 51 | 52 | ```javascript 53 | /** 以下是修改后 三个 文件 配置。*/ 54 | // vite.config.ts 55 | export default defineConfig({ 56 | root: env.VITE_APP_ROOTPATH, // VITE_APP_ROOTPATH 在.env 文件中设置 57 | }) 58 | 59 | // .env 60 | VITE_APP_ROOTPATH = ./src/modules/ 61 | 62 | //main 模块 router/index.ts 63 | const router = createRouter({ 64 | history: createWebHistory('main') 65 | }); 66 | ``` 67 | 68 | 因为本项目模板的 minor 模块没有配置 router,所以没有举例。如果要配 minor 模块的 router,思路按以上说明即可。 69 | 70 | - 执行指令 yarn build,打包出来文件同时包含 main 和 minor 模块,也需要通过在域名后分别加上`main`和`minor`上下文根,才能分别访问到`main`和`minor`模块。如果部署在同个服务器 nginx,也可以分别通过 main 和 minor 去代理。 71 | 72 | ## 开发运行 73 | 74 | ```bash 75 | # 安装依赖 76 | yarn install 77 | 78 | # 本地开发 开启所有模块服务 79 | yarn dev 80 | 81 | # 本地开发 开启单个模块 82 | yarn main 83 | yarn minor 84 | 85 | # 所有模块一起打包 86 | yarn build 87 | 88 | # 单独模块打包 89 | yarn build:main 90 | yarn build:minor 91 | 92 | 93 | ``` 94 | 95 | ## 目录结构 96 | 97 | ``` 98 | ├── public (存放公共文件) 99 | │ └── favicon.ico 100 | ├── src 101 | │ ├── assets (公共资源) 102 | │ │ ├── logo.png 103 | │ │ └── ... 104 | │ ├── components (业务组件) 105 | │ │ └── ... 106 | │ ├── modules (业务模块) 107 | │ │ ├── main (业务模块 1) 108 | │ │ │ ├── router 109 | │ │ │ ├── store 110 | │ │ │ ├── view 111 | │ │ │ ├── App.vue 112 | │ │ │ ├── index.html 113 | │ │ │ └── main.ts 114 | │ │ ├── minor (业务模块 2) 115 | │ │ │ ├── App.vue 116 | │ │ │ ├── index.html 117 | │ │ │ └── main.ts 118 | │ ├── service (公共服务) 119 | │ │ ├── api 120 | │ └─└── http.ts 121 | ├── index.html 122 | ├── package.json 123 | ├── README.md 124 | ├── tsconfig.ts 125 | └── vite.config.ts 126 | ``` 127 | 128 | ## 本项目 git Commit message 统一规范 129 | 130 | 使用[Angular 团队提交规范](https://github.com/angular/angular.js/blob/master/DEVELOPERS.md#-git-commit-guidelines) 131 | 132 | 常用的修改项 133 | 134 | - feat: 增加新功能 135 | - fix: 修复问题/BUG 136 | - style: 代码风格相关无影响运行结果的 137 | - perf: 优化/性能提升 138 | - refactor: 重构 139 | - revert: 撤销修改 140 | - test: 测试相关 141 | - docs: 文档/注释 142 | - chore: 依赖更新/脚手架配置修改等 143 | - ci: 持续集成 144 | 145 | ## 结尾 146 | 147 | 本项目可以免费使用,如果本项目对您有帮助的话,麻烦给个 star 鼓励下~ 148 | 149 | **[⬆ 返回顶部](#前言)** 150 | -------------------------------------------------------------------------------- /commitlint.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { extends: ["@commitlint/config-conventional"] }; 2 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Vite App 8 | 9 | 10 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vue3-vite-multiple-page", 3 | "version": "0.0.1", 4 | "author": "ruanlin", 5 | "description": "一个Vue3.2+ts+vite的多页开发模板", 6 | "license": "ISC", 7 | "repository": "https://github.com/dv-cli/vue3-vite-multiple-page", 8 | "scripts": { 9 | "dev": "vite --host", 10 | "main": "vite serve src/modules/main/ --config ./vite.config.ts", 11 | "minor": "vite serve src/modules/minor/ --config ./vite.config.ts", 12 | "build": "vue-tsc --noEmit && vite build ", 13 | "build:main": "vue-tsc --noEmit && vite build --mode main", 14 | "build:minor": "vue-tsc --noEmit && vite build --mode minor", 15 | "preview": "vite preview", 16 | "lint": "eslint src --fix --ext .ts,.tsx,.vue,.js,.jsx", 17 | "prettier": "prettier --write .", 18 | "prepare": "husky install" 19 | }, 20 | "dependencies": { 21 | "@vueuse/core": "^7.5.5", 22 | "axios": "^0.25.0", 23 | "naive-ui": "^2.34.4", 24 | "nprogress": "^0.2.0", 25 | "pinia": "^2.0.0-rc.10", 26 | "qs": "^6.11.2", 27 | "vue": "^3.2.25", 28 | "vue-router": "4" 29 | }, 30 | "devDependencies": { 31 | "@commitlint/cli": "^16.1.0", 32 | "@commitlint/config-conventional": "^16.0.0", 33 | "@types/node": "^17.0.13", 34 | "@types/nprogress": "^0.2.0", 35 | "@typescript-eslint/eslint-plugin": "^5.10.1", 36 | "@typescript-eslint/parser": "^5.10.1", 37 | "@vicons/ionicons5": "^0.12.0", 38 | "@vitejs/plugin-vue": "^2.0.0", 39 | "eslint": "^8.7.0", 40 | "eslint-config-prettier": "^8.3.0", 41 | "eslint-plugin-prettier": "^4.0.0", 42 | "eslint-plugin-vue": "^8.3.0", 43 | "husky": "^7.0.4", 44 | "less": "^4.1.2", 45 | "lint-staged": "^12.3.1", 46 | "mrm": "^3.0.10", 47 | "prettier": "^2.5.1", 48 | "typescript": "^4.4.4", 49 | "vfonts": "0.0.3", 50 | "vite": "^2.7.2", 51 | "vite-plugin-compression": "^0.5.1", 52 | "vite-plugin-svg-icons": "^2.0.1", 53 | "vue-tsc": "^0.29.8" 54 | }, 55 | "husky": { 56 | "hooks": { 57 | "pre-commit": "lint-staged" 58 | } 59 | }, 60 | "lint-staged": { 61 | "*.{js,jsx,vue,ts,tsx}": [ 62 | "yarn lint", 63 | "prettier --write", 64 | "git add" 65 | ] 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dv-cli/vue3-vite-multiple-page/a5b80c766f353a3cdc99c1a863da218370b56df9/public/favicon.ico -------------------------------------------------------------------------------- /src/assets/cdv1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dv-cli/vue3-vite-multiple-page/a5b80c766f353a3cdc99c1a863da218370b56df9/src/assets/cdv1.png -------------------------------------------------------------------------------- /src/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dv-cli/vue3-vite-multiple-page/a5b80c766f353a3cdc99c1a863da218370b56df9/src/assets/logo.png -------------------------------------------------------------------------------- /src/assets/style/global.less: -------------------------------------------------------------------------------- 1 | html, 2 | body { 3 | height: 100%; 4 | width: 100%; 5 | margin: 0; 6 | padding: 0; 7 | } 8 | -------------------------------------------------------------------------------- /src/assets/style/main.less: -------------------------------------------------------------------------------- 1 | @test-color: red; 2 | -------------------------------------------------------------------------------- /src/components/HelloWorld.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 38 | 39 | 56 | -------------------------------------------------------------------------------- /src/components/SvgIcon.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 32 | -------------------------------------------------------------------------------- /src/components/VueUse.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 32 | -------------------------------------------------------------------------------- /src/components/nprogress/index.ts: -------------------------------------------------------------------------------- 1 | import NProgress from "nprogress"; 2 | import "nprogress/nprogress.css"; 3 | 4 | NProgress.configure({ 5 | easing: "ease", // 动画方式 6 | speed: 500, // 递增进度条的速度 7 | showSpinner: true, // 是否显示加载ico 8 | trickleSpeed: 200, // 自动递增间隔 9 | minimum: 0.3, // 初始化时的最小百分比 10 | }); 11 | export default NProgress; 12 | -------------------------------------------------------------------------------- /src/env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | declare module "*.vue" { 4 | import { DefineComponent } from "vue"; 5 | // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/ban-types 6 | const component: DefineComponent<{}, {}, any>; 7 | export default component; 8 | } 9 | -------------------------------------------------------------------------------- /src/modules/main/App.vue: -------------------------------------------------------------------------------- 1 | 12 | 41 | 57 | -------------------------------------------------------------------------------- /src/modules/main/assets/icons/collapse-left.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/modules/main/assets/icons/collapse-right.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/modules/main/assets/icons/exit-full-screen.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/modules/main/assets/icons/full-screen.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/modules/main/assets/style/global.less: -------------------------------------------------------------------------------- 1 | html, 2 | body { 3 | height: 100%; 4 | width: 100%; 5 | } 6 | -------------------------------------------------------------------------------- /src/modules/main/assets/style/main.less: -------------------------------------------------------------------------------- 1 | @test-color: red; 2 | -------------------------------------------------------------------------------- /src/modules/main/components/header/BasicHeader.vue: -------------------------------------------------------------------------------- 1 | 20 | 21 | 75 | 76 | 101 | -------------------------------------------------------------------------------- /src/modules/main/components/header/components/Breadcrumb.vue: -------------------------------------------------------------------------------- 1 | 13 | 14 | 35 | -------------------------------------------------------------------------------- /src/modules/main/components/header/index.ts: -------------------------------------------------------------------------------- 1 | import BasicHeader from "./BasicHeader.vue"; 2 | 3 | export { BasicHeader }; 4 | -------------------------------------------------------------------------------- /src/modules/main/components/menu/BasicMenu.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 68 | -------------------------------------------------------------------------------- /src/modules/main/components/menu/index.ts: -------------------------------------------------------------------------------- 1 | import BasicMenu from "./BasicMenu.vue"; 2 | 3 | export { BasicMenu }; 4 | -------------------------------------------------------------------------------- /src/modules/main/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | main模块 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /src/modules/main/main.ts: -------------------------------------------------------------------------------- 1 | import { createApp } from "vue"; 2 | import naive from "naive-ui"; 3 | import App from "./App.vue"; 4 | import "./assets/style/global.less"; 5 | import "virtual:svg-icons-register"; 6 | 7 | import router from "./router/index"; 8 | import { createPinia } from "pinia"; 9 | 10 | const app = createApp(App); 11 | 12 | app.use(router); 13 | 14 | app.use(naive); 15 | 16 | app.use(createPinia()); 17 | 18 | app.mount("#app"); 19 | -------------------------------------------------------------------------------- /src/modules/main/router/index.ts: -------------------------------------------------------------------------------- 1 | import { createRouter, createWebHistory, createWebHashHistory } from "vue-router"; 2 | import { constantRouter } from "./modules/constant"; 3 | import { useGlobalStore } from "@main/store"; 4 | import NProgress from "@/components/nprogress"; 5 | 6 | const router = createRouter({ 7 | history: import.meta.env.DEV ? createWebHashHistory() : createWebHistory(), 8 | routes: [...constantRouter], 9 | scrollBehavior: () => ({ left: 0, top: 0 }), 10 | }); 11 | 12 | /** 13 | * @description 路由拦截 beforeEach 14 | * */ 15 | router.beforeEach(async (to, from, next) => { 16 | NProgress.start(); 17 | 18 | // 请求菜单列表并添加路由 19 | const globalStore = useGlobalStore(); 20 | if (!globalStore.menuListGet.length) { 21 | await globalStore.getMenuList(); 22 | return next({ ...to, replace: true }); 23 | } 24 | 25 | next(); 26 | }); 27 | 28 | /** 29 | * @description 路由跳转结束 30 | * */ 31 | router.afterEach(() => { 32 | NProgress.done(); 33 | }); 34 | 35 | /** 36 | * @description 路由跳转错误 37 | * */ 38 | router.onError(error => { 39 | NProgress.done(); 40 | }); 41 | export default router; 42 | -------------------------------------------------------------------------------- /src/modules/main/router/modules/async.ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dv-cli/vue3-vite-multiple-page/a5b80c766f353a3cdc99c1a863da218370b56df9/src/modules/main/router/modules/async.ts -------------------------------------------------------------------------------- /src/modules/main/router/modules/constant.ts: -------------------------------------------------------------------------------- 1 | import { RouteRecordRaw } from "vue-router"; 2 | 3 | /** 4 | * constant(常驻路由) 5 | */ 6 | export const constantRouter: RouteRecordRaw[] = [ 7 | { 8 | path: "/", 9 | redirect: "/home/index", 10 | }, 11 | { 12 | path: "/home", 13 | name: "Home", 14 | redirect: "/home/index", 15 | component: () => import("@main/view/layout/PageLayout.vue"), 16 | meta: { 17 | title: "首页", 18 | }, 19 | children: [ 20 | { 21 | path: "index", 22 | name: "HomePage", 23 | component: () => import("@main/view/home/Home.vue"), 24 | }, 25 | ], 26 | }, 27 | { 28 | path: "/example", 29 | name: "Example", 30 | component: () => import("@main/view/layout/PageLayout.vue"), 31 | meta: { 32 | title: "演示示例", 33 | }, 34 | children: [ 35 | { 36 | path: "virtual-table", 37 | name: "VirtualTable", 38 | component: () => import("@main/view/example/VirtualTable.vue"), 39 | meta: { 40 | title: "虚拟滚动表格", 41 | }, 42 | }, 43 | { 44 | path: "test111", 45 | name: "Test1", 46 | component: () => import("@main/view/example/Test.vue"), 47 | meta: { 48 | title: "测试111", 49 | }, 50 | }, 51 | { 52 | path: "vue-use", 53 | name: "VueUse", 54 | component: () => import("@main/view/example/VueUse.vue"), 55 | meta: { 56 | title: "VueUse用例", 57 | }, 58 | }, 59 | { 60 | path: "hello-world", 61 | name: "HelloWorld", 62 | component: () => import("@main/view/example/HelloWorld.vue"), 63 | meta: { 64 | title: "HelloWorld", 65 | }, 66 | }, 67 | ], 68 | }, 69 | ]; 70 | -------------------------------------------------------------------------------- /src/modules/main/store/index.ts: -------------------------------------------------------------------------------- 1 | import { defineStore } from "pinia"; 2 | import { getMenuListApi } from "@/service/api/mock"; 3 | import { MenuResult } from "@/service/api/mock-data"; 4 | import { ObjectType } from "@/service/api/interface"; 5 | import { getKeepAliveRouterName, getAllBreadcrumbList } from "@/modules/main/utils/nav"; 6 | 7 | interface GlobalState { 8 | name: string; 9 | menuList: any[]; 10 | userInfo?: ObjectType; 11 | themeValue: string; 12 | fullScreen: string; 13 | language: string; 14 | } 15 | 16 | export const useGlobalStore = defineStore({ 17 | id: "globalStore", 18 | state: (): GlobalState => ({ 19 | name: "全局store", 20 | menuList: [], 21 | userInfo: {}, 22 | themeValue: "dark", 23 | fullScreen: "full-screen", 24 | language: "zhCN", 25 | }), 26 | getters: { 27 | nameLength: state => state.name.length, 28 | menuListGet: state => state.menuList, 29 | keepAliveRouterNameGet: state => getKeepAliveRouterName(state.menuList), 30 | breadcrumbObjectGet: state => getAllBreadcrumbList(state.menuList), 31 | }, 32 | actions: { 33 | getMenuList() { 34 | getMenuListApi().then( 35 | ({ data }) => { 36 | this.menuList = data as any; 37 | }, 38 | error => { 39 | this.menuList = MenuResult.data; 40 | } 41 | ); 42 | }, 43 | toggleThem(themeValue: string) { 44 | this.themeValue = themeValue; 45 | }, 46 | toggleFullScreen() { 47 | // fullscreenEnabled属性检查浏览器是否支持全屏模式,并且是否得到了用户的授权。 48 | if (document.fullscreenEnabled) { 49 | // fullscreenElement属性将返回当前处于全屏模式下的元素 50 | // 如果没有元素处于全屏模式下的时候,它将返回null 51 | if (document.fullscreenElement) { 52 | document.exitFullscreen(); 53 | } else { 54 | document.documentElement.requestFullscreen(); 55 | } 56 | } 57 | }, 58 | changeLanguage(language: string) { 59 | this.language = language; 60 | }, 61 | }, 62 | }); 63 | -------------------------------------------------------------------------------- /src/modules/main/store/user.ts: -------------------------------------------------------------------------------- 1 | import { defineStore } from "pinia"; 2 | 3 | export const userStore = defineStore({ 4 | id: "user", 5 | state: () => ({ 6 | name: "超级管理员", 7 | }), 8 | getters: { 9 | nameLength: state => state.name.length, 10 | }, 11 | actions: {}, 12 | }); 13 | -------------------------------------------------------------------------------- /src/modules/main/typings/global.d.ts: -------------------------------------------------------------------------------- 1 | declare namespace Menu { 2 | interface MenuOptions { 3 | path: string; 4 | name: string; 5 | component?: string | (() => Promise); 6 | redirect?: string; 7 | meta: MetaProps; 8 | children?: MenuOptions[]; 9 | } 10 | 11 | interface MetaProps { 12 | icon: string; 13 | title: string; 14 | isKeepAlive: boolean; 15 | } 16 | } 17 | 18 | declare module "qs"; 19 | -------------------------------------------------------------------------------- /src/modules/main/utils/nav.ts: -------------------------------------------------------------------------------- 1 | import { ObjectType } from "@/service/api/interface"; 2 | 3 | /** 4 | * 递归获取keepAlive的组件name 5 | * @param menuList 6 | * @param keepAliveArr 7 | * @return array 8 | */ 9 | export function getKeepAliveRouterName(menuList: Menu.MenuOptions[], keepAliveArr: string[] = []) { 10 | menuList.forEach(menu => { 11 | menu.meta.isKeepAlive && menu.name && keepAliveArr.push(menu.name); 12 | menu.children?.length && getKeepAliveRouterName(menu.children, keepAliveArr); 13 | }); 14 | return keepAliveArr; 15 | } 16 | 17 | /** 18 | * 得到一个path和路由的映射关系对象 19 | * @param menuList 20 | * @returns 21 | */ 22 | export function getAllBreadcrumbList(menuList: Menu.MenuOptions[]) { 23 | const breadcrumbObject: ObjectType = {}; 24 | const loop = (menu: Menu.MenuOptions) => { 25 | if (menu.children?.length) { 26 | menu.children.forEach(item => loop(item)); 27 | } else { 28 | breadcrumbObject[menu.path] = getCurrentBreadcrumb(menu.path, menuList); 29 | } 30 | }; 31 | menuList.forEach(item => loop(item)); 32 | return breadcrumbObject; 33 | } 34 | 35 | /** 36 | * 递归获取当前路由的面包屑对象 37 | * @param path 38 | * @param menuList 39 | * @return array 40 | */ 41 | export function getCurrentBreadcrumb(path: string, menuList: Menu.MenuOptions[]) { 42 | const currentNodes: Menu.MenuOptions[] = []; 43 | 44 | try { 45 | const getNode = (node: Menu.MenuOptions) => { 46 | currentNodes.push(node); 47 | if (path == node.path) throw new Error(); 48 | if (node.children?.length) node.children.forEach(item => getNode(item)); 49 | currentNodes.pop(); 50 | }; 51 | menuList.forEach(item => getNode(item)); 52 | } catch (e) { 53 | return currentNodes; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/modules/main/view/example/HelloWorld.vue: -------------------------------------------------------------------------------- 1 | 29 | 30 | 47 | -------------------------------------------------------------------------------- /src/modules/main/view/example/Test.vue: -------------------------------------------------------------------------------- 1 | 13 | 14 | 27 | -------------------------------------------------------------------------------- /src/modules/main/view/example/VirtualTable.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 79 | -------------------------------------------------------------------------------- /src/modules/main/view/example/VueUse.vue: -------------------------------------------------------------------------------- 1 | 12 | 13 | 34 | -------------------------------------------------------------------------------- /src/modules/main/view/home/Home.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 9 | 10 | 16 | -------------------------------------------------------------------------------- /src/modules/main/view/layout/PageLayout.vue: -------------------------------------------------------------------------------- 1 | 47 | 48 | 55 | 56 | 72 | -------------------------------------------------------------------------------- /src/modules/minor/App.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 16 | -------------------------------------------------------------------------------- /src/modules/minor/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | minor模块 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /src/modules/minor/main.ts: -------------------------------------------------------------------------------- 1 | import { createApp } from "vue"; 2 | import App from "@minor/App.vue"; 3 | import "@/assets/style/global.less"; 4 | 5 | createApp(App).mount("#app"); 6 | -------------------------------------------------------------------------------- /src/service/api/interface.ts: -------------------------------------------------------------------------------- 1 | export interface LoginParams { 2 | userName: string; 3 | passWord: string | number; 4 | } 5 | 6 | export interface ObjectType { 7 | [key: string]: any; 8 | } 9 | 10 | export interface Result { 11 | code: string; 12 | msg: string; 13 | } 14 | 15 | export interface ResultData extends Result { 16 | data: T; 17 | } 18 | -------------------------------------------------------------------------------- /src/service/api/mock-data.ts: -------------------------------------------------------------------------------- 1 | const MenuResult = { 2 | code: "200", 3 | data: [ 4 | { 5 | path: "/home/index", 6 | name: "Home", 7 | component: "/layout/PageLayout", 8 | meta: { 9 | icon: "HomeOutline", 10 | title: "首页", 11 | }, 12 | }, 13 | { 14 | path: "/example", 15 | name: "Example", 16 | component: "/layout/PageLayout", 17 | meta: { 18 | icon: "AirplaneOutline", 19 | title: "演示示例", 20 | }, 21 | children: [ 22 | { 23 | path: "/example/virtual-table", 24 | name: "VirtualTable", 25 | component: "/example/VirtualTable", 26 | meta: { 27 | title: "虚拟滚动表格", 28 | }, 29 | }, 30 | { 31 | path: "/example/test111", 32 | name: "Test1", 33 | component: "/example/Test", 34 | meta: { 35 | title: "测试111", 36 | }, 37 | }, 38 | { 39 | path: "/example/vue-use", 40 | name: "VueUse", 41 | component: "/example/VueUse", 42 | meta: { 43 | title: "VueUse用例", 44 | }, 45 | }, 46 | { 47 | path: "/example/hello-world", 48 | name: "HelloWorld", 49 | component: "/example/HelloWorld", 50 | meta: { 51 | title: "HelloWorld", 52 | }, 53 | }, 54 | ], 55 | }, 56 | ], 57 | msg: "成功", 58 | }; 59 | 60 | export { MenuResult }; 61 | -------------------------------------------------------------------------------- /src/service/api/mock.ts: -------------------------------------------------------------------------------- 1 | import http from "@/service"; 2 | import { LoginParams, ObjectType } from "./interface"; 3 | 4 | const loginApi = (params: LoginParams) => http.post("/login", params); 5 | 6 | const getMenuListApi = (params?: ObjectType) => http.get("/menu", params); 7 | 8 | export { loginApi, getMenuListApi }; 9 | -------------------------------------------------------------------------------- /src/service/helper/axios-cancel.ts: -------------------------------------------------------------------------------- 1 | import axios, { AxiosRequestConfig, Canceler } from "axios"; 2 | import { isFunction } from "@/utils/is"; 3 | import qs from "qs"; 4 | 5 | // * 声明一个 Map 用于存储每个请求的标识 和 取消函数 6 | let pendingMap = new Map(); 7 | 8 | // * 序列化参数 9 | export const getPendingUrl = (config: AxiosRequestConfig) => 10 | [config.method, config.url, qs.stringify(config.data), qs.stringify(config.params)].join("&"); 11 | 12 | export class AxiosCanceler { 13 | /** 14 | * @description: 添加请求 15 | * @param {Object} config 16 | * @return void 17 | */ 18 | addPending(config: AxiosRequestConfig) { 19 | // * 在请求开始前,对之前的请求做检查取消操作 20 | this.removePending(config); 21 | const url = getPendingUrl(config); 22 | config.cancelToken = 23 | config.cancelToken || 24 | new axios.CancelToken(cancel => { 25 | if (!pendingMap.has(url)) { 26 | // 如果 pending 中不存在当前请求,则添加进去 27 | pendingMap.set(url, cancel); 28 | } 29 | }); 30 | } 31 | 32 | /** 33 | * @description: 移除请求 34 | * @param {Object} config 35 | */ 36 | removePending(config: AxiosRequestConfig) { 37 | const url = getPendingUrl(config); 38 | 39 | if (pendingMap.has(url)) { 40 | // 如果在 pending 中存在当前请求标识,需要取消当前请求,并且移除 41 | const cancel = pendingMap.get(url); 42 | cancel && cancel(); 43 | pendingMap.delete(url); 44 | } 45 | } 46 | 47 | /** 48 | * @description: 清空所有pending 49 | */ 50 | removeAllPending() { 51 | pendingMap.forEach(cancel => { 52 | cancel && isFunction(cancel) && cancel(); 53 | }); 54 | pendingMap.clear(); 55 | } 56 | 57 | /** 58 | * @description: 重置 59 | */ 60 | reset(): void { 61 | pendingMap = new Map(); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/service/helper/check-status.ts: -------------------------------------------------------------------------------- 1 | import { createDiscreteApi } from "naive-ui"; 2 | 3 | const { message } = createDiscreteApi(["message"]); 4 | /** 5 | * @description: 校验网络请求状态码 6 | * @param {Number} status 7 | * @return void 8 | */ 9 | export const checkStatus = (status: number): void => { 10 | switch (status) { 11 | case 400: 12 | message.error("请求失败!请您稍后重试"); 13 | break; 14 | case 401: 15 | message.error("登录失效!请您重新登录"); 16 | break; 17 | case 403: 18 | message.error("当前账号无权限访问!"); 19 | break; 20 | case 404: 21 | message.error("你所访问的资源不存在!"); 22 | break; 23 | case 405: 24 | message.error("请求方式错误!请您稍后重试"); 25 | break; 26 | case 408: 27 | message.error("请求超时!请您稍后重试"); 28 | break; 29 | case 500: 30 | message.error("服务异常!"); 31 | break; 32 | case 502: 33 | message.error("网关错误!"); 34 | break; 35 | case 503: 36 | message.error("服务不可用!"); 37 | break; 38 | case 504: 39 | message.error("网关超时!"); 40 | break; 41 | default: 42 | message.error("请求失败!"); 43 | } 44 | }; 45 | -------------------------------------------------------------------------------- /src/service/helper/enum.ts: -------------------------------------------------------------------------------- 1 | // * 请求枚举配置 2 | /** 3 | * @description:请求配置 4 | */ 5 | export enum ResultEnum { 6 | SUCCESS = 200, 7 | ERROR = 500, 8 | OVERDUE = 599, 9 | TIMEOUT = 10000, 10 | TYPE = "success", 11 | } 12 | 13 | /** 14 | * @description:请求方法 15 | */ 16 | export enum RequestEnum { 17 | GET = "GET", 18 | POST = "POST", 19 | PATCH = "PATCH", 20 | PUT = "PUT", 21 | DELETE = "DELETE", 22 | } 23 | 24 | /** 25 | * @description:常用的contentTyp类型 26 | */ 27 | export enum ContentTypeEnum { 28 | // json 29 | JSON = "application/json;charset=UTF-8", 30 | // text 31 | TEXT = "text/plain;charset=UTF-8", 32 | // form-data 一般配合qs 33 | FORM_URLENCODED = "application/x-www-form-urlencoded;charset=UTF-8", 34 | // form-data 上传 35 | FORM_DATA = "multipart/form-data;charset=UTF-8", 36 | } 37 | -------------------------------------------------------------------------------- /src/service/index.ts: -------------------------------------------------------------------------------- 1 | import axios, { AxiosInstance, AxiosError, AxiosRequestConfig, AxiosResponse } from "axios"; 2 | import { AxiosCanceler } from "./helper/axios-cancel"; 3 | import { ResultData } from "@/service/api/interface"; 4 | import { ResultEnum } from "./helper/enum"; 5 | import { checkStatus } from "./helper/check-status"; 6 | import { createDiscreteApi } from "naive-ui"; 7 | 8 | const { message } = createDiscreteApi(["message"]); 9 | 10 | const axiosCanceler = new AxiosCanceler(); 11 | 12 | console.log(import.meta.env.VITE_API_URL); 13 | 14 | const config = { 15 | // 默认地址请求地址,可在 .env 开头文件中修改 16 | baseURL: import.meta.env.VITE_API_URL as string, 17 | // 设置超时时间(10s) 18 | timeout: ResultEnum.TIMEOUT as number, 19 | // 跨域时候允许携带凭证 20 | withCredentials: true, 21 | }; 22 | 23 | class RequestHttp { 24 | service: AxiosInstance; 25 | public constructor(config: AxiosRequestConfig) { 26 | // 实例化axios 27 | this.service = axios.create(config); 28 | 29 | /** 30 | * @description 请求拦截器 31 | * 客户端发送请求 -> [请求拦截器] -> 服务器 32 | */ 33 | this.service.interceptors.request.use( 34 | (config: AxiosRequestConfig) => { 35 | // * 将当前请求添加到 pending 中 36 | axiosCanceler.addPending(config); 37 | 38 | return { ...config, headers: { ...config.headers } }; 39 | }, 40 | (error: AxiosError) => { 41 | return Promise.reject(error); 42 | } 43 | ); 44 | 45 | /** 46 | * @description 响应拦截器 47 | * 服务器换返回信息 -> [拦截统一处理] -> 客户端JS获取到信息 48 | * 49 | */ 50 | this.service.interceptors.response.use( 51 | (response: AxiosResponse) => { 52 | const { data, config } = response; 53 | // * 在请求结束后,移除本次请求,并关闭请求 loading 54 | axiosCanceler.removePending(config); 55 | // * 登陆失效 56 | if (data.code == ResultEnum.OVERDUE) { 57 | message.error(data.msg); 58 | return Promise.reject(data); 59 | } 60 | // * 全局错误信息拦截(防止下载文件得时候返回数据流,没有code,直接报错) 61 | if (data.code && data.code != ResultEnum.SUCCESS) { 62 | message.error(data.msg); 63 | return Promise.reject(data); 64 | } 65 | // * 成功请求(在页面上除非特殊情况,否则不用处理失败逻辑) 66 | return data; 67 | }, 68 | (error: AxiosError) => { 69 | const { response } = error; 70 | // 请求超时单独判断,因为请求超时没有 response 71 | if (error.message.indexOf("timeout") !== -1) message.error("请求超时!请您稍后重试"); 72 | // 根据响应的错误状态码,做不同的处理 73 | if (response) checkStatus(response.status); 74 | 75 | return Promise.reject(error); 76 | } 77 | ); 78 | } 79 | 80 | // * 常用请求方法封装 81 | get(url: string, params?: object, _object = {}): Promise> { 82 | return this.service.get(url, { params, ..._object }); 83 | } 84 | post(url: string, params?: object, _object = {}): Promise> { 85 | return this.service.post(url, params, _object); 86 | } 87 | put(url: string, params?: object, _object = {}): Promise> { 88 | return this.service.put(url, params, _object); 89 | } 90 | delete(url: string, params?: any, _object = {}): Promise> { 91 | return this.service.delete(url, { params, ..._object }); 92 | } 93 | } 94 | 95 | export default new RequestHttp(config); 96 | -------------------------------------------------------------------------------- /src/utils/event.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * 递归树形转列表 3 | * @param menuList 4 | * @returns 5 | */ 6 | export function treeToList(menuList: Menu.MenuOptions[]) { 7 | if (!Array.isArray(menuList)) return; 8 | const result: Menu.MenuOptions[] = []; 9 | const dfs = (tree: Menu.MenuOptions[]) => { 10 | tree.forEach(item => { 11 | result.push(item); 12 | if (item.children) { 13 | dfs(item.children); 14 | Reflect.deleteProperty(item, "children"); 15 | } 16 | }); 17 | }; 18 | dfs(menuList); 19 | return result; 20 | } 21 | -------------------------------------------------------------------------------- /src/utils/is.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * 判断是否是函数 3 | * @param val 4 | * @returns 5 | */ 6 | export function isFunction(val: any): val is string { 7 | return typeof val === "function"; 8 | } 9 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "esnext", 4 | "module": "esnext", 5 | "moduleResolution": "node", 6 | "strict": true, 7 | "jsx": "preserve", 8 | "sourceMap": true, 9 | "resolveJsonModule": true, 10 | "esModuleInterop": true, 11 | "lib": ["esnext", "dom"], 12 | "baseUrl": ".", 13 | "paths": { 14 | "@/*": ["src/*"], 15 | "@main/*": ["src/modules/main/*"], 16 | "@minor/*": ["src/modules/minor/*"] 17 | }, 18 | "types": ["naive-ui/volar"] 19 | }, 20 | "include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"] 21 | } 22 | -------------------------------------------------------------------------------- /vite.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig, loadEnv } from "vite"; 2 | import vue from "@vitejs/plugin-vue"; 3 | import { resolve, join } from "path"; 4 | import { readdirSync } from "fs"; 5 | 6 | import viteCompression from "vite-plugin-compression"; 7 | import { createSvgIconsPlugin } from "vite-plugin-svg-icons"; 8 | 9 | const project_pages = {}; 10 | const entryPath = resolve(__dirname, "./src/modules"); 11 | const entrys = readdirSync(entryPath).reduce((obj, dirname) => { 12 | obj[dirname] = join(entryPath, dirname); 13 | return obj; 14 | }, {}); 15 | 16 | Object.keys(entrys).forEach(pageName => { 17 | project_pages[pageName] = resolve(__dirname, `src/modules/${pageName}/index.html`); 18 | }); 19 | 20 | // https://vitejs.dev/config/ 21 | export default defineConfig(({ mode }) => { 22 | let pages = {}; 23 | const env = loadEnv(mode, process.cwd()); 24 | const isDev = mode === "development"; 25 | 26 | if (isDev) { 27 | pages = { ...project_pages }; 28 | } else { 29 | if (env.VITE_APP_MODEL) { 30 | pages[env.VITE_APP_MODEL] = project_pages[env.VITE_APP_MODEL]; 31 | } else { 32 | pages = { ...project_pages }; 33 | } 34 | } 35 | return { 36 | root: env.VITE_APP_ROOTPATH, 37 | plugins: [ 38 | vue(), 39 | createSvgIconsPlugin({ 40 | // 指定需要缓存的图标文件夹 41 | iconDirs: [ 42 | resolve(process.cwd(), "src/assets/icons"), 43 | resolve(process.cwd(), "src/modules/main/assets/icons"), 44 | ], 45 | // 指定symbolId格式 46 | symbolId: "icon-[dir]-[name]", 47 | }), 48 | // gzip压缩 生产环境生成 .gz 文件 49 | viteCompression({ 50 | verbose: true, 51 | disable: false, 52 | threshold: 10240, 53 | algorithm: "gzip", 54 | ext: ".gz", 55 | }), 56 | ], 57 | resolve: { 58 | extensions: [".js", ".ts", ".vue", ".json"], 59 | alias: { 60 | "@": resolve(__dirname, "src"), 61 | "@main": resolve(__dirname, "src/modules/main"), 62 | "@minor": resolve(__dirname, "src/modules/minor"), 63 | }, 64 | }, 65 | css: { 66 | preprocessorOptions: { 67 | less: { 68 | additionalData: '@import "@/assets/style/main.less";', 69 | }, 70 | }, 71 | }, 72 | server: { 73 | host: "0.0.0.0", 74 | port: 5238, 75 | open: false, 76 | https: false, 77 | proxy: { 78 | "/cnhis": { 79 | target: "https://mock.mengxuegu.com/mock/63856c7c9433403d6c06899c", // easymock 80 | changeOrigin: true, 81 | rewrite: path => path.replace(/^\/cnhis/, ""), 82 | }, 83 | }, 84 | }, 85 | build: { 86 | rollupOptions: { 87 | input: pages, 88 | output: { dir: "./dist" }, 89 | }, 90 | terserOptions: { 91 | compress: { 92 | drop_console: true, 93 | drop_debugger: true, 94 | }, 95 | }, 96 | }, 97 | }; 98 | }); 99 | --------------------------------------------------------------------------------