vue3+ts 实现时间间隔选择器

news2025/4/6 6:43:58

  • 需求背景
  • 解决效果
  • 视频效果
  • balancedTimeElement.vue

需求背景

实现一个分片的时间间隔选择器,需要把显示时间段显示成图表,涉及一下集中数据转换

  • [“02:30-05:30”,“07:30-10:30”,“14:30-17:30”]
  • ‘[(2,5),(7,10),(14,17)]’
  • [4, 5, 6, 7, 8, 9, 10, 14, 15, 16, 17, 18, 19, 20, 28, 29, 30, 31, 32, 33, 34]

解决效果

在这里插入图片描述

视频效果

时间间隔选择器

balancedTimeElement.vue

<!--/**
   * @author: liuk
   * @date: 2023/11/28
   * @describe: 时间间隔选择器
   * @CSDN:https://blog.csdn.net/hr_beginner?type=blog
  */-->
<template>
  <div>
    <div class="hours-container">
      <div class="hours-item-header-box">
        <div class="hours-item-header" v-for="(_, i) in hours.slice(0,24)" :key="i">{{
            (i + 1 + '').padStart(2, 0)
          }}
        </div>
      </div>
      <div class="hours-item-box" ref="hoursItemRef">
        <template v-for="(_, i) in hours" :key="i">
          <div class="hours-item" :class="compClass(i)" @click="handleClick(i)" @mouseover="handleHover(i)"></div>
        </template>
      </div>
    </div>
    <div class="tips">提示: 向右选中,向左取消选择</div>
  </div>
</template>

<script lang="ts" setup>
import {reactive, toRefs, ref, watch} from "vue";

// Props
const props = defineProps(['manual_period'])

// Emits
const emit = defineEmits(['data-passed'])

// Ref
const hoursItemRef = ref(null)

type numOrstr = number | string
type arrOrstr = any[] | string

interface ModelType {
  hours: number[]
  selectStart: boolean
  startIndex: numOrstr
  timeRangeList: string[]
  timeRangeListIndex: numOrstr[]
  tempRangeIndex: number[]
  tips: arrOrstr
}

const model: ModelType = reactive({
  hours: new Array(48).fill('').map((_, i) => i),
  selectStart: false,// 开始
  startIndex: '',// 开始下标
  timeRangeList: [],// 选择的时间段
  timeRangeListIndex: [],// 选中的下标
  tempRangeIndex: [],// 预选下标
  tips: '',

})
const {
  hours,
  selectStart,
  startIndex,
  timeRangeList,
  timeRangeListIndex,
  tempRangeIndex,
  tips,
} = toRefs(model)

watch(() => props.manual_period, (data) => {//'[(2,5),(7,10),(14,17)]'
  const str = data.replace(/\(|\)/g, (val) => { // '[[2,5],[7,10],[14,17]]'
    switch (val) {
      case "(":
        return "["
      case ")":
        return "]"
    }
  })
  model.timeRangeListIndex = JSON.parse(str).map(item => {
    const [x, y] = item
    return new Array(2 * y - 2 * x + 1).fill(0).map((_, i) => i + 2 * x)
  }).flat()//  [4, 5, 6, 7, 8, 9, 10, 14, 15, 16, 17, 18, 19, 20, 28, 29, 30, 31, 32, 33, 34]
  Array.from(hoursItemRef.value.children).forEach((dom: HTMLDivElement, index) => {
    if (model.timeRangeListIndex.includes(index)) {
      dom.className += ' selected'
    }
  })
})

