类似于后台管理系统的layout布局

392 阅读2分钟
1、先从页面分析,系统从登陆需要login.vue页面,再到系统内部的主页面main.vue,其次就是主页面内的子页面。

login.vue页面

<template>
  <div class="login">
    <div class="main blur">
      <ul>
        <li>
          <img src="@/assets/img/logo1.png" alt="">
        </li>
        <li>
          <input type="text" v-model="username" class="username" placeholder="User name" autocomplete="new-password"/>
        </li>
        <li>
          <input type="password"  v-model="password" class="password" placeholder="Password" autocomplete="new-password"/>
        </li>
        <li>
          <p>forgot password ?</p>
        </li>
        <li class="btn">
          <a-button type="primary" block @click="login">
            Sign in
          </a-button>
        </li>
      </ul>
    </div>
  </div>
</template>
<script>
import { Login } from '@/api/index'
import { message } from 'ant-design-vue'

export default {
  name: 'Login',
  components: {},
  data () {
    return {
      username: '',
      password: ''
    }
  },
  mounted () {
    window.addEventListener('keydown', (e) => {
      if (e.keyCode === 13) {
        this.login()
      }
    })
  },
  methods: {
    login () {
      if (this.username && this.password) {
        Login({
          username: this.username,
          password: this.password
        }).then(res => {
          message.success(res.msg)
          if (res.data.token) {
            this.$storejs.set('token', res.data.token)
          }
          this.$router.push('/main/home')
        }).catch(err => {
          console.log(err)
        })
      }
    }
  }
}
</script>

main.vue主页面

<template>
  <div class="home">
    <a-layout id="components-layout-demo-side" style="min-height: 100vh">
      <a-layout-sider v-model="collapsed" :trigger="null" collapsible>
        <img src="@/assets/img/logo.png" alt="" :class="['logo', collapsed?'':'logoImg']">
        <!-- 左侧菜单列表 -->
        <menu-tree :treeData="treeData" defaultSelectItemKey="home"></menu-tree>
      </a-layout-sider>
      <a-layout>
        <a-layout-header style="background: #fff; padding: 0">
          <a-icon
            class="trigger"
            :type="collapsed ? 'menu-unfold' : 'menu-fold'"
            @click="() => (collapsed = !collapsed)"
          />
        </a-layout-header>
        <a-layout-content style="margin: 0 16px">
          <a-breadcrumb style="margin: 16px 0">
            <a-breadcrumb-item>User</a-breadcrumb-item>
            <a-breadcrumb-item>Bill</a-breadcrumb-item>
          </a-breadcrumb>
          <div class="content">
            <!-- 右侧主要内容展示部分 -->
            <router-view></router-view>
          </div>
        </a-layout-content>
        <a-layout-footer style="text-align: center">
          满地都是六便士,他却抬头看见了月亮,他不理睬六便士,却伸手追寻触碰月光
        </a-layout-footer>
      </a-layout>
    </a-layout>
  </div>
</template>

<script>
export default {
  name: 'Layout',
  data () {
    return {
      treeData: [],
      collapsed: false
    }
  },
  created () {
    this.getMenuList()
  },
  methods: {
    getMenuList () {
      this.treeData = this.$router.options.routes[1].children
    }
  }
}
</script>

menu-tree组件页面

<template>
  <div class="menu">
    <a-menu theme="dark" :default-selected-keys="[defaultSelectItemKey]" mode="inline">
      <template v-for="treeItem in treeData">
        <a-sub-menu :key="treeItem.path" v-if="treeItem.children && treeItem.children.length">
          <span slot="title">
            <a-icon :type="treeItem.meta.icon" />
            <span>{{treeItem.meta.title}}</span>
          </span>
          <menu-tree :treeData="treeItem.children" class="menu_children"></menu-tree>
        </a-sub-menu>
         <a-menu-item :key="treeItem.path" v-else>
           <router-link :to="{ path: `/main/${treeItem.path}` }">
              <a-icon :type="treeItem.meta.icon" />
              <span>{{treeItem.meta.title}}</span>
           </router-link>
          </a-menu-item>
        </template>
      </a-menu>
  </div>
</template>

<script>
export default {
  name: 'menu-tree',
  props: {
    // 树状结构菜单数据源
    treeData: {
      type: Array,
      default: () => []
    },
    // 默认展开的项key值
    defaultSelectItemKey: {
      type: String,
      default: () => ['']
    }
  },
}
</script>

2、从路由分析,/login和/main路由属于平级,main.vue页面内的菜单列表的路由属于/main下的子路由,展示的菜单列表也是这一部分路由

/router/index.js

import routerObj from './routesImport'
import Vue from 'vue'
import Router from 'vue-router'

Vue.use(Router)

const router = new Router({
  mode: 'history',
  base: process.env.BASE_URL,
  routes: [
    {
      path: '/',
      redirect: '/login'
    },
    // 菜单列表页面展示
    {
      path: '/main',
      name: 'main',
      // 特别注意layout公共组件不能使用前端工程化导入
      component: () => import('../views/LayOut/index.vue'),
      children: [
        {
          path: 'home',
          name: 'home',
          component: routerObj.Home,
          meta: {
            title: '首页',
            icon: 'home',
            auth: true
          }
        },
        {
          path: 'users',
          name: 'users',
          component: routerObj.Users,
          meta: {
            title: '用户管理',
            icon: 'user',
            auth: true
          }
        },
        {
          path: 'setting',
          name: 'setting',
          component: routerObj.Setting,
          meta: {
            title: '系统设置',
            icon: 'setting',
            auth: true
          }
        }
      ]
    },
    // 登录页面
    {
      path: '/login',
      name: 'login',
      component: routerObj.Login
    },
    // 在动态添加路由的过程中,如果有 404 页面,一定要放在最后添加,否则在登陆的时候添加完页面会重定向到 404 页面。
    // 类似于这样,这种规则一定要最后添加。
    {
      path: '*',
      hidden: true,
      component: () => routerObj.NotFound
    }
  ]
})

export default router

/router/routesImport.js路由导入处理

// 工程自动化文件一次导入
const context = require.context('@/views/', true, /index.vue$/)

const routerObj = {}
context.keys().forEach((item) => {
  // context(item)返回完整的相对路径,name是获取到当前遍历的组件的name属性值
  const name = context(item).default.name
  // 获取的是整个组件
  routerObj[name] = context(item).default
})

export default routerObj
3、vue渲染方式分析,App.vue下的router-view是用来装对应路由的容器,是最大的主容器,负责展示登录页面和系统内部主页面,main.vue页面分左右两侧,点击左侧的菜单对应展示右边内容。菜单列表所属于/main路由下,所以点击菜单列表时,切换的是/main下的子路由,右边是/main下子路由切换时展示的对应内容,和App.vue同理,所以右侧需要一个容器router-view用来展示/main下子路由的内容

image