项目实施

1. login登录模块

views文件夹存储路由控制的组件,components文件夹存储非路由控制的组件

1.1 前期准备

  1. 通过在终端中输入git checkout -b login语句,创建一个新的分支,用于记录对login模块的操作,我们在login分支中进行开发
  2. 在views文件夹下创建login登录组件,并在路由文件router>index.js中声明路由规则
// 路由规则
const routes = [
 { path: '/', redirect: '/login' }, //路由重定向,将主页重定向到login页面中
  { path: '/login', component: Login }, //路由规则
]
  1. 并在App跟组件中的template便签合适位置添加路由占位符<router-view></router-view>
  2. 创建完毕后,我们在谷歌浏览器调试工具中,会看到页面存在边距,这是组件和页面默认给定的边距,因此我们需要去掉这些内外边距,使得组件可以占满整个屏幕。在assests文件夹中创建css>global.css文件,并在main.js文件中添加import '@/assets/css/global.css'引入该css文件
/* 全局css样式 */
html,
body,
#app {
  height: 100%;
  margin: 0;
  padding: 0;
}

1.2 正式开始

1. 对页面样式进行编写,话不多说上代码

<template>
  <!-- 背景大盒子 -->
  <div class="login-container">
    <!-- 登录内容盒子 -->
    <div class="login-box">
      <!-- 头像区域 -->
      <div class="avatar-box">
        <img src="@/assets/head.jpg" alt="头像" />
      </div>
      <!-- 文本框内容区域 -->
      <el-form class="login-form" :model="loginForm" :rules="loginFormRules" ref="loginFormRef">
        <el-form-item prop="username">
          <el-input prefix-icon="el-icon-user" v-model="loginForm.username"></el-input>
        </el-form-item>
        <el-form-item prop="password">
          <el-input prefix-icon="el-icon-lock" v-model="loginForm.password"></el-input>
        </el-form-item>
        <el-form-item class="btns">
          <el-button type="primary" @click="login">登录</el-button>
          <el-button type="info" @click="resetLoginForm">重置</el-button>
        </el-form-item>
      </el-form>
    </div>
  </div>
</template>

<script>
// 行为区域内容在后续讲解 ...
</script>

<style lang="less" scoped>
// 为大盒子设置背景颜色,占满全屏`在这里插入代码片`
.login-container {
  background-color: #2b5b6b;
  height: 100%;
}
// 为登录内容盒子设置样式
.login-box {
  width: 450px;
  height: 300px;
  background-color: #fff;
  border-radius: 10px;
  // 盒子居中显示
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
}
// 头像框区域样式
.avatar-box {
  width: 130px;
  height: 130px;
  border-radius: 50%; //圆形盒子
  border: 1px solid #eee;
  box-shadow: 0 0 10px #ddd;
  padding: 10px;
  background-color: #fff;
  // 设置头像的位置,与父盒子居中偏上位置
  position: absolute;
  left: 50%;
  transform: translate(-50%, -50%);
  img {
    width: 100%;
    height: 100%;
    background-color: #eee;
    border-radius: 50%;
  }
}
.login-form {
  position: absolute;
  bottom: 0;
  width: 100%;
  padding: 0 20px;
  box-sizing: border-box; //设置盒子属性为内减模式,防止设置内边距后右侧突出现象
  .btns {
    display: flex;
    justify-content: flex-end;
  }
}
</style>

样式中form表单添加内边距产生问题:

  • 当给form表单定位到父元素底部时,发现表单宽度变小,不是占满整个父元素,需要给form表单添加width : 100%;占满整个父元素;
  • 当添加内边距padding时,左侧没有问题,右侧会超出父元素,这是因为form的box-sizing是默认content,因此需要添加box-sizing : border-box;

2. 登录操作逻辑实现

