赞
踩
- <template>
- <div class='tree-transfer'>
- <div class="left-tree">
- <div class="tree-tit">{{leftTit || '左侧栏'}}</div>
- <div class="list">
- <el-tree
- ref="treeRefL"
- v-if="reLoad"
- :data="leftData"
- show-checkbox
- default-expand-all
- :node-key="nodeKey"
- highlight-current
- :props="defaultProps"
- />
- </div>
- </div>
- <div class="btn-div">
- <el-button :icon="Back" type="primary" :disabled="disabled" @click="toLeft()" />
- <el-button :icon="Right" type="primary" :disabled="disabled" @click="toRight()" />
- </div>
- <div class="right-tree">
- <div class="tree-tit">{{rightTit || '右侧栏'}}</div>
- <div class="list">
- <el-tree
- ref="treeRefR"
- v-if="reLoad"
- :data="rightData"
- show-checkbox
- default-expand-all
- :node-key="nodeKey"
- highlight-current
- :props="defaultProps"
- />
- </div>
- </div>
- </div>
- </template>
- import { ref, nextTick, defineExpose } from 'vue'
- import { Right, Back } from '@element-plus/icons-vue';
- const props = defineProps({
- nodeKey: String,
- fromData: Array,
- toData: Array,
- defaultProps: {},
- leftTit: String,
- rightTit: String,
- disabled: {
- type: Boolean,
- default: false
- }
- })
- //定义emit
- const emit = defineEmits(['checkVal'])
- const treeRefL = ref([])
- const treeRefR = ref([])
- const leftData = ref([])
- const rightData = ref([])
- const reLoad = ref(true)
-
- //右侧数据
- const toData = ref([])
- // 右侧需要移除的数据
- const removeData = ref([])
-
- defineExpose({
- /**
- * 清空数据
- */
- clearData() {
- toData.value = []
- },
- /**
- * 初始化数据
- */
- initData() {
- const originalLeft = JSON.parse(JSON.stringify(props.fromData))
- const originalRight = JSON.parse(JSON.stringify(props.fromData))
- if (props.toData.length > 0) {
- leftData.value = sortData(originalLeft, props.toData, 'left')
- rightData.value = sortData(originalRight, props.toData, 'right')
- }else{
- leftData.value = originalLeft
- rightData.value = []
- }
- }
- })
-
- //方法
- //去右边
- const toRight = () =>{
- // 将勾选中的数据保存到toData中
- const checkNodes = treeRefL.value.getCheckedNodes(false, false)
- const newArr = toData.value.concat(checkNodes)
- const obj = {};
- const peon = newArr.reduce((cur, next) => {
- obj[next[props.nodeKey]] ? "" : obj[next[props.nodeKey]] = true && cur.push(next);
- return cur;
- },[]) //设置cur默认类型为数组,并且初始值为空的数组
- toData.value = peon
- reLoad.value = false
- const originalLeft = JSON.parse(JSON.stringify(props.fromData))
- const originalRight = JSON.parse(JSON.stringify(props.fromData))
- // 抽离出选中数据中的id
- const ids = extractId(toData.value)
- // 重新整理两侧树中数据
- leftData.value = sortData(originalLeft, ids, 'left')
- rightData.value = sortData(originalRight, ids, 'right')
- nextTick(() => {
- reLoad.value = true
- })
- checkVal()
- }
- //去左边
- const toLeft = () =>{
- // 将勾选中的数据保存到toData中
- const checkNodes = treeRefR.value.getCheckedNodes(false, false)
-
- const newArr = removeData.value.concat(checkNodes)
- const obj = {};
- const peon = newArr.reduce((cur, next) => {
- obj[next[props.nodeKey]] ? "" : obj[next[props.nodeKey]] = true && cur.push(next);
- return cur;
- },[]) //设置cur默认类型为数组,并且初始值为空的数组
- const dataNeedRemove = peon
- reLoad.value = false
- const originalLeft = JSON.parse(JSON.stringify(props.fromData))
- const originalRight = JSON.parse(JSON.stringify(props.fromData))
- // 抽离出选中数据中的id
- const idsNeedRemove = extractId(dataNeedRemove)
- // 删除相同id
- const oldData = removeId(toData.value, idsNeedRemove)
- toData.value = oldData
- // 右侧列表需要保留的数据的id
- const ids = extractId(oldData)
- // 重新整理两侧树中数据
- leftData.value = sortData(originalLeft, ids, 'left')
- rightData.value = sortData(originalRight, ids, 'right')
- nextTick(() => {
- reLoad.value = true
- })
- checkVal()
- }
- /**
- * 将tree中的整理进行整理,判断数据是否再tree中显示
- * @param data tree数据
- * @param condition 被选中的数据
- * @param leftRight 整理左侧tree中的数据还是整理右侧tree中的数据
- */
- const sortData = (data: any, condition: Array<string>, leftRight: string) => {
- if(leftRight === 'left'){
- const result = [];
- for (const item of data) {
- // 判断item的id是否在condition中,如果不在,说明不需要删除
- if (!condition.includes(item.id)) {
- // 如果item有children属性,递归调用本函数,传入item的children和condition
- if (item.children) {
- item.children = sortData(item.children, condition, leftRight);
- }
- // 如果item的children为空数组,删除item的children属性
- if (item.children && item.children.length === 0) {
- delete item.children;
- }
- result.push(item);
- }
- }
- return result;
- }else{
- const result = [];
- for (const item of data) {
- // 如果item的id在condition中,说明该数据需要保留
- if (condition.includes(item.id)) {
- result.push(item);
- } else {
- // 否则,判断item是否有children属性
- if (item.children) {
- const subResult = sortData(item.children, condition, leftRight);
- // 如果返回的结果数组不为空,说明有符合条件的子数据
- if (subResult.length > 0) {
- // 将item的children属性更新为返回的结果数组
- item.children = subResult;
- result.push(item);
- }
- }
- }
- }
- return result;
- }
- }
- /**
- * 如果新数组中的id再旧数组中存在则删除原始数组中的id
- * @param oldIds 原始id
- * @param newIds 新id
- */
- const removeId = (data: any, newIds: Array<string>) => {
- const ids = []
- for (const item of data) {
- if(!newIds.includes(item.id)){
- ids.push(item)
- }
- }
- return ids
- }
- /**
- * 将id从备选中的数据取出
- * @param arr tree中被选中的数据
- */
- const extractId = (arr: any) => {
- const newArr = []
- for(const i in arr){
- newArr.push(arr[i].id)
- }
- return newArr
- }
- //返回父组件
- const checkVal = () =>{
- emit('checkVal', toData.value)
- }
- .tree-transfer{
- width: 100%;
- display: flex;
- justify-content: space-between;
- .left-tree,.right-tree{
- flex-grow: 1;
- width: calc((100% - 60px) / 2);
- .tree-tit{
- margin-bottom: 10px;
- }
- .list{
- overflow: auto;
- height: 300px;
- border: 1px solid #ddd;
- border-radius: 4px;
- .item{
- padding: 0 10px;
- font-size: 14px;
- line-height: 26px;
- cursor: pointer;
- &.active{
- background: #b9d7fa;
- }
- }
- .item-checkbox{
- height: 26px;
- padding: 0 10px;
- font-size: 14px;
- line-height: 26px;
- &>.el-checkbox{
- height: 26px;
- }
- }
- }
- }
- .btn-div{
- width: 120px;
- flex-shrink: 0;
- display: flex;
- // flex-direction: column;
- align-items: center;
- justify-content: center;
- }
- .el-checkbox__input.is-disabled .el-checkbox__inner{
- display: none;
- }
- }
- <tree-transfer ref="treeTransfer" :nodeKey="'id'" :fromData="menuList" :toData="ruleForm.menuIds"
- :defaultProps="transferProps" :leftTit="'可选菜单'" :rightTit="'已选菜单'" @checkVal="checkVal"/>
- /**
- * 将选中菜单存入表单
- * @param val 子组件穿梭框返回
- */
- const checkVal = (val: any) => {
- const arr = []
- for(const i in val){
- arr.push(val[i].id)
- }
- ruleForm.menuIds = arr
- }
字段 | 说明 | 类型 | 是否必传 |
---|---|---|---|
nodeKey | 树中项目对应的唯一id值 | string | true |
fromData | 菜单树 ( 必须包含id name ) | array[object] | true |
toData | 已经选中的值 | array[string] | true |
defaultProps | 列表的列宽 | object | true |
leftTit | 左侧菜单名称 | string | false |
rightTit | 右侧菜单名称 | string | false |
disabled | 是否禁用穿梭框的左右按钮 | boolean | false |
字段 | 说明 | 类型 | 是否必传 |
---|---|---|---|
id | 唯一id值 | string | true |
name | id对应展示渲染值 | string | true |
children | 树的子层级 | string | false |
字段 | 说明 | 类型 | 是否必传 |
---|---|---|---|
label | 指定节点标签为节点对象的某个属性值 | string, function(data, node) | true |
children | 指定子树为节点对象的某个属性值 | string | true |
disabled | 指定节点选择框是否禁用为节点对象的某个属性值 | string, function(data, node) | true |
字段 | 说明 | 类型 |
---|---|---|
initData | 初始化穿梭框中的数据 | function |
clearData | 清空穿梭框中的数据 | function |
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。