element 自定义处理transfer穿梭框

根据项目需求需要实现可以单选进行选择,并且点击左侧的时候自动添加到右侧中去,代码呈上
在这里插入图片描述
在这里插入图片描述
传入的参数:isradio=true时为单选模式只能从左侧选一个,为false的时候可进行多选

主页面:(可以通过修改属性data和value存储到vuex中做到选完重新刷新浏览器选过的数据也不会重置,可以通过axios初始化数据的时候进行默认数据设置)(data为总数组即左侧区域的数组,value为右侧的数组)

<template>
    <section class="main-content">
        <div class="container infor_wrap">
            <dir-transfer 
                filterable
                :filter-method="filterMethod" 
                filter-placeholder="请输入要选择的企业名称" 
                v-model="value" 
                :data="dataList" 
                :titles="['未选择企业', '已选择企业']"
                :isradio='true'></dir-transfer>
            <el-button-group class="pageBtn">
                <el-button size='small' type="primary" @click="prev" icon="el-icon-caret-left">上一步</el-button>
                <el-button size='small' type="primary" @click="next" >下一步<i class="el-icon-caret-right el-icon--right"></i></el-button>
            </el-button-group>
        </div>
    </section>
</template>

<script>
import DirTransfer from './transfermain.vue'
export default {
    name:'ChooseCompany',
    data() {
        const generateData = _ => {
            const dataList = [];
            const company = ['百度', '阿里巴巴', '腾讯', '滴滴', '美团', '饿了么', '阿里巴巴', '腾讯', '滴滴', '美团', '饿了么', '阿里巴巴', '腾讯', '滴滴', '美团', '饿了么', '阿里巴巴', '腾讯', '滴滴', '美团', '饿了么', '阿里巴巴', '腾讯', '滴滴', '美团', '饿了么', '阿里巴巴', '腾讯', '滴滴', '美团', '饿了么', '阿里巴巴', '腾讯', '滴滴', '美团', '饿了么', '阿里巴巴', '腾讯', '滴滴', '美团', '饿了么', '阿里巴巴', '腾讯', '滴滴', '美团', '饿了么', '阿里巴巴', '腾讯', '滴滴', '美团', '饿了么', '阿里巴巴', '腾讯', '滴滴', '美团', '饿了么', '阿里巴巴', '腾讯', '滴滴', '美团', '饿了么'];
            company.forEach((city, index) => {
                dataList.push({
                    label: city,
                    key: index,
                    company: company[index]
                });
            });
            return dataList;
        };
        return {
            dataList: generateData(),
            value: [1],
            filterMethod(query, item) {
                return item.company.indexOf(query) > -1;
            }
        };
    },
    components:{DirTransfer}  
}
</script>

<style lang="scss" scoped>
.main-content{
    height: calc(100% - 82px);
}
.infor_wrap{
    height: calc(100% - 84px);
    /deep/ .el-transfer{
        height: calc(100% - 100px);
        display: flex;
        justify-content: center;
        align-items: center;
    }
    /deep/ .el-transfer-panel{
        width:320px;
        height: 100%;
    }
    /deep/ .el-transfer-panel__body{
        height: calc(100% - 40px);
    }
    /deep/ .el-transfer-panel__filter{
        margin: 0 15px;
    }
    /deep/ .el-transfer-panel .el-transfer-panel__header{
        margin-bottom: 10px;
    }  
    /deep/ .el-transfer-panel__list.is-filterable{
        margin-top: 10px;
        height: calc(100% - 76px);
    } 
}

</style>


transfermain.vue

<template>
  <div class="el-transfer">
    <transfer-panel
      v-bind="$props"
      ref="leftPanel"
      :filterable="true"
      :data="sourceData"
      :title="titles[0] || t('el.transfer.titles.0')"
      :placeholder="filterPlaceholder || t('el.transfer.filterPlaceholder')"
      :default-checked="leftDefaultChecked"
      @checked-change="onSourceCheckedChange">
      <slot name="left-footer"></slot>
    </transfer-panel>
    <!-- <div class="el-transfer__buttons">
      <el-button
        type="primary"
        :class="['el-transfer__button', hasButtonTexts ? 'is-with-texts' : '']"
        @click.native="allToLeft">
        <span>左全选</span>此处本人更改为自己所需按钮,按钮事件名字也从addToLeft改为allToLeft
      </el-button>
      <el-button
        type="primary"
        :class="['el-transfer__button', hasButtonTexts ? 'is-with-texts' : '']"
        @click.native="allToRight">
        <span>右全选</span>此处本人更改为自己所需按钮,按钮事件名字也从addToRight改为allToRight
      </el-button>
    </div> -->
    <transfer-panel
      v-bind="$props"
      :filterable="false"
      :isRight="true"
      ref="rightPanel"
      :data="targetData"
      :title="titles[1] || t('el.transfer.titles.1')"
      :default-checked="rightDefaultChecked"
      :placeholder="filterPlaceholder || t('el.transfer.filterPlaceholder')"
      @checked-change="onTargetCheckedChange">
      <slot name="right-footer"></slot>
    </transfer-panel>
  </div>