<script>
export default {
  name: 'Login',
  data() {
    return {
      //* 登录表单数据对象
      loginForm: {
        username: 'admin',
        password: '123456',
      },
      //* 登录表单的验证规则对象
      loginFormRules: {
        // 用户名验证规则数组
        username: [
          { required: true, message: '请输入用户名', trigger: 'blur' },
          { min: 3, max: 10, message: '长度在 3 到 10 个字符', trigger: 'blur' },
        ],
        // 密码验证规则数组
        password: [
          { required: true, message: '请输入密码', trigger: 'blur' },
          { min: 6, max: 15, message: '长度在 6 到 15 个字符', trigger: 'blur' },
        ],
      },
    }
  },
  methods: {
    //* 点击重置按钮重置表单内容与表单验证
    resetLoginForm() {
      this.$refs.loginFormRef.resetFields()
    },
    //* 点击登录按钮先判断表单验证结果,再向服务器发起登录请求
    login() {
      this.$refs.loginFormRef.validate(async valid => {
        if (!valid) return
        // todo 通过axios发送请求
        const { data: res } = await this.$http.post('login', this.loginForm)
        if (res.meta.status !== 200) return this.$message.error('登录失败')
        this.$message.success('登录成功')
        //todo 将token保存到本地sessionStorage中
        window.sessionStorage.setItem('token', res.data.token)
        this.$router.push('/home')
      })
    },
  },
}
</script>
  • data函数返回的对象是页面上用到的数据,loginForm是登录表单所绑定的数据源对象,loginFormRules是表单验证对象所绑定的数据源对象。
  • method对象定义了方法,重置按钮绑定resetLoginForm()方法,登录按钮绑定了login()方法。
  • <el-form>对象上定义了属性ref=loginFormRef,可以通过this.$refs点出所绑定的表单dom元素,根据element-ui中表单元素的方法.resetFields()重置表单内容,.validate((valid)=>{ })对表单验证的结果进行判断。
  • 当表单验证通过后,通过axios向服务器发送post请求,返回的结果是一个Promise对象,可利用await/async简化异步操作拿到返回的数据data并重新命名为res。
  • 判断res的meta.status的值,与接口文档对比,若不等于200则说明请求失败,返回提示消息,否则将res.data.token的值存入sessionStorage中并通过this.$router.push(‘/home’)进入home页面

1.3 重点内容讲解

添加表单验证步骤:

  • <el-form>添加属性:rules="rules",其中rules是表单验证规则对象,在script中定义
  • 在script中定义表单验证规则
//* 定义表单验证规则对象
      rules: {
        //* 帐号验证规则
        username: [
          { required: true, message: '请输入帐号', trigger: 'blur' },
          { min: 3, max: 10, message: '长度在 3 到 10 个字符', trigger: 'blur' },
        ],
    }
  • 通过<el-form-item>的prop属性设置验证规则<el-form-item prop="username">

导入axios以发送ajax请求:

  • 在main.js中import axios from 'axios'导入axios
  • 给axios配置默认根路径 axios.defaults.baseURL = 'http://127.0.0.1:8888/api/private/v1/'
  • 将axios挂载到vue构造函数的原型上Vue.prototype.$http = axios,命名为$http在各个vue实例中都可以通过this.$http调用axios

配置element弹窗提示:

  • element的Message组件的引用方法与其他组件有些许不同,import导入后,需要通过Vue.prototype.$message = Message方法将Message挂载到Vue构造函数的原型对象上
  • 这样就可以在全部vue实例上通过this.$message调用弹窗提示
  • 错误提示:this.$message.error('登录失败')正确提示:this.$message.success('登录成功')

配置前置路由守卫,实现路由跳转的判定:

  • 通过router.beforeEach来配置全局的前置路由守卫,每当页面发生跳转之前都要触发该路由守卫
  • 先对要访问的路径地址进行判断,访问的是否是登录页面,若访问登录页面则直接放行,否则判断是否sessionStorage存在token
  • 若不存在token,则说明还未登录,没有访问其他页面的权限,next方法自动将页面跳转到login页面
  • 否则token存在,说明已登录,可直接放行
