vue2实现时间选择--点选

相信有一部分小伙伴会做到会议时间选择的业务,有的时候产品经理想让产品显得高级一点,会选择做成点选时间的样式。

需求:

 通过点选时间来选择时间范围

1.如果会议室的使用时间为当天--已经过去的时间不可以被选择;

2.要显示会议室已经被使用的时间并做出突出显示;

3.如果点选的起点与终点之间有被占用的时间需做出提示;

4.点选被占用的时间需给出提示;

5.点选不可用的时间块需要给出提示;

6.会议室可配置可用时间-->即会议室的可使用的开始与结束时间

7.鼠标移入被占用部分可展示使用详情

8.交互--点击选中时间段的中间部分时间段变为点击的块时间(15分钟)

9.交互--如果只有一个时间快选中,再次点击时间清空

本文章提供了两种格式的样式,一种为横向网格型选择器,一种为滚动选择,只需传入type即可切换

网格式:

 竖直滚动式:(可配置时间选择范围)

代码实现:

时间选择组件:meetingVue2.vue文件

<template>
  <div>
    <div>
      日期:{{ `${thismoment}---->${timeArea.start || '--:--'} 至 ${timeArea.end || '--:--'} ` }}
      <div>
        <div v-for="item, index in colorDes" :key="index" class="desBox">
          <div>{{ item.label }}</div>
          <div :class="`showColorBox ${item.class}`"></div>
        </div>
      </div>
    </div>

    <div v-if="type == 'column'">
      <div class="topTime">
        <div v-for="(item, index) in timeTopArry" :key="index">
          {{ item.index }}
        </div>
      </div>
      <div class="meeting-showTime">
        <div class="leftTime">
          <div v-for="(item, index) in timeLeftArry" :key="index">
            {{ item }}
          </div>
        </div>
        <div v-for="(item, index) in timeBoxArry" :key="index" :class="renderclass(item).class"
          @click="handleChoose(item)">
          <div class="tootal" v-if="showTootal && renderclass(item).text">{{ renderclass(item).text }}</div>
        </div>
      </div>
    </div>

    <div v-else class="meeting-time-selector">
      <div class="time-selector-label">
        <div v-for="(item, index) in leftTimeArray" class="time-div" :key="index">
          {{ item.title }}
        </div>
      </div>
      <div class="time-selector-checkbox">
        <div v-for="(item, index) in timeBoxArry" :key="index" :class="renderclass(item).class"
          @click="handleChoose(item)">
          <div class="tootal" v-if="showTootal && renderclass(item).text">{{ renderclass(item).text }}</div>
        </div>
      </div>

    </div>

  </div>
</template>

