# 角色管理模块

# 角色管理介绍

image-20210216191636914

总结:角色的增删改查

image-20210411145611551

  • 用户(员工)账号:用来登录系统
  • 角色:员工的身份,不同的身份拥有不同的菜单,角色可以分配给用户
  • 权限:功能按钮,可以分配给角色

# 角色管理组件布局

根据以上的结构,我们采用element-ui的组件实现**src/views/setting/index.vue**

<template>
  <div class="setting-container">
    <div class="app-container">
      <el-card>
        <el-tabs>
          <!-- 左侧 -->
          <el-tab-pane label="角色管理">
            <!-- 按钮 -->
            <el-button
              icon="el-icon-plus"
              size="small"
              type="primary"
            >新增角色</el-button>
            <!-- 表格 -->
            <el-table>
              <el-table-column label="序号" width="100" />
              <el-table-column label="角色名称" width="240" />
              <el-table-column label="描述" />
              <el-table-column label="操作">
                <el-button size="small" type="success">分配权限</el-button>
                <el-button size="small" type="primary">编辑</el-button>
                <el-button size="small" type="danger">删除</el-button>
              </el-table-column>
            </el-table>
          </el-tab-pane>

          <el-tab-pane label="公司信息">
            <!-- 公司信息 -->
          </el-tab-pane>
        </el-tabs>
      </el-card>
    </div>
  </div>
</template>

总结:

  1. 选项卡组件用法
  2. 表格组件的用法

# 获取角色列表数据

  • 首先,封装读取角色的信息的请求 src/api/setting.js
import request from '@/utils/request'
/**
 * 获取角色列表
 * ***/
export function reqGetRoleList(options) {
  return request({
    method: 'get',
    url: '/sys/role',
    params: options
  })
}
  • 然后,在页面中调用接口获取数据 src/views/setting/index.vue
import { reqGetRoleList } from '@/api/setting.js'

export default {
  name: 'Setting',
  data () {
    return {
      // 查询参数
      fillterParams: {
        page: 1,
        pagesize: 10
      },
      // 角色列表
      list: [],
      // 角色列表的总数
      total: 0
    }
  },
  created () {
    this.loadRoleList()
  },
  methods: {
    // 加载角色列表的数据
    async loadRoleList () {
      try {
        const ret = await reqGetRoleList(this.fillterParams)
        this.list = ret.data.rows
        this.total = ret.data.total
      } catch {
        this.$message.error('获取角色列表失败')
      }
    }
  }
}
  • 绑定表格数据
<el-table :data="list">
  <el-table-column type="index" label="序号" width="120" />
  <el-table-column prop="name" label="角色名称" width="240" />
  <el-table-column prop="description" label="描述" />
  <el-table-column label="操作">
    <el-button size="small" type="success">分配权限</el-button>
    <el-button size="small" type="primary">编辑</el-button>
    <el-button size="small" type="danger">删除</el-button>
  </el-table-column>
</el-table>

总结:调用接口;获取数据;渲染表格

注意:表格列的索引 type='index' 是ElementUI提供的规则

# 处理表格分页

需求:基于ElementUI提供的分页组件实现分页

<el-pagination
  layout="prev, pager, next"
  :total="total"
  :current-page="page"
  :page-size="pagesize"
  @current-change="handleCurrentChange"
/>
handleCurrentChange(index) {
  this.page = index // 更新当前页
  this.getRoleList() // 重新获取数据
}

总结:分页的本质,根据当前的页码的变化,重新加载当前页的数据,并且把之前的数据覆盖了

# 处理序号问题

  • 自定义序号
<el-table-column type="index" :index="handleIndex" label="序号" width="120" />
  • 配置自定义函数
methods: {
    handleIndex (index) {
      return (this.page - 1) * this.pagesize + index + 1
    }
}

总结:动态计算索引的累加值

# 添加表格loading效果

  • 绑定加载状态位
<el-table v-loading="loading" :data="list">
data () {
	return {
		loading: false
	}
}
  • 动态设置 loading 的状态
// 加载角色列表的数据
async loadRoleList () {
  try {
    this.loading = true
    const ret = await reqGetRoleList(this.fillterParams)
    this.list = ret.data.rows
    this.total = ret.data.total
  } catch {
    this.$message.error('获取角色列表失败')
  }
  this.loading = false
}

总结:通过v-loading绑定表格的加载状态,接口调用之前显示加载状态,接口数据返回后隐藏状态。

# 删除角色功能

目标 实现删除角色的功能

  1. 绑定事件
  2. 提示删除
  3. 调用接口实现删除
  • 封装删除角色的api
export function reqDeleteRole(id) {
  return request({
    url: `/sys/role/${id}`,
    method: 'delete'
  })
}
  • 删除按钮注册事件
<el-table-column label="操作">
  <template #default="{ row }">
    <el-button size="small" type="success">分配权限</el-button>
    <el-button size="small" type="primary">编辑</el-button>
    <el-button size="small" type="danger" @click="delRole(row.id)">删除</el-button>
  </template>
