当前位置:   article > 正文

vue3+element plus树穿梭框_vue3 树形穿梭框插件

vue3 树形穿梭框插件

 HTML

  1. <template>
  2. <div class='tree-transfer'>
  3. <div class="left-tree">
  4. <div class="tree-tit">{{leftTit || '左侧栏'}}</div>
  5. <div class="list">
  6. <el-tree
  7. ref="treeRefL"
  8. v-if="reLoad"
  9. :data="leftData"
  10. show-checkbox
  11. default-expand-all
  12. :node-key="nodeKey"
  13. highlight-current
  14. :props="defaultProps"
  15. />
  16. </div>
  17. </div>
  18. <div class="btn-div">
  19. <el-button :icon="Back" type="primary" :disabled="disabled" @click="toLeft()" />
  20. <el-button :icon="Right" type="primary" :disabled="disabled" @click="toRight()" />
  21. </div>
  22. <div class="right-tree">
  23. <div class="tree-tit">{{rightTit || '右侧栏'}}</div>
  24. <div class="list">
  25. <el-tree
  26. ref="treeRefR"
  27. v-if="reLoad"
  28. :data="rightData"
  29. show-checkbox
  30. default-expand-all
  31. :node-key="nodeKey"
  32. highlight-current
  33. :props="defaultProps"
  34. />
  35. </div>
  36. </div>
  37. </div>
  38. </template>

穿梭框控制逻辑

  1. import { ref, nextTick, defineExpose } from 'vue'
  2. import { Right, Back } from '@element-plus/icons-vue';
  3. const props = defineProps({
  4. nodeKey: String,
  5. fromData: Array,
  6. toData: Array,
  7. defaultProps: {},
  8. leftTit: String,
  9. rightTit: String,
  10. disabled: {
  11. type: Boolean,
  12. default: false
  13. }
  14. })
  15. //定义emit
  16. const emit = defineEmits(['checkVal'])
  17. const treeRefL = ref([])
  18. const treeRefR = ref([])
  19. const leftData = ref([])
  20. const rightData = ref([])
  21. const reLoad = ref(true)
  22. //右侧数据
  23. const toData = ref([])
  24. // 右侧需要移除的数据
  25. const removeData = ref([])
  26. defineExpose({
  27. /**
  28. * 清空数据
  29. */
  30. clearData() {
  31. toData.value = []
  32. },
  33. /**
  34. * 初始化数据
  35. */
  36. initData() {
  37. const originalLeft = JSON.parse(JSON.stringify(props.fromData))
  38. const originalRight = JSON.parse(JSON.stringify(props.fromData))
  39. if (props.toData.length > 0) {
  40. leftData.value = sortData(originalLeft, props.toData, 'left')
  41. rightData.value = sortData(originalRight, props.toData, 'right')
  42. }else{
  43. leftData.value = originalLeft
  44. rightData.value = []
  45. }
  46. }
  47. })
  48. //方法
  49. //去右边
  50. const toRight = () =>{
  51. // 将勾选中的数据保存到toData中
  52. const checkNodes = treeRefL.value.getCheckedNodes(false, false)
  53. const newArr = toData.value.concat(checkNodes)
  54. const obj = {};
  55. const peon = newArr.reduce((cur, next) => {
  56. obj[next[props.nodeKey]] ? "" : obj[next[props.nodeKey]] = true && cur.push(next);
  57. return cur;
  58. },[]) //设置cur默认类型为数组,并且初始值为空的数组
  59. toData.value = peon
  60. reLoad.value = false
  61. const originalLeft = JSON.parse(JSON.stringify(props.fromData))
  62. const originalRight = JSON.parse(JSON.stringify(props.fromData))
  63. // 抽离出选中数据中的id
  64. const ids = extractId(toData.value)
  65. // 重新整理两侧树中数据
  66. leftData.value = sortData(originalLeft, ids, 'left')
  67. rightData.value = sortData(originalRight, ids, 'right')
  68. nextTick(() => {
  69. reLoad.value = true
  70. })
  71. checkVal()
  72. }
  73. //去左边
  74. const toLeft = () =>{
  75. // 将勾选中的数据保存到toData中
  76. const checkNodes = treeRefR.value.getCheckedNodes(false, false)
  77. const newArr = removeData.value.concat(checkNodes)
  78. const obj = {};
  79. const peon = newArr.reduce((cur, next) => {
  80. obj[next[props.nodeKey]] ? "" : obj[next[props.nodeKey]] = true && cur.push(next);
  81. return cur;
  82. },[]) //设置cur默认类型为数组,并且初始值为空的数组
  83. const dataNeedRemove = peon
  84. reLoad.value = false
  85. const originalLeft = JSON.parse(JSON.stringify(props.fromData))
  86. const originalRight = JSON.parse(JSON.stringify(props.fromData))
  87. // 抽离出选中数据中的id
  88. const idsNeedRemove = extractId(dataNeedRemove)
  89. // 删除相同id
  90. const oldData = removeId(toData.value, idsNeedRemove)
  91. toData.value = oldData
  92. // 右侧列表需要保留的数据的id
  93. const ids = extractId(oldData)
  94. // 重新整理两侧树中数据
  95. leftData.value = sortData(originalLeft, ids, 'left')
  96. rightData.value = sortData(originalRight, ids, 'right')
  97. nextTick(() => {
  98. reLoad.value = true
  99. })
  100. checkVal()
  101. }
  102. /**
  103. * 将tree中的整理进行整理,判断数据是否再tree中显示
  104. * @param data tree数据
  105. * @param condition 被选中的数据
  106. * @param leftRight 整理左侧tree中的数据还是整理右侧tree中的数据
  107. */
  108. const sortData = (data: any, condition: Array<string>, leftRight: string) => {
  109. if(leftRight === 'left'){
  110. const result = [];
  111. for (const item of data) {
  112. // 判断item的id是否在condition中,如果不在,说明不需要删除
  113. if (!condition.includes(item.id)) {
  114. // 如果item有children属性,递归调用本函数,传入item的children和condition
  115. if (item.children) {
  116. item.children = sortData(item.children, condition, leftRight);
  117. }
  118. // 如果item的children为空数组,删除item的children属性
  119. if (item.children && item.children.length === 0) {
  120. delete item.children;
  121. }
  122. result.push(item);
  123. }
  124. }
  125. return result;
  126. }else{
  127. const result = [];
  128. for (const item of data) {
  129. // 如果item的id在condition中,说明该数据需要保留
  130. if (condition.includes(item.id)) {
  131. result.push(item);
  132. } else {
  133. // 否则,判断item是否有children属性
  134. if (item.children) {
  135. const subResult = sortData(item.children, condition, leftRight);
  136. // 如果返回的结果数组不为空,说明有符合条件的子数据
  137. if (subResult.length > 0) {
  138. // 将item的children属性更新为返回的结果数组
  139. item.children = subResult;
  140. result.push(item);
  141. }
  142. }
  143. }
  144. }
  145. return result;
  146. }
  147. }
  148. /**
  149. * 如果新数组中的id再旧数组中存在则删除原始数组中的id
  150. * @param oldIds 原始id
  151. * @param newIds 新id
  152. */
  153. const removeId = (data: any, newIds: Array<string>) => {
  154. const ids = []
  155. for (const item of data) {
  156. if(!newIds.includes(item.id)){
  157. ids.push(item)
  158. }
  159. }
  160. return ids
  161. }
  162. /**
  163. * 将id从备选中的数据取出
  164. * @param arr tree中被选中的数据
  165. */
  166. const extractId = (arr: any) => {
  167. const newArr = []
  168. for(const i in arr){
  169. newArr.push(arr[i].id)
  170. }
  171. return newArr
  172. }
  173. //返回父组件
  174. const checkVal = () =>{
  175. emit('checkVal', toData.value)
  176. }

