SpringBoot mybatis 实现多级树形菜单的示例代码

一、前言

iview-admin中提供了 v-org-tree这么一个vue组件可以实现树形菜单,下面小编来提供一下在element-ui中的使用教程(项目见:https://github.com/lison16/v-org-tree)

小编集成了el-dropdown下拉菜单(鼠标左击显示菜单),和右击自定义菜单,两种方式,效果图如下:

二、使用教程

(1)安装依赖

npm install clipboard
npm install v-click-outside-x
npm install v-org-tree

(2)引入组件

在main.js文件中引入

import TreeTable from 'tree-table-vue'
import VOrgTree from 'v-org-tree'

(3)引入部分js工具方法

在项目目录下 -> src -> directive文件夹中引入如下4个js文件

clipboard.js

import Clipboard from 'clipboard'
export default {
  bind: (el, binding) => {
    const clipboard = new Clipboard(el, {
      text: () => binding.value.value
    })
    el.__success_callback__ = binding.value.success
    el.__error_callback__ = binding.value.error
    clipboard.on('success', e => {
      const callback = el.__success_callback__
      callback && callback(e)
    })
    clipboard.on('error', e => {
      const callback = el.__error_callback__
      callback && callback(e)
    })
    el.__clipboard__ = clipboard
  },
  update: (el, binding) => {
    el.__clipboard__.text = () => binding.value.value
    el.__success_callback__ = binding.value.success
    el.__error_callback__ = binding.value.error
  },
  unbind: (el, binding) => {
    delete el.__success_callback__
    delete el.__error_callback__
    el.__clipboard__.destroy()
    delete el.__clipboard__
  }
}

draggable.js

import { on } from '@/libs/tools'
export default {
  inserted: (el, binding, vnode) => {
    const triggerDom = document.querySelector(binding.value.trigger)
    triggerDom.style.cursor = 'move'
    const bodyDom = document.querySelector(binding.value.body)
    let pageX = 0
    let pageY = 0
    let transformX = 0
    let transformY = 0
    let canMove = false
    const handleMousedown = e => {
      let transform = /\(.*\)/.exec(bodyDom.style.transform)
      if (transform) {
        transform = transform[0].slice(1, transform[0].length - 1)
        const splitxy = transform.split('px, ')
        transformX = parseFloat(splitxy[0])
        transformY = parseFloat(splitxy[1].split('px')[0])
      }
      pageX = e.pageX
      pageY = e.pageY
      canMove = true
    }
    const handleMousemove = e => {
      const xOffset = e.pageX - pageX + transformX
      const yOffset = e.pageY - pageY + transformY
      if (canMove) bodyDom.style.transform = `translate(${xOffset}px, ${yOffset}px)`
    }
    const handleMouseup = e => {
      canMove = false
    }
    on(triggerDom, 'mousedown', handleMousedown)
    on(document, 'mousemove', handleMousemove)
    on(document, 'mouseup', handleMouseup)
  },
  update: (el, binding, vnode) => {
    if (!binding.value.recover) return
    const bodyDom = document.querySelector(binding.value.body)
    bodyDom.style.transform = ''
  }
}

directives.js

import draggable from './module/draggable'
import clipboard from './module/clipboard'

const directives = {
  draggable,
  clipboard
}

export default directives

index.js

import directive from './directives'

const importDirective = Vue => {
  /**
   * 拖拽指令 v-draggable="options"
   * options = {
   *  trigger: /这里传入作为拖拽触发器的CSS选择器/,
   *  body:    /这里传入需要移动容器的CSS选择器/,
   *  recover: /拖动结束之后是否恢复到原来的位置/
   * }
   */
  Vue.directive('draggable', directive.draggable)
  /**
   * clipboard指令 v-draggable="options"
   * options = {
   *  value:    /在输入框中使用v-model绑定的值/,
   *  success:  /复制成功后的回调/,
   *  error:    /复制失败后的回调/
   * }
   */
  Vue.directive('clipboard', directive.clipboard)
}

export default importDirective

(4)正式使用v-org-tree组件

在所要使用的地方新增如下几个文件,比如我要写在user-group文件夹中

项目\src\components\org-view下面建立二个文件:

index.js

import OrgView from './org-view.vue'
export default OrgView

org-view.vue

<template>
  <div
    ref="dragWrapper"
    class="org-tree-drag-wrapper"
    @mousedown="mousedownView"
    @contextmenu="handleDocumentContextmenu"
  >
    <div class="org-tree-wrapper" :style="orgTreeStyle">
      <v-org-tree
        v-if="data"
        :data="data"
        :node-render="nodeRender"
        :expand-all="true"
        @on-node-click="handleNodeClick"
        collapsable
      ></v-org-tree>
    </div>
  </div>
</template>

<script>
import { on, off } from '@/directive/module/tools'

export default {
  name: 'OrgView',
  props: {
    zoomHandled: {
      type: Number,
      default: 1
    },
    data: Object,
    menuList: {
      type: Array,
      default: function () {
        return [
          {
            key: 'edit',
            label: '编辑公司'
          },
          {
            key: 'detail',
            label: '查看公司'
          },
          {
            key: 'add',
            label: '新增子公司'
          },
          {
            key: 'delete',
            label: '删除公司'
          }
        ]
      }
    }
  },
  data () {
    return {
      currentContextMenuId: '',
      orgTreeOffsetLeft: 0,
      orgTreeOffsetTop: 0,
      initPageX: 0,
      initPageY: 0,
      oldMarginLeft: 0,
      oldMarginTop: 0,
      canMove: false
    }
  },
  computed: {
    orgTreeStyle () {
      return {
        transform: `translate(-50%, -50%) scale(${this.zoomHandled}, ${
          this.zoomHandled
        })`,
        marginLeft: `${this.orgTreeOffsetLeft}px`,
        marginTop: `${this.orgTreeOffsetTop}px`
      }
    }
  },
  methods: {
    handleNodeClick (e, data, expand) {
      expand()
    },
    closeMenu () {
      this.currentContextMenuId = ''
    },
    getBgColor (data) {
      return this.currentContextMenuId === data.id
        ? data.isRoot
          ? '#0d7fe8'
          : '#5d6c7b'
        : ''
    },
    nodeRender (h, data) {
      return (
        <div
          class={[
            'custom-org-node',
            data.children && data.children.length ? 'has-children-label' : ''
          ]}
          on-mousedown={event => event.stopPropagation()}
          on-contextmenu={this.contextmenu.bind(this, data)}
        >
          {data.label}
          <dropdown
            trigger="custom"
            class="context-menu"
            visible={this.currentContextMenuId === data.id}
            nativeOn-click={this.handleDropdownClick}
            on-on-click={this.handleContextMenuClick.bind(this, data)}
            style={{
              transform: `scale(${1 / this.zoomHandled}, ${1 /
                this.zoomHandled})`
            }}
            v-click-outside={this.closeMenu}
          >
            <dropdown-menu slot="list">
              {this.menuList.map(item => {
                return (
                  <dropdown-item name={item.key}>{item.label}</dropdown-item>
                )
              })}
            </dropdown-menu>
          </dropdown>
        </div>
      )
    },
    contextmenu (data, $event) {
      const event = $event || window.event
      event.preventDefault
        ? event.preventDefault()
        : (event.returnValue = false)
      this.currentContextMenuId = data.id
    },
    setDepartmentData (data) {
      data.isRoot = true
      this.departmentData = data
    },
    mousedownView (event) {
      this.canMove = true
      this.initPageX = event.pageX
      this.initPageY = event.pageY
      this.oldMarginLeft = this.orgTreeOffsetLeft
      this.oldMarginTop = this.orgTreeOffsetTop
      on(document, 'mousemove', this.mousemoveView)
      on(document, 'mouseup', this.mouseupView)
    },
    mousemoveView (event) {
      if (!this.canMove) return
      const { pageX, pageY } = event
      this.orgTreeOffsetLeft = this.oldMarginLeft + pageX - this.initPageX
      this.orgTreeOffsetTop = this.oldMarginTop + pageY - this.initPageY
    },
    mouseupView () {
      this.canMove = false
      off(document, 'mousemove', this.mousemoveView)
      off(document, 'mouseup', this.mouseupView)
    },
    handleDropdownClick (event) {
      event.stopPropagation()
    },
    handleDocumentContextmenu () {
      this.canMove = false
    },
    handleContextMenuClick (data, key) {
      this.$emit('on-menu-click', { data, key })
    }
  },
  mounted () {
    on(document, 'contextmenu', this.handleDocumentContextmenu)
  },
  beforeDestroy () {
    off(document, 'contextmenu', this.handleDocumentContextmenu)
  }
}
</script>

<style>
</style>

项目\src\components\zoom-controller下面建立二个文件:

index.js

import ZoomController from './zoom-controller'
export default ZoomController

zoom-controller.vue

<template>
  <div class="zoom-wrapper">
    <button class="zoom-button" @click="scale('down')">
      <Icon type="md-remove" :size="14" color="#fff"/>
    </button>
    <span class="zoom-number">{{ value }}%</span>
    <button class="zoom-button" @click="scale('up')">
      <Icon type="md-add" :size="14" color="#fff"/>
    </button>
  </div>
</template>

<script>
export default {
  name: 'ZoomController',
  props: {
    value: {
      type: Number,
      default: 100
    },
    step: {
      type: Number,
      default: 20
    },
    min: {
      type: Number,
      default: 10
    },
    max: {
      type: Number,
      default: 200
    }
  },
  methods: {
    scale (type) {
      const zoom = this.value + (type === 'down' ? -this.step : this.step)
      if (
        (zoom < this.min && type === 'down') ||
        (zoom > this.max && type === 'up')
      ) {
        return
      }
      this.$emit('input', zoom)
    }
  }
}
</script>

<style lang="less">
.trans(@duration) {
  transition: ~"all @{duration} ease-in";
}
.zoom-wrapper {
  .zoom-button {
    width: 20px;
    height: 20px;
    line-height: 10px;
    border-radius: 50%;
    background: rgba(157, 162, 172, 1);
    box-shadow: 0px 2px 8px 0px rgba(218, 220, 223, 0.7);
    border: none;
    cursor: pointer;
    outline: none;
    &:active {
      box-shadow: 0px 0px 2px 2px rgba(218, 220, 223, 0.2) inset;
    }
    .trans(0.1s);
    &:hover {
      background: #1890ff;
      .trans(0.1s);
    }
  }
  .zoom-number {
    color: #657180;
    padding: 0 8px;
    display: inline-block;
    width: 46px;
    text-align: center;
  }
}
</style>

项目\src\view下面建立org.vue文件

<template>
  <Card shadow style="height: 100%;width: 100%;overflow:hidden">
    <div class="department-outer">
      <div class="zoom-box">
        <zoom-controller v-model="zoom" :min="20" :max="200"></zoom-controller>
      </div>
      <div class="view-box">
        <org-view
            v-if="data"
            :data="data"
            :zoom-handled="zoomHandled"
            :menuList="menuList"
            @on-menu-click="handleMenuClick"
        ></org-view>
      </div>
    </div>
  </Card>
</template>

<script>
import { orgList } from '@/api/org'
import { layerDialog } from '@/libs/Diaglog'
import './org.less'

const OrgView = Vue.component('OrgView', function (resolve) {
  require(['/user/org-view'], resolve)
})
const ZoomController = Vue.component('ZoomController', function (resolve) {
  require(['/user/zoom-controller'], resolve)
})
export default {
  name: 'org_tree_page',
  components: {
    OrgView,
    ZoomController
  },
  data () {
    return {
      data: null,
      zoom: 100,
      menuList: [
        {
          key: 'add',
          label: '新增子公司'
        },
        {
          key: 'edit',
          label: '编辑公司'
        },
        {
          key: 'delete',
          label: '删除公司'
        }
      ]
    }
  },
  computed: {
    zoomHandled () {
      return this.zoom / 100
    }
  },
  methods: {
    setDepartmentData (data) {
      data.isRoot = true
      return data
    },
    handleMenuClick ({ data, key }) {
      switch (key) {
        case 'add':
        case 'edit':
          console.log(data)
          this.showDialog(data, key)
          break
        case 'delete':
          break
      }
    },
    showDialog (data, key) {
      data.key = key
      const option = {
        id: key + 'SaveDialog',
        title: this.$i18n.t(key),
        width: '600px',
        height: '550px',
        url: '/api/org/' + Qs.stringify(data, { arrayFormat: 'brackets' })
      }
      layerDialog(option)
    },
    getDepartmentData () {
      const entity = {}
      const levels = '0,1,2,3,4,5,6'
      entity.status = 1
      getOrgList(entity, 1, 20, levels).then(result => {
        if (result.data.code === 10000) {
          let len = 0
          const list = result.data.data.list
          if (list) {
            len = list.length
          }
          var data = { id: 0, label: '阿里巴巴集团', level: 0 }
          if (len > 0) {
            data.children = this.formatData(list, 1, 0)
          }
          this.data = data
        }
      })
    },
    formatData (list, level, pid, pname) {
      const childrenData = []
      for (let i = 0; i < list.length; i++) {
        const data = {}
        const item = list[i]
        if (level === item.level) {
          data.id = item.id
          data.label = item.name
          data.parentName = pname
          data.level = item.level
          data.children = this.formatData(list, level + 1, item.id, item.name)
          childrenData.push(data)
        }
      }
      return childrenData
    }
  },
  mounted () {
    this.getDepartmentData()
  }
}
</script>

接口中核心代码:

List<Station> stations = stationService.listByEntity(station,levelList);
        List<StationVo> stationVos = new ArrayList();
        for(Station s : stations) {
            StationVo vo = new StationVo();
            vo.setId(s.getId());
            vo.setLevel(s.getLevel());
            vo.setName(s.getName());
            stationVos.add(vo);
        }

数据库中四个字段

mybatis  配置:

返回的json如下:

{
  "data" : {
    "page" : 1,
    "pageSize" : 20,
    "total" : 6,
    "pages" : 1,
    "list" : [ {
      "id" : 1,
      "name" : "天猫科技服务有限公司",
      "level" : 1,
      "status" : 1,
    }, {
      "id" : 2,
      "name" : "淘宝技术服务有限公司",
      "level" : 1,
      "status" : 1,
    }, {
      "id" : 3,
      "name" : "聚划算科技服务有限公司",
      "level" : 1,
      "status" : 1,
    }, {
      "id" : 4,
      "name" : "菜鸟金服",
      "level" : 2,
      "status" : 1,
    }, {
      "id" : 5,
      "name" : "黑鸟网络",
      "level" : 3,
      "status" : 1,
    }, {
      "id" : 6,
      "name" : "白鸟 网络",
      "level" : 3,
      "status" : 1,
    } ]
  },
  "message" : "获取成功",
  "code" : 200
}

到此这篇关于SpringBoot mybatis 实现多级树形菜单的示例代码的文章就介绍到这了,更多相关SpringBoot mybatis多级树形菜单内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • 详解idea搭建springboot+mybatis框架的教程

    1.打开idea编译器,新建一个项目 2.选择Spring Initializr 勾选Default,完成之后点击[Next] 3.创建项目的文件目录结构以及选择jdk版本信息,设置完成后点击[Next] 4.选择项目所需要的依赖,在Web里面勾选Spring Web 在SQL里勾选需要的依赖,点击[Next] 5.修改项目名称,项目存放路径,点击[finish] 6.创建完成之后需要保持网络良好,因为需要加载刚才所选择的依赖(下载依赖可能需要很长时间,一定要在网络良好的情况下载,并且耐心等待)

  • springboot + vue 实现递归生成多级菜单(实例代码)

    开发过程中,涉及到多级菜单的应用,找了一些资料案例实现如下(使用springboot+layui+oracle): 创建菜单表 : -- 创建菜单表 create table wxmini_menus( menu_id number unique, --菜单ID menu_name varchar2(20), menu_url varchar2(200), menu_icon varchar2(100), parent_id number, --父菜单ID status varchar2(10)

  • springboot+mybatis-plus 两种方式打印sql语句的方法

    1.注解方式,yml文件配置上以下就可以直接使用 mybatis-plus: mapper-locations: classpath:mapper/*.xml configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl 2.这一种网上没有,搜过好多资料都没有,我是配置多数据源,所以是在代码中写的config那么yml文件就是失效的,只能一个一个配置,到了打印sql的时候,就怎么都是找不到,后来设置的源码找到灵感,发现可

  • springboot整合Mybatis-plus的实现

    1.添加pom引用 maven的引用很简单,官方已经给出starter,不需要我们考虑它的依赖关系了,此处使用的是2.3版本. <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>2.3</version> </dependency> 2.配置 serve

  • SpringBoot mybatis 实现多级树形菜单的示例代码

    一.前言 iview-admin中提供了 v-org-tree这么一个vue组件可以实现树形菜单,下面小编来提供一下在element-ui中的使用教程(项目见:https://github.com/lison16/v-org-tree) 小编集成了el-dropdown下拉菜单(鼠标左击显示菜单),和右击自定义菜单,两种方式,效果图如下: 二.使用教程 (1)安装依赖 npm install clipboard npm install v-click-outside-x npm install

  • SpringBoot+MyBatis+AOP实现读写分离的示例代码

    目录 一. MySQL 读写分离 1.1.如何实现 MySQL 的读写分离? 1.2.MySQL 主从复制原理? 1.3.MySQL 主从同步延时问题(精华) 二.SpringBoot+AOP+MyBatis实现MySQL读写分离 2.1.AbstractRoutingDataSource 2.2.如何切换数据源 2.3.如何选择数据源 三 .代码实现 3.0.工程目录结构 3.1.引入Maven依赖 3.2.编写配置文件,配置主从数据源 3.3.Enum类,定义主库从库 3.4.ThreadL

  • javascript实现在下拉列表中显示多级树形菜单的方法

    本文实例讲述了javascript实现在下拉列表中显示多级树形菜单的方法.分享给大家供大家参考.具体如下: 这里演示在下拉列表框中显示分级的菜单,在很多网站都可以看到的效果,很实用,下拉列表框中的选项是利用JS控制输出,如果你有更好的办法不用JS来显示,那最好了,因为像这种菜单用JS来实现,多多少少有点麻烦. 运行效果截图如下: 具体代码如下: <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "

  • Java构建树形菜单的实例代码(支持多级菜单)

    效果图:支持多级菜单. 菜单实体类: public class Menu { // 菜单id private String id; // 菜单名称 private String name; // 父菜单id private String parentId; // 菜单url private String url; // 菜单图标 private String icon; // 菜单顺序 private int order; // 子菜单 private List<Menu> children;

  • springboot整合Mybatis、JPA、Redis的示例代码

    引言 在springboot 项目中,我们是用ORM 框架来操作数据库变的非常方便.下面我们分别整合mysql ,spring data jpa 以及redis .让我们感受下快车道. 我们首先创建一个springboot 项目,创建好之后,我们来一步步的实践. 使用mybatis 引入依赖: <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-

  • springboot+mybatis plus实现树形结构查询

    目录 背景 使用场景 设计思路 递归模型 实现代码 注意事项 总结 背景 实际开发过程中经常需要查询节点树,根据指定节点获取子节点列表,以下记录了获取节点树的操作,以备不时之需. 使用场景 可以用于系统部门组织机构.商品分类.城市关系等带有层级关系的数据结构: 设计思路 递归模型 即根节点.枝干节点.叶子节点,数据模型如下: id code name parent_code 1 10000 电脑 0 2 20000 手机 0 3 10001 联想笔记本 10000 4 10002 惠普笔记本 1

  • 开发Vue树形组件的示例代码

    本文介绍了Vue树形组件的示例代码,分享给大家,具体如下: 使用SemanticUI和vue做一个menubar组件,实现方法大概是这样的: <template> <div class="ui menu"> <template v-for="item in leftItems"> <a " v-if="!item.children" @click="item.click"&g

  • Android 绘制多级树形选择列表实例代码

    一.概述 前段时间有个项目的需要在Android端显示一个复选的多层树形控件,主要展示一个公司的组织架构,类似总部下面有各个部门,部门之下是组和员工等.另外需要加上展开与回收部门详情.关闭部分已开展的布局.勾选等功能. 效果图如下: 二.思路分析 毫无疑问,对于这种数据可能达到几千几万行的列表视图,我们需要选择recyclerview等具有回收item功能的控件,因此Item的状态保持放在Model中而不是View中. 由于原始数据是树形结构的,我们需要先将树形结构转换为列表数据,类似根结点 -

  • SpringBoot整合token实现登录认证的示例代码

    1.pom.xml <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</group

  • AngularJS+Bootstrap3多级导航菜单的实现代码

    将介绍如何用AngularJS构建一个强大的web前端系统.angularjs是由Google团队开发的一款非常优秀web前端框架.在当前如此多的web框架下,AngularJS能脱颖而出,从架构设计上就高人一等,双向数据绑定,依赖注入,指令,MVC,模板.Angular.js创新地把后台技术融入前端开发,扫去jQuery一度的光芒.用angularjs就像写后台代码,更规范,更结构化,更可控. 本文将介绍如何实现多限级导航菜单. 目录 1.静态多级菜单实现 2.动态多级菜单实现 1. 静态多级

随机推荐