vuex2实现时间列表选择器

文章介绍了如何使用Vue.js自定义指令实现表格中的动态框选功能,包括单个点击和一次性点击的选择方式。通过监听鼠标事件,获取和处理坐标信息,动态更新选中单元格的样式和数据。同时,文章还详细解释了防止连续点击导致的选区连在一起的处理方法。
摘要由CSDN通过智能技术生成

目录

一、效果展示

二、代码分析

2.1、区域确定与坐标获取

2.2、单个点击与一次性点击

一、效果展示

 

  主要借助自定义指令实现。在表格的"td们"上面进行移动框选,有一次性框选和单个框选,去掉自定义指令里的clearTargetNodes()会连续td,连续框选后会获取对应的值,可以再进行组装。

二、代码分析

<template>
  <div>
    <table border="1" v-cellSelect="weekdayTimeData">
      <tr>
        <th rowspan="2">星期 / 时间</th>
        <th colspan="12">00:00 - 12:00</th>
        <th colspan="12">12:00 - 24:00</th>
      </tr>
      <tr>
        <th v-for="n in 24" :key="n">{{ n - 1 }}</th>
      </tr>
      <tr v-for="n in 7" :key="n" :data-weekday="n - 1">
        <th class="weekday-title">{{ weekdayTitle[n - 1] }}</th>
        <td v-for="m in 24" :key="m" :data-time="m - 1"></td>
      </tr>
    </table>
  </div>
</template>
<script>
export default {
  directives: {
    cellSelect: {
      bind(el, bindings) {
        el.addEventListener("mousedown", handleMouseDown, false);
        function handleMouseDown(e) {
          const tar = e.target;
          const tagName = tar.tagName.toLowerCase();
          const els = this;
          clearTargetNodes(els); //防止单个点击时"td"们连在一起。不加该函数,点击的"td"们会连在一起
          if (tagName === "td") {
            els.start = tar;
            const rowIndex = Number(els.start.parentNode.dataset.weekday);
            const columnIndex = Number(els.start.dataset.time);
            // ===========拿到移动开始时的最终坐标
            setWeekdayTimeData(els, rowIndex, columnIndex);
            addTargetNode(els, tar); //情况一:单个点击移动框选
            els.addEventListener("mousemove", handleMouseMove, false);
            els.addEventListener("mouseup", handleMouseUp, false);
          }
        }
        function handleMouseMove(e) {
          const tar = e.target;
          const tagName = tar.tagName.toLowerCase();
          const els = this;
          if (tagName === "td") {
            const startTarget = els.start;
            const endTarget = tar;
            const startRow = Number(startTarget.parentNode.dataset.weekday);
            const startColumn = Number(startTarget.dataset.time);
            const endRow = Number(endTarget.parentNode.dataset.weekday);
            const endColumn = Number(endTarget.dataset.time);
            // ===========拿到移动停止时的最终坐标
            const currentTargetNodes = getTargetNodes(
              els,
              startRow,
              startColumn,
              endRow,
              endColumn
            );
            // ============拿到起始到结束的所有td数组
            getTargetNodeDiff(els, els.targetNodes, currentTargetNodes);
          }
        }
        function getTargetNodes(els, startRow, startColumn, endRow, endColumn) {
          const { allRows } = els;
          const startR = startRow > endRow ? endRow : startRow;
          const startC = startColumn > endColumn ? endColumn : startColumn;
          const endR = startRow > endRow ? startRow : endRow;
          const endC = startColumn > endColumn ? startColumn : endColumn;
          const targetNodes = new Set(); //防止来回移动,造成冗余数据
          allRows.forEach((tr, rowIndex) => {
            if (rowIndex >= startR && rowIndex <= endR) {
              [...tr.querySelectorAll("td")].forEach((td, columnIndex) => {
                if (columnIndex >= startC && columnIndex <= endC) {
                  targetNodes.add(td);
                  setWeekdayTimeData(els, rowIndex, columnIndex);
                }
              });
            }
          });
          return targetNodes;
        }
        function getAllRows(el) {
          const oAllRows = el.querySelectorAll("tr"); //[tr, tr, tr, tr, tr, tr, tr, tr, tr]
          return [...oAllRows].reduce((prev, tr) => {
            if (tr.dataset.weekday) {
              prev.push(tr);
            }
            return prev;
          }, []);
        }
        function handleMouseUp() {
          const els = this;
          els.removeEventListener("mousemove", handleMouseMove, false);
          els.removeEventListener("mouseup", handleMouseUp, false);
        }
        el.allRows = getAllRows(el); //[tr, tr, tr, tr, tr, tr, tr],原本有9个,只取出带":data-weekday"的tr
        el.targetNodes = new Set();
        el.weekdayTimeData = bindings.value;
        //情况二:"一气呵成"点击移动框选
        function getTargetNodeDiff(els, targetNodes, currentTargetNodes) {
          // currentTargetNodes里面有,而targetNodes没有,就要增加
          currentTargetNodes.forEach((td) => {
            !targetNodes.has(td) && addTargetNode(els, td);
          });
          // targetNodes里面有,而currentTargetNodes没有,就要减少
          targetNodes.forEach((td) => {
            !currentTargetNodes.has(td) && removeTargetNode(els, td);
          });
        }
        function addTargetNode(el, target) {
          //框选完继续扩大面积
          el.targetNodes.add(target);
          target.classList.add("target");
        }
        function removeTargetNode(el, target) {
          //框选完缩小了面积
          el.targetNodes.delete(target);
          target.classList.remove("target");
        }
        function clearTargetNodes(el) {
          el.targetNodes.forEach((target) => {
            target.classList.remove("target");
          });
          el.targetNodes = new Set();
          el.weekdayTimeData = [];
        }
        function setWeekdayTimeData(el, weekday, time) {
          console.log(weekday, time, "el, weekday, time", el.weekdayTimeData);
          //结果:  [ {3: {0, 1, 2, 3}},{4:{0, 1, 2, 3}}]
          //参考: {0:[0,1,2,3]}===>星期一:00:00,01:00,02:00,03:00
          el.weekdayTimeData[weekday]
            ? el.weekdayTimeData[weekday].add(time)
            : (el.weekdayTimeData[weekday] = new Set([time]));
        }
      },
    },
  },
  data() {
    return {
      weekdayTimeData: [],
      weekdayTitle: [
        "星期一",
        "星期二",
        "星期三",
        "星期四",
        "星期五",
        "星期六",
        "星期日",
      ],
    };
  },
};
</script>
<style lang='less' scoped>
table {
  border-collapse: collapse;
  .weekday-title {
    width: 100px;
  }
  tr {
    height: 50px;
  }
  td {
    width: 50px;
    &.target {
      background-color: aqua;
    }
  }
}
</style>

