antd中在vue项目中自定义穿梭框

antd中在vue项目中自定义穿梭框

1、完整代码

<template>
  <a-modal
    title="高危因素选择"
    :width="1000"
    :visible="riskVisible"
    :confirm-loading="confirmLoading"
    @ok="handleOk"
    @cancel="handleCancel"
    okText="确认"
  >
    <a-transfer
      class="tree-transfer"
      :data-source="dataSource"
      :target-keys="targetKeys"
      :render="item => item.title"
      :show-select-all="false"
      @change="onChange"
      :titles="['可选择高危因素', '已选中高危因素']"
    >
      <template
        slot="children"
        slot-scope="{ props: { direction, selectedKeys }, on: { itemSelect } }"
      >
        <div v-if="direction === 'left'">
          <div class="nav-list">
            <div  v-for="(item, index) in navList"
                  :key="index"
                  @click="handleNavChange(index)"
                  :class="['nav-item', currentNav === index ? 'current-color' : '']">
              {{item.title}}
            </div>
          </div>
          <div class="left-ipt">
            <a-input placeholder="搜索可选择内容"
                     v-model="leftKeywords"
                     @pressEnter="handleLeftSearch"
                     @change="handleLeftSearch"
                     allowClear>
              <a-icon slot="prefix" type="search"></a-icon>
            </a-input>
            <a-button type="primary" class="search-btn" @click="handleLeftSearch">
              搜索
            </a-button>
          </div>
          <a-tree
            v-if='treeOriginalData.length'
            :showLine="showLine"
            blockNode
            checkable
            checkStrictly
            :tree-data="renderTreeData"
            :defaultExpandedKeys="['1', '2']"
            :checkedKeys="[...selectedKeys, ...targetKeys]"
            @check="
            (_, props) => {
              onLeftChecked(_, props, [...selectedKeys, ...targetKeys], itemSelect);
            }
          "
          >
            <template slot='custom' slot-scope='item'>
              <div v-if="item.flag === '1'"> {{ item.title+'('+item.level+')' }}</div>
              <div v-if="item.flag === '2'">
                <span :style="{width:'20px',height:'10px',background:item.color,padding:'5px 10px',marginRight:'5px'}">{{ item.levelText}}</span>{{ item.title}}
              </div>
            </template>
          </a-tree>
        </div>
        <div v-if="direction === 'right'">
          <div class="left-ipt">
            <a-input placeholder="搜索可选择内容"
                     v-model="rightKeywords"
                     @pressEnter="handleRightSearch"
                     @change="handleRightSearch"
                     allowClear>
              <a-icon slot="prefix" type="search"></a-icon>
            </a-input>
            <a-button type="primary" class="search-btn" @click="handleRightSearch">
              搜索
            </a-button>
          </div>
          <div>
            <div v-for="item in rightData" :key="item.key" class="right-item">
              <a-checkbox @change="(e) => handleRightChange(e, [...selectedKeys], itemSelect, item.key)">
                <span :style="{width:'20px',height:'10px',background:item.color,padding:'1px 10px',marginRight:'5px'}">{{ item.levelText}}</span>{{ item.title}}
              </a-checkbox>
            </div>
          </div>
        </div>
      </template>
    </a-transfer>
  </a-modal>
