1. 准备工作
1.1 清除项目自带页面
删除views和components目录下所有东西:
1.2 修改App.vue
<script setup lang="ts"></script><template><router-view></router-view>
</template><style scoped></style>
1.3 修改main.css
修改assets/main.css, 默认样式会影响布局
body {margin: 0;
}
1.4 安装 scss
npm install -D sass
2. 创建路由配置
在router/index.ts 创建登录页面路由配置
import { createRouter, createWebHistory } from 'vue-router'const router = createRouter({history: createWebHistory(import.meta.env.BASE_URL),routes: [{path: '',redirect: "/login"},{path: '/login',name: 'login',component: () => import('@/views/Login.vue')},{path: '/401',component: () => import('@/views/error/401.vue')},{path: '/404',component: () => import('@/views/error/404.vue')},]
})export default router
3. 使用pinia 存储token
3.1 pinia持久化
安装插件, 文档: https://prazdevs.github.io/pinia-plugin-persistedstate/zh/guide/
npm i pinia-plugin-persistedstate
在main.ts 中配置:
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'
const pinia = createPinia()
pinia.use(piniaPluginPersistedstate)
使用:
{persist: true // 持久化存储
}
注意: 不要使用js版本的pinia-plugin-persist,导入时会因为类型问题报错
3.2 在stores下创建token.ts, 存储token
// 定义 store
import { defineStore } from "pinia"
import {reactive, ref} from 'vue'
/*第一个参数:名字,唯一性第二个参数:函数,函数的内部可以定义状态的所有内容返回值: 函数*/
export const useTokenStore = defineStore('token', () => {// 响应式变量const tokenInfo = reactive({tokenName: '',tokenValue: ''})// 修改token值函数const setToken = (newTokenName: string, newTokenValue: string) => {tokenInfo.tokenName = newTokenNametokenInfo.tokenValue = newTokenValue}// 移除token值函数const removeToke = () => {tokenInfo.tokenName = ''tokenInfo.tokenValue = ''}return {tokenInfo, setToken, removeToke}
},
{persist: true // 持久化存储
}
)
4.安装 ElementPlus, 并使用
4 .1 安装配置
npm install element-plus --save
ElenentPlus 支持完整导入,按需导入,具体可参考官方文档, 这里使用官网推荐方式,使用按需自动导入。
需要安装unplugin-vue-components 和 unplugin-auto-import这两款插件:
npm install -D unplugin-vue-components unplugin-auto-import
按官网文档, 在vite.config.ts进行如下配置:
// vite.config.ts
import { defineConfig } from 'vite'
import AutoImport from 'unplugin-auto-import/vite'
import Components from 'unplugin-vue-components/vite'
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'export default defineConfig({// ...plugins: [// ...AutoImport({resolvers: [ElementPlusResolver()],}),Components({resolvers: [ElementPlusResolver()],}),],
})
会在根目录下生成这两个文件, 插件会自动处理组将的导入和注册
4.2 图标自动导入
安装依赖
npm install @element-plus/icons-vuenpm i -D unplugin-icons unplugin-auto-import
在vite.config.添加配置:
完整配置文件:
import { fileURLToPath, URL } from 'node:url'import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import vueDevTools from 'vite-plugin-vue-devtools'import AutoImport from 'unplugin-auto-import/vite'
import Components from 'unplugin-vue-components/vite'
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'
// ElementPlus的Icon自动导入
import Icons from 'unplugin-icons/vite'
import IconsResolver from 'unplugin-icons/resolver'// https://vitejs.dev/config/
export default defineConfig({plugins: [vue(),vueDevTools(),AutoImport({resolvers: [ElementPlusResolver(),// 自动导入图标IconsResolver({prefix: 'Icon',}),]}),Components({resolvers: [ElementPlusResolver(),// 自动注册图标IconsResolver({enabledCollections: ['ep'],}),]}),],resolve: {alias: {'@': fileURLToPath(new URL('./src', import.meta.url))}}
})
注: 使用自动导入引用时和其他方式不同:
原来: <Lock />
自动导入: <i-ep-lock />
4.3 ElMessage 导入找不到问题
auto-imports.d.ts中添加:
const ElMessage: typeof import('element-plus/es')['ElMessage']
在tsconfig.app.json 添加:
5. axios 安装配置
安装:
npm install axios
5.1 axios 配置
在src目录下创建request目录, 在目录下创建axios-config.ts文件。
import axios, { type AxiosInstance } from 'axios'// 定义公共前缀,创建请求实例
const baseURL = '/api/'
const instance: AxiosInstance = axios.create({baseURL})import { useTokenStore } from '@/stores/token'// 配置请求拦截器
instance.interceptors.request.use((config) => {// 请求前回调// 添加tokenconst tokenStore = useTokenStore()// 判断有无tokenif (tokenStore.tokenInfo) {config.headers[tokenStore.tokenInfo.tokenName] = tokenStore.tokenInfo.tokenValue}return config},(err) => {// 请求错误的回调Promise.reject(err)}
)import router from "@/router";
// 添加响应拦截器
instance.interceptors.response.use(result => {// //console.log("header:", result)// 判断业务状态码if (result.data.code === 0) {// return result.data;return result.data} else if (result.data.code === 1) {// 操作失败ElMessage.error(result.data.message ? result.data.message : '服务异常')// 异步操作的状态转换为失败return Promise.reject(result)} else {return result}},err => {// 判断响应状态码, 401为未登录,提示登录并跳转到登录页面if (err.response.status === 401) {ElMessage.error('请先登录')router.push('/login')} else if (err.response.status === 403){ElMessage.error('登录超时')router.push('/login')} else {ElMessage.error('服务异常')}// 异步操作的状态转换为失败return Promise.reject(err) }
)export default instance
6. 服务代理配置
在项目根目录下创建两个环境配置文件,分别配置开发和生产环境配置
.env.production // 生产环境
VITE_MODE_NAME=pro
VITE_BASE_URL=api
VITE_TARGET_URL=http://localhost:8999/
.env.development // 开发环境
VITE_MODE_NAME=dev
VITE_BASE_URL=api
VITE_TARGET_URL=http://127.0.0.1:8999/
在vite.config.ts配置:
server: {proxy: {'/api': { // 获取路径中包含了/api的请求//target: 'http://192.168.1.51:8999', // 服务端地址target: env.VITE_TARGET_URL,changeOrigin: true, // 修改源rewrite:(path) => path.replace(/^\/api/, '') // api 替换为 ''}},host: "0.0.0.0" // 局域网其他电脑可访问}
完整配置:
import { fileURLToPath, URL } from 'node:url'import { defineConfig, loadEnv } from 'vite'
import vue from '@vitejs/plugin-vue'
import vueDevTools from 'vite-plugin-vue-devtools'import AutoImport from 'unplugin-auto-import/vite'
import Components from 'unplugin-vue-components/vite'
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'
// ElementPlus的Icon自动导入
import Icons from 'unplugin-icons/vite'
import IconsResolver from 'unplugin-icons/resolver'// 引入path
import path from 'path'// https://vitejs.dev/config/
export default defineConfig(({mode}) => {const env = loadEnv(mode, process.cwd()); return {plugins: [vue(),vueDevTools(),AutoImport({resolvers: [ElementPlusResolver(),// 自动导入图标IconsResolver({prefix: 'Icon',}),]}),Components({resolvers: [ElementPlusResolver(),// 自动注册图标IconsResolver({enabledCollections: ['ep'],}),]}),// 自动安装图标Icons({autoInstall: true,}),],resolve: {alias: {'@': fileURLToPath(new URL('./src', import.meta.url))}},server: {proxy: {'/api': { // 获取路径中包含了/api的请求//target: 'http://192.168.1.51:8999', // 服务端地址target: env.VITE_TARGET_URL,changeOrigin: true, // 修改源rewrite:(path) => path.replace(/^\/api/, '') // api 替换为 ''}},host: "0.0.0.0" // 局域网其他电脑可访问}
}
})