| | |
| | | <template> |
| | | <view class="uni-datetime-picker"> |
| | | <view @click="initTimePicker"> |
| | | <slot> |
| | | <view class="uni-datetime-picker-timebox-pointer" |
| | | :class="{'uni-datetime-picker-disabled': disabled, 'uni-datetime-picker-timebox': border}"> |
| | | <text class="uni-datetime-picker-text">{{time}}</text> |
| | | <view v-if="!time" class="uni-datetime-picker-time"> |
| | | <text class="uni-datetime-picker-text">{{selectTimeText}}</text> |
| | | </view> |
| | | </view> |
| | | </slot> |
| | | </view> |
| | | <view v-if="visible" id="mask" class="uni-datetime-picker-mask" @click="tiggerTimePicker"></view> |
| | | <view v-if="visible" class="uni-datetime-picker-popup" :class="[dateShow && timeShow ? '' : 'fix-nvue-height']" |
| | | :style="fixNvueBug"> |
| | | <view class="uni-title"> |
| | | <text class="uni-datetime-picker-text">{{selectTimeText}}</text> |
| | | </view> |
| | | <view v-if="dateShow" class="uni-datetime-picker__container-box"> |
| | | <picker-view class="uni-datetime-picker-view" :indicator-style="indicatorStyle" :value="ymd" |
| | | @change="bindDateChange"> |
| | | <picker-view-column> |
| | | <view class="uni-datetime-picker-item" v-for="(item,index) in years" :key="index"> |
| | | <text class="uni-datetime-picker-item">{{lessThanTen(item)}}</text> |
| | | </view> |
| | | </picker-view-column> |
| | | <picker-view-column> |
| | | <view class="uni-datetime-picker-item" v-for="(item,index) in months" :key="index"> |
| | | <text class="uni-datetime-picker-item">{{lessThanTen(item)}}</text> |
| | | </view> |
| | | </picker-view-column> |
| | | <picker-view-column> |
| | | <view class="uni-datetime-picker-item" v-for="(item,index) in days" :key="index"> |
| | | <text class="uni-datetime-picker-item">{{lessThanTen(item)}}</text> |
| | | </view> |
| | | </picker-view-column> |
| | | </picker-view> |
| | | <!-- 兼容 nvue 不支持伪类 --> |
| | | <text class="uni-datetime-picker-sign sign-left">-</text> |
| | | <text class="uni-datetime-picker-sign sign-right">-</text> |
| | | </view> |
| | | <view v-if="timeShow" class="uni-datetime-picker__container-box"> |
| | | <picker-view class="uni-datetime-picker-view" :class="[hideSecond ? 'time-hide-second' : '']" |
| | | :indicator-style="indicatorStyle" :value="hms" @change="bindTimeChange"> |
| | | <picker-view-column> |
| | | <view class="uni-datetime-picker-item" v-for="(item,index) in hours" :key="index"> |
| | | <text class="uni-datetime-picker-item">{{lessThanTen(item)}}</text> |
| | | </view> |
| | | </picker-view-column> |
| | | <picker-view-column> |
| | | <view class="uni-datetime-picker-item" v-for="(item,index) in minutes" :key="index"> |
| | | <text class="uni-datetime-picker-item">{{lessThanTen(item)}}</text> |
| | | </view> |
| | | </picker-view-column> |
| | | <picker-view-column v-if="!hideSecond"> |
| | | <view class="uni-datetime-picker-item" v-for="(item,index) in seconds" :key="index"> |
| | | <text class="uni-datetime-picker-item">{{lessThanTen(item)}}</text> |
| | | </view> |
| | | </picker-view-column> |
| | | </picker-view> |
| | | <!-- 兼容 nvue 不支持伪类 --> |
| | | <text class="uni-datetime-picker-sign" :class="[hideSecond ? 'sign-center' : 'sign-left']">:</text> |
| | | <text v-if="!hideSecond" class="uni-datetime-picker-sign sign-right">:</text> |
| | | </view> |
| | | <view class="uni-datetime-picker-btn"> |
| | | <view @click="clearTime"> |
| | | <text class="uni-datetime-picker-btn-text">{{clearText}}</text> |
| | | </view> |
| | | <view class="uni-datetime-picker-btn-group"> |
| | | <view class="uni-datetime-picker-cancel" @click="tiggerTimePicker"> |
| | | <text class="uni-datetime-picker-btn-text">{{cancelText}}</text> |
| | | </view> |
| | | <view @click="setTime"> |
| | | <text class="uni-datetime-picker-btn-text">{{okText}}</text> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | <!-- #ifdef H5 --> |
| | | <!-- <keypress v-if="visible" @esc="tiggerTimePicker" @enter="setTime" /> --> |
| | | <!-- #endif --> |
| | | </view> |
| | | </template> |
| | | |
| | | <script> |
| | | // #ifdef H5 |
| | | import keypress from './keypress' |
| | | // #endif |
| | | import { |
| | | initVueI18n |
| | | } from '@dcloudio/uni-i18n' |
| | | import messages from './i18n/index.js' |
| | | const { t } = initVueI18n(messages) |
| | | |
| | | /** |
| | | * DatetimePicker 时间选择器 |
| | | * @description 可以同时选择日期和时间的选择器 |
| | | * @tutorial https://ext.dcloud.net.cn/plugin?id=xxx |
| | | * @property {String} type = [datetime | date | time] 显示模式 |
| | | * @property {Boolean} multiple = [true|false] 是否多选 |
| | | * @property {String|Number} value 默认值 |
| | | * @property {String|Number} start 起始日期或时间 |
| | | * @property {String|Number} end 起始日期或时间 |
| | | * @property {String} return-type = [timestamp | string] |
| | | * @event {Function} change 选中发生变化触发 |
| | | */ |
| | | |
| | | export default { |
| | | name: 'UniDatetimePicker', |
| | | components: { |
| | | // #ifdef H5 |
| | | keypress |
| | | // #endif |
| | | }, |
| | | data() { |
| | | return { |
| | | indicatorStyle: `height: 50px;`, |
| | | visible: false, |
| | | fixNvueBug: {}, |
| | | dateShow: true, |
| | | timeShow: true, |
| | | title: '日期和时间', |
| | | // 输入框当前时间 |
| | | time: '', |
| | | // 当前的年月日时分秒 |
| | | year: 1920, |
| | | month: 0, |
| | | day: 0, |
| | | hour: 0, |
| | | minute: 0, |
| | | second: 0, |
| | | // 起始时间 |
| | | startYear: 1920, |
| | | startMonth: 1, |
| | | startDay: 1, |
| | | startHour: 0, |
| | | startMinute: 0, |
| | | startSecond: 0, |
| | | // 结束时间 |
| | | endYear: 2120, |
| | | endMonth: 12, |
| | | endDay: 31, |
| | | endHour: 23, |
| | | endMinute: 59, |
| | | endSecond: 59, |
| | | } |
| | | }, |
| | | props: { |
| | | type: { |
| | | type: String, |
| | | default: 'datetime' |
| | | }, |
| | | value: { |
| | | type: [String, Number], |
| | | default: '' |
| | | }, |
| | | modelValue: { |
| | | type: [String, Number], |
| | | default: '' |
| | | }, |
| | | start: { |
| | | type: [Number, String], |
| | | default: '' |
| | | }, |
| | | end: { |
| | | type: [Number, String], |
| | | default: '' |
| | | }, |
| | | returnType: { |
| | | type: String, |
| | | default: 'string' |
| | | }, |
| | | disabled: { |
| | | type: [Boolean, String], |
| | | default: false |
| | | }, |
| | | border: { |
| | | type: [Boolean, String], |
| | | default: true |
| | | }, |
| | | hideSecond: { |
| | | type: [Boolean, String], |
| | | default: false |
| | | } |
| | | }, |
| | | watch: { |
| | | value: { |
| | | handler(newVal, oldVal) { |
| | | if (newVal) { |
| | | this.parseValue(this.fixIosDateFormat(newVal)) //兼容 iOS、safari 日期格式 |
| | | this.initTime(false) |
| | | } else { |
| | | this.time = '' |
| | | this.parseValue(Date.now()) |
| | | } |
| | | }, |
| | | immediate: true |
| | | }, |
| | | type: { |
| | | handler(newValue) { |
| | | if (newValue === 'date') { |
| | | this.dateShow = true |
| | | this.timeShow = false |
| | | this.title = '日期' |
| | | } else if (newValue === 'time') { |
| | | this.dateShow = false |
| | | this.timeShow = true |
| | | this.title = '时间' |
| | | } else { |
| | | this.dateShow = true |
| | | this.timeShow = true |
| | | this.title = '日期和时间' |
| | | } |
| | | }, |
| | | immediate: true |
| | | }, |
| | | start: { |
| | | handler(newVal) { |
| | | this.parseDatetimeRange(this.fixIosDateFormat(newVal), 'start') //兼容 iOS、safari 日期格式 |
| | | }, |
| | | immediate: true |
| | | }, |
| | | end: { |
| | | handler(newVal) { |
| | | this.parseDatetimeRange(this.fixIosDateFormat(newVal), 'end') //兼容 iOS、safari 日期格式 |
| | | }, |
| | | immediate: true |
| | | }, |
| | | |
| | | // 月、日、时、分、秒可选范围变化后,检查当前值是否在范围内,不在则当前值重置为可选范围第一项 |
| | | months(newVal) { |
| | | this.checkValue('month', this.month, newVal) |
| | | }, |
| | | days(newVal) { |
| | | this.checkValue('day', this.day, newVal) |
| | | }, |
| | | hours(newVal) { |
| | | this.checkValue('hour', this.hour, newVal) |
| | | }, |
| | | minutes(newVal) { |
| | | this.checkValue('minute', this.minute, newVal) |
| | | }, |
| | | seconds(newVal) { |
| | | this.checkValue('second', this.second, newVal) |
| | | } |
| | | }, |
| | | computed: { |
| | | // 当前年、月、日、时、分、秒选择范围 |
| | | years() { |
| | | return this.getCurrentRange('year') |
| | | }, |
| | | |
| | | months() { |
| | | return this.getCurrentRange('month') |
| | | }, |
| | | |
| | | days() { |
| | | return this.getCurrentRange('day') |
| | | }, |
| | | |
| | | hours() { |
| | | return this.getCurrentRange('hour') |
| | | }, |
| | | |
| | | minutes() { |
| | | return this.getCurrentRange('minute') |
| | | }, |
| | | |
| | | seconds() { |
| | | return this.getCurrentRange('second') |
| | | }, |
| | | |
| | | // picker 当前值数组 |
| | | ymd() { |
| | | return [this.year - this.minYear, this.month - this.minMonth, this.day - this.minDay] |
| | | }, |
| | | hms() { |
| | | return [this.hour - this.minHour, this.minute - this.minMinute, this.second - this.minSecond] |
| | | }, |
| | | |
| | | // 当前 date 是 start |
| | | currentDateIsStart() { |
| | | return this.year === this.startYear && this.month === this.startMonth && this.day === this.startDay |
| | | }, |
| | | |
| | | // 当前 date 是 end |
| | | currentDateIsEnd() { |
| | | return this.year === this.endYear && this.month === this.endMonth && this.day === this.endDay |
| | | }, |
| | | |
| | | // 当前年、月、日、时、分、秒的最小值和最大值 |
| | | minYear() { |
| | | return this.startYear |
| | | }, |
| | | maxYear() { |
| | | return this.endYear |
| | | }, |
| | | minMonth() { |
| | | if (this.year === this.startYear) { |
| | | return this.startMonth |
| | | } else { |
| | | return 1 |
| | | } |
| | | }, |
| | | maxMonth() { |
| | | if (this.year === this.endYear) { |
| | | return this.endMonth |
| | | } else { |
| | | return 12 |
| | | } |
| | | }, |
| | | minDay() { |
| | | if (this.year === this.startYear && this.month === this.startMonth) { |
| | | return this.startDay |
| | | } else { |
| | | return 1 |
| | | } |
| | | }, |
| | | maxDay() { |
| | | if (this.year === this.endYear && this.month === this.endMonth) { |
| | | return this.endDay |
| | | } else { |
| | | return this.daysInMonth(this.year, this.month) |
| | | } |
| | | }, |
| | | minHour() { |
| | | if (this.type === 'datetime') { |
| | | if (this.currentDateIsStart) { |
| | | return this.startHour |
| | | } else { |
| | | return 0 |
| | | } |
| | | } |
| | | if (this.type === 'time') { |
| | | return this.startHour |
| | | } |
| | | }, |
| | | maxHour() { |
| | | if (this.type === 'datetime') { |
| | | if (this.currentDateIsEnd) { |
| | | return this.endHour |
| | | } else { |
| | | return 23 |
| | | } |
| | | } |
| | | if (this.type === 'time') { |
| | | return this.endHour |
| | | } |
| | | }, |
| | | minMinute() { |
| | | if (this.type === 'datetime') { |
| | | if (this.currentDateIsStart && this.hour === this.startHour) { |
| | | return this.startMinute |
| | | } else { |
| | | return 0 |
| | | } |
| | | } |
| | | if (this.type === 'time') { |
| | | if (this.hour === this.startHour) { |
| | | return this.startMinute |
| | | } else { |
| | | return 0 |
| | | } |
| | | } |
| | | }, |
| | | maxMinute() { |
| | | if (this.type === 'datetime') { |
| | | if (this.currentDateIsEnd && this.hour === this.endHour) { |
| | | return this.endMinute |
| | | } else { |
| | | return 59 |
| | | } |
| | | } |
| | | if (this.type === 'time') { |
| | | if (this.hour === this.endHour) { |
| | | return this.endMinute |
| | | } else { |
| | | return 59 |
| | | } |
| | | } |
| | | }, |
| | | minSecond() { |
| | | if (this.type === 'datetime') { |
| | | if (this.currentDateIsStart && this.hour === this.startHour && this.minute === this.startMinute) { |
| | | return this.startSecond |
| | | } else { |
| | | return 0 |
| | | } |
| | | } |
| | | if (this.type === 'time') { |
| | | if (this.hour === this.startHour && this.minute === this.startMinute) { |
| | | return this.startSecond |
| | | } else { |
| | | return 0 |
| | | } |
| | | } |
| | | }, |
| | | maxSecond() { |
| | | if (this.type === 'datetime') { |
| | | if (this.currentDateIsEnd && this.hour === this.endHour && this.minute === this.endMinute) { |
| | | return this.endSecond |
| | | } else { |
| | | return 59 |
| | | } |
| | | } |
| | | if (this.type === 'time') { |
| | | if (this.hour === this.endHour && this.minute === this.endMinute) { |
| | | return this.endSecond |
| | | } else { |
| | | return 59 |
| | | } |
| | | } |
| | | }, |
| | | |
| | | /** |
| | | * for i18n |
| | | */ |
| | | selectTimeText() { |
| | | return t("uni-datetime-picker.selectTime") |
| | | }, |
| | | okText() { |
| | | return t("uni-datetime-picker.ok") |
| | | }, |
| | | clearText() { |
| | | return t("uni-datetime-picker.clear") |
| | | }, |
| | | cancelText() { |
| | | return t("uni-datetime-picker.cancel") |
| | | } |
| | | }, |
| | | |
| | | mounted() { |
| | | // #ifdef APP-NVUE |
| | | const res = uni.getSystemInfoSync(); |
| | | this.fixNvueBug = { |
| | | top: res.windowHeight / 2, |
| | | left: res.windowWidth / 2 |
| | | } |
| | | // #endif |
| | | }, |
| | | |
| | | methods: { |
| | | /** |
| | | * @param {Object} item |
| | | * 小于 10 在前面加个 0 |
| | | */ |
| | | |
| | | lessThanTen(item) { |
| | | return item < 10 ? '0' + item : item |
| | | }, |
| | | |
| | | /** |
| | | * 解析时分秒字符串,例如:00:00:00 |
| | | * @param {String} timeString |
| | | */ |
| | | parseTimeType(timeString) { |
| | | if (timeString) { |
| | | let timeArr = timeString.split(':') |
| | | this.hour = Number(timeArr[0]) |
| | | this.minute = Number(timeArr[1]) |
| | | this.second = Number(timeArr[2]) |
| | | } |
| | | }, |
| | | |
| | | /** |
| | | * 解析选择器初始值,类型可以是字符串、时间戳,例如:2000-10-02、'08:30:00'、 1610695109000 |
| | | * @param {String | Number} datetime |
| | | */ |
| | | initPickerValue(datetime) { |
| | | let defaultValue = null |
| | | if (datetime) { |
| | | defaultValue = this.compareValueWithStartAndEnd(datetime, this.start, this.end) |
| | | } else { |
| | | defaultValue = Date.now() |
| | | defaultValue = this.compareValueWithStartAndEnd(defaultValue, this.start, this.end) |
| | | } |
| | | this.parseValue(defaultValue) |
| | | }, |
| | | |
| | | /** |
| | | * 初始值规则: |
| | | * - 用户设置初始值 value |
| | | * - 设置了起始时间 start、终止时间 end,并 start < value < end,初始值为 value, 否则初始值为 start |
| | | * - 只设置了起始时间 start,并 start < value,初始值为 value,否则初始值为 start |
| | | * - 只设置了终止时间 end,并 value < end,初始值为 value,否则初始值为 end |
| | | * - 无起始终止时间,则初始值为 value |
| | | * - 无初始值 value,则初始值为当前本地时间 Date.now() |
| | | * @param {Object} value |
| | | * @param {Object} dateBase |
| | | */ |
| | | compareValueWithStartAndEnd(value, start, end) { |
| | | let winner = null |
| | | value = this.superTimeStamp(value) |
| | | start = this.superTimeStamp(start) |
| | | end = this.superTimeStamp(end) |
| | | |
| | | if (start && end) { |
| | | if (value < start) { |
| | | winner = new Date(start) |
| | | } else if (value > end) { |
| | | winner = new Date(end) |
| | | } else { |
| | | winner = new Date(value) |
| | | } |
| | | } else if (start && !end) { |
| | | winner = start <= value ? new Date(value) : new Date(start) |
| | | } else if (!start && end) { |
| | | winner = value <= end ? new Date(value) : new Date(end) |
| | | } else { |
| | | winner = new Date(value) |
| | | } |
| | | |
| | | return winner |
| | | }, |
| | | |
| | | /** |
| | | * 转换为可比较的时间戳,接受日期、时分秒、时间戳 |
| | | * @param {Object} value |
| | | */ |
| | | superTimeStamp(value) { |
| | | let dateBase = '' |
| | | if (this.type === 'time' && value && typeof value === 'string') { |
| | | const now = new Date() |
| | | const year = now.getFullYear() |
| | | const month = now.getMonth() + 1 |
| | | const day = now.getDate() |
| | | dateBase = year + '/' + month + '/' + day + ' ' |
| | | } |
| | | if (Number(value) && typeof value !== NaN) { |
| | | value = parseInt(value) |
| | | dateBase = 0 |
| | | } |
| | | return this.createTimeStamp(dateBase + value) |
| | | }, |
| | | |
| | | /** |
| | | * 解析默认值 value,字符串、时间戳 |
| | | * @param {Object} defaultTime |
| | | */ |
| | | parseValue(value) { |
| | | if (!value) { |
| | | return |
| | | } |
| | | if (this.type === 'time' && typeof value === "string") { |
| | | this.parseTimeType(value) |
| | | } else { |
| | | let defaultDate = null |
| | | defaultDate = new Date(value) |
| | | if (this.type !== 'time') { |
| | | this.year = defaultDate.getFullYear() |
| | | this.month = defaultDate.getMonth() + 1 |
| | | this.day = defaultDate.getDate() |
| | | } |
| | | if (this.type !== 'date') { |
| | | this.hour = defaultDate.getHours() |
| | | this.minute = defaultDate.getMinutes() |
| | | this.second = defaultDate.getSeconds() |
| | | } |
| | | } |
| | | if (this.hideSecond) { |
| | | this.second = 0 |
| | | } |
| | | }, |
| | | |
| | | /** |
| | | * 解析可选择时间范围 start、end,年月日字符串、时间戳 |
| | | * @param {Object} defaultTime |
| | | */ |
| | | parseDatetimeRange(point, pointType) { |
| | | // 时间为空,则重置为初始值 |
| | | if (!point) { |
| | | if (pointType === 'start') { |
| | | this.startYear = 1920 |
| | | this.startMonth = 1 |
| | | this.startDay = 1 |
| | | this.startHour = 0 |
| | | this.startMinute = 0 |
| | | this.startSecond = 0 |
| | | } |
| | | if (pointType === 'end') { |
| | | this.endYear = 2120 |
| | | this.endMonth = 12 |
| | | this.endDay = 31 |
| | | this.endHour = 23 |
| | | this.endMinute = 59 |
| | | this.endSecond = 59 |
| | | } |
| | | return |
| | | } |
| | | if (this.type === 'time') { |
| | | const pointArr = point.split(':') |
| | | this[pointType + 'Hour'] = Number(pointArr[0]) |
| | | this[pointType + 'Minute'] = Number(pointArr[1]) |
| | | this[pointType + 'Second'] = Number(pointArr[2]) |
| | | } else { |
| | | if (!point) { |
| | | pointType === 'start' ? this.startYear = this.year - 60 : this.endYear = this.year + 60 |
| | | return |
| | | } |
| | | if (Number(point) && Number(point) !== NaN) { |
| | | point = parseInt(point) |
| | | } |
| | | // datetime 的 end 没有时分秒, 则不限制 |
| | | const hasTime = /[0-9]:[0-9]/ |
| | | if (this.type === 'datetime' && pointType === 'end' && typeof point === 'string' && !hasTime.test( |
| | | point)) { |
| | | point = point + ' 23:59:59' |
| | | } |
| | | const pointDate = new Date(point) |
| | | this[pointType + 'Year'] = pointDate.getFullYear() |
| | | this[pointType + 'Month'] = pointDate.getMonth() + 1 |
| | | this[pointType + 'Day'] = pointDate.getDate() |
| | | if (this.type === 'datetime') { |
| | | this[pointType + 'Hour'] = pointDate.getHours() |
| | | this[pointType + 'Minute'] = pointDate.getMinutes() |
| | | this[pointType + 'Second'] = pointDate.getSeconds() |
| | | } |
| | | } |
| | | }, |
| | | |
| | | // 获取 年、月、日、时、分、秒 当前可选范围 |
| | | getCurrentRange(value) { |
| | | const range = [] |
| | | for (let i = this['min' + this.capitalize(value)]; i <= this['max' + this.capitalize(value)]; i++) { |
| | | range.push(i) |
| | | } |
| | | return range |
| | | }, |
| | | |
| | | // 字符串首字母大写 |
| | | capitalize(str) { |
| | | return str.charAt(0).toUpperCase() + str.slice(1) |
| | | }, |
| | | |
| | | // 检查当前值是否在范围内,不在则当前值重置为可选范围第一项 |
| | | checkValue(name, value, values) { |
| | | if (values.indexOf(value) === -1) { |
| | | this[name] = values[0] |
| | | } |
| | | }, |
| | | |
| | | // 每个月的实际天数 |
| | | daysInMonth(year, month) { // Use 1 for January, 2 for February, etc. |
| | | return new Date(year, month, 0).getDate(); |
| | | }, |
| | | |
| | | //兼容 iOS、safari 日期格式 |
| | | fixIosDateFormat(value) { |
| | | if (typeof value === 'string') { |
| | | value = value.replace(/-/g, '/') |
| | | } |
| | | return value |
| | | }, |
| | | |
| | | /** |
| | | * 生成时间戳 |
| | | * @param {Object} time |
| | | */ |
| | | createTimeStamp(time) { |
| | | if (!time) return |
| | | if (typeof time === "number") { |
| | | return time |
| | | } else { |
| | | time = time.replace(/-/g, '/') |
| | | if (this.type === 'date') { |
| | | time = time + ' ' + '00:00:00' |
| | | } |
| | | return Date.parse(time) |
| | | } |
| | | }, |
| | | |
| | | /** |
| | | * 生成日期或时间的字符串 |
| | | */ |
| | | createDomSting() { |
| | | const yymmdd = this.year + |
| | | '-' + |
| | | this.lessThanTen(this.month) + |
| | | '-' + |
| | | this.lessThanTen(this.day) |
| | | |
| | | let hhmmss = this.lessThanTen(this.hour) + |
| | | ':' + |
| | | this.lessThanTen(this.minute) |
| | | |
| | | if (!this.hideSecond) { |
| | | hhmmss = hhmmss + ':' + this.lessThanTen(this.second) |
| | | } |
| | | |
| | | if (this.type === 'date') { |
| | | return yymmdd |
| | | } else if (this.type === 'time') { |
| | | return hhmmss |
| | | } else { |
| | | return yymmdd + ' ' + hhmmss |
| | | } |
| | | }, |
| | | |
| | | /** |
| | | * 初始化返回值,并抛出 change 事件 |
| | | */ |
| | | initTime(emit = true) { |
| | | this.time = this.createDomSting() |
| | | if (!emit) return |
| | | if (this.returnType === 'timestamp' && this.type !== 'time') { |
| | | this.$emit('change', this.createTimeStamp(this.time)) |
| | | this.$emit('input', this.createTimeStamp(this.time)) |
| | | this.$emit('update:modelValue', this.createTimeStamp(this.time)) |
| | | } else { |
| | | this.$emit('change', this.time) |
| | | this.$emit('input', this.time) |
| | | this.$emit('update:modelValue', this.time) |
| | | } |
| | | }, |
| | | |
| | | /** |
| | | * 用户选择日期或时间更新 data |
| | | * @param {Object} e |
| | | */ |
| | | bindDateChange(e) { |
| | | const val = e.detail.value |
| | | this.year = this.years[val[0]] |
| | | this.month = this.months[val[1]] |
| | | this.day = this.days[val[2]] |
| | | }, |
| | | bindTimeChange(e) { |
| | | const val = e.detail.value |
| | | this.hour = this.hours[val[0]] |
| | | this.minute = this.minutes[val[1]] |
| | | this.second = this.seconds[val[2]] |
| | | }, |
| | | |
| | | /** |
| | | * 初始化弹出层 |
| | | */ |
| | | initTimePicker() { |
| | | if (this.disabled) return |
| | | const value = this.fixIosDateFormat(this.value) |
| | | this.initPickerValue(value) |
| | | this.visible = !this.visible |
| | | }, |
| | | |
| | | /** |
| | | * 触发或关闭弹框 |
| | | */ |
| | | tiggerTimePicker(e) { |
| | | this.visible = !this.visible |
| | | }, |
| | | |
| | | /** |
| | | * 用户点击“清空”按钮,清空当前值 |
| | | */ |
| | | clearTime() { |
| | | this.time = '' |
| | | this.$emit('change', this.time) |
| | | this.$emit('input', this.time) |
| | | this.$emit('update:modelValue', this.time) |
| | | this.tiggerTimePicker() |
| | | }, |
| | | |
| | | /** |
| | | * 用户点击“确定”按钮 |
| | | */ |
| | | setTime() { |
| | | this.initTime() |
| | | this.tiggerTimePicker() |
| | | } |
| | | } |
| | | } |
| | | </script> |
| | | |
| | | <style> |
| | | .uni-datetime-picker { |
| | | /* #ifndef APP-NVUE */ |
| | | /* width: 100%; */ |
| | | /* #endif */ |
| | | } |
| | | |
| | | .uni-datetime-picker-view { |
| | | height: 130px; |
| | | width: 270px; |
| | | /* #ifndef APP-NVUE */ |
| | | cursor: pointer; |
| | | /* #endif */ |
| | | } |
| | | |
| | | .uni-datetime-picker-item { |
| | | height: 50px; |
| | | line-height: 50px; |
| | | text-align: center; |
| | | font-size: 14px; |
| | | } |
| | | |
| | | .uni-datetime-picker-btn { |
| | | margin-top: 60px; |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | cursor: pointer; |
| | | /* #endif */ |
| | | flex-direction: row; |
| | | justify-content: space-between; |
| | | } |
| | | |
| | | .uni-datetime-picker-btn-text { |
| | | font-size: 14px; |
| | | color: #007AFF; |
| | | } |
| | | |
| | | .uni-datetime-picker-btn-group { |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | /* #endif */ |
| | | flex-direction: row; |
| | | } |
| | | |
| | | .uni-datetime-picker-cancel { |
| | | margin-right: 30px; |
| | | } |
| | | |
| | | .uni-datetime-picker-mask { |
| | | position: fixed; |
| | | bottom: 0px; |
| | | top: 0px; |
| | | left: 0px; |
| | | right: 0px; |
| | | background-color: rgba(0, 0, 0, 0.4); |
| | | transition-duration: 0.3s; |
| | | z-index: 998; |
| | | } |
| | | |
| | | .uni-datetime-picker-popup { |
| | | border-radius: 8px; |
| | | padding: 30px; |
| | | width: 270px; |
| | | /* #ifdef APP-NVUE */ |
| | | height: 500px; |
| | | /* #endif */ |
| | | /* #ifdef APP-NVUE */ |
| | | width: 330px; |
| | | /* #endif */ |
| | | background-color: #fff; |
| | | position: fixed; |
| | | top: 50%; |
| | | left: 50%; |
| | | transform: translate(-50%, -50%); |
| | | transition-duration: 0.3s; |
| | | z-index: 999; |
| | | } |
| | | |
| | | .fix-nvue-height { |
| | | /* #ifdef APP-NVUE */ |
| | | height: 330px; |
| | | /* #endif */ |
| | | } |
| | | |
| | | .uni-datetime-picker-time { |
| | | color: grey; |
| | | } |
| | | |
| | | .uni-datetime-picker-column { |
| | | height: 50px; |
| | | } |
| | | |
| | | .uni-datetime-picker-timebox { |
| | | |
| | | border: 1px solid #E5E5E5; |
| | | border-radius: 5px; |
| | | padding: 7px 10px; |
| | | /* #ifndef APP-NVUE */ |
| | | box-sizing: border-box; |
| | | cursor: pointer; |
| | | /* #endif */ |
| | | } |
| | | |
| | | .uni-datetime-picker-timebox-pointer { |
| | | /* #ifndef APP-NVUE */ |
| | | cursor: pointer; |
| | | /* #endif */ |
| | | } |
| | | |
| | | |
| | | .uni-datetime-picker-disabled { |
| | | opacity: 0.4; |
| | | /* #ifdef H5 */ |
| | | cursor: not-allowed !important; |
| | | /* #endif */ |
| | | } |
| | | |
| | | .uni-datetime-picker-text { |
| | | font-size: 14px; |
| | | } |
| | | |
| | | .uni-datetime-picker-sign { |
| | | position: absolute; |
| | | top: 53px; |
| | | /* 减掉 10px 的元素高度,兼容nvue */ |
| | | color: #999; |
| | | /* #ifdef APP-NVUE */ |
| | | font-size: 16px; |
| | | /* #endif */ |
| | | } |
| | | |
| | | .sign-left { |
| | | left: 86px; |
| | | } |
| | | |
| | | .sign-right { |
| | | right: 86px; |
| | | } |
| | | |
| | | .sign-center { |
| | | left: 135px; |
| | | } |
| | | |
| | | .uni-datetime-picker__container-box { |
| | | position: relative; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | margin-top: 40px; |
| | | } |
| | | |
| | | .time-hide-second { |
| | | width: 180px; |
| | | } |
| | | </style> |
| | | <template>
|
| | | <view class="uni-datetime-picker">
|
| | | <view @click="initTimePicker">
|
| | | <slot>
|
| | | <view class="uni-datetime-picker-timebox-pointer"
|
| | | :class="{'uni-datetime-picker-disabled': disabled, 'uni-datetime-picker-timebox': border}">
|
| | | <text class="uni-datetime-picker-text">{{time}}</text>
|
| | | <view v-if="!time" class="uni-datetime-picker-time">
|
| | | <text class="uni-datetime-picker-text">{{selectTimeText}}</text>
|
| | | </view>
|
| | | </view>
|
| | | </slot>
|
| | | </view>
|
| | | <view v-if="visible" id="mask" class="uni-datetime-picker-mask" @click="tiggerTimePicker"></view>
|
| | | <view v-if="visible" class="uni-datetime-picker-popup" :class="[dateShow && timeShow ? '' : 'fix-nvue-height']"
|
| | | :style="fixNvueBug">
|
| | | <view class="uni-title">
|
| | | <text class="uni-datetime-picker-text">{{selectTimeText}}</text>
|
| | | </view>
|
| | | <view v-if="dateShow" class="uni-datetime-picker__container-box">
|
| | | <picker-view class="uni-datetime-picker-view" :indicator-style="indicatorStyle" :value="ymd"
|
| | | @change="bindDateChange">
|
| | | <picker-view-column>
|
| | | <view class="uni-datetime-picker-item" v-for="(item,index) in years" :key="index">
|
| | | <text class="uni-datetime-picker-item">{{lessThanTen(item)}}</text>
|
| | | </view>
|
| | | </picker-view-column>
|
| | | <picker-view-column>
|
| | | <view class="uni-datetime-picker-item" v-for="(item,index) in months" :key="index">
|
| | | <text class="uni-datetime-picker-item">{{lessThanTen(item)}}</text>
|
| | | </view>
|
| | | </picker-view-column>
|
| | | <picker-view-column>
|
| | | <view class="uni-datetime-picker-item" v-for="(item,index) in days" :key="index">
|
| | | <text class="uni-datetime-picker-item">{{lessThanTen(item)}}</text>
|
| | | </view>
|
| | | </picker-view-column>
|
| | | </picker-view>
|
| | | <!-- 兼容 nvue 不支持伪类 -->
|
| | | <text class="uni-datetime-picker-sign sign-left">-</text>
|
| | | <text class="uni-datetime-picker-sign sign-right">-</text>
|
| | | </view>
|
| | | <view v-if="timeShow" class="uni-datetime-picker__container-box">
|
| | | <picker-view class="uni-datetime-picker-view" :class="[hideSecond ? 'time-hide-second' : '']"
|
| | | :indicator-style="indicatorStyle" :value="hms" @change="bindTimeChange">
|
| | | <picker-view-column>
|
| | | <view class="uni-datetime-picker-item" v-for="(item,index) in hours" :key="index">
|
| | | <text class="uni-datetime-picker-item">{{lessThanTen(item)}}</text>
|
| | | </view>
|
| | | </picker-view-column>
|
| | | <picker-view-column>
|
| | | <view class="uni-datetime-picker-item" v-for="(item,index) in minutes" :key="index">
|
| | | <text class="uni-datetime-picker-item">{{lessThanTen(item)}}</text>
|
| | | </view>
|
| | | </picker-view-column>
|
| | | <picker-view-column v-if="!hideSecond">
|
| | | <view class="uni-datetime-picker-item" v-for="(item,index) in seconds" :key="index">
|
| | | <text class="uni-datetime-picker-item">{{lessThanTen(item)}}</text>
|
| | | </view>
|
| | | </picker-view-column>
|
| | | </picker-view>
|
| | | <!-- 兼容 nvue 不支持伪类 -->
|
| | | <text class="uni-datetime-picker-sign" :class="[hideSecond ? 'sign-center' : 'sign-left']">:</text>
|
| | | <text v-if="!hideSecond" class="uni-datetime-picker-sign sign-right">:</text>
|
| | | </view>
|
| | | <view class="uni-datetime-picker-btn">
|
| | | <view @click="clearTime">
|
| | | <text class="uni-datetime-picker-btn-text">{{clearText}}</text>
|
| | | </view>
|
| | | <view class="uni-datetime-picker-btn-group">
|
| | | <view class="uni-datetime-picker-cancel" @click="tiggerTimePicker">
|
| | | <text class="uni-datetime-picker-btn-text">{{cancelText}}</text>
|
| | | </view>
|
| | | <view @click="setTime">
|
| | | <text class="uni-datetime-picker-btn-text">{{okText}}</text>
|
| | | </view>
|
| | | </view>
|
| | | </view>
|
| | | </view>
|
| | | <!-- #ifdef H5 -->
|
| | | <!-- <keypress v-if="visible" @esc="tiggerTimePicker" @enter="setTime" /> -->
|
| | | <!-- #endif -->
|
| | | </view>
|
| | | </template>
|
| | |
|
| | | <script>
|
| | | // #ifdef H5
|
| | | import keypress from './keypress'
|
| | | // #endif
|
| | | import {
|
| | | initVueI18n
|
| | | } from '@dcloudio/uni-i18n'
|
| | | import messages from './i18n/index.js'
|
| | | const { t } = initVueI18n(messages)
|
| | |
|
| | | /**
|
| | | * DatetimePicker 时间选择器
|
| | | * @description 可以同时选择日期和时间的选择器
|
| | | * @tutorial https://ext.dcloud.net.cn/plugin?id=xxx
|
| | | * @property {String} type = [datetime | date | time] 显示模式
|
| | | * @property {Boolean} multiple = [true|false] 是否多选
|
| | | * @property {String|Number} value 默认值
|
| | | * @property {String|Number} start 起始日期或时间
|
| | | * @property {String|Number} end 起始日期或时间
|
| | | * @property {String} return-type = [timestamp | string]
|
| | | * @event {Function} change 选中发生变化触发
|
| | | */
|
| | |
|
| | | export default {
|
| | | name: 'UniDatetimePicker',
|
| | | components: {
|
| | | // #ifdef H5
|
| | | keypress
|
| | | // #endif
|
| | | },
|
| | | data() {
|
| | | return {
|
| | | indicatorStyle: `height: 50px;`,
|
| | | visible: false,
|
| | | fixNvueBug: {},
|
| | | dateShow: true,
|
| | | timeShow: true,
|
| | | title: '日期和时间',
|
| | | // 输入框当前时间
|
| | | time: '',
|
| | | // 当前的年月日时分秒
|
| | | year: 1920,
|
| | | month: 0,
|
| | | day: 0,
|
| | | hour: 0,
|
| | | minute: 0,
|
| | | second: 0,
|
| | | // 起始时间
|
| | | startYear: 1920,
|
| | | startMonth: 1,
|
| | | startDay: 1,
|
| | | startHour: 0,
|
| | | startMinute: 0,
|
| | | startSecond: 0,
|
| | | // 结束时间
|
| | | endYear: 2120,
|
| | | endMonth: 12,
|
| | | endDay: 31,
|
| | | endHour: 23,
|
| | | endMinute: 59,
|
| | | endSecond: 59,
|
| | | }
|
| | | },
|
| | | props: {
|
| | | type: {
|
| | | type: String,
|
| | | default: 'datetime'
|
| | | },
|
| | | value: {
|
| | | type: [String, Number],
|
| | | default: ''
|
| | | },
|
| | | modelValue: {
|
| | | type: [String, Number],
|
| | | default: ''
|
| | | },
|
| | | start: {
|
| | | type: [Number, String],
|
| | | default: ''
|
| | | },
|
| | | end: {
|
| | | type: [Number, String],
|
| | | default: ''
|
| | | },
|
| | | returnType: {
|
| | | type: String,
|
| | | default: 'string'
|
| | | },
|
| | | disabled: {
|
| | | type: [Boolean, String],
|
| | | default: false
|
| | | },
|
| | | border: {
|
| | | type: [Boolean, String],
|
| | | default: true
|
| | | },
|
| | | hideSecond: {
|
| | | type: [Boolean, String],
|
| | | default: false
|
| | | }
|
| | | },
|
| | | watch: {
|
| | | value: {
|
| | | handler(newVal, oldVal) {
|
| | | if (newVal) {
|
| | | this.parseValue(this.fixIosDateFormat(newVal)) //兼容 iOS、safari 日期格式
|
| | | this.initTime(false)
|
| | | } else {
|
| | | this.time = ''
|
| | | this.parseValue(Date.now())
|
| | | }
|
| | | },
|
| | | immediate: true
|
| | | },
|
| | | type: {
|
| | | handler(newValue) {
|
| | | if (newValue === 'date') {
|
| | | this.dateShow = true
|
| | | this.timeShow = false
|
| | | this.title = '日期'
|
| | | } else if (newValue === 'time') {
|
| | | this.dateShow = false
|
| | | this.timeShow = true
|
| | | this.title = '时间'
|
| | | } else {
|
| | | this.dateShow = true
|
| | | this.timeShow = true
|
| | | this.title = '日期和时间'
|
| | | }
|
| | | },
|
| | | immediate: true
|
| | | },
|
| | | start: {
|
| | | handler(newVal) {
|
| | | this.parseDatetimeRange(this.fixIosDateFormat(newVal), 'start') //兼容 iOS、safari 日期格式
|
| | | },
|
| | | immediate: true
|
| | | },
|
| | | end: {
|
| | | handler(newVal) {
|
| | | this.parseDatetimeRange(this.fixIosDateFormat(newVal), 'end') //兼容 iOS、safari 日期格式
|
| | | },
|
| | | immediate: true
|
| | | },
|
| | |
|
| | | // 月、日、时、分、秒可选范围变化后,检查当前值是否在范围内,不在则当前值重置为可选范围第一项
|
| | | months(newVal) {
|
| | | this.checkValue('month', this.month, newVal)
|
| | | },
|
| | | days(newVal) {
|
| | | this.checkValue('day', this.day, newVal)
|
| | | },
|
| | | hours(newVal) {
|
| | | this.checkValue('hour', this.hour, newVal)
|
| | | },
|
| | | minutes(newVal) {
|
| | | this.checkValue('minute', this.minute, newVal)
|
| | | },
|
| | | seconds(newVal) {
|
| | | this.checkValue('second', this.second, newVal)
|
| | | }
|
| | | },
|
| | | computed: {
|
| | | // 当前年、月、日、时、分、秒选择范围
|
| | | years() {
|
| | | return this.getCurrentRange('year')
|
| | | },
|
| | |
|
| | | months() {
|
| | | return this.getCurrentRange('month')
|
| | | },
|
| | |
|
| | | days() {
|
| | | return this.getCurrentRange('day')
|
| | | },
|
| | |
|
| | | hours() {
|
| | | return this.getCurrentRange('hour')
|
| | | },
|
| | |
|
| | | minutes() {
|
| | | return this.getCurrentRange('minute')
|
| | | },
|
| | |
|
| | | seconds() {
|
| | | return this.getCurrentRange('second')
|
| | | },
|
| | |
|
| | | // picker 当前值数组
|
| | | ymd() {
|
| | | return [this.year - this.minYear, this.month - this.minMonth, this.day - this.minDay]
|
| | | },
|
| | | hms() {
|
| | | return [this.hour - this.minHour, this.minute - this.minMinute, this.second - this.minSecond]
|
| | | },
|
| | |
|
| | | // 当前 date 是 start
|
| | | currentDateIsStart() {
|
| | | return this.year === this.startYear && this.month === this.startMonth && this.day === this.startDay
|
| | | },
|
| | |
|
| | | // 当前 date 是 end
|
| | | currentDateIsEnd() {
|
| | | return this.year === this.endYear && this.month === this.endMonth && this.day === this.endDay
|
| | | },
|
| | |
|
| | | // 当前年、月、日、时、分、秒的最小值和最大值
|
| | | minYear() {
|
| | | return this.startYear
|
| | | },
|
| | | maxYear() {
|
| | | return this.endYear
|
| | | },
|
| | | minMonth() {
|
| | | if (this.year === this.startYear) {
|
| | | return this.startMonth
|
| | | } else {
|
| | | return 1
|
| | | }
|
| | | },
|
| | | maxMonth() {
|
| | | if (this.year === this.endYear) {
|
| | | return this.endMonth
|
| | | } else {
|
| | | return 12
|
| | | }
|
| | | },
|
| | | minDay() {
|
| | | if (this.year === this.startYear && this.month === this.startMonth) {
|
| | | return this.startDay
|
| | | } else {
|
| | | return 1
|
| | | }
|
| | | },
|
| | | maxDay() {
|
| | | if (this.year === this.endYear && this.month === this.endMonth) {
|
| | | return this.endDay
|
| | | } else {
|
| | | return this.daysInMonth(this.year, this.month)
|
| | | }
|
| | | },
|
| | | minHour() {
|
| | | if (this.type === 'datetime') {
|
| | | if (this.currentDateIsStart) {
|
| | | return this.startHour
|
| | | } else {
|
| | | return 0
|
| | | }
|
| | | }
|
| | | if (this.type === 'time') {
|
| | | return this.startHour
|
| | | }
|
| | | },
|
| | | maxHour() {
|
| | | if (this.type === 'datetime') {
|
| | | if (this.currentDateIsEnd) {
|
| | | return this.endHour
|
| | | } else {
|
| | | return 23
|
| | | }
|
| | | }
|
| | | if (this.type === 'time') {
|
| | | return this.endHour
|
| | | }
|
| | | },
|
| | | minMinute() {
|
| | | if (this.type === 'datetime') {
|
| | | if (this.currentDateIsStart && this.hour === this.startHour) {
|
| | | return this.startMinute
|
| | | } else {
|
| | | return 0
|
| | | }
|
| | | }
|
| | | if (this.type === 'time') {
|
| | | if (this.hour === this.startHour) {
|
| | | return this.startMinute
|
| | | } else {
|
| | | return 0
|
| | | }
|
| | | }
|
| | | },
|
| | | maxMinute() {
|
| | | if (this.type === 'datetime') {
|
| | | if (this.currentDateIsEnd && this.hour === this.endHour) {
|
| | | return this.endMinute
|
| | | } else {
|
| | | return 59
|
| | | }
|
| | | }
|
| | | if (this.type === 'time') {
|
| | | if (this.hour === this.endHour) {
|
| | | return this.endMinute
|
| | | } else {
|
| | | return 59
|
| | | }
|
| | | }
|
| | | },
|
| | | minSecond() {
|
| | | if (this.type === 'datetime') {
|
| | | if (this.currentDateIsStart && this.hour === this.startHour && this.minute === this.startMinute) {
|
| | | return this.startSecond
|
| | | } else {
|
| | | return 0
|
| | | }
|
| | | }
|
| | | if (this.type === 'time') {
|
| | | if (this.hour === this.startHour && this.minute === this.startMinute) {
|
| | | return this.startSecond
|
| | | } else {
|
| | | return 0
|
| | | }
|
| | | }
|
| | | },
|
| | | maxSecond() {
|
| | | if (this.type === 'datetime') {
|
| | | if (this.currentDateIsEnd && this.hour === this.endHour && this.minute === this.endMinute) {
|
| | | return this.endSecond
|
| | | } else {
|
| | | return 59
|
| | | }
|
| | | }
|
| | | if (this.type === 'time') {
|
| | | if (this.hour === this.endHour && this.minute === this.endMinute) {
|
| | | return this.endSecond
|
| | | } else {
|
| | | return 59
|
| | | }
|
| | | }
|
| | | },
|
| | |
|
| | | /**
|
| | | * for i18n
|
| | | */
|
| | | selectTimeText() {
|
| | | return t("uni-datetime-picker.selectTime")
|
| | | },
|
| | | okText() {
|
| | | return t("uni-datetime-picker.ok")
|
| | | },
|
| | | clearText() {
|
| | | return t("uni-datetime-picker.clear")
|
| | | },
|
| | | cancelText() {
|
| | | return t("uni-datetime-picker.cancel")
|
| | | }
|
| | | },
|
| | |
|
| | | mounted() {
|
| | | // #ifdef APP-NVUE
|
| | | const res = uni.getSystemInfoSync();
|
| | | this.fixNvueBug = {
|
| | | top: res.windowHeight / 2,
|
| | | left: res.windowWidth / 2
|
| | | }
|
| | | // #endif
|
| | | },
|
| | |
|
| | | methods: {
|
| | | /**
|
| | | * @param {Object} item
|
| | | * 小于 10 在前面加个 0
|
| | | */
|
| | |
|
| | | lessThanTen(item) {
|
| | | return item < 10 ? '0' + item : item
|
| | | },
|
| | |
|
| | | /**
|
| | | * 解析时分秒字符串,例如:00:00:00
|
| | | * @param {String} timeString
|
| | | */
|
| | | parseTimeType(timeString) {
|
| | | if (timeString) {
|
| | | let timeArr = timeString.split(':')
|
| | | this.hour = Number(timeArr[0])
|
| | | this.minute = Number(timeArr[1])
|
| | | this.second = Number(timeArr[2])
|
| | | }
|
| | | },
|
| | |
|
| | | /**
|
| | | * 解析选择器初始值,类型可以是字符串、时间戳,例如:2000-10-02、'08:30:00'、 1610695109000
|
| | | * @param {String | Number} datetime
|
| | | */
|
| | | initPickerValue(datetime) {
|
| | | let defaultValue = null
|
| | | if (datetime) {
|
| | | defaultValue = this.compareValueWithStartAndEnd(datetime, this.start, this.end)
|
| | | } else {
|
| | | defaultValue = Date.now()
|
| | | defaultValue = this.compareValueWithStartAndEnd(defaultValue, this.start, this.end)
|
| | | }
|
| | | this.parseValue(defaultValue)
|
| | | },
|
| | |
|
| | | /**
|
| | | * 初始值规则:
|
| | | * - 用户设置初始值 value
|
| | | * - 设置了起始时间 start、终止时间 end,并 start < value < end,初始值为 value, 否则初始值为 start
|
| | | * - 只设置了起始时间 start,并 start < value,初始值为 value,否则初始值为 start
|
| | | * - 只设置了终止时间 end,并 value < end,初始值为 value,否则初始值为 end
|
| | | * - 无起始终止时间,则初始值为 value
|
| | | * - 无初始值 value,则初始值为当前本地时间 Date.now()
|
| | | * @param {Object} value
|
| | | * @param {Object} dateBase
|
| | | */
|
| | | compareValueWithStartAndEnd(value, start, end) {
|
| | | let winner = null
|
| | | value = this.superTimeStamp(value)
|
| | | start = this.superTimeStamp(start)
|
| | | end = this.superTimeStamp(end)
|
| | |
|
| | | if (start && end) {
|
| | | if (value < start) {
|
| | | winner = new Date(start)
|
| | | } else if (value > end) {
|
| | | winner = new Date(end)
|
| | | } else {
|
| | | winner = new Date(value)
|
| | | }
|
| | | } else if (start && !end) {
|
| | | winner = start <= value ? new Date(value) : new Date(start)
|
| | | } else if (!start && end) {
|
| | | winner = value <= end ? new Date(value) : new Date(end)
|
| | | } else {
|
| | | winner = new Date(value)
|
| | | }
|
| | |
|
| | | return winner
|
| | | },
|
| | |
|
| | | /**
|
| | | * 转换为可比较的时间戳,接受日期、时分秒、时间戳
|
| | | * @param {Object} value
|
| | | */
|
| | | superTimeStamp(value) {
|
| | | let dateBase = ''
|
| | | if (this.type === 'time' && value && typeof value === 'string') {
|
| | | const now = new Date()
|
| | | const year = now.getFullYear()
|
| | | const month = now.getMonth() + 1
|
| | | const day = now.getDate()
|
| | | dateBase = year + '/' + month + '/' + day + ' '
|
| | | }
|
| | | if (Number(value) && typeof value !== NaN) {
|
| | | value = parseInt(value)
|
| | | dateBase = 0
|
| | | }
|
| | | return this.createTimeStamp(dateBase + value)
|
| | | },
|
| | |
|
| | | /**
|
| | | * 解析默认值 value,字符串、时间戳
|
| | | * @param {Object} defaultTime
|
| | | */
|
| | | parseValue(value) {
|
| | | if (!value) {
|
| | | return
|
| | | }
|
| | | if (this.type === 'time' && typeof value === "string") {
|
| | | this.parseTimeType(value)
|
| | | } else {
|
| | | let defaultDate = null
|
| | | defaultDate = new Date(value)
|
| | | if (this.type !== 'time') {
|
| | | this.year = defaultDate.getFullYear()
|
| | | this.month = defaultDate.getMonth() + 1
|
| | | this.day = defaultDate.getDate()
|
| | | }
|
| | | if (this.type !== 'date') {
|
| | | this.hour = defaultDate.getHours()
|
| | | this.minute = defaultDate.getMinutes()
|
| | | this.second = defaultDate.getSeconds()
|
| | | }
|
| | | }
|
| | | if (this.hideSecond) {
|
| | | this.second = 0
|
| | | }
|
| | | },
|
| | |
|
| | | /**
|
| | | * 解析可选择时间范围 start、end,年月日字符串、时间戳
|
| | | * @param {Object} defaultTime
|
| | | */
|
| | | parseDatetimeRange(point, pointType) {
|
| | | // 时间为空,则重置为初始值
|
| | | if (!point) {
|
| | | if (pointType === 'start') {
|
| | | this.startYear = 1920
|
| | | this.startMonth = 1
|
| | | this.startDay = 1
|
| | | this.startHour = 0
|
| | | this.startMinute = 0
|
| | | this.startSecond = 0
|
| | | }
|
| | | if (pointType === 'end') {
|
| | | this.endYear = 2120
|
| | | this.endMonth = 12
|
| | | this.endDay = 31
|
| | | this.endHour = 23
|
| | | this.endMinute = 59
|
| | | this.endSecond = 59
|
| | | }
|
| | | return
|
| | | }
|
| | | if (this.type === 'time') {
|
| | | const pointArr = point.split(':')
|
| | | this[pointType + 'Hour'] = Number(pointArr[0])
|
| | | this[pointType + 'Minute'] = Number(pointArr[1])
|
| | | this[pointType + 'Second'] = Number(pointArr[2])
|
| | | } else {
|
| | | if (!point) {
|
| | | pointType === 'start' ? this.startYear = this.year - 60 : this.endYear = this.year + 60
|
| | | return
|
| | | }
|
| | | if (Number(point) && Number(point) !== NaN) {
|
| | | point = parseInt(point)
|
| | | }
|
| | | // datetime 的 end 没有时分秒, 则不限制
|
| | | const hasTime = /[0-9]:[0-9]/
|
| | | if (this.type === 'datetime' && pointType === 'end' && typeof point === 'string' && !hasTime.test(
|
| | | point)) {
|
| | | point = point + ' 23:59:59'
|
| | | }
|
| | | const pointDate = new Date(point)
|
| | | this[pointType + 'Year'] = pointDate.getFullYear()
|
| | | this[pointType + 'Month'] = pointDate.getMonth() + 1
|
| | | this[pointType + 'Day'] = pointDate.getDate()
|
| | | if (this.type === 'datetime') {
|
| | | this[pointType + 'Hour'] = pointDate.getHours()
|
| | | this[pointType + 'Minute'] = pointDate.getMinutes()
|
| | | this[pointType + 'Second'] = pointDate.getSeconds()
|
| | | }
|
| | | }
|
| | | },
|
| | |
|
| | | // 获取 年、月、日、时、分、秒 当前可选范围
|
| | | getCurrentRange(value) {
|
| | | const range = []
|
| | | for (let i = this['min' + this.capitalize(value)]; i <= this['max' + this.capitalize(value)]; i++) {
|
| | | range.push(i)
|
| | | }
|
| | | return range
|
| | | },
|
| | |
|
| | | // 字符串首字母大写
|
| | | capitalize(str) {
|
| | | return str.charAt(0).toUpperCase() + str.slice(1)
|
| | | },
|
| | |
|
| | | // 检查当前值是否在范围内,不在则当前值重置为可选范围第一项
|
| | | checkValue(name, value, values) {
|
| | | if (values.indexOf(value) === -1) {
|
| | | this[name] = values[0]
|
| | | }
|
| | | },
|
| | |
|
| | | // 每个月的实际天数
|
| | | daysInMonth(year, month) { // Use 1 for January, 2 for February, etc.
|
| | | return new Date(year, month, 0).getDate();
|
| | | },
|
| | |
|
| | | //兼容 iOS、safari 日期格式
|
| | | fixIosDateFormat(value) {
|
| | | if (typeof value === 'string') {
|
| | | value = value.replace(/-/g, '/')
|
| | | }
|
| | | return value
|
| | | },
|
| | |
|
| | | /**
|
| | | * 生成时间戳
|
| | | * @param {Object} time
|
| | | */
|
| | | createTimeStamp(time) {
|
| | | if (!time) return
|
| | | if (typeof time === "number") {
|
| | | return time
|
| | | } else {
|
| | | time = time.replace(/-/g, '/')
|
| | | if (this.type === 'date') {
|
| | | time = time + ' ' + '00:00:00'
|
| | | }
|
| | | return Date.parse(time)
|
| | | }
|
| | | },
|
| | |
|
| | | /**
|
| | | * 生成日期或时间的字符串
|
| | | */
|
| | | createDomSting() {
|
| | | const yymmdd = this.year +
|
| | | '-' +
|
| | | this.lessThanTen(this.month) +
|
| | | '-' +
|
| | | this.lessThanTen(this.day)
|
| | |
|
| | | let hhmmss = this.lessThanTen(this.hour) +
|
| | | ':' +
|
| | | this.lessThanTen(this.minute)
|
| | |
|
| | | if (!this.hideSecond) {
|
| | | hhmmss = hhmmss + ':' + this.lessThanTen(this.second)
|
| | | }
|
| | |
|
| | | if (this.type === 'date') {
|
| | | return yymmdd
|
| | | } else if (this.type === 'time') {
|
| | | return hhmmss
|
| | | } else {
|
| | | return yymmdd + ' ' + hhmmss
|
| | | }
|
| | | },
|
| | |
|
| | | /**
|
| | | * 初始化返回值,并抛出 change 事件
|
| | | */
|
| | | initTime(emit = true) {
|
| | | this.time = this.createDomSting()
|
| | | if (!emit) return
|
| | | if (this.returnType === 'timestamp' && this.type !== 'time') {
|
| | | this.$emit('change', this.createTimeStamp(this.time))
|
| | | this.$emit('input', this.createTimeStamp(this.time))
|
| | | this.$emit('update:modelValue', this.createTimeStamp(this.time))
|
| | | } else {
|
| | | this.$emit('change', this.time)
|
| | | this.$emit('input', this.time)
|
| | | this.$emit('update:modelValue', this.time)
|
| | | }
|
| | | },
|
| | |
|
| | | /**
|
| | | * 用户选择日期或时间更新 data
|
| | | * @param {Object} e
|
| | | */
|
| | | bindDateChange(e) {
|
| | | const val = e.detail.value
|
| | | this.year = this.years[val[0]]
|
| | | this.month = this.months[val[1]]
|
| | | this.day = this.days[val[2]]
|
| | | },
|
| | | bindTimeChange(e) {
|
| | | const val = e.detail.value
|
| | | this.hour = this.hours[val[0]]
|
| | | this.minute = this.minutes[val[1]]
|
| | | this.second = this.seconds[val[2]]
|
| | | },
|
| | |
|
| | | /**
|
| | | * 初始化弹出层
|
| | | */
|
| | | initTimePicker() {
|
| | | if (this.disabled) return
|
| | | const value = this.fixIosDateFormat(this.value)
|
| | | this.initPickerValue(value)
|
| | | this.visible = !this.visible
|
| | | },
|
| | |
|
| | | /**
|
| | | * 触发或关闭弹框
|
| | | */
|
| | | tiggerTimePicker(e) {
|
| | | this.visible = !this.visible
|
| | | },
|
| | |
|
| | | /**
|
| | | * 用户点击“清空”按钮,清空当前值
|
| | | */
|
| | | clearTime() {
|
| | | this.time = ''
|
| | | this.$emit('change', this.time)
|
| | | this.$emit('input', this.time)
|
| | | this.$emit('update:modelValue', this.time)
|
| | | this.tiggerTimePicker()
|
| | | },
|
| | |
|
| | | /**
|
| | | * 用户点击“确定”按钮
|
| | | */
|
| | | setTime() {
|
| | | this.initTime()
|
| | | this.tiggerTimePicker()
|
| | | }
|
| | | }
|
| | | }
|
| | | </script>
|
| | |
|
| | | <style>
|
| | | .uni-datetime-picker {
|
| | | /* #ifndef APP-NVUE */
|
| | | /* width: 100%; */
|
| | | /* #endif */
|
| | | }
|
| | |
|
| | | .uni-datetime-picker-view {
|
| | | height: 130px;
|
| | | width: 270px;
|
| | | /* #ifndef APP-NVUE */
|
| | | cursor: pointer;
|
| | | /* #endif */
|
| | | }
|
| | |
|
| | | .uni-datetime-picker-item {
|
| | | height: 50px;
|
| | | line-height: 50px;
|
| | | text-align: center;
|
| | | font-size: 14px;
|
| | | }
|
| | |
|
| | | .uni-datetime-picker-btn {
|
| | | margin-top: 60px;
|
| | | /* #ifndef APP-NVUE */
|
| | | display: flex;
|
| | | cursor: pointer;
|
| | | /* #endif */
|
| | | flex-direction: row;
|
| | | justify-content: space-between;
|
| | | }
|
| | |
|
| | | .uni-datetime-picker-btn-text {
|
| | | font-size: 14px;
|
| | | color: #007AFF;
|
| | | }
|
| | |
|
| | | .uni-datetime-picker-btn-group {
|
| | | /* #ifndef APP-NVUE */
|
| | | display: flex;
|
| | | /* #endif */
|
| | | flex-direction: row;
|
| | | }
|
| | |
|
| | | .uni-datetime-picker-cancel {
|
| | | margin-right: 30px;
|
| | | }
|
| | |
|
| | | .uni-datetime-picker-mask {
|
| | | position: fixed;
|
| | | bottom: 0px;
|
| | | top: 0px;
|
| | | left: 0px;
|
| | | right: 0px;
|
| | | background-color: rgba(0, 0, 0, 0.4);
|
| | | transition-duration: 0.3s;
|
| | | z-index: 998;
|
| | | }
|
| | |
|
| | | .uni-datetime-picker-popup {
|
| | | border-radius: 8px;
|
| | | padding: 30px;
|
| | | width: 270px;
|
| | | /* #ifdef APP-NVUE */
|
| | | height: 500px;
|
| | | /* #endif */
|
| | | /* #ifdef APP-NVUE */
|
| | | width: 330px;
|
| | | /* #endif */
|
| | | background-color: #fff;
|
| | | position: fixed;
|
| | | top: 50%;
|
| | | left: 50%;
|
| | | transform: translate(-50%, -50%);
|
| | | transition-duration: 0.3s;
|
| | | z-index: 999;
|
| | | }
|
| | |
|
| | | .fix-nvue-height {
|
| | | /* #ifdef APP-NVUE */
|
| | | height: 330px;
|
| | | /* #endif */
|
| | | }
|
| | |
|
| | | .uni-datetime-picker-time {
|
| | | color: grey;
|
| | | }
|
| | |
|
| | | .uni-datetime-picker-column {
|
| | | height: 50px;
|
| | | }
|
| | |
|
| | | .uni-datetime-picker-timebox {
|
| | |
|
| | | border: 1px solid #E5E5E5;
|
| | | border-radius: 5px;
|
| | | padding: 7px 10px;
|
| | | /* #ifndef APP-NVUE */
|
| | | box-sizing: border-box;
|
| | | cursor: pointer;
|
| | | /* #endif */
|
| | | }
|
| | |
|
| | | .uni-datetime-picker-timebox-pointer {
|
| | | /* #ifndef APP-NVUE */
|
| | | cursor: pointer;
|
| | | /* #endif */
|
| | | }
|
| | |
|
| | |
|
| | | .uni-datetime-picker-disabled {
|
| | | opacity: 0.4;
|
| | | /* #ifdef H5 */
|
| | | cursor: not-allowed !important;
|
| | | /* #endif */
|
| | | }
|
| | |
|
| | | .uni-datetime-picker-text {
|
| | | font-size: 14px;
|
| | | }
|
| | |
|
| | | .uni-datetime-picker-sign {
|
| | | position: absolute;
|
| | | top: 53px;
|
| | | /* 减掉 10px 的元素高度,兼容nvue */
|
| | | color: #999;
|
| | | /* #ifdef APP-NVUE */
|
| | | font-size: 16px;
|
| | | /* #endif */
|
| | | }
|
| | |
|
| | | .sign-left {
|
| | | left: 86px;
|
| | | }
|
| | |
|
| | | .sign-right {
|
| | | right: 86px;
|
| | | }
|
| | |
|
| | | .sign-center {
|
| | | left: 135px;
|
| | | }
|
| | |
|
| | | .uni-datetime-picker__container-box {
|
| | | position: relative;
|
| | | display: flex;
|
| | | align-items: center;
|
| | | justify-content: center;
|
| | | margin-top: 40px;
|
| | | }
|
| | |
|
| | | .time-hide-second {
|
| | | width: 180px;
|
| | | }
|
| | | </style>
|