vue项目使用jquery实现在table表格鼠标框选、单选、Ctrl+鼠标左键多选反选功能

737 阅读2分钟

我曾仰慕的身姿,从相伴到相知。为你提笔写名诗···


摸鱼小故事

你:你人哪?

你:都快八点了,你怎么还没来。

老板:老子卖了十几年的煎饼,从来都是无拘无束自由自在,自从认识了你,第一次让我有了上班的感觉😊。

进入正题,看效果。


先看一下效果

src=http___img.mp.itc.cn_upload_20170419_83f1351054dc493e812a4e81832da4d5.jpg&refer=http___img.mp.itc.gif

sorry 放错了重来 hshpg-amky9.gif

所有代码

为了方便操作dom,在vue项目引入jquery即可应用。

<template>
  <div class="hello">
    <table cellspacing="0" id="gridtable" align="center">
      <!-- 表头 -->
      <tr class="tabHead">
        <th class="tabTh" width="4%">
          时间
        </th>
        <th class="tabTh" width="4%" v-for="(item, index) in tableLists" :key="index">
          {{ item.name }}
        </th>
      </tr>
      <tr>
      <tr v-for="(item, index) in regionListRow" :key="index">
        <!-- 时间列 -->
        <td class="dayDataTd">
          {{item.dates}}时
        </td>
        <td class="dayDataList" id="weekList" v-for="(temp, key) in tableLists" :key="key">
          {{item[temp.key]}}
        </td>
      </tr>
    </table>
    <!-- 弹出 -->
    <ul class="handleBatch">
      <li class="handleList" @click="reCheckOpinion()">
        一号
      </li>
      <li class="handleList" @click="reCheckOpinion()">
        二号
      </li>
      <li class="handleList" @click="reCheckOpinion()">
        三号
      </li>
      <li class="handleList" @click="reCheckOpinion()">
        四号
      </li>
    </ul>
  </div>
