• Element - el-tree 树形结构拖拽以及增删改查


    1、SystemTreeItem.vue

    1. :data="treeData"
    2. node-key="id"
    3. default-expand-all
    4. :expand-on-click-node="false"
    5. @node-drop="handleDrop"
    6. @node-drag-enter="nodeDragEnter"
    7. draggable
    8. :allow-drop="allowDrop"
    9. :allow-drag="allowDrag"
    10. >
    11. <span class="custom-tree-node" :class="{isDisabled: node.data.enabled === false}" slot-scope="{ node, data }" :key="data.id">
    12. <template v-if="!node.data.isEdit">
    13. <el-tooltip class="item" effect="light" :content="node.data.knowledgeName" :open-delay="1000" placement="bottom">
    14. <span class="label" v-if="node.data.enabled" @dblclick="nodeClick(data,node,true)">{{ node.data.knowledgeName }}span>
    15. <span class="label isDisabled" v-else-if="node.data.id && !node.data.enabled" @dblclick="nodeClick(data,node,true)">{{ "(已禁用)" + node.data.knowledgeName }}span>
    16. el-tooltip>
    17. template>
    18. <span class="knowledgeCode" v-else-if="node.data.isEdit && hasAuth('knowledge_points_system_edit')">
    19. <el-input v-model.trim="knowledgeCode" placeholder="请输入知识点code" v-focus />
    20. <el-button size="mini" type="primary" :disabled="!knowledgeCode" @click="addKnoeledgeCode(node,data)">确 定el-button>
    21. <el-button size="mini" @click="nodeClick(data,node,false)">取 消el-button>
    22. span>
    23. <span class="operation" v-show="!node.data.isEdit">
    24. <el-button type="text" class="btn" :disabled="!canStatusChange || !node.data.id" @click="insertAfter(data, node)" style="color: #333">
    25. 添加同级
    26. el-button>
    27. <span>|span>
    28. <el-button type="text" class="btn" :disabled="!canStatusChange || !node.data.id" @click="append(data, node)" style="color: #333">
    29. 添加下级
    30. el-button>
    31. <span>|span>
    32. <el-button type="text" class="btn" :disabled="!node.data.id" @click="toDetail(data)" style="color: #333">
    33. 查看详情
    34. el-button>
    35. <span>|span>
    36. <el-button type="text" class="btn" :disabled="!node.data.id" @click="remove(node, data)" style="color: #333">
    37. 删除
    38. el-button>
    39. span>
    40. span>
    41. <script>
    42. import { debounce } from '@/utils/comUtil'
    43. export default {
    44. name: 'systemTreeItem',
    45. data() {
    46. return {
    47. knowledgeCode: '',
    48. }
    49. },
    50. props: {
    51. treeData: Array,
    52. canStatusChange: Boolean
    53. },
    54. directives: {
    55. focus: {
    56. inserted: function(el) {
    57. el.querySelector("input").focus();
    58. }
    59. }
    60. },
    61. methods: {
    62. addKnoeledgeCode(node,data){
    63. this.$emit('addKnoeledgeCode',node,data,this.knowledgeCode)
    64. this.knowledgeCode = ""
    65. },
    66. // 拖拽成功完成时触发的事件
    67. handleDrop(draggingNode, dropNode, dropType, e) {
    68. this.$emit('handleDrop', draggingNode, dropNode, dropType, e)
    69. },
    70. // 获取总层级数
    71. getTotalLevel(node, arr) {
    72. if (node.childNodes && node.childNodes.length) {
    73. node.childNodes.forEach(item => {
    74. arr.push(item.level)
    75. if(item.childNodes && node.childNodes.length) {
    76. this.getTotalLevel(item, arr)
    77. }
    78. })
    79. }else{
    80. arr.push(node.level)
    81. }
    82. },
    83. // 拖拽进入其他节点时触发的事件
    84. nodeDragEnter(draggingNode, dropNode){
    85. // console.log("拖拽进入其他节点时触发的事件",draggingNode, dropNode)
    86. let arr = []
    87. this.getTotalLevel(draggingNode, arr)
    88. const totalLevel = Math.max(...arr) - draggingNode.level + 1 + dropNode.level
    89. if(totalLevel <= 8) return
    90. this.banMessag();
    91. arr = []
    92. },
    93. banMessag: debounce(function(){
    94. this.$message({
    95. type: 'warning',
    96. message: '节点层级已达最大值!',
    97. })
    98. },500),
    99. // 拖拽时判定目标节点能否被放置
    100. allowDrop(draggingNode, dropNode, type) {
    101. //draggingNode 被拖拽的节点
    102. //dropNode 目标节点
    103. //type 参数有三种情况:'prev'、'inner' 和 'next',分别表示放置在目标节点前、插入至目标节点和放置在目标节点后
    104. // console.log(draggingNode.level, dropNode.level, type)
    105. // 获取被拖拽的节点层级
    106. let arr = []
    107. this.getTotalLevel(draggingNode, arr)
    108. const totalLevel = Math.max(...arr) - draggingNode.level + 1 + dropNode.level
    109. // 插入至目标节点内部 节点层级大于8级 不能被放置
    110. if(totalLevel > 8){
    111. this.banMessag();
    112. arr = []
    113. return false
    114. }else{
    115. return true;
    116. }
    117. },
    118. // 判断节点能否被拖拽
    119. allowDrag(draggingNode) {
    120. // console.log("allowDrag", draggingNode)
    121. // 节点处于编辑状态 或者 节点为空节点 不能拖拽
    122. if(draggingNode.data.isEdit || !draggingNode.data.id){
    123. return false
    124. }else{
    125. return true
    126. }
    127. },
    128. // 节点双击事件 节点变为可编辑状态
    129. nodeClick(data, node, isEdit) {
    130. if(isEdit === false){
    131. this.knowledgeCode = ""
    132. }
    133. this.$emit('nodeClick', data, node, isEdit)
    134. },
    135. // 添加同级
    136. insertAfter(data, refNode) {
    137. this.$emit('insertAfter', data, refNode)
    138. },
    139. // 添加下级
    140. append(data, parentNode) {
    141. this.$emit('append', data, parentNode)
    142. },
    143. toDetail(data) {
    144. this.$emit('toDetail', data)
    145. },
    146. remove(node, data) {
    147. this.$emit('remove', node, data)
    148. },
    149. },
    150. }
    151. script>

    2、SystemTree.vue

    拖拽涉及代码是:handleDrop

    1. :treeData="systemTreeList"
    2. :canStatusChange="canStatusChange"
    3. @handleDrop="handleDrop"
    4. @nodeClick="nodeClick"
    5. @insertAfter="insertAfter"
    6. @append="append"
    7. @toDetail="toDetail"
    8. @remove="remove"
    9. @addKnoeledgeCode="addKnoeledgeCode"
    10. />
    11. <script>
    12. import systemTreeService from '@/api/systemTreeService'
    13. import SystemTreeItem from './components/SystemTreeItem.vue'
    14. export default {
    15. name: 'SystemTree',
    16. components: { SystemTreeItem },
    17. data() {
    18. return {
    19. systemTreeList: [], // 体系树列表
    20. list: [], // 备份体系树列表数据
    21. canStatusChange: false //禁用/启用是否可切换 体系树无数据,节点处于编辑状态,节点无id 为false
    22. }
    23. },
    24. created() {
    25. this.getSystemTreeList()
    26. },
    27. methods: {
    28. // 体系树数据排序
    29. sortTreeData(arr){
    30. if(!arr.length) return
    31. arr.sort((a,b) => a.sort - b.sort)
    32. arr.forEach(item => {
    33. if(item.children){
    34. this.sortTreeData(item.children)
    35. }
    36. })
    37. },
    38. // 获取体系树列表
    39. async getSystemTreeList(type) {
    40. if(!this.systemCode) return
    41. if (type === 'add') {
    42. this.systemTreeList = [
    43. {
    44. id: '',
    45. knowledgeCode: '',
    46. knowledgeName: '',
    47. parentId: 'ROOT',
    48. level: 1,
    49. systemCode: this.systemCode,
    50. isEdit: true,
    51. enabled: true,
    52. children: []
    53. },
    54. ]
    55. } else {
    56. let res = await systemTreeService.getSystemTreeList({ systemCode: this.systemCode })
    57. if (res && res.errorCode === 0) {
    58. this.systemTreeList = res.result // 后端返回的数据为树状结构
    59. this.sortTreeData(this.systemTreeList)
    60. // 备份原数据 拖拽失败后还原
    61. this.list = JSON.parse(JSON.stringify(this.systemTreeList))
    62. if(this.systemTreeList.length){
    63. this.canStatusChange = true
    64. }else{
    65. this.systemTreeList = [
    66. {
    67. id: '',
    68. knowledgeCode: '',
    69. knowledgeName: '',
    70. parentId: 'ROOT',
    71. level: 1,
    72. systemCode: this.systemCode,
    73. isEdit: true,
    74. enabled: true,
    75. children: []
    76. },
    77. ]
    78. this.canStatusChange = false
    79. }
    80. } else {
    81. this.systemTreeList = []
    82. this.canStatusChange = false
    83. }
    84. }
    85. },
    86. // 添加知识点
    87. async addKnoeledgeCode(node, data, knowledgeCode) {
    88. // console.log("添加知识点", node, data)
    89. node.data.isEdit = false
    90. data.isEdit = false
    91. if (knowledgeCode) {
    92. if(data.id){
    93. // 替换
    94. // 如果knowledgeCode等于oldKnowledgeCode
    95. let oldKnowledgeCode = data.knowledgeCode
    96. if(knowledgeCode === oldKnowledgeCode){
    97. this.$message({
    98. type: 'warning',
    99. message: '当前knowledgeCode与旧knowledgeCode相同,不能替换!'
    100. })
    101. return
    102. }
    103. let params = {
    104. systemCode: this.systemCode,
    105. nodeId: data.id,
    106. oldKnowledgeCode,
    107. newKnowledgeCode: knowledgeCode,
    108. }
    109. let res = await systemTreeService.updateKnowledgePoint(params)
    110. if (res && res.errorCode === 0) {
    111. this.$message({
    112. type: 'success',
    113. message: res.errorInfo,
    114. })
    115. }
    116. }else{
    117. // 新增 添加同级 添加下级
    118. // 添加同级 若为一级节点 nodeParentId 为 "", 否则为node.parent.data.id
    119. let nodeParentId = node.level === 1 ? "" : node.parent.data.id
    120. let params = {
    121. systemCode: this.systemCode,
    122. knowledgeCode,
    123. nodeParentId,
    124. }
    125. let res = await systemTreeService.addKnowledgePoint(params)
    126. if (res && res.errorCode === 0) {
    127. this.$message({
    128. type: 'success',
    129. message: res.errorInfo,
    130. })
    131. }
    132. }
    133. this.getSystemTreeList()
    134. }
    135. },
    136. // 拖拽事件
    137. async handleDrop(draggingNode, dropNode, dropType,e) {
    138. //draggingNode 被拖拽的节点
    139. //dropNode 目标节点
    140. //dropType 类型 被拖拽的节点相对于目标节点的位置 inner before after
    141. // console.log('tree drop', draggingNode, dropNode, dropType)
    142. // 难点在于获取受影响的节点,然后遍历,后端根据节点ID修改父节点ID以及sort
    143. let paramData = [];
    144. // 当拖拽类型不为inner,同级排序,寻找目标节点的父ID,获取其对象以及所有的子节点
    145. // 当拖拽类型为inner,说明拖拽节点成为了目标节点的子节点,只需获取目标节点对象
    146. let data = dropType != "inner" ? dropNode.parent.data : dropNode.data;
    147. //目标节点为一级节点,并且拖拽类型不为inner即当前节点将成为与目标节点同一级的节点,也是一级节点
    148. //nodeData=dropNode.parent.data,但是因为目标节点已经是一级节点了,因此nodeData还是目标节点
    149. //目标节点为一级节点,并且拖拽类型为inner即当前节点将成为目标节点的子节点即二级节点
    150. //nodeDate=dropNode.parent.data.children,即为目标节点的父节点的子节点,即目标节点同一层级的节点
    151. //目标节点不是一级节点,并且拖拽类型不为inner即当前节点将成为与目标节点同一层级的节点
    152. //nodeDate=dropNode.parent.data.children,即为目标节点的父节点的子节点,即目标节点同一层级的节点
    153. //目标节点不是一级节点,并且拖拽类型为inner即当前节点将成为目标节的子节点
    154. //nodeDate=dropNode.data.children,即目标节点的子节点
    155. let nodeData = dropNode.level == 1 && dropType != "inner" ? data : data.children;
    156. let nodeParentId = ""
    157. nodeData.forEach((item, i) => {
    158. if(dropType != "inner"){
    159. if(draggingNode.data.parentId === dropNode.data.parentId){
    160. nodeParentId = item.parentId
    161. }else{
    162. nodeParentId = dropNode.data.parentId
    163. }
    164. }else{
    165. nodeParentId = data.id
    166. }
    167. let collection = {
    168. nodeId: item.id,
    169. nodeParentId,
    170. sort: i + 1
    171. };
    172. paramData.push(collection);
    173. });
    174. // console.log(paramData)
    175. let params = {
    176. systemCode: this.systemCode,
    177. nodeSortList: paramData
    178. }
    179. let res = await systemTreeService.dragTreeNode(params)
    180. if(res && res.errorCode === 0){
    181. this.$message({
    182. type: 'success',
    183. message: res.errorInfo,
    184. })
    185. this.getSystemTreeList()
    186. }else{
    187. // 接口报错之后,将数据恢复
    188. this.systemTreeList = this.list
    189. }
    190. },
    191. // 节点双击事件
    192. nodeClick(data, node, isEdit) {
    193. // console.log("nodeClick",data,node,isEdit)
    194. // 当节点处于编辑状态 或者 节点id不存在
    195. if(isEdit || !data.id){
    196. this.canStatusChange = false
    197. }else{
    198. this.canStatusChange = true
    199. }
    200. this.$set(data,'isEdit',isEdit)
    201. this.$set(node.data,'isEdit',isEdit)
    202. },
    203. // 添加同级
    204. insertAfter(data, refNode) {
    205. // console.log("添加同级", data, refNode)
    206. const newNode = {
    207. parentId: data.parentId,
    208. id: '',
    209. knowledgeCode: '',
    210. knowledgeName: '',
    211. level: data.level,
    212. systemCode: this.systemCode,
    213. isEdit: true,
    214. enabled: true,
    215. children: [],
    216. }
    217. this.canStatusChange = false
    218. if(data.parentId === 'ROOT'){
    219. refNode.parent.data.push(newNode)
    220. }else{
    221. refNode.parent.data.children.push(newNode)
    222. }
    223. },
    224. //添加下级
    225. append(data, parentNode) {
    226. // console.log('添加下级', data)
    227. if (data.level >= 8) {
    228. this.$message({
    229. type: 'warning',
    230. message: '节点层级已达最大值!',
    231. })
    232. return
    233. } else {
    234. const newChild = {
    235. parentId: data.id,
    236. id: '',
    237. knowledgeCode: '',
    238. knowledgeName: '',
    239. level: data.level + 1,
    240. systemCode: this.systemCode,
    241. isEdit: true,
    242. enabled: true,
    243. children: [],
    244. }
    245. this.canStatusChange = false
    246. if (!data.children) {
    247. this.$set(data, 'children', [])
    248. }
    249. data.children.push(newChild)
    250. }
    251. }
    252. }
    253. }
    254. script>
  • 相关阅读:
    LeetCode【41】缺失的第一个正数
    词法分析器
    MYSQL 自动补全工具
    【svn】svn分支(branch)如何同步主干(trunk)的代码?
    基于低代码平台的PLM系统,实现科学业务管理
    rabbitMQ的exchanages类型以及使用场景
    GO语言-切片底层探索(下)
    告警平台设计方案
    如何开家有品味的咖啡馆
    Redis单线程为什么这么快
  • 原文地址:https://blog.csdn.net/hutuyaoniexi/article/details/127664605