<!--
此处填写
@Author: linqian
@Date: 2019/01/01
-->
<template>
  <div class="dg-transfer">
    <div class="dg-tree-transfer">
      <!-- 左侧穿梭框 -->
      <div class="el-transfer-panel">
        <p class="el-transfer-panel__header">
          <el-checkbox
            ref="checkBoxLeft"
            :indeterminate="sourceIsIndeterminate"
            v-model="sourceCheckAll"
            @change="sourceAllBoxCheck"
          >
            <template v-if="titles[0] !== undefined">{{ titles[0] }}</template>
          </el-checkbox>
          <slot name="title-left"></slot>
        </p>
        <!-- 内容区 -->
        <div class="el-transfer-panel__body">
          <div class="el-transfer-panel__list" style="padding: 0">
            <el-input
              v-if="filterable"
              class="el-transfer-panel__filter"
              :placeholder="placeholder"
              v-model="filterFrom"
              size="small"
            >
              <i slot="prefix" :class="['el-input__icon', 'el-icon-search']"></i>
            </el-input>
            <el-tree
              ref="from-tree"
              :class="[filterable ? `is-filterable` : '']"
              :data="sourceData"
              show-checkbox
              :node-key="valueName"
              @check="sourceTreeChecked"
              :default-expanded-keys="sourceExpandedKeys"
              :props="treeProps"
              :filter-node-method="filterNodeFrom"
              :default-expand-all="openAll"
              :render-content="renderContent"
              :default-checked-keys="sourceCheckKeys"
              :check-strictly="checkStrictly"
            ></el-tree>
          </div>
        </div>
        <p class="el-transfer-panel__footer" v-if="$slots['left-footer']">
          <slot name="left-footer"></slot>
        </p>
      </div>

      <!-- 穿梭区 按钮框 -->
      <div class="el-transfer__buttons">
        <el-button
          type="primary"
          :class="['el-transfer__button', hasButtonTexts ? 'is-with-texts' : '']"
          @click.native="addToSource"
          :disabled="targetDisabled"
        >
          <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="addToTarget"
          :disabled="sourceDisabled"
        >
          <span v-if="buttonTexts[1] !== undefined">{{ buttonTexts[1] }}</span>
          <i class="el-icon-arrow-right"></i>
        </el-button>
      </div>

      <!-- 右侧穿梭框 -->
      <div class="el-transfer-panel">
        <p class="el-transfer-panel__header">
          <el-checkbox
            ref="checkBoxRight"
            :indeterminate="targetIsIndeterminate"
            v-model="targetCheckAll"
            @change="targetAllBoxCheck"
          >
            <template v-if="titles[1] !== undefined">{{ titles[1] }}</template>
          </el-checkbox>
          <slot name="title-right"></slot>
        </p>
        <!-- 内容区 -->
        <div class="el-transfer-panel__body">
          <div class="el-transfer-panel__list" style="padding: 0">
            <el-input
              v-if="filterable && accept !== 'list'"
              class="el-transfer-panel__filter"
              :placeholder="placeholder"
              v-model="filterTo"
              size="small"
            >
              <i slot="prefix" :class="['el-input__icon', 'el-icon-search']"></i>
            </el-input>
            <template v-if="accept === 'tree'">
              <el-tree
                :class="[filterable ? `is-filterable` : '']"
                ref="to-tree"
                :data="targetData"
                show-checkbox
                :node-key="valueName"
                @check="toTreeChecked"
                :default-expanded-keys="targetExpandedKeys"
                :props="treeProps"
                :filter-node-method="filterNodeTo"
                :default-expand-all="openAll"
                :render-content="renderContent"
                :default-checked-keys="targetCheckKeys"
                :check-strictly="checkStrictly"
              ></el-tree>
            </template>
            <template v-if="accept === 'list'">
              <el-checkbox-group class="el-transfer-panel__list" v-model="acceptCheckedKeys">
                <el-checkbox
                  class="el-transfer-panel__item"
                  v-for="(item, index) in acceptData"
                  :label="item.id"
                  :disabled="item[disabledName]"
                  :key="index"
                  :title="item[labelName]"
                  >{{ item[labelName] }}</el-checkbox
                >
              </el-checkbox-group>
            </template>
          </div>
        </div>
        <p class="el-transfer-panel__footer" v-if="$slots['right-footer']">
          <slot name="right-footer"></slot>
        </p>
      </div>
    </div>
  </div>
