Vue2后台管理系统实现难点(1):

1.mock中token登录实现:

  • 首先在mock中的登录js文件中设置一个方法(getMenu)用于校验登录信息,该方法能接收一个参数(config),一般是登陆组件那边传过来的form表单,然后调用getMenu方法,将接收过来的from表单解析成json格式的对象进行校验.校验成功返回需要的数据(menu菜单等…..)

    • getMenu方法
getMenu: config => {
console.log(config);
const { username, password } = JSON.parse(config.body)
console.log(JSON.parse(config.body))
// 先判断用户是否存在
// 判断账号和密码是否对应
// 超级管理员
if (username === 'admin' && password === 'admin') {
return {
code: 20000,
data: {
menu: [
{
path: '/home',
name: 'home',
label: '首页',
icon: 's-home',
url: 'home/index.vue'
},
{
path: '/mall',
name: 'mall',
label: '商品管理',
icon: 'video-play',
url: 'mall/index.vue'
},
{
path: '/user',
name: 'user',
label: '用户管理',
icon: 'user',
url: 'User/index.vue'
},
{
label: '其他',
icon: 'location',
children: [
{
path: '/page1',
name: 'page1',
label: '页面1',
icon: 'setting',
url: 'other/pageOne.vue'
},
{
path: '/page2',
name: 'page2',
label: '页面2',
icon: 'setting',
url: 'other/pageTwo.vue'
}
]
}
],
token: Mock.Random.guid(),//随机生成一个id充当token
message: '获取成功'
}
}
// 次级管理员
} else if (username === 'xiaoxiao' && password === 'xiaoxiao') {
return {
code: 20000,
data: {
menu: [
{
path: '/home',
name: 'home',
label: '首页',
icon: 's-home',
url: 'home/index.vue'
},
{
path: '/mall',
name: 'mall',
label: '商品管理',
icon: 'video-play',
url: 'mall/index.vue'
},
],
token: Mock.Random.guid(),
message: '获取成功'
}
}
} else {
return {
code: -999,
data: {
message: '密码错误'
}
}
}
}
  • 表单发送
    image

2.动态路由的实现:(cookie查看往期博客)

  • 登陆成功过后,首先执行清除路由组件方法(clearMenu)以保证当前页面一片空白,随后在将登陆成功而获取到的Menu数据(权限路由:路由组件的数据)重新通过设置路由组件的方法(setMenu)设置成一个cookie存储起来(为什么需要cookie,避免浏览器刷新时丢失数据),再调用addMenu方法对获取到的Menu数据进行一个二级菜单的判断以及动态路由(包括二级菜单)的转变(绑定component属性来动态指向路由位置),最后再通过addRoute方法将完成动态转变的权限路由(绑定了component属性的Menu数据)绑定到全局的路由器当中(router)
    • addMenu函数执行流程: 首先判断接收过来的Menu是否存在二级菜单(即是否存在子路由),若是存在,则将每一子路由遍历添加component属性,使其变成动态路由(可指向),随后追加到menuArray[]当中(menuArray用于暂时存放完成动态转变的权限路由组件),若无二级菜单权限路由也是如此进行动态路由的转变。最后在通过forEach方法,将menuArray再通过addRoute方法添加到Main路由组件当中

完整代码如下:

// 设置权限路由的cookie
setMenu(state,val){
state.menu = val
// 将数据缓存到cookie中并进行序列化
Cookie.set('menu',JSON.stringify(val))
} ,
// 清除权限路由cookie
clearMenu(state){
// 数据重置
state.menu = []
Cookie.remove('menu')
} ,
// 路由的动态添加
addMenu(state,router){
// 如果cookie中没有找到menu直接return
if(!Cookie.get('menu')){
return
}
const menu = JSON.parse(Cookie.get('menu'))
state.menu = menu
// 接收完成动态路由设置的menu数据
const menuArray = []
menu.forEach(item =>{
// 判断是否存在二级菜单
if(item.children){//存在children属性
item.children = item.children.map(item =>{
// 给menu动态添加component属性用于指向路由
// 实现路由的动态切换该路由的身上必须要有动态属性(component)指向一个二级组件
// es6按需引入menu中静态路由里面的url属性用于指定路由的跳转
item.component = () => import(`../views/${item.url}`)
// 返回由动态路由组件组成的全新的menu(此时这里的menu里面的路由组件里面都具备了component属性了,能够动态指向)
return item
})
menuArray.push(...item.children)
}else{
// 若无二级菜单则直接进行动态路由component属性的添加即可
item.component = () => import(`../views/${item.url}`)
menuArray.push(item)
}
});
// 路由的动态添加(使用到addRoute)
menuArray.forEach(item=> {
// 所有的路由组件(包括动态权限路由组件和静态路由组件)都归于Main组件管理
// 在Main路由身上添加子路由[权限路由(Menu动态路由)]
router.addRoute('Main',item)
})
}

3.面包屑,tags和页面的联动切换显示实现:

