今日目标

1.实现后台首页的基本布局
2.实现左侧菜单栏
3.实现用户列表展示
4.实现添加用户

1.后台首页基本布局

打开Home.vue组件,进行布局:

  1. <el-container class="home-container">
  2. <!-- 头部区域 -->
  3. <el-header>Header<el-button type="info" @click="logout"> 退出 </el-button></el-header>
  4. <!-- 页面主体区域 -->
  5. <el-container>
  6. <!-- 侧边栏 -->
  7. <el-aside width="200px">Aside</el-aside>
  8. <!-- 主体结构 -->
  9. <el-main>Main</el-main>
  10. </el-container>
  11. </el-container>

默认情况下,跟element-ui组件同名的类名可以帮助我们快速的给对应的组件添加样式,如:

  1. .home-container {
  2. height: 100%;
  3. }
  4. .el-header{
  5. background-color:#373D41;
  6. }
  7. .el-aside{
  8. background-color:#333744;
  9. }
  10. .el-main{
  11. background-color:#eaedf1;
  12. }

2.顶部布局,侧边栏布局

  1. <template>
  2. <el-container class="home-container">
  3. <!-- 头部区域 -->
  4. <el-header>
  5. <div>
  6. <!-- 黑马logo -->
  7. <img src="../assets/heima.png" alt="">
  8. <!-- 顶部标题 -->
  9. <span>电商后台管理系统</span>
  10. </div>
  11. <el-button type="info" @click="logout"> 退出 </el-button>
  12. </el-header>
  13. <!-- 页面主体区域 -->
  14. <el-container>
  15. <!-- 侧边栏 -->
  16. <el-aside width="200px">
  17. <!-- 侧边栏菜单 -->
  18. <el-menu
  19. background-color="#333744"
  20. text-color="#fff"
  21. active-text-color="#ffd04b">
  22. <!-- 一级菜单 -->
  23. <el-submenu index="1">
  24. <!-- 一级菜单模板 -->
  25. <template slot="title">
  26. <!-- 图标 -->
  27. <i class="el-icon-location"></i>
  28. <!-- 文本 -->
  29. <span>导航一</span>
  30. </template>
  31. <!-- 二级子菜单 -->
  32. <el-menu-item index="1-4-1">
  33. <!-- 二级菜单模板 -->
  34. <template slot="title">
  35. <!-- 图标 -->
  36. <i class="el-icon-location"></i>
  37. <!-- 文本 -->
  38. <span>子菜单一</span>
  39. </template>
  40. </el-menu-item>
  41. </el-submenu>
  42. </el-menu>
  43. </el-aside>
  44. <!-- 主体结构 -->
  45. <el-main>Main</el-main>
  46. </el-container>
  47. </el-container>
  48. </template>

3.axios请求拦截器

后台除了登录接口之外,都需要token权限验证,我们可以通过添加axios请求拦截器来添加token,以保证拥有获取数据的权限
在main.js中添加代码,在将axios挂载到vue原型之前添加下面的代码

  1. //请求在到达服务器之前,先会调用use中的这个回调函数来添加请求头信息
  2. axios.interceptors.request.use(config=>{
  3. //为请求头对象,添加token验证的Authorization字段
  4. config.headers.Authorization = window.sessionStorage.getItem("token")
  5. return config
  6. })

4.请求侧边栏数据

  1. <script>
  2. export default {
  3. data() {
  4. return {
  5. // 左侧菜单数据
  6. menuList: null
  7. }
  8. },
  9. created() {
  10. // 在created阶段请求左侧菜单数据
  11. this.getMenuList()
  12. },
  13. methods: {
  14. logout() {
  15. window.sessionStorage.clear()
  16. this.$router.push('/login')
  17. },
  18. async getMenuList() {
  19. // 发送请求获取左侧菜单数据
  20. const { data: res } = await this.$http.get('menus')
  21. if (res.meta.status !== 200) return this.$message.error(res.meta.msg)
  22. this.menuList = res.data
  23. console.log(res)
  24. }
  25. }
  26. }
  27. </script>

