vue 后台管理系统(1)路由配置

vue 后台管理系统(1)路由篇

vue-router 结构一致的配置

import Vue from 'vue';
import Router from 'vue-router';
import Layout from './index.vue';

Vue.use(Router);

/**
 * 这种配置方式有以下弊端
 * 1. product 命名空间重复写多次 代码臃肿
 * 2. 导航的链式关系需要额外的代码逻辑指定(preNav), 比如 商品管理 > 商品列表 > 商品详情 > 商品编辑
 *    您可能会想到路由链式关系可以通过路由段来匹配:/goods/product/:id/edit 一个路由段对应一个路由
 *    => (1) /goods (2) /goods/product (3) /goods/product/:id (4) /goods/product/:id/edit
 *    这样做比较麻烦,而且也没有相关API支持

 * 3. 从 $route.matched 获取当前匹配的路径是行不通的,比如访问 /goods/product/1/edit 只匹配到了两个
     (1) /goods
     (2) /goods/product/1/edit
 *  
 */
export const routes = [
  {
    name: 'M1',
    component: Layout,
    path: '/goods',
    redirect: '/goods/product',
    meta: {
      title: '商品管理'
    },
    children: [
      {
        name: 'M2',
        component: () => import('../Product.vue'),
        path: 'product',
        meta: {
          title: '商品列表',
          activeMenu: 'M2'
        }
      },
      {
        name: 'M3',
        component: () => import('../Product.vue'),
        path: 'product/add',
        meta: {
          title: '增加商品',
          activeMenu: 'M2'
        }
      },
      {
        name: 'M4',
        component: () => import('../Product.vue'),
        path: 'product/:id',
        meta: {
          title: '商品详情',
          activeMenu: 'M2'
        }
      },
      {
        name: 'M5',
        component: () => import('../Product.vue'),
        path: 'product/:id/edit',
        meta: {
          title: '编辑商品',
          activeMenu: 'M2',
          preNav: 'M4'
        }
      }
    ]
  }
];

export default new Router({
  routes: routes
});

自动注入

页面文件目录按照某种规格编写,读取文件目录生成路由,与 nuxt 类似,约束性太强,而且目录看起来总觉得很奇怪,故不采用。

自定义结构

路由结构层次清晰,能满足菜单/导航/权限 等配置。

import Vue from 'vue';
import Router from 'vue-router';
import Layout from './index.vue';

Vue.use(Router);

/**
 * routes 包含了导航链式关系 以及 菜单关系
 */
export const routes = [
  {
    name: 'M1',
    component: Layout,
    path: '/product',
    redirect: '/product/index',
    meta: {
      title: '商品管理'
    },
    children: [
      {
        name: 'M2',
        component: () => import('../Product.vue'),
        path: 'index',
        meta: {
          title: '商品列表'
        }
      },
      {
        name: 'M3',
        component: () => import('../Brand.vue'),
        path: 'brand',
        meta: {
          title: '品牌管理'
        }
      },
      {
        name: 'M4',
        component: () => import('../Category.vue'),
        path: 'category',
        meta: {
          title: '类目管理'
        }
      }
    ]
  },
  {
    name: 'M5',
    component: Layout,
    path: '/order',
    redirect: '/order/index',
    meta: {
      title: '订单管理'
    },
    children: [
      {
        name: 'M6',
        component: () => import('../OrderList.vue'),
        path: 'index',
        meta: {
          title: '订单列表'
        },
        children: [
          {
            name: 'M7',
            component: () => import('../OrderDetail.vue'),
            path: 'add',
            hidden: true,
            meta: {
              title: '创建订单'
            }
          },
          {
            name: 'M8',
            component: () => import('../OrderDetail.vue'),
            path: ':orderId',
            hidden: true,
            meta: {
              title: '订单详情'
            },
            children: [
              {
                name: 'M9',
                component: () => import('../OrderDetail.vue'),
                path: 'edit',
                meta: {
                  title: '编辑订单'
                }
              }
            ]
          }
        ]
      }
    ]
  }
];