</el-table-column>
  • 删除功能实现
// 删除角色
handleDelete (id) {
  // 删除部门
    this.$confirm('确认要删除角色吗?, 是否继续?', '提示', {
      confirmButtonText: '确定',
      cancelButtonText: '取消',
      type: 'warning'
    }).then(async () => {
      // 确认删除
      const ret = await reqDeleteRole(id)
      if (!ret.success) {
        this.$message.error(ret.message)
      } else {
        // 删除成功,刷新列表
        // 判断当前页是否还有剩余数据,如果删除的是最后一条数据,需要显示上一页数据
        if (this.total % this.fillterParams.pagesize === 1 && this.fillterParams.page > 1) {
           this.fillterParams.page -= 1
        }
        this.loadRoleList()
      }
    }).catch((e) => {
      if (e !== 'cancel') {
        this.$message.error('删除角色失败')
      }
    })
},

总结:绑定事件;调用接口;刷新列表

# 回顾

  • 组织架构
    • 表单验证(部门名称和编号)
    • 添加部门提交表单
    • 取消操作(重置表单)
    • 调用接口获取要编辑的部门数据并进行回填
    • 基于计算属性定制组件的标题
    • 提交表单区分编辑和添加的操作
    • 重构编辑部门的表单验证(部门名称和部门编号)
    • 控制列表的加载状态
    • 部门名称表单验证规则和后端不匹配,需要迎合后端
  • 角色管理
    • 熟悉角色管理的基本业务
    • 用户 -- 角色 -- 权限 三者之间的关系
    • 角色管理基本布局
    • 角色列表数据的动态渲染
    • 表格的分页操作
    • 定制索引列
    • 控制表格数据的加载状态
    • 删除角色

# 添加角色弹窗控制

添加角色流程

  1. 点击添加按钮,
  2. 显示弹窗,
  3. 提交表单,
  4. 刷新列表
  • 准备弹框
<el-dialog title="弹层标题" :visible="showDialog" @close="showDialog=false">
  我是弹层
</el-dialog>
// data中添加一个状态位
showDialog: false
  • 给按钮注册点击事件
<el-button
  icon="el-icon-plus"
  size="small"
  type="primary"
  @click="showDialog=true"
>新增角色</el-button>
  • 准备弹框表单结构
<el-dialog title="弹层标题" :visible="showDialog" @close="btnCancel">
  <el-form ref="roleForm" :model="form" :rules="rules" label-width="100px">
    <el-form-item label="角色名称" prop="name">
      <el-input v-model="form.name" placeholder="请输入角色名称" />
    </el-form-item>
    <el-form-item label="角色描述">
      <el-input v-model="form.description" placeholder="请输入角色描述" />
    </el-form-item>
  </el-form>

  <template #footer>
    <el-button @click="btnCancel">取消</el-button>
    <el-button type="primary">确认</el-button>
  </template>
</el-dialog>
  • 准备数据 - 注意: 这边接口文档字段有误,namedescription
data() {
  return {
    ...
    form: {
      name: '',
      description: ''
    },
    rules: {
      name: [
        { required: true, message: '请输入角色名称', trigger: ['blur', 'change'] }
      ]
    }
  }
},

总结

  1. 表单的角色描述的名称和接口文档不一致(接口文档有误)
  2. 表单验证
  3. v-model的修饰符.trim/.number

# 添加角色提交表单

  • 封装新增角色功能api setting.js
export function reqAddRole(data) {
  return request({
    url: '/sys/role',
    data,
    method: 'post'
  })
}
  • 校验完成添加功能
<el-button type="primary" @click="handleSubmit">确认</el-button>
// 添加角色提交表单
handleSubmit () {
  this.$refs.roleForm.validate(async valid => {
    if (!valid) return
    try {
      const ret = await reqAddRole(this.form)
      if (!ret.success) {
        this.$message.error(ret.message)
      } else {
        // 操作成功,关闭弹窗,刷新列表
        this.showDialog = false
        this.loadRoleList()
      }
    } catch {
      this.$message.error('添加角色失败')
    }
  })
},

总结:封装接口;绑定事件;验证表单;调用接口

  • 重置表单的用法
    • 重置表单的方法resetFields需要表单项有prop才可以
    • 也可以通过this.$options.data()的方式获取表单的原始数据
// 取消操作
handleCancel () {
  // 隐藏弹窗
  this.showDialog = false
  // this.$refs.roleForm.resetFields()
  // this.$options.data()表示data中的原始数据
  // 重置表单
  this.form = this.$options.data().form
},

# 编辑角色封装接口

编辑角色:

  1. 点击按钮加载角色的数据并且回填表单
  2. 修改信息后提交表单
  • 封装接口
// 修改角色
export function reqUpdateRole(data) {
  return request({
    method: 'put',
    url: `/sys/role/${data.id}`,
    data
  })
}

