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

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

技术栈

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

效果图

在这里插入图片描述

  1. 主体显示
    <view class="uni-row-between selector"><view class="uni-flex-1 left" @click="!props.disabled && openPicker()"><uni-iconscolor="#c0c4cc"type="calendar"size="22"style="position: relative; top: 1px"></uni-icons><text class="label">{{ displayValue || placeholder }}</text></view><uni-iconscolor="#c0c4cc"type="clear"size="22"style="position: relative; top: 1px"v-if="!props.disabled && localValue"@click="clear"></uni-icons></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,},disabled: {type: Boolean,default: false,},
});const localValue = ref(props.modelValue);
watch(() => props.modelValue,(newVal) => {localValue.value = newVal;}
);
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 (localValue.value) {const reg = /(\d{4})年(\d{1,2})月(\d{1,2})日 (\d{1,2})时(\d{1,2})分/;const matched = localValue.value.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);localValue.value = 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.lryc.cn/news/2384442.html

相关文章:

  • 跑通Multi-Agent-Constrained-Policy-Optimisation
  • Visual Studio已更新为17.14+集成deepseek实现高效编程
  • go 基础语法 【教程 go tour】
  • 养生指南:五维打造健康新方式
  • 网络爬虫学习之httpx的使用
  • 无人机桥梁检测效率问题-高精度3D建模及航线规划
  • 想免费使用 AWS 云服务器?注册、验证及开通全攻略
  • 以太联 - Intellinet 闪耀台北 SecuTech 国际安全科技应用博览会
  • Pandas:数据分析中的缺失值检测、加载、设置、可视化与处理
  • 【Linux系列】EVS 与 VBD 的对比
  • 56 在standby待机打通uart调试的方法
  • OceanBase 共享存储:云原生数据库的存储
  • 安卓新建项目时,Gradle下载慢下载如何用国内的镜像
  • 讯联文库开发日志(五)登录拦截校验
  • PCB设计教程【入门篇】——电路分析基础-读懂原理图
  • C语言数据结构
  • 湖北理元理律师事务所债务优化方案:让还款与生活平衡成为可能
  • Java对象内存分配优化教学
  • 精度再升级,可到微米!单位自动换算平米和米
  • 【学习笔记】Sophus (Python) 使用文档
  • 常见算法题目2 - 给定一个字符串,找出其中最长的不重复子串
  • 如何配置jmeter做分布式压测
  • Django 中的 ORM 基础语法
  • C#对象初始化语句:优雅创建对象的黑科技
  • 【计算机网络】TCP如何保障传输可靠性_笔记
  • Robust Kernel Estimation with Outliers Handling for Image Deblurring论文阅读
  • Android Studio 开发环境兼容性检索(AGP / Gradle / Kotlin / JDK)
  • html主题切换小demo
  • AI架构职责分配——支持AI模块的职责边界设计
  • git@gitee.com: Permission denied (publickey). fatal: 无法读取远程仓库