vue3后台管理系统教程2(elementplus+vue-router+axios+pinia等配置)

1,606 阅读5分钟

一、 集成Element-plus

该项目使用的框架是element-plus,首先需要安装和配置element-plus。

1.安装Element Plus和图标组件

npm install element-plus @element-plus/icons-vue

2.全局注册

main.ts:

import ElementPlus from "element-plus";
import "element-plus/dist/index.css";

createApp(App).use(ElementPlus).mount('#app)

3.Element Plus全局组件类型声明

// tsconfig.json
{
  "compilerOptions": {
    // ...
    "types": ["element-plus/global"]
  }
}

4. 页面使用 Element Plus 组件和图标

<!-- src/App.vue -->
<template>
  <div style="text-align: center;margin-top: 10px">
    <el-button :icon="Search" circle></el-button>
    <el-button type="primary" :icon="Edit" circle></el-button>
    <el-button type="success" :icon="Check" circle></el-button>
    <el-button type="info" :icon="Message" circle></el-button>
    <el-button type="warning" :icon="Star" circle></el-button>
    <el-button type="danger" :icon="Delete" circle></el-button>
  </div>
</template>

<script lang="ts" setup>
     import HelloWorld from '/src/components/HelloWorld.vue'
     import {Search, Edit,Check,Message,Star, Delete} from '@element-plus/icons-vue'
</script>

二、路径别名配置

1.vite.config.ts配置

// vite.config.ts
import {defineConfig} from 'vite'
import vue from '@vitejs/plugin-vue'

import path from 'path'

export default defineConfig({
    plugins: [vue()],
    resolve: {
        alias: {
            "@": path.resolve("./src") // 相对路径别名配置,使用 @ 代替 src
        }
    }
})

引用path的时候会报类型错误,记得npm add -D @types/node,安装完以后会多一个文件tsconfig.node.json。

2.TypeScript 编译配置

因为 typescript 特殊的 import 方式 , 需要配置允许默认导入的方式,还有路径别名的配置。

// tsconfig.json
{
  "compilerOptions": {
    "baseUrl": "./", // 解析非相对模块的基地址,默认是当前目录
    "paths": { //路径映射,相对于baseUrl
      "@/*": ["src/*"] 
    },
    "allowSyntheticDefaultImports": true // 允许默认导入
  }
}

三、环境变量配置

image.png

# 变量必须以 VITE_ 为前缀才能暴露给外部读取
NODE_ENV = 'development'
VITE_APP_TITLE = '商城后台管理系统'
VITE_APP_PORT = 3002
VITE_APP_BASE_API = '/dev-api'

配置运行命令:

"scripts": {
    "dev": "vite", // dev环境不需要添加 --mode,默认就是 development
    "build:test": "vue-tsc && vite build --mode test",
    "build:pro": "vue-tsc && vite build --mode production",
}

可以通过process.env.NODE_ENV获取环境变量。获取其他的变量:可通过loadEnv()函数可以获取配置文件中的参数。

import { defineConfig, loadEnv } from 'vite'
export default defineConfig((config) => {
   // 根据当前工作目录中的 `mode` 加载 .env 文件
  // 设置第三个参数为 '' 来加载所有环境变量,而不管是否有 `VITE_` 前缀。
  const { command, mode } = config
  const env = loadEnv(mode, process.cwd(), '')
  console.log(env.VITE_APP_TITLE)
})

四、集成Axios

Axios 是一个流行的基于 Promise 的 HTTP 客户端库,用于在浏览器和 Node.js 中发出 HTTP 请求。Axios 还支持请求和响应的拦截器。

1.请求拦截

请求拦截会在请求发送之前执行,一般用于:

  • 修改请求配置(请求头添加token等);
  • 在请求发送前展示加载动画或提示用户 请求拦截器使用 axios.interceptors.request.use() 方法来注册。该方法接受两个函数作为参数,第一个函数用于成功拦截请求,第二个函数用于拦截请求发生错误的情况。

2.响应拦截

响应拦截会在接收到响应数据后执行,一般用于:

  • 根据错误状态码进行全局提示处理
  • 对响应数据进行格式化或转换并返回
  • 在接收到响应后隐藏加载动画或处理其他通用逻辑 和请求拦截一样,该方法也接受两个函数作为参数。

完整代码(utils/http/index.ts)

import axios from "axios";
import { useUserStore } from "@/store/modules/user";
import router from "@/router/index";
import type {
  AxiosInstance,
  AxiosError,
  AxiosRequestConfig,
  AxiosResponse,
} from "axios";
import type { ResultsData } from "./type";
import { ElMessage } from "element-plus";
import resetStore from "../reset";

const instance: AxiosInstance = axios.create({
  baseURL: import.meta.env.VITE_APP_BASE_API,
  timeout: 1000,
});

// 添加请求拦截器
instance.interceptors.request.use(
  (config) => {
    const userStore = useUserStore();
    // 在发送请求之前做些什么
    const token = userStore.token;
    if (token) {
      config.headers.token = token;
    }
    return config;
  },
  (error: AxiosError) => {
    // 对请求错误做些什么
    ElMessage.error(error);
    return Promise.reject(error);
  },
);

// 添加响应拦截器
instance.interceptors.response.use(
  (response: AxiosResponse) => {
    // 2xx 范围内的状态码都会触发该函数。
    // 对响应数据做点什么
    const { data } = response;
    // 登录失效
    if (data?.code === 203) {
      ElMessage.error(data.message || "请求失败");
      resetStore();
      router.replace("/login");
      return Promise.reject(data);
    }
    if (data?.code !== 200) {
      ElMessage.error(data.message || "请求失败");
      return Promise.reject(data);
    }
    return data;
  },
  (error: AxiosError) => {
    // 超出 2xx 范围的状态码都会触发该函数。
    // 对响应错误做点什么
    let message = "";
    // HTTP 状态码
    const status = error.response?.status;
    switch (status) {
      case 401:
        message = "token 失效,请重新登录";
        break;
      case 403:
        message = "拒绝访问";
        break;
      case 404:
        message = "请求地址错误";
        break;
      case 500:
        message = "服务器故障";
        break;
      default:
        message = "网络连接故障";
    }

    ElMessage.error(message);
    return Promise.reject(error);
  },
);

const http = {
  get<T>(
    url: string,
    data?: object,
    config?: AxiosRequestConfig,
  ): Promise<ResultsData<T>> {
    return instance.get(url, { data, ...config });
  },
  post<T>(
    url: string,
    data?: object,
    config?: AxiosRequestConfig,
  ): Promise<ResultsData<T>> {
    return instance.post(url, data, config);
  },
  put<T>(
    url: string,
    data?: object,
    config?: AxiosRequestConfig,
  ): Promise<ResultsData<T>> {
    return instance.put(url, data, config);
  },

  delete<T>(
    url: string,
    data?: object,
    config?: AxiosRequestConfig,
  ): Promise<ResultsData<T>> {
    return instance.delete(url, { data, ...config });
  },
};

export default http;

五、集成Pinia

yarn add pinia
# 或者使用 npm
npm install pinia

1.创建一个 pinia

新建store/index.ts

import { createPinia } from "pinia";
import piniaPluginPersistedstate from "pinia-plugin-persistedstate";

const pinia = createPinia();
// piniaPersist(持久化)
pinia.use(piniaPluginPersistedstate);

export default pinia;

在main.ts使用pinia

import { createApp } from 'vue'
import pinia from "@/store";
createApp(App).use(pinia).mount('#app')

2.定义一个store

defineStore的第一个参数是store唯一的id,注意不能重复。
新建modules/user/index.ts:

import { defineStore } from "pinia";
import type { UserInfo } from "./type";
import { getUserInfo, logout } from "@/api";
import { useAuthStore } from "../auth";
import RESEETSTORE from "@/utils/reset";
//一个参数是应用程序中 store 的唯一 id
export const useUserStore = defineStore("user", {
  state: () => {
    return {
      userInfo: null,
      token: "",
    };
  },
  actions: {
    setUserInfo(userInfo: UserInfo) {
      this.userInfo = userInfo;
    },
    setToken(token: string) {
      this.token = token;
    },
    async GetInfoAction() {
      const authStore = useAuthStore();
      const { data } = await getUserInfo();
      const { avatar, name, buttons, roles, routes } = data;
      this.setUserInfo({ avatar, name });
      authStore.setAuth({ buttons, roles, routes });
    },
    async logout() {
      await logout();
      RESEETSTORE();
    },
  },
  //持久化配置
  persist: true,
});

3.使用useUserStore

通过const userStore = useUserStore(),获取到对应store的所有信息,包活state、action、gettter等。
在login.vue中使用:

import { useUserStore } from "@/store/modules/user";
const userStore = useUserStore();
const handleSubmitForm = (formEle: FormInstance | undefined | null) => {
  formEle.validate(async (valid) => {
    if (valid) {
      let { data } = await login(loginForm);
      userStore.setToken(data);
    }
  });
};

六、集成router

安装vue-router

npm install vue-router@4

1.创建路由实例

官网例子:

// 1. Define route components.
// These can be imported from other files
const Home = { template: '<div>Home</div>' }
const About = { template: '<div>About</div>' }

// 2. Define some routes
// Each route should map to a component.
// We'll talk about nested routes later.
const routes = [
  { path: '/', component: Home },
  { path: '/about', component: About },
]

// 3. Create the router instance and pass the `routes` option
// You can pass in additional options here, but let's
// keep it simple for now.
const router = VueRouter.createRouter({
  // 4. Provide the history implementation to use. We are using the hash history for simplicity here.
  history: VueRouter.createWebHashHistory(),
  routes, // short for `routes: routes`
})

// 5. Create and mount the root instance.
const app = Vue.createApp({})
// Make sure to _use_ the router instance to make the
// whole app router-aware.
app.use(router)

app.mount('#app')

// Now the app has started!

首先定义route,并引入组件映射

import { createRouter, createWebHashHistory, RouteRecordRaw } from 'vue-router'

export const constantRoutes: Array<RouteRecordRaw> = [
  {
    path: '/',
    component: () => import('@/views/home/index.vue'),
  },
  {
    path: '/login',
    component: () => import('@/views/login/index.vue'),
  },
  {
    path: '/401',
    component: () => import('@/views/error-page/index.vue'),
  },
]

// 创建路由实例
const router = createRouter({
  history: createWebHashHistory(),
  routes: constantRoutes as RouteRecordRaw[],
  // 刷新时,滚动条位置还原
  scrollBehavior: () => ({ left: 0, top: 0 }),
})

export default router

main.ts中,全局注册router

// main.ts
import router from "@/router";

const app = createApp(App)

app.use(router).mount('#app')

参考来源juejin.cn/post/719685…