<template>
  <div class="el-transfer-ztree el-transfer">
    <transfer-panel
      v-bind="$props"
      ref="leftPanel"
      :nodes="sourceData"
      :title="titles[0] || t('el.transfer.titles.0')"
      :default-checked="leftDefaultChecked"
      :placeholder="filterPlaceholder || t('el.transfer.filterPlaceholder')"
      @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="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-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>
    </div>
    <transfer-panel
      v-bind="$props"
      ref="rightPanel"
      :nodes="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 'vue-element-plus/packages/button';
  import Emitter from 'vue-element-plus/src/mixins/emitter';
  import Locale from 'vue-element-plus/src/mixins/locale';
  import TransferPanel from './transfer-panel.vue';
  import Migrating from 'vue-element-plus/src/mixins/migrating';

  export default {
    name: 'ElTransferZtree',

    mixins: [Emitter, Locale, Migrating],

    components: {
      TransferPanel,
      ElButton
    },

    props: {
      setting: {
        type: Object,
        default: function() {
          return {};
        }
      },
      nodes: {
        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'
      }
    },

    data() {
      return {
        leftChecked: [],
        rightChecked: [],
        datas: []
      };
    },

    computed: {
      dataObj() {
        const key = this.props.key;
        return this.nodes.reduce((o, cur) => (o[cur[key]] = cur) && o, {});
      },
  
      sourceData() {
        const data = JSON.parse(JSON.stringify(this.datas));
        const filterData = (list) => {
          return list.filter(item => {
            if (item.children && item.children.length > 0) {
              var children = filterData(item.children);
              if (children === [] || children.length <= 0) {
                return false;
              }
              item.children = children;
            }
            return this.value.indexOf(item[this.props.key]) === -1;
          });
        };
        var treeData = filterData(data);
        return treeData;
      },

      targetData() {
        const data = JSON.parse(JSON.stringify(this.datas));
        const filterData = (list) => {
          return list.filter(item => {
            if (item.children && item.children.length > 0) {
              var children = filterData(item.children);
              if (children === [] || children.length <= 0) {
                return false;
              }
              item.children = children;
              return true;
            }
            return this.value.indexOf(item[this.props.key]) >= 0;
          });
        };
        var treeData = filterData(data);
        return treeData;
      },

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

    watch: {
      value(val) {
        this.dispatch('ElFormItem', 'el.form.change', val);
      },
      nodes: {
        handler: function(val) {
          this.datas = this.toTree(val, this.props.key, 'pid', 'children');
        },
        deep: true,
        immediate: true
      }
    },

    methods: {
      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);
      },

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

      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);
      },

      addToRight() {
        let currentValue = this.value.slice();
        const itemsToBeMoved = [];
        const key = this.props.key;
        this.nodes.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 = '';
        }
      },
      toTree(list, id, parentId, children) {
        const cloneData = JSON.parse(JSON.stringify(list));
        return cloneData.filter(father => {
          const branchArr = cloneData.filter(child => father[id] === child[parentId]);
          branchArr.length > 0 ? father[children] = branchArr : '';
          return father[parentId] === 0 || father[parentId] === undefined;
        });
      },
      formatTree(tree, children) {
        var queen = [];
        var out = [];
        queen = queen.concat(tree);
        while (queen.length) {
          var first = queen.shift();
          if (first[children]) {
            queen = queen.concat(first[children]);
            delete first[children];
          }
  
          out.push(first);
        }
        return out;
      },
      deepCopy(target) {
        // 判断拷贝的数据类型
        let result;
        let targetType = this._checkedType(target);
        if (targetType === 'Object') {
          result = {};
        } else if (targetType === 'Array') {
          result = [];
        } else {
          return target;
        }
        // 遍历目标数据
        for (let i in target) {
          let value = target[i];
          // 判断目标结构里的每一值是否存在对象/数组
          if (this._checkedType(value) === 'Object' || this._checkedType(value) === 'Array') {
            result[i] = this.deepCopy(value);
          } else {
            result[i] = value;
          }
        }
        return result;
      },
      // 定义检测数据类型的功能函数
      _checkedType(target) {
        return Object.prototype.toString.call(target).slice(8, -1);
      }
    }
  };
</script>
