<template>
  <!-- item template -->
  <div class="tree-node" v-show="!item.$isHide&&(!treeObj.showSelectList||selectStatus>0)">
    <div
      class="tree-item row no-wrap items-center"
      :class="(isLastNode()?'':'line')+(treeSelectedItem&&treeSelectedItem.item===item?' selected':'')"
    >
      <q-icon
        :class="(isFolder()?'is-folder ':' ')+(item.$isOpen?'extend rotate-90':'')"
        v-if="isFolder()"
        name="play_arrow"
        @click.stop="toggle(!item.$isOpen)"
        class="e-icon text-grey-6"
      ></q-icon>
      <q-icon
        v-if="!lazy&&selectType!=='none'&&!treeObj.showSelectList&&!isRoot"
        @click.stop="selectValueClick()"
        class="checkbox-icon"
        :color="getBoxClass()"
        :name="selectStatusClass[selectStatus][1]"
      ></q-icon>
      <slot name="icon" v-bind="item">
        <q-icon v-if="item.icon" :name="item.icon" class="node-icon"></q-icon>
      </slot>
      <div
        v-if="!item.$isEdit"
        class="tree-title q-pa-xs"
        clickable
        @dblclick="updateItem(item)"
        @keyup.enter="updateItem(item)"
        @mouseenter="mouseAction(true)"
        @mouseleave="mouseAction(false)"
        :class="getTitleClass()"
        @click="selectItem($event,true)"
      >
        <slot name="title" v-bind="item">
          <span v-text="item[titleKey]"></span>
          <q-btn
            class="q-ml-xs"
            v-if="isEdit&&item.$mouseover"
            icon="edit"
            color="primary"
            flat
            dense
            size="0.60em"
            @click="updateItem()"
          >
            <q-tooltip>修改</q-tooltip>
          </q-btn>
          <q-btn
            v-if="isEdit&&item.$mouseover"
            icon="clear"
            color="negative"
            flat
            dense
            size="0.60em"
            @click="delItem()"
          >
            <q-tooltip>删除</q-tooltip>
          </q-btn>
        </slot>
      </div>
      <slot name="edit" v-bind="item">
        <input
          placeholder="输入名称"
          v-focus="item.$isEdit"
          v-if="item.$isEdit"
          v-model="item[titleKey]"
          class="q-pa-xs"
          @click.stop
          @keyup.esc="cancelSave"
          @keyup.enter="$event.target.blur()"
          @focus="focus($event)"
          @blur="save"
        />
      </slot>
    </div>
    <div
      class="tree-children"
      :class="isLastNode()?'':'line'"
      v-show="item.$isOpen"
      v-if="isFolder()"
    >
      <kyb-tree-item
        :ref="'item_'+index"
        is-node
        v-for="(child, index) in item.children"
        :key="itemKeyIn?child[itemKeyIn]:index"
        :select-type="selectType"
        :lazy="lazy && child.level <= maxLevel"
        :is-edit="isEdit"
        :root="rootInner"
        :tree-obj="treeObj"
        :item="child"
        :entity="entity"
        :item-key="itemKey"
        :tree-selected-item="treeSelectedItem"
        @add-item="$emit('add-item', $event)"
        @select-item="$emit('select-item', $event)"
        @load-child="$emit('load-child', $event)"
        @item-change="itemChange"
        @select-value="$emit('select-value', $event)"
        @set-value="setValueByEmit($event)"
        @mounted-item="$emit('mounted-item', $event)"
        @del-item="delItem"
        @del-action="delAction"
        @save-to-server="$emit('save-to-server', $event)"
      >
        <template #icon="child">
          <slot name="icon" v-bind="child" />
        </template>
        <template #title="child">
          <slot name="title" v-bind="child" />
        </template>
        <template #edit="child">
          <slot name="edit" v-bind="child" />
        </template>
      </kyb-tree-item>
      <!-- <div class="add" @click="$emit('add-item', item)">+</div> -->
    </div>
  </div>
</template>

<script>
import { uid } from 'quasar'
// define the kyb-tree2 component
import KybMdata from '../kyb-mdata.js'

// 递归执行所有子孙
import di from './di'