// 下标区间转换成时间区间
const transformedSection = () => {
  model.timeRangeList = [];
  let startTime = '', endTime = '', len = model.hours.length;
  for (let index = model.hours[0] * 2; index < 2 * (len + 1); index++) {
    if (model.timeRangeListIndex.indexOf(index) > -1) {
      if (startTime) {// 如果有开始时间,直接确定结束时间
        let endHour = Math.floor((index + 1) / 2);
        let endMin = (index + 1) % 2 === 0 ? "00" : "30";
        endTime = `${endHour < 10 ? '0' + endHour : endHour}:${endMin}`;
      } else {// 没有开始时间,确定当前点为开始时间
        let startHour = Math.floor(index / 2);
        let startMin = index % 2 === 0 ? "00" : "30";
        startTime = `${startHour < 10 ? '0' + startHour : startHour}:${startMin}`;
      }
      if (index === 2 * model.hours.length + 1) { // 如果是最后一格,直接结束
        endTime = `${Math.floor((index + 1) / 2)}:00`;
        model.timeRangeList.push(`${startTime ? startTime : "23:30"}-${endTime}`);
        startTime = '';
        endTime = '';
      }
    } else { // 若这个点不在选择区间,确定一个时间段
      if (startTime && endTime) {
        model.timeRangeList.push(`${startTime}-${endTime}`);
        startTime = '';
        endTime = '';
      } else if (startTime && !endTime) {// 这里可能只选半个小时
        let endHour = Math.floor(index / 2);
        let endMin = index % 2 === 0 ? "00" : "30";
        endTime = `${endHour < 10 ? '0' + endHour : endHour}:${endMin}`;
        model.timeRangeList.push(`${startTime}-${endTime}`);
        startTime = '';
        endTime = '';
      }
    }
  }
  model.tips = model.timeRangeList && model.timeRangeList.length > 0 ? model.timeRangeList : '';
  emit('data-passed', model.tips);
}

// 点击事件
const handleClick = (index) => {
  if (model.selectStart) {
    if (index === model.startIndex) {// 双击取反
      if (model.timeRangeListIndex.indexOf(index) > -1) {
        model.timeRangeListIndex.splice(model.timeRangeListIndex.indexOf(index), 1);
      } else {
        model.timeRangeListIndex.push(model.startIndex);
      }
    } else if (index > model.startIndex) {// 选取数据--向右添加,向左取消
      while (index >= model.startIndex) {
        model.timeRangeListIndex.push(model.startIndex);
        model.startIndex = +model.startIndex + 1;
      }
      model.timeRangeListIndex = Array.from(new Set(model.timeRangeListIndex));
    } else {// 删除数据
      while (model.startIndex >= index) {
        if (model.timeRangeListIndex.indexOf(index) > -1) {
          model.timeRangeListIndex.splice(model.timeRangeListIndex.indexOf(index), 1);
        }
        index++;
      }
    }
    model.startIndex = '';
    transformedSection();
    model.tempRangeIndex = [];
  } else {
    model.startIndex = index;
  }
  model.selectStart = !model.selectStart;
}
// 预选区间
const handleHover = (index) => {
  if (model.selectStart) {
    model.tempRangeIndex = [];
    if (index > model.startIndex) {// 选取数据--向右添加,向左取消
      while (index >= model.startIndex) {
        model.tempRangeIndex.push(index);
        index--;
      }
    } else {// 删除数据
      while (model.startIndex >= index) {
        model.tempRangeIndex.push(index);
        index++;
      }
    }
  }
}
// 是否选中,计算className
const compClass = (index) => {
  if (index === model.startIndex) {
    return 'hours-item-left preSelected';
  }
  if (index >= model.startIndex) {
    if (model.tempRangeIndex.indexOf(index) > -1) {
      return 'hours-item-left preSelected';
    }
  } else {
    if (model.tempRangeIndex.indexOf(index) > -1) {
      return 'hours-item-left unSelected';
    }
  }
  return model.timeRangeListIndex.indexOf(index) > -1 ? 'hours-item-left selected' : 'hours-item-left';
}
</script>

<style lang="scss" scoped>
.hours-container {
  cursor: pointer;
  color: slategray;

  .hours-item-header-box {
    display: flex;
    width: 100%;
    height: 30px;

    .hours-item-header {
      width: 30px;
      height: 30px;
      text-align: center;
      box-sizing: border-box;
      line-height: 30px;
      border: 1px solid #5a5a5a;
      border-left: none;
      border-bottom: none;

      &:first-child {
        border-left: 1px solid #5a5a5a;
      }
    }
  }

  .hours-item-box {
    display: flex;
    width: 100%;

    .hours-item {
      width: 15px;
      height: 30px;
      border: 1px solid #474747;
      box-sizing: border-box;

      &.selected {
        background-color: #ffbf00 !important;
        border-bottom: 1px solid #c2d0f3;
      }

      &.preSelected {
        background-color: rgb(255, 191, 0);
        border-bottom: 1px solid #c2d0f3;
      }

      &.unSelected {
        background-color: #ffffff;
        border-bottom: 1px solid #c2d0f3;
      }
    }
  }
}