<script>
import moment from "moment";
import { ElMessage } from "element-plus";
export default {
  data() {
    return {
      timeBoxArry: [], //时间生成方块
      timeTopArry: [], //上方时间段
      timeLeftArry: ["0-15", "15-30", "30-45", "45-60"], //左侧时间段
      leftTimeArray: [],//竖直模式左侧时间段
      colorDes: [
        { label: '可用', class: 'green' },
        { label: '被占用', class: 'usecolor' },
        { label: '不可用', class: 'hui' },
        { label: '当前选择', class: 'blue-time' },
      ]
    };
  },
  props: {
    type: {//组件样式---row:滚动垂直选择;column:横向选择
      type: String,
      default: 'row'
    },
    useTime: {
      //会议室被占用的时间段
      type: Array,
      default: [
        // { start_time: "6:00", end_time: "7:15" }
      ],
    },
    thismoment: {
      //当前时间--当前会议室的使用日期
      type: String,
      default: moment().format("YYYY-HH-DD"),
    },
    timeArea: {
      //当前选择的时间段
      type: Object,
      default: () => ({
        start: "",
        end: "",
      }),
    },
    RoomCouldUseTime: {//会议室的可用时间
      type: Object,
      default: () => ({
        openTime: '00:00',
        closeTime: '24:00',
      }),
    },
    showTootal: {//是否展示被占用时间的tootal默认不展示
      type: Boolean,
      default: true,
    },
    disabled: {//点击选择禁用
      type: Boolean,
      default: false,
    },

    rowTimeRange: {//当类型为row时可配置时间范围
      type: Object,
      default: () => ({
        start: "01:00",
        end: "24:00",
      }),
    }

  },
  methods: {
    //生成时间段数组
    creatTime(startT, endT, interval) {
      let starTime = moment(startT, "HH:mm");
      let endTime = moment(endT, "HH:mm");
      let diff = endTime.diff(starTime, "minutes");
      let num = Math.ceil(diff / interval);
      var segs = [];
      for (let i = 1; i <= num; i++) {
        let timeFrom = starTime.clone().add((i - 1) * interval, "minutes");
        let timeTo = starTime.clone().add(i * interval, "minutes");
        let minutes = timeFrom.minutes();
        let hour = timeFrom.hour();
        if (timeTo.format("HH:mm") == "00:00") {
          //24:00整
          timeTo = moment("23:59", "HH:mm");
        }
        segs.push({
          timeFromtt: timeFrom.format("HH:mm"),
          timeTott: timeTo.format("HH:mm"),
          timeFrom: timeFrom,
          timeTo: timeTo,
          // seg: minutes === 0 ? true : false,
          // title: (hour < 12 ? "上午" : "下午") + hour + '点'
          title: timeTo.format("HH:mm") == "00:00" ? '24:00' : hour + ":00",
          index: i < 10 ? "0" + (i - 1) : i - 1,
        });
      }
      return segs;
    },

    //根据时间段返回类名
    renderclass(boxData) {
      let boxclass = "";
      let _value = {
        openTime: moment(this.RoomCouldUseTime.openTime, "HH:mm"),
        closeTime: moment(this.RoomCouldUseTime.closeTime, "HH:mm"),
      };

      //得到会议室可用时间 openTime --过去的时间置灰
      let momstart = moment(
        //当前会议室开始时间 --当天00时
        this.thismoment + " " + this.RoomCouldUseTime.openTime,
        "YYYY-MM-DD HH:mm"
      );

      let nowTime = moment();

      if (nowTime.isAfter(momstart)) {
        if (nowTime >= boxData.timeFrom && nowTime < boxData.timeTo) {
          _value.openTime = boxData.timeFrom;
        } else {
          _value.openTime = nowTime;
        }
      } else {
        _value.openTime = moment(this.RoomCouldUseTime.openTime, "HH:mm");
      }


      //根据被选择的时间段返回相应颜色与文字
      //已经被占用的时间段
      let text = ''
      this.useTime.forEach((item, index) => {
        //编辑时--放开当前使用时间
        // if (this.isexit && item.meeting_id == modalVisible.meetingId)
        //   return;
        let time_used = [
          moment(item.start_time, "HH:mm"),
          moment(item.end_time, "HH:mm"),
        ];
        //被使用的时间段返回灰色,边框颜色改变
        if (
          time_used[0].isBefore(boxData.timeTo) &&
          time_used[1].isAfter(boxData.timeFrom)
        ) {
          boxclass = "usecolor";
          text = item.detail
        }
      });

      //优先返回被使用的时间段
      if (boxclass === "usecolor") {
        return { class: `commen-box ${boxclass}`, text: text };
      }

      // 可使用的时间段颜色
      // 开始时间在会议室可用之间之后,并且在关闭时间之前
      // 开始时间在当前时间条之间
      // console.log(_value.openTime.format('YYYY-MM-DD HH:mm:ss'))
      if (
        _value.openTime.isSameOrBefore(boxData.timeFrom)
        && _value.closeTime.isAfter(boxData.timeFrom)
      ) {
        boxclass = "green";
      }

      //当前选择的时间段颜色
      if (
        moment(this.timeArea.start, "HH:mm").isBefore(boxData.timeTo) &&
        moment(this.timeArea.end, "HH:mm").isAfter(boxData.timeFrom)
      ) {
        boxclass = "blue-time";
      }
      return { class: `commen-box ${boxclass}`, text: '' };
    },

    //点击时间条
    handleChoose(row) {
      if (this.disabled) return
      let _value = {
        openTime: moment(this.RoomCouldUseTime.openTime, "HH:mm"),
        closeTime: moment(this.RoomCouldUseTime.closeTime, "HH:mm"),
      };
      let opentime = "";
      //过去的的时间不可选
      let momstart = moment(
        //当前会议室开始时间
        this.thismoment + " " + _value.openTime.format("HH:mm"),
        "YYYY-MM-DD HH:mm"
      );

      if (moment().isAfter(momstart)) {
        opentime = moment();
      } else {
        opentime = moment(_value.openTime, "HH:mm");
      }
      // console.log(opentime.format("HH:mm"));

      //是否选择的可预订的时间段
      if (row.timeFrom.isBefore(opentime) ||
        row.timeFrom.isAfter(moment(_value.closeTime, "HH:mm")) ||
        row.timeTo.isAfter(moment(_value.closeTime, "HH:mm"))) {
        ElMessage.error("请选择可预定的时间段");
        return;
      }


      let array = { ...this.timeArea };
      if (!this.timeArea.start) {
        array = {
          start: row.timeFromtt,
          end: row.timeTott,
        };
      } else if (this.timeArea.start) {
        if (moment(this.timeArea.start, "HH:mm").isAfter(row.timeFrom)) {
          array = {
            start: row.timeFromtt,
            end: this.timeArea.end,
          };
        } else {
          array = {
            start: this.timeArea.start,
            end: row.timeTott,
          };
        }
      }

      //点击中间的条
      if (
        row.timeTo.isBefore(moment(this.timeArea.end, "HH:mm")) &&
        row.timeFrom.isAfter(moment(this.timeArea.start, "HH:mm"))
      ) {
        array = {
          start: row.timeFromtt,
          end: row.timeTott,
        };
      }

      //点击同一个条
      if (
        row.timeTott == this.timeArea.end &&
        row.timeFromtt == this.timeArea.start
      ) {
        array = {
          start: "",
          end: "",
        };
      }

      //判断时间段是否可用
      let pass = true;
      this.useTime.forEach((item, index) => {
        // if (this.isexit && item.meeting_id == modalVisible.meetingId)
        //   return;
        let time_used = [
          moment(item.start_time, "HH:mm"),
          moment(item.end_time, "HH:mm"),
        ];
        //占用时间段的开始时间在点击的时间段起始时间之前并且占用的结束时间在点击时间的开始时间之前 || 占用时间段的开始时间与时间条的相等
        if (
          (time_used[0].isBefore(row.timeFrom) &&
            time_used[1].isAfter(row.timeFrom)) ||
          (time_used[0].isSame(row.timeFrom) &&
            time_used[1].isAfter(row.timeFrom))
        ) {
          ElMessage.error("当前时间段已被选择");
          pass = false;
        }
        if (
          moment(array.start, "HH:mm").isBefore(time_used[0]) &&
          moment(array.end, "HH:mm").isAfter(time_used[1])
        ) {
          ElMessage.error("当前选择的时间段内有时间段已被选择");
          pass = false;
        }
      });
      if (pass) {
        // this.timeArea = array;
        this.$emit("changeTime", array);
      }
    },

  },
  mounted() {
    this.leftTimeArray = this.creatTime(this.rowTimeRange.start, this.rowTimeRange.end, 60);

    let _rowList = this.creatTime(this.rowTimeRange.start, this.rowTimeRange.end, 15)
    this.timeBoxArry = this.type == 'column' ? this.creatTime("00:00", "24:00", 15) : _rowList.slice(0, _rowList.length - 4);
    this.timeTopArry = this.creatTime("00:00", "24:00", 60);
  },
};
</script>