export default {
  mixins: [KybMdata],
  name: 'kyb-tree-item',
  props: {
    lazy: Boolean, // 懒加载，每次从服务器只加载子对象，否则一次加载完成对象
    isRoot: Boolean,
    isEdit: Boolean, // 是否可编辑
    isNode: Boolean,
    item: Object, // 数据对象,
    root: Object,
    selectType: { type: String, default: 'none' },
    treeObj: Object,
    treeSelectedItem: Object,
    entity: String, // 服务器端实体,
    itemKey: { type: String, default: '_id' }, // 唯一Id字段
    titleKey: { type: String, default: 'name' }, // 显示字段
    valueKey: { type: String, default: '_id' }, // 值的字段
    // 最大显示级别
    maxLevel: {
      type: Number,
      default: 2
    }
  },
  data: function() {
    return {
      itemKeyIn: this.itemKey,
      rootInner: this.root ? this.root : this,
      selectStatus: 0, // 三态选择，0:不选，1:子孙对象部分选择，2:子孙对象全部选择
      selectStatusClass: {
        0: ['', 'check_box_outline_blank'],
        1: ['grey-7', 'check_box'],
        2: ['primary', 'check_box']
      }
    }
  },
  created: function() {
    // console.log("created", this);
  },
  // 当有新的节点挂载，不停向上传递消息，只到根节点为止
  mounted() {
    this.$emit('mounted-item', this)
    // if (this.selectType === 'more') {
    //   this.selectStatus = this.treeObj.selectListInnerMap[this.item[this.itemKey]] ? 2 : 0
    // }
  },
  watch: {
    // 'treeObj.selectListInner'() {
    //   if (this.selectType === 'more') {
    //     this.selectStatus = this.treeObj.selectListInnerMap[this.item[this.itemKey]] ? 2 : 0
    //   }
    // }
  },
  computed: {},
  methods: {
    isFolder: function() {
      return (
        this.item &&
        (this.lazy || (this.item.children && this.item.children.length))
      )
    },
    isLastNode: function() {
      let c = this.$parent ? this.$parent.item.children : []
      c || (c = [])
      let index = c.indexOf(this.item)
      let len = c.length
      let isLastIndex = true
      if (this.treeObj.showSelectList && index > -1) {
        let ji = 0
        for (let i = index + 1; i < len; i++) {
          let obj = c[i][this.itemKey]
          // console.log('----obj', obj, i)
          if (this.treeObj.selectListInnerMap[obj]) {
            ji++
            break
          }
        }
        isLastIndex = ji === 0
      } else {
        isLastIndex = index === len - 1
      }
      return this.$parent && isLastIndex
    },
    getBoxClass() {
      if (this.item.$disabled) {
        return 'grey-7 cursor-not-allowed'
      } else {
        return this.selectStatusClass[this.selectStatus][0]
      }
    },
    getTitleClass() {
      if (this.item.$disabled) {
        return 'cursor-not-allowed'
      } else {
        return this.treeSelectedItem && this.treeSelectedItem.item === this.item
          ? 'selected'
          : ''
      }
    },
    itemChange(type, child) {
      this.$emit('item-change', type, child)
    },
    toggle: function(isOpen, changeChild) {
      this.item.$isOpen = isOpen
      if (this.lazy) {
        if (this.item.$isOpen) {
          this.loadChild()
        }
      } else {
        if (changeChild && this.item.children && this.item.children.length) {
          this.item.children.forEach((v, k) => {
            this.$refs['item_' + k][0].toggle(isOpen, changeChild)
          })
        }
      }
      this.$forceUpdate()
    },
    loadChild: async function() {
      if (!this.entity) {
        this.$q.showNotifi('需设置 entity')
        return
      }

      let url = await this.genBaseUrl(
        `/query?_pid=${this.item[this.itemKey]}&pageSize=5000`
      )
      let response = await this.$axios.get(url)
      this.item.children = response.data.items
      this.$forceUpdate()
    },
    focus: function(event) {
      event.currentTarget.select()
    },
    selectItem($event, isClick, notExtend) {
      // console.log(this.item)
      if (isClick && this.item.$disabled) {
        return
      }
      if (!this.lazy && this.selectType !== 'none' && !notExtend && this.item) {
        this.item.$isOpen = !this.item.$isOpen
        // this.setValueAction();
      }
      this.$forceUpdate()
      this.$emit('select-item', this)
    },

    selectValueChild(obj, status, isClick) {
      if (obj.item && obj.item.children && obj.item.children.length) {
        obj.item.children.forEach((v, k) => {
          let child = obj.$refs['item_' + k][0]
          if (child) {
            if (isClick && child.item.$disabled) {
            } else {
              child.selectStatus = status
              // console.log('-----', child.item._id)
              child.$forceUpdate()
            }
            this.selectValueChild(child, status, isClick)
          }
        })
      }
    },
    /**
     * 树内部点击复选框事件改编选择的值列表，这个是层层向上传递，最终将通过input将值传递出去
     * 因此需要根据当前节点的关系，将变动的值传递出去
     */
    selectValueClick() {
      // 根据当前节点计算是选择还是取消
      if (this.item.$disabled) {
        return
      }
      if (this.selectType === 'more') {
        let status = !this.selectStatus || this.selectStatus === 1 ? 2 : 0
        // 将this.treeObj.selectListInner转换为map，便于添加或删除数据
        let map = this.treeObj.selectListInnerMap
        // 递归所有儿子
        di(this, { status, map }, (obj, { status, map }) => {
          if (obj.item.$disabled) return
          if (status === 2) {
            map[obj.item[obj.valueKey]] = true
          } else {
            delete map[obj.item[obj.valueKey]]
          }
        })
        // 检查所有父亲的选择状态
        let p = this.$parent
        while (p != null && p.isNode) {
          if (status === 2) {
            map[p.item[p.valueKey]] = true
          } else {
            // 取消情况，需要判断其他儿子是否已选，如果选了就取消当前
            let selectedNum = 0
            for (let k in p.item.children) {
              let child = p.$refs['item_' + k][0]
              if (child) {
                if (child.selectStatus !== 0) {
                  selectedNum++
                  if (selectedNum > 1) {
                    break
                  }
                }
              }
            }
            if (selectedNum <= 1) {
              delete map[p.item[p.valueKey]]
            }
          }
          p = p.$parent
        }
        this.treeObj.selectListInner = Object.keys(map)
        // console.log('----tree', this.treeObj.selectListInner)
      } else if (this.selectType === 'one') {
        this.treeObj.selectListInner = this.selectStatus
          ? []
          : [this.item[this.valueKey]]
      }
      this.treeObj.sendInput()
      // this.$emit('select-value', this)
    },
    /**
     * 选择当前节点
     * isClick: 是否单击的方式，主要用于disable属性是否有效的判断，
     * status: 如果该参数指定了值，就使用指定值，否则根据自身变化
     * notSendInput: 对于从外面赋值进来的情况，就不能传递 select-value 事件去处，否则就会造成死循环
     */
    setValueAction(isClick, status) {
      if (isClick && this.item.$disabled) {
        return
      }
      // this.$set(this.item, "$selected", !this.item.$selected);

      if (this.selectType === 'more') {
        if (status !== null && status !== undefined) {
          this.selectStatus = status ? 2 : 0
        } else {
          this.selectStatus =
            !this.selectStatus || this.selectStatus === 1 ? 2 : 0
        }
        // 将我的状态迭代给我的子孙
        this.selectValueChild(this, this.selectStatus, isClick)
      } else if (this.selectType === 'one') {
        if (status !== null && status !== undefined) {
          this.selectStatus = status ? 2 : 0
        } else {
          if (!this.selectStatus) {
            this.selectStatus = 2
          } else {
            this.selectStatus = 0
          }
        }
      }
      this.$emit('set-value', this)
    },
    /**
     * 树外向树内设置值的情况，这个事件是不会通过input传递值出去
     */
    setValueByEmit(obj) {
      // 不停的向父亲传递
      if (this.selectType === 'more') {
        let selectedNum = 0,
          partNum = 0,
          notNum = 0
        if (this.item && this.item.children && this.item.children.length) {
          this.item.children.forEach((v, k) => {
            let child = this.$refs['item_' + k][0]
            if (child) {
              if (child.selectStatus === 2) {
                selectedNum++
              } else if (child.selectStatus === 1) {
                partNum++
              } else {
                notNum++
              }
            }
          })
        }
        if (selectedNum === 0 && partNum === 0) {
          this.selectStatus = 0
        } else if (selectedNum > 0 && partNum === 0 && notNum === 0) {
          this.selectStatus = 2
        } else {
          this.selectStatus = 1
        }
        this.item.$selectStatus = this.selectStatus
      }
      this.$emit('set-value', obj)
    },
    cancelSave: function() {
      // this.save();
      // 取消添加的情况
      this.item[this.titleKey] = this.item['$old_' + this.titleKey]
      this.item.$isEdit = false
      this.$forceUpdate()
      if (this.item.$isAdd) {
        this.item.$delete = true
        this.delItem()
      }
    },
    async save() {
      if (this.item.$delete) return
      this.item.$isEdit = false
      if (this.item.$isAdd) {
        this.item[this.itemKey] = uid()
        this.item.pid = this.$parent.item[this.itemKey]
      }
      if (this.entity) {
        let re = await this.$axios.post(
          await this.genBaseUrl('/save'),
          this.item
        )
        if (re.data) {
          Object.assign(this.item, re.data)
        }
      }
      // 如果是多选，并且父亲为全选就默认选择
      if (this.selectType === 'more' && this.$parent.selectStatus === 2) {
        this.selectStatus = 2
        this.treeObj.selectListInner.push(this.item[this.itemKey])
        setTimeout(() => {
          this.treeObj.sendInput()
        }, 300)
      }
      delete this.item.$isAdd
      this.$forceUpdate()
      this.$emit('item-change', 'saveChild', this)
    },
    addItem(item) {
      this.item.children || (this.item.children = [])
      let n = {
        $isEdit: true,
        $isAdd: true
      }

      this.item.children.push(n)
      this.$set(this.item, 'children', this.item.children)
      this.item.$isOpen = true
      this.$forceUpdate()

      if (this.item.children.length > 1) {
        let c = this.$refs['item_' + (this.item.children.length - 2)]
        if (c && c.length && c[0]) {
          c[0].$forceUpdate()
        }
      }

      this.$emit('add-item', this)
      this.$emit('item-change', 'addChild', this)
    },
    updateItem(item) {
      if (!this.isEdit) return
      item = item || this.item
      this.item['$old_' + this.titleKey] = this.item[this.titleKey]
      this.item.$isEdit = true
      this.$forceUpdate()
    },
    /**
     * 获取到子传过来的事件，obj为需要删除的子对象
     */
    async doDelAction(obj) {
      if (this.entity && !obj.item.$isAdd) {
        // 服务器默认的API已经实现删除子孙的功能
        await this.$axios.post(await this.genBaseUrl('/del'), {
          _id: obj.item[obj.itemKey]
        })
      }
      // 在多选的情况下如果obj有子孙，同时也删除子孙的选择
      let map = this.treeObj.selectListInnerMap
      // 递归所有儿子
      di(obj, { map }, (v, { map }) => {
        delete map[v.item[v.valueKey]]
      })
      this.treeObj.selectListInner = Object.keys(map)
      this.treeObj.sendInput()

      let isSelectSelf = this.treeSelectedItem === obj
      let indexOf = this.item.children.indexOf(obj.item)
      this.item.children.splice(indexOf, 1)

      let last = null
      if (this.item.children.length > 0) {
        let next = indexOf + 1
        if (indexOf === this.item.children.length) {
          next = indexOf - 1
        }
        last = this.$refs['item_' + next][0]
        last.$forceUpdate()
      } else {
        last = this
      }

      if (isSelectSelf) {
        // 目前选中的是当前删除的item
        if (last.item.$disabled) {
          last = this.rootInner
        }
        last.selectItem(null, true, true)
      }
      this.$forceUpdate()
      setTimeout(() => {
        this.$emit('item-change', 'delChild', obj)
      }, 300)
    },
    delAction(obj) {
      if (obj.item.$isAdd) {
        this.doDelAction(obj)
      } else {
        this.$q.showConfirm('是否删除该节点!', async () => {
          if (
            obj.item.lasy ||
            (obj && obj.item && obj.item.children && obj.item.children.length)
          ) {
            this.$q.showConfirm(
              '将同时删除该节点的所有子孙节点，请确认!',
              async () => {
                this.doDelAction(obj)
              }
            )
          } else {
            this.doDelAction(obj)
          }
        })
      }
    },
    delItem() {
      // 告诉父亲做具体的删除动作, 比较关键，都是通过父亲来删除自己的。
      this.$emit('del-action', this)
    },
    mouseAction(v) {
      this.item.$mouseover = v
      this.$forceUpdate()
    }
  }
}
</script>

