<template>
  <div
    v-if="!hide"
    v-show="display"
    class="es-tree"
    v-loading="loading"
    element-loading-text="加载中..."
    element-loading-spinner="el-icon-loading"
    element-loading-background="rgba(0, 0, 0, 0.65)"
  >
    <div v-if="title" class="es-tree-title">{{ title }}</div>
    <div
      class="es-tree-search"
      v-if="showSearch || $slots.filtrate || $slots.button"
    >
      <template v-if="showSearch">
        <es-select
          v-if="search.type && search.type === 'select'"
          class="es-tree-search-item"
          size="medium"
          :label-key="labelKey"
          :value-key="valueKey"
          v-bind="search"
          v-model="searchValue"
          @change="handleChange"
        ></es-select>
        <es-input
          v-else
          v-model="searchValue"
          class="es-tree-search-item"
          size="medium"
          :placeholder="search.placeholder"
        >
          <template slot="append">
            <el-button
              v-if="isRemote"
              icon="el-icon-search"
              type="primary"
              size="medium"
              @click="handleSearch"
            ></el-button>
          </template>
        </es-input>
      </template>
      <div class="es-tree-filtrate" v-if="$slots.filtrate">
        <slot name="filtrate"></slot>
      </div>
      <div class="es-tree-button" v-if="$slots.button">
        <slot name="button"></slot>
      </div>
    </div>
    <div class="es-tree-content">
      <el-scrollbar class="es-scrollbar">
        <el-tree
          ref="oaTree"
          v-bind="{ ...$attrs, ...config }"
          :class="{ 'es-filter-tree': true, 'es-unset': !tips }"
          :empty-text="!loading ? '暂无数据' : ''"
          :node-key="nodeKey"
          :indent="12"
          :lazy="lazy"
          :data="treeData"
          :props="defaultProps"
          :filter-node-method="filterNode"
          :expand-on-click-node="false"
          :highlight-current="true"
          :show-checkbox="showCheckbox"
          :default-checked-keys="defaultCheckeds"
          :current-node-key="currentNodeKey"
          v-on="{
            ...$listeners,
            'node-click': handleNodeClick,
            'check-change': handleCheckChange,
            'node-expand': handleExpand
          }"
        >
          <tree-label
            :node="node"
            :data="data"
            slot-scope="{ node, data }"
            :tips="tips"
            :icon="icon"
          ></tree-label>
        </el-tree>
      </el-scrollbar>
    </div>
  </div>
</template>