通过v-for双重循环渲染左侧菜单

  1. <el-menu
  2. background-color="#333744"
  3. text-color="#fff"
  4. active-text-color="#ffd04b">
  5. <!-- 一级菜单 -->
  6. <el-submenu :index="item.id+''" v-for="item in menuList" :key="item.id">
  7. <!-- 一级菜单模板 -->
  8. <template slot="title">
  9. <!-- 图标 -->
  10. <i class="el-icon-location"></i>
  11. <!-- 文本 -->
  12. <span>{{item.authName}}</span>
  13. </template>
  14. <!-- 二级子菜单 -->
  15. <el-menu-item :index="subItem.id+''" v-for="subItem in item.children" :key="subItem.id">
  16. <!-- 二级菜单模板 -->
  17. <template slot="title">
  18. <!-- 图标 -->
  19. <i class="el-icon-location"></i>
  20. <!-- 文本 -->
  21. <span>{{subItem.authName}}</span>
  22. </template>
  23. </el-menu-item>
  24. </el-submenu>
  25. </el-menu>

5.设置激活子菜单样式

通过更改el-menu的active-text-color属性可以设置侧边栏菜单中点击的激活项的文字颜色
通过更改菜单项模板(template)中的i标签的类名,可以将左侧菜单栏的图标进行设置,我们需要在项目中使用第三方字体图标
在数据中添加一个iconsObj:

  1. iconsObj: {
  2. '125':'iconfont icon-user',
  3. '103':'iconfont icon-tijikongjian',
  4. '101':'iconfont icon-shangpin',
  5. '102':'iconfont icon-danju',
  6. '145':'iconfont icon-baobiao'
  7. }

然后将图标类名进行数据绑定,绑定iconsObj中的数据:
为了保持左侧菜单每次只能打开一个,显示其中的子菜单,我们可以在el-menu中添加一个属性unique-opened
或者也可以数据绑定进行设置(此时true认为是一个bool值,而不是字符串) :unique-opened=”true”

6.制作侧边菜单栏的伸缩功能

在菜单栏上方添加一个div

  1. <!-- 侧边栏,宽度根据是否折叠进行设置 -->
  2. <el-aside :width="isCollapse ? '64px':'200px'">
  3. <!-- 伸缩侧边栏按钮 -->
  4. <div class="toggle-button" @click="toggleCollapse">|||</div>
  5. <!-- 侧边栏菜单,:collapse="isCollapse"(设置折叠菜单为绑定的 isCollapse 值),:collapse-transition="false"(关闭默认的折叠动画) -->
  6. <el-menu
  7. :collapse="isCollapse"
  8. :collapse-transition="false"
  9. ......

然后给div添加样式,给div添加事件:

7.在后台首页添加子级路由

新增子级路由组件Welcome.vue
在router.js中导入子级路由组件,并设置路由规则以及子级路由的默认重定向
打开Home.vue,在main的主体结构中添加一个路由占位符
制作好了Welcome子级路由之后,我们需要将所有的侧边栏二级菜单都改造成子级路由链接
我们只需要将el-menu的router属性设置为true就可以了,此时当我们点击二级菜单的时候,就会根据菜单的index
属性进行路由跳转,如: /110,
使用index id来作为跳转的路径不合适,我们可以重新绑定index的值为 :index=”‘/‘+subItem.path”

8.完成用户列表主体区域

新建用户列表组件 user/Users.vue
在router.js中导入子级路由组件Users.vue,并设置路由规则
当点击二级菜单的时候,被点击的二级子菜单并没有高亮,我们需要正在被使用的功能高亮显示
我们可以通过设置el-menu的default-active属性来设置当前激活菜单的index
但是default-active属性也不能写死,固定为某个菜单值
所以我们可以先给所有的二级菜单添加点击事件,并将path值作为方法的参数
@click=”saveNavState(‘/‘+subItem.path)”
在saveNavState方法中将path保存到sessionStorage中
saveNavState( path ){
//点击二级菜单的时候保存被点击的二级菜单信息
window.sessionStorage.setItem(“activePath”,path);
this.activePath = path;
}
然后在数据中添加一个activePath绑定数据,并将el-menu的default-active属性设置为activePath
最后在created中将sessionStorage中的数据赋值给activePath
this.activePath = window.sessionStorage.getItem(“activePath”)

9.绘制用户列表基本结构