//* 设置前置路由守卫,对访问的url地址进行权限判定
router.beforeEach((to, from, next) => {
  //* to表示要访问的路径
  //* from表示从哪个页面跳转进来
  //* next是一个方法,表示放行 next()放行 next('/login')跳转到login页面

  // *当要访问的路径是/login则直接放行
  if (to.path === '/login') return next()
  // *否则需要对sessuonStorage中的token进行验证
  const token = window.sessionStorage.getItem('token')
  // *当token为空,则直接跳转到登录页面
  if (!token) return next('/login')
  // *否则说明token存在,直接放行
  next()
})

1.4 后续工作

至此登录功能已经全部完成,需要将代码全部提交到gitee存储,vscode安装git插件能够对git操作进行可视化操作,非常方便,但处于学习目的,还是用老办法通过git命令行实现代码的提交

  1. git add .将修改过的文件全部保存到暂存区
  2. git commit -m '完成login登录模块全出操作'将暂存区中的文件提交到本地仓库
  3. git status 查看当前暂存区是否干净
  4. git push -u origin login将本地仓库中的login分支提交到远程仓库中
  5. git checkout master回到主分支上
  6. git branch查看当前分支
  7. git merge login将login分支上的内容合并到当前所在的master分支上
  8. git push将当前分支master推送到远程仓库中保存

至此所有git操作已经完成,刷新码云,可以看到远程仓库中添加了最新的内容和消息。

2. 主页模块

2.1 前期准备

  1. 在上一模块结束后,我们处于master主分支上。通过在终端中输入git checkout -b home语句,创建一个新的分支,用于记录对主页模块的操作,我们在home分支中进行开发
  2. 实现退出登陆功能。在页面中添加退出登录按钮,并为其绑定点击事件。退出登录的逻辑是:点击按钮,先清除sessionStorage中的token,再将页面跳转回login登录页面
//* 点击退出登陆按钮,退出登录
    logout() {
      //* 1. 先清除sessionStorage中的token
      window.sessionStorage.clear()
      //* 2. 页面跳转回login
      this.$router.replace('/login')
    },
  1. 为防止用户在未登录的情况下在地址栏中输入后续页面的url地址直接进入网页,因此需要为axios配置请求拦截器interceptors,在每次发起请求之前都要为该请求的Authorization添加token。在main。js文件中调用axios.interceptors.request.use表示每次发起请求之前都要调用use中的回调函数,形参config表示请求对象
//* 在挂载到Vue构造函数之前,先对axios配置请求拦截器
//* axios.interceptors.request 表示在每次请求之前都要调用use回调函数
axios.interceptors.request.use(config => {
  //* 在每次请求的请求头中挂载Authorization = token,服务器就能根据是饭后接收到token进行登录判断
  config.headers.Authorization = window.sessionStorage.getItem('token')
  return config //固定写法
})
  1. 在views文件夹下创建home.vue文件,并在router>index.js路由规则中声明home组件。{ path : '/home', component : 'Home'}

2.2 正式开始

  1. 使用element ui中的页面布局容器组件,将当前页面划分成头部标题栏,左侧导航栏和右侧内容栏
<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>
  1. 其中头部标题栏功能单一,由左侧logo和title与右侧退出登陆按钮组成
<!-- 头部区域 -->
   <el-header>
     <!-- 左侧标题 -->
     <div>
       <img src="@/assets/heima.png" alt="logo" />
       <span>后台电商管理平台</span>
     </div>
     <!-- 右侧按钮 -->
     <el-button type="info" @click="logout">退出登陆</el-button>
   </el-header>
   
   <!-- 样式区域 -->
   <style lang="less" scoped>
	.home-container {
	  height: 100%;
	}
	.el-header {
	  background-color: #373d41;
	  display: flex;
	  justify-content: space-between;
	  align-items: center;
	  div {
	    display: flex;
	    align-items: center;
	    span {
	      font-size: 20px;
	      margin-left: 20px;
	      color: #fff;
	    }
	  }
	}
	</style>
  1. 获取侧边栏数据,通过axios.get向服务器请求侧边栏内容列表