<script>
import { getOrgMainTree } from 'eoss-ui/src/config/api.js';
import { debounce } from 'throttle-debounce';
import util from 'eoss-ui/src/utils/util';
export default {
  name: 'EsTree',
  inheritAttrs: false,
  components: {
    treeLabel: {
      functional: true,
      props: {
        node: Object,
        data: Object,
        tips: Boolean,
        icon: Boolean
      },
      render: (h, res) => {
        let label = h(
          'div',
          {
            class: [
              {
                'es-icon-node': res.props.icon,
                'es-tree-parent': !res.props.node.isLeaf,
                'es-icon-open': res.props.node.expanded
              },
              res.props.data.attributes &&
              res.props.data.attributes.isentity === '0'
                ? 'es-icon-group'
                : res.props.data.attributes && res.props.data.attributes.orgType
                ? res.props.data.attributes.orgType
                : res.props.data.iconSkin
                ? 'es-icon-' + res.props.data.iconSkin
                : ''
            ]
          },
          [h('div', { class: 'es-tree-label' }, [res.props.node.label])]
        );
        if (res.props.tips) {
          return h(
            'el-tooltip',
            {
              props: {
                content: res.props.node.label,
                placement: 'right',
                auto: true
              }
            },
            [label]
          );
        }
        return label;
      }
    }
  },
  inject: {
    esPage: {
      default: ''
    }
  },
  props: {
    host: {
      type: String,
      default: ''
    },
    title: {
      type: String,
      default: ''
    },
    display: {
      type: Boolean,
      default: true
    },
    hide: {
      type: Boolean,
      default: false
    },
    // 	选择项本地数据
    data: Array,
    //是否选中首级节点
    currentFirst: Boolean,
    //当前选中的节点
    currentNodeKey: [String, Number],
    icon: {
      type: Boolean,
      default: true
    },
    props: Object,
    // 选择项远程地址
    url: {
      type: String,
      default: getOrgMainTree
    },
    system: {
      type: Boolean,
      default: false
    },
    tips: {
      type: Boolean,
      default: true
    },
    labelKey: String,
    valueKey: String,
    search: {
      type: Object,
      default() {
        return {
          // text (文本框),select (下拉选择)
          type: 'text',
          // 搜索关键字，请求时作为参数名
          name: 'querykeywords',
          value: '',
          // 下拉选择框的选项数据
          data: '',
          placeholder: ''
        };
      }
    },
    showSearch: {
      type: Boolean,
      default: false
    },
    //是否远程
    remote: {
      type: Boolean,
      default: true
    },
    //展开节点回调函数
    onExpand: Function,
    //懒加载
    lazy: {
      type: Boolean,
      default: false
    },
    //是否默认所有节点都是父节点
    enableSubNode: {
      type: Boolean,
      default: false
    },
    onChange: Function,
    onSearch: Function,

    // 排序字段
    sort: {
      type: String,
      default: ''
    },
    // 排序方式 asc (升序), desc (降序)
    order: {
      type: String,
      default: 'asc'
    },
    iconClass: {
      type: String,
      default: ''
    },
    config: {
      type: Object,
      default() {
        return {};
      }
    },
    //节点是否可被选择
    showCheckbox: {
      type: Boolean,
      default: false
    },
    //是否勾选首级节点
    checkedFirst: Boolean,
    //默认勾选的节点的 key 的数组
    defaultCheckedKeys: {
      type: Array,
      default() {
        return [];
      }
    },
    multiple: {
      type: Boolean,
      default: false
    },
    limit: {
      type: Number,
      default: 0
    },
    // 父节点配置项
    parent: {
      type: Object,
      default() {
        return {
          click: true,
          // 是否允许父节点显示选择框
          checked: ''
        };
      }
    },
    load: {
      type: Function,
      default() {
        return () => {};
      }
    },
    // 树形数据请求方法
    method: {
      type: String,
      default: 'get'
    },
    nodeKey: {
      type: String,
      default: 'id'
    },
    param: {
      type: Object,
      default() {
        return {
          showarea: 0,
          orgType: 'org',
          nocheck: true,
          id: 'all',
          isroot: true,
          synchro: true,
          pvd: true,
          levels: 1,
          showlevelsub: true
        };
      }
    },
    where: {
      type: Object,
      default() {
        return {};
      }
    },
    parseData: Function,
    onLoadsub: Function,
    parseParams: Function,
    onNodeClick: Function,
    onCheckChange: Function,
    changeData: [String, Array, Object]
  },
  data() {
    return {
      loading: false,
      searchValue: '',
      expandedKeys: [],
      treeList: [],
      checkedKeys: this.defaultCheckedKeys
    };
  },
  computed: {
    treeData() {
      if (this.data !== undefined) {
        this.loading = false;
        return this.data;
      } else {
        return this.treeList;
      }
    },
    defaultProps() {
      if (this.props !== undefined) {
        return this.props;
      }
      return {
        label: 'name',
        children: 'children'
      };
    },
    isRemote() {
      if (this.data !== undefined && this.data.length) {
        return false;
      }
      return this.remote;
    },
    defaultCheckeds() {
      return [...this.defaultCheckedKeys, ...this.checkedKeys];
    }
  },
  watch: {
    searchValue: {
      deep: true,
      handler(val) {
        if (!this.isRemote && val !== this.search.value) {
          this.$refs.oaTree.filter(val);
        }
      }
    },
    param: {
      deep: true,
      handler() {
        this.getTreeData();
      }
    },
    where: {
      deep: true,
      handler(val, old) {
        if (!util.isObjectEqual(val, old)) {
          this.getTreeData();
        }
      }
    }
  },
  beforeCreate() {
    this.getTreeData = debounce(500, (param) => {
      this.getTreeDatas(param);
    });
  },
  created() {
    if (Object.prototype.hasOwnProperty.call(this.search, 'value')) {
      this.searchValue = this.search.value;
    }
    this.getTreeData(this.param);
  },
  mounted() {},
  methods: {
    // 树节点展开
    handleExpand(data, node) {
      if (this.onExpand) {
        this.onExpand(data, node);
      } else {
        if (this.remote) {
          this.loadsub(data, node);
        }
        this.$emit('node-expand', data, node);
      }
    },
    //获取树节点
    getTreeDatas(param) {
      let params = util.extend({}, this.param, param || {}, this.where);
      if (!this.url || !this.isRemote) return;
      this.loading = true;
      util
        .ajax({
          url: this.url,
          method: this.method,
          data: params,
          params: params
        })
        .then((res) => {
          this.loading = false;
          if (res.rCode === 0) {
            let treeList = this.parseData
              ? this.parseData(
                  JSON.parse(JSON.stringify(res.results)),
                  'results'
                )
              : JSON.parse(JSON.stringify(res.results));
            this.$emit('results', treeList);
            if (this.currentFirst) {
              let currentNode = treeList[0].id;
              if (!this.currentNodeKey && this.currentNodeKey != 0) {
                this.$emit('current-first', 'current', treeList[0]);
                this.$nextTick(() => {
                  this.$refs.oaTree.setCurrentKey(currentNode);
                });
              }
            }
            if (this.checkedFirst) {
              let checkedNode = treeList[0].id;
              if (
                this.defaultCheckedKeys &&
                this.defaultCheckedKeys.indexOf(checkedNode) == -1
              ) {
                this.$emit('current-first', 'checked', treeList[0]);
                this.checkedKeys = [checkedNode];
              }
            }
            this.expandedKeys = treeList.filter((item) => {
              if (item.open) {
                return item.id;
              }
            });
            if (this.enableSubNode) {
              this.treeList = this.addSubNode(treeList);
            } else {
              this.treeList = treeList;
            }
          }
        })
        .catch((err) => {
          this.loading = false;
          if (err.message && err.message !== 'canceled') {
            this.$message.error(err.message);
          }
        });
    },
    //给每个树节点默认加subNode属性，递归执行
    addSubNode(data) {
      if (!data) return data;
      if (Array.isArray(data)) {
        return data.map((item) => this.addSubNode(item));
      }
      const result = { ...data, subNode: true };
      if (result.children && result.children.length > 0) {
        result.open = true;
        result.children = this.addSubNode(result.children);
      } else {
        result.open = false;
      }
      return result;
    },

    //请求子节点数据
    loadsub(data, node) {
      if (data.state !== '' && (!data.children || data.children.length === 0)) {
        let params = util.extend({}, this.param, this.where, {
          id: data.id,
          orgType: data.orgType,
          isentity: data.isentity,
          layerlevel: data.layerlevel
        });
        if (this.parseParams) {
          params = this.parseParams({ data, params, node });
        }
        this.loading = true;
        if (this.onLoadsub) {
          this.onLoadsub({ data, params, node })
            .then((res) => {
              this.loading = false;
              this.$set(data, 'children', JSON.parse(JSON.stringify(res)));
            })
            .catch((e) => {});
        } else {
          util
            .ajax({
              method: this.method,
              url: this.url,
              data: params,
              params: params
            })
            .then((res) => {
              if (res.rCode === 0) {
                this.loading = false;
                let results = this.parseData
                  ? this.parseData(
                      JSON.parse(JSON.stringify(res.results)),
                      'sub-results'
                    )
                  : JSON.parse(JSON.stringify(res.results));
                this.$emit('sub-results', results);
                if (Array.isArray(results)) {
                  if (results.length > 0 && data.id === results[0].id) {
                    this.$set(
                      data,
                      'children',
                      results[0].children.map((item) => {
                        return {
                          ...item,
                          subNode: this.enableSubNode,
                          open: false
                        };
                      })
                    );
                  } else if (results.length <= 0) {
                    node.isLeaf = true;
                    console.log('node', node);
                    this.$set(data, 'children', []);
                  } else {
                    this.$set(
                      data,
                      'children',
                      results.map((item) => {
                        return {
                          ...item,
                          subNode: this.enableSubNode,
                          open: false
                        };
                      })
                    );
                  }
                } else if (data.id === results.id) {
                  this.$set(
                    data,
                    'children',
                    results.children.map((item) => {
                      return {
                        ...item,
                        subNode: this.enableSubNode,
                        open: false
                      };
                    })
                  );
                } else {
                  console.error('tree数据格式错误');
                }
              }
            })
            .catch((err) => {
              this.loading = false;
              console.log('errr', err);
              if (err.message && err.message !== 'canceled') {
                this.$message.error(err.message);
              }
            });
        }
      }
    },
    handleChange() {
      if (this.onChange !== undefined) {
        this.onChange(this.search);
      } else {
        let obj = {};
        obj[this.search.name] = this.searchValue;
        this.getTreeData(obj);
      }
    },
    handleSearch() {
      let obj = {};
      obj[this.search.name] = this.searchValue;
      if (this.onSearch !== undefined) {
        this.onSearch(obj);
      } else {
        this.getTreeData(obj);
      }
    },

    loadNode(node, resolve) {
      this.$emit('load', node, resolve);
    },
    sortTree(arr) {
      return arr.sort((a, b) => {
        if (this.order === 'asc' && a[this.sort]) {
          return a[this.sort] - b[this.sort];
        } else {
          return b[this.sort] - a[this.sort];
        }
      });
    },
    //筛选本地数据
    filterNode(value, data) {
      if (!value) return true;
      return String(data[this.search.name]).indexOf(value) !== -1;
    },
    check(data, options) {
      if (this.limit) {
        if (options.checkedKeys.length > this.limit) {
          const checkedKeys = options.checkedKeys.splice(0, 10);
          this.setCheckedKeys(checkedKeys);
          this.$message({
            showClose: true,
            message: '已达最大选择个数',
            type: 'warning'
          });
        }
      }
      this.$emit('check', data, options);
    },
    getOptionItemLabel(item) {
      if (this.search.optionLabelKey) return item[this.optionLabelKey];
      if (this.search.key) return item[this.optionLabelKey];
      return item.label;
    },
    getOptionItemValue(item) {
      if (this.search.optionValueKey) return item[this.optionValueKey];
      return item.value;
    },
    handleNodeClick(data, node, component) {
      if (this.esPage && this.changeData) {
        let changeData = {};
        if (util.isObject(this.changeData)) {
          for (let i in this.changeData) {
            if (util.isObject(this.changeData[i])) {
              let key = Object.keys(this.changeData[i])[0];
              changeData[i] = data[key][this.changeData[i][key]];
            } else {
              changeData[i] = data[this.changeData[i]];
            }
          }
        } else if (Array.isArray(this.changeData)) {
          this.changeData.forEach((item) => {
            changeData[item] = data[item];
          });
        } else {
          changeData[this.changeData] = data[this.changeData];
        }
        this.esPage.handleChangeData(changeData);
      }
      if (this.onNodeClick) {
        this.onNodeClick(this, data, node, component);
      } else {
        this.$emit('node-click', data, node, component);
      }
    },
    handleCheckChange(data, checked, indeterminate) {
      if (this.esPage && this.changeData) {
        let checkeds = this.getCheckedNodes();
        let changeData = {};
        if (util.isObject(this.changeData)) {
          for (let i in this.changeData) {
            changeData[i] = checkeds.map((item) => {
              return item[this.changeData[i]];
            });
          }
        } else if (Array.isArray(this.changeData)) {
          this.changeData.forEach((item) => {
            changeData[item] = checkeds.map((ele) => {
              return ele[this.changeData[i]];
            });
          });
        } else {
          changeData[this.changeData] = checkeds;
        }
        this.esPage.handleChangeData(changeData);
      }
      if (this.onCheckChange) {
        this.onCheckChange(data, checked, indeterminate, this);
      } else {
        this.$emit('check-change', data, checked, indeterminate, this);
      }
    },
    filter(parms = this.search) {
      return this.$refs.oaTree.filter(parms);
    },
    updateKeyChildren(key, data) {
      return this.$refs.oaTree.updateKeyChildren(key, data);
    },
    getCheckedNodes(leafOnly = false, includeHalfChecked = false) {
      return this.$refs.oaTree.getCheckedNodes(leafOnly, includeHalfChecked);
    },
    setCheckedNodes(nodes) {
      return this.$refs.oaTree.setCheckedNodes(nodes);
    },
    getCheckedKeys(leafOnly = false) {
      return this.$refs.oaTree.getCheckedKeys(leafOnly);
    },
    setCheckedKeys(keys, leafOnly) {
      return this.$refs.oaTree.setCheckedKeys(keys, leafOnly);
    },
    setChecked(key, checked, deep) {
      return this.$refs.oaTree.setChecked(key, checked, deep);
    },
    getHalfCheckedNodes() {
      return this.$refs.oaTree.getHalfCheckedNodes();
    },
    getHalfCheckedKeys() {
      return this.$refs.oaTree.getHalfCheckedKeys();
    },
    getCurrentKey() {
      return this.$refs.oaTree.getCurrentKey();
    },
    getCurrentNode() {
      let nodes = this.$refs.oaTree.getCurrentNode();
      return nodes;
    },
    setCurrentKey(key) {
      return this.$refs.oaTree.setCurrentKey(key);
    },
    setCurrentNode(node) {
      return this.$refs.oaTree.setCurrentNode(node);
    },
    getNode(data) {
      return this.$refs.oaTree.getNode(data);
    },
    remove(node) {
      return this.$refs.oaTree.remove(node);
    },
    append(data, parentNode) {
      return this.$refs.oaTree.append(data, parentNode);
    },
    insertBefore(node, refNode) {
      return this.$refs.oaTree.insertBefore(node, refNode);
    },
    insertAfter(data, refNode) {
      return this.$refs.oaTree.insertAfter(data, refNode);
    },
    getKeys(data, result) {
      data.forEach((item) => {
        result.push(item.id);
        if (
          Object.prototype.hasOwnProperty.call(item, 'children') &&
          item.children.length
        ) {
          this.getKeys(item.children, result);
        }
      });
    },
    handleCheck(keys) {
      if (keys) {
        if (!Array.isArray(keys)) {
          console.error('请传入数组类型');
          return false;
        }
      } else {
        keys = [];
        this.getKeys(this.treeData, keys);
      }
      this.setCheckedKeys(keys);
    }
  }
};
</script>