/**
 * 生成 vue-router routes
 */
const makeRoutes = () => {
  const rList = [];

  const rawChildren = (parent, children = [], top) => {
    children.forEach(item => {
      item.path = (parent.path + '/' + item.path).replace('//', '/');
      const child = {
        ...item
      };
      delete child.children;
      top.children.push(child);
      rawChildren(item, item.children, top);
    });
  };

  routes.forEach(item => {
    const child = {
      ...item,
      children: []
    };
    rawChildren(child, item.children || [], child);
    rList.push(child);
  });

  return rList;
};

export default new Router({
  routes: makeRoutes()
});

左侧菜单配置:

<template>
  <el-menu
    class="menu"
    :default-active="activeMenu"
    background-color="#282c43"
    text-color="#fff"
    active-text-color="#fc652f"
  >
    <MenuItem v-for="item in routes" :menu="item" :key="item.name" :routes="routes" />
  </el-menu>
</template>

<script>
// sidebar.vue
import { routes } from '../router';
import MenuItem from './menu-item';

export default {
  components: {
    MenuItem
  },
  data() {
    return {
      routes,
      isCollapse: true
    };
  },
  computed: {
    activeMenu() {
      const meta = this.$route.meta;
      return meta && meta.menuValue ? meta.menuValue : this.$route.name;
    }
  }
};
</script>

<style scoped>
.menu {
  border-right: 0;
}
.menu-item {
  padding: 0 12px;
  line-height: 40px;
}
</style>
<template>
  <div v-if="menu.meta && !menu.hidden">
    <template v-if="!hasChildren(menu)" >
      <el-menu-item>
        <template slot="title">
          <span slot="title">{{ menu.meta.title }}</span>
        </template>
      </el-menu-item>
    </template>

    <template v-else>
      <el-submenu :index="getMenuIndex(menu)">
        <template slot="title">
          <i class="el-icon-user-solid" v-if="showIcon"></i>
          <span slot="title">{{ menu.meta.title }}</span>
        </template>
        <child-menu
          v-for="child in menu.children"
          :key="child.name"
          :menu="child"
          :show-icon="false"
        />
      </el-submenu>
    </template>
  </div>
</template>

<script>
// menu-item.vue
import router from '../router';

export default {
  name: 'child-menu',
  props: {
    menu: {
      type: Object,
      default: null
    },
    'show-icon': {
      type: Boolean,
      default: true
    }
  },

  created() {
    this.$watch('menu', v => {
      console.log(v);
    });
  },

  methods: {
    getMenuIndex($route) {
      return $route.name;
    },

    hasChildren(menu) {
      if (!menu.children) return false;
      return !!menu.children.filter(menu => !menu.hidden).length;
    },

    gotoPath(route) {
      if (route.name !== this.$route.name) {
        this.$router.push({ name: route.name });
      }
    },

    hasPermission(route) {
      return true;
    }
  }
};
</script>

<style scoped>
.menu {
  border-right: 0;
}

.menu-item {
  padding: 0 12px;
  line-height: 40px;
}
</style>

导航面包屑:

<template>
  <div>
    <span v-for="bread in breadList" :key="bread.name">
      {{ bread.meta.title }}
    </span>
  </div>
</template>

<script>
import { routes } from './router';

export default {
  data() {
    return {
      routes,
      breadList: []
    };
  },
  created() {
    let res = null;
    const find = (list, path) => {
      list.forEach(item => {
        const curPath = [...path].concat(item);
        if (item.name === this.$route.name) {
          res = curPath;
        }
        item.children && find(item.children, curPath);
      });
    };

    find(routes, []);
    this.breadList = res;
  }
};
</script>
原文地址:https://www.cnblogs.com/GManba/p/13736971.html