<template>
  <main
    ref="canvas"
    class="canvas"
    :style="[
      {
        backgroundImage: data.background_image_url ? `url(${data.background_image_url})` : '',
        backgroundSize: data.background_image_url
          ? `${data.background_position.width}% ${data.background_position.height}%`
          : '',
        backgroundPosition: data.background_image_url
          ? `${data.background_position.left}% ${data.background_position.top}%`
          : '',
      },
    ]"
  >
    <template v-if="isEdit">
      <span class="drag-line" data-row="-1"></span>
      <!-- 行 -->
      <template v-for="(row, i) in data.row_list">
        <div :key="i" class="row" :style="getMultipleColStyle(i)">
          <!-- 列 -->
          <template v-for="(col, j) in row.col_list">
            <span
              v-if="j === 0"
              :key="`start-${i}-${j}`"
              class="drag-vertical-line col-start"
              :data-row="i"
              :data-col="j"
            ></span>
            <div :key="j" :class="['col', `col-${i}-${j}`]" :style="computedColStyle(col)">
              <!-- 网格 -->
              <template v-for="(grid, k) in col.grid_list">
                <span
                  v-if="k === 0"
                  :key="`start-${i}-${j}-${k}`"
                  class="drag-line grid-line drag-row"
                  :style="{ gridArea: 'grid-top' }"
                  :data-row="i"
                  :data-col="j"
                  :data-grid="k"
                  data-type="row"
                ></span>
                <span
                  :key="`left-${i}-${j}-${k}`"
                  :style="{ gridArea: `left-${grid.grid_area}` }"
                  :data-row="i"
                  :data-col="j"
                  :data-grid="k"
                  data-type="col-left"
                  class="drag-vertical-line grid-line grid-line-left"
                ></span>
                <component
                  :is="componentList[grid.type]"
                  :id="grid.id"
                  ref="component"
                  :key="`grid-${grid.id}`"
                  :class="[grid.id]"
                  :style="{ gridArea: grid.grid_area, height: grid.height, marginTop: grid.row !== 1 ? '16px' : '0' }"
                  :delete-component="deleteComponent(i, j, k)"
                  :component-move="componentMove(i, j, k)"
                  @showSetting="showSetting"
                  @changeData="changeData"
                />
                <span
                  :key="`right-${i}-${j}-${k}`"
                  :style="{ gridArea: `right-${grid.grid_area}` }"
                  :data-row="i"
                  :data-col="j"
                  :data-grid="k + 1"
                  data-type="col-right"
                  class="drag-vertical-line grid-line grid-line-right"
                ></span>
                <span
                  v-if="k === col.grid_list.length - 1"
                  :key="`end-${i}-${j}-${k}`"
                  class="drag-line grid-line drag-row"
                  :style="{ gridArea: `grid-bottom` }"
                  :data-row="i"
                  :data-col="j"
                  :data-grid="k + 1"
                  data-type="row"
                ></span>
              </template>
            </div>
            <span :key="`end-${i}-${j}`" class="drag-vertical-line col-end" :data-row="i" :data-col="j + 1"></span>
          </template>
        </div>
        <span v-if="i < data.row_list.length - 1" :key="`row-${i}`" class="drag-line" :data-row="i"></span>
      </template>
      <span class="drag-line" :data-row="data.row_list.length - 1"></span>
    </template>

    <PreviewEdit v-else :courseware-id="courseware_id" :row-list="data.row_list" @computedMoveData="computedMoveData" />
  </main>
</template>

<script>
import { getRandomNumber } from '@/utils/index';
import { componentList } from '../../data/bookType';
import { SaveCoursewareContent, GetCoursewareContent } from '@/api/book';

import PreviewEdit from './PreviewEdit.vue';