<style lang="stylus">
.tree-node:hover, .tree-node.selected {
  // background $grey-8
}

.tree-children {
  padding-left: 30px;
  position: relative;
}

.tree-item {
  position: relative;
  padding: 2px;
}

.tree-title {
  margin: 1px;
  min-height: 30px;
  min-width: 50px;
  cursor: pointer;
}

.tree-title.cursor-not-allowed {
  text-shadow: 0 0 3px #999;
  color: $grey-6;
}

.tree-title:hover, .tree-title.selected {
  background-color: $grey-3;
  border-radius: 3px;
}

.tree-title.cursor-not-allowed:hover {
  background-color: transparent;
}

.tree-title.selected {
  color: $primary;
}

.tree-item.line::before, .tree-children.line::before {
  content: '';
  position: absolute;
  left: -18px;
  top: 0;
  width: 16px;
  bottom: 0;
  border-left: 1px solid $grey-5;
}

.tree-item::after {
  content: '';
  position: absolute;
  left: -18px;
  top: -10%;
  width: 18px;
  height: 60%;
  border-left: 1px solid $grey-5;
  border-bottom: 1px solid $grey-5;
}

.e-icon {
  transition: all 0.5s;
  font-size: 20px;
  width: 20px;
  height: 20px;
  border-radius: 3px;
  cursor: pointer;
}

.e-icon.is-folder:hover {
  background: $grey-4;
}

.node-icon {
  padding: 6px;
  font-size: 18px;
}

.checkbox-icon {
  font-size: 22px;
}

.fade-enter-active, .fade-leave-active {
  transition: all 0.4s;
  overflow: hidden;
}

.fade-enter, .fade-leave-to {
  // opacity: 0;
  height: 0px;
}
</style>
