commit ae0ebde1e3f5e90a1f49b6b6a5a47e46126cfac8 Author: LAPTOP-MI\Lau-mi Date: Wed Jun 12 17:47:25 2024 +0800 chore:更新相关库版本,优化项目目录结构等 diff --git a/.env.development b/.env.development new file mode 100644 index 0000000..5f3b995 --- /dev/null +++ b/.env.development @@ -0,0 +1,3 @@ +VITE_APP_TITLE = DEV标题 +VITE_BASE_URL = / +VITE_API_BASE_URL = /api \ No newline at end of file diff --git a/.env.production b/.env.production new file mode 100644 index 0000000..34d7d5c --- /dev/null +++ b/.env.production @@ -0,0 +1,3 @@ +VITE_APP_TITLE = PROD标题 +VITE_BASE_URL = ./ +VITE_API_BASE_URL = /api \ No newline at end of file diff --git a/.eslintrc.cjs b/.eslintrc.cjs new file mode 100644 index 0000000..e52b5f2 --- /dev/null +++ b/.eslintrc.cjs @@ -0,0 +1,17 @@ +/* eslint-env node */ +require('@rushstack/eslint-patch/modern-module-resolution') + +module.exports = { + root: true, + extends: [ + 'plugin:vue/vue3-essential', + 'eslint:recommended', + '@vue/eslint-config-prettier/skip-formatting' + ], + parserOptions: { + ecmaVersion: 'latest' + }, + rules: { + 'vue/multi-word-component-names': 'off' + } +} diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..692d9a9 --- /dev/null +++ b/.gitignore @@ -0,0 +1,28 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +.DS_Store +dist +dist-ssr +coverage +*.local +package-lock.json + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? + +*.tsbuildinfo diff --git a/.prettierrc.json b/.prettierrc.json new file mode 100644 index 0000000..66e2335 --- /dev/null +++ b/.prettierrc.json @@ -0,0 +1,8 @@ +{ + "$schema": "https://json.schemastore.org/prettierrc", + "semi": false, + "tabWidth": 2, + "singleQuote": true, + "printWidth": 100, + "trailingComma": "none" +} \ No newline at end of file diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 0000000..009a534 --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,8 @@ +{ + "recommendations": [ + "Vue.volar", + "Vue.vscode-typescript-vue-plugin", + "dbaeumer.vscode-eslint", + "esbenp.prettier-vscode" + ] +} diff --git a/README.md b/README.md new file mode 100644 index 0000000..8901462 --- /dev/null +++ b/README.md @@ -0,0 +1,60 @@ +# vue3-template + +使用vite构建的vue3项目基础模板 + +vue3:3.4.27 + +vue-router:4.3.2 + +pinia:2.1.7 + +element-plus:2.7.5 + +store持久化存储:[pinia-plugin-persistedstate](https://prazdevs.github.io/pinia-plugin-persistedstate/guide/config.html):3.2.1 + +mitt:3.0.1 + +## Customize configuration + +vite配置 [Vite Configuration Reference](https://vitejs.dev/config/). + +## 安装依赖 + +```sh +npm install +``` + +### 运行dev服务 + +```sh +npm run dev +``` + +### 构建项目 + +```sh +npm run build +``` + +### [ESLint](https://eslint.org/) + +```sh +npm run lint +``` + +## Git提交规范 + +- 参考 [vue](https://github.com/vuejs/vue/blob/dev/.github/COMMIT_CONVENTION.md) 规范 ([Angular](https://github.com/conventional-changelog/conventional-changelog/tree/master/packages/conventional-changelog-angular)) + + - `feat` 增加新功能 + - `fix` 修复问题/BUG + - `style` 代码风格相关无影响运行结果的 + - `perf` 优化/性能提升 + - `refactor` 重构 + - `revert` 撤销修改 + - `test` 测试相关 + - `docs` 文档/注释 + - `chore` 依赖更新/脚手架配置修改等 + - `workflow` 工作流改进 + - `ci` 持续集成 + - `types` 类型定义文件更改 diff --git a/index.html b/index.html new file mode 100644 index 0000000..fca4dd9 --- /dev/null +++ b/index.html @@ -0,0 +1,13 @@ + + + + + + + Vite App + + +
+ + + diff --git a/jsconfig.json b/jsconfig.json new file mode 100644 index 0000000..58f54a0 --- /dev/null +++ b/jsconfig.json @@ -0,0 +1,10 @@ +{ + "compilerOptions": { + "paths": { + "@/*": ["./src/*"], + "@v/*": ["./src/views/*"], + "@c/*": ["./src/components/*"] + } + }, + "exclude": ["node_modules", "dist"] +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..f4f83b3 --- /dev/null +++ b/package.json @@ -0,0 +1,41 @@ +{ + "name": "vue3-template", + "version": "0.1.0", + "private": true, + "type": "module", + "scripts": { + "dev": "vite --mode development", + "serve": "vite --mode development", + "build": "vite build --mode production", + "preview": "vite preview --mode development", + "lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs --fix --ignore-path .gitignore", + "format": "prettier --write src/" + }, + "dependencies": { + "axios": "1.7.2", + "element-plus": "2.7.5", + "mitt": "3.0.1", + "pinia": "2.1.7", + "pinia-plugin-persistedstate": "3.2.1", + "qs": "6.11.2", + "vue": "3.4.27", + "vue-router": "4.3.2" + }, + "devDependencies": { + "@rushstack/eslint-patch": "~1.3.3", + "@vitejs/plugin-vue": "~4.5.2", + "@vue/eslint-config-prettier": "~8.0.0", + "eslint": "~8.49.0", + "eslint-plugin-vue": "~9.17.0", + "prettier": "~3.0.3", + "rollup-plugin-visualizer": "~5.12.0", + "sass": "~1.69.5", + "unplugin-auto-import": "~0.17.2", + "unplugin-vue-components": "~0.26.0", + "vite": "~5.2.0" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=10.0.0" + } +} diff --git a/public/favicon.ico b/public/favicon.ico new file mode 100644 index 0000000..df36fcf Binary files /dev/null and b/public/favicon.ico differ diff --git a/src/App.vue b/src/App.vue new file mode 100644 index 0000000..2ce9019 --- /dev/null +++ b/src/App.vue @@ -0,0 +1,7 @@ + + + + + diff --git a/src/api/login.js b/src/api/login.js new file mode 100644 index 0000000..20285e4 --- /dev/null +++ b/src/api/login.js @@ -0,0 +1,23 @@ +import http from '@/utils/request' + +/** 登录接口 + * @function login 登录接口 + * @param {Object} data 登录数据 + * @param {String} data.account 账号 + * @param {String} data.password 密码 + * */ +export const login = (data) => { + return http({ + url: '/login', + method: 'post', + data + }) +} + +export const test = () => { + return http({ + url: '/test', + method: 'post', + data: { uname: 'tom' } + }) +} diff --git a/src/assets/base.css b/src/assets/base.css new file mode 100644 index 0000000..e69de29 diff --git a/src/assets/reset.css b/src/assets/reset.css new file mode 100644 index 0000000..7841499 --- /dev/null +++ b/src/assets/reset.css @@ -0,0 +1,18 @@ +html, +body, +ul, +ol, +li, +img, +h1, +h2, +h3, +h4, +h5, +h6, +p, +div { + margin: 0; + padding: 0; +} + diff --git a/src/directives/index.js b/src/directives/index.js new file mode 100644 index 0000000..903b4f4 --- /dev/null +++ b/src/directives/index.js @@ -0,0 +1,6 @@ +import { setupPermissionDirective } from './perm' +const setupGlobDirectives = (app) => { + setupPermissionDirective(app) +} + +export { setupGlobDirectives } diff --git a/src/directives/perm.js b/src/directives/perm.js new file mode 100644 index 0000000..721abe4 --- /dev/null +++ b/src/directives/perm.js @@ -0,0 +1,12 @@ +const setupPermissionDirective = (app) => { + app.directive('perm', { + mounted(el, binding) { + const { value } = binding + if (value.includes(1)) { + el.hidden = true + } + } + }) +} + +export { setupPermissionDirective } diff --git a/src/locales/index.js b/src/locales/index.js new file mode 100644 index 0000000..d7462aa --- /dev/null +++ b/src/locales/index.js @@ -0,0 +1,5 @@ +const setupLocales = (app) => { + // app.use(i18n) +} + +export { setupLocales } diff --git a/src/main.js b/src/main.js new file mode 100644 index 0000000..771d828 --- /dev/null +++ b/src/main.js @@ -0,0 +1,33 @@ +import './assets/reset.css' +import './assets/base.css' + +import { createApp } from 'vue' +import App from './App.vue' + +import 'element-plus/dist/index.css' + +//router +import { setupRouter } from './router' +// store +import { setupStore } from './store' +// 全局指令 +import { setupGlobDirectives } from './directives' +// 多语言 +import { setupLocales } from './locales' + +const app = createApp(App) + +const bootstrap = async () => { + // store + setupStore(app) + // 全局自定义指令 + setupGlobDirectives(app) + // 多语言 + setupLocales(app) + // router + setupRouter(app) + + app.mount('#app') +} + +bootstrap() diff --git a/src/router/guard/index.js b/src/router/guard/index.js new file mode 100644 index 0000000..ffbb83a --- /dev/null +++ b/src/router/guard/index.js @@ -0,0 +1,8 @@ +export const useGuard = (router) => { + // 路由守卫 + router.beforeEach(() => {}) + + router.afterEach((to) => { + document.title = import.meta.env.VITE_APP_TITLE + ` | ${to.meta.title || ''}` + }) +} diff --git a/src/router/index.js b/src/router/index.js new file mode 100644 index 0000000..4488a72 --- /dev/null +++ b/src/router/index.js @@ -0,0 +1,20 @@ +import { createRouter, createWebHistory } from 'vue-router' +import { useGuard } from './guard' +import routes from './routes' + + +const router = createRouter({ + history: createWebHistory(import.meta.env.BASE_URL), + routes: routes, + scrollBehavior: () => ({ left: 0, top: 0, behavior: 'smooth' }) +}) + +// 路由守卫 +useGuard(router) + +// 挂载路由模块 +const setupRouter = (app) => { + app.use(router) +} + +export { router, setupRouter } diff --git a/src/router/routes/basic.js b/src/router/routes/basic.js new file mode 100644 index 0000000..21ba08a --- /dev/null +++ b/src/router/routes/basic.js @@ -0,0 +1,14 @@ +import { Home } from '@/views/Home' + +const homeRoute = { + path: '/', + name: 'home', + meta: { + title: '首页' + }, + component: Home +} + +const basicRoute = [homeRoute] + +export { basicRoute } diff --git a/src/router/routes/index.js b/src/router/routes/index.js new file mode 100644 index 0000000..69f7fe4 --- /dev/null +++ b/src/router/routes/index.js @@ -0,0 +1,5 @@ +import { basicRoute } from './basic' + +const routes = [...basicRoute] + +export default routes diff --git a/src/router/routes/module/whiteList.js b/src/router/routes/module/whiteList.js new file mode 100644 index 0000000..e69de29 diff --git a/src/store/index.js b/src/store/index.js new file mode 100644 index 0000000..f30737e --- /dev/null +++ b/src/store/index.js @@ -0,0 +1,11 @@ +import { createPinia } from 'pinia' +import piniaPluginPersistedstate from 'pinia-plugin-persistedstate' + +const store = createPinia() + +store.use(piniaPluginPersistedstate) +const setupStore = (app) => { + app.use(store) +} + +export { store, setupStore } diff --git a/src/store/module/user.js b/src/store/module/user.js new file mode 100644 index 0000000..12d2543 --- /dev/null +++ b/src/store/module/user.js @@ -0,0 +1,17 @@ +import { defineStore } from 'pinia' + +const useUserStore = defineStore('user', { + state: () => { + return { + userInfo: { + account: 'test' + } + } + }, + // 持久化存储 + persist: { + storage: window.localStorage + } +}) + +export { useUserStore } diff --git a/src/utils/ls.js b/src/utils/ls.js new file mode 100644 index 0000000..508d5b2 --- /dev/null +++ b/src/utils/ls.js @@ -0,0 +1,23 @@ +const getItem = (itemName) => { + return window.localStorage.getItem(itemName) +} + +const setItem = (itemName, itemValue) => { + return window.localStorage.setItem(itemName, itemValue) +} + +const removeItem = (itemName) => { + return window.localStorage.removeItem(itemName) +} + +const clear = () => { + return window.localStorage.clear() +} + +export const getToken = () => getItem('token') + +export const setToken = (token) => setItem('token', token) + +export const clearToken = () => removeItem('token') + +export const clearAll = () => clear() diff --git a/src/utils/px2vw.scss b/src/utils/px2vw.scss new file mode 100644 index 0000000..f15fb71 --- /dev/null +++ b/src/utils/px2vw.scss @@ -0,0 +1,5 @@ +$vw: 1900px; // 设计图宽度 + +@function px2vw($px, $vw: $vw) { + @return calc(($px / $vw) * 100vw); +} diff --git a/src/utils/request/index.js b/src/utils/request/index.js new file mode 100644 index 0000000..e2febf1 --- /dev/null +++ b/src/utils/request/index.js @@ -0,0 +1,67 @@ +import axios from 'axios' +import { useRouter } from 'vue-router' +import { ElMessage, ElNotification } from 'element-plus' + +const router = useRouter() + +const API_URL = import.meta.env.VITE_API_BASE_URL + +// console.log(API_URL) + +const $http = axios.create({ + baseURL: API_URL, + timeout: 30 * 1000, + headers: { + 'Content-Type': 'application/json;charset=utf-8' + }, + // 表示跨域请求时是否需要使用凭证 + withCredentials: false, + // 服务器响应的数据类型,可以是 'arraybuffer', 'blob', 'document', 'json', 'text', 'stream' + responseType: 'json' +}) + +// 请求拦截 +$http.interceptors.request.use( + (config) => { + config.headers.Authorization = localStorage.getItem('token') + ? `Bearer ${localStorage.getItem('token')}` + : 'NO TOKEN' + return config + }, + (error) => { + return Promise.reject(error) + } +) + +// 响应拦截 +$http.interceptors.response.use( + (res) => { + if (res.status === 200) { + const code = res.data.code || 0 + // 根据返回的自定义code来做不同的处理 + if (code === 401) { + ElMessage({ + type: 'warning', + message: '登录已过期 或 未登录,请重新登录', + duration: 2000 + }) + router.push({ path: '/login' }) + } + } else { + ElNotification({ + title: '错误', + message: res.data.message, + type: 'error', + duration: 2000 + }) + } + + return res.data + }, + + (error) => { + return Promise.reject(error) + } +) + +export default $http diff --git a/src/views/Home/index.js b/src/views/Home/index.js new file mode 100644 index 0000000..d42a8ac --- /dev/null +++ b/src/views/Home/index.js @@ -0,0 +1,3 @@ +import Home from './src/Home.vue' + +export { Home } diff --git a/src/views/Home/src/Home.vue b/src/views/Home/src/Home.vue new file mode 100644 index 0000000..150940f --- /dev/null +++ b/src/views/Home/src/Home.vue @@ -0,0 +1,17 @@ + + + + + diff --git a/vite.config.js b/vite.config.js new file mode 100644 index 0000000..4d8357d --- /dev/null +++ b/vite.config.js @@ -0,0 +1,81 @@ +import { defineConfig, loadEnv } from 'vite' +import { fileURLToPath, URL } from 'node:url' +import process from 'node:process' +import vue from '@vitejs/plugin-vue' + +// 按需导入 Element-plus 模块 +import AutoImport from 'unplugin-auto-import/vite' +import Components from 'unplugin-vue-components/vite' +import { ElementPlusResolver } from 'unplugin-vue-components/resolvers' + +// 打包文件可视化分析 +import { visualizer } from 'rollup-plugin-visualizer' + +// https://vitejs.dev/config/ +export default defineConfig(({ mode }) => { + return { + base: loadEnv(mode, process.cwd()).VITE_BASE_URL, + define: { + // 启用生产环境构建下激活不匹配的详细警告 + __VUE_PROD_HYDRATION_MISMATCH_DETAILS__: 'true' // vue 3.4+ + }, + plugins: [ + vue(), + // Element-plus 按需导入模块 + AutoImport({ + resolvers: [ElementPlusResolver()] + }), + Components({ + resolvers: [ElementPlusResolver()] + }), + visualizer({ + open: true, + filename: 'stats.html', + gzipSize: true, + brotliSize: true + }) + ], + resolve: { + alias: { + '@': fileURLToPath(new URL('./src', import.meta.url)), + '@v': fileURLToPath(new URL('./src/views', import.meta.url)), + '@c': fileURLToPath(new URL('./src/components', import.meta.url)) + } + }, + server: { + host: '0.0.0.0', + port: 3002, + proxy: { + /* '/api': { + target: 'http://localhost:8081/', + changeOrigin: true, + rewrite: (path) => path.replace(/^\/api/, '') + } */ + } + }, + esbuild: { + drop: ['console', 'debugger'] //去除生产环境下的console.xxxx和debugger + }, + build: { + // chunkSize 限制和警告 + chunkSizeWarningLimit: 500 * 1024, // 设置为500KB,超过此大小的chunk会发出警告 + // rollup配置 + rollupOptions: { + output: { + entryFileNames: 'js/[name]-[hash].js', + chunkFileNames: 'js/[name]-chunk-[hash].js', + assetFileNames: (assetInfo) => { + if (assetInfo.name.endsWith('.css')) { + return 'css/[name]-[hash].css' + } + const imgExts = ['.jpg', '.jpeg', '.png', '.webp', '.gif', '.svg', '.ico'] + if (imgExts.some((ext) => assetInfo.name.endsWith(ext))) { + return 'img/[name]-[hash].[ext]' + } + return 'assets/[name]-[hash].[ext]' + } + } + } + } + } +})