</template>
<script>
import { getAction, postAction} from '@api/manage';
import { handleChooseArr, handleTreeData, handleOriginalData } from './utils'
export default {
  props: {
    riskVisible: {
      type: Boolean,
      default: false
    },
    aap001: {
      type: String,
      default: ''
    },
  },

  data() {
    return {
      dataSource: [],
      dataSourceCopy: [],
      selectedKeys: [],
      confirmLoading: false,
      targetKeys: [],
      treeOriginalData: [],
      treeOriginalDataCopy: [],
      navList:[
        {
          title: '颜色分类',
        },
        {
          title: '常用分类'
        },
        {
          title: '疾病分类'
        }
      ],
      currentNav: 0,
      showLine: true,
      rightData: [],
      rightSelectKeys: [],
      leftKeywords: '', // 左侧搜索关键字
      rightKeywords: '', // 右侧搜索关键字
      url: {
        saveRiskChoose: '/biz/bizFiveColorConfigTags/saveDoctorTags',
        detailById: '/biz/bizFiveColorConfigTags/getFiveColorTag',
      },
      renderTreeData: [],
      hasMarkTags: []
    };
  },
  watch:{
    riskVisible(bool) {
      if (bool) {
        this.initData();
      } else {
        this.leftKeywords = '';
        this.rightKeywords = '';
      }
    },
  },
  methods: {
    // 获取已经标识的数据
    getMarkerData() {
      getAction(this.url.detailById, { aap001: this.aap001 }).then(res => {
        if (res.success) {
          this.hasMarkTags = res.result;
          this.targetKeys = res.result.map(item => item.configId);
          this.renderTreeData = handleTreeData(this.treeOriginalData, this.targetKeys);
          let resultData = [];
          this.treeOriginalData.forEach(item => {
            if(item.children && item.children != null){
              item.children.forEach(it => {
                if(this.targetKeys.includes(it.key)) {
                  resultData.push(it);
                }
              })
            }
          })
          this.rightData = resultData;
          this.rightDataCopy = resultData;
        } else {
          this.hasMarkTags = [];
        }
      })
    },
    initData () {
      getAction("/biz/bizFiveColorConfig/getTreeData",null).then(res => {
        if (res.success) {
          this.treeOriginalData = res.result;
          this.treeOriginalDataCopy = res.result;
          this.dataSource = handleOriginalData(res.result);
          this.dataSourceCopy = handleOriginalData(res.result);
          this.renderTreeData = handleTreeData(this.treeOriginalData, this.targetKeys);
          this.getMarkerData();
        } else {
          this.treeOriginalData = [];
          this.treeOriginalDataCopy = [];
          this.dataSource = [];
          this.dataSourceCopy = [];
          this.renderTreeData = handleTreeData(this.treeOriginalData, this.targetKeys);
        }
      })
    },
    handleRightChange(event, checkedKeys, itemSelect, key) {
      itemSelect(key, !this.isChecked(checkedKeys, key));
      const { target } = event;
      if(target.checked) {
        this.rightSelectKeys.push(key);
        this.rightSelectKeys = [...new Set(this.rightSelectKeys)];
      }
    },
    isChecked(selectedKeys, eventKey) {
      return selectedKeys.indexOf(eventKey) !== -1;
    },
    // 左右箭头切换
    onChange(targetKeys, direction) {
      if(direction == 'right') {
        this.targetKeys = targetKeys;
        let resultData = [];
        this.treeOriginalData.forEach(item => {
          if(item.children && item.children != null){
            item.children.forEach(it => {
              if(this.targetKeys.includes(it.key)) {
                resultData.push(it);
              }
            })
          }
        })
        this.rightData = resultData;
        console.log(this.rightData, 'this.rightData isis');
        this.rightDataCopy = resultData;
        this.renderTreeData = handleTreeData(this.treeOriginalData, this.targetKeys);
      } else {
        const resKeys = [];
        const resultRightData = [];
        this.rightData.forEach(item => {
          if(!this.rightSelectKeys.includes(item.key)) {
            resKeys.push(item.key);
            resultRightData.push(item);
          }
        })
        this.targetKeys = resKeys;
        this.rightData = resultRightData;
        this.rightDataCopy = resultRightData;
        this.renderTreeData = handleTreeData(this.treeOriginalData, this.targetKeys);
      }
    },
    // 左侧复选框选中
    onLeftChecked(_, e, checkedKeys, itemSelect) {
      const { eventKey } = e.node;
      itemSelect(eventKey, !this.isChecked(checkedKeys, eventKey));
    },
    // 确认
    handleOk() {
      const chooseFactorArr = [];
      this.treeOriginalData.forEach(item => {
        let children = [];
        if (item.children && item.children != null) {
          children = item.children.filter(it => it.disabled);
          if (children.length) {
            chooseFactorArr.push({
              ...item,
              children
            })
          }
        }
      })
      this.confirmLoading = true;
      if (this.aap001 && chooseFactorArr) {
        console.log(chooseFactorArr, 'chooseFactorArr isis');
        let result = [];
        chooseFactorArr.forEach(item => {
          if (item.children && item.children != null) {
            item.children.forEach(it => {
              result.push({
                configId: it.key,
                fiveColorLevel: it.level,
                aap001: this.aap001
              })
            })
          }
        })
        console.log(result, 'result isis');
        postAction(this.url.saveRiskChoose, result).then(res => {
          if (res.success) {
            this.$notification['success']({
              message: "成功",
              description: res.message,
              duration: 4,
            });
          } else {
            this.$notification['error']({
              message: "错误",
              description: res.message,
              duration: 4,
            });
          }
          this.$emit('handleRiskVisible', { bool: false, chooseFactorArr });
        }).finally(() => {
          this.confirmLoading = false;
        })
      } else {
        setTimeout(() => {
          this.$emit('handleRiskVisible', { bool: false, chooseFactorArr });
          this.confirmLoading = false;
        }, 1000);
      }
    },
    // 取消
    handleCancel() {
      this.$emit('handleRiskVisible', { bool: false})
    },
    // nav切换
    handleNavChange(index) {
      this.currentNav = index;
    },
    // 右侧搜索
    handleRightSearch() {
      if (this.rightKeywords) {
        this.rightData = this.rightData.filter(item => item.title.includes(this.rightKeywords));
      } else {
        this.rightData = this.rightDataCopy;
      }
    },
    // 左侧的搜索
    handleLeftSearch() {
      if (this.leftKeywords) {
        let chooseDataSource = handleChooseArr(this.dataSourceCopy, this.leftKeywords);
        let chooseTreeData = handleChooseArr(this.treeOriginalDataCopy, this.leftKeywords);
        if(chooseDataSource.length && chooseTreeData.length) {
          this.dataSource = chooseDataSource;
          this.treeOriginalData = chooseTreeData;
          this.renderTreeData = handleTreeData(this.treeOriginalData, this.targetKeys);
        } else {
          this.dataSource = [];
          this.treeOriginalData = [];
          this.renderTreeData = handleTreeData([], this.targetKeys);
        }
      } else {
        this.dataSource = this.dataSourceCopy;
        this.treeOriginalData = this.treeOriginalDataCopy;
        this.renderTreeData = handleTreeData(this.treeOriginalDataCopy, this.targetKeys);
      }
    },
  },
};
</script>
<style scoped lang="less">

