zhangzengfei
2023-11-29 0d3db253cad1fb49c4fae9b9a537c8c318c7172f
src/components/treeMenu/jsTree/treeItem.vue
@@ -1,279 +1,299 @@
<template>
  <li
    role="treeitem"
    :class="classes"
    :draggable="draggable"
    @dragstart.stop="onItemDragStart($event, _self, _self.model)"
    @dragend.stop.prevent="onItemDragEnd($event, _self, _self.model)"
    @dragover.stop.prevent="isDragEnter = true"
    @dragenter.stop.prevent="isDragEnter = true"
    @dragleave.stop.prevent="isDragEnter = false"
    @drop.stop.prevent="handleItemDrop($event, _self, _self.model)"
  >
    <div role="presentation" :class="wholeRowClasses" v-if="isWholeRow">&nbsp;</div>
    <i class="tree-icon tree-ocl" role="presentation" @click="handleItemToggle"></i>
    <div :class="anchorClasses" v-on="events">
      <i
        class="tree-icon tree-checkbox"
        role="presentation"
        v-show="isShowCheckBox"
        @click.stop="handleCheckbox($event)"
      ></i>
      <slot :vm="this" :model="model">
        <i :class="themeIconClasses" role="presentation" v-if="!model.loading"></i>
        <span v-html="model[textFieldName]"></span>
      </slot>
    </div>
    <ul role="group" ref="group" class="tree-children" v-if="isFolder" :style="groupStyle">
      <tree-item
        v-for="(child, index) in model[childrenFieldName]"
        :key="index"
        :data="child"
        :text-field-name="textFieldName"
        :value-field-name="valueFieldName"
        :children-field-name="childrenFieldName"
        :item-events="itemEvents"
        :whole-row="wholeRow"
        :show-checkbox="showCheckbox"
        :allow-transition="allowTransition"
        :height="height"
        :parent-item="model[childrenFieldName]"
        :draggable="draggable"
        :drag-over-background-color="dragOverBackgroundColor"
        :on-item-click="onItemClick"
        :on-item-toggle="onItemToggle"
        :on-item-drag-start="onItemDragStart"
        :on-item-drag-end="onItemDragEnd"
        :on-item-drop="onItemDrop"
        :on-checkbox-click="onCheckboxClick"
        :klass="
          index === model[childrenFieldName].length - 1 ? 'tree-last' : ''
        "
      >
        <template slot-scope="_">
          <slot :vm="_.vm" :model="_.model">
            <i :class="_.vm.themeIconClasses" role="presentation" v-if="!model.loading"></i>
            <span v-html="_.model[textFieldName]"></span>
          </slot>
        </template>
      </tree-item>
    </ul>
  </li>