<style scoped lang="scss">
.commen-box {
  width: 20px;
  height: 20px;
  background: #ccc;
  margin-right: 2px;
  margin-bottom: 2px;
  cursor: pointer;
  position: relative;

  .tootal {
    width: 100px;
    // height: 30px;
    background-color: #fff;
    border-radius: 4px;
    position: absolute;
    top: 20px;
    right: 0;
    display: none;
    z-index: 99;
    box-shadow: 2px 1px 7px 1px #664646a8;
    padding: 5px;
  }

  &:hover .tootal {
    display: initial;
  }
}

.meeting-showTime {
  width: 580px;
  height: 100px;
  display: flex;
  flex-direction: column;
  flex-wrap: wrap;

  .leftTime {
    width: 45px;
    text-align: right;
    font-size: 14px;
    padding-right: 5px;
  }
}

.meeting-time-selector {
  margin-top: 20px;
  padding-right: 20px;
  display: flex;

  .commen-box {
    width: 400px;
    margin-bottom: 1px;

  }

  .time-selector-label {
    width: 61px;
    display: flex;
    flex-direction: column;
    justify-content: space-between;
    margin-top: -10px;
    margin-bottom: -11px;

    .time-div {
      position: relative;
      top: 1px;

      &::after {
        position: absolute;
        right: 0;
        top: 10px;
        content: "";
        width: 4px;
        height: 1px;
        background-color: #b5b5b8;
      }
    }
  }

  .time-selector-checkbox {
    .time-line-m {
      width: 458px;
      height: 24px;
      opacity: 0.56;
      background: #dedede;
      // margin-bottom: 1px;
      border-bottom: 2px solid #ccc;
    }
  }
}