export default {
  name: 'CreateCanvas',
  components: {
    PreviewEdit,
  },
  inject: ['getCurSettingId'],
  props: {
    isEdit: {
      type: Boolean,
      required: true,
    },
  },
  data() {
    const { book_id, chapter_id } = this.$route.query;

    return {
      courseware_id: this.$route.params.courseware_id,
      book_id,
      chapter_id,
      data: {
        background_image_url: '',
        background_position: {
          width: 100,
          height: 100,
          top: 0,
          left: 0,
        },
        // 组件列表
        row_list: [],
      },
      curType: 'divider',
      componentList,
      curRow: -2,
      curCol: -1,
      curGrid: -1,
      gridInsertType: '', // 网格插入类型
      enterCanvas: false, // 是否进入画布
      // 拖拽状态
      drag: {
        clientX: 0,
        clientY: 0,
        dragging: false,
      },
    };
  },
  watch: {
    drag: {
      handler(val) {
        if (val.dragging) {
          const dragging = document.querySelector('.dragging');
          dragging.style.left = `${val.clientX}px`;
          dragging.style.top = `${val.clientY}px`;
        }
      },
      deep: true,
    },
    enterCanvas: {
      handler(val) {
        if (val) return;
        const dragLineList = document.querySelectorAll('.drag-line');
        dragLineList.forEach((item) => {
          item.style.opacity = 0;
        });
        this.curRow = -2;
        const dragVerticalLineList = document.querySelectorAll('.drag-vertical-line');
        dragVerticalLineList.forEach((item) => {
          item.style.opacity = 0;
        });
        this.curCol = -1;
        this.curGrid = -1;
        this.gridInsertType = '';
      },
    },
    'data.row_list': {
      handler(val) {
        // row_list 更改时判断是否有当前设置 id 的组件
        const curSettingId = this.getCurSettingId();
        const find = val.find((row) => {
          return row.col_list.find((col) => {
            return col.grid_list.find((grid) => grid.id === curSettingId);
          });
        });
        if (!find) {
          this.$emit('showSettingEmpty');
        }
      },
      immediate: true,
    },
  },
  created() {
    GetCoursewareContent({ id: this.courseware_id }).then(({ content }) => {
      if (content) {
        this.data = JSON.parse(content);
      }
      this.$watch(
        'data',
        () => {
          this.changeData();
        },
        {
          deep: true,
        },
      );
    });
  },
  mounted() {
    document.addEventListener('mousemove', this.dragMove);
    document.addEventListener('mouseup', this.dragEnd);
  },
  beforeDestroy() {
    document.removeEventListener('mousemove', this.dragMove);
    document.removeEventListener('mouseup', this.dragEnd);
  },
  methods: {
    changeData() {
      this.$emit('changeData');
    },
    /**
     * 保存课件内容
     * @param {string} type 类型
     */
    saveCoursewareContent(type) {
      let component_id_list = this.data.row_list.flatMap((row) =>
        row.col_list.flatMap((col) => col.grid_list.map((grid) => grid.id)),
      );

      this.$refs?.component.forEach((item) => {
        item.saveCoursewareComponentContent();
      });

      const loading = this.$loading({
        lock: true,
        text: '保存中...',
        spinner: 'el-icon-loading',
        background: 'rgba(0, 0, 0, 0.7)',
      });
      SaveCoursewareContent({
        id: this.courseware_id,
        category: 'NEW',
        content: JSON.stringify(this.data),
        component_id_list,
      }).then(() => {
        this.$message.success('保存成功');
        loading.close();
        if (type === 'quit') {
          this.$emit('back');
        }
        if (type === 'edit') {
          this.$emit('changeEditStatus');
        }
      });
    },
    setBackgroundImage(url, position) {
      this.data.background_image_url = url;
      this.data.background_position = position;

      this.$message.success('设置背景图成功');
    },
    /**
     * 显示设置
     * @param {object} setting
     * @param {string} type
     * @param {string} id
     * @param {object} params
     */
    showSetting(setting, type, id, params = {}) {
      this.$emit('showSetting', setting, type, id, params);
    },
    /**
     * 计算组件移动
     * @param {number} i 行
     * @param {number} j 列
     * @param {number} k 格子
     */
    componentMove(i, j, k) {
      return ({ type, offsetX, offsetY, id }) => {
        this.computedMoveData({ i, j, k, type, offsetX, offsetY, id });
      };
    },
    /**
     * 计算移动数据
     * @param {number} i 行
     * @param {number} j 列
     * @param {number} k 格子
     * @param {string} type 移动类型
     * @param {number} offsetX x 轴偏移量
     * @param {number} offsetY y 轴偏移量
     * @param {string} id 组件 id
     */
    computedMoveData({ i, j, k, type, offsetX, offsetY, id, min_width, min_height, row_width }) {
      const row = this.data.row_list[i];
      const col = row.col_list[j];
      const grid = col.grid_list[k];

      // 上下移动
      if (['top', 'bottom'].includes(type)) {
        // 高度为 auto 时
        if (grid.height === 'auto') {
          const gridHeight = document.querySelector(`.${id}`).offsetHeight;
          const height = gridHeight + offsetY;
          if (height < min_height) {
            return;
          }
          grid.height = `${gridHeight + offsetY}px`;
          col.grid_template_rows = `0 ${col.grid_list.map(({ height }) => height).join(' 16px ')} 0`;
          return;
        }
        // 高度为数字时
        const height = Number(grid.height.replace('px', ''));
        const _h = height + offsetY;
        if (_h < min_height) {
          return;
        }
        if (_h >= 50) {
          grid.height = `${_h}px`;
          col.grid_template_rows = `0 ${col.grid_list.map(({ height }) => height).join(' 16px ')} 0`;
        }
      }

      let gridList = col.grid_list.filter((item) => item.row === grid.row);
      // 一行中有多个格子
      if (gridList.length > 1) {
        let find = gridList.findIndex((item) => item.id === id);
        // 移动类型为 left 且不是第一个格子
        if (type === 'left' && find > 0) {
          const prevGrid = gridList[find - 1];
          const prevWidth = Number(prevGrid.width.replace('fr', ''));
          const width = Number(grid.width.replace('fr', ''));
          const max = prevWidth + width - 10;
          if (prevWidth + offsetX < 10 || prevWidth + offsetX > max || width - offsetX > max || width - offsetX < 10) {
            return;
          }
          // 计算拖动的距离与总宽度的比例
          const ratio = (offsetX / document.querySelector('.row').offsetWidth) * 100;

          const _w = width - ratio;
          if ((_w / 100) * row_width < min_width) {
            return;
          }
          col.grid_list[k - 1].width = `${prevWidth + ratio}fr`;
          grid.width = `${_w}fr`;
        }

        // 移动类型为 right 且不是最后一个格子
        if (type === 'right' && find < gridList.length - 1) {
          let nextGrid = gridList[find + 1];
          const nextWidth = Number(nextGrid.width.replace('fr', ''));
          const width = Number(grid.width.replace('fr', ''));
          const max = nextWidth + width - 10;
          if (nextWidth - offsetX < 10 || nextWidth - offsetX > max || width + offsetX > max || width + offsetX < 10) {
            return;
          }
          const ratio = (offsetX / document.querySelector('.row').offsetWidth) * 100;
          const _w = width + ratio;
          if ((_w / 100) * row_width < min_width) {
            return;
          }
          col.grid_list[k + 1].width = `${nextWidth - ratio}fr`;
          grid.width = `${width + ratio}fr`;
        }
        return;
      }
      // 移动类型为 left 且不是第一个格子
      if (type === 'left' && j > 0) {
        const prevGrid = row.width_list[j - 1];
        const prevWidth = Number(prevGrid.replace('fr', ''));
        const width = Number(row.width_list[j].replace('fr', ''));
        const max = prevWidth + width - 10;
        if (prevWidth + offsetX < 10 || prevWidth + offsetX > max || width - offsetX > max || width - offsetX < 10) {
          return;
        }
        // 计算拖动的距离与总宽度的比例
        const ratio = (offsetX / document.querySelector('.row').offsetWidth) * 100;

        const _w = width - ratio;
        if ((_w / 100) * row_width < min_width) {
          return;
        }
        row.width_list[j - 1] = `${prevWidth + ratio}fr`;
        row.width_list[j] = `${width - ratio}fr`;
      }

      // 移动类型为 right 且不是最后一个格子
      if (type === 'right' && j < row.col_list.length - 1) {
        let nextGrid = row.width_list[j + 1];
        const nextWidth = Number(nextGrid.replace('fr', ''));
        const width = Number(row.width_list[j].replace('fr', ''));
        const max = nextWidth + width - 10;
        if (nextWidth - offsetX < 10 || nextWidth - offsetX > max || width + offsetX > max || width + offsetX < 10) {
          return;
        }
        const ratio = (offsetX / document.querySelector('.row').offsetWidth) * 100;
        const _w = width + ratio;
        if ((_w / 100) * row_width < min_width) {
          return;
        }
        row.width_list[j + 1] = `${nextWidth - ratio}fr`;
        row.width_list[j] = `${width + ratio}fr`;
      }
      this.$forceUpdate();
    },
    /**
     * 重新计算格子宽度
     * @param {number} i 行
     * @param {number} j 列
     */
    recalculateGridWidth(i, j) {
      let col = this.data.row_list[i].col_list[j];
      let grid_template_columns = '0';
      col.grid_list.forEach(({ width }, i) => {
        const w = `${width}`;
        if (i === col.grid_list.length - 1) {
          grid_template_columns += ` ${w} 0`;
        } else {
          grid_template_columns += ` ${w} `;
        }
      });
      col.grid_template_columns = grid_template_columns;
    },
    /**
     * 删除组件
     * @param {number} i 行
     * @param {number} j 列
     * @param {number} k 格子
     */
    deleteComponent(i, j, k) {
      return () => {
        const gridList = this.data.row_list[i].col_list[j].grid_list;
        let delRow = gridList[k].row; // 删除的 grid 的 row
        let delW = gridList[k].width; // 删除的 grid 的 width
        gridList.splice(k, 1);

        // 如果删除后没有 grid 了则删除列
        const colList = this.data.row_list[i].col_list[j];
        if (colList.grid_list.length === 0) {
          this.data.row_list[i].col_list.splice(j, 1);
          let width_list = this.data.row_list[i].width_list;
          const delW = width_list[j];
          width_list.splice(j, 1);
          this.data.row_list[i].width_list = width_list.map((item) => {
            return `${Number(item.replace('fr', '')) + Number(delW.replace('fr', '') / width_list.length)}fr`;
          });
        }

        // 如果删除后没有列了则删除行
        if (this.data.row_list[i].col_list.length === 0) {
          this.data.row_list.splice(i, 1);
        }

        // 如果删除后还有 grid 则重新计算 grid 的 row 和 width
        if (gridList?.length > 0) {
          let delNum = gridList.filter(({ row }) => row === delRow).length;
          let diff = Number(delW.replace('fr', '')) / delNum;
          if (delNum === 0) {
            // 删除 grid 后面的 row 都减 1
            gridList.forEach((item) => {
              if (item.row > delRow) {
                item.row -= 1;
              }
            });
          } else {
            gridList.forEach((item) => {
              if (item.row === delRow) {
                item.width = `${Number(item.width.replace('fr', '')) + diff}fr`;
              }
            });
          }
        }
      };
    },
    computedColStyle(col) {
      let grid = col.grid_list;

      let maxCol = 0; // 最大列数
      let rowList = new Map();
      grid.forEach(({ row }) => {
        rowList.set(row, (rowList.get(row) || 0) + 1);
      });
      let curMaxRow = 0; // 当前数量最大 row 的值
      rowList.forEach((value, key) => {
        if (value > maxCol) {
          maxCol = value;
          curMaxRow = key;
        }
      });
      // 计算 grid_template_areas
      let gridStr = '';
      let gridArr = [];
      grid.forEach(({ grid_area, row }) => {
        if (!gridArr[row - 1]) {
          gridArr[row - 1] = [];
        }
        if (curMaxRow === row) {
          gridArr[row - 1].push(`left-${grid_area} ${grid_area} right-${grid_area}`);
        } else {
          let filter = grid.filter((item) => item.row === row);
          let find = filter.findIndex((item) => item.grid_area === grid_area);
          let needNum = (maxCol - filter.length) * 3; // 需要的数量

          let str = '';
          if (filter.length === 1) {
            str = ` ${grid_area} `.repeat(needNum + 1);
          } else {
            let arr = this.splitInteger(needNum, filter.length);
            str = arr[find] === 0 ? ` ${grid_area} ` : ` ${grid_area} `.repeat(arr[find] + 1);
          }
          gridArr[row - 1].push(`left-${grid_area} ${str} right-${grid_area}`);
        }
      });
      gridArr.forEach((item) => {
        gridStr += `'${item.join(' ')}' `;
      });

      // 计算 grid_template_columns
      let gridTemCols = '';
      let max = { row: 0, num: 0 };
      grid.forEach(({ row }) => {
        // 计算出 row 的哪个值最多
        let len = grid.filter((item) => item.row === row).length;
        if (max.num < len) {
          max.num = len;
          max.row = row;
        }
      });
      grid.forEach((item) => {
        if (item.row === max.row) {
          gridTemCols += `${item.width} 8px 8px `;
        }
      });

      // 计算 grid_template_rows
      let gridTemplateRows = '';
      // 将 grid 按照 row 分组
      let gridMap = new Map();
      grid.forEach((item) => {
        if (!gridMap.has(item.row)) {
          gridMap.set(item.row, []);
        }
        gridMap.get(item.row).push(item.height);
      });
      gridMap.forEach((value) => {
        if (value.length === 1) {
          gridTemplateRows += `${value[0]} `;
        } else {
          let isAllAuto = value.every((item) => item === 'auto'); // 是否全是 auto
          gridTemplateRows += isAllAuto ? 'auto ' : `max(${value.join(', ')}) `;
        }
      });

      return {
        width: col.width,
        gridTemplateAreas: `'${'grid-top '.repeat(maxCol * 3)}' ${gridStr} '${'grid-bottom '.repeat(maxCol * 3)}'`,
        gridTemplateColumns: `0 ${gridTemCols.slice(0, gridTemCols.length - 8)} 0`,
        gridTemplateRows: `0 ${gridTemplateRows} 0`,
      };
    },
    /**
     * 分割整数为多个 3 的倍数
     * @param {number} num
     * @param {number} parts
     */
    splitInteger(num, parts) {
      let base = Math.floor(num / parts / 3) * 3;
      let arr = Array(parts).fill(base);
      let remainder = num - base * parts;
      for (let i = 0; remainder > 0; i = (i + 1) % parts) {
        arr[i] += 3;
        remainder -= 3;
      }
      return arr;
    },
    /**
     * 拖拽开始
     * 用点击模拟拖拽
     * @param {MouseEvent} event
     * @param {string} type
     */
    dragStart(event, type) {
      // 获取鼠标位置
      const { clientX, clientY } = event;
      document.body.style.userSelect = 'none'; // 禁止选中文本
      this.drag.dragging = true;
      this.curType = type;
      // 在鼠标位置创建一个拖拽元素
      const dragging = document.createElement('div');
      dragging.className = 'dragging';
      this.drag.clientX = clientX;
      this.drag.clientY = clientY;
      document.body.appendChild(dragging);
    },
    /**
     * 鼠标移动
     */
    dragMove(event) {
      if (!this.drag.dragging) return;

      const { clientX, clientY } = event;
      this.drag.clientX = clientX;
      this.drag.clientY = clientY;

      let { isInsideCanvas } = this.getMarginDifferences();

      this.enterCanvas = isInsideCanvas;
      if (!isInsideCanvas) return;

      this.showRecentLine(clientX, clientY);
    },
    /**
     * 显示最近的线
     * @param {number} clientX
     * @param {number} clientY
     */
    showRecentLine(clientX, clientY) {
      const dragLineList = document.querySelectorAll('.drag-line'); // 获取所有的横线
      const dragVerticalLineList = document.querySelectorAll('.drag-vertical-line'); // 获取所有的竖线
      let minDistance = Infinity;
      let minIndex = -1;
      const list = [...dragLineList, ...dragVerticalLineList];

      list.forEach((item, index) => {
        const rect = item.getBoundingClientRect();
        const distance = Math.sqrt(
          Math.pow(clientX - rect.left - rect.width / 2, 2) + Math.pow(clientY - rect.top - rect.height / 2, 2),
        );
        if (distance < minDistance) {
          minDistance = distance;
          minIndex = index;
        }
      });
      list.forEach((item, index) => {
        if (index === minIndex) {
          this.curRow = Number(item.getAttribute('data-row'));
          this.curCol = Number(item.getAttribute('data-col') || -1);
          this.curGrid = Number(item.getAttribute('data-grid') || -1);
          this.gridInsertType = item.getAttribute('data-type') || '';

          item.style.opacity = 1;
        } else {
          item.style.opacity = 0;
        }
      });
    },

    /**
     * 鼠标松开
     */
    dragEnd() {
      document.body.style.userSelect = 'auto';
      const dragging = document.querySelector('.dragging');
      if (dragging) {
        document.body.removeChild(dragging);
        this.drag.dragging = false;
      }

      if (!this.isEdit) return;

      if (this.enterCanvas) {
        if (this.curRow >= -1 && this.curCol <= -1) {
          this.calculateRowInsertedObject();
        }

        if (this.curRow >= -1 && this.curCol > -1 && this.curGrid <= -1) {
          this.calculateColObject();
        }

        if (this.curRow >= -1 && this.curCol > -1 && this.curGrid > -1) {
          this.calculateGridObject();
        }
      }
      this.enterCanvas = false;
    },
    getMultipleColStyle(i) {
      let row = this.data.row_list[i];
      let col = row.col_list;
      if (col.length <= 1) {
        return {
          gridTemplateColumns: '0 100fr 0',
        };
      }
      let str = row.width_list
        .map((item) => {
          return `${item}`;
        })
        .join(' 16px ');
      let gridTemplateColumns = `0 ${str} 0`;

      return {
        gridAutoFlow: 'column',
        gridTemplateColumns,
        gridTemplateRows: 'auto',
      };
    },
    /**
     * 计算网格插入的对象
     */
    calculateGridObject() {
      const id = `ID-${getRandomNumber(12, true)}`;
      const letter = `L${getRandomNumber(6, true)}`;

      let row = this.data.row_list[this.curRow];
      let col = row.col_list[this.curCol];
      let grid = col.grid_list;
      let type = this.gridInsertType;

      if (type === 'row') {
        let rowNum = this.curGrid === 0 ? 1 : grid[this.curGrid - 1].row + 1;
        grid.splice(this.curGrid, 0, {
          id,
          grid_area: letter,
          width: '100fr',
          height: 'auto',
          row: rowNum,
          type: this.curType,
        });
        // 在新加入的 grid 后面的 row 都加 1
        grid.forEach((item, i) => {
          if (i > this.curGrid) {
            item.row += 1;
          }
        });
      }

      if (['col-left', 'col-right'].includes(type)) {
        let rowNum = grid[type === 'col-left' ? this.curGrid : this.curGrid - 1].row;
        grid.splice(this.curGrid, 0, {
          id,
          grid_area: letter,
          width: '100fr',
          height: 'auto',
          row: rowNum,
          type: this.curType,
        });

        let allRowNum = grid.filter(({ row }) => row === rowNum).length;
        let w = 0;
        grid.forEach((item, i) => {
          if (item.row === rowNum && i !== this.curGrid) {
            let width = Number(item.width.replace('fr', ''));
            let diff = width / allRowNum;
            item.width = `${width - diff}fr`;
            w += diff;
          }
        });
        grid[this.curGrid].width = `${w}fr`;
      }
    },
    /**
     * 计算列插入的对象
     */
    calculateColObject() {
      const id = `ID-${getRandomNumber(12, true)}`;
      const letter = `L${getRandomNumber(6, true)}`;

      let row = this.data.row_list[this.curRow];
      let col = row.col_list;

      let w = 0;
      row.width_list.forEach((item, i) => {
        let itemW = Number(item.replace('fr', ''));
        let rowW = itemW / (row.width_list.length + 1);
        w += rowW;
        row.width_list[i] = `${itemW - rowW}fr`;
      });
      row.width_list.splice(this.curCol, 0, `${w}fr`);

      col.splice(this.curCol, 0, {
        width: '100fr',
        height: 'auto',
        grid_list: [
          {
            id,
            grid_area: letter,
            row: 1,
            width: '100fr',
            height: 'auto',
            type: this.curType,
          },
        ],
      });
    },
    /**
     * 计算行插入的对象
     */
    calculateRowInsertedObject() {
      const id = `ID-${getRandomNumber(12, true)}`;
      const letter = `L${getRandomNumber(6, true)}`;

      this.data.row_list.splice(this.curRow + 1, 0, {
        width_list: ['100fr'],
        col_list: [
          {
            width: '100fr',
            height: 'auto',
            grid_list: [
              {
                id,
                grid_area: letter,
                width: '100fr',
                height: 'auto',
                row: 1,
                type: this.curType,
              },
            ],
          },
        ],
      });
    },
    /**
     * 获取拖拽元素和画布的边距差值
     * @returns {object} { leftMarginDifference, topMarginDifference, isInsideCanvas }
     * leftMarginDifference: 拖拽元素和画布左边距差值
     * topMarginDifference: 拖拽元素和画布上边距差值
     * isInsideCanvas: 是否在画布内
     */
    getMarginDifferences() {
      const rect1 = document.querySelector('.dragging').getBoundingClientRect();
      const rect2 = this.$refs.canvas.getBoundingClientRect();

      const leftMarginDifference = rect1.left - rect2.left + 128;
      const topMarginDifference = rect1.top - rect2.top + 72;

      let isInsideCanvas =
        leftMarginDifference > 0 &&
        leftMarginDifference < rect2.width &&
        topMarginDifference > 0 &&
        topMarginDifference < rect2.height;

      return { leftMarginDifference, topMarginDifference, isInsideCanvas };
    },

    // 获取子组件
    findChildComponentByKey(key) {
      return this.$children.find((child) => child.$vnode.key === key);
    },
  },
};
</script>

