一、前言
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多级树形菜单内容请搜索自学编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持自学编程网!
- 本文固定链接: https://zxbcw.cn/post/211451/
- 转载请注明:必须在正文中标注并保留原文链接
- QQ群: PHP高手阵营官方总群(344148542)
- QQ群: Yii2.0开发(304864863)