//* 获取左侧导航栏列表数据
    async getMenuList() {
      const { data: res } = await this.$http.get('menus')
      if (res.meta.status !== 200) return this.$message.error('菜单列表请求失败')
      this.menuList = res.data
      // console.log(this.menuList)
    },
  1. 通过element ui中的Menu组件,通过双重v-for循环渲染左侧侧边栏
<!-- 左侧菜单导航栏 -->
        <el-menu
          background-color="#333744"
          text-color="#fff"
          active-text-color="#409eff"
          unique-opened
          :collapse="isCollapse"
          :collapse-transition="false"
          router
          :default-active="this.$route.path"
        >
          <el-submenu :index="item.id + ''" v-for="item in menuList" :key="item.id">
            <template slot="title">
              <i :class="iconObj[item.id]"></i>
              <span>{{ item.authName }}</span>
            </template>
            <el-menu-item
              :index="'/' + subItem.path"
              v-for="subItem in item.children"
              :key="subItem.id"
            >
              <template slot="title">
                <i class="el-icon-menu"></i>
                <span>{{ subItem.authName }}</span>
              </template>
            </el-menu-item>
          </el-submenu>
        </el-menu>
  • <el-menu>
属性作用
background-color设置导航栏背景颜色
text-color未选中的文本颜色
active-text-color当前选中文本颜色
unique-opened是否只保持一个子菜单的展开
collapse是否折叠
collapse-transition是否开启折叠动画
router是否使用 vue-router 的模式,启用该模式会在激活导航时以 index 作为 path 进行路由跳转
default-active当前激活菜单的 index
  • <el-submenu>是可折叠的标题栏,通过v-for循环从服务器获取到的左侧菜单栏数据数组menuList,其中每一循环项item的authName就是当前折叠列表的标题。属性index表示当前折叠标题栏的唯一标识符,用item.id来指代。
  • <el-menu-item>是不可折叠的标题栏,及当前目录下的子标题,通过v-for循环当前折叠标题栏下的子标题及item.children数组。因为<el-menu>启用了router路由模式,因此每次点击当前<el-menu-item>都会跳转到index指代的页面,因此这里的index不用subItem.id指代而是用subItem.path指代。
  • 最终效果
    在这里插入图片描述
  1. 添加折叠按钮,在<el-menu>上方添加一个div,并绑定点击事件切换<el-menu>的collapse的值
<!-- 侧边栏 -->
<el-aside :width="isCollapse ? '64px' : '200px'">
  <!-- 切换折叠按钮 -->
  <div class="toggle-button" @click="changeCollapse">|||</div>
  <el-menu        
   :collapse="isCollapse"
   :collapse-transition="false"
   ...
  >
  ...
  <script>
 data() {
    return {
      //* 控制侧边栏折叠与展开
      isCollapse: false,
    }
  },
  methods: {
    //* 点击切换按钮折叠展开侧边栏
    changeCollapse() {
      this.isCollapse = !this.isCollapse
    },
  },
  </script>
  • 为div绑定了点击事件,isCollapse的值默认为false,每次点击都将isCollapse的值都取反
  • <el-menu>中根据isCollapse决定是否折叠
  • <el-aside>的宽度width根据isCollapse决定
  1. 右侧主体部分放置路由占位符
<!-- 右侧内容主体 -->
      <el-main>
        <router-view></router-view>
      </el-main>

2.3 页面代码

