<template>
  <div class="el-draggable-transfer">
    <transfer-panel
        v-bind="$props"
        ref="leftPanel"
        panelType="left"
        :data="sourceData"
        :title="titles[0] || '列表1'"
        :default-checked="leftDefaultChecked"
        :placeholder="filterPlaceholder || '请输入搜索内容'"
        :virtual='virtual'
        :virtual-item-height='virtualItemHeight'
        @checked-change="onSourceCheckedChange">
      <slot name="classify" slot="classify"></slot>
      <slot name="left-footer"></slot>
    </transfer-panel>
    <div class="el-draggable-transfer__buttons">
      <el-button
          type="primary"
          :class="['el-draggable-transfer__button', hasButtonTexts ? 'is-with-texts' : '']"
          @click.native="addAllToLeft"
          :disabled="hasAllToLeft">
        <i class="el-icon-d-arrow-left"></i>
        <span v-if="buttonTexts[0] !== undefined">{{ buttonTexts[0] }}</span>
      </el-button>
      <el-button
          type="primary"
          :class="['el-draggable-transfer__button', hasButtonTexts ? 'is-with-texts' : '']"
          @click.native="addToLeft"
          :disabled="rightChecked.length === 0">
        <i class="el-icon-arrow-left"></i>
        <span v-if="buttonTexts[0] !== undefined">{{ buttonTexts[0] }}</span>
      </el-button>
      <el-button
          type="primary"
          :class="['el-draggable-transfer__button', hasButtonTexts ? 'is-with-texts' : '']"
          @click.native="addToRight"
          :disabled="leftChecked.length === 0">
        <span v-if="buttonTexts[1] !== undefined">{{ buttonTexts[1] }}</span>
        <i class="el-icon-arrow-right"></i>
      </el-button>
      <el-button
          type="primary"
          :class="['el-draggable-transfer__button', hasButtonTexts ? 'is-with-texts' : '']"
          @click.native="addAllToRight"
          :disabled="hasAllToRight">
        <span v-if="buttonTexts[1] !== undefined">{{ buttonTexts[1] }}</span>
        <i class="el-icon-d-arrow-right"></i>
      </el-button>
    </div>
    <transfer-panel
        v-bind="$props"
        ref="rightPanel"
        panelType="right"
        :data="targetData"
        :title="titles[1] || '列表2'"
        :default-checked="rightDefaultChecked"
        @change="handleChange"
        :placeholder="filterPlaceholder || '请输入搜索内容'"
        :virtual='virtual'
        :virtual-item-height='virtualItemHeight'
        @checked-change="onTargetCheckedChange">
      <slot name="right-footer"></slot>
    </transfer-panel>
  </div>
</template>

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

