<template>
  <div class="rule-field-edit">
    <div class="field-part field-part-source">
      <div class="field-title">
        <el-checkbox v-model="sourceAll" @change="sourceAllChange"
          >来源字段</el-checkbox
        >
      </div>
      <div class="field-body">
        <div v-for="item in sourceList" :key="item.code" class="field-item">
          <el-checkbox v-model="item.checked" @change="sourceItemChange">{{
            item.name
          }}</el-checkbox>
        </div>
      </div>
    </div>
    <div class="field-part field-part-action">
      <div class="action-body">
        <el-button
          size="small"
          icon="el-icon-arrow-left"
          @click="toLeft"
        ></el-button>
        <el-button
          size="small"
          icon="el-icon-arrow-right"
          @click="toRight"
        ></el-button>
      </div>
    </div>
    <div class="field-part field-part-source">
      <div class="field-title">
        <el-checkbox v-model="targetAll" @change="targetAllChange"
          >目的字段</el-checkbox
        >
      </div>
      <div
        class="field-body"
        @drop.prevent="dropInnerElement"
        @dragover.prevent="dragOver($event)"
        @dragleave.prevent
      >
        <div
          v-for="item in targetList"
          :key="item.code"
          :class="[
            'field-item',
            {
              'after-drop': item.code === curDropElementCode && isDragDown,
              'before-drop': item.code === curDropElementCode && !isDragDown,
            },
          ]"
          :id="`field-item-${item.code}`"
          draggable="true"
          @dragstart="($event) => dragStart($event, item)"
          @dragend.prevent="dragEnd"
        >
          <el-checkbox v-model="item.checked" @change="targetItemChange">{{
            item.name
          }}</el-checkbox>
        </div>
      </div>
    </div>
  </div>
</template>

<script>
export default {
  name: "rule-field-edit",
  props: {
    value: {
      type: Array,
      default() {
        return [];
        // [{code:'',name:''}]
      },
    },
    data: {
      type: Array,
      default() {
        return [];
        // [{code:'',name:''}]
      },
    },
  },
  data() {
    return {
      sourceAll: false,
      sourceList: [],
      targetAll: false,
      targetList: [],
      // drag
      curDragElement: null,
      curDropElementCode: null,
      dragStartPageY: null,
      isDragDown: false,
    };
  },
  watch: {
    value() {
      this.initData();
    },
    data() {
      this.initData();
    },
  },
  methods: {
    initData() {
      const fields = this.data.map((item) => {
        return {
          ...item,
          checked: false,
        };
      });
      const valCodes = this.value.map((item) => item.code);
      this.sourceList = fields.filter((item) => !valCodes.includes(item.code));
      this.targetList = this.value.map((item) => {
        return { ...item, checked: false };
      });
    },
    sourceAllChange() {
      this.sourceList.forEach((item) => {
        item.checked = this.sourceAll;
      });
    },
    targetAllChange() {
      this.targetList.forEach((item) => {
        item.checked = this.targetAll;
      });
    },
    sourceItemChange() {
      this.sourceAll = !this.sourceList.some((item) => !item.checked);
    },
    targetItemChange() {
      this.targetAll = !this.targetList.some((item) => !item.checked);
    },
    toLeft() {
      const checkedFields = this.targetList.filter((item) => item.checked);
      if (!checkedFields.length) {
        this.$message.error("请选择目的字段！");
        return;
      }

      this.sourceList.push(
        ...checkedFields.map((item) => {
          return {
            ...item,
            checked: false,
          };
        })
      );
      this.targetList = this.targetList.filter((item) => !item.checked);
      this.targetAll = false;
      this.emitChange();
    },
    toRight() {
      const checkedFields = this.sourceList.filter((item) => item.checked);
      if (!checkedFields.length) {
        this.$message.error("请选择来源字段！");
        return;
      }

      this.targetList.push(
        ...checkedFields.map((item) => {
          return {
            ...item,
            checked: false,
          };
        })
      );
      this.sourceList = this.sourceList.filter((item) => !item.checked);
      this.sourceAll = false;
      this.emitChange();
    },
    emitChange() {
      const targetFields = this.targetList.map((item) => {
        return {
          name: item.name,
          code: item.code,
        };
      });
      this.$emit("input", targetFields);
      this.$emit("change", targetFields);
    },
    // drag
    getRelateElement(dom) {
      let element = null;
      let parentNode = dom;
      while (!element && !parentNode.className.includes("field-body")) {
        if (
          !element &&
          parentNode["id"] &&
          parentNode["id"].includes("field-item")
        ) {
          element = parentNode;
        } else {
          parentNode = parentNode.parentNode;
        }
      }

      return element;
    },
    getElementCode(fieldId) {
      return fieldId.replace("field-item-", "");
    },
    getSiblingElement(fieldCode, offset) {
      const pos = this.targetList.findIndex((elem) => elem.code === fieldCode);
      return this.targetList[pos + offset] || null;
    },
    moveElementToElement(curElement, toElementCode, isDragDown) {
      const curPos = this.targetList.findIndex(
        (elem) => elem.code === curElement.code
      );
      this.targetList.splice(curPos, 1);

      const toPos = this.targetList.findIndex(
        (elem) => elem.code === toElementCode
      );
      const offset = isDragDown ? 1 : 0;
      this.targetList.splice(toPos + offset, 0, { ...curElement });
      this.emitChange();
    },
    dragStart(e, element) {
      this.dragStartPageY = e.pageY;
      this.curDragElement = element;
    },
    dragOver(e) {
      // console.log(e);
      this.isDragDown = e.pageY > this.dragStartPageY;
      if (e.target.className.includes("field-body")) {
        const curDropElement = this.isDragDown
          ? this.targetList.slice(-1)[0]
          : this.targetList[0];
        this.curDropElementCode = curDropElement.code;
        return;
      }

      const elementDom = this.getRelateElement(e.target);
      if (!elementDom) return;

      const fieldCode = this.getElementCode(elementDom.id);
      const curDropElement = this.getSiblingElement(fieldCode, 0);
      this.curDropElementCode = curDropElement.code;
    },
    dropInnerElement() {
      if (this.curDragElement.code === this.curDropElementCode) return;

      // 往下：target上一个位置
      // 往上：target下一个位置
      this.moveElementToElement(
        this.curDragElement,
        this.curDropElementCode,
        this.isDragDown
      );
    },
    dragEnd() {
      this.curDragElement = null;
      this.curDropElementCode = null;
      this.dragStartPageY = null;
    },
  },
};
</script>
