vue2项目从0搭建(二):配置代理,登录功能和菜单权限

news2024/11/28 6:52:29

前言:

发送ajax,fetch,websocket请求获取服务端的数据,配置代理是必须的环节

登录功能和菜单权限是后台管理系统中非常经典且十分重要的业务,这里涉及的知识点也是比较多的,坑也多,面试也是很重要的一环。

这里必须得会,没错是必须

配置服务代理

创建两个node服务

在和src平级的目录创建一个服务器文件,我这里给的名字是node_server,里面创建两个服务

下载express
npm i express
服务1:server1.js
const express = require('express')

const app1 = express()
app1.get('/server1',(req,res)=>{
    res.send('我是server1')
})

//开启3001接口的服务
app1.listen(3001)
服务2:server2.js
const express = require('express')

const app2 = express()
app2.get('/server2',(req,res)=>{
    res.send('我是server2')
})

//开启3000接口的服务
app2.listen(3002)

然后打开两个终端,分别启用这两个服务

如图所示,server2.js同理

 测试自己的服务

没问题,模拟了最简单的服务

vue工程配置代理服务器(核心代码)

在vue.config.js中配置devserver

const { defineConfig } = require('@vue/cli-service')
module.exports = defineConfig({
  transpileDependencies: true,
  //关闭语法检查
  lintOnSave: false,
  //配置代理(可配置多个)
  devServer:{
    port:8080,
    open:true,
    proxy:{
       //代理1   
      '/server1':{
        target:'http://localhost:3001',
        ws:false,
        changeOrigin:true,
        pathRewrite:{
          '^/server1':''
        }
      },
       //代理2
      '/serve2':{
        target:'http://localhost:3002',
        ws:false,
        changeOrigin:true,
        pathRewrite:{
          '^/server2':''
        }
      }
    }
  }
})

 配置axios请求工具

axios文档:axios中文文档

下载axios

npm i axios

 创建一个request.js文件,对axios进行基础的配置

import axios from 'axios'

const instance = axios.create({
    // baseURL: '',
    // timeout: 1000,
    // headers: {'X-Custom-Header': 'foobar'}
  });


// 添加请求拦截器
instance.interceptors.request.use(function (config) {

    return config;
  }, function (error) {

    return Promise.reject(error);
  });

// 添加响应拦截器
instance.interceptors.response.use(function (response) {
 
    //在这里修改一下,将response改为response.data,方便提取响应数据
    return response.data;
  }, function (error) {
  
    return Promise.reject(error);
  });
console.log(instance,'axios实例')
  export default instance

 在main.js中引入并配置到vue实例上

//...
import axios from '@/utils/request' 
Vue.prototype.$axios = axios
new Vue({
  //...
  axios,
  render: h => h(App),
}).$mount('#app')

测试demo

    created(){
      this.$axios.get('/server1/login').then(res=>{
        console.log(res,'接口1请求回来的数据')
      })
      this.$axios.get('/server2/getRole').then(res=>{
        console.log(res,'接口2请求回来的数据')
      })
    }

 

登录功能和token校验

创建登录接口

server1.js(登录接口)

const express = require('express')

const app1 = express()
app1.get('/login', (req, res) => {
    switch (req.query.user) {
        case 'admin':  //管理员
            res.send({
                userName: 'admin',
                token: 'admin_token'
            })
            break
        case 'common':  //普通
            res.send({
                userName: 'common',
                token: 'common_token'
            })
            break
        case 'temporary':  //临时
            res.send({
                userName: 'temporary',
                token: 'temporary_token'
            })
            break
    }

})

//开启3001接口的服务
app1.listen(3001)

 极简版登录页面(将就着看吧)

<template>
  <div class="loginCom">
    <div class="loginForm">
    <el-button @click="login('admin')" type="primary">管理人员登录</el-button>
    <el-button @click="login('common')" type="primary">普通用户登录</el-button>
    <el-button @click="login('temporary')" type="primary">临时用户登录</el-button>
    </div>

  </div> 
</template>
<script>
 export default {
    name:'login',
    data(){
        return{

        }
    },
    methods:{
      login(userType){
        this.$axios.get(`/server1/login?user=${userType}`,).then(res=>{
          //存储用户信息
          this.$store.dispatch('changeUserInfo',res)
          localStorage.setItem('vue2_userInfo',JSON.stringify(res))
          localStorage.setItem('vue2_token',res.token)
          this.$router.push('/dashboard')
        })
      }
    },
    created(){
    }
 }