1.面包屑联动页面切换:

  • 首先调用饿了么Ui组件库里面的面包屑组件,它里面提供两个参数配置,separator(分隔符),和to(路由的跳转指向),如下图所示:
    image

  • 随后我们在Vuex中设置一个tabsList用来存放面包屑数据,再在mutations中设置一个selectMenu方法用于判断我们通过点击侧边栏获取到的路由数据来对tabsList面包屑数据进行增删改查,判断该当前的面包屑列表中是否存在你点击的路由的面包屑

  • Vuex

    //Vuex中*************************************************
    // 定义状态
    state:{
    // 判断是否闭合侧边栏
    isCollapse:false,
    // 面包屑数据
    tabsList:[
    {
    path:'/home',
    name:'home',
    label:'首页',
    icon:'home'
    }
    ],
    // 设置当前面包屑
    currentMenu:null,
    // 设置登录获取回来的menu[管理员能看到的路由页面]
    menu:[]
    },
    mutations:{
    // 是否折叠侧边栏
    collapseMenu(state){
    // 状态反转
    state.isCollapse = !state.isCollapse
    },
    // 点击菜单改变当前面包屑
    selectMenu(state,val){//val为通过点击侧边栏获取到的权限路由数据
    if(val.name !== 'home'){
    state.currentMenu = val
    // 判断该当前的面包屑列表中是否存在你点击的路由的面包屑
    const result = state.tabsList.findIndex(item => item.name === val.name)
    if(result === -1){//判断原来的面包屑中是否存在
    state.tabsList.push(val)//添加面包屑
    }else{
    state.currentMenu = null
    }
    }
    }
    }
  • 随后我们在页面上通过mapState方法获取到vuex中的tabsList来遍历生成面包屑即可
    image
    image

2.tag联动面包屑和页面

  • 首先引入饿了么Ui组件库中的tag组件,并且使用vuex中的面包屑数据(tags = tabsList)同步生成等量的tag
<!-- closable:是否可关闭,effect:主题特效, click:点击触发, close:关闭触发-->
<el-tag size="small"
v-for="(tag,index) in tags"
:key="tag.name"
:closable="tag.name !== 'home'"
:effect="$route.name === tag.name ? 'dark' : 'plain'"
@click="changeMenu(tag)"
@close="handleClose(tag,index)"
>
{{tag.label}}
</el-tag>

image

  • 随后进行点击和删除事件的编写,点击跳转路由没什么好说的了,点击删除就有很大的说法了。

  • 删除事件的逻辑实现:

    • 1.点击删除首先判断当前删除的tag是否为当前聚焦的tag,如果不是,删除就行

    image

    • 2.若当前删除的tag与当前聚焦的tag为同一个:

      • (1).若点击删除的tag为最右边,删除过后,tag的聚焦以及路由的跳转向左移动一位

        image

      • (2).若点击删除的tag不为最右边,删除过后,tag的聚焦以及路由的跳转向右移动一位

        image

    • 3.最后再同步删除vuex中的tag(tabsList)指定的数据即可

代码展示:

  • CommonTag.vue(面包屑和tags组件)
    methods: {
    // 引入辅助方法
    ...mapMutations({
    close:'closeTag'
    }),
    // 点击相关的tag进行跳转(路由的跳转)
    changeMenu(tag){
    this.$router.push({name:tag.name})
    },
    // 删除tag(分两部分:1.点击删除vuex中的数据,2.删除tag后焦点自动往左移)
    handleClose(tag,index){
    // 首先获取到当前的tag的长度(即一共有多少个tag)
    const length = this.tags.length - 1 //减1是因为后续要和index比较,index是从0开始的
    // this.$store.commit('closeTag',tag)//常规的调用mutations方法
    this.close(tag)

    // 1.判断当前删除的tag是否为当前聚焦的tag,如果不是,删除就行
    if(tag.name !== this.$route.name){
    // 删除即可(不需要做过多的逻辑)
    return;
    }
    // 2.判断该当前删除的tag与当前聚焦的tag为同一个
    if(index === length){//如果当前聚焦的是最后一个tag(权限路由组件)
    this.$router.push({
    // 路由的聚焦向左移动一位
    name:this.tags[index - 1].name
    })
    }else{
    this.$router.push({//如果当前聚焦的不后一个tag(权限路由组件)
    // 路由的聚焦向右移动一位
    name:this.tags[index].name
    })
    }
    }
    },
  • store里面的tab.js
    export default {
    // 定义状态
    state:{
    //还有其他的数据............

    // 面包屑数据
    tabsList:[
    {
    path:'/home',
    name:'home',
    label:'首页',
    icon:'home'
    }
    ],
    // 设置当前面包屑
    currentMenu:null,
    // 设置登录获取回来的menu[管理员能看到的路由页面]
    menu:[]
    },
    mutations:{
    // 点击菜单改变当前面包屑
    selectMenu(state,val){//val为通过点击侧边栏获取到的权限路由数据
    if(val.name !== 'home'){
    state.currentMenu = val
    // 判断该当前的面包屑列表中是否存在你点击的路由的面包屑
    const result = state.tabsList.findIndex(item => item.name === val.name)
    if(result === -1){//判断原来的面包屑中是否存在
    state.tabsList.push(val)//添加面包屑
    }else{
    state.currentMenu = null
    }
    }
    },
    // 删除tag
    closeTag(state,val){
    const result = state.tabsList.findIndex(item => item.name === val.name)
    state.tabsList.splice(result,1)
    }
    //还有其他的mutations方法 ..................................
    }
    }