当前位置: 首页 > news >正文

【前端开发】Uniapp日期时间选择器:实现分钟动态步长设置

技术栈

  • Uniapp + Vue3 + uView
  • 年份显示前后一年,分钟动态设置间隔

效果图

在这里插入图片描述

  1. 主体显示
    <view class="uni-row selector" @click="openPicker"><uni-icons color="#c0c4cc" type="calendar" size="22"></uni-icons><textclass="label":style="{ color: props.modelValue ? '#333' : '#999' }">{{ displayValue || placeholder }}</text></view>
  1. 底部弹窗
    <transition name="fade"><view v-if="showPicker" class="overlay" @click="closePicker"></view><view v-if="showPicker" class="picker-modal"><view class="title">{{ placeholder }}</view><view class="uni-row tab-container"><view:class="['tab', activeTab === 'date' ? 'active' : '']"@click="switchTab('date')">选择日期</view><view:class="['tab', activeTab === 'time' ? 'active' : '']"@click="switchTab('time')":style="{pointerEvents: dateConfirmed ? 'auto' : 'none',}">选择时间</view></view><picker-viewv-show="activeTab === 'date'"class="picker-view":indicator-style="'height: 50px;'":value="[yearIndex, monthIndex, dayIndex]"@change="onDateChange"><picker-view-column><view v-for="(y, i) in years" :key="i" class="picker-item">{{ y }}</view></picker-view-column><picker-view-column><view v-for="(m, i) in months" :key="i" class="picker-item">{{ m }}</view></picker-view-column><picker-view-column><view v-for="(d, i) in days" :key="i" class="picker-item">{{ d }}</view></picker-view-column></picker-view><picker-viewv-show="activeTab === 'time'"class="picker-view":indicator-style="'height: 50px;'":value="[hourIndex, minuteIndex]"@change="onTimeChange"><picker-view-column><view v-for="(h, i) in hours" :key="i" class="picker-item">{{ h }}</view></picker-view-column><picker-view-column><view v-for="(m, i) in minutes" :key="i" class="picker-item">{{ m }}</view></picker-view-column></picker-view><view class="picker-footer"><buttonv-if="activeTab === 'date'"class="btn-next"@click="goToTime">下一步</button><button v-else class="btn-confirm" @click="confirm">确定</button></view><view class="close-btn" @click="closePicker"></view></view></transition>
  1. 组件抛出
const props = defineProps({modelValue: {type: String,default: "",},placeholder: {type: String,default: "请选择时间",},minuteStep: {type: Number,default: 1,},
});
const emit = defineEmits(["update:modelValue"]);
  1. 年月列表项和默认值
const now = new Date();
const currentYear = now.getFullYear();
const currentMonth = now.getMonth() + 1;// 年份只显示3年
const years = [currentYear - 1, currentYear, currentYear + 1];
const months = Array.from({ length: 12 }, (_, i) => i + 1);// 默认选中Index
const yearIndex = ref(1);
const monthIndex = ref(currentMonth - 1);
  1. 时分列表项和默认值
const currentHour = now.getHours();
const currentMinute = now.getMinutes();const hours = Array.from({ length: 24 }, (_, i) => i);
const minutes = computed(() => {const step = props.minuteStep;return Array.from({ length: Math.floor(60 / step) }, (_, i) => i * step);
});// 默认选中Index
const hourIndex = ref(currentHour);
const minuteIndex = ref(Math.floor(currentMinute / props.minuteStep));
  1. 监听年月变化,更新天数
// 默认选中Index
const dayIndex = ref(currentDay - 1);
// 计算天数
const updateDays = () => {const y = years[yearIndex.value];const m = months[monthIndex.value];const dayCount = new Date(y, m, 0).getDate();days.value = Array.from({ length: dayCount }, (_, i) => i + 1);if (dayIndex.value >= dayCount) {dayIndex.value = dayCount - 1;}
};// 监听年月变化,更新天数
watch([yearIndex, monthIndex], updateDays);
  1. 初始化当天日期时间
onMounted(() => {updateDays();// 初始化选中项if (props.modelValue) {const reg = /(\d{4})年(\d{1,2})月(\d{1,2})日 (\d{1,2})时(\d{1,2})分/;const matched = props.modelValue.match(reg);if (matched) {const [_, y, mo, d, h, mi] = matched;const yNum = +y,moNum = +mo,dNum = +d,hNum = +h,miNum = +mi;const yi = years.indexOf(yNum);yearIndex.value = yi !== -1 ? yi : 1;monthIndex.value = moNum - 1;dayIndex.value = dNum - 1;hourIndex.value = hNum;minuteIndex.value = Math.floor(miNum / props.minuteStep);updateDays();}}
});
  1. 选项变化更新对应值