</script>
<style lang="less" scoped>
.loginCom{
  display: flex;
  width: 100%;
  height: 100vh;
  justify-content: center;
  align-items: center;
}
</style>

使用路由拦截器进行token校验(核心代码)

在router/index.js中配置

//...
const router = new Router({
    routes:RouteList,
    mode:'history'
})

router.beforeEach((to,from,next)=>{
    let token = localStorage.getItem('vue2_token')
    //判断是否有token
    if(token){
    //如果是去往登录页面,直接返回到主页
        if(to.path === '/login'){
            next('/dashboard')
        }else{
    //去原定跳转的页面
            next()
        }
    }else{
    //没有token的情况下,所有路由都指向登录页面
        if(to.path === '/login'){
            next()
        }else{
            next('/login')
        }
    }
})

export default router

这里不做过多解释,仅展示具体代码和demo效果

有需要的可以去vue-router官方文档去看,那里有很详细的解释

路由权限设计

创建权限接口

  server2.js(权限接口)

const express = require('express')

const app2 = express()
app2.get('/getRole', (req, res) => {
    switch (req.query.userType) {
        case 'admin':  //管理人员权限
            res.send([
                'instructions', 'communication', 'common', 'admin'
            ])
            break
        case 'common':  //普通用户权限
            res.send([
                'instructions', 'communication', 'common'
            ])
            break
        case 'temporary':  //临时用户权限
            res.send([
                'instructions', 'communication'
            ])
    }
})

//开启3000接口的服务
app2.listen(3002)
给vuex配置新的全局值
const userMode = {
    state:()=>({
        //用户信息
        userInfo:{
            name:'wjt',
            age:28
        },
        //权限字段
        userRole:[],
        //符合权限的异步路由
        asyncRouteList:[]
    }),
    mutations:{
        CHANGE_USEINFO:(state,info)=>{
            state.userInfo = info
        },
        CHANGE_USERROLE:(state,info)=>{
            state.userRole = info
        },
        CHANGE_ASYNCROUTELIST:(state,info)=>{
            state.asyncRouteList = info
        }
    },
    actions:{
        changeUserInfo:(({commit},data)=>{
            commit('CHANGE_USEINFO',data)
        }),
        changeUserRole:(({commit},data)=>{
            commit('CHANGE_USERROLE',data)
        }),
        changeAsyncRouteList:(({commit},data)=>{
            commit('CHANGE_ASYNCROUTELIST',data)
        })
    }
}
export default userMode
新增路由

普通用户页面和管理员页面都是根据用户角色的权限去展示的

const list = [
    {
        name:'dashboard',
        path:'/dashboard',
        meta:{
            name:'主页',
        },
        component:()=>import('@/pages/dashboard/index.vue'),
        children:[
            {
                name:'communication',
                path:'/dashboard/communication',
                meta:{
                    name:'组件通信',
                    icon:'el-icon-phone'
                },
                component:()=>import('@/pages/communication/index.vue')
            },
            {
                name:'instructions',
                path:'/dashboard/instructions',
                meta:{
                    name:'指令',
                    icon:'el-icon-thumb'
                },
                component:()=>import('@/pages/instructions/index.vue')
            },
            {
                name:'common',
                path:'/dashboard/common',
                meta:{
                    name:'普通用户页面',
                    icon:'el-icon-user'
                },
                component:()=>import('@/pages/common/index.vue')
            },
            {
                name:'admin',
                path:'/dashboard/admin',
                meta:{
                    name:'管理员页面',
                    icon:'el-icon-s-custom'
                },
                component:()=>import('@/pages/admin/index.vue')
            },
        ]
    },

]
export default list
在路由守卫中根据权限动态添加路由(核心代码)
import Vue from 'vue'
import Router from 'vue-router'
import asyncRouteList from './asyncRouteList'
import globalRouteList from './globalRouteList'
import store from '@/store/index'
import {getUserRoleFun} from '@/globalFun/index'
import { cloneDeep } from 'lodash'

const RouteList = [].concat(globalRouteList)
let initAsyncList = cloneDeep(asyncRouteList)
Vue.use(Router)