A.使用element-ui面包屑组件完成顶部导航路径(复制面包屑代码,在element.js中导入组件Breadcrumb,BreadcrumbItem)
B.使用element-ui卡片组件完成主体表格(复制卡片组件代码,在element.js中导入组件Card),再使用element-ui输入框完成搜索框及搜索按钮,
此时我们需要使用栅格布局来划分结构(复制卡片组件代码,在element.js中导入组件Row,Col),然后再使用el-button制作添加用户按钮

  1. <div>
  2. <h3>用户列表组件</h3>
  3. <!-- 面包屑导航 -->
  4. <el-breadcrumb separator="/">
  5. <el-breadcrumb-item :to="{ path: '/home' }">首页</el-breadcrumb-item>
  6. <el-breadcrumb-item>用户管理</el-breadcrumb-item>
  7. <el-breadcrumb-item>用户列表</el-breadcrumb-item>
  8. </el-breadcrumb>
  9. <!-- 卡片视图区域 -->
  10. <el-card>
  11. <!-- 搜索与添加区域 -->
  12. <el-row :gutter="20">
  13. <el-col :span="7">
  14. <el-input placeholder="请输入内容">
  15. <el-button slot="append" icon="el-icon-search"></el-button>
  16. </el-input>
  17. </el-col>
  18. <el-col :span="4">
  19. <el-button type="primary">添加用户</el-button>
  20. </el-col>
  21. </el-row>
  22. </el-card>
  23. </div>

10.请求用户列表数据

  1. <script>
  2. export default {
  3. data() {
  4. return {
  5. //获取查询用户信息的参数
  6. queryInfo: {
  7. query: '',
  8. pagenum: 1,
  9. pagesize: 2
  10. },
  11. //保存请求回来的用户列表数据
  12. userList:[],
  13. total:0
  14. }
  15. },
  16. created() {
  17. this.getUserList()
  18. },
  19. methods: {
  20. async getUserList() {
  21. //发送请求获取用户列表数据
  22. const { res: data } = await this.$http.get('users', {
  23. params: this.queryInfo
  24. })
  25. //如果返回状态为异常状态则报错并返回
  26. if (res.meta.status !== 200)
  27. return this.$message.error('获取用户列表失败')
  28. //如果返回状态正常,将请求的数据保存在data中
  29. this.userList = res.data.users;
  30. this.total = res.data.total;
  31. }
  32. }
  33. }
  34. </script>

11.将用户列表数据展示

使用表格来展示用户列表数据,使用element-ui表格组件完成列表展示数据(复制表格代码,在element.js中导入组件Table,TableColumn)
在渲染展示状态时,会使用作用域插槽获取每一行的数据
再使用switch开关组件展示状态信息(复制开关组件代码,在element.js中导入组件Switch)
而渲染操作列时,也是使用作用域插槽来进行渲染的,
在操作列中包含了修改,删除,分配角色按钮,当我们把鼠标放到分配角色按钮上时
希望能有一些文字提示,此时我们需要使用文字提示组件(复制文字提示组件代码,在element.js中导入组件Tooltip),将分配角色按钮包含
代码结构如下:

  1. <!-- 用户列表(表格)区域 -->
  2. <el-table :data="userList" border stripe>
  3. <el-table-column type="index"></el-table-column>
  4. <el-table-column label="姓名" prop="username"></el-table-column>
  5. <el-table-column label="邮箱" prop="email"></el-table-column>
  6. <el-table-column label="电话" prop="mobile"></el-table-column>
  7. <el-table-column label="角色" prop="role_name"></el-table-column>
  8. <el-table-column label="状态">
  9. <template slot-scope="scope">
  10. <el-switch v-model="scope.row.mg_state"></el-switch>
  11. </template>
  12. </el-table-column>
  13. <el-table-column label="操作" width="180px">
  14. <template slot-scope="scope">
  15. <!-- 修改 -->
  16. <el-button type="primary" icon="el-icon-edit" size='mini'></el-button>
  17. <!-- 删除 -->
  18. <el-button type="danger" icon="el-icon-delete" size='mini'></el-button>
  19. <!-- 分配角色 -->
  20. <el-tooltip class="item" effect="dark" content="分配角色" placement="top" :enterable="false">
  21. <el-button type="warning" icon="el-icon-setting" size='mini'></el-button>
  22. </el-tooltip>
  23. </template>
  24. </el-table-column>
  25. </el-table>