.left-ipt {
  display: flex;
  margin: 15px 0 13px;
}

.search-btn {
  margin-left: 8px;
}

.nav-list {
  display: flex;
  cursor: pointer;
}

.nav-item {
  padding: 0 8px;
  height: 24px;
  line-height: 24px;
  background: #EEEEEE;
  border-radius: 4px;
  margin-right: 16px;
  font-size: 12px;
  font-weight: 400;
  color: #333333;
}

.current-color {
  background: #1677FF;
  color: white;
}

.right-ipt {
  display: flex;
}

/deep/.ant-modal-footer {
  border-top: none;
  padding: 4px 24px 30px;
}

.tips {
  margin-top: 10px;
  font-size: 14px;
  color: #666666;
  line-height: 20px;
}

/deep/.ant-transfer-list-content-item {
  padding: 0 12px;
}

/deep/.ant-transfer-list {
  border: none;
}

/deep/.ant-transfer-list-header {
  border-bottom: none;
  border-radius: 0;
}

/deep/.ant-transfer-list-body {
  border-radius: 4px;
  border: 1px solid #DDDDDD;
}

/deep/.ant-transfer-list-header-selected span:first-child {
  position: relative;
  left: 100px;

  font-size: 12px;
  color: #999999;
}
.folder-icon {
  width: 14px;
  height: 11px;
  margin: 4px 0 6px 0;
}
/deep/.ant-transfer-list-header-title {
  left: 0;
  font-size: 14px;
  font-weight: 500;
  color: #333333;
}

/deep/.ant-input-affix-wrapper {
  color: #999999;
}

/deep/.ant-transfer-list {
  width: 100%;
  overflow: auto;
}

/deep/.ant-tree.ant-tree-block-node li span.ant-tree-checkbox + .ant-tree-node-content-wrapper {
  text-overflow: ellipsis;
  overflow: hidden;
  white-space: nowrap;
}

.right-item {
  margin-bottom: 8px;
}
</style>

2、utils.js文件

function handleChooseArr(arr, keywords) {
  let newArr = [];
  arr.forEach(item => {
    if (item.title.includes(keywords)) {
      newArr.push(item);
    } else {
      if (item.children && item.children != null) {
        const childrenArr = [];
        item.children.forEach(it => {
          if (it.title.includes(keywords)) {
            childrenArr.push(it);
          }
        })
        if(childrenArr.length) {
          newArr.push({
            ...item,
            children: childrenArr
          });
        }
      }
    }
  })
  return newArr;
}

function handleTreeData(data, targetKeys = []) {
  data.forEach(item => {
    item['disabled'] = true;
    if (item.children && item.children != null) {
      item.children.forEach(it => {
        if(targetKeys.includes(it.key)) {
          it.disabled = true;
        } else {
          it.disabled = false
        }
      })
    }
  });
  return data;
};

function handleOriginalData(data) {
  let newArr = [];
  data.forEach(item => {
    newArr.push({title: item.title, key: item.key});
    if(item.children && item.children !== null) {
      item.children.forEach(it => {
        newArr.push({
          title: it.title,
          key: it.key,
        });
      })
    }
  })
  return newArr;
};

export {
  handleChooseArr,
  handleTreeData,
  handleOriginalData
}

3、以上需要注意的点:

dataSource做为穿梭框的数据源需要是一维数组,并且里面的属性title和key都要为string类型,不然就会报错(dataSource类型不正确)

在这里插入图片描述

4、最终实现的效果
在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论