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 = newTokenName
tokenInfo.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-vue
npm 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) => {
// 请求前回调
// 添加token
const tokenStore = useTokenStore()
// 判断有无token
if (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" // 局域网其他电脑可访问
}
}
})