主页功能开发

Flex布局

采用Flex布局得元素,成为Flex容器,简称容器。它得所有子元素自动成为容器成员,成为Flex项(Flex item)

Flex布局有两个轴,主轴和侧轴(cross,交叉轴)。

默认X为主轴,从左向右;侧轴Y轴,垂直向下。默认线性沿着主轴排列,不换行。

  • justify-content 设置主轴上子元素排列方式
    • flex-start 默认值,沿着主轴排列。如果主轴是row,子元素从左至右排列
    • flex-end 从右开始对齐
    • center 子元素居中对齐
    • space-around 平分剩余空间
    • space-between 先两边贴边,再平分剩余空间(重要)
  • align-items 设置侧轴上的子元素的排列方式(单行)
    • 控制一行元素侧轴上的排列效果
    • flex-start 从头开始
    • flex-end 从尾开始
    • center 居中
    • stretch 沿着侧轴拉伸,子元素不要给高度

div a中增加2个div b和c,默认div是block,所以这b、c是各占一行。a采用flex后,b、c称为Flex item,并挤在一起了。默认所有子元素按照行从左开始排列,默认不折行。

主页布局

背景参考可以i参考:https://preview.pro.ant.design/dashboard/analysis/

参考 https://element.eleme.cn/#/zh-CN/component/container,选择一个合适的布局

<el-container>
 <el-header>Header</el-header>
 <el-container>
   <el-aside width="200px">Aside</el-aside>
   <el-main>Main</el-main>
 </el-container>
</el-container>

image

主页组件

src/plugins/element.js

import Vue from 'vue'
import { Form, FormItem, Input, Button, Message, Container, Header, Aside, 
Main } from 'element-ui'
import 'element-ui/lib/theme-chalk/index.css' // UI组件样式
Vue.use(Form)
Vue.use(FormItem)
Vue.use(Input)
Vue.use(Button)
Vue.use(Container)
Vue.use(Header)
Vue.use(Aside)
Vue.use(Main)
// 全局导入
Vue.prototype.$message = Message

scr/Views/HomeViews.vue

在Header中增加2个div,一个放logo,一个放退出按钮

<template>
  <el-container>
    <el-header>
      <div class="logo">
        <img src="../assets/logo.png" alt="logo" />
        <div class="title">马哥教育猛犸运维系统管理平台</div>
      </div>
      <div class="info">
        <el-button type="info" @click="logout">退出</el-button>
      </div>
    </el-header>
    <el-container>
      <el-aside width="200px">Aside</el-aside>
      <el-main>Main</el-main>
    </el-container>
  </el-container>
</template>
<script>
export default {
  methods: {
    logout() {
      window.localStorage.removeItem('token')
      this.$router.push('/login')
   }
 }
}
</script>
<style lang="less" scoped>
.el-container {
  height: 100%;
}

.el-header {
  display: flex;
  justify-content: space-between;

  .logo {
    display: flex;

    img {
      width: 30px;
      height: 30px;
      margin-top: 15px;
    }

    .title {
      font-size: 24px;
      margin-left: 5px;
      margin-top: 15px;
    }

    i {
      font-size: 30px;
      margin-top: 15px;
    }
  }
}

.el-aside {
  background-color: #112233;
}

.el-button {
  margin-top: 10px;
}

.el-main {
  background-color: #f0f2f4;
}

.el-menu {
  border-right: none;
}
</style>

侧边栏菜单布局

src/plugins/element.js

import Vue from 'vue'
import { Form, FormItem, Input, Button, Message, Container, Header, Aside, 
Main, Menu, MenuItem, Submenu } from 'element-ui'
import 'element-ui/lib/theme-chalk/index.css' // UI组件样式
Vue.use(Form)
Vue.use(FormItem)
Vue.use(Input)
Vue.use(Button)
Vue.use(Container)
Vue.use(Header)
Vue.use(Aside)
Vue.use(Main)
Vue.use(Menu)
Vue.use(MenuItem)
Vue.use(Submenu)
// 全局导入
Vue.prototype.$message = Message

侧栏导航菜单 https://element.eleme.cn/#/zh-CN/component/menu#ce-lan

基本嵌套结构

