技术栈
Uniapp + Vue3 + uView 年份显示前后一年,分钟动态设置间隔
效果图
主体显示
< view class = "uni-row-between selector" >
< view class = "uni-flex-1 left" @click= "!props.disabled && openPicker()" >
< uni- icons
color= "#c0c4cc"
type= "calendar"
size= "22"
style= "position: relative; top: 1px"
> < / uni- icons>
< text class = "label" >
{ { displayValue || placeholder } }
< / text>
< / view>
< uni- icons
color= "#c0c4cc"
type= "clear"
size= "22"
style= "position: relative; top: 1px"
v- if = "!props.disabled && localValue"
@click= "clear"
> < / uni- icons>
< / view>
底部弹窗
< 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- view
v- 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- view
v- 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" >
< button
v- 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>
组件抛出
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" ] ) ;
年月列表项和默认值
const now = new Date ( ) ;
const currentYear = now. getFullYear ( ) ;
const currentMonth = now. getMonth ( ) + 1 ;
const years = [ currentYear - 1 , currentYear, currentYear + 1 ] ;
const months = Array. from ( { length : 12 } , ( _, i ) => i + 1 ) ;
const yearIndex = ref ( 1 ) ;
const monthIndex = ref ( currentMonth - 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) ;
} ) ;
const hourIndex = ref ( currentHour) ;
const minuteIndex = ref ( Math. floor ( currentMinute / props. minuteStep) ) ;
监听年月变化,更新天数
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) ;
初始化当天日期时间
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 ( ) ;
}
}
} ) ;
选项变化更新对应值
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;
} ;
确定事件,抛出最新值
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 ;
} ;
组件样式
< 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>
注册组件进行调用
import DateTimePicker from "@/components/date-time-picker" ;
app. component ( "DateTimePicker" , DateTimePicker) ;
< DateTimePicker
style= "width: 100%"
: modelValue= "data.applyForm.DateTime"
: minute- step= "10"
@update: modelValue= "getChangeItemValue" / >