</template>

<script>
  import ElButton from 'element-ui/packages/button';
  import Emitter from 'element-ui/src/mixins/emitter';
  import Locale from 'element-ui/src/mixins/locale';
  import TransferPanel from './transferPanel.vue';
  import Migrating from 'element-ui/src/mixins/migrating';

  export default {
    name: 'ElTransfer',

    mixins: [Emitter, Locale, Migrating],

    components: {
      TransferPanel,
      ElButton
    },

    props: {
      data: {
        type: Array,
        default() {
          return [];
        }
      },
      titles: {
        type: Array,
        default() {
          return [];
        }
      },
      buttonTexts: {
        type: Array,
        default() {
          return [];
        }
      },
      filterPlaceholder: {
        type: String,
        default: ''
      },
      filterMethod: Function,
      leftDefaultChecked: {
        type: Array,
        default() {
          return [];
        }
      },
      rightDefaultChecked: {
        type: Array,
        default() {
          return [];
        }
      },
      renderContent: Function,
      value: {
        type: Array,
        default() {
          return [];
        }
      },
      format: {
        type: Object,
        default() {
          return {};
        }
      },
      filterable: Boolean,
      props: {
        type: Object,
        default() {
          return {
            label: 'label',
            key: 'key',
            disabled: 'disabled'
          };
        }
      },
      targetOrder: {
        type: String,
        default: 'original'
      },
      isradio:{
          type:Boolean,
          default:false
      }
    },

    data() {
      return {
        leftChecked: [],
        rightChecked: [],
        checkedleft:true,
        checkedright:false
      };
    },
    created(){
        this.loadLeftDisabled()//初始化右侧有数据则左侧判断时候需要禁止点击
    },
    computed: {
      dataObj() {
        const key = this.props.key;
        return this.data.reduce((o, cur) => (o[cur[key]] = cur) && o, {});
      },
  
      sourceData() {
        return this.data.filter(item => this.value.indexOf(item[this.props.key]) === -1);
      },

      targetData() {
        return this.targetOrder === 'original'
            ? this.data.filter(item => this.value.indexOf(item[this.props.key]) > -1)
            : this.value.map(key => this.dataObj[key]);

      },

      hasButtonTexts() {
        return this.buttonTexts.length === 2;
      }
    },

    watch: {
        targetData(){//监测右侧data的变化当右侧数据为1的时候触发
            if(this.targetData.length === 1){
                if(this.isradio){//传入的props中isradio为true,单选给左侧数据添加disabled
                    this.sourceData.forEach( (item,index) => {
                        item['disabled']=true
                    })
                }
                
            }else if(this.value.length === 0){//右侧数据为0时,左侧数据中的disabled删除掉,
                if(this.isradio){
                    this.sourceData.forEach( (item,index) => {
                        delete item['disabled']
                    })
                }
            }
        },
        value(val) {
            this.dispatch('ElFormItem', 'el.form.change', val);
        }
    },

    methods: {
        loadLeftDisabled() {
            if(this.value.length === 1){
                if(this.isradio){
                    this.sourceData.forEach( (item,index) => {
                        item['disabled']=true
                    })
                }
            }
        },
      getMigratingConfig() {
        return {
          props: {
            'footer-format': 'footer-format is renamed to format.'
          }
        };
      },

      onSourceCheckedChange(val, movedKeys) {
        this.leftChecked = val;
        if (movedKeys === undefined) return;
        this.$emit('left-check-change', val, movedKeys);
        this.addToRight();//在此处直接把选中项添加到右边框,实现单击操作,而不需通过按钮再次操作
      },

      onTargetCheckedChange(val, movedKeys) {
        this.rightChecked = val;
        if (movedKeys === undefined) return;
        this.$emit('right-check-change', val, movedKeys);
        this.addToLeft();//在此处直接把选中项添加到左边框,实现单击操作,而不需通过按钮再次操作
      },
      allToLeft(){
         this.$refs.leftPanel.handleAllCheckedChange(true);//直接出发全选事件,父组件调用子组件方法需要通过this.$refs.组件ref值.子组件方法名
      },
      addToLeft() {
        let currentValue = this.value.slice();
        this.rightChecked.forEach(item => {
          const index = currentValue.indexOf(item);
          if (index > -1) {
            currentValue.splice(index, 1);
          }
        });
        this.$emit('input', currentValue);
        this.$emit('change', currentValue, 'left', this.rightChecked);
      },
      allToRight(){
         this.$refs.rightPanel.handleAllCheckedChange(true);//直接出发全选事件,父组件调用子组件方法需要通过this.$refs.组件ref值.子组件方法名
      },
      addToRight() {
        let currentValue = this.value.slice();
        const itemsToBeMoved = [];
        const key = this.props.key;
        this.data.forEach(item => {
          const itemKey = item[key];
          if (
            this.leftChecked.indexOf(itemKey) > -1 &&
            this.value.indexOf(itemKey) === -1
          ) {
            itemsToBeMoved.push(itemKey);
          }
        });
        currentValue = this.targetOrder === 'unshift'
          ? itemsToBeMoved.concat(currentValue)
          : currentValue.concat(itemsToBeMoved);
        this.$emit('input', currentValue);
        this.$emit('change', currentValue, 'right', this.leftChecked);
      },

      clearQuery(which) {
        if (which === 'left') {
          this.$refs.leftPanel.query = '';
        } else if (which === 'right') {
          this.$refs.rightPanel.query = '';
        }
      }
    }
  };
