<template>
  <div :class="b()">
    <div :class="b('filter')" v-if="vaildData(option.filter, true)">
      <el-input :placeholder="labels.enterKeywordsToFilter" :size="size" v-model="filterText">
        <el-button
          slot="append"
          :size="size"
          @click="parentAdd"
          icon="el-icon-plus"
          v-if="vaildData(option.addBtn, true)"
        ></el-button>
        <template slot="append">
          <slot name="addBtn"></slot>
        </template>
      </el-input>
    </div>
    <el-tree
      ref="tree"
      :data="list"
      :lazy="lazy"
      :load="treeLoad"
      :props="treeProps"
      highlight-current
      :show-checkbox="multiple"
      :accordion="accordion"
      :node-key="nodeKey"
      :check-strictly="checkStrictly"
      :filter-node-method="filterNode"
      :expand-on-click-node="false"
      @check-change="handleCheckChange"
      :default-expand-all="defaultExpandAll"
    >
      <div slot-scope="{ node, data }" :class="b('item')">
        <div
          :class="b('title')"
          @click.self="nodeClick(data)"
          @mouseenter="data.isShow = true"
          @mouseleave="data.isShow = false"
        >
          {{ data[labelKey] }}
          <div :class="b('menu')" v-show="data.isShow && vaildData(option.menu, true)">
            <el-dropdown trigger="click">
              <i class="el-icon-more" :class="b('icon')"></i>
              <el-dropdown-menu slot="dropdown">
                <el-dropdown-item
                  v-if="vaildData(option.addBtn, true)"
                  @click.native="append(node, data)"
                  >{{ labels.new }}</el-dropdown-item
                >
                <el-dropdown-item
                  v-if="vaildData(option.editBtn, true)"
                  @click.native="edit(node, data)"
                  >{{ labels.modify }}</el-dropdown-item
                >
                <el-dropdown-item
                  v-if="vaildData(option.delBtn, true)"
                  @click.native="remove(node, data)"
                  >{{ labels.delete }}</el-dropdown-item
                >
                <slot name="menuBtn" :node="node" :data="data"></slot>
              </el-dropdown-menu>
            </el-dropdown>
          </div>
        </div>
      </div>
    </el-tree>
    <el-dialog
      :title="obj[labelKey]"
      :visible.sync="box"
      modal-append-to-body
      append-to-body
      @close="hide"
      :width="vaildData(option.dialogWidth, '50%')"
    >
      <avue-form v-model="form" :option="formOption" ref="form" @submit="handleSubmit"></avue-form>
    </el-dialog>
  </div>
</template>

<script>
import cloneDeep from 'lodash/cloneDeep';
import { DIC_PROPS } from './variable';
import { vaildData, ft } from '../../../lib/utils';
import create from './create';

const LABELS = {
  new: ft('i18n_common_new', '新增'),
  delete: ft('i18n_common_delete', '删除'),
  modify: ft('i18n_common_please_select', '修改'),
  enterKeywordsToFilter: ft('i18n_common_enter_keywords_to_filter', '输入关键字进行过滤')
};