const router = new Router({
    routes:RouteList,
    mode:'history'
})
if(localStorage.getItem('vue2_userInfo')){
    store.dispatch('changeUserInfo',JSON.parse(localStorage.getItem('vue2_userInfo')))
}

//根据后台返回权限字段匹配路由
function addAsyncRouterFun(roleInfo){
    let filerOverList = []
    filterRouteFun(initAsyncList[0].children,roleInfo,filerOverList)
    let allAsyncRouteList = asyncRouteList[0]
    allAsyncRouteList.children = filerOverList    
    router.options.routes = [].concat(globalRouteList)
    router.addRoutes([allAsyncRouteList])   //动态添加路由
    router.options.routes.push(allAsyncRouteList)  //给$router的options添加权限路由
    store.dispatch('changeAsyncRouteList',allAsyncRouteList.children)
}

//递归匹配多级路由
function filterRouteFun(initAsyncList,roleInfo,filerOverList){
    initAsyncList.forEach(item=>{
    if(roleInfo.includes(item.name)){
        let RouteItem = cloneDeep(item)
        if(RouteItem.children){
         RouteItem.children = []
         filterRouteFun(item.children,roleInfo,RouteItem.children)
        }
        filerOverList.push(RouteItem)
    }
   })
}

router.beforeEach(async(to,from,next)=>{
    let token = localStorage.getItem('vue2_token')
    if(token){
        let roleList = store.state.user.userRole
        //没有权限数据
        if(roleList.length<1){
            let role = await getUserRoleFun(store.state.user.userInfo.userName)  //请求权限数据
            store.dispatch('changeUserRole',role) 
            addAsyncRouterFun(role)  //根据权限字段值过滤符合添加的路由配置项
            next({...to,replace:true}) //重新访问,走一遍前置守卫
        }else{
            if(to.path === '/login'){
                next('/dashboard')
            }else{
                next()
            }
        }

    }else{
        if(to.path === '/login'){
            next()
        }else{
            next('/login')
        }
    }
})

export default router

获取权限参数的方法globalFun/index.js

import axios from '@/utils/request'
export function getUserRoleFun(userType){
    return new Promise(resolve=>{
        axios.get(`/server2/getRole?userType=${userType}`)
        .then(res=>{
            resolve(res)         
        }).catch(error=>{
            console.log(error,'error')
        })
    },reject=>{
        reject('error')
    }) 
}
退出登录清理用户信息
      outLogin(){
        this.$store.dispatch('changeUserInfo',{
          name:'wjt'
        })
        this.$store.dispatch('changeUserRole',[])
        this.$store.dispatch('changeAsyncRouteList',[])
        localStorage.removeItem('vue2_userInfo')
        localStorage.removeItem('vue2_token')
        this.$router.push('/login')
      }
效果如下

管理员

 普通用户

 临时用户

 ok,结束!

后语及存在的坑点

这里写的东西还是比较多的,虽然功能常见,网上也一大堆子,但是我这里的代码基本都能直接用,你只具体的核心代码也都给标注出来了。

其实很多人在实际开发开发业务中未必会有机会写真实的登录和路由权限场景,但是还是必须得会,很重要。

而且这里是有坑的,比如很多人在开发时会遇到两个问题

1.是和devserver有关的一个遮蔽框,虽然你可以把它关掉,但是还是很坑

你需要关闭开发阶段的报错和警告(这里是说框架提供的,而非你业务代码里的)

解决方法:vue.config.js

  devServer:{
     client:{
       overlay:false
     },
     ...
}

2.addRoutes不生效白屏的问题(这里的代码已经解决了这个问题,但我没有解释)

其实很多人对路由前置守卫中动态添加路由没有太多的理解,开发中不撞几次墙都不行的,这里有一个解释我没有纯手敲,而是引用别人的博客内容,感觉他写的比较清晰明白,大家请看:

Vue 动态添加用户的权限路由(动态路由) - 知乎

懒得去看的直接看我截图总结

我对这篇文章绝对是用心了,感谢大家给个赞或收藏

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1216312.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

±15kV ESD 保护、3V-5.5V 供电、真 RS-232 收发器MS2232/MS2232T

