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

  • 需求背景
  • 解决效果
  • 视频效果
  • 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>

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Windows10系统卸载服务和删除服务

记录一下Windows10系统卸载服务和删除服务 最近在使用自己win电脑的时候 发现服务里存在很久之前就没有使用的应用&#xff0c;对应的文件夹也都已经删除了&#xff0c;但是在win服务里一直存在&#xff0c;不知道会不会影响性能&#xff0c;看着吧还是强迫自己删掉好一些&…

安防视频监控/视频融合/云存储EasyCVR页面数据显示不全该如何解决?

安防视频监控/视频集中存储/云存储/磁盘阵列EasyCVR平台可拓展性强、视频能力灵活、部署轻快&#xff0c;可支持的主流标准协议有国标GB28181、RTSP/Onvif、RTMP等&#xff0c;以及支持厂家私有协议与SDK接入&#xff0c;包括海康Ehome、海大宇等设备的SDK等。平台既具备传统安…

linux文件管理命令_切换创建复制移动删除查看修改

1.3 文件管理命令 1.3.1 cd&#xff1a;切换目录&#xff08;change directory&#xff09; cd 绝对路径/相对路径 # 根目录 [rootlocalhost ~]# cd / # 家目录 [rootlocalhost /]# cd [rootlocalhost /]# cd ~ # 父级目录 [rootlocalhost /]# cd .. # 返回上一次目录 [roo…

C++中的模板

模板概论 c提供了函数模板 (function template.) 所谓函数模板&#xff0c;实际上是建立一个通用函 数&#xff0c;其函数类型和形参类型不具体制定&#xff0c;用一个虚拟的类型来代表。这个通用函数 就成为函数模板。凡是函数体相同的函数都可以用这个模板代替&#xff0c;不…

人工智能_AI服务器安装清华开源_CHATGLM大语言模型_GLM-6B安装部署_人工智能工作笔记0092

看到的这个开源的大模型,很牛,~关键让我们自己也可以部署体验一把了,虽然不知道具体内部怎么构造的但是,也可以自己使用也挺好. 可以部署在自己的机器上也可以部署在云服务器上. 安装以后,是可以使用python代码进行提问,然后返回结果的,这样就可以实现我们自己的chat应用了, …

TiDB 在咪咕云原生场景下的实践

导读 咪咕是中国移动旗下的视频科技公司&#xff0c;门户系统是其核心业务之一。 为满足用户的多样化需求&#xff0c;咪咕计划对其数据库进行升级。 经过对中国主流国产数据库的测试评估后&#xff0c;咪咕选择了 TiDB&#xff0c;并成功将其落地于门户系统云化项目。 TiDB 为…

太阳能监控智慧杆供电系统

太阳能监控智慧杆系统工作时无需水、油、汽、燃料&#xff0c;只要有光就能发电的特点&#xff0c;是清洁、无污染的可再生能源&#xff0c;而且安装维护简单&#xff0c;使用寿命长&#xff0c;可以实现无人值守&#xff0c;倍受人们的青睐&#xff0c;是新能源的领头羊。近年…

【封装UI组件库系列】封装Button图标组件

封装UI组件库系列第四篇封装Button按钮组件 &#x1f31f;前言 &#x1f31f;封装Button组件 1.分析封装组件所需支持的属性与事件 支持的属性&#xff1a; 支持的事件&#xff1a; 2.创建Button组件 &#x1f31f;封装功能属性 type主题颜色 plain是否朴素 loading等…

鸿蒙开发学习——应用程序框架

文章目录 UIAbility的生命周期Create状态WindowStageCreateForeground和Background前后台展示控制onWindowStageDestroyDestory 总结 UIAbility的生命周期 感觉这里他讲的不清晰&#xff0c;UIAbility的4个声明周期是Create、Foreground&#xff08;桌面展示&#xff09;、Back…

kali linux nmap 端口扫描 简单教程

本次实验所用工具如下&#xff1a; VMwarekali linux (namp扫描工具)Windows sever 2016 需开启&#xff08;FTP&#xff0c;smp&#xff0c;Telnet&#xff0c;rdp&#xff09;端口namp操作所用部分代码&#xff1a; -sP ping 扫描 -P 指定端口范围 -sV 服务版本探测 -A …

