<template>
  <div class="ztree-box">
    <div class="ztree-search" v-if="searchable">
      <div class="ztree-search-wrapper" :class="searchClass">
        <div class="icon"></div>
        <div class="input-box">
          <input
            type="text"
            v-model.trim="keyword"
            class="query-input"
            ref='queryInputRef'
            :placeholder="placeholder"
            @compositionstart="typing=true"
            @compositionend="onCompositionEnd"
            @focus="onQueryInputFocus"
            @blur="onQueryInputBlur"
            @input="onInput"
          />
        </div>
      </div>
    </div>
    <div class="ztree-container">
      <div class="ztree" :id="ztreeId" v-if="nodes && nodes.length"></div>
      <div class="empty" v-else>
        暂无数据
      </div>
    </div>
  </div>
</template>

<script>
import { debounce } from 'throttle-debounce'
import $ from 'jquery'
if (!window.jQuery) {
  window.jQuery = $
}
require('./lib/jquery.ztree.all')

function fn(pArray, key, showIcon) {
  // 对传递过来的数据进行处理，主要是增加图标，及去除childList:[]为空的
  pArray.forEach(pData => {
    if (showIcon) {
      // 如果显示图标
      if (pData.nodeType) {
        pData.customType = pData.nodeType * 1
      } else {
        if (!pData.customType) {
          // 如果没有定义图标
          if (pData[key] && pData[key].length === 0) {
            pData.customType = 2
          } else if (pData[key] && pData[key].length > 0) {
            pData.customType = 1
          }
        }
      }
    }

    if (pData[key] && pData[key].length === 0) {
      delete pData[key]
    } else if (pData[key] && pData[key].length > 0) {
      fn(pData[key], key, showIcon)
    }
  })
}

export default {
  name: 'SySearchTree',
  props: {
    setting: {
      type: Object,
      required: false,
      default: () => ({})
    },
    nodes: {
      type: Array,
      required: true,
      default: () => []
    },
    searchable: {
      type: Boolean,
      required: false,
      default: true
    },
    placeholder: {
      type: String,
      required: false,
      default: '请输入搜索关键字'
    },
    beforeClickFn: { type: Function },
    beforeCheckFn: { type: Function }
  },
  data() {
    return {
      keyword: '', // 搜索关键字
      isSearching: false, // 当前正处于输入状态
      curSelectedNode: null, // 当前选中的节点
      searchIsActive: false,
      ztreeId: 'ztree_' + parseInt(Math.random() * 1e10),
      ztreeObj: null,
      typing: false,
      ztreeSetting: {
        view: {
          showIcon: true // default to hide icon
        },
        callback: {
          beforeClick: (treeId, treeNode, clickFlag) => {
            return this.beforeClickFn && this.beforeClickFn(treeNode)
          },
          beforeCheck: (treeId, treeNode) => {
            return this.beforeCheckFn && this.beforeCheckFn(treeNode)
          },
          onAsyncError: (...arg) => {
            this.$emit('onAsyncError', ...arg)
          },
          onAsyncSuccess: (...arg) => {
            this.$emit('onAsyncSuccess', ...arg)
          },
          onCheck: (...arg) => {
            this.$emit('onCheck', ...arg)
          },
          onClick: (...arg) => {
            this.curSelectedNode = arg[2]
            this.$emit('onClick', ...arg)
          },
          onCollapse: (...arg) => {
            this.$emit('onCollapse', ...arg)
          },
          onDblClick: (...arg) => {
            this.$emit('onDblClick', ...arg)
          },
          onDrag: (...arg) => {
            this.$emit('onDrag', ...arg)
          },
          onDragMove: (...arg) => {
            this.$emit('onDragMove', ...arg)
          },
          onDrop: (...arg) => {
            this.$emit('onDrop', ...arg)
          },
          onExpand: (...arg) => {
            this.$emit('onExpand', ...arg)
          },
          onMouseDown: (...arg) => {
            this.$emit('onMouseDown', ...arg)
          },
          onMouseUp: (...arg) => {
            this.$emit('onMouseUp', ...arg)
          },
          onRemove: (...arg) => {
            this.$emit('onRemove', ...arg)
          },
          onRename: (...arg) => {
            this.$emit('onRename', ...arg)
          },
          onRightClick: (...arg) => {
            this.$emit('onRightClick', ...arg)
          }
        }
      }
    }
  },
  computed: {
    searchClass() {
      return { 'is-active': this.searchIsActive }
    }
  },
  watch: {
    keyword(newVal) {
      this.isSearching = !!newVal
    },
    nodes: {
      handler: function(nodes) {
        const idKey = this.setting.data.key.idKey || 'nodeId'
        const showIcon = (this.setting.view && this.setting.view.showIcon) || false
        fn(nodes, this.setting.data.key.children, showIcon)
        const list = nodes

        // 数据变更后更新树
        if (this.ztreeObj) {
          this.ztreeObj.destroy()
        }
        this.$nextTick(() => {
          this.ztreeObj = $.fn.zTree.init($('#' + this.ztreeId), Object.assign({}, this.ztreeSetting, this.setting), list)
          this.$emit('onCreated', this.ztreeObj, this.curSelectedNode)
          if (!this.isSearching && this.curSelectedNode) {
            // 如果不处于搜索状态
            this.setSelectedNode(this.curSelectedNode[idKey], true)
          }
          if (this.isSearching) {
            // 如果是搜索展开所有
            this.ztreeObj.expandAll(true)
          }
          this.$refs.queryInputRef.focus()
        })
      },
      deep: true,
      immediate: true
    }
  },
  methods: {
    onQueryInputFocus() {
      // 搜索框获得焦点
      this.searchIsActive = true
    },
    onQueryInputBlur() {
      // 搜索框失去焦点
      this.searchIsActive = false
    },
    onCompositionEnd() {
      this.typing = false
      this.onSearchTree()
    },
    onInput() {
      // 正在输入中文时就不执行后面的代码
      if (this.typing) return
      this.onSearchTree()
    },
    onSearchTree: debounce(200, function() {
      // 根据关键字搜索树
      this.$emit('searchTree', this.keyword)
      this.$refs.queryInputRef.focus()
    }),
    setSelectedNode(selectedKey, noEmit) {
      // 设置默认选中的节点
      if (!selectedKey) return
      const idKey = this.setting.data.key.idKey || 'nodeId'
      const nodes = this.ztreeObj.getNodesByParam(idKey, selectedKey, null)
      if (nodes.length) {
        const node = nodes[0]
        this.ztreeObj.selectNode(node)
        this.curSelectedNode = node
        if (noEmit) return
        this.$emit('onClick', ...[{ type: 0 }, null, node])
      }
    },
    setCheckedNodes(checkedKeys) {
      // 设置默认勾选的节点
      if (!checkedKeys || !(checkedKeys instanceof Array)) {
        console.log('请传递数组')
        return false
      }
      const idKey = this.setting.data.key.idKey || 'nodeId'
      checkedKeys.forEach(key => {
        const nodes = this.ztreeObj.getNodesByParam(idKey, key, null)
        if (nodes.length) {
          this.ztreeObj.checkNode(nodes[0], true, true)
        }
      })
    },
    cancelAllSelectedNode() {
      // 取消所有选中
      this.ztreeObj.cancelSelectedNode()
      this.curSelectedNode = null
    }
  }
}
</script>