12.实现用户列表分页

A.使用表格来展示用户列表数据,可以使用分页组件完成列表分页展示数据(复制分页组件代码,在element.js中导入组件Pagination)
B.更改组件中的绑定数据

  1. <!-- 分页导航区域
  2. @size-change(pagesize改变时触发)
  3. @current-change(页码发生改变时触发)
  4. :current-page(设置当前页码)
  5. :page-size(设置每页的数据条数)
  6. :total(设置总页数) -->
  7. <el-pagination @size-change="handleSizeChange" @current-change="handleCurrentChange" :current-page="queryInfo.pagenum" :page-sizes="[1, 2, 5, 10]" :page-size="queryInfo.pagesize" layout="total, sizes, prev, pager, next, jumper" :total="total">
  8. </el-pagination>

C.添加两个事件的事件处理函数@size-change,current-change

  1. handleSizeChange(newSize) {
  2. //pagesize改变时触发,当pagesize发生改变的时候,我们应该
  3. //以最新的pagesize来请求数据并展示数据
  4. // console.log(newSize)
  5. this.queryInfo.pagesize = newSize;
  6. //重新按照pagesize发送请求,请求最新的数据
  7. this.getUserList();
  8. },
  9. handleCurrentChange( current ) {
  10. //页码发生改变时触发当current发生改变的时候,我们应该
  11. //以最新的current页码来请求数据并展示数据
  12. // console.log(current)
  13. this.queryInfo.pagenum = current;
  14. //重新按照pagenum发送请求,请求最新的数据
  15. this.getUserList();
  16. }

13.实现更新用户状态

当用户点击列表中的switch组件时,用户的状态应该跟随发生改变。
A.首先监听用户点击switch组件的事件,并将作用域插槽的数据当做事件参数进行传递

  1. <el-switch v-model="scope.row.mg_state" @change="userStateChanged(scope.row)"></el-switch>

B.在事件中发送请求完成状态的更改

  1. async userStateChanged(row) {
  2. //发送请求进行状态修改
  3. const { data: res } = await this.$http.put(
  4. `users/${row.id}/state/${row.mg_state}`
  5. )
  6. //如果返回状态为异常状态则报错并返回
  7. if (res.meta.status !== 200) {
  8. row.mg_state = !row.mg_state
  9. return this.$message.error('修改状态失败')
  10. }
  11. this.$message.success('更新状态成功')
  12. },

14.实现搜索功能

添加数据绑定,添加搜索按钮的点击事件(当用户点击搜索按钮的时候,调用getUserList方法根据文本框内容重新请求用户列表数据)
当我们在输入框中输入内容并点击搜索之后,会按照搜索关键字搜索,我们希望能够提供一个X删除搜索关键字并重新获取所有的用户列表数据,只需要给文本框添加clearable属性并添加clear事件,在clear事件中重新请求数据即可

  1. <el-col :span="7">
  2. <el-input placeholder="请输入内容" v-model="queryInfo.query" clearable @clear="getUserList">
  3. <el-button slot="append" icon="el-icon-search" @click="getUserList"></el-button>
  4. </el-input>
  5. </el-col>

15.实现添加用户

A.当我们点击添加用户按钮的时候,弹出一个对话框来实现添加用户的功能,首先我们需要复制对话框组件的代码并在element.js文件中引入Dialog组件
B.接下来我们要为“添加用户”按钮添加点击事件,在事件中将addDialogVisible设置为true,即显示对话框
C.更改Dialog组件中的内容

  1. <!-- 对话框组件 :visible.sync(设置是否显示对话框) width(设置对话框的宽度)
  2. :before-close(在对话框关闭前触发的事件) -->
  3. <el-dialog title="添加用户" :visible.sync="addDialogVisible" width="50%">
  4. <!-- 对话框主体区域 -->
  5. <el-form :model="addForm" :rules="addFormRules" ref="addFormRef" label-width="70px">
  6. <el-form-item label="用户名" prop="username">
  7. <el-input v-model="addForm.username"></el-input>
  8. </el-form-item>
  9. <el-form-item label="密码" prop="password">
  10. <el-input v-model="addForm.password"></el-input>
  11. </el-form-item>
  12. <el-form-item label="邮箱" prop="email">
  13. <el-input v-model="addForm.email"></el-input>
  14. </el-form-item>
  15. <el-form-item label="电话" prop="mobile">
  16. <el-input v-model="addForm.mobile"></el-input>
  17. </el-form-item>
  18. </el-form>
  19. <!-- 对话框底部区域 -->
  20. <span slot="footer" class="dialog-footer">
  21. <el-button @click="addDialogVisible = false">取 消</el-button>
  22. <el-button type="primary" @click="addDialogVisible = false">确 定</el-button>
  23. </span>
  24. </el-dialog>