产品简述 MS2232/MS2232T 芯片是集成电荷泵&#xff0c;具有 15kV ESD 保护的 RS-232 收发器&#xff0c;包括两路接收器、两路发送器。 芯片满足 TIA/EIA-232 标准&#xff0c;为异步通信控制器和串口连 接器提供通信接口。 芯片采用 3V-5.5V 供电&#xff0c;电荷泵仅用…

代码随想录算法训练营|五十五天

两个字符串的删除操作 583. 两个字符串的删除操作 - 力扣&#xff08;LeetCode&#xff09; 因为两个字符串都能删除&#xff0c;所以字符不匹配的话就有三个方向取最小值赋值给dp[i,j]&#xff0c;不过这里dp[i-1,j-1]2dp[i,j-1]1&#xff0c;从字面上理解 就是 当 同时删wo…

mfc140u.dll丢失的解决方法,以及mfc140u.dll解决方法的优缺点

在使用电脑过程中&#xff0c;有时会遇到一些与动态链接库文件&#xff08;DLL&#xff09;相关的错误。其中&#xff0c;mfc140u.dll丢失的错误是较为常见的一种。当这个关键的mfc140u.dll文件丢失或损坏时&#xff0c;可能会导致某些应用程序无法正常运行。在本文中&#xff…

怎样班群发成绩?

身为老师&#xff0c;定期发布学生成绩是项重要任务。在过去&#xff0c;这项任务需要手动操作&#xff0c;工作量大而且总是发错。不过诶&#xff0c;现在我们可以通过各种方式实现学生自助查询成绩&#xff0c;既提高了效率又不会发错&#xff01; 就是需要制作一个查询系统。…

日本水稻(Oryza sativa Japonica rice)的基因组染色质长度 IRGSP-1.0

创作日志&#xff1a; 在看scHi-C综述的时候发现了一个在2021年发布在Nature Plants上的数据集&#xff0c;想拿来用&#xff0c;首先就要知道其对应的水稻品种以及染色质长度。最终在UCSC上找到了对应的组装好的基因组&#xff0c;版本名为 IRGSP-1.0。 UCSC链接&#xff1a;h…

Apipost 推出IDEA插件一键生成API文档

今天给大家推荐一款IDEA插件&#xff1a;Apipost-Helper-2.0&#xff0c;写完代码IDEA内一键生成API文档&#xff0c;无需安装、打开任何其他软件&#xff1b;写完代码IDEA内一键调试&#xff0c;无需安装、打开任何其他软件&#xff1b;生成API目录树&#xff0c;双击即可快速…

SpringCloud Alibaba组件入门全方面汇总(中):服务熔断降级-Sentinel

文章目录 Sentinel常见的容错思路Sentinel流量控制规则sentinel 自定义异常 sentinelresources 注解使用Feign整合Sentinel**面试题&#xff1a;结合Feign后&#xff0c;你在项目中的降级方法中会实现什么样的操作/功能&#xff1f;** Sentinel Sentinel是阿里巴巴开源的分布…

SAP PI/PO中使用UDF解决按字节拆分字符串的需求

需求背景&#xff1a; SAP需要将采购订单信息通过PI发送到SFTP服务器上&#xff0c;生成文件&#xff0c;一般对日项目上文件内容通常都是按照指定的字节数拆分的&#xff0c;而不是字符数&#xff0c;类似下面的格式。 问题点&#xff1a; 如果是使用FTP适配器&#xff0c;则…

VBA技术资料MF83:将Word文档批量另存为PDF文件

我给VBA的定义&#xff1a;VBA是个人小型自动化处理的有效工具。利用好了&#xff0c;可以大大提高自己的工作效率&#xff0c;而且可以提高数据的准确度。我的教程一共九套&#xff0c;分为初级、中级、高级三大部分。是对VBA的系统讲解&#xff0c;从简单的入门&#xff0c;到…

c/c++语言算法技巧汇总大复习2

标题前面打*号的为多数考纲范围外的&#xff0c;可以选择性查看 &#x1f517;链接&#xff1a;严书代码大全 &#x1f517;链接&#xff1a;c/c语言算法技巧汇总大复习1 &#x1f517;链接&#xff1a;c/c语言算法技巧汇总大复习2 目录 Dp动态规划入门练习 青蛙跳台阶练习&…

印染污水处理设备的物理法分类有哪些设备?