2.1、区域确定与坐标获取

(1)给表格绑定自定义指令拿到它本身,按下时取到所有的td(框选总面积),见handleMouseDown()函数。

(2)防止单个点击时"td"们连在一起。不加该函数,点击的"td"们会连在一起,见clearTargetNodes()函数

(3)拿到移动开始时的最终坐标:rowIndex、columnIndex;

    拿到移动停止时的最终坐标:startRow、startColumn、endRow、endColumn

(4)拿到起始到结束的所有td数组,见getTargetNodes()函数,防止来回移动,造成冗余数据,原本有9个,只取出带":data-weekday"的tr

2.2、单个点击与一次性点击

情况一:单个点击移动框选

在handleMouseDown时,直接调用addTargetNode()函数

情况二:"一气呵成"点击移动框选

getTargetNodeDiff这个比较函数,就是要比较在原有框选区域上要扩大/缩小:

currentTargetNodes里面有,而targetNodes没有,就要增加==》框选完继续扩大面积addTargetNode()

targetNodes里面有,而currentTargetNodes没有,就要减少==》框选完缩小了面积removeTargetNode()

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
你可以使用第三方插件 `vue-scroll-picker` 来实现滚动时间选择器效果。以下是使用步骤: 1. 安装 `vue-scroll-picker` 插件: ``` npm install vue-scroll-picker --save ``` 2. 在需要使用的组件中引入 `vue-scroll-picker`: ```javascript import VueScrollPicker from 'vue-scroll-picker'; ``` 3. 在模板中使用 `vue-scroll-picker`: ```html <vue-scroll-picker :data="data" v-model="selectedValue"> <template slot="item" slot-scope="{ item }"> {{ item.text }} </template> </vue-scroll-picker> ``` 其中,`data` 是一个数组,包含需要选择的时间范围,如: ```javascript data: [ { text: '00:00', value: '00:00' }, { text: '01:00', value: '01:00' }, { text: '02:00', value: '02:00' }, // ... { text: '23:00', value: '23:00' }, ] ``` `selectedValue` 是选中的时间值,可以在组件中通过 `v-model` 双向绑定。 4. 样式自定义 你可以通过 CSS 样式自定义滚动时间选择器。例如,以下是一个简单的样式示例: ```css .vue-scroll-picker { width: 100%; height: 200px; position: relative; overflow: hidden; margin: 0 auto; } .vue-scroll-picker-wrapper { width: 100%; height: 100%; position: absolute; top: 0; left: 0; display: flex; justify-content: center; align-items: center; } .vue-scroll-picker-item { font-size: 24px; line-height: 48px; color: #333; text-align: center; height: 48px; flex-shrink: 0; cursor: pointer; } ``` 以上就是使用 `vue-scroll-picker` 实现滚动时间选择器效果的简单步骤,你可以根据自己的需要进行进一步的样式和功能定制。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值