Vue 拖选表格: 模拟排班功能

演示

在线访问: https://wenkil.github.io/drag_table_demo (请使用PC端打开)

预览

源码

<template>
  <div>
    <div style="margin: 30px 0; text-align: right;">
      <el-button @click="reset" class="reset-btn">重置</el-button>
      <!--      <el-button type="primary" @click="changeGrid">确认修改</el-button>-->
    </div>
    <div class="box" @mousedown="handleMouseDown">
      <div class="mask" v-show="is_show_mask"
           :style="'width:'+mask_width+'left:'+mask_left+'height:'+mask_height+'top:'+mask_top">
      </div>
      <table border="1" cellspacing="0" class="table">
        <th v-for="(item,index) in weekEnum" class="week-data-th" :key="index">
          {{ item }}
        </th>
        <tr v-for="(item,index) in list">
          <td
              v-for="(temp,idx) in item.child"
              @click="handleClickTimeData(temp,index)"
              :key="idx"
              class="week-data-td"
              :style="`background:${temp.checked?'#3af':''}`"
          >
            <p>{{ temp.val }}</p>
          </td>
        </tr>
      </table>
    </div>
  </div>
</template>


<script>
export default {
  components: {},
  props: {
    tHead: {
      type: Array,
      default: () => []
    },
    tBody: {
      type: Array,
      default: () => []
    }
  },
  data() {
    return {
      is_show_mask: false,
      start_x: 0,
      start_y: 0,
      end_x: 0,
      end_y: 0,
      box_screen_left: 0,
      box_screen_top: 0,
      weekEnum: [
        "星期一",
        "星期二",
        "星期三",
        "星期四",
        "星期五",
        "星期六",
        "星期日"
      ],
      list: [],
      tdLength: 30
    };
  },
  computed: {
    // 框选操作
    mask_width() {
      return `${Math.abs(this.end_x - this.start_x)}px;`;
    },


    mask_height() {
      return `${Math.abs(this.end_y - this.start_y)}px;`;
    },


    mask_left() {
      return `${
          Math.min(this.start_x, this.end_x) - this.box_screen_left
      }px;`;
    },


    mask_top() {
      return `${
          Math.min(this.start_y, this.end_y) - this.box_screen_top
      }px;`;
    }
  },
  mounted() {
    window.addEventListener("scroll", this.windowScroll);//监听页面滚动
    this.initTable();
    this.initWidth();
  },
  beforeDestroy() {
    window.removeEventListener("scroll", this.windowScroll);
  },
  methods: {
    initWidth() {
      const dom_box = document.querySelector(".box");
      this.box_screen_left = dom_box.getBoundingClientRect().left;
      this.box_screen_top = dom_box.getBoundingClientRect().top;
      console.log("this.box_screen_left", this.box_screen_left);
      console.log("this.box_screen_top", this.box_screen_top);
    },
    //页面滚动时初始化位置
    windowScroll() {
      this.resSetXY();
      this.initWidth();
    },
    initTable() {
      let tableList = [...new Array(this.tdLength).keys()];
      console.log("tableList", tableList);
      this.list = tableList.map((item, index) => {
        let temp = Array.from(new Array(7).keys());
        console.log("temp", temp);
        let child = temp.map((v, key) => {
          return {
            key: key + index * 7,
            val: ""
          };
        });
        console.log("child", child);
        return { child };
      });
      console.log("this.list", this.list);
    },


    reset() {
      let tableList = JSON.parse(JSON.stringify(this.list));
      tableList.map((item) => {
        item.child.map((temp) => {
          temp.checked = false;
        });
      });
      this.list = tableList;
    },


    handleClickTimeData(obj, index) {
      let tableList = JSON.parse(JSON.stringify(this.list));
      console.log("tableList", tableList);
      tableList[index].child.map((item) => {
        if (item.key == obj.key) {
          item.checked = !item.checked;
        }
      });
      this.list = tableList;
    },


    changeGrid() {
      this.handleDomSelect();//选中后的判断,找到框选内的格子
      this.resSetXY();//调用此方法让框选消失
    },


    handleMouseDown(e) {
      console.log("event", e);
      this.is_show_mask = true;
      this.start_x = e.clientX;
      this.start_y = e.clientY;
      this.end_x = e.clientX;
      this.end_y = e.clientY;
      window.addEventListener("mousemove", this.handleMouseMove);
      window.addEventListener("mouseup", this.handleMouseUp);
    },
    handleMouseMove(event) {
      this.end_x = event.clientX;
      this.end_y = event.clientY;
    },
    handleMouseUp() {
      window.removeEventListener("mousemove", this.handleMouseMove);
      window.removeEventListener("mouseup", this.handleMouseUp);
      this.changeGrid();
    },
    resSetXY() {
      this.start_x = 0;
      this.start_y = 0;
      this.end_x = 0;
      this.end_y = 0;
    },
    handleDomSelect() {
      const dom_mask = window.document.querySelector(".mask");
      const rect_select = dom_mask.getClientRects()[0];
      console.log("rect_select", rect_select);


      let selectKeys = [];
      document.querySelectorAll(".week-data-td").forEach((node, index) => {
        const rects = node.getClientRects()[0];
        console.log("rects", rects);
        if (this.collide(rects, rect_select) === true) {
          selectKeys.push(index);
        }
      });
      console.log("selectKeys", selectKeys);
      if (selectKeys.length < 2) return;
      let tableList = JSON.parse(JSON.stringify(this.list));
      tableList = tableList.map((item, key) => {
        let child = item.child.map((temp) => {
          if (selectKeys.indexOf(temp.key) > -1) {
            temp.checked = !temp.checked;
          }
          return temp;
        });
        return { child };
      });
      this.list = tableList;
      console.log("this.list", this.list);
    },
    collide(rect1, rect2) {
      const maxX = Math.max(rect1.x + rect1.width, rect2.x + rect2.width);
      const maxY = Math.max(rect1.y + rect1.height, rect2.y + rect2.height);
      const minX = Math.min(rect1.x, rect2.x);
      const minY = Math.min(rect1.y, rect2.y);
      if (maxX - minX <= rect1.width + rect2.width && maxY - minY <= rect1.height + rect2.height) {
        return true;
      }
      else {
        return false;
      }
    }


  }
};
</script>


<style lang="scss" scoped>
td {
  width: 300px;
  text-align: center;
}


.box {
  width: 100%;
  height: auto;
  margin: 20px auto;
  position: relative;
  user-select: none;


  .table {
    width: 100%;
  }


  .mask {
    position: absolute;
    background: #409eff;
    opacity: 0.4;
  }
}


.week-data-th {
  min-width: 100px;
  height: 30px;
}


.week-data-td {
  height: 50px;
}
</style>