uniapp微信小程序vue3封装时间段范围选择组件

一、前言

目前uni-ui组件的日期选择组件,有日期选择、日期范围选择、日期时间选择、日期时间范围选择,就是没有时间段的选择,懒的去找第三方插件,而我的需求就是简单的取一个时间段的范围,就自己封装了。

二、最终效果

三、参数配置

1、代码示例

html 复制代码
<TTimeRange v-model="time" v-model:show="showPicker" @confirm="onConfirm" @cancel="showPicker=false" />

2、组件使用

html 复制代码
<TInput
    title="每日可装车时段"
     inputType="select"
     v-model:select-value="state.orderMsg.loadTime"
     @clickInput="showPicker=true"
   />
<TTimeRange v-model:show="showPicker" @confirm="onConfirm" @cancel="showPicker=false" />

四、组件源码

html 复制代码
<template>
  <view class="t_time-range-picker">
    <!-- 遮罩层 -->
    <view class="mask" v-if="show" @tap="cancel"></view>

    <!-- 选择器容器 -->
    <view class="picker-container" v-if="show">
      <view class="picker-header">
        <text class="btn cancel" @tap="cancel">取消</text>
        <text class="btn clear" v-if="isShowClear" @tap="clearSelection">清空</text>
        <text class="title">选择时间段</text>
        <text class="btn confirm" @tap="confirm">确定</text>
      </view>

      <!-- 时间选择器 -->
      <picker-view
        class="picker-view"
        :value="pickerValue"
        @change="onPickerChange"
        indicator-style="height: 60rpx;"
      >
        <!-- 开始时间-小时 -->
        <picker-view-column>
          <view class="picker-item" v-for="(h, index) in hours" :key="index">{{ h }}时</view>
        </picker-view-column>

        <!-- 开始时间-分钟 -->
        <picker-view-column>
          <view class="picker-item" v-for="(m, index) in minutes" :key="index">{{ m }}分</view>
        </picker-view-column>

        <view class="separator">至</view>

        <!-- 结束时间-小时 -->
        <picker-view-column>
          <view class="picker-item" v-for="(h, index) in hours" :key="index">{{ h }}时</view>
        </picker-view-column>

        <!-- 结束时间-分钟 -->
        <picker-view-column>
          <view class="picker-item" v-for="(m, index) in minutes" :key="index">{{ m }}分</view>
        </picker-view-column>
      </picker-view>
    </view>
  </view>
</template>

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

const props = defineProps({
  // 组件显示状态
  show: {
    type: Boolean,
    default: false
  },
  isShowClear: {
    type: Boolean,
    default: false
  },
  // 双向绑定的时间段值
  modelValue: {
    type: Object as () => { start: string; end: string },
    default: () => ({ start: "00:00", end: "00:00" })
  }
});

const emit = defineEmits(["update:modelValue", "confirm", "cancel"]);

// 生成小时数组 (00-23)
const hours = Array.from({ length: 24 }, (_, i) => i.toString().padStart(2, "0"));

// 生成分钟数组 (00-59)
const minutes = Array.from({ length: 60 }, (_, i) => i.toString().padStart(2, "0"));

// picker-view的当前索引值 [开始小时, 开始分钟, 结束小时, 结束分钟]
const pickerValue = ref<number[]>([0, 0, 0, 0]);

// 当前选择的时间段
const timeRange = reactive({
  start: props.modelValue.start,
  end: props.modelValue.end
});

// 将时间字符串转换为picker索引
const timeToIndex = (time: string): number[] => {
  const [hour, minute] = time.split(":").map(Number);
  return [
    hours.findIndex(h => h === hour?.toString().padStart(2, "0")),
    minutes.findIndex(m => m === minute?.toString().padStart(2, "0"))
  ];
};

// 初始化picker值
const initPicker = () => {
  const startIdx = timeToIndex(timeRange.start);
  const endIdx = timeToIndex(timeRange.end);
  pickerValue.value = [...startIdx, ...endIdx];
};

// 监听显示状态变化
watch(
  () => props.show,
  val => {
    if (val) initPicker();
  }
);