</template>
<script>
/* eslint-disable */
export default {
  name: "TreeItem",
  props: {
    data: { type: Object, required: true },
    textFieldName: { type: String },
    valueFieldName: { type: String },
    childrenFieldName: { type: String },
    itemEvents: { type: Object },
    wholeRow: { type: Boolean, default: false },
    showCheckbox: { type: Boolean, default: false },
    allowTransition: { type: Boolean, default: false },
    height: { type: Number, default: 24 },
    parentItem: { type: Array },
    draggable: { type: Boolean, default: false },
    dragOverBackgroundColor: { type: String },
    onItemClick: {
      type: Function,
      default: () => false
    },
    onItemToggle: {
      type: Function,
      default: () => false
    },
    onItemDragStart: {
      type: Function,
      default: () => false
    },
    onItemDragEnd: {
      type: Function,
      default: () => false
    },
    onItemDrop: {
      type: Function,
      default: () => false
    },
    onCheckboxClick: {
      type: Function,
      default: () => false
    },
    klass: String
  },
  data() {
    return {
      isHover: false,
      isDragEnter: false,
      model: this.data,
      maxHeight: 0,
      events: {}
    };
  },
  watch: {
    isDragEnter(newValue) {
      if (newValue) {
        this.$el.style.backgroundColor = this.dragOverBackgroundColor;
      } else {
        this.$el.style.backgroundColor = "inherit";
      }
    },
    data(newValue) {
      this.model = newValue;
    },
    "model.opened": {
      handler: function(val, oldVal) {
        this.onItemToggle(this, this.model);
        this.handleGroupMaxHeight();
      },
      deep: true
    }
  },
  computed: {
    isShowCheckBox() {
      return this.showCheckbox && (this.isHover || this.model.selected);
    },
    isFolder() {
      return (
        this.model[this.childrenFieldName] &&
        this.model[this.childrenFieldName].length
      );
    },
    classes() {
      return [
        { "tree-node": true },
        { "tree-open": this.model.opened },
        { "tree-closed": !this.model.opened },
        { "tree-leaf": !this.isFolder },
        { "tree-loading": !!this.model.loading },
        { "tree-drag-enter": this.isDragEnter },
        { [this.klass]: !!this.klass }
      ];
    },
    anchorClasses() {
      return [
        { "tree-anchor": true },
        { "tree-disabled": this.model.disabled },
        { "tree-selected": this.model.selected },
        { "tree-hovered": this.isHover }
      ];
    },
    wholeRowClasses() {
      return [
        { "tree-wholerow": true },
        { "tree-wholerow-clicked": this.model.selected },
        { "tree-wholerow-hovered": this.isHover }
      ];
    },
    themeIconClasses() {
      return [
        { "tree-icon": true },
        { "tree-themeicon": true },
        { [this.model.icon]: !!this.model.icon },
        { "tree-themeicon-custom": !!this.model.icon }
      ];
    },
    isWholeRow() {
      if (this.wholeRow) {
        if (
          this.$parent.model === undefined ||
          this.$parent.model.opened === true
        ) {
          return true;
        }
      }
      return false;
    },
    groupStyle() {
      return {
        position: this.model.opened ? "" : "relative",
        "max-height": !!this.allowTransition ? this.maxHeight + "px" : "",
        "transition-duration": this.allowTransition
          ? Math.ceil(this.model[this.childrenFieldName].length / 100) * 300 +
            "ms"
          : "",
        "transition-property": !!this.allowTransition ? "max-height" : "",
        display: this.allowTransition
          ? "block"
          : this.model.opened
          ? "block"
          : "none"
      };
    }
  },
  methods: {
    handleItemToggle(e) {
      if (this.isFolder) {
        this.model.opened = !this.model.opened;
        this.onItemToggle(this, this.model);
      }
    },
    handleGroupMaxHeight() {
      if (this.allowTransition) {
        let length = 0;
        let childHeight = 0;
        if (this.model.opened) {
          length = this.$children.length;
          for (let children of this.$children) {
            childHeight += children.maxHeight;
          }
        }
        this.maxHeight = length * this.height + childHeight;
        if (this.$parent.$options._componentTag === "tree-item") {
          this.$parent.handleGroupMaxHeight();
        }
      }
    },
    handleItemClick(e) {
      if (this.model.disabled) return;
      this.model.selected = !this.model.selected;
      this.onItemClick(this, this.model, e);
    },
    handleItemMouseOver() {
      this.isHover = true;
    },
    handleItemMouseOut() {
      this.isHover = false;
    },
    handleItemDrop(e, oriNode, oriItem) {
      this.$el.style.backgroundColor = "inherit";
      this.onItemDrop(e, oriNode, oriItem);
    },
    handleCheckbox(e) {
      if (this.model.disabled) return;
      this.model.selected = !this.model.selected;
      this.onCheckboxClick(this, this.model, e);
    }
  },
  created() {
    const self = this;
    const events = {
      click: this.handleItemClick,
      mouseover: this.handleItemMouseOver,
      mouseout: this.handleItemMouseOut
    };
    for (let itemEvent in this.itemEvents) {
      let itemEventCallback = this.itemEvents[itemEvent];
      if (events.hasOwnProperty(itemEvent)) {
        let eventCallback = events[itemEvent];
        events[itemEvent] = function(event) {
          eventCallback(self, self.model, event);
          itemEventCallback(self, self.model, event);
        };
      } else {
        events[itemEvent] = function(event) {
          itemEventCallback(self, self.model, event);
        };
      }
    }
    this.events = events;
  },
  mounted() {
    this.handleGroupMaxHeight();
  }
};
</script>
<template>
  <li
    role="treeitem"
    :class="classes"
    :draggable="draggable"
    @dragstart.stop="onItemDragStart($event, _self, _self.model)"
    @dragend.stop.prevent="onItemDragEnd($event, _self, _self.model)"
    @dragover.stop.prevent="isDragEnter = true"
    @dragenter.stop.prevent="isDragEnter = true"
    @dragleave.stop.prevent="isDragEnter = false"
    @drop.stop.prevent="handleItemDrop($event, _self, _self.model)"
  >
    <div role="presentation" :class="wholeRowClasses" v-if="isWholeRow">
      &nbsp;
    </div>
    <i
      class="tree-icon tree-ocl"
      role="presentation"
      @click="handleItemToggle"
    ></i>
    <div :class="anchorClasses" v-on="events">
      <i
        class="tree-icon tree-checkbox"
        role="presentation"
        v-show="isShowCheckBox"
        @click.stop="handleCheckbox($event)"
      ></i>
      <slot :vm="this" :model="model">
        <i
          :class="themeIconClasses"
          role="presentation"
          v-if="!model.loading"
        ></i>
        <span v-html="model[textFieldName]"></span>
      </slot>
    </div>
    <ul
      role="group"
      ref="group"
      class="tree-children"
      v-if="isFolder"
      :style="groupStyle"
    >
      <tree-item
        v-for="(child, index) in model[childrenFieldName]"
        :key="index"
        :data="child"
        :text-field-name="textFieldName"
        :value-field-name="valueFieldName"
        :children-field-name="childrenFieldName"
        :item-events="itemEvents"
        :whole-row="wholeRow"
        :show-checkbox="showCheckbox"
        :allow-transition="allowTransition"
        :height="height"
        :parent-item="model[childrenFieldName]"
        :draggable="draggable"
        :drag-over-background-color="dragOverBackgroundColor"
        :on-item-click="onItemClick"
        :on-item-toggle="onItemToggle"
        :on-item-drag-start="onItemDragStart"
        :on-item-drag-end="onItemDragEnd"
        :on-item-drop="onItemDrop"
        :on-checkbox-click="onCheckboxClick"
        :klass="
          index === model[childrenFieldName].length - 1 ? 'tree-last' : ''
        "
      >
        <template slot-scope="_">
          <slot :vm="_.vm" :model="_.model">
            <i
              :class="_.vm.themeIconClasses"
              role="presentation"
              v-if="!model.loading"
            ></i>
            <span v-html="_.model[textFieldName]"></span>
          </slot>
        </template>
      </tree-item>
    </ul>
  </li>
