vue+Node.js+mySql实现动态路由菜单(保存在数据库)

1.数据库设计

菜单管理就一个表,通过层级及父级id进行判断,一级菜单level为0且父级id为null,子级level为1且父级id不为null

 

 2 Node.js(express)

接口准备

addMenu = async function (req, res, next) {
    try {
        const { name, icon, url, view, parent_id } = req.query
        if (!name || !icon || !url || !view) {
            res.send({
                status: 402,
                msg: '缺少参数'

            })
            return
        }
        var flakeIdGen = new FlakeId();
        let sql
        console.log('parent_id', parent_id)
        if (parent_id) {
            sql = `insert into menu(id,name,icon,url,page,level,parent_id) values ('${intformat(flakeIdGen.next(), 'dec')}','${name}','${icon}','${url}','${view}',1,'${parent_id}')`
        } else {
            sql = `insert into menu(id,name,icon,url,page,level) values ('${intformat(flakeIdGen.next(), 'dec')}','${name}','${icon}','${url}','${view}',0)`
        }

        let arr = []
        sqlQuery(sql, arr)   //封装好的数据操作
        res.send({
            status: 0,
            msg: parent_id ? '修改成功' : '添加成功'
        })
    } catch (error) {
        next(error)
    }
}


delMenu = async function (req, res, next) {
    try {
        const { id } = req.query
        const sql = `delete  from menu where id='${id}' or parent_id='${id}'`
        let arr = []
        sqlQuery(sql, arr)
        res.send({
            status: 0,
            msg: '删除成功'
        })
    } catch (error) {
        next(error)
    }
}

editMenu = function (req, res, next) {
    try {
        const { id, name, icon, url, view, parent_id } = req.query
        if (!id || !name || !icon || !url || !view) {
            res.send({
                status: 402,
                msg: '缺少参数'

            })
            return
        }
        let pID
        if (parent_id) {
            pID = parent_id
        } else {
            pID = ''
        }
        const sql = `update menu set name='${name}',icon='${icon}',url='${url}',page='${view}',parent_id='${pID}' where id='${id}'`
        sqlQuery(sql, arr = []).then(e => {
            res.send({
                status: 0,
                msg: '修改成功'
            })
        })
    } catch (error) {
        error
    }
}


editMenuStatus = function (req, res, next) {
    try {
        const { id, status } = req.query
        const arr = []
        const sql = `update menu set status='${status}' where id='${id}'`
        sqlQuery(sql, arr).then(e => {
            res.send({
                status: 0,
                msg: '修改成功'
            })
        })
    } catch (error) {
        next(error)
    }
}


getAppointMenu = async (req, res, next) => {
    try {
        const { role_id } = req.query
        if (!role_id) {
            res.send({
                status: 402,
                msg: '缺少参数'

            })
            return
        }
        const sql = `select * from t_role_menu where role_id='${role_id}'`
        let e = await sqlQuery(sql, arr = [])
        let val = ''
        e.forEach(el => {
            val += el.menu_id + ','
        })
        const sql1 = `select * from menu where id in (${val.substring(0, val.length - 1)})`
        let menuData = await sqlQuery(sql1, arr = [])

        const dataVal = menuData.filter(e => { return e.level == 0 })
        const data = JSON.parse(JSON.stringify(dataVal))
        //const levelThreeData=await getLevelOne(2)
        data.forEach((a, index) => {
            dataVal[index].children = []
            menuData.forEach(b => {
                if (b.parent_id == a.id) {
                    dataVal[index].children.push(b)
                }
            })
        })

        res.send({
            status: 0,
            data: dataVal
        })
    } catch (error) {
        next(error)
    }
}



module.exports = {
    addMenu,
    delMenu,
    editMenuStatus,
    editMenu,
    getAppointMenu
}

3 vue 

前端在前置路由守卫中进行判断,若Vuex中无菜单数据,则通过接口请求菜单数据,处理格式后通过 router.addRoute动态进行渲染

/**
 * 全站权限配置
 *
 */
import NProgress from 'nprogress'; // progress bar
import router from "../router";
import store from "@/store/index";
import 'nprogress/nprogress.css';
import http from "@/utils/index.js";
import _this from '@/main.js'
import { MessageBox } from "element-ui";


NProgress.configure(
    {
        showSpinner: false,
        easing: 'ease',
        speed: 500,
        trickleSpeed: 200,
        minimum: 0.3
    }); // NProgress是封装的进度条 是否有转圈效果