CSS

  1. .tree-transfer{
  2. width: 100%;
  3. display: flex;
  4. justify-content: space-between;
  5. .left-tree,.right-tree{
  6. flex-grow: 1;
  7. width: calc((100% - 60px) / 2);
  8. .tree-tit{
  9. margin-bottom: 10px;
  10. }
  11. .list{
  12. overflow: auto;
  13. height: 300px;
  14. border: 1px solid #ddd;
  15. border-radius: 4px;
  16. .item{
  17. padding: 0 10px;
  18. font-size: 14px;
  19. line-height: 26px;
  20. cursor: pointer;
  21. &.active{
  22. background: #b9d7fa;
  23. }
  24. }
  25. .item-checkbox{
  26. height: 26px;
  27. padding: 0 10px;
  28. font-size: 14px;
  29. line-height: 26px;
  30. &>.el-checkbox{
  31. height: 26px;
  32. }
  33. }
  34. }
  35. }
  36. .btn-div{
  37. width: 120px;
  38. flex-shrink: 0;
  39. display: flex;
  40. // flex-direction: column;
  41. align-items: center;
  42. justify-content: center;
  43. }
  44. .el-checkbox__input.is-disabled .el-checkbox__inner{
  45. display: none;
  46. }
  47. }

父组件调用

  1. <tree-transfer ref="treeTransfer" :nodeKey="'id'" :fromData="menuList" :toData="ruleForm.menuIds"
  2. :defaultProps="transferProps" :leftTit="'可选菜单'" :rightTit="'已选菜单'" @checkVal="checkVal"/>
  1. /**
  2. * 将选中菜单存入表单
  3. * @param val 子组件穿梭框返回
  4. */
  5. const checkVal = (val: any) => {
  6. const arr = []
  7. for(const i in val){
  8. arr.push(val[i].id)
  9. }
  10. ruleForm.menuIds = arr
  11. }

穿梭框参数文档

属性

列表

参数说明
字段说明类型是否必传
nodeKey树中项目对应的唯一id值stringtrue
fromData菜单树 ( 必须包含id name )array[object]true
toData已经选中的值array[string]true
defaultProps列表的列宽objecttrue
leftTit左侧菜单名称stringfalse
rightTit右侧菜单名称stringfalse
disabled是否禁用穿梭框的左右按钮booleanfalse
dataLabel 中的 fromData说明
字段说明类型是否必传
id唯一id值stringtrue
nameid对应展示渲染值stringtrue
children树的子层级stringfalse
dataLabel 中的 defaultProps说明
字段说明类型是否必传
label指定节点标签为节点对象的某个属性值string, function(data, node)true
children指定子树为节点对象的某个属性值stringtrue
disabled指定节点选择框是否禁用为节点对象的某个属性值string, function(data, node)true

Expose

字段说明类型
initData初始化穿梭框中的数据function
clearData清空穿梭框中的数据function
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/你好赵伟/article/detail/223927
推荐阅读
相关标签
  

闽ICP备14008679号