// 监听外部传入的值变化
watch(
  () => props.modelValue,
  val => {
    timeRange.start = val.start;
    timeRange.end = val.end;
  }
);

// 选择器变化事件
const onPickerChange = (e: any) => {
  const values: number[] = e.detail.value;

  // 获取新选择的时间
  const newStart = `${hours[values[0]]}:${minutes[values[1]]}`;
  const newEnd = `${hours[values[2]]}:${minutes[values[3]]}`;

  // 时间有效性校验
  if (newStart > newEnd) {
    // 如果开始时间晚于结束时间,自动交换
    timeRange.start = newEnd;
    timeRange.end = newStart;
    pickerValue.value = [...timeToIndex(newEnd), ...timeToIndex(newStart)];
  } else {
    timeRange.start = newStart;
    timeRange.end = newEnd;
    pickerValue.value = values;
  }
};

// 确认选择
const confirm = () => {
  emit("update:modelValue", { ...timeRange });
  emit("confirm", { ...timeRange });
};

// 取消选择
const cancel = () => {
  initPicker(); // 重置为原始值
  emit("cancel");
};
// 清空选择的方法
const clearSelection = () => {
  // const defaultTime = { start: "00:00", end: "00:00" };
  timeRange.start = "";
  timeRange.end = "";
  initPicker(); // 更新 pickerValue 到默认值
  emit("update:modelValue", { ...timeRange });
};
defineExpose({ cancel, clearSelection });
</script>

<style  lang="scss" scoped>
.t_time-range-picker {
  position: relative;
  z-index: 999;
  .mask {
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    background: rgba(0, 0, 0, 0.5);
  }
  .picker-container {
    position: fixed;
    bottom: 0;
    left: 0;
    right: 0;
    background: #fff;
    border-radius: 24rpx 24rpx 0 0;
    padding: 20rpx 0;
    .picker-header {
      display: flex;
      justify-content: space-between;
      align-items: center;
      padding: 20rpx 30rpx;
      border-bottom: 1rpx solid #eee;
      .title {
        font-size: 32rpx;
        font-weight: bold;
      }
      .btn {
        padding: 10rpx 30rpx;
        font-size: 32rpx;
      }

      .cancel {
        color: #101010;
      }
      .clear {
        color: #ff3b30;
      }

      .confirm {
        color: #007aff;
      }
    }
    .picker-view {
      height: 520rpx;
      margin-top: 20rpx;
      .picker-item {
        line-height: 50rpx;
        text-align: center;
        font-size: 30rpx;
      }
      .separator {
        position: absolute;
        left: 50%;
        top: 50%;
        transform: translate(-50%, -50%);
        font-size: 32rpx;
        color: #333;
      }
    }
  }
}
</style>

相关文章

基于ElementUi再次封装基础组件文档


vue3+ts基于Element-plus再次封装基础组件文档

相关推荐
拾光拾趣录10 分钟前
Element Plus表格表头动态刷新难题:零闪动更新方案
前端·vue.js·element
空の鱼1 小时前
js与vue基础学习
javascript·vue.js·学习
鱼樱前端1 小时前
2025前端SSR框架之十分钟快速上手Nuxt3搭建项目
前端·vue.js
HYI2 小时前
naive-ui n-data-table 使用踩坑总结
vue.js
The_era_achievs_hero2 小时前
微信小程序141~150
微信小程序·小程序·notepad++
JosieBook2 小时前
【前端】Vue 3 页面开发标准框架解析:基于实战案例的完整指南
前端·javascript·vue.js
薄荷椰果抹茶3 小时前
前端技术之---应用国际化(vue-i18n)
前端·javascript·vue.js
墨苒孤3 小时前
【Vue】tailwindcss + ant-design-vue + vue-cropper 图片裁剪功能(解决遇到的坑)
vue.js·vue-croppper
结城3 小时前
使用位运算优化 Vue.js 应用:高效状态管理技巧
vue.js
初出茅庐的3 小时前
uniapp - AI 聊天页面布局的实现
前端·vue.js·uni-app