router.beforeEach(async (to, from, next) => {
    console.log('跳转数据',to,from)
    // 登录拦截
    NProgress.start()
    if (to.path === '/login') { //去往登录页直接放行
        next()
    } else {
        const token = localStorage.getItem('token')//去往非登录页判断是否有token

        if (!token) {
            next({
                path: '/login'
            })
        } else {
            //判断是否有菜单数据,若无则重新请求,防止数据丢失
            console.log('菜单数据长度',store.state.menuList.length)
            if (store.state.menuList.length == 0) {
                let res = await http.getRequest('/getMenuList')
                if (res.status === 0) {
                    store.dispatch('dynamicRoutes', res.data).then(() => {
                        if (res.data && res.data.length > 0) {
                            res.data.forEach(e => {
                                const childrenRouter = e.children.map(el => {
                                    return {
                                        name: el.path,
                                        path: el.url,
                                        meta: { parent_name: e.name, name: el.name,icon:el.icon },
                                        component: () => import(`@/views${el.url}.vue`),
                                    }
                                })
                                const routerObj = {
                                    path: `/${e.page}`,
                                    name: e.page,
                                    meta: { parent_name: e.name, name: e.name ,icon:e.icon},
                                    component: () => import(`@/views${e.url}.vue`),
                                    children: childrenRouter
                                }
                                // console.log('路由', routerObj)
                                router.addRoute(routerObj)
                            })

                        }

                    })

                }else{
                    localStorage.removeItem("token");
                    store.dispatch("dynamicRoutes", []);
                    store.dispatch("dynamicUserInfo", {});
                    store.commit("setEditableTabs", []);
                    next({path:'/login'})
                }

            }
            if (Object.keys(store.state.userInfo).length == 0) {
                let res = await http.getRequest('/users/getUserInfo')
                if (res.status == 0) {
                    store.dispatch('dynamicUserInfo', res.data).then(res => {
                       
                    })
                   if(from.fullPath=='/login'){
                    next(
                        {path:store.state.menuList[0].children[0].url}
                    )
                   }else{
                    next({path:to.path})
                   }
                   
                }
            }
            next()

        }
    }
});

router.afterEach(() => {
    NProgress.done(); // 一定要关闭进度条

    // const title = store.getters.tag.label;
    // 根据当前的标签也获取label的值动态设置浏览器标题

    // router.$avueRouter.setTitle(title);
});

然后将处理好的菜单数据存于Vuex中供页面菜单栏进行展示

 

 在菜单处进行取出,通过循环完成渲染即可

 computed: {
    menuList() {
      return this.$store.state.menuList;
    },
  },
<el-menu
      :default-active="defaultIndex"
      class="el-menu-vertical-demo"
      background-color="#545c64"
      text-color="#fff"
      active-text-color="#ffd04b"
      :collapse="isCollapse_copy"
      
    >
      <el-submenu
        :index="index + ''"
        v-for="(item, index) in menuList"
        :key="index"
      >
        <template slot="title">
          <i :class="'iconfont ' + item.icon"></i>
          <span>{{ item.name }}</span>
        </template>

        <div v-if="item.children.length != 0">
          <el-menu-item
            :index="`${index}-${index1}`"
            v-for="(item1, index1) in item.children"
            :key="index1"
            @click="menuTo(item1)"
          >
            <template slot="title">
              <i :class="'iconfont ' + item1.icon"></i>
              <span>{{ item1.name }}</span>
            </template>
          </el-menu-item>
        </div>
      </el-submenu>
    </el-menu>

 完成上面操作后,即可动态控制路由菜单,效果如下

  • 3
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
前后端分离是一种开发模式,它将前端和后端的开发分离,前端主要负责用户界面的展示和交互,后端主要负责数据的处理和存储。Vue2、Node.jsMySQL可以结合使用来实现前后端分离。 首先,我们可以使用Vue2作为前端框架,通过它来开发用户界面。Vue2提供了一套响应式的数据绑定和组件化的架构,使得前端开发更加高效和灵活。我们可以使用Vue的官方脚手架工具vue-cli来快速搭建项目的基础结构。 其次,Node.js可以用作后端技术,作为一个基于事件驱动的服务器端JavaScript运行环境,它提供了丰富的模块和工具,使得后端开发更加便捷。我们可以使用Express框架来构建Node.js的后端应用,通过定义路由和处理请求,与前端进行数据的交互。 最后,MySQL是一个开源的关系型数据库管理系统,它可以存储和管理数据。我们可以使用Node.jsmysql库来连接和操作MySQL数据库,通过编写SQL语句来实现数据的增删改查。 在实际开发中,前端通过Ajax或者Axios等工具向后端发送请求,后端接收请求后,通过与MySQL数据库的交互来获取或处理数据,并将结果返回给前端。前端通过Vue2的数据绑定和渲染机制,将后端返回的数据展示在用户界面上。 通过Vue2、Node.jsMySQL的组合,我们可以实现一个完整的前后端分离的应用程序。Vue2提供了优秀的用户界面,Node.js作为后端技术提供了强大的功能和灵活性,MySQL作为数据库管理系统提供了数据的存储和管理。这样的开发模式可以提高开发效率和代码的维护性,同时也能够实现更好的用户体验和性能。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值