vue3+ts+axios+pinia动态路由

本文介绍了在一个Vue应用中,如何使用Pinia进行全局状态管理,以及如何通过axios进行API请求的封装,包括基础请求方法、错误处理和菜单权限获取。同时展示了路由守卫和菜单渲染的实现细节。
摘要由CSDN通过智能技术生成

项目结构介绍

                                  

模块名说明
views页面
storespinia全局状态管理
router路由
requestaxios请求封装
modules模块数据格式封装
directive自定义指令
components组件

 请求封装

        request工具类封装

//导入axios
import axios from "axios";
import type{ AxiosInstance, AxiosResponse, InternalAxiosRequestConfig, AxiosRequestConfig,AxiosError } from "axios"
//element-plus 弹窗
import { ElMessage } from 'element-plus'
//响应结果类型
import type{result} from '@/modules/base/Base.ts'
//导入路由对象
import router from '@/router/index'
//导入token状态管理
import {useUserInfoStore} from '@/stores/UserInfo'
//根路径
export const config={
  baseURL: 'http://localhost:9001',  
  timeout: 10000,
  headers:{
      'Content-Type':'application/json'
  }
}

//请求类
export class HttpBase{
  // axios的实例
  private instance: AxiosInstance;
  // 初始化的作用
  constructor(configs: AxiosRequestConfig) {
    // 创建axios的实例
    this.instance = axios.create(configs)
    // 配置拦截器
    this.interceptors()
  }
  private interceptors() {
    // 拦截请求
    this.instance.interceptors.request.use((config: InternalAxiosRequestConfig) => {
        //获取token
        const token=useUserInfoStore().getToken
        if(token){
          config.headers.token=token
        }
        return config
    }), (error: any) => {
        error.data = {}
        error.data.message = '服务器异常请联系管理员'
        return error
    }
    // 拦截响应
    this.instance.interceptors.response.use((res: AxiosResponse) => {
        //获取token
        const token=res.headers.token;
        if(token){
          useUserInfoStore().setToken(token)
        }
        // 这里面可以做一些接口返回状态的判断
        //执行成功返回状态码以2开头,主要作用对于修改成功、新增成功等操作的消息提醒
        if ( /^2/.test(res.data.code)) {
          if(res.data.code!=200){
            ElMessage.success(res.data.message)
          }
            return Promise.resolve(res.data)
        } else {
          //执行异常的消息提醒
            ElMessage.error(res.data.message)
            return Promise.reject(res.data.message || '接口报错')
        }
    },(error: AxiosError) => {
        if(error.code==="ERR_NETWORK"){
            ElMessage.error('请检查您的网络')

        }else{
            const status=error.response.status;
            HttpBase.errorHandle(status)
        }
        
        return Promise.reject(error)
    })
  }
  // 下面是对几个请求方式的封装
  // post
  protected post<T = result>(url: string, data?: object): Promise<T> {
    return this.instance.post(url, data)
  }
  //put
  protected put<T = result>(url: string, data?: object): Promise<T> {
    return this.instance.put(url, data)
  }
  // get
  protected get<T = result>(url: string, params?: object): Promise<T> {
    return this.instance.get(url, { params })
  }
  // delete
  protected delete<T = result>(url: string): Promise<T> {
    return this.instance.delete(url)
  }
  // 图片上传
  protected upload<T = result>(url: string, params?: object): Promise<T> {
    return this.instance.post(url, params, {
        headers: {
            'Content-Type': 'multipart/form-data'
        }
    })
  }
  private static errorHandle(status:number) {
    // 状态码判断
    switch (status) {
      case 404:
        router.push('/404')
        break
      case 500:
        router.push('/500')
        break
      default:
    }
  }
}

         菜单请求封装

import {HttpBase,config} from '@/request/base/index'
import type{ListMenu} from '@/modules/menu/MenuInfo'
// axios的配置项
const BaseUrl=config.baseURL+'/auth/menu'

class HttpMenu extends HttpBase{
  /**
   * 获取用户可访问权限
   * @returns 
   */
  async getMenuByUser():Promise<Array<ListMenu>>{
    //获取用户可访问菜单
    const url=BaseUrl+'/getMenuByUserId'
    return (await super.get(url)).data
  }
}
export default new HttpMenu(config);

数据实体封装

import type{BaseModule} from '@/modules/base/Base'
interface ListMenu extends BaseModule{
  menuName:string,//菜单名称
  menuCode:string,//菜单编码
  parentId:number,//菜单父id
  routerPath:string,//路由地址
  componentPath:string,//组件所在位置
  menuType:number,//菜单类型
  authorityCode:string,//权限编码
  requestUrl:string,//请求路径
  children:Array<ListMenu>//子级
  icon:string//菜单图标

}


export type{ListMenu} 

pinia全局状态管理封装