<style lang="scss" scoped>
.canvas {
  display: flex;
  flex-direction: column;
  row-gap: 6px;
  width: $courseware-width;
  min-height: calc(100% - 56px);
  padding: 24px;
  margin: 0 auto;
  background-color: #fff;
  background-repeat: no-repeat;
  border-radius: 4px;

  .row {
    display: grid;
    row-gap: 16px;

    > .drag-vertical-line:not(:first-child, :last-child, .grid-line) {
      left: 6px;
    }

    .drag-vertical-line.col-start {
      left: -12px;
    }

    .drag-vertical-line.col-end {
      right: -8px;
    }

    .col {
      display: grid;

      .drag-vertical-line:first-child {
        left: -8px;
      }

      .drag-vertical-line:not(:first-child, :last-child, .grid-line) {
        left: 6px;
      }

      .drag-vertical-line:last-child {
        right: -4px;
      }
    }
  }

  .drag-line {
    z-index: 2;
    width: calc(100% - 16px);
    height: 4px;
    margin: 0 8px;
    background-color: #379fff;
    border-radius: 4px;
    opacity: 0;

    &.drag-row {
      background-color: $right-color;
    }

    &.grid-line:not(:first-child, :last-child) {
      position: relative;
      top: 6px;
    }
  }

  .drag-vertical-line {
    position: relative;
    z-index: 2;
    width: 4px;
    height: 100%;
    background-color: #379fff;
    border-radius: 4px;
    opacity: 0;

    &.grid-line {
      background-color: #f43;
    }
  }
}
</style>

<style lang="scss">
.dragging {
  position: fixed;
  z-index: 999;
  width: 320px;
  height: 180px;
  background-color: #eaf5ff;
  border: 1px solid #b5dbff;
  border-radius: 4px;
  opacity: 0.5;
  transform: translate(-40%, -40%);
}
</style>
