<template> <div class="org-tree-com"> <div class="search-area"> <!-- <el-input v-model="inputVal" clearable placeholder="请输入组织名称"> <el-button slot="append" icon="el-icon-search" @click="handleSubmit" /> </el-input> --> <el-input v-model="inputVal" clearable size="small" class="search-input" placeholder="请输入组织名称" /> <dee-tools :tools="tools" style="width:120px" mode="dual" /> </div> <el-tree :key="treeKey" ref="tree" node-key="id" :data="treeData" :expand-on-click-node="false" :highlight-current="true" :default-expanded-keys="defaultExpandedKeys" draggable :allow-drop="allowDrop" @node-drag-end="handleDragEnd" @node-click="nodeClick" @node-expand="highLightTreeNode" > <span slot-scope="{ node, data }" class="custom-tree-node"> <div> <img v-if="data.independentManageFlag" class="independentManageFlag" src="/icons/dulizuzhi.png" title="独立组织"> <span class="change-text">{{ data.name }}</span> </div> <span v-if="!ternaryUserInfo.isSecAdmin" class="tree-btn-box"> <span class="btn-item" title="新增" @click="() => append(data, node)"> <img src="/icons/add-L.png" alt=""> </span> <span class="btn-item" title="编辑"> <img src="/icons/edit-L.png" alt="" @click="() => editFun(node, data)"> </span> <span v-if="node.level!==1" class="btn-item" title="删除" @click="() => remove(node, data)"> <img src="/icons/delete-L.png" alt=""> </span> </span> </span> </el-tree> <dee-dialog v-if="dialogVisible" width="580px" :title="title" :dialog-visible="dialogVisible" @handleClose="handleClose" > <dee-as-com :lay-config="{ typeName: 'DxOrganization', layKey: basicData && basicData.id ? 'defaultEdit' : 'defaultCreate' }" :basic-data="basicData" :form="bindForm" parent-show-mode="dialog" @completeEven="completeEven" @on-cancel="handleClose" /> </dee-dialog> </div> </template> <script> import { getOrgTree, sortOrgTree } from '@/api/userSystem/user' export default { name: 'OrgTreeForUser', componentName: '组织树(用户管理)', components: { }, props: { }, data() { return { defaultExpandedKeys: [], treeKey: 0, treeData: [], inputVal: '', title: '', dialogVisible: false, basicData: null, curNode: null, bindForm: {}, evenList: [ { even: 'nodeClick', name: '节点被点击时的回调' } ], filterTool: { keyword: '', currentIndex: -1, nodes: [] }, expandAll: false, // 是否已被展开全BOM(数据,非界面) tools: [ { name: '下一个', icon: '/icons/c-next.png', handler: { click: () => { this.findNext() } } }, { name: '上一个', icon: '/icons/c-last.png', handler: { click: () => { this.findPrev() } } }, { name: '全部展开', icon: '/icons/b-expansion.png', handler: { click: () => { this.expandAllFun(true) } } }, { name: '全部收起', icon: '/icons/b-allpackup.png', handler: { click: () => { this.expandAllFun(false) } } }, { name: '刷新', icon: '/icons/b-fresh.png', handler: { click: () => { this.loadNode() } } }] } }, computed: { // 三元用户信息 ternaryUserInfo() { return this.$store.getters.ternaryUserInfo } }, watch: { inputVal: { handler: function(keyword) { this.filterTool.currentIndex = -1 this.highLightTreeNode() } } }, mounted() { this.loadNode() this.$bus.$on('addOrgComplete', () => { this.loadNode() }) }, methods: { handleDragEnd(draggingNode, dropNode, dropType, ev) { sortOrgTree({ sourceOrgId: draggingNode.data.id, targetOrgId: dropNode.data.id }).then(res => { this.$message.success('调整顺序成功') }) }, allowDrop(draggingNode, dropNode, type) { if (dropNode.data.parentId === draggingNode.data.parentId) { return type !== 'inner' } }, loadNode(data) { const serverParams = { 'searchItems': { 'children': [], 'items': [], 'operator': 'AND' }, 'sortItem': [ { 'fieldName': 'orgSort', 'sortOrder': 'asc' }, { 'fieldName': 'num', 'sortOrder': 'asc' } ], 'toValidateKeys': '' } const userLevel = JSON.parse(localStorage.getItem('userLevel')) ? JSON.parse(localStorage.getItem('userLevel')).level : '' if (userLevel === '1') { serverParams.searchItems = { items: [ { fieldName: 'parentId', operator: 'EQ', value: 0 } ] } } else { serverParams.searchItems = { items: [] } } getOrgTree(serverParams).then(res => { this.treeData = res.items.content || [] if (this.treeData.length) { this.defaultExpandedKeys = [this.treeData[0].id] if (data) { this.$refs.tree.setCurrentKey(data.id) this.$emit('nodeClick', data) } } // resolve(res.items.content || []) // if (node.level === 0) { // this.$nextTick(() => { // if (res.items.content && res.items.content.length) { // this.treeData = res.items.content // this.$refs.tree.setCurrentKey(res.items.content[0].id) // // 根据el-tree的getNode方法获取到val相应的Node // this.$emit('nodeClick', res.items.content[0]) // } // }) // } }) }, append(data, node) { this.title = '新增组织' this.curNode = node this.bindForm = { parentId: data.id } this.basicData = null this.dialogVisible = true }, editFun(node, data) { this.title = '编辑组织' this.curNode = node this.basicData = data this.dialogVisible = true }, remove(node, data) { this.$confirm('是否确认删除该组织?', '提示', { confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning' }).then(() => { this.$api.remove('DxOrganization', data.id).then(res => { this.$utils.showMessageSuccess('移除成功') const parent = node.parent const children = parent.data.children || parent.data const index = children.findIndex(d => d.id === data.id) children.splice(index, 1) // node.parent.loaded = false // node.parent.expand() }) }).catch((e) => { console.log(e) this.$message({ type: 'info', message: '已取消删除' }) }) }, nodeClick(data) { this.$emit('nodeClick', data) }, handleSubmit() { this.$emit('handleSearchOrg', this.keywords) }, completeEven(data) { if (data.buttonInformation.type === 'submit') { if (this.basicData && this.basicData.id) { this.loadNode(this.basicData) } else { if (!this.curNode.data.children) { this.$set(this.curNode.data, 'children', []) } this.curNode.data.children.push(data.res.items) } } this.handleClose() }, handleClose() { this.bindForm = {} this.dialogVisible = false }, expandAllFun(bool) { var nodes = this.$refs.tree.store._getAllNodes() this.expandAll = bool this.setNodeExpanded(nodes, bool) this.highLightTreeNode() }, setNodeExpanded(nodes, bool) { nodes.forEach(Node => { if (bool) { Node.expand() } else { Node.collapse() } }) }, // 高亮匹配的文字 highLightTreeNode() { this.$nextTick(() => { const treeNode = this.$refs.tree.$el const spans = treeNode.getElementsByClassName('change-text') if (spans.length > 0) { for (let i = 0; i < spans.length; i++) { var labelText = spans[i].textContent const reg = this.inputVal .replace(/\(/g, '\\(') .replace(/\)/g, '\\)') .replace(/\./g, '\\.') const allVal = labelText.match(new RegExp(reg, 'ig')) if (allVal) { const newAllVal = [...new Set(allVal)] for (let j = 0; j < newAllVal.length; j++) { if (newAllVal[j]) { const tp = newAllVal[j] .replace(/\(/g, '\\(') .replace(/\)/g, '\\)') .replace(/\./g, '\\.') labelText = labelText.replace( new RegExp(tp, 'g'), '*' + newAllVal[j] + '*' ) } } for (let k = 0; k < allVal.length; k++) { if (allVal[k]) { labelText = labelText.replace( '*' + allVal[k] + '*', '<span class="highlight-text">' + allVal[k] + '</span>' ) } } } spans[i].innerHTML = labelText } } }) }, // 展开匹配的节点 expandToMatchNode() { const node = this.filterTool.nodes[this.filterTool.currentIndex] let currentNode = node.parent while (currentNode) { currentNode.expanded = true currentNode = currentNode.parent } this.$refs.tree.setCurrentKey(node.key) this.highLightTreeNode() // 保持滚动条内容可见 this.$nextTick(() => { const treeEl = document.getElementsByClassName('el-tree')[0] const el = this.$refs.tree.$el.getElementsByClassName('is-current')[0] let parentNode = el.parentNode let offsetParent = el.offsetParent let offsetTop = el.offsetTop || 0 while (parentNode !== treeEl) { if (offsetParent !== parentNode.offsetParent) { offsetTop += parentNode.offsetTop || 0 } else { offsetParent = parentNode.offsetParent } parentNode = parentNode.parentNode } treeEl.scrollTop = offsetTop - treeEl.clientHeight * 0.5 + 10 }) }, // 查询下一个 findNext() { if (!this.inputVal) { return } this.findAll().then(() => { if (this.filterTool.nodes.length < 1) { return } if (++this.filterTool.currentIndex >= this.filterTool.nodes.length) { this.filterTool.currentIndex = 0 } this.expandToMatchNode() }) }, // 查询上一个 findPrev() { if (!this.inputVal) { return } this.findAll().then(() => { if (this.filterTool.nodes.length < 1) { return } if (--this.filterTool.currentIndex < 0) { this.filterTool.currentIndex = this.filterTool.nodes.length - 1 } this.expandToMatchNode() }) }, // 查找所有满足条件的节点,先从服务端装载所有BOM结构 findAll() { return new Promise((resolve) => { if (!this.expandAll) { // this.expandAllFun(true).then(() => { this.findMatchNodes() resolve() // }) } else { this.findMatchNodes() resolve() } }) }, // 查找节点 findMatchNodes() { let nodeMap = this.$refs.tree.store.nodesMap nodeMap = Object.values(nodeMap) this.filterTool.nodes = nodeMap.filter(node => node.data.name.toUpperCase().includes(this.inputVal.toUpperCase())) }, reloadTreeData() { const nodes = this.$refs.tree.store._getAllNodes() nodes[0].loaded = false nodes[0].expand() } } } </script> <style lang="scss" > .org-tree-com{ height: 100%; .search-area { //border-bottom: 1px solid #DDDDDD; display: flex; align-items: center; justify-content:flex-start; padding: 6px 0px; .search-input{ // width: calc(100% - 100px); flex:1; margin-right:10px; } } .el-tree{ height: calc(100% - 50px); overflow-y: scroll; } .independentManageFlag{ margin-right:6px; } .custom-tree-node { display: flex; align-items: center; justify-content: space-between; font-size: 14px; padding-right: 8px; flex: 1; &:hover { .tree-btn-box { display: block; } } } .tree-btn-box { // width: 66px; display: flex; justify-content: space-between; align-items: center; height: 100%; display: none; .btn-item img { padding: 4px; width: 14px; height: 14px; } } .highlight-text{ background-color: #FFFF00; } .is-current{ &> div> .bom-tree-node >.change-text > .highlight-text{ background-color: #FF9632; } } } </style>