.tips {
  width: 100%;
  line-height: 30px;
  margin-top: 10px;
}

</style>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1268340.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

SpringBoot项目发送邮件

&#x1f4d1;前言 本文主要是【SpringBoot】——SpringBoot项目发送邮件的文章&#xff0c;如果有什么需要改进的地方还请大佬指出⛺️ &#x1f3ac;作者简介&#xff1a;大家好&#xff0c;我是听风与他&#x1f947; ☁️博客首页&#xff1a;CSDN主页听风与他 &#x1f3…

【人工智能Ⅰ】实验2:遗传算法

实验2 遗传算法实验 一、实验目的 熟悉和掌握遗传算法的原理、流程和编码策略&#xff0c;理解求解TSP问题的流程并测试主要参数对结果的影响&#xff0c;掌握遗传算法的基本实现方法。 二、实验原理 旅行商问题&#xff0c;即TSP问题&#xff08;Traveling Salesman Proble…

每日一题 2336. 无限集中的最小数字(中等)

感觉就是模拟它的两个过程就行了啊 class SmallestInfiniteSet:def __init__(self):self.small 1self.delset set()def popSmallest(self) -> int:ans self.smallself.delset.add(self.small)while self.small in self.delset:self.small 1return ansdef addBack(self,…

MATLAB | 官方举办的动图绘制大赛 | 第三周赛情回顾

MATHWORKS官方举办的迷你黑客大赛第三期(MATLAB Flipbook Mini Hack)的最新进展&#xff01;&#xff01; 很荣幸前三周都成为了阶段性获奖者~&#xff1a; https://ww2.mathworks.cn/matlabcentral/communitycontests/contests/6/entries/13382 https://ww2.mathworks.cn/mat…

展开运算符(...)

假如我们有一个数组&#xff1a; const arr [7,8,9];● 我们如果想要数组中的元素&#xff0c;我们必须一个一个手动的去获取&#xff0c;如下&#xff1a; const arr [7,8,9]; const badNewArr [5, 6, arr[0], arr[1],arr[2]]; console.log(badNewArr);● 但是通过展开运…

计算机基础知识61

JsonResponse 功能例子 你自己写一个类&#xff0c;实现JsonResponse 功能&#xff0c;不需要传safeFalse&#xff0c;无论字典或列表&#xff0c;都能完成序列化返回给前端 1 响应头例子 四种情况&#xff0c;在响应头返回数据 xxxx # 第一种情况 JsonResponse def show(req…

软件测试项目大全,看你需要哪一个

软件测试是使用人工或者自动的手段来运行或者测定某个软件系统的过程&#xff0c;其目的在于检验它是否满足规定的需求或弄清预期结果与实际结果之间的差别。 在软件投入使用前&#xff0c;要经过一系列的严格测试&#xff0c;才能保证交付质量。 一、引言 1.编写目的 本文档…

Nat. Mach. Intell. | 预测人工智能的未来:在指数级增长的知识网络中使用基于机器学习的链接预测

今天为大家介绍的是来自Mario Krenn团队的一篇论文。一个能够通过从科学文献中获取洞见来建议新的个性化研究方向和想法的工具&#xff0c;可以加速科学的进步。一个可能受益于这种工具的领域是人工智能&#xff08;AI&#xff09;研究&#xff0c;近年来科学出版物的数量呈指数…

Kong处理web服务跨域

前言 好久没写文章了&#xff0c;大概有半年多了&#xff0c;这半年故事太多&#xff0c;本文写不下&#xff0c;就写写文章标题问题&#xff01; 问题描述 关于跨域的本质问题我这里不过多介绍&#xff0c;详细请看历史文章 跨域产生的原因以及常见的解决方案。 我这边是新…

对二分搜索的理解 Go语言版

二分搜索大家都很熟悉&#xff0c;首先我们先来看看基本框架 func binarySearch(nums []int, target int) int {left, right : 0, ...for ... {mid : left (right-left)/2if nums[mid] target {...} else if nums[mid] < target {left ...} else if nums[mid] > targ…

【Pytorch】Visualization of Feature Maps(4)——Saliency Maps

学习参考来自 Saliency Maps的原理与简单实现(使用Pytorch实现)https://github.com/wmn7/ML_Practice/tree/master/2019_07_08/Saliency%20Maps Saliency Maps 原理 《Deep Inside Convolutional Networks: Visualising Image Classification Models and Saliency Maps》&…

如何设置Linux终端提示信息

如何设置Linux终端提示信息 1 方法一&#xff1a;只能在VSCode或者Pycharm显示2 方法二&#xff1a;只能在MobaXterm等远程软件上显示&#xff0c;但全用户都会显示3 方法三&#xff1a;避免用户没看到上面的提示&#xff0c;上面两种都设置一下 在使用远程终端时&#xff0c;由…

基于Qt QChart和QChartView实现正弦、余弦、正切图表

# 源码地址 https://gitcode.com/m0_45463480/QChartView/tree/main# .pro QT += charts​​HEADERS += \ chart.h \ chartview.h​​SOURCES += \ main.cpp \ chart.cpp \ chartview.cpp​​target.path = $$[QT_INSTALL_EXAMPLES]/charts/zoomlinechartINSTAL…

L1-004:计算摄氏温度

题目描述 给定一个华氏温度F&#xff0c;本题要求编写程序&#xff0c;计算对应的摄氏温度C。计算公式&#xff1a;C5(F−32)/9。题目保证输入与输出均在整型范围内。 输入格式&#xff1a;输入在一行中给出一个华氏温度。 输出格式&#xff1a;在一行中按照格式“Celsius C”…

如何使用录屏软件在电脑录制PDF文件

我有一个PDF文件&#xff0c;想用录屏软件将它录制下来并添加上详细的注释&#xff0c;然后发给客户看&#xff0c;请问应该如何录制呢&#xff1f;有没有推荐的录屏软件呢&#xff1f; 不用担心&#xff0c;本文将会详细的为您讲解如何使用录屏软件在电脑端录制PDF文件&#…

GoLang切片

一、切片基础 1、切片的定义 切片&#xff08;Slice&#xff09;是一个拥有相同类型元素的可变长度的序列它是基于数组类型做的一层封装它非常灵活&#xff0c;支持自动扩容切片是一个引用类型&#xff0c;它的内部结构包含地址、长度和容量声明切片类型的基本语法如下&#…

Mac单独修改应用语言

方法1: 方法2: defaults write com.microsoft.Excel AppleLanguages ("zh-cn") defaults write com.microsoft.Word AppleLanguages ("zh-cn")参考&#xff1a;https://www.zhihu.com/question/24976020

Javaweb之Vue组件库Element案例的详细解析

4.4 案例 4.4.1 案例需求 参考 资料/页面原型/tlias智能学习辅助系统/首页.html 文件&#xff0c;浏览器打开&#xff0c;点击页面中的左侧栏的员工管理&#xff0c;如下所示&#xff1a; 需求说明&#xff1a; 制作类似格式的页面 即上面是标题&#xff0c;左侧栏是导航&…

vue高频面试题(2023),有回答思路,并且让你回答清晰

一、对MVC&#xff0c;MVP&#xff0c;MVVM的理解 三者都是项目的架构模式&#xff08;不是类的设计模式&#xff09;&#xff0c;即&#xff1a;一个项目的结构&#xff0c;如何分层&#xff0c;不同层负责不同的职责。 1、MVC&#xff1a; MVC的出现是用在后端&#xff08;…

SpringMVC—拦截器

1 拦截器概念 1.1 简介 拦截器是一种动态拦截方法调用的机制&#xff0c;在 SpringMVC 中动态拦截控制器方法的执行 【注】拦截器底层实现为AOP 作用&#xff1a; 在指定的方法调用前后执行预先设定的代码阻止原始方法的执行 1.2 拦截器和过滤器的区别 ① 归属不同&#…