</template>
<script>
/* eslint-disable */
export default {
  name: "TreeItem",
  props: {
    data: { type: Object, required: true },
    textFieldName: { type: String },
    valueFieldName: { type: String },
    childrenFieldName: { type: String },
    itemEvents: { type: Object },
    wholeRow: { type: Boolean, default: false },
    showCheckbox: { type: Boolean, default: false },
    allowTransition: { type: Boolean, default: false },
    height: { type: Number, default: 24 },
    parentItem: { type: Array },
    draggable: { type: Boolean, default: false },
    dragOverBackgroundColor: { type: String },
    onItemClick: {
      type: Function,
      default: () => false,
    },
    onItemToggle: {
      type: Function,
      default: () => false,
    },
    onItemDragStart: {
      type: Function,
      default: () => false,
    },
    onItemDragEnd: {
      type: Function,
      default: () => false,
    },
    onItemDrop: {
      type: Function,
      default: () => false,
    },
    onCheckboxClick: {
      type: Function,
      default: () => false,
    },
    klass: String,
  },
  data() {
    return {
      isHover: false,
      isDragEnter: false,
      model: this.data,
      maxHeight: 0,
      events: {},
    };
  },
  watch: {
    isDragEnter(newValue) {
      if (newValue) {
        this.$el.style.backgroundColor = this.dragOverBackgroundColor;
      } else {
        this.$el.style.backgroundColor = "inherit";
      }
    },
    data(newValue) {
      this.model = newValue;
    },
    "model.opened": {
      handler: function (val, oldVal) {
        this.onItemToggle(this, this.model);
        this.handleGroupMaxHeight();
      },
      deep: true,
    },
  },
  computed: {
    isShowCheckBox() {
      return this.showCheckbox && (this.isHover || this.model.selected);
    },
    isFolder() {
      return (
        this.model[this.childrenFieldName] &&
        this.model[this.childrenFieldName].length
      );
    },
    classes() {
      return [
        { "tree-node": true },
        { "tree-open": this.model.opened },
        { "tree-closed": !this.model.opened },
        { "tree-leaf": !this.isFolder },
        { "tree-loading": !!this.model.loading },
        { "tree-drag-enter": this.isDragEnter },
        { [this.klass]: !!this.klass },
      ];
    },
    anchorClasses() {
      return [
        { "tree-anchor": true },
        { "tree-disabled": this.model.disabled },
        { "tree-selected": this.model.selected },
        { "tree-hovered": this.isHover },
      ];
    },
    wholeRowClasses() {
      return [
        { "tree-wholerow": true },
        { "tree-wholerow-clicked": this.model.selected },
        { "tree-wholerow-hovered": this.isHover },
      ];
    },
    themeIconClasses() {
      return [
        { "tree-icon": true },
        { "tree-themeicon": true },
        { [this.model.icon]: !!this.model.icon },
        { "tree-themeicon-custom": !!this.model.icon },
      ];
    },
    isWholeRow() {
      if (this.wholeRow) {
        if (
          this.$parent.model === undefined ||
          this.$parent.model.opened === true
        ) {
          return true;
        }
      }
      return false;
    },
    groupStyle() {
      return {
        position: this.model.opened ? "" : "relative",
        "max-height": !!this.allowTransition ? this.maxHeight + "px" : "",
        "transition-duration": this.allowTransition
          ? Math.ceil(this.model[this.childrenFieldName].length / 100) * 300 +
            "ms"
          : "",
        "transition-property": !!this.allowTransition ? "max-height" : "",
        display: this.allowTransition
          ? "block"
          : this.model.opened
          ? "block"
          : "none",
      };
    },
  },
  methods: {
    handleItemToggle(e) {
      if (this.isFolder) {
        this.model.opened = !this.model.opened;
        this.onItemToggle(this, this.model);
      }
    },
    handleGroupMaxHeight() {
      if (this.allowTransition) {
        let length = 0;
        let childHeight = 0;
        if (this.model.opened) {
          length = this.$children.length;
          for (let children of this.$children) {
            childHeight += children.maxHeight;
          }
        }
        this.maxHeight = length * this.height + childHeight;
        if (this.$parent.$options._componentTag === "tree-item") {
          this.$parent.handleGroupMaxHeight();
        }
      }
    },
    handleItemClick(e) {
      if (this.model.disabled) return;
      this.model.selected = !this.model.selected;
      this.onItemClick(this, this.model, e);
    },
    handleItemMouseOver() {
      this.isHover = true;
    },
    handleItemMouseOut() {
      this.isHover = false;
    },
    handleItemDrop(e, oriNode, oriItem) {
      this.$el.style.backgroundColor = "inherit";
      this.onItemDrop(e, oriNode, oriItem);
    },
    handleCheckbox(e) {
      if (this.model.disabled) return;
      this.model.selected = !this.model.selected;
      this.onCheckboxClick(this, this.model, e);
    },
  },
  created() {
    const self = this;
    const events = {
      click: this.handleItemClick,
      mouseover: this.handleItemMouseOver,
      mouseout: this.handleItemMouseOut,
    };
    for (let itemEvent in this.itemEvents) {
      let itemEventCallback = this.itemEvents[itemEvent];
      if (events.hasOwnProperty(itemEvent)) {
        let eventCallback = events[itemEvent];
        events[itemEvent] = function (event) {
          eventCallback(self, self.model, event);
          itemEventCallback(self, self.model, event);
        };
      } else {
        events[itemEvent] = function (event) {
          itemEventCallback(self, self.model, event);
        };
      }
    }
    this.events = events;
  },
  mounted() {
    this.handleGroupMaxHeight();
  },
};
</script>