import { defineStore } from 'pinia'
import {computed, reactive, ref} from 'vue'
import type{ListMenu} from '@/modules/menu/MenuInfo'
import HttpMenu from '@/request/menu/Menu';
export const useMenuInfoStore = defineStore('menuInfo', () => {
  //获取标识符
    const menuFlag=ref<Boolean>(false)
    //获取菜单是否获取标识符
    const getMenuFlag=computed(()=>menuFlag.value)
    
    //响应式菜单集合数据
    const menuListInfo=reactive<Array<ListMenu>>([]);

    //获取菜单权限
    const getMenu=computed(async ()=>{
      //判断是否有权限
        if(!menuFlag.value){
          await getMenuByUser();
        }
        menuFlag.value=true;
        return menuListInfo;
    })
    //发送请求获取用户可访问菜单权限
    async function getMenuByUser(){
      try{
        const result=await HttpMenu.getMenuByUser()
        menuListInfo.push(...result)
     }catch(e){
        console.log(e)
     }
    }
    return {getMenu,getMenuFlag}
})

路由封装

import { createRouter, createWebHistory } from 'vue-router'
//登录页
import LoginView from '@/views/login/LoginView.vue'
//首页
import IndexView from '@/views/index/IndexView.vue'
//导入token 状态管理
import {useUserInfoStore} from '@/stores/UserInfo'
//导入获取权限状态管理
import { useMenuInfoStore } from '@/stores/MenuInfo'



const router = createRouter({
  history: createWebHistory(import.meta.env.BASE_URL),
  routes: [
    {
      path:'/',
      redirect:'/login'
    },
    {
      path:'/login',
      name:'login',
      component: LoginView
    },
    {
      path:'/index',
      name:'index',
      component: IndexView
    }
  ]
})
router.beforeEach( async (to,from,next)=>{
  
  const userStore=useUserInfoStore();
  const menuStore=useMenuInfoStore();
  //验证是否登录
  if(to.path!='/login'){
    //获取token
    const token=userStore.getToken
    if(!token){
      router.push('/login')
      return ;
    }
    const menuFlag=menuStore.getMenuFlag;
    //判断是否为首页并且没有获取过菜单权限
    if(!menuFlag ){
    const modules = import.meta.glob("@/views/*/*.vue");
    //获取菜单权限
    const menuList= await menuStore.getMenu
    //循环遍历菜单
    for(const menuInfo of menuList){
     
      const children=menuInfo.children
      
      if(children){
      
        for(const childrenInfo of children){
            const menu={ 
              path:childrenInfo.routerPath,
              name:childrenInfo.menuCode,
              // component :() => import(`..${childrenInfo.componentPath}.vue`)
              component :modules[`/src${childrenInfo.componentPath}.vue`]
            }
            router.addRoute('index',menu)
        }
      }
    }
   
    next({...to,replace:true})
    return
  }
}
  next()
})
export default router

菜单渲染

<template>
  <el-row class="tac">
    <el-col >
      <el-menu class="el-menu-vertical-demo" :router="true">
        <el-menu-item index="/index">
          <el-icon><House /></el-icon>
          <span>首页</span>
        </el-menu-item>
        <el-sub-menu v-for="menu in menuList" :key="menu.id" :index="menu.id">
          <template #title>
            <el-icon><component :is="menu.icon"></component></el-icon>
            <span>{{ menu.menuName }}</span>
          </template><el-menu-item-group>
            <template v-for="children in menu.children" :key="children.id">
              <el-menu-item :index="children.id" :route="children.routerPath">
                {{ children.menuName }}
              </el-menu-item>
            </template>
          </el-menu-item-group>
        </el-sub-menu>
      </el-menu>
    </el-col>
  </el-row>
</template>

<script lang="ts" setup>
import { useMenuInfoStore } from '@/stores/MenuInfo'
import { reactive } from 'vue'
import type { ListMenu } from '@/modules/menu/MenuInfo'
import { onMounted } from 'vue'
//菜单集合
let menuList = reactive<Array<ListMenu>>([])
  //菜单全局管理对象
const menuStore=useMenuInfoStore();
//获取菜单集合
const getMenu = async () => {
  const menuInfoList = await menuStore.getMenu
  menuList.push(...menuInfoList)
}
onMounted(() => {
  getMenu()
})
</script>
<style scoped>
.el-row {
  height: 100%;
}
.el-menu {
  height: 100%;
}

</style>

  • 8
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
在Pinia中封装axios请求头可以通过在store中定义一个action来实现。首先,在store中引入axios库,并在action中使用axios发送请求。然后,在请求中设置请求头。以下是一个示例代码: ```javascript import { defineStore } from 'pinia'; import axios from 'axios'; export const useUserInfoStore = defineStore('UserInfo', { state: () => ({ // 状态数据 }), actions: { async fetchData() { try { const response = await axios.get('https://api.example.com/data', { headers: { // 设置请求头 Authorization: 'Bearer your_token', 'Content-Type': 'application/json', }, }); // 处理响应数据 // ... } catch (error) { // 处理错误 // ... } }, }, }); ``` 在上述代码中,我们在`fetchData` action中使用axios发送GET请求,并在请求中设置了请求头。你可以根据自己的需求修改请求方法和请求头的内容。 请注意,这只是一个示例代码,你需要根据自己的实际情况进行修改和适配。 #### 引用[.reference_title] - *1* *2* [用Pinia在vue3中实现axios请求](https://blog.csdn.net/qq_49668888/article/details/130981307)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insert_down1,239^v3^insert_chatgpt"}} ] [.reference_item] - *3* [vue3+ts+axios+pinia实现无感刷新](https://blog.csdn.net/weixin_73184582/article/details/129518773)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insert_down1,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

半夜燃烧的香烟

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值