</script>

transferPge

<template>
  <div class="el-transfer-panel">
    <p class="el-transfer-panel__header">
      <el-checkbox v-show="false" 
        v-model="allChecked"
        @change="handleAllCheckedChange"
        :indeterminate="isIndeterminate">
        {{ title }}
        <span>{{ checkedSummary }}</span>
      </el-checkbox>
       {{ title }}
    </p>
    
    <div :class="['el-transfer-panel__body', hasFooter ? 'is-with-footer' : '']">
      <el-input
        class="el-transfer-panel__filter"
        v-model="query"
        size="small"
        :placeholder="placeholder"
        @mouseenter.native="inputHover = true"
        @mouseleave.native="inputHover = false"
        v-if="filterable">
        <i slot="prefix"
          :class="['el-input__icon', 'el-icon-' + inputIcon]"
          @click="clearQuery"
        ></i>
      </el-input>
      <el-checkbox-group
        v-model="checked"
        v-show="!hasNoMatch && data.length > 0"
        :class="{ 'is-filterable': filterable }"
        class="el-transfer-panel__list">
        <el-checkbox
          class="el-transfer-panel__item"
          :label="item[keyProp]"
          :disabled="item[disabledProp]"
          :key="item[keyProp]"
          v-for="item in filteredData">
          <option-content :option="item"></option-content>
          <span v-if="isRight" class="el-icon-remove-outline" style="float:right;margin:5px 10px 0 5px;width:20px;line-height:20px;text-align:center;height:20px;border-radius:50%;"></span>
        </el-checkbox><!--此处加了个判断是否为右边框给其加上相应的关闭按钮,isRight通过属性值进行传入-->
      </el-checkbox-group>
      <p
        class="el-transfer-panel__empty"
        v-show="hasNoMatch">{{ t('el.transfer.noMatch') }}</p>
      <p
        class="el-transfer-panel__empty"
        v-show="data.length === 0 && !hasNoMatch">{{ t('el.transfer.noData') }}</p>
    </div>
    <p class="el-transfer-panel__footer" v-if="hasFooter">
      <slot></slot>
    </p>
  </div>
</template>