export default {
  name: 'ElDraggableTransfer',

  mixins: [Emitter, Locale, Migrating],

  components: {
    TransferPanel,
    ElButton
  },

  props: {
    // 数据源
    data: {
      type: Array,
      default() {
        return [];
      }
    },
    // value是否为对象--当为true时left-check-change,right-check-change无效
    isObjectValue: {
      type: Boolean,
      default: false
    },
    // 是否可对右边列表拖动排序
    isSortable: {
      type: Boolean,
      default: false
    },

    // sortablejs参数--不包括事件及方法 参考http://www.sortablejs.com/options.html
    sortableProps: {
      type: Object,
      default() {
        return {
          animation: 150,
          easing: 'cubic-bezier(0.65, 0, 0.35, 1)'
        };
      }
    },

    // 自定义列表标题
    titles: {
      type: Array,
      default() {
        return [];
      }
    },
    // 自定义按钮文案
    buttonTexts: {
      type: Array,
      default() {
        return [];
      }
    },
    // 搜索框占位符
    filterPlaceholder: {
      type: String,
      default: ''
    },
    // 自定义搜索方法
    filterMethod: Function,
    // 初始状态下左侧列表的已勾选项的 key 数组
    leftDefaultChecked: {
      type: Array,
      default() {
        return [];
      }
    },
    // 初始状态下右侧列表的已勾选项的 key 数组
    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'
        };
      }
    },
    // 右侧列表元素的排序策略：若为 original，则保持与数据源相同的顺序；若为 push，则新加入的元素排在最后；若为 unshift，则新加入的元素排在最前
    // 当isObjectValue，isObjectValue为true时，targetOrder='original'无效--且默认push方式
    targetOrder: {
      type: String,
      default: 'original'
    },
    // 显示在右侧框数据的 key 集合
    targetKeys: {
      type: Array,
      default() {
        return [];
      }
    },
    // 是否开启虚拟滚动，默认false
    virtual: {
      type: Boolean,
      default: false
    },
    // 启用虚拟滚动时，每一项的高度
    virtualItemHeight: {
      type: Number,
      default: 30
    }
  },
  data() {
    return {
      leftChecked: [],
      rightChecked: []
    };
  },
  computed: {
    // data 字典
    dataObj() {
      const key = this.props.key;
      return this.data.reduce((o, cur) => (o[cur[key]] = cur) && o, {});
    },

    // value 字典
    currentValueMap() {
      if (!this.isObjectValue) {
        return {};
      }
      const currentValue = this.value.slice();
      const currentValueMap = {};
      const key = this.props.key;
      currentValue.forEach((item) => {
        currentValueMap[item[key]] = item;
      });
      return currentValueMap;
    },

    sourceData() {
      if (this.isObjectValue) {
        return this.data.filter(item => !this.currentValueMap[item[this.props.key]]);
      } else {
        return this.data.filter(item => this.value.indexOf(item[this.props.key]) === -1);
      }
    },

    targetData() {
      if (this.isObjectValue) {
        return this.value;
      } else {
        if (this.targetOrder === 'original' && !this.isSortable) {
          return this.data.filter(item => this.value.indexOf(item[this.props.key]) > -1);
        } else {
          return this.value.reduce((arr, cur) => {
            const val = this.dataObj[cur];
            if (val) {
              arr.push(val);
            }
            return arr;
          }, []);
        }
      }
    },

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

    hasAllToLeft() {
      const disabled = this.props.disabled;
      const moveList = this.targetData.filter((item) => {
        return !item[disabled];
      });
      return moveList.length === 0;
    },

    hasAllToRight() {
      const disabled = this.props.disabled;
      const moveList = this.sourceData.filter((item) => {
        return !item[disabled];
      });
      return moveList.length === 0;
    }
  },
  methods: {
    getMigratingConfig() {
      return {
        props: {
          'footer-format': 'footer-format is renamed to format.'
        }
      };
    },

    onSourceCheckedChange(val, movedKeys) {
      this.leftChecked = val;
      if (movedKeys === undefined) return;
      if (!this.isObjectValue) {
        this.$emit('left-check-change', val, movedKeys);
      }
    },

    onTargetCheckedChange(val, movedKeys) {
      this.rightChecked = val;
      if (movedKeys === undefined) return;
      if (!this.isObjectValue) {
        this.$emit('right-check-change', val, movedKeys);
      }
    },

    addAllToLeft() {
      const disabled = this.props.disabled;
      let currentValue = this.value.slice();
      const key = this.props.key;
      const rightChecked = [];
      if (!this.isObjectValue) {
        this.targetData.forEach(item => {
          const index = currentValue.indexOf(item[key]);
          if (!item[disabled] && index > -1) {
            currentValue.splice(index, 1);
            rightChecked.push(item[key]);
          }
        });
      } else {
        this.targetData.forEach((item) => {
          const index = currentValue.indexOf(item);
          if (!item[disabled]) {
            currentValue.splice(index, 1);
            rightChecked.push(item);
          }
        });
      }
      this.$emit('input', currentValue);
      this.$emit('change', currentValue, 'left', rightChecked);
    },
    addToLeft() {
      let currentValue = this.value.slice();
      const rightChecked = [];
      if (!this.isObjectValue) {
        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);
      } else {
        this.rightChecked.forEach(item => {
          const index = currentValue.indexOf(this.currentValueMap[item]);
          if (index > -1) {
            currentValue.splice(index, 1);
            rightChecked.push(this.currentValueMap[item]);
          }
        });
        this.$emit('input', currentValue);
        this.$emit('change', currentValue, 'left', rightChecked);
      }
    },

    addToRight() {
      let currentValue = this.value.slice();
      const itemsToBeMoved = [];
      const key = this.props.key;
      let leftChecked = [];
      if (!this.isObjectValue) {
        this.data.forEach(item => {
          const itemKey = item[key];
          if (
            this.leftChecked.indexOf(itemKey) > -1 &&
              this.value.indexOf(itemKey) === -1
          ) {
            itemsToBeMoved.push(itemKey);
          }
        });
        leftChecked = this.leftChecked;
      } else {
        this.data.forEach(item => {
          const itemKey = item[key];
          if (
            !this.currentValueMap[itemKey] &&
              this.leftChecked.indexOf(itemKey) > -1 &&
              this.value.indexOf(item) === -1
          ) {
            itemsToBeMoved.push(item);
            leftChecked.push(item);
          }
        });
        // currentValue = currentValue.concat(itemsToBeMoved);
      }
      currentValue = this.targetOrder === 'unshift'
        ? itemsToBeMoved.concat(currentValue)
        : currentValue.concat(itemsToBeMoved);
      this.$emit('input', currentValue);
      this.$emit('change', currentValue, 'right', leftChecked);
    },
    addAllToRight() {
      let currentValue = this.value.slice();
      const itemsToBeMoved = [];
      const key = this.props.key;
      const disabled = this.props.disabled;
      const leftChecked = [];
      if (!this.isObjectValue) {
        this.data.forEach(item => {
          const itemKey = item[key];
          if (
            !item[disabled] &&
              this.value.indexOf(itemKey) === -1
          ) {
            itemsToBeMoved.push(itemKey);
            leftChecked.push(itemKey);
          }
        });
      } else {
        this.data.forEach(item => {
          const itemKey = item[key];
          if (
            !item[disabled] &&
              !this.currentValueMap[itemKey]
          ) {
            itemsToBeMoved.push(item);
            leftChecked.push(item);
          }
        });
        // console.log(currentValue)
      }
      currentValue = this.targetOrder === 'unshift'
        ? itemsToBeMoved.concat(currentValue)
        : currentValue.concat(itemsToBeMoved);
      this.$emit('input', currentValue);
      this.$emit('change', currentValue, 'right', leftChecked);
    },

    handleChange(val) {
      const currentValue = val;
      this.$emit('input', currentValue);
      this.$emit('change', currentValue, 'sort');
    },

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