D.添加数据绑定和校验规则:

  1. data() {
  2. //验证邮箱的规则
  3. var checkEmail = (rule, value, cb) => {
  4. const regEmail = /^\w+@\w+(\.\w+)+$/
  5. if (regEmail.test(value)) {
  6. return cb()
  7. }
  8. //返回一个错误提示
  9. cb(new Error('请输入合法的邮箱'))
  10. }
  11. //验证手机号码的规则
  12. var checkMobile = (rule, value, cb) => {
  13. const regMobile = /^1[34578]\d{9}$/
  14. if (regMobile.test(value)) {
  15. return cb()
  16. }
  17. //返回一个错误提示
  18. cb(new Error('请输入合法的手机号码'))
  19. }
  20. return {
  21. //获取查询用户信息的参数
  22. queryInfo: {
  23. // 查询的条件
  24. query: '',
  25. // 当前的页数,即页码
  26. pagenum: 1,
  27. // 每页显示的数据条数
  28. pagesize: 2
  29. },
  30. //保存请求回来的用户列表数据
  31. userList: [],
  32. total: 0,
  33. //是否显示添加用户弹出窗
  34. addDialogVisible: false,
  35. // 添加用户的表单数据
  36. addForm: {
  37. username: '',
  38. password: '',
  39. email: '',
  40. mobile: ''
  41. },
  42. // 添加表单的验证规则对象
  43. addFormRules: {
  44. username: [
  45. { required: true, message: '请输入用户名称', trigger: 'blur' },
  46. {
  47. min: 3,
  48. max: 10,
  49. message: '用户名在3~10个字符之间',
  50. trigger: 'blur'
  51. }
  52. ],
  53. password: [
  54. { required: true, message: '请输入密码', trigger: 'blur' },
  55. {
  56. min: 6,
  57. max: 15,
  58. message: '用户名在6~15个字符之间',
  59. trigger: 'blur'
  60. }
  61. ],
  62. email: [
  63. { required: true, message: '请输入邮箱', trigger: 'blur' },
  64. { validator:checkEmail, message: '邮箱格式不正确,请重新输入', trigger: 'blur'}
  65. ],
  66. mobile: [
  67. { required: true, message: '请输入手机号码', trigger: 'blur' },
  68. { validator:checkMobile, message: '手机号码不正确,请重新输入', trigger: 'blur'}
  69. ]
  70. }
  71. }
  72. }

E.当关闭对话框时,重置表单
给el-dialog添加@close事件,在事件中添加重置表单的代码

  1. methods:{
  2. ....
  3. addDialogClosed(){
  4. //对话框关闭之后,重置表达
  5. this.$refs.addFormRef.resetFields();
  6. }
  7. }

F.点击对话框中的确定按钮,发送请求完成添加用户的操作
首先给确定按钮添加点击事件,在点击事件中完成业务逻辑代码

  1. methods:{
  2. ....
  3. addUser(){
  4. //点击确定按钮,添加新用户
  5. //调用validate进行表单验证
  6. this.$refs.addFormRef.validate( async valid => {
  7. if(!valid) return this.$message.error("请填写完整用户信息");
  8. //发送请求完成添加用户的操作
  9. const {data:res} = await this.$http.post("users",this.addForm)
  10. //判断如果添加失败,就做提示
  11. if (res.meta.status !== 200)
  12. return this.$message.error('添加用户失败')
  13. //添加成功的提示
  14. this.$message.success("添加用户成功")
  15. //关闭对话框
  16. this.addDialogVisible = false
  17. //重新请求最新的数据
  18. this.getUserList()
  19. })
  20. }
  21. }