</template>
<script>
import $ from 'jquery'
export default {
  name: 'HelloWorld',
  data () {
    return {
      tableLists: [], // 表头
      regionListRow: [], // tab数据
      selectedList: [] // 选中集合
    }
  },
  created () {
    this.tableLists = [
      {
        name: 'PM₁₀(μg/m³)',
        code: '104',
        key: 'pm10'
      },
      {
        name: 'PM₂.₅(μg/m³)',
        code: '105',
        key: 'pm25'
      },
      {
        name: 'SO₂(μg/m³)',
        code: '100',
        key: 'so2'
      },
      {
        name: 'NO₂(μg/m³)',
        code: '101',
        key: 'no2'
      },
      {
        name: 'CO(mg/m³)',
        code: '103',
        key: 'co'
      }
    ]
    this.regionListRow = new Array(24).fill().map((item, index) => {
      return { id: '1000' + index, dates: '2022-3-10 ' + (index + 1), co: '80', no2: '20', so2: '2', pm25: '10', pm10: '23' }
    })
  },
  mounted () {
    const atPresent = {} // 按下位置记录
    // 按下事件
    $('#gridtable').mousedown((event) => {
      // ctrl + 左
      // console.log(event.target.className)
      if (!(event.button === 0 && event.ctrlKey && event.target.className.indexOf('dayDataList') !== -1)) {
        this.removeStyle() // 清除数据
      }
      if (event.target.className.indexOf('dayDataList') !== -1) {
        const link = $("<span class='boxChoose'></span>").css({
          border: '1px solid #666',
          'background-color': 'rgba(47, 170, 228, 0.39)',
          position: 'absolute',
          top: event.pageY,
          left: event.pageX
        })
        $('#gridtable').append(link)
        atPresent.atPresentX = event.pageX
        atPresent.atPresentY = event.pageY
      }
    })
    // 当前事件位置
    $('#gridtable').mousemove((e) => {
      // const pageX = e.pageX - parseInt(atPresent.atPresentX)
      // const pageY = e.pageY - parseInt(atPresent.atPresentY)
      // $('.boxChoose').css({ height: pageY, width: pageX })

      const pageX = e.pageX - parseInt(atPresent.atPresentX)
      const pageY = e.pageY - parseInt(atPresent.atPresentY)
      if (pageX >= 0 && pageY >= 0) {
        $('.boxChoose').css({
          width: pageX,
          height: pageY
        })
      } else if (pageX <= 0 && pageY <= 0) {
        $('.boxChoose').css({
          width: Math.abs(pageX),
          height: Math.abs(pageY),
          top: e.pageY,
          left: e.pageX
        })
      } else if (pageX <= 0) {
        $('.boxChoose').css({
          width: Math.abs(pageX),
          height: Math.abs(pageY),
          left: e.pageX
        })
      } else {
        $('.boxChoose').css({
          width: Math.abs(pageX),
          height: Math.abs(pageY),
          top: e.pageY
        })
      }
    })
    // 抬起事件
    $('#gridtable').mouseup((e) => {
      // 获取元素
      const blockList = $('#gridtable .dayDataList')
      const boxChoose = $('.boxChoose')[0]
      if (!boxChoose) return
      // 框定位信息
      const boxChooseLeft = boxChoose.style.left.split('px')[0]
      const boxChooseRight = Number(boxChooseLeft) + Number(boxChoose.style.width.split('px')[0])
      const boxChooseTop = boxChoose.style.top.split('px')[0]
      const boxChooseBottom = Number(boxChooseTop) + Number(boxChoose.style.height.split('px')[0])
      // 批量
      for (let i = 0; i < blockList.length; i++) {
        // 计算每个格子的定位信息
        const left = $(blockList[i]).offset().left
        const right = $(blockList[i]).width() + left
        const top = $(blockList[i]).offset().top
        const bottom = $(blockList[i]).height() + top
        // 判断每个块是否被遮罩盖住(即选中)
        const oneLine = boxChooseLeft >= left && right >= boxChooseRight
        const leftFlag = boxChooseLeft <= left && left <= boxChooseRight
        const rightFlag = boxChooseLeft <= right && right <= boxChooseRight

        const single = boxChooseTop >= top && bottom >= boxChooseBottom
        const topFlag = boxChooseTop <= top && top <= boxChooseBottom
        const bottomFlag = boxChooseTop <= bottom && bottom <= boxChooseBottom
        if ((oneLine || leftFlag || rightFlag) && (single || topFlag || bottomFlag)) {
          let cow = true
          this.selectedList.forEach((element, index) => {
            if (element === blockList[i]) {
              $(blockList[i]).removeClass('treeBoxStyles')
              this.selectedList.splice(index, 1)
              cow = false
            }
          })
          if (cow) {
            $(blockList[i]).addClass('treeBoxStyles')
            this.selectedList.push(blockList[i])
          }
        }
      }
      // console.log(this.selectedList)
      // 显示弹出
      $('.handleBatch').css({
        display: 'block',
        top: $(window).height() - $('.handleBatch').height() > e.pageY ? e.pageY : e.pageY - $('.handleBatch').height(),
        left: Number(boxChoose.style.left.split('px')[0]) + Number(boxChoose.style.width.split('px')[0]) + 30
      })
      $('.boxChoose').remove()
      // 批量操作显示
    })
    // 点击表格意外删除
    $(window).click(e => {
      console.log(e.target.className)
      if (e.target.className.indexOf('dayDataList') === -1) {
        this.removeStyle()
      }
    })
  },
  methods: {
    // 清楚样式
    removeStyle () {
      this.selectedList = []
      $('.boxChoose').remove()
      $('.handleBatch').css({ display: 'none' })
      $('#gridtable .dayDataList').removeClass('treeBoxStyles')
    },
    reCheckOpinion () {
      const dataResult = []
      for (let i = 0; i < this.selectedList.length; i++) {
        const column = $(this.selectedList[i].parentNode).prevAll().length - 2 // 行索引
        const line = $(this.selectedList[i].cellIndex)[0] - 1 // 列索引
        dataResult.push({
          ids: this.regionListRow[column].id,
          date: this.regionListRow[column].dates,
          code: this.tableLists[line].code,
          name: this.tableLists[line].name
        })
      }
      console.log('请求数据', dataResult)
    }
  }
}
</script>