// 获取角色详情
export function reqGetRoleDetail(id) {
  return request({
    method: 'get',
    url: `/sys/role/${id}`
  })
}

# 编辑角色显示数据

  • 点击编辑按钮
<el-button size="small" type="primary" @click="toEdit(scope.row.id)">编辑</el-button>
  • 显示弹层, 获取数据回显
import { reqAddRole, reqDeleteRole, reqGetRoleDetail, reqGetRoleList } from '@/api/setting'

// 去编辑
async toEdit (id) {
  try {
    // 根据id查询详情数据,然后填充表单
    const ret = await reqGetRoleDetail(id)
    if (ret.success) {
      // 回填表单
      this.form = ret.data
      // 显示弹窗
      this.showDialog = true
    } else {
      this.$message.error(ret.message)
    }
  } catch {
    this.$message.error('获取角色信息失败')
  }
},
  • 计算属性 - 控制标题
computed: {
  title() {
    return this.form.id ? '编辑角色' : '添加角色'
  }
},
  • 关闭时要重置数据
handleCancel () {
  // 隐藏弹窗
  this.showDialog = false
  // this.$refs.roleForm.resetFields()
  // this.$options.data()表示data中的原始数据
  // 重置表单
  this.form = this.$options.data().form
},
  • 编辑完成
// 添加角色提交表单
handleSubmit () {
  this.$refs.roleForm.validate(async valid => {
    if (!valid) return
    // 判断是添加角色还是修改角色
    if (this.form.id) {
      // 编辑操作
      try {
        const ret = await reqUpdateRole(this.form)
        if (!ret.success) {
          this.$message.error(ret.message)
        } else {
          // 操作成功,关闭弹窗,刷新列表
          this.showDialog = false
          this.loadRoleList()
        }
      } catch {
        this.$message.error('编辑角色失败')
      }
    } else {
      try {
        const ret = await reqAddRole(this.form)
        if (!ret.success) {
          this.$message.error(ret.message)
        } else {
          // 操作成功,关闭弹窗,刷新列表
          this.showDialog = false
          this.loadRoleList()
        }
      } catch {
        this.$message.error('添加角色失败')
      }
    }
  })
},

总结:添加和编辑角色重用提交逻辑(通过表单数据的id存在与否区分两种操作)

# 公司信息

需求:展示公司的基本信息,调用接口;获取数据;填充表单;不允许编辑

image-20210412100241845

  • 基本布局
<el-tab-pane label="公司信息">
  <!-- 警告信息 -->
  <el-alert
    title="对公司名称、公司地址、营业执照、公司地区的更新,将使得公司资料被重新审核,请谨慎修改"
    type="info"
    show-icon
    :closable="false"
  />
  <!-- 表单 -->
  <el-form label-width="120px" style="margin-top:50px">
    <el-form-item label="公司名称">
      <el-input disabled style="width:400px" />
    </el-form-item>
    <el-form-item label="公司地址">
      <el-input disabled style="width:400px" />
    </el-form-item>
    <el-form-item label="邮箱">
      <el-input disabled style="width:400px" />
    </el-form-item>
    <el-form-item label="备注">
      <el-input type="textarea" :rows="3" disabled style="width:400px" />
    </el-form-item>
  </el-form>
</el-tab-pane>
  • 封装接口
export function reqGetCompanyInfo(companyId) {
  return request({
    url: `/company/${companyId}`
  })
}
  • 这里需要的 companyId 在 vuex 个人信息里
computed: {
  ...mapState('user', ['userInfo']),
  showTitle() {
    return this.form.id ? '编辑角色' : '添加角色'
  }
},
// 监听Tab的切换
async changeTab ({name}) {
  // 参数的解构赋值
  if (name === 'company') {
    try {
      // 获取公司的基本信息
      const ret = await reqGetCompanyInfo(this.userInfo.companyId)
      this.companyInfo = ret.data
      if (!ret.success) {
        this.$message.error(ret.message)
      }
    } catch {
      this.$message.error('获取公司信息失败')
    }
  }     
},
  • 渲染数据
<!-- 表单 -->
<el-form label-width="120px" style="margin-top:50px">
  <el-form-item label="公司名称">
    <el-input v-model="companyInfo.name" disabled style="width:400px" />
  </el-form-item>
  <el-form-item label="公司地址">
    <el-input v-model="companyInfo.companyAddress" disabled style="width:400px" />
  </el-form-item>
  <el-form-item label="邮箱">
    <el-input v-model="companyInfo.mailbox" disabled style="width:400px" />
  </el-form-item>
  <el-form-item label="备注">
    <el-input v-model="companyInfo.remarks" type="textarea" :rows="3" disabled style="width:400px" />
  </el-form-item>
</el-form>

总结:

  1. 如何获取Vuex中的状态数据
  2. 选项卡组件的用法(切换选项卡时调用接口)
  3. 调用接口;获取数据;填充表单