<script>
  import ElCheckboxGroup from 'element-ui/packages/checkbox-group';
  import ElCheckbox from 'element-ui/packages/checkbox';
  import ElInput from 'element-ui/packages/input';
  import Locale from 'element-ui/src/mixins/locale';

  export default {
    mixins: [Locale],

    name: 'ElTransferPanel',

    componentName: 'ElTransferPanel',

    components: {
      ElCheckboxGroup,
      ElCheckbox,
      ElInput,
      OptionContent: {
        props: {
          option: Object
        },
        render(h) {
          const getParent = vm => {
            if (vm.$options.componentName === 'ElTransferPanel') {
              return vm;
            } else if (vm.$parent) {
              return getParent(vm.$parent);
            } else {
              return vm;
            }
          };
          const panel = getParent(this);
          const transfer = panel.$parent || panel;
          return panel.renderContent
            ? panel.renderContent(h, this.option)
            : transfer.$scopedSlots.default
              ? transfer.$scopedSlots.default({ option: this.option })
              : <span>{ this.option[panel.labelProp] || this.option[panel.keyProp] }</span>;
        }
      }
    },

    props: {
      data: {
        type: Array,
        default() {
          return [];
        }
      },
      renderContent: Function,
      placeholder: String,
      title: String,
      filterable: Boolean,
      isRight:Boolean,
      format: Object,
      filterMethod: Function,
      defaultChecked: Array,
      props: Object
    },

    data() {
      return {
        checked: [],
        allChecked: false,
        query: '',
        inputHover: false,
        checkChangeByUser: true
      };
    },

    watch: {
      checked(val, oldVal) {
        this.updateAllChecked();
        if (this.checkChangeByUser) {
          const movedKeys = val.concat(oldVal)
            .filter(v => val.indexOf(v) === -1 || oldVal.indexOf(v) === -1);
          this.$emit('checked-change', val, movedKeys);
        } else {
          this.$emit('checked-change', val);
          this.checkChangeByUser = true;
        }
      },

      data() {
        const checked = [];
        const filteredDataKeys = this.filteredData.map(item => item[this.keyProp]);
        this.checked.forEach(item => {
          if (filteredDataKeys.indexOf(item) > -1) {
            checked.push(item);
          }
        });
        this.checkChangeByUser = false;
        this.checked = checked;
      },

      checkableData() {
        this.updateAllChecked();
      },

      defaultChecked: {
        immediate: true,
        handler(val, oldVal) {
          if (oldVal && val.length === oldVal.length &&
            val.every(item => oldVal.indexOf(item) > -1)) return;
          const checked = [];
          const checkableDataKeys = this.checkableData.map(item => item[this.keyProp]);
          val.forEach(item => {
            if (checkableDataKeys.indexOf(item) > -1) {
              checked.push(item);
            }
          });
          this.checkChangeByUser = false;
          this.checked = checked;
        }
      }
    },

    computed: {
      filteredData() {
        return this.data.filter(item => {
          if (typeof this.filterMethod === 'function') {
            return this.filterMethod(this.query, item);
          } else {
            const label = item[this.labelProp] || item[this.keyProp].toString();
            return label.toLowerCase().indexOf(this.query.toLowerCase()) > -1;
          }
        });
      },

      checkableData() {
        return this.filteredData.filter(item => !item[this.disabledProp]);
      },

      checkedSummary() {
        const checkedLength = this.checked.length;
        const dataLength = this.data.length;
        const { noChecked, hasChecked } = this.format;
        if (noChecked && hasChecked) {
          return checkedLength > 0
            ? hasChecked.replace(/\${checked}/g, checkedLength).replace(/\${total}/g, dataLength)
            : noChecked.replace(/\${total}/g, dataLength);
        } else {
          return `${ checkedLength }/${ dataLength }`;
        }
      },

      isIndeterminate() {
        const checkedLength = this.checked.length;
        return checkedLength > 0 && checkedLength < this.checkableData.length;
      },

      hasNoMatch() {
        return this.query.length > 0 && this.filteredData.length === 0;
      },

      inputIcon() {
        return this.query.length > 0 && this.inputHover
          ? 'circle-close'
          : 'search';
      },

      labelProp() {
        return this.props.label || 'label';
      },

      keyProp() {
        return this.props.key || 'key';
      },

      disabledProp() {
        return this.props.disabled || 'disabled';
      },

      hasFooter() {
        return !!this.$slots.default;
      }
    },

    methods: {
      updateAllChecked() {
        const checkableDataKeys = this.checkableData.map(item => item[this.keyProp]);
        this.allChecked = checkableDataKeys.length > 0 &&
          checkableDataKeys.every(item => this.checked.indexOf(item) > -1);
      },

      handleAllCheckedChange(value) {
        console.log("value",value);
        this.checked = value
          ? this.checkableData.map(item => item[this.keyProp])
          : [];
      },

      clearQuery() {
        if (this.inputIcon === 'circle-close') {
          this.query = '';
        }
      }
    }
  };
</script>

参考文章:https://blog.csdn.net/programarqin/article/details/81042546

发布了29 篇原创文章 · 获赞 22 · 访问量 3万+

猜你喜欢

转载自blog.csdn.net/weixin_41628411/article/details/93473894