├── .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 | 
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 |
10 | {{ msg }}
11 |
12 |
13 | Recommended IDE setup:
14 | VSCode
15 | +
16 | Volar
17 |
18 |
19 |
20 | See
21 | README.md
22 | for more information.
23 |
24 |
25 |
26 | Vite Docs
27 | |
28 | Vue 3 Docs
29 |
30 |
31 |
32 |
33 | Edit
34 | components/HelloWorld.vue
35 | to test hot module replacement.
36 |
37 |
38 |
39 |
56 |
--------------------------------------------------------------------------------
/src/components/SvgIcon.vue:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
32 |
--------------------------------------------------------------------------------
/src/components/VueUse.vue:
--------------------------------------------------------------------------------
1 |
2 | VueUse简单使用示例
3 |
4 |
鼠标指针当前的坐标:
5 | X:{{ x }};Y:{{ y }}
6 |
7 | usePreferredDark判断用户是否设置了浏览器深色主题:{{ isDark }}
8 | useLocalStorage存储api:{{ store }}
9 |
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 |
2 |
7 |
8 |
9 |
10 |
11 |
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 |
2 |
19 |
20 |
21 |
75 |
76 |
101 |
--------------------------------------------------------------------------------
/src/modules/main/components/header/components/Breadcrumb.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
9 | {{ breadcrumb.meta.title }}
10 |
11 |
12 |
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 |
2 |
9 |
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 |
2 |
3 |
4 | Recommended IDE setup:
5 | VSCode
6 | +
7 | Volar
8 |
9 |
10 |
11 | See
12 | README.md
13 | for more information.
14 |
15 |
16 |
17 | Vite Docs
18 | |
19 | Vue 3 Docs
20 |
21 |
22 |
23 | Edit
24 | components/HelloWorld.vue
25 | to test hot module replacement.
26 |
27 |
28 |
29 |
30 |
47 |
--------------------------------------------------------------------------------
/src/modules/main/view/example/Test.vue:
--------------------------------------------------------------------------------
1 |
13 |
14 |
15 |
16 |
17 | 用户名:{{ user.name }}
18 |
19 | 长度:{{ user.nameLength }}
20 |
21 |
22 |
修改store中的name
23 |
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/src/modules/main/view/example/VirtualTable.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
79 |
--------------------------------------------------------------------------------
/src/modules/main/view/example/VueUse.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
VueUse简单使用示例
4 |
5 |
鼠标指针当前的坐标:
6 | X:{{ x }};Y:{{ y }}
7 |
8 |
usePreferredDark判断用户是否设置了浏览器深色主题:{{ isDark }}
9 |
useLocalStorage存储api:{{ store }}
10 |
11 |
12 |
13 |
34 |
--------------------------------------------------------------------------------
/src/modules/main/view/home/Home.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
placeholder随着国际化切换而变化
4 |
5 |
6 |
7 |
8 |
9 |
10 |
16 |
--------------------------------------------------------------------------------
/src/modules/main/view/layout/PageLayout.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
15 |
16 |
17 | 太古里
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 | 城府路
42 |
43 |
44 |
45 |
46 |
47 |
48 |
55 |
56 |
72 |
--------------------------------------------------------------------------------
/src/modules/minor/App.vue:
--------------------------------------------------------------------------------
1 | 我是minor模块
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 |
--------------------------------------------------------------------------------