<style scoped>
.ztree-box {
  height: 100%;
  display: flex;
  flex-direction: column;
}
.ztree-search {
  height: 30px;
  padding-top: 4px;
  margin-bottom: 12px;
}
.ztree-search-wrapper {
  height: 30px;
  width: 90%;
  margin: auto;
  border: 1px solid #dcdfe6;
  border-radius: 2px;
  display: flex;
  align-items: center;
  padding: 0 10px;
}
.ztree-search * {
  box-sizing: border-box;
}
.ztree-search-wrapper.is-active {
  border-color: #027aff;
}
.ztree-search-wrapper .icon {
  flex-basis: 16px;
  width: 16px;
  height: 16px;
  background: url('./zoom-out.png') no-repeat;
}
.ztree-search-wrapper .input-box {
  flex: 1;
}
.ztree-search-wrapper .query-input {
  height: 28px;
  line-height: 29px;
  width: 100%;
  border: none;
  padding: 0 8px;
  background-color: transparent;
}
.ztree-search-wrapper .query-input::placeholder {
  color: #aaa;
}
.ztree-container {
  flex: 1;
  overflow-y: auto;
}
.empty {
    width: 200px;
    color: #888;
    text-align: center;
    padding-top: 40px;
}
/* beauty ztree! */
.ztree {
  text-align: left;
  font-size: 14px;
  padding-left: 8px;
}
/* .ztree >>> .node-iconfont {
  font-size: 20px;
} */
.ztree >>> li {
  list-style-type: none;
  white-space: nowrap;
  outline: none;
}
.ztree >>> li ul {
  position: relative;
  padding: 0 0 0 18px;
  margin: 0;
}
.ztree >>> .line:before {
  position: absolute;
  top: 0;
  left: 10px;
  height: 100%;
  content: '';
  border-right: 1px dotted #dbdbdb;
}
.ztree >>> .roots_docu:before,
.ztree >>> .roots_docu:after,
.ztree >>> .center_docu:before,
.ztree >>> .bottom_docu:before,
.ztree >>> .center_docu:after,
.ztree >>> .bottom_docu:after {
  position: absolute;
  content: '';
  border: 0 dotted #dbdbdb;
}
.ztree >>> .roots_docu:before {
  left: 10px;
  height: 50%;
  top: 50%;
  border-left-width: 1px;
}
.ztree >>> .roots_docu:after {
  top: 50%;
  left: 11px;
  width: 50%;
  border-top-width: 1px;
}
.ztree >>> .center_docu:before {
  left: 10px;
  height: 100%;
  border-left-width: 1px;
}
.ztree >>> .center_docu:after {
  top: 50%;
  left: 11px;
  width: 50%;
  border-top-width: 1px;
}
.ztree >>> .bottom_docu:before {
  left: 10px;
  height: 50%;
  border-left-width: 1px;
}
.ztree >>> .bottom_docu:after {
  top: 50%;
  left: 11px;
  width: 50%;
  border-top-width: 1px;
}
.ztree >>> li a {
  display: inline-block;
  line-height: 22px;
  height: 22px;
  margin: 0;
  cursor: pointer;
  transition: none;
  vertical-align: middle;
  color: #606266;
}
.ztree >>> .node_name {
  display: inline-block;
  padding: 0 3px;
  border-radius: 4px;
}
.ztree >>> .curSelectedNode .node_name {
  color: #000;
  background-color: #c9e9f7;
}
.ztree >>> .curSelectedNode_Edit {
  height: 20px;
  opacity: 0.8;
  color: #000;
  border: 1px #6cc2e8 solid;
  background-color: #9dd6f0;
}
.ztree >>> .tmpTargetNode_inner {
  opacity: 0.8;
  color: #fff;
  background-color: #4fcbf0;
  filter: alpha(opacity=80);
}
.ztree >>> .rename {
  font-size: 12px;
  line-height: 22px;
  width: 80px;
  height: 22px;
  margin: 0;
  padding: 0;
  vertical-align: top;
  border: 0;
  background: none;
}
.ztree >>> .button {
  position: relative;
  display: inline-block;
  line-height: 22px;
  height: 22px;
  width: 18px;
  cursor: pointer;
  text-align: center;
  vertical-align: middle;
  font-size: 14px;
  color: rgb(74, 141, 240);
}