export default create({
  name: 'tree',
  // mixins: [locale],
  props: {
    option: {
      type: Object,
      default: () => {
        return {};
      }
    },
    data: {
      type: Array,
      default: () => {
        return [];
      }
    },
    value: {
      type: Object,
      default: () => {
        return {};
      }
    }
  },
  computed: {
    treeProps() {
      return Object.assign(this.props, {
        isLeaf: this.leafKey
      });
    },
    treeLoad() {
      return this.option.treeLoad;
    },
    checkStrictly() {
      return this.option.checkStrictly;
    },
    accordion() {
      return this.option.accordion;
    },
    multiple() {
      return this.option.multiple;
    },
    lazy() {
      return this.option.lazy;
    },
    addText() {
      return '确定';
    },
    addFlag() {
      return this.type === 'add' || this.type === 'parentAdd';
    },
    size() {
      return this.option.size || 'small';
    },
    props() {
      return this.option.props || {};
    },
    leafKey() {
      return this.props.leaf || DIC_PROPS.leaf;
    },
    valueKey() {
      return this.props.value || DIC_PROPS.value;
    },
    labelText() {
      return this.props.labelText || DIC_PROPS.labelText;
    },
    labelKey() {
      return this.props.label || DIC_PROPS.label;
    },
    childrenKey() {
      return this.props.children || DIC_PROPS.children;
    },
    nodeKey() {
      return this.option.nodeKey || DIC_PROPS.nodeKey;
    },
    defaultExpandAll() {
      return this.option.defaultExpandAll;
    },
    columnOption() {
      return this.appendKey(cloneDeep(this.data || []));
    },
    formColumnOption() {
      return (this.option.formOption || {}).column || [];
    },
    formOption() {
      return Object.assign(
        {
          submitText: this.addText,
          column: [
            {
              label: this.labelText,
              prop: this.labelKey,
              rules: [
                {
                  required: true,
                  message: ` ${this.labelText}`,
                  trigger: 'blur'
                }
              ]
            },
            ...this.formColumnOption
          ]
        },
        (() => {
          const option = this.option.formOption || {};
          delete option.column;
          return option;
        })()
      );
    }
  },
  data() {
    return {
      labels: LABELS,
      filterText: '',
      box: false,
      type: '',
      node: {},
      obj: {},
      form: {},
      list: []
    };
  },
  created() {
    this.vaildData = vaildData;
    this.list = cloneDeep(this.columnOption);
  },
  watch: {
    columnOption() {
      this.list = cloneDeep(this.columnOption);
    },
    option() {
      this.init();
    },
    filterText(val) {
      this.$refs.tree.filter(val);
    },
    value(val) {
      this.form = val;
    },
    form(val) {
      this.$emit('input', val);
    }
  },

  methods: {
    handleCheckChange(data, checked, indeterminate) {
      this.$emit('check-change', data, checked, indeterminate);
    },
    handleSubmit(form, done) {
      this.addFlag ? this.save(form, done) : this.update(form, done);
    },
    appendKey(list) {
      list.forEach((ele) => {
        ele.isShow = false;
        if (ele[this.childrenKey]) {
          this.appendKey(ele[this.childrenKey]);
        }
      });
      return list;
    },
    nodeClick(data) {
      this.$emit('node-click', data);
    },
    filterNode(value, data) {
      if (!value) {
        return true;
      }
      return data[this.labelKey].indexOf(value) !== -1;
    },
    hide() {
      this.box = false;
      this.node = {};
      this.obj = {};
      this.$refs.form.resetForm();
      this.$refs.form.clearValidate();
    },
    save(data, done) {
      const callback = () => {
        const form = cloneDeep(Object.assign(this.form, { isShow: false }));
        if (this.type === 'add') {
          if (!this.obj[this.childrenKey]) {
            this.$set(this.obj, 'children', []);
          }
          this.obj.children.push(form);
        } else if (this.type === 'parentAdd') {
          this.obj.push(form);
        }
        this.hide();
        done();
      };
      this.$emit('save', this.obj, this.node, callback, done);
    },
    update(data, done) {
      const callback = () => {
        const parent = this.node.parent;
        const children = parent.data[this.childrenKey] || parent.data;
        const index = children.findIndex((item) => item[this.nodeKey] === this.form[this.nodeKey]);
        children.splice(index, 1, this.form);
        this.hide();
        done();
      };
      this.$emit('update', this.obj, this.node, callback, done);
    },

    edit(node, data) {
      this.type = 'edit';
      this.node = node;
      this.obj = data;
      this.form = cloneDeep(this.obj);
      this.show();
    },
    parentAdd() {
      this.type = 'parentAdd';
      this.obj = this.list;
      this.show();
    },
    append(node, data) {
      this.type = 'add';
      this.obj = data;
      this.node = node;
      this.show();
    },
    show() {
      this.box = true;
      setTimeout(() => {
        this.$refs.form.clearValidate();
      }, 0);
    },
    remove(node, data) {
      this.obj = data;
      this.node = node;
      const callback = () => {
        const parent = node.parent;
        const children = parent.data.children || parent.data;
        const index = children.findIndex((d) => d.id === data.id);
        children.splice(index, 1);
      };
      this.$emit('del', this.obj, this.node, callback);
    }
  }
});
</script>
<style lang="scss" scoped>
.fast2-tree {
  &__item {
    position: relative;
    width: 100%;
  }
  &__title {
    font-size: 14px;
  }
  &__icon {
    transform: rotate(90deg);
  }
  &__menu {
    position: absolute;
    top: 2px;
    right: 2px;
    z-index: 1024;
  }
  &__filter {
    margin-bottom: 15px;
    display: flex;
    align-items: center;
  }
}
</style>