【UE】绘制抛物线并投射物体

效果 步骤 1. 先新建父类为Actor的蓝图&#xff0c;这里命名为“BP_发射物” 打开“BP_发射物”&#xff0c;添加一个球形的静态网格体和一个发射物移动组件 2. 新建一个父类为角色的蓝图&#xff0c;这里命名为“BP_绘制抛物线” 打开“BP_绘制抛物线” 我们希望可以通过控制…

2023.11.28-电商平台建设03 - 大数据调优手段

1.优化手段 1.1分桶表 HIVE的分桶本质上就是MR的分区操作 建表语句: create table 表名(字段 类型,.... ) clustered by(分桶字段) [sorted by (字段 [asc | desc])] into N buckets --- 定义分桶表核心语句 row format...... 分桶的作用 1) 进行数据采样工作 1.1) …

【古月居《ros入门21讲》学习笔记】14_参数的使用与编程方法

目录 说明&#xff1a; 1. 参数模型&#xff08;全局字典&#xff09; 2. 实现过程&#xff08;C&#xff09; 创建功能包 参数命令行的使用 YAML参数文件 rosparam命令 使用示例 编程方法&#xff08;C&#xff09; 配置代码编译规则 编译并运行 编译 运行 3. 实…

五丶openlayer之LOD和分辨率

LOD是Levels of Detail的简写&#xff0c;用于根据当前的环境&#xff0c;渲染不同的图像&#xff0c;用于降低非重要的细节度&#xff0c;从而提高渲染效率&#xff0c;在电子游戏中经常运用&#xff0c;对于需要显示全球地图的GIS系统而言&#xff0c;更需要应用这项技术。 …

Docker+Anaconda+CUDA+cuDNN

一、导语 因为要复现文献的需求和实验室里师兄想要给我提升能力的多方面因素在一起&#xff0c;所以学习并实现了相关安装。在这里做一个记录&#xff0c;方便日后查看&#xff0c;如果能给其他同学带来便捷就更好了。 在这篇文章中&#xff0c;我的目标是搭建一个可以使用Py…

VSCode Vue 开发环境配置

Vue是前端开发中的重要工具与框架&#xff0c;可以保住开发者高效构建用户界面。 Vue2官方文档&#xff1a;https://v2.cn.vuejs.org/ Vue3官方文档&#xff1a;https://cn.vuejs.org/ Vue的安装和引用 Vue2的官方安装指南&#xff1a;https://v2.cn.vuejs.org/v2/guide/ins…

github新建项目

参考链接&#xff1a;Github上建立新项目超详细方法过程 在这里新建一个repositories 接下来就选择相关的信息&#xff1a; 然后create a new就行了 接下来需要创建文件&#xff1a;&#xff08;同时通过upload上传文件&#xff09; 每次最多上传100个文件&#xff0c;然后保…

WEB渗透—反序列化(八)

Web渗透—反序列化 课程学习分享&#xff08;课程非本人制作&#xff0c;仅提供学习分享&#xff09; 靶场下载地址&#xff1a;GitHub - mcc0624/php_ser_Class: php反序列化靶场课程&#xff0c;基于课程制作的靶场 课程地址&#xff1a;PHP反序列化漏洞学习_哔哩哔_…

11.27二叉查找树,遍历二叉树,层序(判断是不是完全二叉树),根据遍历序列重构二叉树,递归输入建树(树的定义,结构体细节,typedef)

如果left<right&#xff0c;就表明其之间还有元素&#xff0c;即左右指针重合&#xff0c;区间只有一个元素也被包含其中&#xff1b; left<right,就表明递归过程中&#xff0c;只允许区间有两个及以上的元素&#xff0c;不允许区间只有一个元素&#xff0c;那么对应地&…

【前端】浅谈async/await异步传染性

文章目录 概述观点无法解决可以解决 来源 概述 "异步传染性"问题通常是指&#xff0c;当一个函数使用了async和await&#xff0c;其调用者也需要使用async和await处理异步操作&#xff0c;导致整个调用链都变成异步的。这种情况可能导致代码变得更复杂&#xff0c;不…