zoukankan      html  css  js  c++  java
  • Vue + Element UI 实现权限管理系统 前端篇(十四):菜单功能实现

    菜单功能实现

    菜单接口封装

    菜单管理是一个对菜单树结构的增删改查操作。

    提供一个菜单查询接口,查询整颗菜单树形结构。

    http/modules/menu.js 添加 findMenuTree 接口。

    import axios from '../axios'
    
    /* 
     * 菜单管理模块
     */
    
     // 保存
    export const save = (data) => {
        return axios({
            url: '/menu/save',
            method: 'post',
            data
        })
    }
    // 删除
    export const batchDelete = (data) => {
        return axios({
            url: '/menu/delete',
            method: 'post',
            data
        })
    }
    // 查找导航菜单树
    export const findNavTree = (params) => {
        return axios({
            url: '/menu/findNavTree',
            method: 'get',
            params
        })
    }
    // 查找导航菜单树
    export const findMenuTree = () => {
        return axios({
            url: '/menu/findMenuTree',
            method: 'get'
        })
    }

    菜单管理界面

    菜单管理界面是使用封装的表格树组件显示菜单结构,并提供增删改查的功能。

    Menu.vue

    <template>
      <div class="container" style="99%;margin-top:-25px;">
        <!--工具栏-->
        <div class="toolbar" style="float:left;padding-top:10px;padding-left:15px;">
            <el-form :inline="true" :model="filters" :size="size">
                <el-form-item>
                    <el-input v-model="filters.name" placeholder="名称"></el-input>
                </el-form-item>
                <el-form-item>
                    <kt-button label="查询" perms="sys:menu:view" type="primary" @click="findTreeData(null)"/>
                </el-form-item>
                <el-form-item>
                    <kt-button label="新增" perms="sys:menu:add" type="primary" @click="handleAdd"/>
                </el-form-item>
            </el-form>
        </div>
        <!--表格树内容栏-->
        <el-table :data="tableTreeDdata" stripe size="mini" style=" 100%;"
          v-loading="loading" element-loading-text="拼命加载中">
          <el-table-column
            prop="id" header-align="center" align="center" width="80" label="ID">
          </el-table-column>
          <table-tree-column 
            prop="name" header-align="center" treeKey="id" width="150" label="名称">
          </table-tree-column>
          <el-table-column header-align="center" align="center" label="图标">
            <template slot-scope="scope">
              <i :class="scope.row.icon || ''"></i>
            </template>
          </el-table-column>
          <el-table-column prop="type" header-align="center" align="center" label="类型">
            <template slot-scope="scope">
              <el-tag v-if="scope.row.type === 0" size="small">目录</el-tag>
              <el-tag v-else-if="scope.row.type === 1" size="small" type="success">菜单</el-tag>
              <el-tag v-else-if="scope.row.type === 2" size="small" type="info">按钮</el-tag>
            </template>
          </el-table-column>
          <el-table-column 
            prop="parentName" header-align="center" align="center" width="120" label="上级菜单">
          </el-table-column>
          <el-table-column
            prop="url" header-align="center" align="center" width="150" 
            :show-overflow-tooltip="true" label="菜单URL">
          </el-table-column>
          <el-table-column
            prop="perms" header-align="center" align="center" width="150" 
            :show-overflow-tooltip="true" label="授权标识">
          </el-table-column>
          <el-table-column
            prop="orderNum" header-align="center" align="center" label="排序">
          </el-table-column>
          <el-table-column
            fixed="right" header-align="center" align="center" width="150" label="操作">
            <template slot-scope="scope">
              <kt-button label="修改" perms="sys:menu:edit" @click="handleEdit(scope.row)"/>
              <kt-button label="删除" perms="sys:menu:delete" type="danger" @click="handleDelete(scope.row)"/>
            </template>
          </el-table-column>
        </el-table>
        <!-- 新增修改界面 -->
        <el-dialog :title="!dataForm.id ? '新增' : '修改'" width="40%" :visible.sync="dialogVisible" :close-on-click-modal="false">
          <el-form :model="dataForm" :rules="dataRule" ref="dataForm" @keyup.enter.native="submitForm()" 
            label-width="80px" :size="size" style="text-align:left;">
            <el-form-item label="菜单类型" prop="type">
              <el-radio-group v-model="dataForm.type">
                <el-radio v-for="(type, index) in menuTypeList" :label="index" :key="index">{{ type }}</el-radio>
              </el-radio-group>
            </el-form-item>
            <el-form-item :label="menuTypeList[dataForm.type] + '名称'" prop="name">
              <el-input v-model="dataForm.name" :placeholder="menuTypeList[dataForm.type] + '名称'"></el-input>
            </el-form-item>
            <el-form-item label="上级菜单" prop="parentName">
                <popup-tree-input 
                  :data="popupTreeData" :props="popupTreeProps" :prop="dataForm.parentName==null?'根节点':dataForm.parentName" 
                  :nodeKey="''+dataForm.parentId" :currentChangeHandle="handleTreeSelectChange">
                </popup-tree-input>
            </el-form-item>
            <el-form-item v-if="dataForm.type === 1" label="菜单路由" prop="url">
              <el-input v-model="dataForm.url" placeholder="菜单路由"></el-input>
            </el-form-item>
            <el-form-item v-if="dataForm.type !== 0" label="授权标识" prop="perms">
              <el-input v-model="dataForm.perms" placeholder="如: sys:user:add, sys:user:edit, sys:user:delete"></el-input>
            </el-form-item>
            <el-form-item v-if="dataForm.type !== 2" label="排序编号" prop="orderNum">
              <el-input-number v-model="dataForm.orderNum" controls-position="right" :min="0" label="排序编号"></el-input-number>
            </el-form-item>
            <el-form-item v-if="dataForm.type !== 2" label="菜单图标" prop="icon">
              <el-row>
                <el-col :span="22">
                  <!-- <el-popover
                    ref="iconListPopover"
                    placement="bottom-start"
                    trigger="click"
                    popper-class="mod-menu__icon-popover">
                    <div class="mod-menu__icon-list">
                      <el-button
                        v-for="(item, index) in dataForm.iconList"
                        :key="index"
                        @click="iconActiveHandle(item)"
                        :class="{ 'is-active': item === dataForm.icon }">
                        <icon-svg :name="item"></icon-svg>
                      </el-button>
                    </div>
                  </el-popover> -->
                  <el-input v-model="dataForm.icon" v-popover:iconListPopover :readonly="true" placeholder="菜单图标名称(如:fa fa-home fa-lg)" class="icon-list__input"></el-input>
                </el-col>
                <el-col :span="2" class="icon-list__tips">
                  <fa-icon-tooltip />
                </el-col>
              </el-row>
            </el-form-item>
          </el-form>
          <span slot="footer" class="dialog-footer">
            <el-button :size="size"  @click="dialogVisible = false">取消</el-button>
            <el-button :size="size"  type="primary" @click="submitForm()">确定</el-button>
          </span>
        </el-dialog>
      </div>
    </template>
    
    <script>
    import KtButton from "@/views/Core/KtButton"
    import TableTreeColumn from '@/views/Core/TableTreeColumn'
    import PopupTreeInput from "@/components/PopupTreeInput"
    import FaIconTooltip from "@/components/FaIconTooltip"
    export default {
        components:{
        PopupTreeInput,
        KtButton,
        TableTreeColumn,
        FaIconTooltip
        },
        data() {
            return {
                size: 'small',
                loading: false,
                filters: {
                    name: ''
          },
          tableTreeDdata: [],
          dialogVisible: false,
          menuTypeList: ['目录', '菜单', '按钮'],
          dataForm: {
            id: 0,
            type: 1,
            name: '',
            parentId: 0,
            parentName: '',
            url: '',
            perms: '',
            orderNum: 0,
            icon: '',
            iconList: []
          },
          dataRule: {
            name: [
              { required: true, message: '菜单名称不能为空', trigger: 'blur' }
            ],
            parentName: [
              { required: true, message: '上级菜单不能为空', trigger: 'change' }
            ]
          },
          popupTreeData: [],
          popupTreeProps: {
                    label: 'name',
                    children: 'children'
                }
            }
        },
        methods: {
            // 获取数据
        findTreeData: function () {
          this.loading = true
                this.$api.menu.findMenuTree().then((res) => {
            this.tableTreeDdata = res.data
            this.popupTreeData = this.getParentMenuTree(res.data)
            this.loading = false
                })
        },
            // 获取上级菜单树
        getParentMenuTree: function (tableTreeDdata) {
          let parent = {
            parentId: -1,
            name: '根节点',
            children: tableTreeDdata
          }
          return [parent]
        },
            // 显示新增界面
            handleAdd: function () {
                this.dialogVisible = true
                this.dataForm = {
            id: 0,
            type: 1,
            typeList: ['目录', '菜单', '按钮'],
            name: '',
            parentId: 0,
            parentName: '',
            url: '',
            perms: '',
            orderNum: 0,
            icon: '',
            iconList: []
          }
            },
            // 显示编辑界面
            handleEdit: function (row) {
          this.dialogVisible = true
          Object.assign(this.dataForm, row);
            },
        // 删除
        handleDelete (row) {
          this.$confirm('确认删除选中记录吗?', '提示', {
                    type: 'warning'
          }).then(() => {
            let params = this.getDeleteIds([], row)
            this.$api.menu.batchDelete(params).then( res => {
              this.findTreeData()
              this.$message({message: '删除成功', type: 'success'})
            })
          })
        },
        // 获取删除的包含子菜单的id列表
        getDeleteIds (ids, row) {
          ids.push({id:row.id})
          if(row.children != null) {
            for(let i=0, len=row.children.length; i<len; i++) {
              this.getDeleteIds(ids, row.children[i])
            }
          }
          return ids
        },
          // 菜单树选中
        handleTreeSelectChange (data, node) {
          this.dataForm.parentId = data.id
          this.dataForm.parentName = data.name
        },
        // 图标选中
        iconActiveHandle (iconName) {
          this.dataForm.icon = iconName
        },
        // 表单提交
        submitForm () {
          this.$refs['dataForm'].validate((valid) => {
            if (valid) {
                        this.$confirm('确认提交吗?', '提示', {}).then(() => {
                            this.editLoading = true
                            let params = Object.assign({}, this.dataForm)
                            this.$api.menu.save(params).then((res) => {
                  if(res.code == 200) {
                                    this.$message({ message: '操作成功', type: 'success' })
                                } else {
                                    this.$message({message: '操作失败, ' + res.msg, type: 'error'})
                                }
                                this.editLoading = false
                                this.$refs['dataForm'].resetFields()
                                this.dialogVisible = false
                                this.findTreeData()
                            })
                        })
                    }
          })
        }
        },
        mounted() {
        this.findTreeData()
        }
    }
    </script>
    
    <style scoped>
    
    </style>

    其中对表格树组件进行了简单的封装。

    views/Core/TableTreeColumn.vue

    <template>
      <el-table-column :prop="prop" v-bind="$attrs">
        <template slot-scope="scope">
          <span @click.prevent="toggleHandle(scope.$index, scope.row)" :style="childStyles(scope.row)">
            <i :class="iconClasses(scope.row)" :style="iconStyles(scope.row)"></i>
            {{ scope.row[prop] }}
          </span>
        </template>
      </el-table-column>
    </template>
    
    <script>
      import isArray from 'lodash/isArray'
      export default {
        name: 'table-tree-column',
        props: {
          prop: {
            type: String
          },
          treeKey: {
            type: String,
            default: 'id'
          },
          parentKey: {
            type: String,
            default: 'parentId'
          },
          levelKey: {
            type: String,
            default: 'level'
          },
          childKey: {
            type: String,
            default: 'children'
          }
        },
        methods: {
          childStyles (row) {
            return { 'padding-left': (row[this.levelKey] * 25) + 'px' }
          },
          iconClasses (row) {
            return [ !row._expanded ? 'el-icon-caret-right' : 'el-icon-caret-bottom' ]
          },
          iconStyles (row) {
            return { 'visibility': this.hasChild(row) ? 'visible' : 'hidden' }
          },
          hasChild (row) {
            return (isArray(row[this.childKey]) && row[this.childKey].length >= 1) || false
          },
          // 切换处理
          toggleHandle (index, row) {
            if (this.hasChild(row)) {
              var data = this.$parent.store.states.data.slice(0)
              data[index]._expanded = !data[index]._expanded
              if (data[index]._expanded) {
                data = data.splice(0, index + 1).concat(row[this.childKey]).concat(data)
              } else {
                data = this.removeChildNode(data, row[this.treeKey])
              }
              this.$parent.store.commit('setData', data)
              this.$nextTick(() => {
                this.$parent.doLayout()
              })
            }
          },
          // 移除子节点
          removeChildNode (data, parentId) {
            var parentIds = isArray(parentId) ? parentId : [parentId]
            if (parentId.length <= 0) {
              return data
            }
            var ids = []
            for (var i = 0; i < data.length; i++) {
              if (parentIds.indexOf(data[i][this.parentKey]) !== -1 && parentIds.indexOf(data[i][this.treeKey]) === -1) {
                ids.push(data.splice(i, 1)[0][this.treeKey])
                i--
              }
            }
            return this.removeChildNode(data, ids)
          }
        }
      }
    </script>

    测试效果

    最终测试效果下图所示。

    源码下载

    后端:https://gitee.com/liuge1988/kitty

    前端:https://gitee.com/liuge1988/kitty-ui.git


    作者:朝雨忆轻尘
    出处:https://www.cnblogs.com/xifengxiaoma/ 
    版权所有,欢迎转载,转载请注明原文作者及出处。

  • 相关阅读:
    ♫【插件】插入Flash swfobject
    ☀【Alice】
    _#【Vim】
    _#【选择器】链式class选择器
    _#【HTML】
    _#【CSS】display:inlineblock
    【折叠】一
    图解SSIS自动维护SQL索引
    wininet.dll函数库:检查网络状态
    sqlserver中动态sql语句应用
  • 原文地址:https://www.cnblogs.com/xifengxiaoma/p/9679022.html
Copyright © 2011-2022 走看看