.usecolor {
  background: rgb(205, 146, 121) !important;
}

.green {
  background-color: #9eeac4 !important;
  cursor: pointer;

  &:hover {
    opacity: 0.7;
  }
}

.blue-time {
  background-color: rgb(102, 132, 180) !important;
}

.hui {
  background: #ececec;
}

.desBox {
  display: flex;
  align-items: center;

  div:first-child {
    width: 60px;
    text-align: right;
  }

  .showColorBox {
    width: 30px;
    height: 10px;
    margin-left: 20px;
  }

}

.topTime {
  display: flex;
  width: 534px;
  justify-content: space-around;
  margin-left: 45px;
}
</style>

组件调用:

<script setup>
import { onMounted, ref, onUnmounted } from "vue";
import Meeting from "./meetingVue2.vue";

const useTime = ref([{ start_time: "6:00", end_time: "7:15", detail: '这时间被阿福占用了!' }]);
const timeArea = ref({
  start: "12:00",
  end: "16:00",
});
const changeTime = (data) => {
  // console.log(data);
  timeArea.value = data;
};
const thismoment = ref("2023-6-18");
const RoomCouldUseTime = ref({
  openTime: '05:00',
  closeTime: '24:00',
})
onMounted(() => { });

</script>

<template>
  <Meeting
  :RoomCouldUseTime="RoomCouldUseTime" 
  :useTime="useTime" @changeTime="changeTime" 
  :timeArea="timeArea"
  :thismoment="thismoment" 
  />
</template>

<style scoped></style>

希望对你有所帮助~

  • 12
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
要在Vue2中使用Vant实现时间选择器,需要先安装Vant组件库,然后在Vue组件中引入相关的时间选择器组件。 首先,在命令行中使用以下命令安装Vant: ``` npm install vant@next -S ``` 然后在Vue组件中引入相关的时间选择器组件,例如: ```vue <template> <div> <van-popup v-model="showPicker" position="bottom"> <van-datetime-picker ref="picker" type="datetime" :min-date="minDate" :max-date="maxDate" :value="currentDate" @confirm="onConfirm" /> </van-popup> <van-button type="primary" @click="showPicker = true">选择时间</van-button> </div> </template> <script> export default { data() { return { showPicker: false, currentDate: new Date(), minDate: new Date(2010, 0, 1), maxDate: new Date(2022, 11, 31), }; }, methods: { onConfirm(value) { this.currentDate = value; this.showPicker = false; }, }, }; </script> ``` 在上面的代码中,我们使用了Vant的`van-popup`组件来实现时间选择器的弹出框。我们设置了`position`为`bottom`,表示弹出框从底部弹出。我们还使用了`v-model`来控制弹出框的显示与隐藏。 在弹出框中,我们使用了`van-datetime-picker`组件来实现时间选择器。我们设置了组件的`type`为`datetime`,表示选择的是日期和时间。我们还设置了`min-date`和`max-date`来限制可时间范围。我们使用了`value`来设置当前的时间值。当用户点击确认按钮时,会触发`@confirm`事件,我们在`onConfirm`方法中将选择时间值赋给`currentDate`,并将弹出框隐藏起来。 最后,我们在页面上放置了一个按钮,当用户点击该按钮时,弹出框会从底部弹出,让用户可以选择时间。 以上就是使用Vant实现时间选择器的基本步骤。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值