You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

359 lines
10 KiB

9 months ago
<template>
<u-popup :maskCloseAble="maskCloseAble" mode="bottom" :popup="false" v-model="value" length="auto"
:safeAreaInsetBottom="safeAreaInsetBottom" @close="close" :z-index="uZIndex">
<view class="u-datetime-picker">
<view class="u-picker-header" @touchmove.stop.prevent="">
<view class="u-btn-picker u-btn-picker--tips" :style="{ color: cancelColor }" hover-class="u-opacity"
:hover-stay-time="150" @tap="getResult('cancel')">{{cancelText}}</view>
<view class="u-picker__title">{{ title }}</view>
<view class="u-btn-picker u-btn-picker--primary" :style="{ color: moving ? cancelColor : confirmColor }"
hover-class="u-opacity" :hover-stay-time="150" @touchmove.stop="" @tap.stop="getResult('confirm')">
{{confirmText}}
</view>
</view>
<view class="u-picker-body">
<picker-view :value="valueArr" @change="change" class="u-picker-view" @pickstart="pickstart"
@pickend="pickend">
<picker-view-column v-if="!reset">
<view class="u-column-item" v-for="(item, index) in hours" :key="index">
{{ item }}
<text class="u-text" v-if="showTimeTag"></text>
</view>
</picker-view-column>
<picker-view-column v-if="!reset">
<view class="u-column-item" v-for="(item, index) in minutes" :key="index">
{{ item }}
<text class="u-text" v-if="showTimeTag"></text>
</view>
</picker-view-column>
<picker-view-column v-if="!reset">
<view class="u-column-item" v-for="(item, index) in seconds" :key="index">
{{ item }}
<text class="u-text" v-if="showTimeTag"></text>
</view>
</picker-view-column>
<picker-view-column v-if="!reset">
<view class="u-column-item">-</view>
</picker-view-column>
<picker-view-column v-if="!reset">
<view class="u-column-item" v-for="(item, index) in hours" :key="index">
{{ item }}
<text class="u-text" v-if="showTimeTag"></text>
</view>
</picker-view-column>
<picker-view-column v-if="!reset">
<view class="u-column-item" v-for="(item, index) in minutes" :key="index">
{{ item }}
<text class="u-text" v-if="showTimeTag"></text>
</view>
</picker-view-column>
<picker-view-column v-if="!reset">
<view class="u-column-item" v-for="(item, index) in seconds" :key="index">
{{ item }}
<text class="u-text" v-if="showTimeTag"></text>
</view>
</picker-view-column>
</picker-view>
</view>
</view>
</u-popup>
</template>
<script>
/**
* time-range弹出选择器
* @property {String Number} start-year 可选的开始年份默认1950
* @property {String Number} end-year 可选的结束年份默认2050
* @property {Boolean} safe-area-inset-bottom 是否开启底部安全区适配默认false
* @property {Boolean} show-time-tag 时间模式时是否显示后面的年月日中文提示
* @property {String} cancel-color 取消按钮的颜色默认#606266
* @property {String} confirm-color 确认按钮的颜色默认#2979ff
* @property {String} default-time 默认选中的时间
* @property {String} confirm-text 确认按钮的文字
* @property {String} cancel-text 取消按钮的文字
* @property {Boolean} mask-close-able 是否允许通过点击遮罩关闭Picker默认true
* @property {String Number} z-index 弹出时的z-index值默认1075
* @property {Array} default-selector 数组形式其中每一项表示选择了range对应项中的第几个
* @event {Function} confirm 点击确定按钮返回当前选择的值
* @event {Function} cancel 点击取消按钮返回当前选择的值
*/
export default {
name: 'u-picker',
props: {
// 年份开始时间
startYear: {
type: [String, Number],
default: 1950
},
// 年份结束时间
endYear: {
type: [String, Number],
default: 2050
},
// "取消"按钮的颜色
cancelColor: {
type: String,
default: '#606266'
},
// "确定"按钮的颜色
confirmColor: {
type: String,
default: '#2979ff'
},
// 默认显示的时间
defaultTime: {
type: Array,
default: () => []
},
// 时间模式时,是否显示后面的年月日中文提示
showTimeTag: {
type: Boolean,
default: true
},
safeAreaInsetBottom: {
type: Boolean,
default: false
},
// 是否允许通过点击遮罩关闭Picker
maskCloseAble: {
type: Boolean,
default: true
},
// 通过双向绑定控制组件的弹出与收起
value: {
type: Boolean,
default: false
},
// 弹出的z-index值
zIndex: {
type: [String, Number],
default: 0
},
// 顶部标题
title: {
type: String,
default: ''
},
// 取消按钮的文字
cancelText: {
type: String,
default: '取消'
},
// 确认按钮的文字
confirmText: {
type: String,
default: '确认'
}
},
data() {
return {
hours: [],
minutes: [],
seconds: [],
hour: 0,
minute: 0,
second: 0,
reset: false,
startDate: '',
endDate: '',
valueArr: [0, 0, 0, 0, 0, 0, 0],
moving: false // 列是否还在滑动中,微信小程序如果在滑动中就点确定,结果可能不准确
};
},
mounted() {
this.init();
},
computed: {
propsChange() {
// 引用这几个变量,是为了监听其变化
return `${this.defaultTime}`;
},
uZIndex() {
// 如果用户有传递z-index值优先使用
return this.zIndex ? this.zIndex : this.$u.zIndex.popup;
}
},
watch: {
propsChange() {
this.reset = true;
setTimeout(() => this.init(), 10);
},
// 微信和QQ小程序由于一些奇怪的原因(故同时对所有平台均初始化一遍),需要重新初始化才能显示正确的值
value(n) {
if (n) {
this.reset = true;
setTimeout(() => this.init(), 10);
}
}
},
methods: {
// 标识滑动开始,只有微信小程序才有这样的事件
pickstart() {
// #ifdef MP-WEIXIN
this.moving = true;
// #endif
},
// 标识滑动结束
pickend() {
// #ifdef MP-WEIXIN
this.moving = false;
// #endif
},
// 小于10前面补0用于月份日期时分秒等
formatNumber(num) {
return +num < 10 ? '0' + num : String(num);
},
// 生成递进的数组
generateArray: function(start, end) {
// 转为数值格式否则用户给end-year等传递字符串值时下面的end+1会导致字符串拼接而不是相加
start = Number(start);
end = Number(end);
end = end > start ? end : start;
// 生成数组,获取其中的索引,并剪出来
let list = [...Array(end + 1).keys()].slice(start)
list = list.map(o => this.formatNumber(o))
return list;
},
getIndex: function(arr, val) {
let index = arr.indexOf(val);
// 如果index为-1(即找不到index值)~(-1)=-(-1)-1=0导致条件不成立
return ~index ? index : 0;
},
//日期时间处理
initTimeValue() {
if (this.defaultTime.length === 2) {
const arr1 = this.defaultTime[0].split(':')
const arr2 = this.defaultTime[1].split(':')
this.valueArr[0] = this.getIndex(this.hours, arr1[0])
this.valueArr[1] = this.getIndex(this.minutes, arr1[1])
this.valueArr[2] = this.getIndex(this.seconds, arr1[2])
this.valueArr[4] = this.getIndex(this.hours, arr2[0])
this.valueArr[5] = this.getIndex(this.minutes, arr2[1])
this.valueArr[6] = this.getIndex(this.seconds, arr2[2])
} else {
const time = new Date()
const currHour = this.formatNumber(time.getHours())
const currMinute = this.formatNumber(time.getMinutes())
const currSecond = this.formatNumber(time.getSeconds())
this.valueArr[0] = this.getIndex(this.hours, currHour)
this.valueArr[4] = this.getIndex(this.hours, currHour)
this.valueArr[1] = this.getIndex(this.minutes, currMinute)
this.valueArr[5] = this.getIndex(this.minutes, currMinute)
this.valueArr[2] = this.getIndex(this.seconds, currSecond)
this.valueArr[6] = this.getIndex(this.seconds, currSecond)
}
},
init() {
this.valueArr = [0, 0, 0, 0, 0, 0, 0];
this.reset = false;
this.hours = this.generateArray(0, 23);
this.minutes = this.generateArray(0, 59);
this.seconds = this.generateArray(0, 59);
this.initTimeValue();
this.$forceUpdate();
},
close() {
this.$emit('input', false);
},
// 用户更改picker的列选项
change(e) {
this.valueArr = e.detail.value;
},
// 用户点击确定按钮
getResult(event = null) {
// #ifdef MP-WEIXIN
if (this.moving) return;
// #endif
const valueArr = this.valueArr.map(o => this.formatNumber(o))
const item1 = `${valueArr[0]}:${valueArr[1]}:${valueArr[2]}`
const item2 = `${valueArr[4]}:${valueArr[5]}:${valueArr[6]}`
const result = [item1, item2]
if (event) this.$emit(event, result);
this.close();
},
}
};
</script>
<style lang="scss" scoped>
@import "@/uview-ui/libs/css/style.components.scss";
.u-datetime-picker {
position: relative;
z-index: 999;
}
.u-picker-view {
height: 100%;
box-sizing: border-box;
}
.u-picker-header {
width: 100%;
height: 90rpx;
padding: 0 40rpx;
@include vue-flex;
justify-content: space-between;
align-items: center;
box-sizing: border-box;
font-size: 30rpx;
background: #fff;
position: relative;
}
.u-picker-header::after {
content: '';
position: absolute;
border-bottom: 1rpx solid #eaeef1;
-webkit-transform: scaleY(0.5);
transform: scaleY(0.5);
bottom: 0;
right: 0;
left: 0;
}
.u-picker__title {
color: $u-content-color;
}
.u-picker-body {
width: 100%;
height: 500rpx;
overflow: hidden;
background-color: #fff;
}
.u-column-item {
@include vue-flex;
align-items: center;
justify-content: center;
font-size: 32rpx;
color: $u-main-color;
padding: 0 8rpx;
}
.u-text {
font-size: 24rpx;
padding-left: 8rpx;
}
.u-btn-picker {
padding: 16rpx;
box-sizing: border-box;
text-align: center;
text-decoration: none;
}
.u-opacity {
opacity: 0.5;
}
.u-btn-picker--primary {
color: $u-type-primary;
}
.u-btn-picker--tips {
color: $u-tips-color;
}
</style>