印染污水处理设备的物理法分类主要有以下几种设备&#xff1a; 筛滤截留法&#xff1a;主要采用筛网、格栅、滤池与微滤机等设备&#xff0c;用于去除污水中的悬浮物和漂浮物。重力分离法&#xff1a;主要采用沉砂池、沉淀池、隔油池与气浮机等设备&#xff0c;利用重力或浮力…

牛客 —— 链表中倒数第k个结点(C语言,快慢指针,配图)

目录 1. 思路1&#xff1a;倒数第K个节点&#xff0c;就是整数第N-K1的节点 2. 思路2&#xff1a;快慢指针 1. 思路1&#xff1a;倒数第K个节点&#xff0c;就是整数第N-K1的节点 链表中&#xff0c;一共有N个节点&#xff0c;如果我们想要得出倒数第K个节点&#xff0c;我们…

【SpringBoot篇】分页查询 | 扩展SpringMvc的消息转换器

文章目录 &#x1f6f8;什么是分页查询&#x1f339;代码实现⭐问题&#x1f384;解决方法 做了几个项目&#xff0c;发现在这几个项目里面&#xff0c;都实现了分页查询效果&#xff0c;所以就总结一下&#xff0c;方便学习 我们基于黑马程序员的苍穹外卖来讲解分页查询的要点…

深度学习之基于YoloV5安检仪危险品识别系统

欢迎大家点赞、收藏、关注、评论啦 &#xff0c;由于篇幅有限&#xff0c;只展示了部分核心代码。 文章目录 一项目简介 深度学习之基于 YOLOv5 安检仪危险品识别系统介绍YOLOv5 简介安检仪危险品识别系统系统架构应用场景 二、功能三、系统四. 总结 一项目简介 深度学习之基于…

ESP32 Arduino实战基础篇-使用中断和定时器

本教程介绍如何使用 PIR 运动传感器通过 ESP32 检测运动。在此示例中,当检测到运动(触发中断)时,ESP32 会启动计时器并打开 LED 并持续预定义的秒数。当计时器倒计时结束时,LED 自动关闭。 通过这个例子,我们还将探讨两个重要的概念:中断和定时器。 中断介绍 要使用 P…

照亮夜晚的台灯:户外空间的闪亮之选

户外台灯是家庭和社交空间的重要元素&#xff0c;它们不仅提供照明&#xff0c;还可以为您的户外区域增添美感&#xff0c;以及创造一个温馨的社交氛围。以下是一些关于户外台灯的信息&#xff0c;以帮助您更好地了解它们的多功能性和用途。 1、照明的重要性&#xff1a;户外台…

蒸汽流量计量表

数字化场景&#xff1a;蒸汽监测 定义 监测蒸汽流量 单位是 立方米 很难计算 等效碳排 不是按楼&#xff0c;也不是按层&#xff0c;比如宁波某园区&#xff0c;就6个蒸汽流量表 看懂蒸汽表&#xff0c;了解蒸发焓或潜热(hfg) 由于水是从0C加热到饱和温度的&#xff0c;它…

Linux 安装多版本 JDK 详细过程

背景说明 服务器已安装jdk1.8,但随着spring全家桶的升级换代&#xff0c;已不满足使用&#xff0c;先要用高版本jdk&#xff0c;暂时不想卸载旧的版本&#xff0c;故安装两个版本&#xff0c;jdk1.8和jdk17,jdk1.8的已经安装过了&#xff0c;所以此次只安装jdk17,以及配置jdk切…

【Linux系统编程十八】:(基础IO5)--动静态库共享/动静态加载问题(涉及地址空间)

【Linux系统编程十八】&#xff1a;动静态库共享/动静态加载问题(涉及地址空间&#xff09; 一.可执行程序如何被加载的1.加载之前2.加载之后①如何执行第一条命令②缺页中断/与地址空间建立联系 二.动态库如何加载的三.动态库如何实现多进程间共享的 一.可执行程序如何被加载的…

IDEA插件推荐:Apipost-Helper

IDEA是一款功能强大的集成开发环境&#xff08;IDE&#xff09;&#xff0c;它可以帮助开发人员更加高效地编写、调试和部署软件应用程序。我们在编写完接口代码后需要进行接口调试等操作&#xff0c;一般需要打开额外的调试工具。 今天给大家介绍一款IDEA插件&#xff1a;Api…