<template>
  <el-container class="home-container">
    <!-- 头部区域 -->
    <el-header>
      <!-- 左侧标题 -->
      <div>
        <img src="@/assets/heima.png" alt="logo" />
        <span>后台电商管理平台</span>
      </div>
      <!-- 右侧按钮 -->
      <el-button type="info" @click="logout">退出登陆</el-button>
    </el-header>
    <!-- 下方内容 -->
    <el-container>
      <!-- 侧边栏 -->
      <el-aside :width="isCollapse ? '64px' : '200px'">
        <!-- 切换折叠按钮 -->
        <div class="toggle-button" @click="changeCollapse">|||</div>
        <!-- 左侧菜单导航栏 -->
        <el-menu
          background-color="#333744"
          text-color="#fff"
          active-text-color="#409eff"
          unique-opened
          :collapse="isCollapse"
          :collapse-transition="false"
          router
          :default-active="this.$route.path"
        >
          <el-submenu :index="item.id + ''" v-for="item in menuList" :key="item.id">
            <template slot="title">
              <i :class="iconObj[item.id]"></i>
              <span>{{ item.authName }}</span>
            </template>
            <el-menu-item
              :index="'/' + subItem.path"
              v-for="subItem in item.children"
              :key="subItem.id"
            >
              <template slot="title">
                <i class="el-icon-menu"></i>
                <span>{{ subItem.authName }}</span>
              </template>
            </el-menu-item>
          </el-submenu>
        </el-menu>
      </el-aside>
      <!-- 右侧内容主体 -->
      <el-main>
        <router-view></router-view>
      </el-main>
    </el-container>
  </el-container>
</template>

<style lang="less" scoped>
.home-container {
  height: 100%;
}
.el-header {
  background-color: #373d41;
  display: flex;
  justify-content: space-between;
  align-items: center;
  div {
    display: flex;
    align-items: center;
    span {
      font-size: 20px;
      margin-left: 20px;
      color: #fff;
    }
  }
}
.el-aside {
  background-color: #333744;
}
.el-main {
  background-color: #eaedf1;
}
.iconfont {
  margin-right: 10px;
}
.toggle-button {
  background-color: #4a5064;
  color: #fff;
  text-align: center;
  line-height: 24px;
  font-size: 10px;
  letter-spacing: 0.2em; //字间距
  cursor: pointer; //鼠标经过变小手
}
.el-menu {
  border-right: none;
}
</style>
<script>
export default {
  name: 'Home',
  data() {
    return {
      //* 左侧数据列表
      menuList: [],

      //* 字体图标与id对应关系对象
      iconObj: {
        125: 'iconfont icon-users',
        103: 'iconfont icon-tijikongjian',
        101: 'iconfont icon-shangpin',
        102: 'iconfont icon-danju',
        145: 'iconfont icon-baobiao',
      },

      //* 控制侧边栏折叠与展开
      isCollapse: false,
    }
  },
  methods: {
    //* 退出登陆
    logout() {
      // 1.清除sessionStorage中的token
      window.sessionStorage.removeItem('token')
      // 2.跳转到login页面
      this.$router.replace('/login')
    },

    //* 获取左侧导航栏列表数据
    async getMenuList() {
      const { data: res } = await this.$http.get('menus')
      if (res.meta.status !== 200) return this.$message.error('菜单列表请求失败')
      this.menuList = res.data
      // console.log(this.menuList)
    },

    //* 点击切换按钮折叠展开侧边栏
    changeCollapse() {
      this.isCollapse = !this.isCollapse
    },
  },
  created() {
    this.getMenuList()
  },
}
</script>

2.4 后续工作

至此home组件以完成,向gitee仓库中提交代码

  1. git add .将修改过的文件全部保存到暂存区
  2. git commit -m '完成home主页模块全出操作'将暂存区中的文件提交到本地仓库
  3. git status 查看当前暂存区是否干净
  4. git push -u origin home将本地仓库中的home分支提交到远程仓库中
  5. git checkout master回到主分支上
  6. git branch查看当前分支
  7. git merge home将home分支上的内容合并到当前所在的master分支上
  8. git push将当前分支master推送到远程仓库中保存

因篇幅有限将后续功能写在下一文档中

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