</template>

<script>
import _ from 'lodash';
import { Transfer } from 'element-ui';
import getTreeDataMixin from './mixin';

export default {
  name: 'TransferTree',
  mixins: [getTreeDataMixin],
  props: {
    leftDefaultChecked: {
      type: Array,
      default() {
        return [];
      }
    },
    rightDefaultChecked: {
      type: Array,
      default() {
        return [];
      }
    },
    // 默认选中值
    value: {
      type: Array,
      default() {
        return [];
      }
    },
    // 选项配置数据
    data: {
      type: Array,
      default() {
        return [];
      }
    },
    // 值的字段名称
    valueName: {
      type: String,
      default: 'value'
    },
    // 显示的字段名称
    labelName: {
      type: String,
      default: 'label'
    },
    // 禁用值的名称
    disabledName: {
      type: String,
      default: 'disabled'
    },
    // 子节点数组的名称
    childrenName: {
      type: String,
      default: 'children'
    },
    // 父级节点名称
    pidName: {
      type: String,
      default: 'pid'
    },
    // 标题配置
    titles: {
      type: Array,
      default: () => ['源列表', '目标列表']
    },
    // 按钮文本配置
    buttonTexts: {
      type: Array,
      default: () => []
    },
    // 穿梭后是否展开节点
    transferOpenNode: {
      type: Boolean,
      default: false
    },
    // 筛选 placeholder
    placeholder: {
      type: String,
      default: '输入关键字进行过滤'
    },
    // 是否展开所有节点
    openAll: {
      type: Boolean,
      default: true
    },
    // 自定义树节点
    renderContent: Function,
    // 是否启用筛选
    filterable: {
      type: Boolean,
      default: false
    },
    // 目标数据的展现形式，仅在 `type` 参数值为 `tree` 时生效，可选项为 `list`
    accept: {
      type: String,
      default: 'tree'
    },
    // 是否父子关联
    checkStrictly: {
      type: Boolean,
      default: false
    }
  },
  data() {
    return {
      // props 的字段别名
      treeProps: {
        label: this.labelName,
        children: this.childrenName,
        disabled: this.disabledName
      },
      // 是否半选源列表数据状态
      sourceIsIndeterminate: false,
      // 是否半选源列表数据状态
      sourceCheckAll: false,
      // 是否半选目标列表数据状态
      targetIsIndeterminate: false,
      // 是否半选目标列表数据状态
      targetCheckAll: false,
      // 源数据选中key数组 以此属性关联穿梭按钮，总全选、半选状态
      sourceCheckKeys: [],
      // 目标数据选中key数组 以此属性关联穿梭按钮，总全选、半选状态
      targetCheckKeys: [],
      // 源数据展开节点
      sourceExpandedKeys: [],
      // 目标数据展开节点
      targetExpandedKeys: [],
      // 添加按钮是否禁用
      sourceDisabled: true,
      // 移除按钮是否禁用
      targetDisabled: true,
      // 源数据筛选
      filterFrom: '',
      // 目标数据筛选
      filterTo: '',
      // 目标列表数据勾选
      acceptCheckedKeys: []
    };
  },
  computed: {
    /**
     * 检测按钮文本值
     *
     */
    /**
     * 接收列表数据
     *
     * @return {Array}
     */
    acceptData() {
      // 初始化数据
      let _treeData = _.cloneDeep(this.dataSource);

      // 目标列表数据
      let _selectedList = [];

      // 获取选中值列表选项数据
      this.constructListBox(this.value, _treeData, _selectedList);

      return _selectedList;
    },
    /**
     * 过滤掉禁用的数据
     *
     * @return { Array }
     */
    filterAcceptData() {
      return this.acceptData.filter((item) => !item[this.disabledName]);
    },
    hasButtonTexts() {
      return this.buttonTexts.length === 2;
    }
  },

  watch: {
    // 监听数据
    data: {
      deep: true,
      immediate: true,
      handler(val) {
        this.dataSource = val;
      }
    },
    // 目标列表数据 状态检测
    acceptCheckedKeys(val) {
      // 检测是否存在可选值
      let existValue = [];
      val.forEach((p) => {
        let filterValue = this.filterAcceptData.filter((item) => item[this.valueName] === p);
        if (filterValue.length > 0) {
          existValue.push(p);
        }
      });

      if (existValue.length > 0) {
        // 穿梭按钮是否禁用
        this.targetDisabled = false;
        // 总半选是否开启
        this.targetIsIndeterminate = true;

        // 总全选是否开启 - 根据选中节点中为根节点的数量是否和源数据长度相等
        let allCheck = existValue;
        if (allCheck.length == this.acceptData.length) {
          // 关闭半选 开启全选
          this.targetIsIndeterminate = false;
          this.targetCheckAll = true;
        } else {
          this.targetIsIndeterminate = true;
          this.targetCheckAll = false;
        }
      } else {
        this.targetDisabled = true;
        this.targetIsIndeterminate = false;
        this.targetCheckAll = false;
      }
    },
    // 源数据 状态监测
    sourceCheckKeys(val) {
      // val -> obj, data -> keyArray
      if (val.length > 0) {
        // 穿梭按钮是否禁用
        this.sourceDisabled = false;
        // 总半选是否开启
        this.sourceIsIndeterminate = true;

        // 默认数据个数
        let data = this.allKeyValue(true, this.sourceData);

        if (val.length == data.length) {
          // 关闭半选 开启全选
          this.sourceIsIndeterminate = false;
          this.sourceCheckAll = true;
        } else {
          this.sourceIsIndeterminate = true;
          this.sourceCheckAll = false;
        }
      } else {
        this.sourceDisabled = true;
        this.sourceIsIndeterminate = false;
        this.sourceCheckAll = false;
      }
    },
    // 目标树数据 状态监测
    targetCheckKeys(val) {
      // 穿梭按钮是否禁用
      this.targetDisabled = false;
      // 总半选是否开启
      this.targetIsIndeterminate = false;

      // 列表类型
      if (this.accept === 'list') {
        // 检测监听到真实的值
        let existValue = [];
        val.forEach((p) => {
          let filterValue = this.filterAcceptData.filter((item) => item[this.valueName] === p);
          if (filterValue.length > 0) {
            existValue.push(p);
          }
        });

        // 不选中时， 全部关闭
        if (existValue.length <= 0) {
          this.targetDisabled = true;
          this.targetIsIndeterminate = false;
          this.targetCheckAll = false;
          return;
        }

        if (existValue.length === this.acceptData.length) {
          // 关闭半选 开启全选
          this.targetIsIndeterminate = false;
          this.targetCheckAll = true;
        } else {
          this.targetIsIndeterminate = true;
          this.targetCheckAll = false;
        }
      } else {
        // 不选中时， 全部关闭
        if (val.length > 0) {
          let data = this.allKeyValue(true, this.targetData);

          if (val.length === data.length) {
            // 关闭半选 开启全选
            this.targetIsIndeterminate = false;
            this.targetCheckAll = true;
          } else {
            this.targetIsIndeterminate = true;
            this.targetCheckAll = false;
          }
        } else {
          this.targetDisabled = true;
          this.targetIsIndeterminate = false;
          this.targetCheckAll = false;
        }
      }
    },
    // 左侧 数据筛选
    filterFrom(val) {
      this.$refs['from-tree'].filter(val);
    },
    // 右侧 数据筛选（目前未对目标列表数据做筛选）
    filterTo(val) {
      this.$refs['to-tree'].filter(val);
    }
  },

  components: {
    ElTransfer: Transfer
  },

  methods: {
    /**
     * 加入左侧穿梭框
     *
     */
    addToSource() {
      // 初始化
      let keys = [];

      // 检测接入数据的方式
      if (this.accept === 'list') {
        keys = this.acceptCheckedKeys;
      } else {
        keys = this.$refs['to-tree'].getCheckedKeys(true);
      }
      let _newValue = _.difference(this.value, keys);

      // 触发输入框的改变
      this.$emit('input', _newValue);
      // 触发 change 事件的改变
      this.$emit('change', _newValue, 'left', keys);

      // 处理完毕按钮恢复禁用状态
      this.targetCheckKeys = [];
      this.$refs.checkBoxRight.$el.control.checked = false;

      // 目标数据节点展开
      if (this.transferOpenNode) {
        this.sourceExpandedKeys = keys;
      }

      // 重置过滤值
      if (this.filterFrom) {
        let defaults = this.filterFrom;
        this.filterFrom = '';

        // 延迟执行
        this.$nextTick(() => {
          this.filterFrom = defaults;
        });
      }

      // 清空检索值
      this.filterTo = '';
    },
    /**
     * 加入右侧穿梭框
     *
     */
    addToTarget() {
      let keys = this.$refs['from-tree'].getCheckedKeys(true);
      //  let aa =  this.$refs['from-tree'].getCheckedNodes(false, true);
      let _newValue = _.union(this.value, keys);

      // 触发输入框的改变
      this.$emit('input', _newValue);

      // 触发 change 事件的改变
      this.$emit('change', _newValue, 'right', keys);

      // 处理完毕按钮恢复禁用状态
      this.sourceCheckKeys = [];
      this.$refs.checkBoxLeft.$el.control.checked = false;

      // 清空选中数据
      if (this.accept === 'list') {
        this.acceptCheckedKeys = [];
      }

      // 目标数据节点展开
      if (this.transferOpenNode) {
        this.targetExpandedKeys = keys;
      }

      // 重置过滤值
      if (this.filterTo) {
        let defaults = this.filterTo;
        this.filterTo = '';

        // 延迟执行
        this.$nextTick(() => {
          this.filterTo = defaults;
        });
      }

      // 清空检索值
      this.filterFrom = '';
    },
    /**
     * 源列表数据全选改变状态
     *
     * @param val
     */
    sourceAllBoxCheck(val) {
      if (this.sourceData.length == 0) {
        this.sourceDisabled = true;
        this.sourceIsIndeterminate = false;
        this.sourceCheckAll = false;
        return;
      }

      if (val) {
        let node = this.$refs['from-tree'].store.root;
        let keys = this.getVisibleNodeKeys(node);

        this.sourceCheckKeys = keys;
        this.$refs['from-tree'].setCheckedKeys(keys);
      } else {
        this.$refs['from-tree'].setCheckedNodes([]);
        this.sourceCheckKeys = [];
      }
    },
    /**
     * 目标列表数据全选改变状态
     *
     * @param val
     */
    targetAllBoxCheck(val) {
      // 列表类型接收收据
      if (this.accept === 'list') {
        // 检测是否存在可选列表值
        if (this.filterAcceptData.length == 0) return;

        if (val) {
          // 获取可选列表值的 Key 值数据
          let selectedValue = this.filterAcceptData.map((item) => item[this.valueName]);

          this.targetCheckKeys = selectedValue;
          this.acceptCheckedKeys = selectedValue;
        } else {
          this.targetCheckKeys = [];
          this.acceptCheckedKeys = [];
        }
      } else {
        if (this.targetData.length == 0) {
          this.targetDisabled = true;
          this.targetIsIndeterminate = false;
          this.targetCheckAll = false;
          return;
        }

        if (val) {
          let node = this.$refs['to-tree'].store.root;
          let keys = this.getVisibleNodeKeys(node);

          this.targetCheckKeys = keys;
          this.$refs['to-tree'].setCheckedKeys(keys);
        } else {
          this.$refs['to-tree'].setCheckedNodes([]);
          this.targetCheckKeys = [];
        }
      }
    },
    /**
     * 源列表数据选中事件 - 是否禁用穿梭按钮
     *
     * @param nodeObj
     * @param treeObj
     */
    sourceTreeChecked(nodeObj, treeObj) {
      // 查找父节点
      let pids = treeObj.checkedNodes.map((p) => p[this.pidName]);

      // 过滤后的元素
      let filterKeys = _.difference(treeObj.checkedKeys, pids);
      this.sourceCheckKeys = filterKeys;

      this.$emit('left-check-change', nodeObj, treeObj);
    },
    // 目标树选中事件 - 是否禁用穿梭按钮
    toTreeChecked(nodeObj, treeObj) {
      // 查找父节点
      let pids = treeObj.checkedNodes.map((p) => p[this.pidName]);

      // 过滤后的元素
      let filterKeys = _.difference(treeObj.checkedKeys, pids);
      this.targetCheckKeys = filterKeys;

      this.$emit('right-check-change', nodeObj, treeObj);
    },
    /**
     * 源列表数据 筛选
     *
     */
    filterNodeFrom(value, data) {
      // 支持自定义过滤事件
      if (this.$attrs['filter-node-from']) {
        return this.$attrs['filter-node-from'](value, data);
      }

      if (!value) return true;
      return data[this.treeProps.label].indexOf(value) !== -1;
    },
    /**
     * 目标列表数据 筛选
     *
     * @param value
     * @param data
     */
    filterNodeTo(value, data) {
      // 支持自定义过滤事件
      if (this.$attrs['filter-node-to']) {
        return this.$attrs['filter-node-to'](value, data);
      }

      if (!value) return true;
      return data[this.treeProps.label].indexOf(value) !== -1;
    },

    /**
     * 构建目标列表数据
     *
     * @param value
     * @param data
     * @return {*}
     */
    constructListBox(value, data, selectedList) {
      // 初始化数据
      let self = this;

      // 检测数据数组
      if (data.length === 0 || value.length === 0) return [];

      // 遍历数据结构
      let _data = data.map((base) => {
        // 检测子节点数据是否存在
        if (base[self.childrenName] && base[self.childrenName].length > 0) {
          // 若存在，则进行递归，获取子节点
          base[self.childrenName] = self.constructListBox(value, base[self.childrenName], selectedList);
        }

        return base;
      });

      // 过滤数据
      value.forEach((item) => {
        _data.forEach((base) => {
          if (base[self.valueName] === item) {
            selectedList.push(base);
          }
        });
      });

      return _data;
    },

    /**
     * 获取可视化的子节点的值
     *
     * @node node
     */
    getVisibleNodeKeys(node) {
      // 节点 keys 数组
      let keys = [];

      // 节点的数据
      if (node.childNodes.length > 0) {
        node.childNodes.forEach((node) => {
          let childKeys = this.getVisibleNodeKeys(node);
          keys = _.concat(childKeys, keys);
        });
      }

      if (node.visible && node.isLeaf && !node.disabled) {
        keys.push(node.key);
      }

      return keys;
    }
  },

  created() {
    this.sourceCheckKeys = this.leftDefaultChecked;
    this.targetCheckKeys = this.rightDefaultChecked;
    if (this.accept === 'list') {
      this.acceptCheckedKeys = this.rightDefaultChecked;
    }
  }
};
</script>
