在 uni-app 开发中,uView 作为主流的 UI 组件库,其u-timepicker时间选择器组件能满足大部分时间选择场景,但在需要精确到秒 的业务场景中(如烹饪步骤耗时、视频剪辑时间点、设备操作时长等),u-timepicker仅支持时分选择的限制就无法满足需求。本文将分享基于u-picker自定义实现支持时分秒选择 + 反显的时间选择器,解决秒级选择的业务痛点。
一、实现思路
核心思路是基于 uView 的u-picker基础选择器,手动构建时分秒三列数据,同时实现 "时间字符串→选择器索引" 的解析逻辑(支持反显),以及 "选择器索引→时间字符串" 的拼接逻辑(支持确认更新),具体拆解为以下步骤:
- 基于
u-picker搭建选择器基础结构,通过show控制显隐; - 封装列数据生成函数,构建 "小时(00-23)、分钟(00-59)、秒(00-59)" 三列标准数据;
- 实现时间字符串解析函数,将业务中已有的时间(如
02:01:00)转换为选择器列索引,支持反显; - 处理选择器确认 / 取消事件,更新业务数据并保证响应式,同时做边界校验避免异常。
二、核心代码实现与解析
1. 模板结构:u-picker 基础布局
首先搭建u-picker的基础模板,绑定核心属性和事件,同时添加触发选择器的按钮:
vue
xml
<template>
<!-- 自定义时分秒选择器 -->
<u-picker
:show="showPickerVis"
:columns="timeColumns"
:defaultIndex="defaultIndex"
@cancel="handleClosePicker"
@confirm="handleConfirm">
</u-picker>
<!-- 触发选择器的按钮(示例:烹饪步骤耗时选择) -->
<view class="time-select" @tap="showTimePicker(step, index)"></view>
</template>
关键属性说明:
show:控制选择器显隐;columns:选择器的列数据(时分秒三列);defaultIndex:默认选中的索引(实现反显的核心);@cancel/@confirm:取消 / 确认选择的事件回调。
2. 数据初始化:定义核心响应式数据
在data中定义选择器运行所需的核心数据,保证响应式:
javascript
运行
javascript
data() {
return {
showPickerVis: false, // 选择器显隐状态
currentTimeStepIndex: -1, // 当前操作的业务数据索引(如烹饪步骤索引)
timeColumns: [], // 时分秒列数据([[小时], [分钟], [秒]])
defaultIndex: [0,0,0], // 选择器默认索引(反显用)
formData: {
cookingSteps: [] // 业务数据示例:烹饪步骤列表,含estSpendTime(耗时)字段
}
}
}
3. 列数据生成:构建标准时分秒列
封装fillTimeColumns函数,生成标准化的时分秒列数据,保证格式统一(两位数字,不足补 0):
javascript
运行
scss
fillTimeColumns(initialArr = [[], [], []]) {
// 辅助函数:数字补零(如1→01)
const padZero = (num) => num.toString().padStart(2, '0')
// 生成小时列(00-23):兼容初始数据,无数据则重新生成
const hoursColumn = initialArr[0].length === 24
? initialArr[0]
: Array.from({ length: 24 }, (_, index) => padZero(index));
// 生成分钟/秒列(00-59)
const minuteSecondColumn = Array.from({ length: 60 }, (_, index) => padZero(index));
// 赋值给列数据,供u-picker渲染
this.timeColumns = [ hoursColumn, minuteSecondColumn, minuteSecondColumn ]
}
核心逻辑:
- 用
padZero保证所有时间单位都是两位字符串(如05而非5),避免格式混乱; - 小时列限制为 00-23,分秒列限制为 00-59,符合时间规范;
- 兼容初始数据,若已有完整小时列则复用,否则重新生成,提升灵活性。
4. 反显核心:时间字符串转选择器索引
封装getTimeColumnIndexes函数,将业务中的时间字符串(如02:01:00或02:01:00)解析为选择器的列索引,实现反显:
javascript
运行
javascript
/**
* 根据时间字符串获取时分秒在timeColumns中的对应索引
* @param {string} timeStr - 时间字符串,格式如 "02:01:00"(中文冒号)或 "02:01:00"(英文冒号)
* @param {Array<Array<string>>} timeColumns - 时分秒列数据
* @returns {Array<number>} [小时索引, 分钟索引, 秒索引]
*/
getTimeColumnIndexes(timeStr, timeColumns) {
// 边界校验:列数据异常则返回默认索引
if (!Array.isArray(timeColumns) || timeColumns.length < 3) {
console.warn('timeColumns格式异常');
return [0, 0, 0];
}
// 统一分隔符(兼容中文/英文冒号),分割为时/分/秒
const timeParts = timeStr.replace(/:/g, ':').split(':');
// 格式校验:必须是HH:MM:SS格式
if (timeParts.length !== 3 || !/^\d{2}$/.test(timeParts[0]) || !/^\d{2}$/.test(timeParts[1]) || !/^\d{2}$/.test(timeParts[2])) {
console.warn('时间格式错误,应为 "HH:MM:SS" 或 "HH:MM:SS"');
return [0, 0, 0];
}
const [hourStr, minuteStr, secondStr] = timeParts;
// 查找对应索引,找不到则返回0
const hourIndex = timeColumns[0].findIndex(item => item === hourStr) || 0;
const minuteIndex = timeColumns[1].findIndex(item => item === minuteStr) || 0;
const secondIndex = timeColumns[2].findIndex(item => item === secondStr) || 0;
return [hourIndex, minuteIndex, secondIndex];
}
核心逻辑:
- 兼容中英文冒号,解决业务中时间字符串格式不统一的问题;
- 严格的格式校验,避免非法时间字符串导致选择器异常;
- 从列数据中精准查找索引,保证反显的准确性。
5. 事件处理:打开 / 确认 / 关闭选择器
(1)打开选择器:记录索引 + 设置反显
javascript
运行
kotlin
showTimePicker(step, index) {
// 1. 解析当前步骤的时间字符串,设置反显索引
this.defaultIndex = this.getTimeColumnIndexes(step.estSpendTime, this.timeColumns);
// 2. 记录当前操作的业务数据索引(如烹饪步骤索引)
this.currentTimeStepIndex = index;
// 3. 显示选择器
this.showPickerVis = true;
}
打开选择器时,先解析当前业务数据的时间字符串,设置defaultIndex实现反显,同时记录当前操作的索引,为后续更新数据做准备。
(2)确认选择:更新业务数据
javascript
运行
kotlin
handleConfirm(e) {
// 1. 边界校验:避免索引越界
if (this.currentTimeStepIndex < 0 || this.currentTimeStepIndex >= this.formData.cookingSteps.length) {
this.showPickerVis = false;
return;
}
// 2. 拼接选择的时分秒为标准字符串
const [hour, minute, second] = e.value;
const estSpendTime = `${hour}:${minute}:${second}`;
// 3. $set更新响应式数组(关键:保证数据响应式)
this.$set(
this.formData.cookingSteps[this.currentTimeStepIndex],
'estSpendTime',
estSpendTime
);
// 4. 关闭选择器
this.showPickerVis = false;
}
核心注意点:
- 必须做索引边界校验,避免操作不存在的业务数据;
- 使用
this.$set更新数组元素,保证 Vue 响应式(直接修改数组元素无法触发视图更新); - 拼接时间字符串为标准
HH:MM:SS格式,统一业务数据格式。
(3)取消 / 关闭选择器
javascript
运行
ini
handleClosePicker() {
this.showPickerVis = false;
}
取消选择时仅隐藏选择器,不修改业务数据,保证操作的合理性。
三、使用说明
- 初始化列数据 :在页面
onLoad或onShow中调用fillTimeColumns(),初始化时分秒列数据:
javascript
运行
javascript
onLoad() {
this.fillTimeColumns();
}
- 业务数据适配 :将示例中的
formData.cookingSteps替换为实际业务数据(如订单耗时、视频时长等),保证数据结构中包含时间字段(如estSpendTime); - 样式调整 :根据业务需求调整
u-picker和触发按钮的样式,适配页面 UI。
四、总结
本文基于 uView 的u-picker组件,实现了支持时分秒选择 + 反显 的自定义时间选择器,解决了u-timepicker不支持秒选择的痛点。核心亮点在于:
- 兼容中英文冒号的时间字符串解析,适配不同格式的业务数据;
- 完善的边界校验,避免索引越界、格式错误等异常场景;
- 保证 Vue 响应式更新,避免数据修改后视图不刷新的问题。
该自定义选择器可复用性强,适用于烹饪步骤耗时、设备操作时长、视频剪辑时间点等需要秒级时间选择的场景,只需稍作调整即可适配不同业务场景的时间选择需求。