.ztree >>> .button.edit {
  color: #25ae88;
}
.ztree >>> .button.remove {
  color: #cb4042;
}
.ztree >>> .button.chk {
  position: relative;
  width: 14px;
  height: 14px;
  margin: 0 4px 0 0;
  border: 1px solid #d7dde4;
  border-radius: 2px;
  background: #fff;
}
.ztree >>> .chk.radio_true_full,
.ztree >>> .chk.radio_false_full,
.ztree >>> .chk.radio_true_full_focus,
.ztree >>> .chk.radio_false_full_focus,
.ztree >>> .chk.radio_false_disable,
.ztree >>> .chk.radio_true_disable,
.ztree >>> .chk.radio_true_part,
.ztree >>> .chk.radio_false_part,
.ztree >>> .chk.radio_true_part_focus,
.ztree >>> .chk.radio_false_part_focus {
  border-radius: 8px;
}
.ztree >>> .button.chk:after {
  position: absolute;
  top: 1px;
  left: 4px;
  width: 4px;
  height: 8px;
  content: '';
  transition: -webkit-transform 0.2s ease-in-out;
  transition: transform 0.2s ease-in-out;
  transition: transform 0.2s ease-in-out, -webkit-transform 0.2s ease-in-out;
  -webkit-transform: rotate(0deg) scale(0);
  transform: rotate(0deg) scale(0);
  border-right: 2px solid #fff;
  border-bottom: 2px solid #fff;
}
.ztree >>> .button.checkbox_false_full_focus {
  border-color: #ccc;
}
.ztree >>> .button.checkbox_true_full,
.ztree >>> .button.checkbox_true_full_focus,
.ztree >>> .button.checkbox_true_part,
.ztree >>> .button.checkbox_true_part_focus,
.ztree >>> .button.checkbox_true_disable {
  border-color: #39f;
  background-color: #39f;
}
.ztree >>> .button.checkbox_true_full:after,
.ztree >>> .button.checkbox_true_full_focus:after,
.ztree >>> .button.checkbox_true_disable:after {
  -webkit-transform: rotate(45deg) scale(1);
  transform: rotate(45deg) scale(1);
}
.ztree >>> .button.checkbox_true_part:after,
.ztree >>> .button.checkbox_true_part_focus:after {
  top: 5px;
  left: 1px;
  width: 10px;
  height: 1px;
  -webkit-transform: rotate(0deg) scale(1);
  transform: rotate(0deg) scale(1);
  border-right: 0;
}
.ztree >>> .button.radio_true_full,
.ztree >>> .chk.radio_true_full_focus,
.ztree >>> .chk.radio_true_part,
.ztree >>> .chk.radio_true_part_focus {
  border-color: #39f;
}
.ztree >>> .button.radio_true_full:after,
.ztree >>> .chk.radio_true_full_focus:after,
.ztree >>> .chk.radio_true_part:after,
.ztree >>> .chk.radio_true_part_focus:after {
  top: 3px;
  left: 3px;
  width: 8px;
  -webkit-transform: rotate(0deg) scale(1);
  transform: rotate(0deg) scale(1);
  border: 0;
  border-radius: 4px;
  background: #39f;
}
.ztree >>> .button.checkbox_true_disable,
.ztree >>> .button.checkbox_false_disable,
.ztree >>> .chk.radio_false_disable,
.ztree >>> .chk.radio_true_disable {
  cursor: not-allowed;
}
.ztree >>> .button.checkbox_false_disable {
  background-color: #f3f3f3;
}
.ztree >>> .button.noline_close:before,
.ztree >>> .button.noline_open:before,
.ztree >>> .button.root_open:before,
.ztree >>> .button.root_close:before,
.ztree >>> .button.roots_open:before,
.ztree >>> .button.roots_close:before,
.ztree >>> .button.bottom_open:before,
.ztree >>> .button.bottom_close:before,
.ztree >>> .button.center_open:before,
.ztree >>> .button.center_close:before {
  position: absolute;
  top: 6px;
  left: 5px;
  content: '';
  transition: -webkit-transform ease 0.3s;
  transition: transform ease 0.3s;
  transition: transform ease 0.3s, -webkit-transform ease 0.3s;
  -webkit-transform: rotateZ(0deg);
  transform: rotateZ(0deg);
  -webkit-transform-origin: 25% 50%;
  transform-origin: 25% 50%;
  border: 5px solid;
  border-color: transparent transparent transparent #999;
}
.ztree >>> .button.noline_open:before,
.ztree >>> .button.root_open:before,
.ztree >>> .button.roots_open:before,
.ztree >>> .button.bottom_open:before,
.ztree >>> .button.center_open:before {
  -webkit-transform: rotateZ(90deg);
  transform: rotateZ(90deg);
}
.ztree >>> .button.ico_loading {
  margin-right: 2px;
  background: url('data:image/gif;base64,R0lGODlhEAAQAKIGAMLY8YSx5HOm4Mjc88/g9Ofw+v///wAAACH/C05FVFNDQVBFMi4wAwEAAAAh+QQFCgAGACwAAAAAEAAQAAADMGi6RbUwGjKIXCAA016PgRBElAVlG/RdLOO0X9nK61W39qvqiwz5Ls/rRqrggsdkAgAh+QQFCgAGACwCAAAABwAFAAADD2hqELAmiFBIYY4MAutdCQAh+QQFCgAGACwGAAAABwAFAAADD1hU1kaDOKMYCGAGEeYFCQAh+QQFCgAGACwKAAIABQAHAAADEFhUZjSkKdZqBQG0IELDQAIAIfkEBQoABgAsCgAGAAUABwAAAxBoVlRKgyjmlAIBqCDCzUoCACH5BAUKAAYALAYACgAHAAUAAAMPaGpFtYYMAgJgLogA610JACH5BAUKAAYALAIACgAHAAUAAAMPCAHWFiI4o1ghZZJB5i0JACH5BAUKAAYALAAABgAFAAcAAAMQCAFmIaEp1motpDQySMNFAgA7')
    0 center no-repeat;
}
.ztree >>> .tmpTargetzTree {
  opacity: 0.8;
  background-color: #2ea9df;
  filter: alpha(opacity=80);
}
.ztree >>> .tmpzTreeMove_arrow {
  position: absolute;
  width: 18px;
  height: 18px;
  color: #4fcbf0;
}
.ztree >>> .ztree.zTreeDragUL {
  overflow: hidden;
  position: absolute;
  width: auto;
  height: auto;
  margin: 0;
  padding: 0;
  opacity: 0.8;
  border: 1px #176b53 dotted;
  background-color: #dbdbdb;
  filter: alpha(opacity=80);
}
.zTreeMask {
  position: absolute;
  z-index: 10000;
  opacity: 0;
  background-color: #cfcfcf;
  filter: alpha(opacity=0);
}
</style>