<style>
.hello {
  -moz-user-select: none; /*火狐*/
  -webkit-user-select: none; /*webkit浏览器*/
  -ms-user-select: none; /*IE10*/
  -khtml-user-select: none; /*早期浏览器*/
  width: 100%;
  height: 100%;
  background-color: rgba(255, 255, 255, 0.582);
}
#gridtable {
  user-select: none;
  margin-top: 30px;
  height: 90%;
  width: 1000px;
  border-top: 1px solid #ccc;
  border-left: 1px solid #ccc;
}
.tabHead {
  background-color: #f3f3f3;
}
.tabTh {
  border-right: 1px solid #ccc;
  font-weight: 400;
}
.dayDataTd {
  border-right: 1px solid #ccc;
  border-bottom: 1px solid #ccc;
}
.dayDataList {
  margin: 0;
  padding: 0;
  text-align: center;
  border-right: 1px solid #ccc;
  border-bottom: 1px solid #ccc;
  cursor: pointer;
}
/* 背景颜色 */
.treeBoxStyles {
  background-color: #00cdff !important;
}
.handleBatch {
  transition: 1s;
  z-index: 999;
  position: absolute;
  top: 0;
  width: 140px;
  border: 1px solid rgb(163, 163, 163);
  margin: 0;
  padding: 0;
  cursor: pointer;
  display: none;
  background-color: #f0f0f0c7;
  border-radius: 3px;
}
.handleBatch li {
  list-style: none;
  color: rgb(114, 113, 113);
  transition: 0.3s;
  line-height: 20px;
  font-size: 15px;
  padding: 5px 5px;
  text-align: center;
  border-bottom: 1px solid #ccc;
}
.handleBatch li:hover {
  background-color: rgb(224, 223, 223);
}
</style>

说一下实现思路

使用jq,不代表只能用jq实现。主要是思路有了,实现举不胜举。 1.首先要知道三个事件鼠标按下mousedown鼠标移动mousemove鼠标抬起mouseupQQ截图20220310121542.png 当鼠标按下的时候,创建jq创建一个元素,获取鼠标距离浏览器坐标位置,赋值给元素top、left
当鼠标移动的时候,获取鼠标距离浏览器坐标位置,持续给元素的宽高赋值。
当鼠标抬起的时候,记录当前元素 距离浏览器坐标位置和元素宽高
这三个事件触发后,就可以获取到创建元素盒子的top,left,right,bottom。
2. 获取表格内所有小格子 image.png 获取每一个小各子距离top,left,right,bottom。
3.计算符合要求的小格子

  • 横轴判断
    image.png
    看一下,我们框选小格子的时候有这四种情况满足要求
  • 竖轴判断 竖轴也是一样的道理。
  • 计算公式
    const oneLine = 框Left >= 格left && 格right >= 框Right
    const leftFlag = 框Left <= 格left && 格left <= 框Right
    const rightFlag = 框Left <= 格right && 格right <= 框Right
    const single = 框Top >= 格top && 格bottom >= 框Bottom
    const topFlag = 框Top <= 格top && 格top <= 框Bottom
    const bottomFlag = 框Top <= 格bottom && 格bottom <= 框Bottom
    
    if ((oneLine || leftFlag || rightFlag) && (single || topFlag || bottomFlag)) {
        // 满足的元素
    }

QQ截图20220218150329.png image.png


3.获取小格子对于的行列索引

const column = $(元素.parentNode).prevAll().length - 2 // 行索引
const line = $(元素.cellIndex)[0] - 1 // 列索引

拿到索索引后,就可以通过索引查询数组中对于的数据了。


a061da211d452cc32dd2a58e16ffa764.gif