const onDateChange = (e) => {const [y, m, d] = e.detail.value;yearIndex.value = y;monthIndex.value = m;dayIndex.value = d;updateDays();
};const onTimeChange = (e) => {const [h, mm] = e.detail.value;hourIndex.value = h;minuteIndex.value = mm;
};
  1. 确定事件,抛出最新值
const confirm = () => {const y = years[yearIndex.value];const m = String(months[monthIndex.value]).padStart(2, "0");const d = String(days.value[dayIndex.value]).padStart(2, "0");const h = String(hours[hourIndex.value]).padStart(2, "0");const mm = String(minutes.value[minuteIndex.value]).padStart(2, "0");const val = `${y}-${m}-${d} ${h}:${mm}`;emit("update:modelValue", val);showPicker.value = false;
};
  1. 组件样式
<style lang="scss" scoped>
.time-box {width: 100%;.selector {width: 100%;border: 1px solid #eee;border-radius: 10rpx;padding: 0 24rpx;height: 70rpx;font-size: 0.32rem;color: #999;justify-content: flex-start;.label {margin-left: 15rpx;}}.overlay {position: fixed;top: 0;left: 0;right: 0;bottom: 0;background-color: rgba(0, 0, 0, 0.5);z-index: 998;}.picker-modal {position: fixed;bottom: 0;left: 0;right: 0;background: #fff;border-top-left-radius: $border-radius;border-top-right-radius: $border-radius;box-shadow: 0 -2px 10px rgba(0, 0, 0, 0.15);z-index: 999;padding-bottom: 40rpx;.title {font-weight: bold;text-align: center;font-size: 0.32rem;line-height: 110rpx;}.tab-container {border-bottom: 1px solid #eee;.tab {flex: 1;text-align: center;font-size: 0.32rem;padding: 20rpx 0;color: #999;position: relative;&.active {color: $primary-color;font-weight: bold;&::after {content: "";position: absolute;bottom: -1px;left: 30%;right: 30%;height: 2px;background-color: $primary-color;}}}}.picker-view {background: $background-color;height: 400rpx;.picker-item {height: 100rpx;line-height: 100rpx;text-align: center;font-size: 0.34rem;color: #333;}}.picker-footer {padding: 32rpx 24px;border-top: 1px solid #eee;.btn-next,.btn-confirm {width: 100%;background-color: $primary-color;border: none;border-radius: $border-radius;color: #fff;font-size: 0.36rem;}}.close-btn {position: absolute;top: 20rpx;right: 40rpx;font-size: 0.4rem;cursor: pointer;color: #999;}}
}
.fade-enter-active,
.fade-leave-active {transition: opacity 0.3s ease;
}
.fade-enter-from,
.fade-leave-to {opacity: 0;
}
.fade-enter-to,
.fade-leave-from {opacity: 1;
}
</style>
  1. 注册组件进行调用
import DateTimePicker from "@/components/date-time-picker";
app.component("DateTimePicker", DateTimePicker);<DateTimePickerstyle="width: 100%":modelValue="data.applyForm.DateTime":minute-step="10"@update:modelValue="getChangeItemValue"/>

相关文章:

  • 深入解析Spring Boot与Kafka的集成实践
  • 密码学实验
  • 【Unity】DOTween的常用函数解释
  • (一) 本地hadoop虚拟机系统设置
  • 自学嵌入式 day21 - 数据结构 双向链表
  • Keil软件中STM32(ARM)与C51兼容方法
  • STM32--串口函数
  • 优化Hadoop性能:如何修改Block块大小
  • AWS CodePipeline+ Elastic Beanstalk(AWS中国云CI/CD)
  • html+css+js趣味小游戏~记忆卡片配对(附源码)
  • c语言- 如何构建CMake项目(Linux/VSCode)
  • 去中心化算力池:基于IPFS+智能合约的跨校GPU资源共享平台设计
  • 零基础设计模式——创建型模式 - 工厂方法模式
  • 电商运营数据分析指南之流量指标
  • <uniapp><vuex><状态管理>在uniapp中,如何使用vuex实现数据共享与传递?
  • uni-app 中使用 mumu模拟器 进行调试和运行详细教程
  • 青少年编程与数学 02-019 Rust 编程基础 20课题、面向对象
  • idea查看class文件源码
  • Electron+vite+vue3 从0到1搭建项目,开发Win、Mac客户端
  • 青少年编程与数学 02-019 Rust 编程基础 19课题、项目发布
  • 同济大学党委常务副书记冯身洪履新中国科协党组副书记
  • 上海普陀:探索1岁以下托育服务的保育内容、人员配备等关键要素
  • 又是“9+2”复式票,浦东退休阿姨擒大乐透1153万头奖
  • 上海将完善隔代照料支持措施:建设老幼共享设施、提高科学育儿指导力度
  • 广州某科技公司遭网络攻击,境外“黑手”被锁定
  • 这位中国电影早期的全能奇才,90年前唱响国歌