Spring Security(六):前端菜单,角色权限页面的搭建

1,345 阅读4分钟

目前项目中做了几个有关权限控制的页面,主要做了三个,角色管理,资源管理,角色权限的页面。

角色管理

该页面主要是对角色的crud,这个比较简单就不多讲了。

资源管理

这个页面其实是对菜单的配置,也就是路由的设置,我采用了组件复用的思路,就是增加和更新页面用的是同一个组件,启用前判断是增加还是更新就好了。

如果是新增的话有一个会初始化
更新的话就会得到信息,显示出来

Vue代码

  1. 新建src\views\pre\perm\edit.vue文件,这个其实编辑按钮引用的组件,通过这里可以设置我们要进行的操作的是编辑,然后通过scope获取到数据
		<template>
		  <div>
		    <el-button size="mini" type="success" @click="to">编辑</el-button>
		    <eForm ref="form" :menus="menus" :sup_this="sup_this" :is-add="false"/>
		  </div>
		</template>
		<script>
		import eForm from './form'
		export default {
		  components: { eForm },
		  props: {
		    data: {
		      type: Object,
		      required: true
		    },
		    sup_this: {
		      type: Object,
		      required: true
		    },
		    menus: {
		      type: Array,
		      required: true
		    }
		  },
		  methods: {
		    to() {
		      const _this = this.$refs.form
		      _this.roleIds = []
		      _this.form = { per_id: this.data.per_id, per_component: this.data.per_component, per_name: this.data.per_name, per_sort: this.data.per_sort,
		        per_parent_id: this.data.per_parent_id, per_resource: this.data.per_resource, per_icon: this.data.per_icon, per_describe: this.data.per_describe }
		      _this.dialog = true
		    }
		  }
		}
		</script>
		
		<style scoped>
		  div{
		    display: inline-block;
		    margin-right: 3px;
		  }
		</style>

