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

【前端开发】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"/>
http://www.xdnf.cn/news/7508.html

相关文章:

  • 链表面试题9之环形链表进阶
  • 微服务架构中的多进程通信--内存池、共享内存、socket
  • Canvas SVG BpmnJS编辑器中Canvas与SVG职能详解
  • 50天50个小项目 (Vue3 + Tailwindcss V4) ✨ | Rotating Navigation (旋转导航)
  • 新浪《经济新闻》丨珈和科技联合蒲江政府打造“数字茶园+智能工厂+文旅综合体“创新模式
  • Python、Pytorch、TensorFlow、Anconda、PySide、Jupyter
  • 欧拉系统离线部署docker
  • iOS苹果和Android安卓测试APP应用程序的区别差异
  • 【Linux】进程间通信(三):命名管道
  • 嵌入式开发学习日志(linux系统编程--文件读写函数)Day24
  • vr制作公司提供什么服务?
  • Linux跨网络通信中IP与MAC的作用
  • Electron+vite+vue3 从0到1搭建项目,开发Win、Mac客户端
  • spark调度系统核心组件SparkContext、DAGSchedul、TaskScheduler介绍
  • 项目管理进阶:基于IPD流程的项目管理部分问题及建议书【附全文阅读】
  • 怎么样进行定性分析
  • 交通拥堵预测器(python)
  • Linux云计算训练营笔记day11【Linux CentOS7(cat、less、head、tail、lscpu、lsblk、hostname、vim、which、mount、alias)】
  • Python训练营打卡——DAY30(2025.5.19)
  • 苹果的人工智能领域慢热
  • esp32课设记录(三)mqtt通信记录 附mqtt介绍
  • thinkphp6实现统一监听并记录所有执行的sql语句除查询外
  • 2021-10-29 C++求位数及各位和
  • MathType公式如何按照(1)(2)…编号
  • 定积分的“偶倍奇零”性质及其使用条件
  • 软件设计师“关系模式和关系代数”真题考点分析——求三连
  • Mergekit——高频合并算法 TIES解析
  • C 语言学习笔记(函数2)
  • 【实战教程】如何添加git仓库的子模块
  • ipynb文件的一键访问(顺带启动jupyter)实现程序演示