<el-menu default-active="2-2">
  <el-submenu index="1">
 <template slot="title"><i class="el-icon-menu"></i>导航一</template>
 <el-menu-item index="1-1">修改密码</el-menu-item>
 <el-menu-item index="1-2">用户列表</el-menu-item>
  </el-submenu>
  <el-submenu index="2">
 <template slot="title"><i class="el-icon-menu"></i>导航二</template>
 <el-menu-item index="2-1">修改密码</el-menu-item>
 <el-menu-item index="2-2">用户列表</el-menu-item>
  </el-submenu>
</el-menu>

src/Views/HomeView.vue

<template>
  <el-container>
    <el-header>
      <div class="logo">
        <img src="../assets/logo.png" alt="logo" />
        <div class="title">马哥教育猛犸运维管理平台</div>
        <i :class="isCollapse ? 'el-icon-s-unfold' : 'el-icon-s-fold'" @click="isCollapse = !isCollapse"></i>
      </div>
      <div class="info">
        <el-button type="primary" @click="logout">退出</el-button>
      </div>
    </el-header>
    <el-container>
      <el-aside :width="isCollapse ? '64px' : '240px'">
        <el-menu
          router
          default-active="101"
          background-color="#123"
          text-color="#fff"
          active-text-color="#ffd04b"
          :collapse="isCollapse"
        >
          <el-submenu :index="item.id + ''" v-for="item in menuList" :key="item.id">
            <template slot="title">
              <i class="el-icon-menu"></i>
              <span>{{ item.name }}</span>
            </template>
            <el-menu-item :index="sub.path" v-for="sub in item.children" :key="sub.id">
              {{ sub.name }}
            </el-menu-item>
          </el-submenu>
          <el-menu-item index="2">
            <i class="el-icon-menu"></i>
            <span slot="title">导航二</span>
          </el-menu-item>
        </el-menu>
      </el-aside>
      <el-main>
        <!-- 嵌套路由 呈现 子组件的地方 -->
        <router-view></router-view>
      </el-main>
    </el-container>
  </el-container>
</template>

<script>
export default {
  created() {
    // 生命周期钩子函数。该组件js内存中的实例对象被创建出来
    this.getMenuList()
  },
  data() {
    return {
      menuList: [],
      isCollapse: false
    }
  },
  methods: {
    logout() {
      window.localStorage.removeItem('token')
      this.$router.push('/login')
    },
    async getMenuList() {
      // const {data:response} = await this.$http.get('xxx/', {
      //   params: {a:1, b:2},
      //   headers: {'Authorization': 'Bearer ' + window.localStorage.getItem('token')},
      // }) // 需要token
      const { data: response } = await this.$http.get('users/menulist/') // token header
      console.log(response)
      this.menuList = response
    }
  }
}
</script>

<style lang="less" scoped>
.el-container {
  height: 100%;
}
.el-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding-left: 5px;
  .logo {
    display: flex;
    align-items: center;
    img {
      height: 32px;
    }
    .title {
      font-size: 24px;
      margin-left: 5px;
    }
    i {
      font-size: 30px;
      margin-left: 5px;
    }
  }
}

.el-aside {
  background-color: #123;
}
.el-main {
  background-color: #f0f2f4;
}
.el-menu {
  border-right: none;
}
</style>

左边栏菜单内容可以客户端写死,也可以从服务端动态获取。

从服务端获取,可以先认证用户后,根据用户的权限,返回菜单项。

axios拦截器

前端通过token判断是否登录,但是更多得数据时服务器端返回得,当浏览器端发请求到服务器端,服务器端必须能认证身份,那么浏览器端必须在请求得Header中增加Authorization字段。

文档:https://www.kancloud.cn/yunye/axios/234845

策略:只要向服务器端发请求,想获取数据,就必须进行token认证。

src/main.js

import Vue from 'vue'
import App from './App.vue'
import router from './router'
import './plugins/element.js'
import './assets/css/main.css'
import axios from 'axios'
// axios全局设置
// 全局拦截器,只要发起异步请求都要带上这个token
axios.interceptors.request.use(config => {
  config.headers.Authorization = 'Bearer ' + window.localStorage.getItem('token')
  return config
})
// baseURL指向后台服务
axios.defaults.baseURL = '/api/v1/' // 代理到'http://127.0.0.1:8000/'
// 为Vue类增加全局属性$http,这样所有组件实例都可以使用该属性了
Vue.prototype.$http = axios
Vue.config.productionTip = false
new Vue({
  router,
  render: h => h(App)
}).$mount('#app')