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

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: ''
    }
  },
  watch: {

  },
  created () {

  },
  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>
<style lang='scss' scoped>
.login {
  height: 100%;
  background: url("../../assets/img/login_bg.png") no-repeat center center;
  background-size: 100% 100%;
  .main {
    position: fixed;
    right: 20%;
    top: 15%;
    width: 4rem;
    height: 6rem;
    box-shadow: 0px 0px .24rem -.06rem black;
  }
  .blur {
    position: absolute;
    overflow: hidden;
    z-index: 1;
  }

  .blur::after {
    content: "";
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    z-index: -1;
    margin: -.3rem;
    background-image: url("../../assets/img/login_bg.png");
    background-position: center;
    background-size: 100% 100%;
    background-attachment: fixed;
    filter: blur(10px);
  }
  .main {
    li {
      background: rgba(255, 255, 255, 0.3);
      &:nth-of-type(1){
        text-align: center;
        padding-top: .4rem;
         background: rgba(255, 255, 255, 0);
        img{
          height: 1.8rem;
          width: 60%;
        }
      }
      p{
        height: .5rem;
        line-height: .5rem;
        text-align: center;
        color: black;
        opacity: .5;
      }
      .username,
      .password {
        width: 100%;
        border: none;
        height: .6rem;
        font-size: .2rem;
        line-height: .5rem;
        padding-left: .1rem;
        border-bottom: 1px solid rgba(255, 255, 255, 0.4);
        background: rgba(255, 255, 255, 0.1);
        border-radius: 0;
        outline: none;
      }
    }
    .btn{
      height: 1rem;
      text-align: center;
      button{
        border-radius: 0;
        width: 90%;
        height: 60%;
      }
    }
  }
}
</style>

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>
// import router from '@/router/index'
// import { getMenuList } from '@/api/index'
// import { createMenuTree } from '@/utils/index'

export default {
  name: 'Layout',
  data () {
    return {
      treeData: [],
      collapsed: false
    }
  },
  components: {
  },
  created () {
    this.getMenuList()
  },
  methods: {
    getMenuList () {
      // console.log(this.$router.options.routes[0])
      this.treeData = this.$router.options.routes[1].children
      // this.$router.getRoutes()
      // getMenuList().then((res) => {
      //   // parent = createMenuTree(res, parent)
      //   console.log(createMenuTree(res))
      //   this.treeData = createMenuTree(res, parent)
      // })
    }
  }
}
</script>
<style lang="scss" scoped>
.home{
  .content{
    padding: 24px;
    background: #fff;
    min-height: 360px;
  }
}
#components-layout-demo-side .logo {
  height: 70px;
  transform: translateX(-5px);
}
#components-layout-demo-side .logoImg{
  margin-left: 56px;
}
#components-layout-demo-side .trigger {
  font-size: 18px;
  line-height: 64px;
  padding: 0 24px;
  cursor: pointer;
  transition: color 0.3s;
}
#components-layout-demo-side .trigger:hover {
  color: #1890ff;
}

::v-deep{
  .ant-layout-sider{
    background: black;
  }
  .ant-menu-dark, .ant-menu-dark .ant-menu-sub{
    background: black;
  }
  .ant-layout-sider-children{
    overflow: hidden;
  }
  .ant-menu-dark .ant-menu-inline.ant-menu-sub{
    background: black;
  }
}
</style>

点击查看代码
<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',
  data () {
    return {
    }
  },
  props: {
    // 树状结构菜单数据源
    treeData: {
      type: Array,
      default: () => []
    },
    // 默认展开的项key值
    defaultSelectItemKey: {
      type: String,
      default: () => ['']
    }
    // 默认展开的列表key值
    // defaultOpenItemKey: {
    //   type: String,
    //   default: () => ['']
    // }
  },
  created () {
  },
  methods: {
  }
}
</script>
<style lang="scss" scoped>
.menu{
  .menu_children{
    margin-left: .2rem;
  }
}
</style>

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

posted @ 2021-11-10 17:17  ~柚子~  阅读(130)  评论(0)    收藏  举报