精华内容
下载资源
问答
  • vue动态路由

    2021-01-19 18:57:18
    VUE动态路由vue动态路由先看实现数据库数据格式表数据后端代码实现组装菜单格式数据前端代码实现前端目录结构紧接着登录成功后菜单页渲染大功告成 vue动态路由 之前一直被动态路由这个问题所困扰, 现在终于实现了,...

    vue动态路由

    之前一直被动态路由这个问题所困扰, 现在终于实现了, 姑且写篇文章记录下来。

    先看实现

    在这里插入图片描述
    主要是左侧菜单,废话不多说 直接上代码

    数据库数据格式

    在这里插入图片描述

    数据

    在这里插入图片描述
    在这里插入图片描述

    后端代码实现

    组装菜单格式数据

    组装菜单格式数据这一步的操作在登录之后, 关于登录功能就不必讲了吧
    在这里插入图片描述
    菜单实体类

    @Data
    @EqualsAndHashCode(callSuper = true)
    @Accessors(chain = true)
    @TableName("sys_menu")
    public class SysMenuEntity extends BaseEntity<SysMenuEntity> {
    
        private static final long serialVersionUID = 1L;
    
        @ApiModelProperty(value = "id")
        @TableId(value = "id", type = IdType.UUID)
        private String id;
    
        @ApiModelProperty(value = "菜单名称")
        @TableField("menu_name")
        private String menuName;
    
        @ApiModelProperty(value = "上级菜单id")
        @TableField("parent_id")
        private String parentId;
    
        @ApiModelProperty(value = "上级菜单名称")
        @TableField(value = "parentMenuName", exist = false)
        private String parentMenuName;
    
        @ApiModelProperty(value = "图标")
        @TableField("icon")
        private String icon;
    
        @ApiModelProperty(value = "图标颜色")
        @TableField("icon_color")
        private String iconColor;
    
        @ApiModelProperty(value = "路径")
        @TableField("path")
        private String path;
    
        @ApiModelProperty(value = "重定向")
        @TableField("redirect")
        private String redirect;
    
        @ApiModelProperty(value = "引用组件")
        @TableField("component")
        private String component;
    
        @ApiModelProperty(value = "跳转方式")
        @TableField("target")
        private String target;
    
        @ApiModelProperty(value = "是否展开(0否 1是)")
        @TableField("is_open")
        private Integer isOpen;
    
        @ApiModelProperty(value = "路由名称")
        @TableField("route_name")
        private String routeName;
    
        @ApiModelProperty(value = "序号")
        @TableField("order_num")
        private Integer orderNum;
    
        @ApiModelProperty(value = "创建时间")
        @TableField(value = "create_time", fill = FieldFill.INSERT)
        private LocalDateTime createTime;
    
        @ApiModelProperty(value = "创建人")
        @TableField(value = "create_user", fill = FieldFill.INSERT)
        private String createUser;
    
        @ApiModelProperty(value = "修改时间")
        @TableField(value = "update_time", fill = FieldFill.UPDATE)
        private LocalDateTime updateTime;
    
        @ApiModelProperty(value = "修改人")
        @TableField(value = "update_user", fill = FieldFill.UPDATE)
        private String updateUser;
    
        @ApiModelProperty(value = "状态(1有效 0无效 99已删除)")
        @TableField("status")
        private Integer status;
        /**
         * 子菜单
         */
        @TableField(value = "children", exist = false)
        private List<SysMenuEntity> children = new ArrayList<>();
    
        @Override
        protected Serializable pkVal() {
            return this.id;
        }
    
    }
    

    我的一级菜单pid是0 这里根据你们自己的数据来

    /**
         * 根据用户ID查询菜单列表
         *
         * @param id
         * @return
         */
        @Override
        public List<SysMenuEntity> selectMenusByUserId(String id) {
            if (StringUtils.isEmpty(id)) {
                throw new BusinessException(ResultEnum.PARAM_IS_EMPTY);
            }
            //List<SysMenuEntity> menuLists = this.menuMapper.selectMenusByUserId(id);
            //TODO 暂时修改成查询所有的
            List<SysMenuEntity> menuLists = this.menuMapper.selectList(null);
    
            //组装成带层级的数据
            List<SysMenuEntity> nodes = new ArrayList<>();
            menuLists.forEach(e -> {
                //先挑出一级菜单
                if (StatusEnum.INVALID_STRING.getStringCode().equals(e.getParentId())) {
                    nodes.add(e);
                }
                //筛选二级菜单
                menuLists.forEach(p -> {
                    try {
                        //过滤pid
                        if (e.getId().equals(p.getParentId()) && !StatusEnum.INVALID_STRING.getStringCode().equals(p.getParentId())) {
                            e.getChildren().add(p);
                        }
                    } catch (Exception e1) {
                        throw new BusinessException(e1);
                    }
                });
            });
    
            //排序父节点
            nodes.sort(Comparator.comparingInt(SysMenuEntity::getOrderNum));
    
            //排序子节点
            nodes.forEach(e -> {
                e.getChildren().sort(Comparator.comparingInt(SysMenuEntity::getOrderNum));
            });
            return nodes;
        }
    

    后端只负责根据用户的不同角色组装个数据

    前端代码实现

    前端目录结构

    在这里插入图片描述

    紧接着登录成功后

    //设置token    
    setToken(res.data.userToken)
    //设置菜单              
    setRouter(res.data.menus)            
    this.$router.push({path: '/'})
    
    export function setRouter(route) {
        return window.sessionStorage.setItem(routeKey, JSON.stringify(route))
    }
    

    这里是把数据存到了session中

    /**
     User: gubingxu
     Date: 2020/5/28 16:28
     Description:  路由权限处理
     */
    import router from './router'
    import store from './store'
    import {Message} from 'element-ui'
    import user from './store/modules/user'
    import NProgress from 'nprogress' // progress bar
    import 'nprogress/nprogress.css' // progress bar style
    import {getToken, getRouter, removeToken} from '@/utils/auth' // get token from cookie
    import getPageTitle from '@/utils/get-page-title'
    import {resolveRouter} from './router/resolveRouter'
    import ca from 'element-ui/src/locale/lang/ca'
    
    //NProgress.configure({showSpinner: false}) // NProgress Configuration
    
    //路由白名单
    const whiteList = ['/login'] // no redirect whitelist
    
    //路由守卫(路由过滤)
    router.beforeEach((to, from, next) => {
      // 开启进度条
      NProgress.start()
    
      // 设置页面标题
      document.title = getPageTitle(to.meta.title)
    
      // 用户token
      const hasToken = getToken()
    
      // 是否已登录
      if (hasToken) {
        if (to.path === '/login') {
          Message({message: '你已经登录', type: 'info'})
          // 如果已经登录, 则定向到首页
          next({path: '/'})
        } else {
          //用户token存在直接放行
          if (user.state.init) {
            next()
          } else {
            //跳转到获取动态路由的方法
            gotoRouter(to, next)
          }
        }
        if (user.state.init) {
          next()
        } else {
          //跳转到获取动态路由的方法
          gotoRouter(to, next)
        }
        //没有登录
      } else {
        //路由白名单直接进入
        if (whiteList.indexOf(to.path) !== -1) {
          next()
        } else {
          if (to.path !== '/login') {
            // 重定向到登录页面 不能这么写 因为假如之前的角色是 管理员页面 后又登陆了非管理员 重定向的页面就可能不存在,就会导致404
            // next(`/login?redirect=${to.path}`)
            next('/login')
          } else {
            next()
          }
        }
      }
      NProgress.done()
    })
    
    router.afterEach(() => {
      // finish progress bar
      NProgress.done()
    })
    
    
    function gotoRouter(to, next) {
    
      //获取后端传递的路由
      let routeData = JSON.parse(getRouter())
    
      //解析路由
      let finalRoute = resolveRouter(routeData)
    
      //将路由添加到vuex中 渲染菜单
      store.commit('user/set_router_list', finalRoute)
    
      //设置用户 初始化为完成
      store.commit('user/set_init', true)
    
      try {
        // 后置添加404页面,防止刷新404
        finalRoute.push({
          path: '*',
          redirect: '/404',
          hidden: true
        })
    
        //将路由添加到vue中
        router.addRoutes(finalRoute) // vue-router提供的addRouter方法进行路由拼接
    
        //跳转
        next({...to, replace: true}) // hack方法 确保addRoutes已完成
      } catch (e) {
        console.error(e)
        removeToken()
      }
    
    }
    
    

    这里全局统一处理路由 permission.js

    import Vue from 'vue'
    import Router from 'vue-router'
    
    import {constantRoutes} from './static_router'
    
    Vue.use(Router)
    
    const createRouter = () =>
        new Router({
        // mode: 'history', // require service support
        // 解决vue框架页面跳转有白色不可追踪色块的bug
        scrollBehavior: () => ({y: 0}),
        routes: constantRoutes
    })
    
    const router = createRouter()
    
    // Detail see: https://github.com/vuejs/vue-router/issues/1234#issuecomment-357941465
    export function resetRouter() {
        const newRouter = createRouter()
        router.matcher = newRouter.matcher // reset router
    }
    
    export default router
     
    

    这里是 router/index.js

    
    /* layout */
    import layout from '@/layout'
    
    export const constantRoutes = [
      {
        path: '/login',
        component: () => import('@/views/login/index'),
        hidden: true,
        meta: {title: '登录', icon: 'login'}
      },
    
      {
        path: '/404',
        component: () => import('@/views/404'),
        hidden: true,
        meta: {title: '404', icon: '404'}
      },
    
      {
        path: '/',
        component: layout,
        redirect: '/main',
        name: 'Main',
        meta: {title: '主页', icon: 'fa fa-home'},
        // 菜单序号
        menuOrder: 1,
        // 是否单独菜单 --> 即只有一级
        isSingleMenu: true,
        children: [
          {
            path: 'main',
            component: () => import('@/views/main/index')
          },
        ]
      },
    
      //当使用了动态路由时  就不需要在手动添加了  不然会出现刷新页面404的错误
      /*{path: '*', redirect: '/404', hidden: true, meta: {title: '404', icon: 'main'}}*/
    ]
    
    

    静态路由表, 一个固定的路由,例如首页 404等

    /**
     User: gubingxu
     Date: 2020/5/28 16:28
     Description:  解析后端返回的路由表  返回vue接受的路由格式
     */
    
    import {Message} from 'element-ui'
    
    /* layout */
    import layout from '@/layout'
    
    //解析组件的方法 (线上环境为路由懒加载模式)
    const _import = require('../router/_import_' + process.env.VUE_APP_BASE_EV)  //获取组件的方法
    
    export function resolveRouter(routeData) {
      //最终返回的路由格式
      const route = []
      try {
        //遍历路由数据
        routeData.forEach(e => {
          let newRoute = {
            //路径
            path: e.path,
            //名称
            name: e.routeName,
            //解析component(由于后端传递的component 为字符串,则需要解析为vue接受的component组件)
            component: _resolveComponent(e.component)
          }
    
          //如果存在子级
          if (e.children) {
            //递归解析
            const children = resolveRouter(e.children)
            //保存子级
            newRoute = {...newRoute, children: children}
          }
    
          //redirect
          if (e.redirect) {
            //递归解析
            //保存子级
            newRoute = {...newRoute, redirect: e.redirect}
          }
    
          //图标和标题
          if (e.icon && e.menuName) {
            newRoute = {...newRoute, meta: {title: e.menuName, icon: e.icon}}
          }
    
          //图标颜色
          if (e.iconColor) {
            newRoute = {...newRoute, iconColor: e.iconColor}
          }
    
          //图标为空 名字不为空(子级菜单)
          if (e.menuName && !e.icon) {
            newRoute = {...newRoute, meta: {title: e.menuName, icon: ''}}
          }
    
          //将组装的数据放进route中
          route.push(newRoute)
        })
      } catch (e) {
        Message.error({
          message: `解析路由数据失败!${e.message}`,
          showClose: true,
          type: 'warning'
        })
        console.error(e)
        return []
      }
      //返回组装好的路由
      return route
    }
    
    
    //将字符串 转换为组件
    function _resolveComponent(componentData) {
      //component处理
      if (componentData) {
        if (componentData === 'layout') { //layout组件特殊处理
          componentData = layout
        } else {
          //截取掉第一个 / (数据库中农保存的为带 / 的数据)
          componentData = _import(componentData.slice(1))
        }
      }
      return componentData
    }
    
    

    路由解析页 resolveRouter.js

    module.exports = file => require('@/views/' + file + '.vue').default 
    //线上环境懒加载
    module.exports = file => () => import('@/views/' + file + '.vue')
    

    解析路由的两个方法

    菜单页渲染

    <template>
      <div class="side-bar-container">
        <side-bar-logo/>
        <hr/>
        <!--
          :collapse 是否展开菜单
          :unique-opened 是否只保持一个菜单为打开状态
          :collapse-transition 是否显示展开动画
        -->
        <el-menu
            :default-active="activeMenu"
            :background-color="variables.sideBarBackgroundColor"
            text-color="#fff"
            :active-text-color="variables.sideBarPickTextColor"
            :collapse="this.sidebar.isExpand"
            :collapse-transition="true"
            mode="vertical"
            router
        >
          <el-menu-item index="/main" key="/main" @click="clickMenu('main','/main')">
            <template slot="title">
              <i class="fa fa-home" :style="{'color':themeColor}"></i>
              <span slot="title">主页</span>
            </template>
          </el-menu-item>
          <template v-for="(item, index) in menuList">
            <!-- 单独菜单 -->
            <!--<el-menu-item :index="item.path" :key="index" v-if="item.isSingleMenu">
              <template slot="title">
                <i :class="item.meta.icon"></i>
                <span slot="title">{{item.meta.title}} + {{index}} ++ {{item.path}}</span>
              </template>
            </el-menu-item>-->
    
            <!-- 带子菜单 -->
            <el-submenu :index="item.path" :key="index" v-if="!item.isSingleMenu && item.children.length > 0">
              <template slot="title">
                <i :class="'fa ' + 'fa-' + item.meta.icon" :style="{'color':item.iconColor || themeColor}"></i>
                <!--<side-bar-icon :icon="item.meta.icon"/>-->
                <span slot="title">{{item.meta.title}}</span>
              </template>
              <el-menu-item v-for="(children_item, children_index) in item.children" :key="children_index"
                            :index="children_item.path"
                            @click="clickMenu(children_item.meta.title,children_item.path)">
                <!--子菜单图标-->
                <!--<i :class="item.meta.icon"></i>-->
                <span>{{children_item.meta.title}}</span>
              </el-menu-item>
            </el-submenu>
          </template>
    
        </el-menu>
      </div>
    </template>
    ```js
    <script>
      import variables from '@/styles/variables.scss'
      import {mapGetters} from 'vuex'
      import settings from '@/settings'
      import SideBarLogo from '@/layout/components/side-bar/side-bar-logo'
      import NavBar from '@/layout/components/nav-bar/nav-bar'
    
      export default {
        name: 'side-bar',
        components: {NavBar, SideBarLogo},
        data() {
          return {}
        },
    
        computed: {
          // 从store的 getters里 获取sidebar
          ...mapGetters(['sidebar']),
          variables() {
            return variables
          },
    
          //菜单颜色
          themeColor() {
            return settings.themeColor
          },
    
          /**
           * 计算当前激活菜单是哪个
           */
          activeMenu() {
            const route = this.$route
            const {meta, path} = route
            if (meta.activeMenu) {
              return meta.activeMenu
            }
            return path
          },
    
    
          /**
           * 获取菜单数据
           */
          menuList() {
    
            let ml = this.$store.state.user.routerList
    
            // TODO 获取静态路由表中需要添加的路由, 将路由渲染到菜单中。
            /*let tempRouter = []
            constantRoutes.forEach(e => {
              if (e && e.menuOrder) {
                tempRouter.push(e)
              }
            })
            //先排序 --> 在插入到路由表中
            tempRouter.sort(this.compare).forEach(e => ml.unshift(e))
            console.log(ml)*/
            //过滤掉404 路由
            return ml.filter(e => {
              if (e.redirect !== '/404') {
                return e
              }
            })
          },
        },
    
        created() {
    
        },
    
        mounted() {
        },
    
        methods: {
        
          /**
           * 菜单的点击事件
           * @param label 标题
           * @param path 路由
           */
          clickMenu(label, path) {
            let val = {
              label: label,
              path: path
            }
            // 存储到vuex中 方便面包屑组件与tab组件使用
            this.$store.commit('router/select_menu', val)
          }
        }
      }
    </script>
    

    大功告成

    至此, 已实现开头页面效果。
    以梦为马,不负韶华。

    展开全文
  • Vue动态路由

    2020-12-23 12:07:45
    Vue动态路由什么是动态路由?实例 什么是动态路由? 我们经常需要把某种模式匹配到的所有路由,全都映射到同个组件。例如,我们有一个 User 组件,对于所有 ID 各不相同的用户,都要使用这个组件来渲染。那么,我们...

    什么是动态路由?

    我们经常需要把某种模式匹配到的所有路由,全都映射到同个组件。例如,我们有一个 User 组件,对于所有 ID 各不相同的用户,都要使用这个组件来渲染。那么,我们可以在 vue-router 的路由路径中使用“动态路径参数”(dynamic segment) 来达到这个效果。

    实例

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>动态路由</title>
    </head>
    <body>
        <!-- 代码位置 在路由配置中的path属性:'/:id' -->
        <!-- 在视图中获取id值{{$route.params.id}} -->
        <div id="app">
        <!-- 使用router-link组件来导航 -->
         <!-- 通过传入 `to` 属性指定链接 -->
          <!-- <router-link> 默认会被渲染成一个 `<a>` 标签 -->
            <router-link to="/baskball">篮球</router-link>
            <router-link to="/football">足球</router-link>
            <router-link to="/ppq">乒乓球</router-link>
            <!-- 设置容器 -->
            <!-- 路由出口 -->
            <!-- 路由匹配到的组件将渲染在这里 -->
            <router-view></router-view>
            <!-- 引入vue.js和vue-router.js文件 -->
            <script src="./vue.js"></script>
            <script src="./vue-router.js"></script>
            <script>
                // 3、提供要渲染的组件
                var Ball = {
                    template:` <div>球组件-{{$route.params.id}} </div>`
                }
                // 4、配置路由(设置路由匹配规则)
                var routes = [
                    // 根据不同的router-link的标识在容器router-view渲染不同的组件
                    // {path:'/a',component:comA},
                    // {path:'/b',component:comB},
                    // {path:'/c',component:comC},
                    // 动态路由
                    // :id参数名,值是变化的
                    {path:'/:id',component:Ball}
                ]
                // 5、实例化路由并且使用路由配置
                var router = new VueRouter({
                    routes
                })
    
                new Vue({
                    el:'#app',
                    router,
                    data:{
    
                    },
                    methods:{
    
                    }
                })
            </script>
        </div>
    </body>
    </html>
    
    展开全文
  • vue 动态路由

    2020-03-26 15:45:34
    vue 动态路由 routes配置 routes = [ { path: '/movie', name: 'Movie', component: () => import(/* webpackChunkName: "movie" */ '../views/Movie'), ...

    vue 动态路由

     

    routes配置

    routes = [
            {
                path: '/movie',
                name: 'Movie',
                component: () => import(/* webpackChunkName: "movie" */ '../views/Movie'),
                children: [
                {
                    path: 'detail/:movieId',
                    components: {
                      detail: () => import('@/views/Detail'),
                    },
                    props: {
                      detail: true
                    }
               }
           }
    ]

    父组件嵌套

    <router-view name="detail" />  // 命名路由

    跳转

    this.$router.push('/movie/detail/' + movieId);

    子组件获取路由参数

    props: ['movieId'],
            
    通过 this.movieId就可以啦

     

    展开全文
  • Vue 动态路由

    2019-11-19 12:12:04
    动态路由使用场景:由数据动态生成的列表,如新闻列表、问题列表、论坛标题列表等,它们拥有的数量很多,可能由成百上千条,如果一个一个添加路由信息是不可能实现的,这时候就用到了动态路由:设置一个路由信息,可...

    动态路由使用场景:由数据动态生成的列表,如新闻列表、问题列表、论坛标题列表等,它们拥有的数量很多,可能由成百上千条,如果一个一个添加路由信息是不可能实现的,这时候就用到了动态路由:设置一个路由信息,可传递不同的ID值,根据每个不同的ID值获取相应的数据生成不同的显示页面。
    使用方法:
    1.创建一个组件,如:Question.vue
    2.配置router,在router文件中添加路由信息,如:

    {
      path:'/question/:id',  		 // 和普通路由区别在于此处设置可以进行传入ID
      name:'question',
      component:() => import ('./views/Question.vue')
    }
    

    3.引入使用:当跳转至该路由时需要传递值进去,此处使用了一个跳转按钮进行示例,如下面:

    <router-link 
                tag="li"
                :to="{name:'question',params:{id:question.questionId}}"  // 设置该行属性 重点 
                 v-for="question in questionList" 
                :key="question.questionID"> {{question.title}}
    </router-link>
    

    4.页面在跳转挂载时需要接收传递进来的值并根据值进行数据获取

    mounted() {		//在组件挂载生命周期函数中操作
        const id = this.$route.params.id;      	// 获取ID值
        this.getData(id);				// 通过ID获取数据
    }
    

    5.通过上面方式即可实现从跳转传值到挂载取值渲染的过程,但是如果已在当前页面,然后进行比如上翻页下翻页等操作时,由于其已经在此页面且该页面也并不会重新进行构建,所以此时触发不了mounted函数,即无法再取到id值进行重新渲染页面,此时可以使用组件守卫beforeRouteUpdate来获取值并进行操作,如下:

    beforeRouteUpdate(to, from, next) {
    	const id = to.params.id;          	   // 使用to方法获取ID
     	this.getData(id);
     	next()				   // next执行不能忘记
    }
    

    上面示例是通过router-link跳转标签进行跳转的,如果通过点击事件跳转可以使用this.$router.push({ name: “question”, params: { id: id } })方式进行跳转;注意此时不刷新页面不会触发mounted

    展开全文
  • VUE 动态路由

    2019-01-23 09:32:38
    动态添加路由的方法 添加 报错页面组件的动态路由 格式化路由

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 5,155
精华内容 2,062
关键字:

vue动态路由

vue 订阅