2.新建一个src\views\pre\perm\form.vue文件 这个就是我们要复用的组件了

	<template>
	  <el-dialog :append-to-body="true" :visible.sync="dialog" :title="isAdd ? '新增菜单' : '编辑菜单'" width="600px">
	    <el-form ref="form" :model="form" :rules="rules" size="small" label-width="80px">
	      <el-form-item label="菜单图标">
	        <el-popover
	          placement="bottom-start"
	          width="460"
	          trigger="click"
	          @show="$refs['iconSelect'].reset()">
	          <el-input slot="reference" v-model="form.icon" style="width: 460px;" placeholder="点击选择图标" readonly>
	            <svg-icon v-if="form.icon" slot="prefix" :icon-class="form.icon" class="el-input__icon" style="height: 32px;width: 16px;" />
	            <i v-else slot="prefix" class="el-icon-search el-input__icon"/>
	          </el-input>
	        </el-popover>
	      </el-form-item>
	      <el-form-item label="菜单名称" prop="per_name">
	        <el-input v-model="form.per_name" placeholder="名称" style="width: 460px;"/>
	      </el-form-item>
	      <el-form-item label="菜单排序" prop="per_sort">
	        <el-input v-model.number="form.per_sort" placeholder="序号越小越靠前" style="width: 460px;"/>
	      </el-form-item>
	      <el-form-item label="URL地址">
	        <el-input v-model="form.per_resource" placeholder="组件路径" style="width: 460px;"/>
	      </el-form-item>
	      <el-form-item label="组件路径">
	        <el-input v-model="form.per_component" placeholder="组件路径" style="width: 460px;"/>
	      </el-form-item>
	      <el-form-item label="上级菜单">
	        <treeselect v-model="form.per_parent_id" :options="menus" style="width: 460px;" placeholder="选择上级类目" />
	      </el-form-item>
	      <el-form-item label="菜单描述" prop="per_describe">
	        <el-input v-model.number="form.per_describe" placeholder="请填写菜单描述" style="width: 460px;"/>
	      </el-form-item>
	    </el-form>
	    <div slot="footer" class="dialog-footer">
	      <el-button type="text" @click="cancel">取消</el-button>
	      <el-button :loading="loading" type="primary" @click="doSubmit">确认</el-button>
	    </div>
	  </el-dialog>
	</template>
	
	<script>
	// import the component
	import Treeselect from '@riophae/vue-treeselect'
	// import the styles
	import '@riophae/vue-treeselect/dist/vue-treeselect.css'
	import { add, edit } from '@/api/menu'
	export default {
	  components: { Treeselect },
	  props: {
	    menus: {
	      type: Array,
	      required: true
	    },
	    isAdd: {
	      type: Boolean,
	      required: true
	    },
	    sup_this: {
	      type: Object,
	      default: null
	    }
	  },
	  data() {
	    return {
	      loading: false, dialog: false,
	      form: { per_id: null, per_name: '', per_sort: 999, per_resource: '', per_component: '', per_parent_id: 0, per_icon: '', per_describe: '' },
	      roleIds: [],
	      rules: {
	        per_name: [
	          { required: true, message: '请输入名称', trigger: 'blur' }
	        ],
	        per_sort: [
	          { required: true, message: '请输入序号', trigger: 'blur', type: 'number' }
	        ]
	      }
	    }
	  },
	  methods: {
	    cancel() {
	      this.resetForm()
	    },
	    doSubmit() {
	      this.$refs['form'].validate((valid) => {
	        if (valid) {
	          this.loading = true
	          this.form.roles = []
	          const _this = this
	          this.roleIds.forEach(function(data, index) {
	            const role = { id: data }
	            _this.form.roles.push(role)
	          })
	          if (this.isAdd) {
	            this.doAdd()
	          } else this.doEdit()
	        } else {
	          return false
	        }
	      })
	    },
	    doAdd() {
	      add(this.form).then(res => {
	        this.resetForm()
	        this.$notify({
	          title: '添加成功',
	          type: 'success',
	          duration: 2500
	        })
	        this.loading = false
	        this.$parent.$parent.init()
	        this.$parent.$parent.getMenus()
	      }).catch(error => {
	        this.loading = false
	        this.$message({
	          showClose: true,
	          message: error,
	          type: 'error'
	        })
	      })
	    },
	    doEdit() {
	      edit(this.form).then(res => {
	        this.resetForm()
	        this.$notify({
	          title: '修改成功',
	          type: 'success',
	          duration: 2500
	        })
	        this.loading = false
	        this.sup_this.init()
	        this.sup_this.getMenus()
	      }).catch(err => {
	        this.loading = false
	        console.log(err.msg)
	      })
	    },
	    resetForm() {
	      this.dialog = false
	      this.$refs['form'].resetFields()
	      this.form = { name: '', sort: 999, path: '', component: '', iframe: 'false', pid: 0, icon: '' }
	      this.roleIds = []
	    },
	    selected(name) {
	      this.form.icon = name
	    }
	  }
	}
	</script>
	
	<style scoped>
	
	</style>

做了这个通用的组件就不用在代码中同时引用两个组件了。

角色权限

我的这个项目前端对权限的控制对象主要是页面的路由,可以动态的去设置角色可以显示哪些菜单。使用了el-tree这个组件...不会css...页面很丑莫见怪... 系统管理员的页面

测试用户,当然正常情况下用户是不应该可以看到这些关于全县的页面的,我这里只是拿来测试。
可以看到测试用户那边的路由页面根据我们的配置就显示出来了

动态配置的思路

角色权限这个页面中的树形组件其实是对我们数据库中的RolePermission组件进行增删改查,大致过程是:

  • 通过角色id获得用户的路由集合,然后通过el-tree组件去渲染是否被选中
  • 更新的思路是首先删除RolePermission表中这个角色id所有的数据,然后根据我们前端拿到的id集合去批量增加。

小问题

由于el-tree组件拿到我们后台给的父节点的id,会直接把子节点全部选中,但是这个明显不符合我们的要求,目前的解决办法是把父子节点的关联移除,这样有个问题是我们自己去配置的时候选中父节点,子节点要一个一个去选择...我还在想解决办法...