Commit 66f2f988fcfb5216cd67a28607c76ff4aff5f82f

Authored by fengtao
1 parent 066d78cb

feat:新增一个时间选择插件,替换告警筛选的时间插件

  1 +## 2.2.3(2022-03-28)
  2 +- 修复 Vue3 下动态赋值未响应的 bug
  3 +## 2.2.2(2021-12-10)
  4 +- 修复 clear-icon 属性在小程序平台不生效的 bug
  5 +## 2.2.1(2021-12-10)
  6 +- 修复 日期范围选在小程序平台,必须多点击一次才能取消选中状态的 bug
  7 +## 2.2.0(2021-11-19)
  8 +- 优化 组件UI,并提供设计资源,详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource)
  9 +- 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-datetime-picker](https://uniapp.dcloud.io/component/uniui/uni-datetime-picker)
  10 +## 2.1.5(2021-11-09)
  11 +- 新增 提供组件设计资源,组件样式调整
  12 +## 2.1.4(2021-09-10)
  13 +- 修复 hide-second 在移动端的 bug
  14 +- 修复 单选赋默认值时,赋值日期未高亮的 bug
  15 +- 修复 赋默认值时,移动端未正确显示时间的 bug
  16 +## 2.1.3(2021-09-09)
  17 +- 新增 hide-second 属性,支持只使用时分,隐藏秒
  18 +## 2.1.2(2021-09-03)
  19 +- 优化 取消选中时(范围选)直接开始下一次选择, 避免多点一次
  20 +- 优化 移动端支持清除按钮,同时支持通过 ref 调用组件的 clear 方法
  21 +- 优化 调整字号大小,美化日历界面
  22 +- 修复 因国际化导致的 placeholder 失效的 bug
  23 +## 2.1.1(2021-08-24)
  24 +- 新增 支持国际化
  25 +- 优化 范围选择器在 pc 端过宽的问题
  26 +## 2.1.0(2021-08-09)
  27 +- 新增 适配 vue3
  28 +## 2.0.19(2021-08-09)
  29 +- 新增 支持作为 uni-forms 子组件相关功能
  30 +- 修复 在 uni-forms 中使用时,选择时间报 NAN 错误的 bug
  31 +## 2.0.18(2021-08-05)
  32 +- 修复 type 属性动态赋值无效的 bug
  33 +- 修复 ‘确认’按钮被 tabbar 遮盖 bug
  34 +- 修复 组件未赋值时范围选左、右日历相同的 bug
  35 +## 2.0.17(2021-08-04)
  36 +- 修复 范围选未正确显示当前值的 bug
  37 +- 修复 h5 平台(移动端)报错 'cale' of undefined 的 bug
  38 +## 2.0.16(2021-07-21)
  39 +- 新增 return-type 属性支持返回 date 日期对象
  40 +## 2.0.15(2021-07-14)
  41 +- 修复 单选日期类型,初始赋值后不在当前日历的 bug
  42 +- 新增 clearIcon 属性,显示框的清空按钮可配置显示隐藏(仅 pc 有效)
  43 +- 优化 移动端移除显示框的清空按钮,无实际用途
  44 +## 2.0.14(2021-07-14)
  45 +- 修复 组件赋值为空,界面未更新的 bug
  46 +- 修复 start 和 end 不能动态赋值的 bug
  47 +- 修复 范围选类型,用户选择后再次选择右侧日历(结束日期)显示不正确的 bug
  48 +## 2.0.13(2021-07-08)
  49 +- 修复 范围选择不能动态赋值的 bug
  50 +## 2.0.12(2021-07-08)
  51 +- 修复 范围选择的初始时间在一个月内时,造成无法选择的bug
  52 +## 2.0.11(2021-07-08)
  53 +- 优化 弹出层在超出视窗边缘定位不准确的问题
  54 +## 2.0.10(2021-07-08)
  55 +- 修复 范围起始点样式的背景色与今日样式的字体前景色融合,导致日期字体看不清的 bug
  56 +- 优化 弹出层在超出视窗边缘被遮盖的问题
  57 +## 2.0.9(2021-07-07)
  58 +- 新增 maskClick 事件
  59 +- 修复 特殊情况日历 rpx 布局错误的 bug,rpx -> px
  60 +- 修复 范围选择时清空返回值不合理的bug,['', ''] -> []
  61 +## 2.0.8(2021-07-07)
  62 +- 新增 日期时间显示框支持插槽
  63 +## 2.0.7(2021-07-01)
  64 +- 优化 添加 uni-icons 依赖
  65 +## 2.0.6(2021-05-22)
  66 +- 修复 图标在小程序上不显示的 bug
  67 +- 优化 重命名引用组件,避免潜在组件命名冲突
  68 +## 2.0.5(2021-05-20)
  69 +- 优化 代码目录扁平化
  70 +## 2.0.4(2021-05-12)
  71 +- 新增 组件示例地址
  72 +## 2.0.3(2021-05-10)
  73 +- 修复 ios 下不识别 '-' 日期格式的 bug
  74 +- 优化 pc 下弹出层添加边框和阴影
  75 +## 2.0.2(2021-05-08)
  76 +- 修复 在 admin 中获取弹出层定位错误的bug
  77 +## 2.0.1(2021-05-08)
  78 +- 修复 type 属性向下兼容,默认值从 date 变更为 datetime
  79 +## 2.0.0(2021-04-30)
  80 +- 支持日历形式的日期+时间的范围选择
  81 + > 注意:此版本不向后兼容,不再支持单独时间选择(type=time)及相关的 hide-second 属性(时间选可使用内置组件 picker)
  82 +## 1.0.6(2021-03-18)
  83 +- 新增 hide-second 属性,时间支持仅选择时、分
  84 +- 修复 选择跟显示的日期不一样的 bug
  85 +- 修复 chang事件触发2次的 bug
  86 +- 修复 分、秒 end 范围错误的 bug
  87 +- 优化 更好的 nvue 适配
... ...
  1 +<template>
  2 + <view class="uni-calendar-item__weeks-box" :class="{
  3 + 'uni-calendar-item--disable':weeks.disable,
  4 + 'uni-calendar-item--before-checked-x':weeks.beforeMultiple,
  5 + 'uni-calendar-item--multiple': weeks.multiple,
  6 + 'uni-calendar-item--after-checked-x':weeks.afterMultiple,
  7 + }" @click="choiceDate(weeks)" @mouseenter="handleMousemove(weeks)">
  8 + <view class="uni-calendar-item__weeks-box-item" :class="{
  9 + 'uni-calendar-item--checked':calendar.fullDate === weeks.fullDate && (calendar.userChecked || !checkHover),
  10 + 'uni-calendar-item--checked-range-text': checkHover,
  11 + 'uni-calendar-item--before-checked':weeks.beforeMultiple,
  12 + 'uni-calendar-item--multiple': weeks.multiple,
  13 + 'uni-calendar-item--after-checked':weeks.afterMultiple,
  14 + 'uni-calendar-item--disable':weeks.disable,
  15 + }">
  16 + <text v-if="selected&&weeks.extraInfo" class="uni-calendar-item__weeks-box-circle"></text>
  17 + <text class="uni-calendar-item__weeks-box-text uni-calendar-item__weeks-box-text-disable uni-calendar-item--checked-text">{{weeks.date}}</text>
  18 + </view>
  19 + <view :class="{'uni-calendar-item--isDay': weeks.isDay}"></view>
  20 + </view>
  21 +</template>
  22 +
  23 +<script>
  24 + export default {
  25 + props: {
  26 + weeks: {
  27 + type: Object,
  28 + default () {
  29 + return {}
  30 + }
  31 + },
  32 + calendar: {
  33 + type: Object,
  34 + default: () => {
  35 + return {}
  36 + }
  37 + },
  38 + selected: {
  39 + type: Array,
  40 + default: () => {
  41 + return []
  42 + }
  43 + },
  44 + lunar: {
  45 + type: Boolean,
  46 + default: false
  47 + },
  48 + checkHover: {
  49 + type: Boolean,
  50 + default: false
  51 + }
  52 + },
  53 + methods: {
  54 + choiceDate(weeks) {
  55 + this.$emit('change', weeks)
  56 + },
  57 + handleMousemove(weeks) {
  58 + this.$emit('handleMouse', weeks)
  59 + }
  60 + }
  61 + }
  62 +</script>
  63 +
  64 +<style lang="scss" >
  65 + .uni-calendar-item__weeks-box {
  66 + flex: 1;
  67 + /* #ifndef APP-NVUE */
  68 + display: flex;
  69 + /* #endif */
  70 + flex-direction: column;
  71 + justify-content: center;
  72 + align-items: center;
  73 + margin: 1px 0;
  74 + position: relative;
  75 + }
  76 +
  77 + .uni-calendar-item__weeks-box-text {
  78 + font-size: 14px;
  79 + // font-family: Lato-Bold, Lato;
  80 + font-weight: bold;
  81 + color: #455997;
  82 + }
  83 +
  84 + .uni-calendar-item__weeks-lunar-text {
  85 + font-size: 12px;
  86 + color: #333;
  87 + }
  88 +
  89 + .uni-calendar-item__weeks-box-item {
  90 + position: relative;
  91 + /* #ifndef APP-NVUE */
  92 + display: flex;
  93 + /* #endif */
  94 + flex-direction: column;
  95 + justify-content: center;
  96 + align-items: center;
  97 + width: 40px;
  98 + height: 40px;
  99 + /* #ifdef H5 */
  100 + cursor: pointer;
  101 + /* #endif */
  102 + }
  103 +
  104 +
  105 + .uni-calendar-item__weeks-box-circle {
  106 + position: absolute;
  107 + top: 5px;
  108 + right: 5px;
  109 + width: 8px;
  110 + height: 8px;
  111 + border-radius: 8px;
  112 + background-color: #dd524d;
  113 +
  114 + }
  115 +
  116 + .uni-calendar-item__weeks-box .uni-calendar-item--disable {
  117 + // background-color: rgba(249, 249, 249, $uni-opacity-disabled);
  118 + cursor: default;
  119 + }
  120 +
  121 + .uni-calendar-item--disable .uni-calendar-item__weeks-box-text-disable {
  122 + color: #D1D1D1;
  123 + }
  124 +
  125 + .uni-calendar-item--isDay {
  126 + position: absolute;
  127 + top: 10px;
  128 + right: 17%;
  129 + background-color: #dd524d;
  130 + width:6px;
  131 + height: 6px;
  132 + border-radius: 50%;
  133 + }
  134 +
  135 + .uni-calendar-item--extra {
  136 + color: #dd524d;
  137 + opacity: 0.8;
  138 + }
  139 +
  140 + .uni-calendar-item__weeks-box .uni-calendar-item--checked {
  141 + background-color: #007aff;
  142 + border-radius: 50%;
  143 + box-sizing: border-box;
  144 + border: 3px solid #fff;
  145 + }
  146 +
  147 + .uni-calendar-item--checked .uni-calendar-item--checked-text {
  148 + color: #fff;
  149 + }
  150 +
  151 + .uni-calendar-item--multiple .uni-calendar-item--checked-range-text {
  152 + color: #333;
  153 + }
  154 +
  155 + .uni-calendar-item--multiple {
  156 + background-color: #F6F7FC;
  157 + // color: #fff;
  158 + }
  159 +
  160 + .uni-calendar-item--multiple .uni-calendar-item--before-checked,
  161 + .uni-calendar-item--multiple .uni-calendar-item--after-checked {
  162 + background-color: #409eff;
  163 + border-radius: 50%;
  164 + box-sizing: border-box;
  165 + border: 3px solid #F6F7FC;
  166 + }
  167 +
  168 + .uni-calendar-item--before-checked .uni-calendar-item--checked-text,
  169 + .uni-calendar-item--after-checked .uni-calendar-item--checked-text {
  170 + color: #fff;
  171 + }
  172 +
  173 + .uni-calendar-item--before-checked-x {
  174 + border-top-left-radius: 50px;
  175 + border-bottom-left-radius: 50px;
  176 + box-sizing: border-box;
  177 + background-color: #F6F7FC;
  178 + }
  179 +
  180 + .uni-calendar-item--after-checked-x {
  181 + border-top-right-radius: 50px;
  182 + border-bottom-right-radius: 50px;
  183 + background-color: #F6F7FC;
  184 + }
  185 +</style>
... ...
  1 +<template>
  2 + <view class="uni-calendar" @mouseleave="leaveCale">
  3 + <view v-if="!insert&&show" class="uni-calendar__mask" :class="{'uni-calendar--mask-show':aniMaskShow}"
  4 + @click="clean"></view>
  5 + <view v-if="insert || show" class="uni-calendar__content"
  6 + :class="{'uni-calendar--fixed':!insert,'uni-calendar--ani-show':aniMaskShow, 'uni-calendar__content-mobile': aniMaskShow}">
  7 + <view class="uni-calendar__header" :class="{'uni-calendar__header-mobile' :!insert}">
  8 + <view v-if="left" class="uni-calendar__header-btn-box" @click.stop="pre">
  9 + <view class="uni-calendar__header-btn uni-calendar--left"></view>
  10 + </view>
  11 + <picker mode="date" :value="date" fields="month" @change="bindDateChange">
  12 + <text
  13 + class="uni-calendar__header-text">{{ (nowDate.year||'') + ' ' + ( nowDate.month||'') +' 月'}}</text>
  14 + </picker>
  15 + <view v-if="right" class="uni-calendar__header-btn-box" @click.stop="next">
  16 + <view class="uni-calendar__header-btn uni-calendar--right"></view>
  17 + </view>
  18 + <view v-if="!insert" class="dialog-close" @click="clean">
  19 + <view class="dialog-close-plus" data-id="close"></view>
  20 + <view class="dialog-close-plus dialog-close-rotate" data-id="close"></view>
  21 + </view>
  22 +
  23 + <!-- <text class="uni-calendar__backtoday" @click="backtoday">回到今天</text> -->
  24 + </view>
  25 + <view class="uni-calendar__box">
  26 + <view v-if="showMonth" class="uni-calendar__box-bg">
  27 + <text class="uni-calendar__box-bg-text">{{nowDate.month}}</text>
  28 + </view>
  29 + <view class="uni-calendar__weeks" style="padding-bottom: 7px;">
  30 + <view class="uni-calendar__weeks-day">
  31 + <text class="uni-calendar__weeks-day-text">{{SUNText}}</text>
  32 + </view>
  33 + <view class="uni-calendar__weeks-day">
  34 + <text class="uni-calendar__weeks-day-text">{{monText}}</text>
  35 + </view>
  36 + <view class="uni-calendar__weeks-day">
  37 + <text class="uni-calendar__weeks-day-text">{{TUEText}}</text>
  38 + </view>
  39 + <view class="uni-calendar__weeks-day">
  40 + <text class="uni-calendar__weeks-day-text">{{WEDText}}</text>
  41 + </view>
  42 + <view class="uni-calendar__weeks-day">
  43 + <text class="uni-calendar__weeks-day-text">{{THUText}}</text>
  44 + </view>
  45 + <view class="uni-calendar__weeks-day">
  46 + <text class="uni-calendar__weeks-day-text">{{FRIText}}</text>
  47 + </view>
  48 + <view class="uni-calendar__weeks-day">
  49 + <text class="uni-calendar__weeks-day-text">{{SATText}}</text>
  50 + </view>
  51 + </view>
  52 + <view class="uni-calendar__weeks" v-for="(item,weekIndex) in weeks" :key="weekIndex">
  53 + <view class="uni-calendar__weeks-item" v-for="(weeks,weeksIndex) in item" :key="weeksIndex">
  54 + <calendar-item class="uni-calendar-item--hook" :weeks="weeks" :calendar="calendar"
  55 + :selected="selected" :lunar="lunar" :checkHover="range" @change="choiceDate"
  56 + @handleMouse="handleMouse">
  57 + </calendar-item>
  58 + </view>
  59 + </view>
  60 + </view>
  61 + <view v-if="!insert && !range && typeHasTime" class="uni-date-changed uni-calendar--fixed-top"
  62 + style="padding: 0 80px;">
  63 + <view class="uni-date-changed--time-date">{{tempSingleDate ? tempSingleDate : selectDateText}}</view>
  64 + <time-picker type="time" :start="reactStartTime" :end="reactEndTime" v-model="time"
  65 + :disabled="!tempSingleDate" :border="false" :hide-second="hideSecond" class="time-picker-style">
  66 + </time-picker>
  67 + </view>
  68 +
  69 + <view v-if="!insert && range && typeHasTime" class="uni-date-changed uni-calendar--fixed-top">
  70 + <view class="uni-date-changed--time-start">
  71 + <view class="uni-date-changed--time-date">{{tempRange.before ? tempRange.before : startDateText}}
  72 + </view>
  73 + <time-picker type="time" :start="reactStartTime" v-model="timeRange.startTime" :border="false"
  74 + :hide-second="hideSecond" :disabled="!tempRange.before" class="time-picker-style">
  75 + </time-picker>
  76 + </view>
  77 + <uni-icons type="arrowthinright" color="#999" style="line-height: 50px;"></uni-icons>
  78 + <view class="uni-date-changed--time-end">
  79 + <view class="uni-date-changed--time-date">{{tempRange.after ? tempRange.after : endDateText}}</view>
  80 + <time-picker type="time" :end="reactEndTime" v-model="timeRange.endTime" :border="false"
  81 + :hide-second="hideSecond" :disabled="!tempRange.after" class="time-picker-style">
  82 + </time-picker>
  83 + </view>
  84 + </view>
  85 + <view v-if="!insert" class="uni-date-changed uni-date-btn--ok">
  86 + <!-- <view class="uni-calendar__header-btn-box">
  87 + <text class="uni-calendar__button-text uni-calendar--fixed-width">{{okText}}</text>
  88 + </view> -->
  89 + <view class="uni-datetime-picker--btn" @click="confirm">确认</view>
  90 + </view>
  91 + </view>
  92 + </view>
  93 +</template>
  94 +
  95 +<script>
  96 + import Calendar from './util.js';
  97 + import calendarItem from './calendar-item.vue'
  98 + import timePicker from './time-picker.vue'
  99 + import {
  100 + initVueI18n
  101 + } from '@dcloudio/uni-i18n'
  102 + import messages from './i18n/index.js'
  103 + const {
  104 + t
  105 + } = initVueI18n(messages)
  106 + /**
  107 + * Calendar 日历
  108 + * @description 日历组件可以查看日期,选择任意范围内的日期,打点操作。常用场景如:酒店日期预订、火车机票选择购买日期、上下班打卡等
  109 + * @tutorial https://ext.dcloud.net.cn/plugin?id=56
  110 + * @property {String} date 自定义当前时间,默认为今天
  111 + * @property {Boolean} lunar 显示农历
  112 + * @property {String} startDate 日期选择范围-开始日期
  113 + * @property {String} endDate 日期选择范围-结束日期
  114 + * @property {Boolean} range 范围选择
  115 + * @property {Boolean} insert = [true|false] 插入模式,默认为false
  116 + * @value true 弹窗模式
  117 + * @value false 插入模式
  118 + * @property {Boolean} clearDate = [true|false] 弹窗模式是否清空上次选择内容
  119 + * @property {Array} selected 打点,期待格式[{date: '2019-06-27', info: '签到', data: { custom: '自定义信息', name: '自定义消息头',xxx:xxx... }}]
  120 + * @property {Boolean} showMonth 是否选择月份为背景
  121 + * @event {Function} change 日期改变,`insert :ture` 时生效
  122 + * @event {Function} confirm 确认选择`insert :false` 时生效
  123 + * @event {Function} monthSwitch 切换月份时触发
  124 + * @example <uni-calendar :insert="true":lunar="true" :start-date="'2019-3-2'":end-date="'2019-5-20'"@change="change" />
  125 + */
  126 + export default {
  127 + components: {
  128 + calendarItem,
  129 + timePicker
  130 + },
  131 + props: {
  132 + date: {
  133 + type: String,
  134 + default: ''
  135 + },
  136 + defTime: {
  137 + type: [String, Object],
  138 + default: ''
  139 + },
  140 + selectableTimes: {
  141 + type: [Object],
  142 + default () {
  143 + return {}
  144 + }
  145 + },
  146 + selected: {
  147 + type: Array,
  148 + default () {
  149 + return []
  150 + }
  151 + },
  152 + lunar: {
  153 + type: Boolean,
  154 + default: false
  155 + },
  156 + startDate: {
  157 + type: String,
  158 + default: ''
  159 + },
  160 + endDate: {
  161 + type: String,
  162 + default: ''
  163 + },
  164 + range: {
  165 + type: Boolean,
  166 + default: false
  167 + },
  168 + typeHasTime: {
  169 + type: Boolean,
  170 + default: false
  171 + },
  172 + insert: {
  173 + type: Boolean,
  174 + default: true
  175 + },
  176 + showMonth: {
  177 + type: Boolean,
  178 + default: true
  179 + },
  180 + clearDate: {
  181 + type: Boolean,
  182 + default: true
  183 + },
  184 + left: {
  185 + type: Boolean,
  186 + default: true
  187 + },
  188 + right: {
  189 + type: Boolean,
  190 + default: true
  191 + },
  192 + checkHover: {
  193 + type: Boolean,
  194 + default: true
  195 + },
  196 + hideSecond: {
  197 + type: [Boolean],
  198 + default: false
  199 + },
  200 + pleStatus: {
  201 + type: Object,
  202 + default () {
  203 + return {
  204 + before: '',
  205 + after: '',
  206 + data: [],
  207 + fulldate: ''
  208 + }
  209 + }
  210 + }
  211 + },
  212 + data() {
  213 + return {
  214 + show: false,
  215 + weeks: [],
  216 + calendar: {},
  217 + nowDate: '',
  218 + aniMaskShow: false,
  219 + firstEnter: true,
  220 + time: '',
  221 + timeRange: {
  222 + startTime: '',
  223 + endTime: ''
  224 + },
  225 + tempSingleDate: '',
  226 + tempRange: {
  227 + before: '',
  228 + after: ''
  229 + }
  230 + }
  231 + },
  232 + watch: {
  233 + date: {
  234 + immediate: true,
  235 + handler(newVal, oldVal) {
  236 + if (!this.range) {
  237 + this.tempSingleDate = newVal
  238 + setTimeout(() => {
  239 + this.init(newVal)
  240 + }, 100)
  241 + }
  242 + }
  243 + },
  244 + defTime: {
  245 + immediate: true,
  246 + handler(newVal, oldVal) {
  247 + if (!this.range) {
  248 + this.time = newVal
  249 + } else {
  250 + // console.log('-----', newVal);
  251 + this.timeRange.startTime = newVal.start
  252 + this.timeRange.endTime = newVal.end
  253 + }
  254 + }
  255 + },
  256 + startDate(val) {
  257 + this.cale.resetSatrtDate(val)
  258 + this.cale.setDate(this.nowDate.fullDate)
  259 + this.weeks = this.cale.weeks
  260 + },
  261 + endDate(val) {
  262 + this.cale.resetEndDate(val)
  263 + this.cale.setDate(this.nowDate.fullDate)
  264 + this.weeks = this.cale.weeks
  265 + },
  266 + selected(newVal) {
  267 + this.cale.setSelectInfo(this.nowDate.fullDate, newVal)
  268 + this.weeks = this.cale.weeks
  269 + },
  270 + pleStatus: {
  271 + immediate: true,
  272 + handler(newVal, oldVal) {
  273 + const {
  274 + before,
  275 + after,
  276 + fulldate,
  277 + which
  278 + } = newVal
  279 + this.tempRange.before = before
  280 + this.tempRange.after = after
  281 + setTimeout(() => {
  282 + if (fulldate) {
  283 + this.cale.setHoverMultiple(fulldate)
  284 + if (before && after) {
  285 + this.cale.lastHover = true
  286 + if (this.rangeWithinMonth(after, before)) return
  287 + this.setDate(before)
  288 + } else {
  289 + this.cale.setMultiple(fulldate)
  290 + this.setDate(this.nowDate.fullDate)
  291 + this.calendar.fullDate = ''
  292 + this.cale.lastHover = false
  293 + }
  294 + } else {
  295 + this.cale.setDefaultMultiple(before, after)
  296 + if (which === 'left') {
  297 + this.setDate(before)
  298 + this.weeks = this.cale.weeks
  299 + } else {
  300 + this.setDate(after)
  301 + this.weeks = this.cale.weeks
  302 + }
  303 + this.cale.lastHover = true
  304 + }
  305 + }, 16)
  306 + }
  307 + }
  308 + },
  309 + computed: {
  310 + reactStartTime() {
  311 + const activeDate = this.range ? this.tempRange.before : this.calendar.fullDate
  312 + const res = activeDate === this.startDate ? this.selectableTimes.start : ''
  313 + return res
  314 + },
  315 + reactEndTime() {
  316 + const activeDate = this.range ? this.tempRange.after : this.calendar.fullDate
  317 + const res = activeDate === this.endDate ? this.selectableTimes.end : ''
  318 + return res
  319 + },
  320 + /**
  321 + * for i18n
  322 + */
  323 + selectDateText() {
  324 + return t("uni-datetime-picker.selectDate")
  325 + },
  326 + startDateText() {
  327 + return this.startPlaceholder || t("uni-datetime-picker.startDate")
  328 + },
  329 + endDateText() {
  330 + return this.endPlaceholder || t("uni-datetime-picker.endDate")
  331 + },
  332 + okText() {
  333 + return t("uni-datetime-picker.ok")
  334 + },
  335 + monText() {
  336 + return t("uni-calender.MON")
  337 + },
  338 + TUEText() {
  339 + return t("uni-calender.TUE")
  340 + },
  341 + WEDText() {
  342 + return t("uni-calender.WED")
  343 + },
  344 + THUText() {
  345 + return t("uni-calender.THU")
  346 + },
  347 + FRIText() {
  348 + return t("uni-calender.FRI")
  349 + },
  350 + SATText() {
  351 + return t("uni-calender.SAT")
  352 + },
  353 + SUNText() {
  354 + return t("uni-calender.SUN")
  355 + },
  356 + },
  357 + created() {
  358 + // 获取日历方法实例
  359 + this.cale = new Calendar({
  360 + // date: new Date(),
  361 + selected: this.selected,
  362 + startDate: this.startDate,
  363 + endDate: this.endDate,
  364 + range: this.range,
  365 + // multipleStatus: this.pleStatus
  366 + })
  367 + // 选中某一天
  368 + // this.cale.setDate(this.date)
  369 + this.init(this.date)
  370 + // this.setDay
  371 + },
  372 + methods: {
  373 + leaveCale() {
  374 + this.firstEnter = true
  375 + },
  376 + handleMouse(weeks) {
  377 + if (weeks.disable) return
  378 + if (this.cale.lastHover) return
  379 + let {
  380 + before,
  381 + after
  382 + } = this.cale.multipleStatus
  383 + if (!before) return
  384 + this.calendar = weeks
  385 + // 设置范围选
  386 + this.cale.setHoverMultiple(this.calendar.fullDate)
  387 + this.weeks = this.cale.weeks
  388 + // hover时,进入一个日历,更新另一个
  389 + if (this.firstEnter) {
  390 + this.$emit('firstEnterCale', this.cale.multipleStatus)
  391 + this.firstEnter = false
  392 + }
  393 + },
  394 + rangeWithinMonth(A, B) {
  395 + const [yearA, monthA] = A.split('-')
  396 + const [yearB, monthB] = B.split('-')
  397 + return yearA === yearB && monthA === monthB
  398 + },
  399 +
  400 + // 取消穿透
  401 + clean() {
  402 + this.close()
  403 + },
  404 +
  405 + clearCalender() {
  406 + if (this.range) {
  407 + this.timeRange.startTime = ''
  408 + this.timeRange.endTime = ''
  409 + this.tempRange.before = ''
  410 + this.tempRange.after = ''
  411 + this.cale.multipleStatus.before = ''
  412 + this.cale.multipleStatus.after = ''
  413 + this.cale.multipleStatus.data = []
  414 + this.cale.lastHover = false
  415 + } else {
  416 + this.time = ''
  417 + this.tempSingleDate = ''
  418 + }
  419 + this.calendar.fullDate = ''
  420 + this.setDate()
  421 + },
  422 +
  423 + bindDateChange(e) {
  424 + const value = e.detail.value + '-1'
  425 + this.init(value)
  426 + },
  427 + /**
  428 + * 初始化日期显示
  429 + * @param {Object} date
  430 + */
  431 + init(date) {
  432 + this.cale.setDate(date)
  433 + this.weeks = this.cale.weeks
  434 + this.nowDate = this.calendar = this.cale.getInfo(date)
  435 + },
  436 + // choiceDate(weeks) {
  437 + // if (weeks.disable) return
  438 + // this.calendar = weeks
  439 + // // 设置多选
  440 + // this.cale.setMultiple(this.calendar.fullDate, true)
  441 + // this.weeks = this.cale.weeks
  442 + // this.tempSingleDate = this.calendar.fullDate
  443 + // this.tempRange.before = this.cale.multipleStatus.before
  444 + // this.tempRange.after = this.cale.multipleStatus.after
  445 + // this.change()
  446 + // },
  447 + /**
  448 + * 打开日历弹窗
  449 + */
  450 + open() {
  451 + // 弹窗模式并且清理数据
  452 + if (this.clearDate && !this.insert) {
  453 + this.cale.cleanMultipleStatus()
  454 + // this.cale.setDate(this.date)
  455 + this.init(this.date)
  456 + }
  457 + this.show = true
  458 + this.$nextTick(() => {
  459 + setTimeout(() => {
  460 + this.aniMaskShow = true
  461 + }, 50)
  462 + })
  463 + },
  464 + /**
  465 + * 关闭日历弹窗
  466 + */
  467 + close() {
  468 + this.aniMaskShow = false
  469 + this.$nextTick(() => {
  470 + setTimeout(() => {
  471 + this.show = false
  472 + this.$emit('close')
  473 + }, 300)
  474 + })
  475 + },
  476 + /**
  477 + * 确认按钮
  478 + */
  479 + confirm() {
  480 + this.setEmit('confirm')
  481 + this.close()
  482 + },
  483 + /**
  484 + * 变化触发
  485 + */
  486 + change() {
  487 + if (!this.insert) return
  488 + this.setEmit('change')
  489 + },
  490 + /**
  491 + * 选择月份触发
  492 + */
  493 + monthSwitch() {
  494 + let {
  495 + year,
  496 + month
  497 + } = this.nowDate
  498 + this.$emit('monthSwitch', {
  499 + year,
  500 + month: Number(month)
  501 + })
  502 + },
  503 + /**
  504 + * 派发事件
  505 + * @param {Object} name
  506 + */
  507 + setEmit(name) {
  508 + let {
  509 + year,
  510 + month,
  511 + date,
  512 + fullDate,
  513 + lunar,
  514 + extraInfo
  515 + } = this.calendar
  516 + this.$emit(name, {
  517 + range: this.cale.multipleStatus,
  518 + year,
  519 + month,
  520 + date,
  521 + time: this.time,
  522 + timeRange: this.timeRange,
  523 + fulldate: fullDate,
  524 + lunar,
  525 + extraInfo: extraInfo || {}
  526 + })
  527 + },
  528 + /**
  529 + * 选择天触发
  530 + * @param {Object} weeks
  531 + */
  532 + choiceDate(weeks) {
  533 + if (weeks.disable) return
  534 + this.calendar = weeks
  535 + this.calendar.userChecked = true
  536 + // 设置多选
  537 + this.cale.setMultiple(this.calendar.fullDate, true)
  538 + this.weeks = this.cale.weeks
  539 + this.tempSingleDate = this.calendar.fullDate
  540 + this.tempRange.before = this.cale.multipleStatus.before
  541 + this.tempRange.after = this.cale.multipleStatus.after
  542 + this.change()
  543 + },
  544 + /**
  545 + * 回到今天
  546 + */
  547 + backtoday() {
  548 + let date = this.cale.getDate(new Date()).fullDate
  549 + // this.cale.setDate(date)
  550 + this.init(date)
  551 + this.change()
  552 + },
  553 + /**
  554 + * 比较时间大小
  555 + */
  556 + dateCompare(startDate, endDate) {
  557 + // 计算截止时间
  558 + startDate = new Date(startDate.replace('-', '/').replace('-', '/'))
  559 + // 计算详细项的截止时间
  560 + endDate = new Date(endDate.replace('-', '/').replace('-', '/'))
  561 + if (startDate <= endDate) {
  562 + return true
  563 + } else {
  564 + return false
  565 + }
  566 + },
  567 + /**
  568 + * 上个月
  569 + */
  570 + pre() {
  571 + const preDate = this.cale.getDate(this.nowDate.fullDate, -1, 'month').fullDate
  572 + this.setDate(preDate)
  573 + this.monthSwitch()
  574 +
  575 + },
  576 + /**
  577 + * 下个月
  578 + */
  579 + next() {
  580 + const nextDate = this.cale.getDate(this.nowDate.fullDate, +1, 'month').fullDate
  581 + this.setDate(nextDate)
  582 + this.monthSwitch()
  583 + },
  584 + /**
  585 + * 设置日期
  586 + * @param {Object} date
  587 + */
  588 + setDate(date) {
  589 + this.cale.setDate(date)
  590 + this.weeks = this.cale.weeks
  591 + this.nowDate = this.cale.getInfo(date)
  592 + }
  593 + }
  594 + }
  595 +</script>
  596 +
  597 +<style lang="scss" >
  598 + .uni-calendar {
  599 + /* #ifndef APP-NVUE */
  600 + display: flex;
  601 + /* #endif */
  602 + flex-direction: column;
  603 + }
  604 +
  605 + .uni-calendar__mask {
  606 + position: fixed;
  607 + bottom: 0;
  608 + top: 0;
  609 + left: 0;
  610 + right: 0;
  611 + background-color: rgba(0, 0, 0, 0.4);
  612 + transition-property: opacity;
  613 + transition-duration: 0.3s;
  614 + opacity: 0;
  615 + /* #ifndef APP-NVUE */
  616 + z-index: 99;
  617 + /* #endif */
  618 + }
  619 +
  620 + .uni-calendar--mask-show {
  621 + opacity: 1
  622 + }
  623 +
  624 + .uni-calendar--fixed {
  625 + position: fixed;
  626 + bottom: calc(var(--window-bottom));
  627 + left: 0;
  628 + right: 0;
  629 + transition-property: transform;
  630 + transition-duration: 0.3s;
  631 + transform: translateY(460px);
  632 + /* #ifndef APP-NVUE */
  633 + z-index: 99;
  634 + /* #endif */
  635 + }
  636 +
  637 + .uni-calendar--ani-show {
  638 + transform: translateY(0);
  639 + }
  640 +
  641 + .uni-calendar__content {
  642 + background-color: #fff;
  643 + }
  644 +
  645 + .uni-calendar__content-mobile {
  646 + border-top-left-radius: 10px;
  647 + border-top-right-radius: 10px;
  648 + box-shadow: 0px 0px 5px 3px rgba(0, 0, 0, 0.1);
  649 + }
  650 +
  651 + .uni-calendar__header {
  652 + position: relative;
  653 + /* #ifndef APP-NVUE */
  654 + display: flex;
  655 + /* #endif */
  656 + flex-direction: row;
  657 + justify-content: center;
  658 + align-items: center;
  659 + height: 50px;
  660 + }
  661 +
  662 + .uni-calendar__header-mobile {
  663 + padding: 10px;
  664 + padding-bottom: 0;
  665 + }
  666 +
  667 + .uni-calendar--fixed-top {
  668 + /* #ifndef APP-NVUE */
  669 + display: flex;
  670 + /* #endif */
  671 + flex-direction: row;
  672 + justify-content: space-between;
  673 + border-top-color: rgba(0, 0, 0, 0.4);
  674 + border-top-style: solid;
  675 + border-top-width: 1px;
  676 + }
  677 +
  678 + .uni-calendar--fixed-width {
  679 + width: 50px;
  680 + }
  681 +
  682 + .uni-calendar__backtoday {
  683 + position: absolute;
  684 + right: 0;
  685 + top: 25rpx;
  686 + padding: 0 5px;
  687 + padding-left: 10px;
  688 + height: 25px;
  689 + line-height: 25px;
  690 + font-size: 12px;
  691 + border-top-left-radius: 25px;
  692 + border-bottom-left-radius: 25px;
  693 + color: #fff;
  694 + background-color: #f1f1f1;
  695 + }
  696 +
  697 + .uni-calendar__header-text {
  698 + text-align: center;
  699 + width: 100px;
  700 + font-size: 15px;
  701 + color: #666;
  702 + }
  703 +
  704 + .uni-calendar__button-text {
  705 + text-align: center;
  706 + width: 100px;
  707 + font-size: 14px;
  708 + color: #007aff;
  709 + /* #ifndef APP-NVUE */
  710 + letter-spacing: 3px;
  711 + /* #endif */
  712 + }
  713 +
  714 + .uni-calendar__header-btn-box {
  715 + /* #ifndef APP-NVUE */
  716 + display: flex;
  717 + /* #endif */
  718 + flex-direction: row;
  719 + align-items: center;
  720 + justify-content: center;
  721 + width: 50px;
  722 + height: 50px;
  723 + }
  724 +
  725 + .uni-calendar__header-btn {
  726 + width: 9px;
  727 + height: 9px;
  728 + border-left-color: #808080;
  729 + border-left-style: solid;
  730 + border-left-width: 1px;
  731 + border-top-color: #555555;
  732 + border-top-style: solid;
  733 + border-top-width: 1px;
  734 + }
  735 +
  736 + .uni-calendar--left {
  737 + transform: rotate(-45deg);
  738 + }
  739 +
  740 + .uni-calendar--right {
  741 + transform: rotate(135deg);
  742 + }
  743 +
  744 +
  745 + .uni-calendar__weeks {
  746 + position: relative;
  747 + /* #ifndef APP-NVUE */
  748 + display: flex;
  749 + /* #endif */
  750 + flex-direction: row;
  751 + }
  752 +
  753 + .uni-calendar__weeks-item {
  754 + flex: 1;
  755 + }
  756 +
  757 + .uni-calendar__weeks-day {
  758 + flex: 1;
  759 + /* #ifndef APP-NVUE */
  760 + display: flex;
  761 + /* #endif */
  762 + flex-direction: column;
  763 + justify-content: center;
  764 + align-items: center;
  765 + height: 40px;
  766 + border-bottom-color: #F5F5F5;
  767 + border-bottom-style: solid;
  768 + border-bottom-width: 1px;
  769 + }
  770 +
  771 + .uni-calendar__weeks-day-text {
  772 + font-size: 12px;
  773 + color: #B2B2B2;
  774 + }
  775 +
  776 + .uni-calendar__box {
  777 + position: relative;
  778 + // padding: 0 10px;
  779 + padding-bottom: 7px;
  780 + }
  781 +
  782 + .uni-calendar__box-bg {
  783 + /* #ifndef APP-NVUE */
  784 + display: flex;
  785 + /* #endif */
  786 + justify-content: center;
  787 + align-items: center;
  788 + position: absolute;
  789 + top: 0;
  790 + left: 0;
  791 + right: 0;
  792 + bottom: 0;
  793 + }
  794 +
  795 + .uni-calendar__box-bg-text {
  796 + font-size: 200px;
  797 + font-weight: bold;
  798 + color: #999;
  799 + opacity: 0.1;
  800 + text-align: center;
  801 + /* #ifndef APP-NVUE */
  802 + line-height: 1;
  803 + /* #endif */
  804 + }
  805 +
  806 + .uni-date-changed {
  807 + padding: 0 10px;
  808 + // line-height: 50px;
  809 + text-align: center;
  810 + color: #333;
  811 + border-top-color: #DCDCDC;
  812 + ;
  813 + border-top-style: solid;
  814 + border-top-width: 1px;
  815 + flex: 1;
  816 + }
  817 +
  818 + .uni-date-btn--ok {
  819 + padding: 20px 15px;
  820 + }
  821 +
  822 + .uni-date-changed--time-start {
  823 + /* #ifndef APP-NVUE */
  824 + display: flex;
  825 + /* #endif */
  826 + align-items: center;
  827 + }
  828 +
  829 + .uni-date-changed--time-end {
  830 + /* #ifndef APP-NVUE */
  831 + display: flex;
  832 + /* #endif */
  833 + align-items: center;
  834 + }
  835 +
  836 + .uni-date-changed--time-date {
  837 + color: #999;
  838 + line-height: 50px;
  839 + margin-right: 5px;
  840 + // opacity: 0.6;
  841 + }
  842 +
  843 + .time-picker-style {
  844 + // width: 62px;
  845 + /* #ifndef APP-NVUE */
  846 + display: flex;
  847 + /* #endif */
  848 + justify-content: center;
  849 + align-items: center
  850 + }
  851 +
  852 + .mr-10 {
  853 + margin-right: 10px;
  854 + }
  855 +
  856 + .dialog-close {
  857 + position: absolute;
  858 + top: 0;
  859 + right: 0;
  860 + bottom: 0;
  861 + /* #ifndef APP-NVUE */
  862 + display: flex;
  863 + /* #endif */
  864 + flex-direction: row;
  865 + align-items: center;
  866 + padding: 0 25px;
  867 + margin-top: 10px;
  868 + }
  869 +
  870 + .dialog-close-plus {
  871 + width: 16px;
  872 + height: 2px;
  873 + background-color: #737987;
  874 + border-radius: 2px;
  875 + transform: rotate(45deg);
  876 + }
  877 +
  878 + .dialog-close-rotate {
  879 + position: absolute;
  880 + transform: rotate(-45deg);
  881 + }
  882 +
  883 + .uni-datetime-picker--btn {
  884 + border-radius: 100px;
  885 + height: 40px;
  886 + line-height: 40px;
  887 + background-color: #007aff;
  888 + color: #fff;
  889 + font-size: 16px;
  890 + letter-spacing: 5px;
  891 + }
  892 +
  893 + /* #ifndef APP-NVUE */
  894 + .uni-datetime-picker--btn:active {
  895 + opacity: 0.7;
  896 + }
  897 + /* #endif */
  898 +</style>
... ...
  1 +{
  2 + "uni-datetime-picker.selectDate": "select date",
  3 + "uni-datetime-picker.selectTime": "select time",
  4 + "uni-datetime-picker.selectDateTime": "select datetime",
  5 + "uni-datetime-picker.startDate": "start date",
  6 + "uni-datetime-picker.endDate": "end date",
  7 + "uni-datetime-picker.startTime": "start time",
  8 + "uni-datetime-picker.endTime": "end time",
  9 + "uni-datetime-picker.ok": "ok",
  10 + "uni-datetime-picker.clear": "clear",
  11 + "uni-datetime-picker.cancel": "cancel",
  12 + "uni-calender.MON": "MON",
  13 + "uni-calender.TUE": "TUE",
  14 + "uni-calender.WED": "WED",
  15 + "uni-calender.THU": "THU",
  16 + "uni-calender.FRI": "FRI",
  17 + "uni-calender.SAT": "SAT",
  18 + "uni-calender.SUN": "SUN"
  19 +}
... ...
  1 +import en from './en.json'
  2 +import zhHans from './zh-Hans.json'
  3 +import zhHant from './zh-Hant.json'
  4 +export default {
  5 + en,
  6 + 'zh-Hans': zhHans,
  7 + 'zh-Hant': zhHant
  8 +}
... ...
  1 +{
  2 + "uni-datetime-picker.selectDate": "选择日期",
  3 + "uni-datetime-picker.selectTime": "选择时间",
  4 + "uni-datetime-picker.selectDateTime": "选择日期时间",
  5 + "uni-datetime-picker.startDate": "开始日期",
  6 + "uni-datetime-picker.endDate": "结束日期",
  7 + "uni-datetime-picker.startTime": "开始时间",
  8 + "uni-datetime-picker.endTime": "结束时间",
  9 + "uni-datetime-picker.ok": "确定",
  10 + "uni-datetime-picker.clear": "清除",
  11 + "uni-datetime-picker.cancel": "取消",
  12 + "uni-calender.SUN": "日",
  13 + "uni-calender.MON": "一",
  14 + "uni-calender.TUE": "二",
  15 + "uni-calender.WED": "三",
  16 + "uni-calender.THU": "四",
  17 + "uni-calender.FRI": "五",
  18 + "uni-calender.SAT": "六"
  19 +}
... ...
  1 +{
  2 + "uni-datetime-picker.selectDate": "選擇日期",
  3 + "uni-datetime-picker.selectTime": "選擇時間",
  4 + "uni-datetime-picker.selectDateTime": "選擇日期時間",
  5 + "uni-datetime-picker.startDate": "開始日期",
  6 + "uni-datetime-picker.endDate": "結束日期",
  7 + "uni-datetime-picker.startTime": "開始时间",
  8 + "uni-datetime-picker.endTime": "結束时间",
  9 + "uni-datetime-picker.ok": "確定",
  10 + "uni-datetime-picker.clear": "清除",
  11 + "uni-datetime-picker.cancel": "取消",
  12 + "uni-calender.SUN": "日",
  13 + "uni-calender.MON": "一",
  14 + "uni-calender.TUE": "二",
  15 + "uni-calender.WED": "三",
  16 + "uni-calender.THU": "四",
  17 + "uni-calender.FRI": "五",
  18 + "uni-calender.SAT": "六"
  19 +}
... ...
  1 +// #ifdef H5
  2 +export default {
  3 + name: 'Keypress',
  4 + props: {
  5 + disable: {
  6 + type: Boolean,
  7 + default: false
  8 + }
  9 + },
  10 + mounted () {
  11 + const keyNames = {
  12 + esc: ['Esc', 'Escape'],
  13 + tab: 'Tab',
  14 + enter: 'Enter',
  15 + space: [' ', 'Spacebar'],
  16 + up: ['Up', 'ArrowUp'],
  17 + left: ['Left', 'ArrowLeft'],
  18 + right: ['Right', 'ArrowRight'],
  19 + down: ['Down', 'ArrowDown'],
  20 + delete: ['Backspace', 'Delete', 'Del']
  21 + }
  22 + const listener = ($event) => {
  23 + if (this.disable) {
  24 + return
  25 + }
  26 + const keyName = Object.keys(keyNames).find(key => {
  27 + const keyName = $event.key
  28 + const value = keyNames[key]
  29 + return value === keyName || (Array.isArray(value) && value.includes(keyName))
  30 + })
  31 + if (keyName) {
  32 + // 避免和其他按键事件冲突
  33 + setTimeout(() => {
  34 + this.$emit(keyName, {})
  35 + }, 0)
  36 + }
  37 + }
  38 + document.addEventListener('keyup', listener)
  39 + this.$once('hook:beforeDestroy', () => {
  40 + document.removeEventListener('keyup', listener)
  41 + })
  42 + },
  43 + render: () => {}
  44 +}
  45 +// #endif
\ No newline at end of file
... ...
  1 +<template>
  2 + <view class="uni-datetime-picker">
  3 + <view @click="initTimePicker">
  4 + <slot>
  5 + <view class="uni-datetime-picker-timebox-pointer"
  6 + :class="{'uni-datetime-picker-disabled': disabled, 'uni-datetime-picker-timebox': border}">
  7 + <text class="uni-datetime-picker-text">{{time}}</text>
  8 + <view v-if="!time" class="uni-datetime-picker-time">
  9 + <text class="uni-datetime-picker-text">{{selectTimeText}}</text>
  10 + </view>
  11 + </view>
  12 + </slot>
  13 + </view>
  14 + <view v-if="visible" id="mask" class="uni-datetime-picker-mask" @click="tiggerTimePicker"></view>
  15 + <view v-if="visible" class="uni-datetime-picker-popup" :class="[dateShow && timeShow ? '' : 'fix-nvue-height']"
  16 + :style="fixNvueBug">
  17 + <view class="uni-title">
  18 + <text class="uni-datetime-picker-text">{{selectTimeText}}</text>
  19 + </view>
  20 + <view v-if="dateShow" class="uni-datetime-picker__container-box">
  21 + <picker-view class="uni-datetime-picker-view" :indicator-style="indicatorStyle" :value="ymd"
  22 + @change="bindDateChange">
  23 + <picker-view-column>
  24 + <view class="uni-datetime-picker-item" v-for="(item,index) in years" :key="index">
  25 + <text class="uni-datetime-picker-item">{{lessThanTen(item)}}</text>
  26 + </view>
  27 + </picker-view-column>
  28 + <picker-view-column>
  29 + <view class="uni-datetime-picker-item" v-for="(item,index) in months" :key="index">
  30 + <text class="uni-datetime-picker-item">{{lessThanTen(item)}}</text>
  31 + </view>
  32 + </picker-view-column>
  33 + <picker-view-column>
  34 + <view class="uni-datetime-picker-item" v-for="(item,index) in days" :key="index">
  35 + <text class="uni-datetime-picker-item">{{lessThanTen(item)}}</text>
  36 + </view>
  37 + </picker-view-column>
  38 + </picker-view>
  39 + <!-- 兼容 nvue 不支持伪类 -->
  40 + <text class="uni-datetime-picker-sign sign-left">-</text>
  41 + <text class="uni-datetime-picker-sign sign-right">-</text>
  42 + </view>
  43 + <view v-if="timeShow" class="uni-datetime-picker__container-box">
  44 + <picker-view class="uni-datetime-picker-view" :class="[hideSecond ? 'time-hide-second' : '']"
  45 + :indicator-style="indicatorStyle" :value="hms" @change="bindTimeChange">
  46 + <picker-view-column>
  47 + <view class="uni-datetime-picker-item" v-for="(item,index) in hours" :key="index">
  48 + <text class="uni-datetime-picker-item">{{lessThanTen(item)}}</text>
  49 + </view>
  50 + </picker-view-column>
  51 + <picker-view-column>
  52 + <view class="uni-datetime-picker-item" v-for="(item,index) in minutes" :key="index">
  53 + <text class="uni-datetime-picker-item">{{lessThanTen(item)}}</text>
  54 + </view>
  55 + </picker-view-column>
  56 + <picker-view-column v-if="!hideSecond">
  57 + <view class="uni-datetime-picker-item" v-for="(item,index) in seconds" :key="index">
  58 + <text class="uni-datetime-picker-item">{{lessThanTen(item)}}</text>
  59 + </view>
  60 + </picker-view-column>
  61 + </picker-view>
  62 + <!-- 兼容 nvue 不支持伪类 -->
  63 + <text class="uni-datetime-picker-sign" :class="[hideSecond ? 'sign-center' : 'sign-left']">:</text>
  64 + <text v-if="!hideSecond" class="uni-datetime-picker-sign sign-right">:</text>
  65 + </view>
  66 + <view class="uni-datetime-picker-btn">
  67 + <view @click="clearTime">
  68 + <text class="uni-datetime-picker-btn-text">{{clearText}}</text>
  69 + </view>
  70 + <view class="uni-datetime-picker-btn-group">
  71 + <view class="uni-datetime-picker-cancel" @click="tiggerTimePicker">
  72 + <text class="uni-datetime-picker-btn-text">{{cancelText}}</text>
  73 + </view>
  74 + <view @click="setTime">
  75 + <text class="uni-datetime-picker-btn-text">{{okText}}</text>
  76 + </view>
  77 + </view>
  78 + </view>
  79 + </view>
  80 + <!-- #ifdef H5 -->
  81 + <!-- <keypress v-if="visible" @esc="tiggerTimePicker" @enter="setTime" /> -->
  82 + <!-- #endif -->
  83 + </view>
  84 +</template>
  85 +
  86 +<script>
  87 + // #ifdef H5
  88 + import keypress from './keypress'
  89 + // #endif
  90 + import {
  91 + initVueI18n
  92 + } from '@dcloudio/uni-i18n'
  93 + import messages from './i18n/index.js'
  94 + const { t } = initVueI18n(messages)
  95 +
  96 + /**
  97 + * DatetimePicker 时间选择器
  98 + * @description 可以同时选择日期和时间的选择器
  99 + * @tutorial https://ext.dcloud.net.cn/plugin?id=xxx
  100 + * @property {String} type = [datetime | date | time] 显示模式
  101 + * @property {Boolean} multiple = [true|false] 是否多选
  102 + * @property {String|Number} value 默认值
  103 + * @property {String|Number} start 起始日期或时间
  104 + * @property {String|Number} end 起始日期或时间
  105 + * @property {String} return-type = [timestamp | string]
  106 + * @event {Function} change 选中发生变化触发
  107 + */
  108 +
  109 + export default {
  110 + name: 'UniDatetimePicker',
  111 + components: {
  112 + // #ifdef H5
  113 + keypress
  114 + // #endif
  115 + },
  116 + data() {
  117 + return {
  118 + indicatorStyle: `height: 50px;`,
  119 + visible: false,
  120 + fixNvueBug: {},
  121 + dateShow: true,
  122 + timeShow: true,
  123 + title: '日期和时间',
  124 + // 输入框当前时间
  125 + time: '',
  126 + // 当前的年月日时分秒
  127 + year: 1920,
  128 + month: 0,
  129 + day: 0,
  130 + hour: 0,
  131 + minute: 0,
  132 + second: 0,
  133 + // 起始时间
  134 + startYear: 1920,
  135 + startMonth: 1,
  136 + startDay: 1,
  137 + startHour: 0,
  138 + startMinute: 0,
  139 + startSecond: 0,
  140 + // 结束时间
  141 + endYear: 2120,
  142 + endMonth: 12,
  143 + endDay: 31,
  144 + endHour: 23,
  145 + endMinute: 59,
  146 + endSecond: 59,
  147 + }
  148 + },
  149 + props: {
  150 + type: {
  151 + type: String,
  152 + default: 'datetime'
  153 + },
  154 + value: {
  155 + type: [String, Number],
  156 + default: ''
  157 + },
  158 + modelValue: {
  159 + type: [String, Number],
  160 + default: ''
  161 + },
  162 + start: {
  163 + type: [Number, String],
  164 + default: ''
  165 + },
  166 + end: {
  167 + type: [Number, String],
  168 + default: ''
  169 + },
  170 + returnType: {
  171 + type: String,
  172 + default: 'string'
  173 + },
  174 + disabled: {
  175 + type: [Boolean, String],
  176 + default: false
  177 + },
  178 + border: {
  179 + type: [Boolean, String],
  180 + default: true
  181 + },
  182 + hideSecond: {
  183 + type: [Boolean, String],
  184 + default: false
  185 + }
  186 + },
  187 + watch: {
  188 + value: {
  189 + handler(newVal, oldVal) {
  190 + if (newVal) {
  191 + this.parseValue(this.fixIosDateFormat(newVal)) //兼容 iOS、safari 日期格式
  192 + this.initTime(false)
  193 + } else {
  194 + this.time = ''
  195 + this.parseValue(Date.now())
  196 + }
  197 + },
  198 + immediate: true
  199 + },
  200 + type: {
  201 + handler(newValue) {
  202 + if (newValue === 'date') {
  203 + this.dateShow = true
  204 + this.timeShow = false
  205 + this.title = '日期'
  206 + } else if (newValue === 'time') {
  207 + this.dateShow = false
  208 + this.timeShow = true
  209 + this.title = '时间'
  210 + } else {
  211 + this.dateShow = true
  212 + this.timeShow = true
  213 + this.title = '日期和时间'
  214 + }
  215 + },
  216 + immediate: true
  217 + },
  218 + start: {
  219 + handler(newVal) {
  220 + this.parseDatetimeRange(this.fixIosDateFormat(newVal), 'start') //兼容 iOS、safari 日期格式
  221 + },
  222 + immediate: true
  223 + },
  224 + end: {
  225 + handler(newVal) {
  226 + this.parseDatetimeRange(this.fixIosDateFormat(newVal), 'end') //兼容 iOS、safari 日期格式
  227 + },
  228 + immediate: true
  229 + },
  230 +
  231 + // 月、日、时、分、秒可选范围变化后,检查当前值是否在范围内,不在则当前值重置为可选范围第一项
  232 + months(newVal) {
  233 + this.checkValue('month', this.month, newVal)
  234 + },
  235 + days(newVal) {
  236 + this.checkValue('day', this.day, newVal)
  237 + },
  238 + hours(newVal) {
  239 + this.checkValue('hour', this.hour, newVal)
  240 + },
  241 + minutes(newVal) {
  242 + this.checkValue('minute', this.minute, newVal)
  243 + },
  244 + seconds(newVal) {
  245 + this.checkValue('second', this.second, newVal)
  246 + }
  247 + },
  248 + computed: {
  249 + // 当前年、月、日、时、分、秒选择范围
  250 + years() {
  251 + return this.getCurrentRange('year')
  252 + },
  253 +
  254 + months() {
  255 + return this.getCurrentRange('month')
  256 + },
  257 +
  258 + days() {
  259 + return this.getCurrentRange('day')
  260 + },
  261 +
  262 + hours() {
  263 + return this.getCurrentRange('hour')
  264 + },
  265 +
  266 + minutes() {
  267 + return this.getCurrentRange('minute')
  268 + },
  269 +
  270 + seconds() {
  271 + return this.getCurrentRange('second')
  272 + },
  273 +
  274 + // picker 当前值数组
  275 + ymd() {
  276 + return [this.year - this.minYear, this.month - this.minMonth, this.day - this.minDay]
  277 + },
  278 + hms() {
  279 + return [this.hour - this.minHour, this.minute - this.minMinute, this.second - this.minSecond]
  280 + },
  281 +
  282 + // 当前 date 是 start
  283 + currentDateIsStart() {
  284 + return this.year === this.startYear && this.month === this.startMonth && this.day === this.startDay
  285 + },
  286 +
  287 + // 当前 date 是 end
  288 + currentDateIsEnd() {
  289 + return this.year === this.endYear && this.month === this.endMonth && this.day === this.endDay
  290 + },
  291 +
  292 + // 当前年、月、日、时、分、秒的最小值和最大值
  293 + minYear() {
  294 + return this.startYear
  295 + },
  296 + maxYear() {
  297 + return this.endYear
  298 + },
  299 + minMonth() {
  300 + if (this.year === this.startYear) {
  301 + return this.startMonth
  302 + } else {
  303 + return 1
  304 + }
  305 + },
  306 + maxMonth() {
  307 + if (this.year === this.endYear) {
  308 + return this.endMonth
  309 + } else {
  310 + return 12
  311 + }
  312 + },
  313 + minDay() {
  314 + if (this.year === this.startYear && this.month === this.startMonth) {
  315 + return this.startDay
  316 + } else {
  317 + return 1
  318 + }
  319 + },
  320 + maxDay() {
  321 + if (this.year === this.endYear && this.month === this.endMonth) {
  322 + return this.endDay
  323 + } else {
  324 + return this.daysInMonth(this.year, this.month)
  325 + }
  326 + },
  327 + minHour() {
  328 + if (this.type === 'datetime') {
  329 + if (this.currentDateIsStart) {
  330 + return this.startHour
  331 + } else {
  332 + return 0
  333 + }
  334 + }
  335 + if (this.type === 'time') {
  336 + return this.startHour
  337 + }
  338 + },
  339 + maxHour() {
  340 + if (this.type === 'datetime') {
  341 + if (this.currentDateIsEnd) {
  342 + return this.endHour
  343 + } else {
  344 + return 23
  345 + }
  346 + }
  347 + if (this.type === 'time') {
  348 + return this.endHour
  349 + }
  350 + },
  351 + minMinute() {
  352 + if (this.type === 'datetime') {
  353 + if (this.currentDateIsStart && this.hour === this.startHour) {
  354 + return this.startMinute
  355 + } else {
  356 + return 0
  357 + }
  358 + }
  359 + if (this.type === 'time') {
  360 + if (this.hour === this.startHour) {
  361 + return this.startMinute
  362 + } else {
  363 + return 0
  364 + }
  365 + }
  366 + },
  367 + maxMinute() {
  368 + if (this.type === 'datetime') {
  369 + if (this.currentDateIsEnd && this.hour === this.endHour) {
  370 + return this.endMinute
  371 + } else {
  372 + return 59
  373 + }
  374 + }
  375 + if (this.type === 'time') {
  376 + if (this.hour === this.endHour) {
  377 + return this.endMinute
  378 + } else {
  379 + return 59
  380 + }
  381 + }
  382 + },
  383 + minSecond() {
  384 + if (this.type === 'datetime') {
  385 + if (this.currentDateIsStart && this.hour === this.startHour && this.minute === this.startMinute) {
  386 + return this.startSecond
  387 + } else {
  388 + return 0
  389 + }
  390 + }
  391 + if (this.type === 'time') {
  392 + if (this.hour === this.startHour && this.minute === this.startMinute) {
  393 + return this.startSecond
  394 + } else {
  395 + return 0
  396 + }
  397 + }
  398 + },
  399 + maxSecond() {
  400 + if (this.type === 'datetime') {
  401 + if (this.currentDateIsEnd && this.hour === this.endHour && this.minute === this.endMinute) {
  402 + return this.endSecond
  403 + } else {
  404 + return 59
  405 + }
  406 + }
  407 + if (this.type === 'time') {
  408 + if (this.hour === this.endHour && this.minute === this.endMinute) {
  409 + return this.endSecond
  410 + } else {
  411 + return 59
  412 + }
  413 + }
  414 + },
  415 +
  416 + /**
  417 + * for i18n
  418 + */
  419 + selectTimeText() {
  420 + return t("uni-datetime-picker.selectTime")
  421 + },
  422 + okText() {
  423 + return t("uni-datetime-picker.ok")
  424 + },
  425 + clearText() {
  426 + return t("uni-datetime-picker.clear")
  427 + },
  428 + cancelText() {
  429 + return t("uni-datetime-picker.cancel")
  430 + }
  431 + },
  432 +
  433 + mounted() {
  434 + // #ifdef APP-NVUE
  435 + const res = uni.getSystemInfoSync();
  436 + this.fixNvueBug = {
  437 + top: res.windowHeight / 2,
  438 + left: res.windowWidth / 2
  439 + }
  440 + // #endif
  441 + },
  442 +
  443 + methods: {
  444 + /**
  445 + * @param {Object} item
  446 + * 小于 10 在前面加个 0
  447 + */
  448 +
  449 + lessThanTen(item) {
  450 + return item < 10 ? '0' + item : item
  451 + },
  452 +
  453 + /**
  454 + * 解析时分秒字符串,例如:00:00:00
  455 + * @param {String} timeString
  456 + */
  457 + parseTimeType(timeString) {
  458 + if (timeString) {
  459 + let timeArr = timeString.split(':')
  460 + this.hour = Number(timeArr[0])
  461 + this.minute = Number(timeArr[1])
  462 + this.second = Number(timeArr[2])
  463 + }
  464 + },
  465 +
  466 + /**
  467 + * 解析选择器初始值,类型可以是字符串、时间戳,例如:2000-10-02、'08:30:00'、 1610695109000
  468 + * @param {String | Number} datetime
  469 + */
  470 + initPickerValue(datetime) {
  471 + let defaultValue = null
  472 + if (datetime) {
  473 + defaultValue = this.compareValueWithStartAndEnd(datetime, this.start, this.end)
  474 + } else {
  475 + defaultValue = Date.now()
  476 + defaultValue = this.compareValueWithStartAndEnd(defaultValue, this.start, this.end)
  477 + }
  478 + this.parseValue(defaultValue)
  479 + },
  480 +
  481 + /**
  482 + * 初始值规则:
  483 + * - 用户设置初始值 value
  484 + * - 设置了起始时间 start、终止时间 end,并 start < value < end,初始值为 value, 否则初始值为 start
  485 + * - 只设置了起始时间 start,并 start < value,初始值为 value,否则初始值为 start
  486 + * - 只设置了终止时间 end,并 value < end,初始值为 value,否则初始值为 end
  487 + * - 无起始终止时间,则初始值为 value
  488 + * - 无初始值 value,则初始值为当前本地时间 Date.now()
  489 + * @param {Object} value
  490 + * @param {Object} dateBase
  491 + */
  492 + compareValueWithStartAndEnd(value, start, end) {
  493 + let winner = null
  494 + value = this.superTimeStamp(value)
  495 + start = this.superTimeStamp(start)
  496 + end = this.superTimeStamp(end)
  497 +
  498 + if (start && end) {
  499 + if (value < start) {
  500 + winner = new Date(start)
  501 + } else if (value > end) {
  502 + winner = new Date(end)
  503 + } else {
  504 + winner = new Date(value)
  505 + }
  506 + } else if (start && !end) {
  507 + winner = start <= value ? new Date(value) : new Date(start)
  508 + } else if (!start && end) {
  509 + winner = value <= end ? new Date(value) : new Date(end)
  510 + } else {
  511 + winner = new Date(value)
  512 + }
  513 +
  514 + return winner
  515 + },
  516 +
  517 + /**
  518 + * 转换为可比较的时间戳,接受日期、时分秒、时间戳
  519 + * @param {Object} value
  520 + */
  521 + superTimeStamp(value) {
  522 + let dateBase = ''
  523 + if (this.type === 'time' && value && typeof value === 'string') {
  524 + const now = new Date()
  525 + const year = now.getFullYear()
  526 + const month = now.getMonth() + 1
  527 + const day = now.getDate()
  528 + dateBase = year + '/' + month + '/' + day + ' '
  529 + }
  530 + if (Number(value) && typeof value !== NaN) {
  531 + value = parseInt(value)
  532 + dateBase = 0
  533 + }
  534 + return this.createTimeStamp(dateBase + value)
  535 + },
  536 +
  537 + /**
  538 + * 解析默认值 value,字符串、时间戳
  539 + * @param {Object} defaultTime
  540 + */
  541 + parseValue(value) {
  542 + if (!value) {
  543 + return
  544 + }
  545 + if (this.type === 'time' && typeof value === "string") {
  546 + this.parseTimeType(value)
  547 + } else {
  548 + let defaultDate = null
  549 + defaultDate = new Date(value)
  550 + if (this.type !== 'time') {
  551 + this.year = defaultDate.getFullYear()
  552 + this.month = defaultDate.getMonth() + 1
  553 + this.day = defaultDate.getDate()
  554 + }
  555 + if (this.type !== 'date') {
  556 + this.hour = defaultDate.getHours()
  557 + this.minute = defaultDate.getMinutes()
  558 + this.second = defaultDate.getSeconds()
  559 + }
  560 + }
  561 + if (this.hideSecond) {
  562 + this.second = 0
  563 + }
  564 + },
  565 +
  566 + /**
  567 + * 解析可选择时间范围 start、end,年月日字符串、时间戳
  568 + * @param {Object} defaultTime
  569 + */
  570 + parseDatetimeRange(point, pointType) {
  571 + // 时间为空,则重置为初始值
  572 + if (!point) {
  573 + if (pointType === 'start') {
  574 + this.startYear = 1920
  575 + this.startMonth = 1
  576 + this.startDay = 1
  577 + this.startHour = 0
  578 + this.startMinute = 0
  579 + this.startSecond = 0
  580 + }
  581 + if (pointType === 'end') {
  582 + this.endYear = 2120
  583 + this.endMonth = 12
  584 + this.endDay = 31
  585 + this.endHour = 23
  586 + this.endMinute = 59
  587 + this.endSecond = 59
  588 + }
  589 + return
  590 + }
  591 + if (this.type === 'time') {
  592 + const pointArr = point.split(':')
  593 + this[pointType + 'Hour'] = Number(pointArr[0])
  594 + this[pointType + 'Minute'] = Number(pointArr[1])
  595 + this[pointType + 'Second'] = Number(pointArr[2])
  596 + } else {
  597 + if (!point) {
  598 + pointType === 'start' ? this.startYear = this.year - 60 : this.endYear = this.year + 60
  599 + return
  600 + }
  601 + if (Number(point) && Number(point) !== NaN) {
  602 + point = parseInt(point)
  603 + }
  604 + // datetime 的 end 没有时分秒, 则不限制
  605 + const hasTime = /[0-9]:[0-9]/
  606 + if (this.type === 'datetime' && pointType === 'end' && typeof point === 'string' && !hasTime.test(
  607 + point)) {
  608 + point = point + ' 23:59:59'
  609 + }
  610 + const pointDate = new Date(point)
  611 + this[pointType + 'Year'] = pointDate.getFullYear()
  612 + this[pointType + 'Month'] = pointDate.getMonth() + 1
  613 + this[pointType + 'Day'] = pointDate.getDate()
  614 + if (this.type === 'datetime') {
  615 + this[pointType + 'Hour'] = pointDate.getHours()
  616 + this[pointType + 'Minute'] = pointDate.getMinutes()
  617 + this[pointType + 'Second'] = pointDate.getSeconds()
  618 + }
  619 + }
  620 + },
  621 +
  622 + // 获取 年、月、日、时、分、秒 当前可选范围
  623 + getCurrentRange(value) {
  624 + const range = []
  625 + for (let i = this['min' + this.capitalize(value)]; i <= this['max' + this.capitalize(value)]; i++) {
  626 + range.push(i)
  627 + }
  628 + return range
  629 + },
  630 +
  631 + // 字符串首字母大写
  632 + capitalize(str) {
  633 + return str.charAt(0).toUpperCase() + str.slice(1)
  634 + },
  635 +
  636 + // 检查当前值是否在范围内,不在则当前值重置为可选范围第一项
  637 + checkValue(name, value, values) {
  638 + if (values.indexOf(value) === -1) {
  639 + this[name] = values[0]
  640 + }
  641 + },
  642 +
  643 + // 每个月的实际天数
  644 + daysInMonth(year, month) { // Use 1 for January, 2 for February, etc.
  645 + return new Date(year, month, 0).getDate();
  646 + },
  647 +
  648 + //兼容 iOS、safari 日期格式
  649 + fixIosDateFormat(value) {
  650 + if (typeof value === 'string') {
  651 + value = value.replace(/-/g, '/')
  652 + }
  653 + return value
  654 + },
  655 +
  656 + /**
  657 + * 生成时间戳
  658 + * @param {Object} time
  659 + */
  660 + createTimeStamp(time) {
  661 + if (!time) return
  662 + if (typeof time === "number") {
  663 + return time
  664 + } else {
  665 + time = time.replace(/-/g, '/')
  666 + if (this.type === 'date') {
  667 + time = time + ' ' + '00:00:00'
  668 + }
  669 + return Date.parse(time)
  670 + }
  671 + },
  672 +
  673 + /**
  674 + * 生成日期或时间的字符串
  675 + */
  676 + createDomSting() {
  677 + const yymmdd = this.year +
  678 + '-' +
  679 + this.lessThanTen(this.month) +
  680 + '-' +
  681 + this.lessThanTen(this.day)
  682 +
  683 + let hhmmss = this.lessThanTen(this.hour) +
  684 + ':' +
  685 + this.lessThanTen(this.minute)
  686 +
  687 + if (!this.hideSecond) {
  688 + hhmmss = hhmmss + ':' + this.lessThanTen(this.second)
  689 + }
  690 +
  691 + if (this.type === 'date') {
  692 + return yymmdd
  693 + } else if (this.type === 'time') {
  694 + return hhmmss
  695 + } else {
  696 + return yymmdd + ' ' + hhmmss
  697 + }
  698 + },
  699 +
  700 + /**
  701 + * 初始化返回值,并抛出 change 事件
  702 + */
  703 + initTime(emit = true) {
  704 + this.time = this.createDomSting()
  705 + if (!emit) return
  706 + if (this.returnType === 'timestamp' && this.type !== 'time') {
  707 + this.$emit('change', this.createTimeStamp(this.time))
  708 + this.$emit('input', this.createTimeStamp(this.time))
  709 + this.$emit('update:modelValue', this.createTimeStamp(this.time))
  710 + } else {
  711 + this.$emit('change', this.time)
  712 + this.$emit('input', this.time)
  713 + this.$emit('update:modelValue', this.time)
  714 + }
  715 + },
  716 +
  717 + /**
  718 + * 用户选择日期或时间更新 data
  719 + * @param {Object} e
  720 + */
  721 + bindDateChange(e) {
  722 + const val = e.detail.value
  723 + this.year = this.years[val[0]]
  724 + this.month = this.months[val[1]]
  725 + this.day = this.days[val[2]]
  726 + },
  727 + bindTimeChange(e) {
  728 + const val = e.detail.value
  729 + this.hour = this.hours[val[0]]
  730 + this.minute = this.minutes[val[1]]
  731 + this.second = this.seconds[val[2]]
  732 + },
  733 +
  734 + /**
  735 + * 初始化弹出层
  736 + */
  737 + initTimePicker() {
  738 + if (this.disabled) return
  739 + const value = this.fixIosDateFormat(this.value)
  740 + this.initPickerValue(value)
  741 + this.visible = !this.visible
  742 + },
  743 +
  744 + /**
  745 + * 触发或关闭弹框
  746 + */
  747 + tiggerTimePicker(e) {
  748 + this.visible = !this.visible
  749 + },
  750 +
  751 + /**
  752 + * 用户点击“清空”按钮,清空当前值
  753 + */
  754 + clearTime() {
  755 + this.time = ''
  756 + this.$emit('change', this.time)
  757 + this.$emit('input', this.time)
  758 + this.$emit('update:modelValue', this.time)
  759 + this.tiggerTimePicker()
  760 + },
  761 +
  762 + /**
  763 + * 用户点击“确定”按钮
  764 + */
  765 + setTime() {
  766 + this.initTime()
  767 + this.tiggerTimePicker()
  768 + }
  769 + }
  770 + }
  771 +</script>
  772 +
  773 +<style>
  774 + .uni-datetime-picker {
  775 + /* #ifndef APP-NVUE */
  776 + /* width: 100%; */
  777 + /* #endif */
  778 + }
  779 +
  780 + .uni-datetime-picker-view {
  781 + height: 130px;
  782 + width: 270px;
  783 + /* #ifndef APP-NVUE */
  784 + cursor: pointer;
  785 + /* #endif */
  786 + }
  787 +
  788 + .uni-datetime-picker-item {
  789 + height: 50px;
  790 + line-height: 50px;
  791 + text-align: center;
  792 + font-size: 14px;
  793 + }
  794 +
  795 + .uni-datetime-picker-btn {
  796 + margin-top: 60px;
  797 + /* #ifndef APP-NVUE */
  798 + display: flex;
  799 + cursor: pointer;
  800 + /* #endif */
  801 + flex-direction: row;
  802 + justify-content: space-between;
  803 + }
  804 +
  805 + .uni-datetime-picker-btn-text {
  806 + font-size: 14px;
  807 + color: #007AFF;
  808 + }
  809 +
  810 + .uni-datetime-picker-btn-group {
  811 + /* #ifndef APP-NVUE */
  812 + display: flex;
  813 + /* #endif */
  814 + flex-direction: row;
  815 + }
  816 +
  817 + .uni-datetime-picker-cancel {
  818 + margin-right: 30px;
  819 + }
  820 +
  821 + .uni-datetime-picker-mask {
  822 + position: fixed;
  823 + bottom: 0px;
  824 + top: 0px;
  825 + left: 0px;
  826 + right: 0px;
  827 + background-color: rgba(0, 0, 0, 0.4);
  828 + transition-duration: 0.3s;
  829 + z-index: 998;
  830 + }
  831 +
  832 + .uni-datetime-picker-popup {
  833 + border-radius: 8px;
  834 + padding: 30px;
  835 + width: 270px;
  836 + /* #ifdef APP-NVUE */
  837 + height: 500px;
  838 + /* #endif */
  839 + /* #ifdef APP-NVUE */
  840 + width: 330px;
  841 + /* #endif */
  842 + background-color: #fff;
  843 + position: fixed;
  844 + top: 50%;
  845 + left: 50%;
  846 + transform: translate(-50%, -50%);
  847 + transition-duration: 0.3s;
  848 + z-index: 999;
  849 + }
  850 +
  851 + .fix-nvue-height {
  852 + /* #ifdef APP-NVUE */
  853 + height: 330px;
  854 + /* #endif */
  855 + }
  856 +
  857 + .uni-datetime-picker-time {
  858 + color: grey;
  859 + }
  860 +
  861 + .uni-datetime-picker-column {
  862 + height: 50px;
  863 + }
  864 +
  865 + .uni-datetime-picker-timebox {
  866 +
  867 + border: 1px solid #E5E5E5;
  868 + border-radius: 5px;
  869 + padding: 7px 10px;
  870 + /* #ifndef APP-NVUE */
  871 + box-sizing: border-box;
  872 + cursor: pointer;
  873 + /* #endif */
  874 + }
  875 +
  876 + .uni-datetime-picker-timebox-pointer {
  877 + /* #ifndef APP-NVUE */
  878 + cursor: pointer;
  879 + /* #endif */
  880 + }
  881 +
  882 +
  883 + .uni-datetime-picker-disabled {
  884 + opacity: 0.4;
  885 + /* #ifdef H5 */
  886 + cursor: not-allowed !important;
  887 + /* #endif */
  888 + }
  889 +
  890 + .uni-datetime-picker-text {
  891 + font-size: 14px;
  892 + }
  893 +
  894 + .uni-datetime-picker-sign {
  895 + position: absolute;
  896 + top: 53px;
  897 + /* 减掉 10px 的元素高度,兼容nvue */
  898 + color: #999;
  899 + /* #ifdef APP-NVUE */
  900 + font-size: 16px;
  901 + /* #endif */
  902 + }
  903 +
  904 + .sign-left {
  905 + left: 86px;
  906 + }
  907 +
  908 + .sign-right {
  909 + right: 86px;
  910 + }
  911 +
  912 + .sign-center {
  913 + left: 135px;
  914 + }
  915 +
  916 + .uni-datetime-picker__container-box {
  917 + position: relative;
  918 + display: flex;
  919 + align-items: center;
  920 + justify-content: center;
  921 + margin-top: 40px;
  922 + }
  923 +
  924 + .time-hide-second {
  925 + width: 180px;
  926 + }
  927 +</style>
... ...
  1 +<template>
  2 + <view class="uni-date">
  3 + <view class="uni-date-editor" @click="show">
  4 + <slot>
  5 + <view class="uni-date-editor--x" :class="{'uni-date-editor--x__disabled': disabled,
  6 + 'uni-date-x--border': border}">
  7 + <view v-if="!isRange" class="uni-date-x uni-date-single">
  8 + <uni-icons type="calendar" color="#e1e1e1" size="22"></uni-icons>
  9 + <input class="uni-date__x-input" type="text" v-model="singleVal"
  10 + :placeholder="singlePlaceholderText" :disabled="true" />
  11 + </view>
  12 + <view v-else class="uni-date-x uni-date-range">
  13 + <uni-icons type="calendar" color="#e1e1e1" size="22"></uni-icons>
  14 + <input class="uni-date__x-input t-c" type="text" v-model="range.startDate"
  15 + :placeholder="startPlaceholderText" :disabled="true" />
  16 + <slot>
  17 + <view class="">{{rangeSeparator}}</view>
  18 + </slot>
  19 + <input class="uni-date__x-input t-c" type="text" v-model="range.endDate"
  20 + :placeholder="endPlaceholderText" :disabled="true" />
  21 + </view>
  22 + <view v-if="showClearIcon" class="uni-date__icon-clear" @click.stop="clear">
  23 + <uni-icons type="clear" color="#e1e1e1" size="18"></uni-icons>
  24 + </view>
  25 + </view>
  26 + </slot>
  27 + </view>
  28 +
  29 + <view v-show="popup" class="uni-date-mask" @click="close"></view>
  30 + <view v-if="!isPhone" ref="datePicker" v-show="popup" class="uni-date-picker__container">
  31 + <view v-if="!isRange" class="uni-date-single--x" :style="popover">
  32 + <view class="uni-popper__arrow"></view>
  33 + <view v-if="hasTime" class="uni-date-changed popup-x-header">
  34 + <input class="uni-date__input t-c" type="text" v-model="tempSingleDate"
  35 + :placeholder="selectDateText" />
  36 + <time-picker type="time" v-model="time" :border="false" :disabled="!tempSingleDate"
  37 + :start="reactStartTime" :end="reactEndTime" :hideSecond="hideSecond" style="width: 100%;">
  38 + <input class="uni-date__input t-c" type="text" v-model="time" :placeholder="selectTimeText"
  39 + :disabled="!tempSingleDate" />
  40 + </time-picker>
  41 + </view>
  42 + <calendar ref="pcSingle" :showMonth="false"
  43 + :start-date="caleRange.startDate" :end-date="caleRange.endDate" :date="defSingleDate"
  44 + @change="singleChange" style="padding: 0 8px;" />
  45 + <view v-if="hasTime" class="popup-x-footer">
  46 + <!-- <text class="">此刻</text> -->
  47 + <text class="confirm" @click="confirmSingleChange">{{okText}}</text>
  48 + </view>
  49 + <view class="uni-date-popper__arrow"></view>
  50 + </view>
  51 +
  52 + <view v-else class="uni-date-range--x" :style="popover">
  53 + <view class="uni-popper__arrow"></view>
  54 + <view v-if="hasTime" class="popup-x-header uni-date-changed">
  55 + <view class="popup-x-header--datetime">
  56 + <input class="uni-date__input uni-date-range__input" type="text" v-model="tempRange.startDate"
  57 + :placeholder="startDateText" />
  58 + <time-picker type="time" v-model="tempRange.startTime" :start="reactStartTime" :border="false"
  59 + :disabled="!tempRange.startDate" :hideSecond="hideSecond">
  60 + <input class="uni-date__input uni-date-range__input" type="text"
  61 + v-model="tempRange.startTime" :placeholder="startTimeText"
  62 + :disabled="!tempRange.startDate" />
  63 + </time-picker>
  64 + </view>
  65 + <uni-icons type="arrowthinright" color="#999" style="line-height: 40px;"></uni-icons>
  66 + <view class="popup-x-header--datetime">
  67 + <input class="uni-date__input uni-date-range__input" type="text" v-model="tempRange.endDate"
  68 + :placeholder="endDateText" />
  69 + <time-picker type="time" v-model="tempRange.endTime" :end="reactEndTime" :border="false"
  70 + :disabled="!tempRange.endDate" :hideSecond="hideSecond">
  71 + <input class="uni-date__input uni-date-range__input" type="text" v-model="tempRange.endTime"
  72 + :placeholder="endTimeText" :disabled="!tempRange.endDate" />
  73 + </time-picker>
  74 + </view>
  75 + </view>
  76 + <view class="popup-x-body">
  77 + <calendar ref="left" :showMonth="false"
  78 + :start-date="caleRange.startDate" :end-date="caleRange.endDate" :range="true"
  79 + @change="leftChange" :pleStatus="endMultipleStatus" @firstEnterCale="updateRightCale"
  80 + @monthSwitch="leftMonthSwitch" style="padding: 0 8px;" />
  81 + <calendar ref="right" :showMonth="false"
  82 + :start-date="caleRange.startDate" :end-date="caleRange.endDate" :range="true"
  83 + @change="rightChange" :pleStatus="startMultipleStatus" @firstEnterCale="updateLeftCale"
  84 + @monthSwitch="rightMonthSwitch" style="padding: 0 8px;border-left: 1px solid #F1F1F1;" />
  85 + </view>
  86 + <view v-if="hasTime" class="popup-x-footer">
  87 + <text class="" @click="clear">{{clearText}}</text>
  88 + <text class="confirm" @click="confirmRangeChange">{{okText}}</text>
  89 + </view>
  90 + </view>
  91 + </view>
  92 + <calendar v-show="isPhone" ref="mobile" :clearDate="false" :date="defSingleDate" :defTime="reactMobDefTime"
  93 + :start-date="caleRange.startDate" :end-date="caleRange.endDate" :selectableTimes="mobSelectableTime"
  94 + :pleStatus="endMultipleStatus" :showMonth="false" :range="isRange" :typeHasTime="hasTime" :insert="false"
  95 + :hideSecond="hideSecond" @confirm="mobileChange" />
  96 + </view>
  97 +</template>
  98 +<script>
  99 + /**
  100 + * DatetimePicker 时间选择器
  101 + * @description 同时支持 PC 和移动端使用日历选择日期和日期范围
  102 + * @tutorial https://ext.dcloud.net.cn/plugin?id=3962
  103 + * @property {String} type 选择器类型
  104 + * @property {String|Number|Array|Date} value 绑定值
  105 + * @property {String} placeholder 单选择时的占位内容
  106 + * @property {String} start 起始时间
  107 + * @property {String} end 终止时间
  108 + * @property {String} start-placeholder 范围选择时开始日期的占位内容
  109 + * @property {String} end-placeholder 范围选择时结束日期的占位内容
  110 + * @property {String} range-separator 选择范围时的分隔符
  111 + * @property {Boolean} border = [true|false] 是否有边框
  112 + * @property {Boolean} disabled = [true|false] 是否禁用
  113 + * @property {Boolean} clearIcon = [true|false] 是否显示清除按钮(仅PC端适用)
  114 + * @event {Function} change 确定日期时触发的事件
  115 + * @event {Function} show 打开弹出层
  116 + * @event {Function} close 关闭弹出层
  117 + * @event {Function} clear 清除上次选中的状态和值
  118 + **/
  119 + import calendar from './calendar.vue'
  120 + import timePicker from './time-picker.vue'
  121 + import {
  122 + initVueI18n
  123 + } from '@dcloudio/uni-i18n'
  124 + import messages from './i18n/index.js'
  125 + const {
  126 + t
  127 + } = initVueI18n(messages)
  128 +
  129 + export default {
  130 + name: 'UniDatetimePicker',
  131 + components: {
  132 + calendar,
  133 + timePicker
  134 + },
  135 + data() {
  136 + return {
  137 + isRange: false,
  138 + hasTime: false,
  139 + mobileRange: false,
  140 + // 单选
  141 + singleVal: '',
  142 + tempSingleDate: '',
  143 + defSingleDate: '',
  144 + time: '',
  145 + // 范围选
  146 + caleRange: {
  147 + startDate: '',
  148 + startTime: '',
  149 + endDate: '',
  150 + endTime: ''
  151 + },
  152 + range: {
  153 + startDate: '',
  154 + // startTime: '',
  155 + endDate: '',
  156 + // endTime: ''
  157 + },
  158 + tempRange: {
  159 + startDate: '',
  160 + startTime: '',
  161 + endDate: '',
  162 + endTime: ''
  163 + },
  164 + // 左右日历同步数据
  165 + startMultipleStatus: {
  166 + before: '',
  167 + after: '',
  168 + data: [],
  169 + fulldate: ''
  170 + },
  171 + endMultipleStatus: {
  172 + before: '',
  173 + after: '',
  174 + data: [],
  175 + fulldate: ''
  176 + },
  177 + visible: false,
  178 + popup: false,
  179 + popover: null,
  180 + isEmitValue: false,
  181 + isPhone: false,
  182 + isFirstShow: true,
  183 + }
  184 + },
  185 + props: {
  186 + type: {
  187 + type: String,
  188 + default: 'datetime'
  189 + },
  190 + value: {
  191 + type: [String, Number, Array, Date],
  192 + default: ''
  193 + },
  194 + modelValue: {
  195 + type: [String, Number, Array, Date],
  196 + default: ''
  197 + },
  198 + start: {
  199 + type: [Number, String],
  200 + default: ''
  201 + },
  202 + end: {
  203 + type: [Number, String],
  204 + default: ''
  205 + },
  206 + returnType: {
  207 + type: String,
  208 + default: 'string'
  209 + },
  210 + placeholder: {
  211 + type: String,
  212 + default: ''
  213 + },
  214 + startPlaceholder: {
  215 + type: String,
  216 + default: ''
  217 + },
  218 + endPlaceholder: {
  219 + type: String,
  220 + default: ''
  221 + },
  222 + rangeSeparator: {
  223 + type: String,
  224 + default: '-'
  225 + },
  226 + border: {
  227 + type: [Boolean],
  228 + default: true
  229 + },
  230 + disabled: {
  231 + type: [Boolean],
  232 + default: false
  233 + },
  234 + clearIcon: {
  235 + type: [Boolean],
  236 + default: true
  237 + },
  238 + hideSecond: {
  239 + type: [Boolean],
  240 + default: false
  241 + }
  242 + },
  243 + watch: {
  244 + type: {
  245 + immediate: true,
  246 + handler(newVal, oldVal) {
  247 + if (newVal.indexOf('time') !== -1) {
  248 + this.hasTime = true
  249 + } else {
  250 + this.hasTime = false
  251 + }
  252 + if (newVal.indexOf('range') !== -1) {
  253 + this.isRange = true
  254 + } else {
  255 + this.isRange = false
  256 + }
  257 + }
  258 + },
  259 + value: {
  260 + immediate: true,
  261 + handler(newVal, oldVal) {
  262 + if (this.isEmitValue) {
  263 + this.isEmitValue = false
  264 + return
  265 + }
  266 + this.initPicker(newVal)
  267 + }
  268 + },
  269 + modelValue: {
  270 + immediate: true,
  271 + handler(newVal, oldVal) {
  272 + if (this.isEmitValue) {
  273 + this.isEmitValue = false
  274 + return
  275 + }
  276 + this.initPicker(newVal)
  277 + }
  278 + },
  279 + start: {
  280 + immediate: true,
  281 + handler(newVal, oldVal) {
  282 + if (!newVal) return
  283 + const {
  284 + defDate,
  285 + defTime
  286 + } = this.parseDate(newVal)
  287 + this.caleRange.startDate = defDate
  288 + if (this.hasTime) {
  289 + this.caleRange.startTime = defTime
  290 + }
  291 + }
  292 + },
  293 + end: {
  294 + immediate: true,
  295 + handler(newVal, oldVal) {
  296 + if (!newVal) return
  297 + const {
  298 + defDate,
  299 + defTime
  300 + } = this.parseDate(newVal)
  301 + this.caleRange.endDate = defDate
  302 + if (this.hasTime) {
  303 + this.caleRange.endTime = defTime
  304 + }
  305 + }
  306 + },
  307 + },
  308 + computed: {
  309 + reactStartTime() {
  310 + const activeDate = this.isRange ? this.tempRange.startDate : this.tempSingleDate
  311 + const res = activeDate === this.caleRange.startDate ? this.caleRange.startTime : ''
  312 + return res
  313 + },
  314 + reactEndTime() {
  315 + const activeDate = this.isRange ? this.tempRange.endDate : this.tempSingleDate
  316 + const res = activeDate === this.caleRange.endDate ? this.caleRange.endTime : ''
  317 + return res
  318 + },
  319 + reactMobDefTime() {
  320 + const times = {
  321 + start: this.tempRange.startTime,
  322 + end: this.tempRange.endTime
  323 + }
  324 + return this.isRange ? times : this.time
  325 + },
  326 + mobSelectableTime() {
  327 + return {
  328 + start: this.caleRange.startTime,
  329 + end: this.caleRange.endTime
  330 + }
  331 + },
  332 + datePopupWidth() {
  333 + // todo
  334 + return this.isRange ? 653 : 301
  335 + },
  336 +
  337 + /**
  338 + * for i18n
  339 + */
  340 + singlePlaceholderText() {
  341 + return this.placeholder || (this.type === 'date' ? this.selectDateText : t(
  342 + "uni-datetime-picker.selectDateTime"))
  343 + },
  344 + startPlaceholderText() {
  345 + return this.startPlaceholder || this.startDateText
  346 + },
  347 + endPlaceholderText() {
  348 + return this.endPlaceholder || this.endDateText
  349 + },
  350 + selectDateText() {
  351 + return t("uni-datetime-picker.selectDate")
  352 + },
  353 + selectTimeText() {
  354 + return t("uni-datetime-picker.selectTime")
  355 + },
  356 + startDateText() {
  357 + return this.startPlaceholder || t("uni-datetime-picker.startDate")
  358 + },
  359 + startTimeText() {
  360 + return t("uni-datetime-picker.startTime")
  361 + },
  362 + endDateText() {
  363 + return this.endPlaceholder || t("uni-datetime-picker.endDate")
  364 + },
  365 + endTimeText() {
  366 + return t("uni-datetime-picker.endTime")
  367 + },
  368 + okText() {
  369 + return t("uni-datetime-picker.ok")
  370 + },
  371 + clearText() {
  372 + return t("uni-datetime-picker.clear")
  373 + },
  374 + showClearIcon() {
  375 + const { clearIcon, disabled, singleVal, range } = this
  376 + const bool = clearIcon && !disabled && (singleVal || (range.startDate && range.endDate))
  377 + return bool
  378 + }
  379 + },
  380 + created() {
  381 + this.form = this.getForm('uniForms')
  382 + this.formItem = this.getForm('uniFormsItem')
  383 +
  384 + // if (this.formItem) {
  385 + // if (this.formItem.name) {
  386 + // this.rename = this.formItem.name
  387 + // this.form.inputChildrens.push(this)
  388 + // }
  389 + // }
  390 + },
  391 + mounted() {
  392 + this.platform()
  393 + },
  394 + methods: {
  395 + /**
  396 + * 获取父元素实例
  397 + */
  398 + getForm(name = 'uniForms') {
  399 + let parent = this.$parent;
  400 + let parentName = parent.$options.name;
  401 + while (parentName !== name) {
  402 + parent = parent.$parent;
  403 + if (!parent) return false
  404 + parentName = parent.$options.name;
  405 + }
  406 + return parent;
  407 + },
  408 + initPicker(newVal) {
  409 + if (!newVal || Array.isArray(newVal) && !newVal.length) {
  410 + this.$nextTick(() => {
  411 + this.clear(false)
  412 + })
  413 + return
  414 + }
  415 + if (!Array.isArray(newVal) && !this.isRange) {
  416 + const {
  417 + defDate,
  418 + defTime
  419 + } = this.parseDate(newVal)
  420 + this.singleVal = defDate
  421 + this.tempSingleDate = defDate
  422 + this.defSingleDate = defDate
  423 + if (this.hasTime) {
  424 + this.singleVal = defDate + ' ' + defTime
  425 + this.time = defTime
  426 + }
  427 + } else {
  428 + const [before, after] = newVal
  429 + if (!before && !after) return
  430 + const defBefore = this.parseDate(before)
  431 + const defAfter = this.parseDate(after)
  432 + const startDate = defBefore.defDate
  433 + const endDate = defAfter.defDate
  434 + this.range.startDate = this.tempRange.startDate = startDate
  435 + this.range.endDate = this.tempRange.endDate = endDate
  436 +
  437 + if (this.hasTime) {
  438 + this.range.startDate = defBefore.defDate + ' ' + defBefore.defTime
  439 + this.range.endDate = defAfter.defDate + ' ' + defAfter.defTime
  440 + this.tempRange.startTime = defBefore.defTime
  441 + this.tempRange.endTime = defAfter.defTime
  442 + }
  443 + const defaultRange = {
  444 + before: defBefore.defDate,
  445 + after: defAfter.defDate
  446 + }
  447 + this.startMultipleStatus = Object.assign({}, this.startMultipleStatus, defaultRange, {
  448 + which: 'right'
  449 + })
  450 + this.endMultipleStatus = Object.assign({}, this.endMultipleStatus, defaultRange, {
  451 + which: 'left'
  452 + })
  453 + }
  454 + },
  455 + updateLeftCale(e) {
  456 + const left = this.$refs.left
  457 + // 设置范围选
  458 + left.cale.setHoverMultiple(e.after)
  459 + left.setDate(this.$refs.left.nowDate.fullDate)
  460 + },
  461 + updateRightCale(e) {
  462 + const right = this.$refs.right
  463 + // 设置范围选
  464 + right.cale.setHoverMultiple(e.after)
  465 + right.setDate(this.$refs.right.nowDate.fullDate)
  466 + },
  467 + platform() {
  468 + const systemInfo = uni.getSystemInfoSync()
  469 + this.isPhone = systemInfo.windowWidth <= 500
  470 + this.windowWidth = systemInfo.windowWidth
  471 + },
  472 + show(event) {
  473 + if (this.disabled) {
  474 + return
  475 + }
  476 + this.platform()
  477 + if (this.isPhone) {
  478 + this.$refs.mobile.open()
  479 + return
  480 + }
  481 + this.popover = {
  482 + top: '10px'
  483 + }
  484 + const dateEditor = uni.createSelectorQuery().in(this).select(".uni-date-editor")
  485 + dateEditor.boundingClientRect(rect => {
  486 + if (this.windowWidth - rect.left < this.datePopupWidth) {
  487 + this.popover.right = 0
  488 + }
  489 + }).exec()
  490 + setTimeout(() => {
  491 + this.popup = !this.popup
  492 + if (!this.isPhone && this.isRange && this.isFirstShow) {
  493 + this.isFirstShow = false
  494 + const {
  495 + startDate,
  496 + endDate
  497 + } = this.range
  498 + if (startDate && endDate) {
  499 + if (this.diffDate(startDate, endDate) < 30) {
  500 + this.$refs.right.next()
  501 + }
  502 + } else {
  503 + this.$refs.right.next()
  504 + this.$refs.right.cale.lastHover = false
  505 + }
  506 + }
  507 +
  508 + }, 50)
  509 + },
  510 +
  511 + close() {
  512 + setTimeout(() => {
  513 + this.popup = false
  514 + this.$emit('maskClick', this.value)
  515 + }, 20)
  516 + },
  517 + setEmit(value) {
  518 + if (this.returnType === "timestamp" || this.returnType === "date") {
  519 + if (!Array.isArray(value)) {
  520 + if (!this.hasTime) {
  521 + value = value + ' ' + '00:00:00'
  522 + }
  523 + value = this.createTimestamp(value)
  524 + if (this.returnType === "date") {
  525 + value = new Date(value)
  526 + }
  527 + } else {
  528 + if (!this.hasTime) {
  529 + value[0] = value[0] + ' ' + '00:00:00'
  530 + value[1] = value[1] + ' ' + '00:00:00'
  531 + }
  532 + value[0] = this.createTimestamp(value[0])
  533 + value[1] = this.createTimestamp(value[1])
  534 + if (this.returnType === "date") {
  535 + value[0] = new Date(value[0])
  536 + value[1] = new Date(value[1])
  537 + }
  538 + }
  539 + }
  540 + this.formItem && this.formItem.setValue(value)
  541 + this.$emit('change', value)
  542 + this.$emit('input', value)
  543 + this.$emit('update:modelValue', value)
  544 + this.isEmitValue = true
  545 + },
  546 + createTimestamp(date) {
  547 + date = this.fixIosDateFormat(date)
  548 + return Date.parse(new Date(date))
  549 + },
  550 + singleChange(e) {
  551 + this.tempSingleDate = e.fulldate
  552 + if (this.hasTime) return
  553 + this.confirmSingleChange()
  554 + },
  555 +
  556 + confirmSingleChange() {
  557 + if (!this.tempSingleDate) {
  558 + this.popup = false
  559 + return
  560 + }
  561 + if (this.hasTime) {
  562 + this.singleVal = this.tempSingleDate + ' ' + (this.time ? this.time : '00:00:00')
  563 + } else {
  564 + this.singleVal = this.tempSingleDate
  565 + }
  566 + this.setEmit(this.singleVal)
  567 + this.popup = false
  568 + },
  569 +
  570 + leftChange(e) {
  571 + const {
  572 + before,
  573 + after
  574 + } = e.range
  575 + this.rangeChange(before, after)
  576 + const obj = {
  577 + before: e.range.before,
  578 + after: e.range.after,
  579 + data: e.range.data,
  580 + fulldate: e.fulldate
  581 + }
  582 + this.startMultipleStatus = Object.assign({}, this.startMultipleStatus, obj)
  583 + },
  584 +
  585 + rightChange(e) {
  586 + const {
  587 + before,
  588 + after
  589 + } = e.range
  590 + this.rangeChange(before, after)
  591 + const obj = {
  592 + before: e.range.before,
  593 + after: e.range.after,
  594 + data: e.range.data,
  595 + fulldate: e.fulldate
  596 + }
  597 + this.endMultipleStatus = Object.assign({}, this.endMultipleStatus, obj)
  598 + },
  599 +
  600 + mobileChange(e) {
  601 + if (this.isRange) {
  602 + const {
  603 + before,
  604 + after
  605 + } = e.range
  606 + this.handleStartAndEnd(before, after, true)
  607 + if (this.hasTime) {
  608 + const {
  609 + startTime,
  610 + endTime
  611 + } = e.timeRange
  612 + this.tempRange.startTime = startTime
  613 + this.tempRange.endTime = endTime
  614 + }
  615 + this.confirmRangeChange()
  616 +
  617 + } else {
  618 + if (this.hasTime) {
  619 + this.singleVal = e.fulldate + ' ' + e.time
  620 + } else {
  621 + this.singleVal = e.fulldate
  622 + }
  623 + this.setEmit(this.singleVal)
  624 + }
  625 + this.$refs.mobile.close()
  626 + },
  627 +
  628 + rangeChange(before, after) {
  629 + if (!(before && after)) return
  630 + this.handleStartAndEnd(before, after, true)
  631 + if (this.hasTime) return
  632 + this.confirmRangeChange()
  633 + },
  634 +
  635 + confirmRangeChange() {
  636 + if (!this.tempRange.startDate && !this.tempRange.endDate) {
  637 + this.popup = false
  638 + return
  639 + }
  640 + let start, end
  641 + if (!this.hasTime) {
  642 + start = this.range.startDate = this.tempRange.startDate
  643 + end = this.range.endDate = this.tempRange.endDate
  644 + } else {
  645 + start = this.range.startDate = this.tempRange.startDate + ' ' +
  646 + (this.tempRange.startTime ? this.tempRange.startTime : '00:00:00')
  647 + end = this.range.endDate = this.tempRange.endDate + ' ' +
  648 + (this.tempRange.endTime ? this.tempRange.endTime : '00:00:00')
  649 + }
  650 + const displayRange = [start, end]
  651 + this.setEmit(displayRange)
  652 + this.popup = false
  653 + },
  654 +
  655 + handleStartAndEnd(before, after, temp = false) {
  656 + if (!(before && after)) return
  657 + const type = temp ? 'tempRange' : 'range'
  658 + if (this.dateCompare(before, after)) {
  659 + this[type].startDate = before
  660 + this[type].endDate = after
  661 + } else {
  662 + this[type].startDate = after
  663 + this[type].endDate = before
  664 + }
  665 + },
  666 +
  667 + /**
  668 + * 比较时间大小
  669 + */
  670 + dateCompare(startDate, endDate) {
  671 + // 计算截止时间
  672 + startDate = new Date(startDate.replace('-', '/').replace('-', '/'))
  673 + // 计算详细项的截止时间
  674 + endDate = new Date(endDate.replace('-', '/').replace('-', '/'))
  675 + if (startDate <= endDate) {
  676 + return true
  677 + } else {
  678 + return false
  679 + }
  680 + },
  681 +
  682 + /**
  683 + * 比较时间差
  684 + */
  685 + diffDate(startDate, endDate) {
  686 + // 计算截止时间
  687 + startDate = new Date(startDate.replace('-', '/').replace('-', '/'))
  688 + // 计算详细项的截止时间
  689 + endDate = new Date(endDate.replace('-', '/').replace('-', '/'))
  690 + const diff = (endDate - startDate) / (24 * 60 * 60 * 1000)
  691 + return Math.abs(diff)
  692 + },
  693 +
  694 + clear(needEmit = true) {
  695 + if (!this.isRange) {
  696 + this.singleVal = ''
  697 + this.tempSingleDate = ''
  698 + this.time = ''
  699 + if (this.isPhone) {
  700 + this.$refs.mobile && this.$refs.mobile.clearCalender()
  701 + } else {
  702 + this.$refs.pcSingle && this.$refs.pcSingle.clearCalender()
  703 + }
  704 + if (needEmit) {
  705 + this.formItem && this.formItem.setValue('')
  706 + this.$emit('change', '')
  707 + this.$emit('input', '')
  708 + this.$emit('update:modelValue', '')
  709 + }
  710 + } else {
  711 + this.range.startDate = ''
  712 + this.range.endDate = ''
  713 + this.tempRange.startDate = ''
  714 + this.tempRange.startTime = ''
  715 + this.tempRange.endDate = ''
  716 + this.tempRange.endTime = ''
  717 + if (this.isPhone) {
  718 + this.$refs.mobile && this.$refs.mobile.clearCalender()
  719 + } else {
  720 + this.$refs.left && this.$refs.left.clearCalender()
  721 + this.$refs.right && this.$refs.right.clearCalender()
  722 + this.$refs.right && this.$refs.right.next()
  723 + }
  724 + if (needEmit) {
  725 + this.formItem && this.formItem.setValue([])
  726 + this.$emit('change', [])
  727 + this.$emit('input', [])
  728 + this.$emit('update:modelValue', [])
  729 + }
  730 + }
  731 + },
  732 +
  733 + parseDate(date) {
  734 + date = this.fixIosDateFormat(date)
  735 + const defVal = new Date(date)
  736 + const year = defVal.getFullYear()
  737 + const month = defVal.getMonth() + 1
  738 + const day = defVal.getDate()
  739 + const hour = defVal.getHours()
  740 + const minute = defVal.getMinutes()
  741 + const second = defVal.getSeconds()
  742 + const defDate = year + '-' + this.lessTen(month) + '-' + this.lessTen(day)
  743 + const defTime = this.lessTen(hour) + ':' + this.lessTen(minute) + (this.hideSecond ? '' : (':' + this
  744 + .lessTen(second)))
  745 + return {
  746 + defDate,
  747 + defTime
  748 + }
  749 + },
  750 +
  751 + lessTen(item) {
  752 + return item < 10 ? '0' + item : item
  753 + },
  754 +
  755 + //兼容 iOS、safari 日期格式
  756 + fixIosDateFormat(value) {
  757 + if (typeof value === 'string') {
  758 + value = value.replace(/-/g, '/')
  759 + }
  760 + return value
  761 + },
  762 +
  763 + leftMonthSwitch(e) {
  764 + // console.log('leftMonthSwitch 返回:', e)
  765 + },
  766 + rightMonthSwitch(e) {
  767 + // console.log('rightMonthSwitch 返回:', e)
  768 + }
  769 + }
  770 + }
  771 +</script>
  772 +
  773 +<style>
  774 + .uni-date-x {
  775 + display: flex;
  776 + flex-direction: row;
  777 + align-items: center;
  778 + justify-content: center;
  779 + padding: 0 10px;
  780 + border-radius: 4px;
  781 + background-color: #fff;
  782 + color: #666;
  783 + font-size: 14px;
  784 + }
  785 +
  786 + .uni-date-x--border {
  787 + box-sizing: border-box;
  788 + border-radius: 4px;
  789 + border: 1px solid #dcdfe6;
  790 + }
  791 +
  792 + .uni-date-editor--x {
  793 + position: relative;
  794 + }
  795 +
  796 + .uni-date-editor--x .uni-date__icon-clear {
  797 + position: absolute;
  798 + top: 0;
  799 + right: 0;
  800 + display: inline-block;
  801 + box-sizing: border-box;
  802 + border: 9px solid transparent;
  803 + /* #ifdef H5 */
  804 + cursor: pointer;
  805 + /* #endif */
  806 + }
  807 +
  808 + .uni-date__x-input {
  809 + padding: 0 8px;
  810 + height: 40px;
  811 + width: 100%;
  812 + line-height: 40px;
  813 + font-size: 14px;
  814 + }
  815 +
  816 + .t-c {
  817 + text-align: center;
  818 + }
  819 +
  820 + .uni-date__input {
  821 + height: 40px;
  822 + width: 100%;
  823 + line-height: 40px;
  824 + font-size: 14px;
  825 + }
  826 +
  827 + .uni-date-range__input {
  828 + text-align: center;
  829 + max-width: 142px;
  830 + }
  831 +
  832 + .uni-date-picker__container {
  833 + position: relative;
  834 + /* position: fixed;
  835 + left: 0;
  836 + right: 0;
  837 + top: 0;
  838 + bottom: 0;
  839 + box-sizing: border-box;
  840 + z-index: 996;
  841 + font-size: 14px; */
  842 + }
  843 +
  844 + .uni-date-mask {
  845 + position: fixed;
  846 + bottom: 0px;
  847 + top: 0px;
  848 + left: 0px;
  849 + right: 0px;
  850 + background-color: rgba(0, 0, 0, 0);
  851 + transition-duration: 0.3s;
  852 + z-index: 996;
  853 + }
  854 +
  855 + .uni-date-single--x {
  856 + /* padding: 0 8px; */
  857 + background-color: #fff;
  858 + position: absolute;
  859 + top: 0;
  860 + z-index: 999;
  861 + border: 1px solid #EBEEF5;
  862 + box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
  863 + border-radius: 4px;
  864 + }
  865 +
  866 + .uni-date-range--x {
  867 + /* padding: 0 8px; */
  868 + background-color: #fff;
  869 + position: absolute;
  870 + top: 0;
  871 + z-index: 999;
  872 + border: 1px solid #EBEEF5;
  873 + box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
  874 + border-radius: 4px;
  875 + }
  876 +
  877 + .uni-date-editor--x__disabled {
  878 + opacity: 0.4;
  879 + cursor: default;
  880 + }
  881 +
  882 + .uni-date-editor--logo {
  883 + width: 16px;
  884 + height: 16px;
  885 + vertical-align: middle;
  886 + }
  887 +
  888 + /* 添加时间 */
  889 + .popup-x-header {
  890 + /* #ifndef APP-NVUE */
  891 + display: flex;
  892 + /* #endif */
  893 + flex-direction: row;
  894 + /* justify-content: space-between; */
  895 + }
  896 +
  897 + .popup-x-header--datetime {
  898 + /* #ifndef APP-NVUE */
  899 + display: flex;
  900 + /* #endif */
  901 + flex-direction: row;
  902 + flex: 1;
  903 + }
  904 +
  905 + .popup-x-body {
  906 + display: flex;
  907 + }
  908 +
  909 + .popup-x-footer {
  910 + padding: 0 15px;
  911 + border-top-color: #F1F1F1;
  912 + border-top-style: solid;
  913 + border-top-width: 1px;
  914 + /* background-color: #fff; */
  915 + line-height: 40px;
  916 + text-align: right;
  917 + color: #666;
  918 + }
  919 +
  920 + .popup-x-footer text:hover {
  921 + color: #007aff;
  922 + cursor: pointer;
  923 + opacity: 0.8;
  924 + }
  925 +
  926 + .popup-x-footer .confirm {
  927 + margin-left: 20px;
  928 + color: #007aff;
  929 + }
  930 +
  931 + .uni-date-changed {
  932 + /* background-color: #fff; */
  933 + text-align: center;
  934 + color: #333;
  935 + border-bottom-color: #F1F1F1;
  936 + border-bottom-style: solid;
  937 + border-bottom-width: 1px;
  938 + /* padding: 0 50px; */
  939 + }
  940 +
  941 + .uni-date-changed--time text {
  942 + /* padding: 0 20px; */
  943 + height: 50px;
  944 + line-height: 50px;
  945 + }
  946 +
  947 + .uni-date-changed .uni-date-changed--time {
  948 + /* display: flex; */
  949 + flex: 1;
  950 + }
  951 +
  952 + .uni-date-changed--time-date {
  953 + color: #333;
  954 + opacity: 0.6;
  955 + }
  956 +
  957 + .mr-50 {
  958 + margin-right: 50px;
  959 + }
  960 +
  961 + /* picker 弹出层通用的指示小三角, todo:扩展至上下左右方向定位 */
  962 + .uni-popper__arrow,
  963 + .uni-popper__arrow::after {
  964 + position: absolute;
  965 + display: block;
  966 + width: 0;
  967 + height: 0;
  968 + border-color: transparent;
  969 + border-style: solid;
  970 + border-width: 6px;
  971 + }
  972 +
  973 + .uni-popper__arrow {
  974 + filter: drop-shadow(0 2px 12px rgba(0, 0, 0, 0.03));
  975 + top: -6px;
  976 + left: 10%;
  977 + margin-right: 3px;
  978 + border-top-width: 0;
  979 + border-bottom-color: #EBEEF5;
  980 + }
  981 +
  982 + .uni-popper__arrow::after {
  983 + content: " ";
  984 + top: 1px;
  985 + margin-left: -6px;
  986 + border-top-width: 0;
  987 + border-bottom-color: #fff;
  988 + }
  989 +</style>
... ...
  1 +class Calendar {
  2 + constructor({
  3 + date,
  4 + selected,
  5 + startDate,
  6 + endDate,
  7 + range,
  8 + // multipleStatus
  9 + } = {}) {
  10 + // 当前日期
  11 + this.date = this.getDate(new Date()) // 当前初入日期
  12 + // 打点信息
  13 + this.selected = selected || [];
  14 + // 范围开始
  15 + this.startDate = startDate
  16 + // 范围结束
  17 + this.endDate = endDate
  18 + this.range = range
  19 + // 多选状态
  20 + this.cleanMultipleStatus()
  21 + // 每周日期
  22 + this.weeks = {}
  23 + // this._getWeek(this.date.fullDate)
  24 + // this.multipleStatus = multipleStatus
  25 + this.lastHover = false
  26 + }
  27 + /**
  28 + * 设置日期
  29 + * @param {Object} date
  30 + */
  31 + setDate(date) {
  32 + this.selectDate = this.getDate(date)
  33 + this._getWeek(this.selectDate.fullDate)
  34 + }
  35 +
  36 + /**
  37 + * 清理多选状态
  38 + */
  39 + cleanMultipleStatus() {
  40 + this.multipleStatus = {
  41 + before: '',
  42 + after: '',
  43 + data: []
  44 + }
  45 + }
  46 +
  47 + /**
  48 + * 重置开始日期
  49 + */
  50 + resetSatrtDate(startDate) {
  51 + // 范围开始
  52 + this.startDate = startDate
  53 +
  54 + }
  55 +
  56 + /**
  57 + * 重置结束日期
  58 + */
  59 + resetEndDate(endDate) {
  60 + // 范围结束
  61 + this.endDate = endDate
  62 + }
  63 +
  64 + /**
  65 + * 获取任意时间
  66 + */
  67 + getDate(date, AddDayCount = 0, str = 'day') {
  68 + if (!date) {
  69 + date = new Date()
  70 + }
  71 + if (typeof date !== 'object') {
  72 + date = date.replace(/-/g, '/')
  73 + }
  74 + const dd = new Date(date)
  75 + switch (str) {
  76 + case 'day':
  77 + dd.setDate(dd.getDate() + AddDayCount) // 获取AddDayCount天后的日期
  78 + break
  79 + case 'month':
  80 + if (dd.getDate() === 31) {
  81 + dd.setDate(dd.getDate() + AddDayCount)
  82 + } else {
  83 + dd.setMonth(dd.getMonth() + AddDayCount) // 获取AddDayCount天后的日期
  84 + }
  85 + break
  86 + case 'year':
  87 + dd.setFullYear(dd.getFullYear() + AddDayCount) // 获取AddDayCount天后的日期
  88 + break
  89 + }
  90 + const y = dd.getFullYear()
  91 + const m = dd.getMonth() + 1 < 10 ? '0' + (dd.getMonth() + 1) : dd.getMonth() + 1 // 获取当前月份的日期,不足10补0
  92 + const d = dd.getDate() < 10 ? '0' + dd.getDate() : dd.getDate() // 获取当前几号,不足10补0
  93 + return {
  94 + fullDate: y + '-' + m + '-' + d,
  95 + year: y,
  96 + month: m,
  97 + date: d,
  98 + day: dd.getDay()
  99 + }
  100 + }
  101 +
  102 +
  103 + /**
  104 + * 获取上月剩余天数
  105 + */
  106 + _getLastMonthDays(firstDay, full) {
  107 + let dateArr = []
  108 + for (let i = firstDay; i > 0; i--) {
  109 + const beforeDate = new Date(full.year, full.month - 1, -i + 1).getDate()
  110 + dateArr.push({
  111 + date: beforeDate,
  112 + month: full.month - 1,
  113 + disable: true
  114 + })
  115 + }
  116 + return dateArr
  117 + }
  118 + /**
  119 + * 获取本月天数
  120 + */
  121 + _currentMonthDys(dateData, full) {
  122 + let dateArr = []
  123 + let fullDate = this.date.fullDate
  124 + for (let i = 1; i <= dateData; i++) {
  125 + let isinfo = false
  126 + let nowDate = full.year + '-' + (full.month < 10 ?
  127 + full.month : full.month) + '-' + (i < 10 ?
  128 + '0' + i : i)
  129 + // 是否今天
  130 + let isDay = fullDate === nowDate
  131 + // 获取打点信息
  132 + let info = this.selected && this.selected.find((item) => {
  133 + if (this.dateEqual(nowDate, item.date)) {
  134 + return item
  135 + }
  136 + })
  137 +
  138 + // 日期禁用
  139 + let disableBefore = true
  140 + let disableAfter = true
  141 + if (this.startDate) {
  142 + // let dateCompBefore = this.dateCompare(this.startDate, fullDate)
  143 + // disableBefore = this.dateCompare(dateCompBefore ? this.startDate : fullDate, nowDate)
  144 + disableBefore = this.dateCompare(this.startDate, nowDate)
  145 + }
  146 +
  147 + if (this.endDate) {
  148 + // let dateCompAfter = this.dateCompare(fullDate, this.endDate)
  149 + // disableAfter = this.dateCompare(nowDate, dateCompAfter ? this.endDate : fullDate)
  150 + disableAfter = this.dateCompare(nowDate, this.endDate)
  151 + }
  152 + let multiples = this.multipleStatus.data
  153 + let checked = false
  154 + let multiplesStatus = -1
  155 + if (this.range) {
  156 + if (multiples) {
  157 + multiplesStatus = multiples.findIndex((item) => {
  158 + return this.dateEqual(item, nowDate)
  159 + })
  160 + }
  161 + if (multiplesStatus !== -1) {
  162 + checked = true
  163 + }
  164 + }
  165 + let data = {
  166 + fullDate: nowDate,
  167 + year: full.year,
  168 + date: i,
  169 + multiple: this.range ? checked : false,
  170 + beforeMultiple: this.isLogicBefore(nowDate, this.multipleStatus.before, this.multipleStatus.after),
  171 + afterMultiple: this.isLogicAfter(nowDate, this.multipleStatus.before, this.multipleStatus.after),
  172 + month: full.month,
  173 + disable: !(disableBefore && disableAfter),
  174 + isDay,
  175 + userChecked: false
  176 + }
  177 + if (info) {
  178 + data.extraInfo = info
  179 + }
  180 +
  181 + dateArr.push(data)
  182 + }
  183 + return dateArr
  184 + }
  185 + /**
  186 + * 获取下月天数
  187 + */
  188 + _getNextMonthDays(surplus, full) {
  189 + let dateArr = []
  190 + for (let i = 1; i < surplus + 1; i++) {
  191 + dateArr.push({
  192 + date: i,
  193 + month: Number(full.month) + 1,
  194 + disable: true
  195 + })
  196 + }
  197 + return dateArr
  198 + }
  199 +
  200 + /**
  201 + * 获取当前日期详情
  202 + * @param {Object} date
  203 + */
  204 + getInfo(date) {
  205 + if (!date) {
  206 + date = new Date()
  207 + }
  208 + const dateInfo = this.canlender.find(item => item.fullDate === this.getDate(date).fullDate)
  209 + return dateInfo
  210 + }
  211 +
  212 + /**
  213 + * 比较时间大小
  214 + */
  215 + dateCompare(startDate, endDate) {
  216 + // 计算截止时间
  217 + startDate = new Date(startDate.replace('-', '/').replace('-', '/'))
  218 + // 计算详细项的截止时间
  219 + endDate = new Date(endDate.replace('-', '/').replace('-', '/'))
  220 + if (startDate <= endDate) {
  221 + return true
  222 + } else {
  223 + return false
  224 + }
  225 + }
  226 +
  227 + /**
  228 + * 比较时间是否相等
  229 + */
  230 + dateEqual(before, after) {
  231 + // 计算截止时间
  232 + before = new Date(before.replace('-', '/').replace('-', '/'))
  233 + // 计算详细项的截止时间
  234 + after = new Date(after.replace('-', '/').replace('-', '/'))
  235 + if (before.getTime() - after.getTime() === 0) {
  236 + return true
  237 + } else {
  238 + return false
  239 + }
  240 + }
  241 +
  242 + /**
  243 + * 比较真实起始日期
  244 + */
  245 +
  246 + isLogicBefore(currentDay, before, after) {
  247 + let logicBefore = before
  248 + if (before && after) {
  249 + logicBefore = this.dateCompare(before, after) ? before : after
  250 + }
  251 + return this.dateEqual(logicBefore, currentDay)
  252 + }
  253 +
  254 + isLogicAfter(currentDay, before, after) {
  255 + let logicAfter = after
  256 + if (before && after) {
  257 + logicAfter = this.dateCompare(before, after) ? after : before
  258 + }
  259 + return this.dateEqual(logicAfter, currentDay)
  260 + }
  261 +
  262 + /**
  263 + * 获取日期范围内所有日期
  264 + * @param {Object} begin
  265 + * @param {Object} end
  266 + */
  267 + geDateAll(begin, end) {
  268 + var arr = []
  269 + var ab = begin.split('-')
  270 + var ae = end.split('-')
  271 + var db = new Date()
  272 + db.setFullYear(ab[0], ab[1] - 1, ab[2])
  273 + var de = new Date()
  274 + de.setFullYear(ae[0], ae[1] - 1, ae[2])
  275 + var unixDb = db.getTime() - 24 * 60 * 60 * 1000
  276 + var unixDe = de.getTime() - 24 * 60 * 60 * 1000
  277 + for (var k = unixDb; k <= unixDe;) {
  278 + k = k + 24 * 60 * 60 * 1000
  279 + arr.push(this.getDate(new Date(parseInt(k))).fullDate)
  280 + }
  281 + return arr
  282 + }
  283 +
  284 + /**
  285 + * 获取多选状态
  286 + */
  287 + setMultiple(fullDate) {
  288 + let {
  289 + before,
  290 + after
  291 + } = this.multipleStatus
  292 + if (!this.range) return
  293 + if (before && after) {
  294 + if (!this.lastHover) {
  295 + this.lastHover = true
  296 + return
  297 + }
  298 + this.multipleStatus.before = fullDate
  299 + this.multipleStatus.after = ''
  300 + this.multipleStatus.data = []
  301 + this.multipleStatus.fulldate = ''
  302 + this.lastHover = false
  303 + } else {
  304 + if (!before) {
  305 + this.multipleStatus.before = fullDate
  306 + this.lastHover = false
  307 + } else {
  308 + this.multipleStatus.after = fullDate
  309 + if (this.dateCompare(this.multipleStatus.before, this.multipleStatus.after)) {
  310 + this.multipleStatus.data = this.geDateAll(this.multipleStatus.before, this.multipleStatus
  311 + .after);
  312 + } else {
  313 + this.multipleStatus.data = this.geDateAll(this.multipleStatus.after, this.multipleStatus
  314 + .before);
  315 + }
  316 + this.lastHover = true
  317 + }
  318 + }
  319 + this._getWeek(fullDate)
  320 + }
  321 +
  322 + /**
  323 + * 鼠标 hover 更新多选状态
  324 + */
  325 + setHoverMultiple(fullDate) {
  326 + let {
  327 + before,
  328 + after
  329 + } = this.multipleStatus
  330 +
  331 + if (!this.range) return
  332 + if (this.lastHover) return
  333 +
  334 + if (!before) {
  335 + this.multipleStatus.before = fullDate
  336 + } else {
  337 + this.multipleStatus.after = fullDate
  338 + if (this.dateCompare(this.multipleStatus.before, this.multipleStatus.after)) {
  339 + this.multipleStatus.data = this.geDateAll(this.multipleStatus.before, this.multipleStatus.after);
  340 + } else {
  341 + this.multipleStatus.data = this.geDateAll(this.multipleStatus.after, this.multipleStatus.before);
  342 + }
  343 + }
  344 + this._getWeek(fullDate)
  345 + }
  346 +
  347 + /**
  348 + * 更新默认值多选状态
  349 + */
  350 + setDefaultMultiple(before, after) {
  351 + this.multipleStatus.before = before
  352 + this.multipleStatus.after = after
  353 + if (before && after) {
  354 + if (this.dateCompare(before, after)) {
  355 + this.multipleStatus.data = this.geDateAll(before, after);
  356 + this._getWeek(after)
  357 + } else {
  358 + this.multipleStatus.data = this.geDateAll(after, before);
  359 + this._getWeek(before)
  360 + }
  361 + }
  362 + }
  363 +
  364 + /**
  365 + * 获取每周数据
  366 + * @param {Object} dateData
  367 + */
  368 + _getWeek(dateData) {
  369 + const {
  370 + fullDate,
  371 + year,
  372 + month,
  373 + date,
  374 + day
  375 + } = this.getDate(dateData)
  376 + let firstDay = new Date(year, month - 1, 1).getDay()
  377 + let currentDay = new Date(year, month, 0).getDate()
  378 + let dates = {
  379 + lastMonthDays: this._getLastMonthDays(firstDay, this.getDate(dateData)), // 上个月末尾几天
  380 + currentMonthDys: this._currentMonthDys(currentDay, this.getDate(dateData)), // 本月天数
  381 + nextMonthDays: [], // 下个月开始几天
  382 + weeks: []
  383 + }
  384 + let canlender = []
  385 + const surplus = 42 - (dates.lastMonthDays.length + dates.currentMonthDys.length)
  386 + dates.nextMonthDays = this._getNextMonthDays(surplus, this.getDate(dateData))
  387 + canlender = canlender.concat(dates.lastMonthDays, dates.currentMonthDys, dates.nextMonthDays)
  388 + let weeks = {}
  389 + // 拼接数组 上个月开始几天 + 本月天数+ 下个月开始几天
  390 + for (let i = 0; i < canlender.length; i++) {
  391 + if (i % 7 === 0) {
  392 + weeks[parseInt(i / 7)] = new Array(7)
  393 + }
  394 + weeks[parseInt(i / 7)][i % 7] = canlender[i]
  395 + }
  396 + this.canlender = canlender
  397 + this.weeks = weeks
  398 + }
  399 +
  400 + //静态方法
  401 + // static init(date) {
  402 + // if (!this.instance) {
  403 + // this.instance = new Calendar(date);
  404 + // }
  405 + // return this.instance;
  406 + // }
  407 +}
  408 +
  409 +
  410 +export default Calendar
... ...
  1 +{
  2 + "id": "uni-datetime-picker",
  3 + "displayName": "uni-datetime-picker 日期选择器",
  4 + "version": "2.2.3",
  5 + "description": "uni-datetime-picker 日期时间选择器,支持日历,支持范围选择",
  6 + "keywords": [
  7 + "uni-datetime-picker",
  8 + "uni-ui",
  9 + "uniui",
  10 + "日期时间选择器",
  11 + "日期时间"
  12 +],
  13 + "repository": "https://github.com/dcloudio/uni-ui",
  14 + "engines": {
  15 + "HBuilderX": ""
  16 + },
  17 + "directories": {
  18 + "example": "../../temps/example_temps"
  19 + },
  20 + "dcloudext": {
  21 + "category": [
  22 + "前端组件",
  23 + "通用组件"
  24 + ],
  25 + "sale": {
  26 + "regular": {
  27 + "price": "0.00"
  28 + },
  29 + "sourcecode": {
  30 + "price": "0.00"
  31 + }
  32 + },
  33 + "contact": {
  34 + "qq": ""
  35 + },
  36 + "declaration": {
  37 + "ads": "无",
  38 + "data": "无",
  39 + "permissions": "无"
  40 + },
  41 + "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui"
  42 + },
  43 + "uni_modules": {
  44 + "dependencies": [
  45 + "uni-scss",
  46 + "uni-icons"
  47 + ],
  48 + "encrypt": [],
  49 + "platforms": {
  50 + "cloud": {
  51 + "tcb": "y",
  52 + "aliyun": "y"
  53 + },
  54 + "client": {
  55 + "App": {
  56 + "app-vue": "y",
  57 + "app-nvue": "n"
  58 + },
  59 + "H5-mobile": {
  60 + "Safari": "y",
  61 + "Android Browser": "y",
  62 + "微信浏览器(Android)": "y",
  63 + "QQ浏览器(Android)": "y"
  64 + },
  65 + "H5-pc": {
  66 + "Chrome": "y",
  67 + "IE": "y",
  68 + "Edge": "y",
  69 + "Firefox": "y",
  70 + "Safari": "y"
  71 + },
  72 + "小程序": {
  73 + "微信": "y",
  74 + "阿里": "y",
  75 + "百度": "y",
  76 + "字节跳动": "y",
  77 + "QQ": "y"
  78 + },
  79 + "快应用": {
  80 + "华为": "u",
  81 + "联盟": "u"
  82 + },
  83 + "Vue": {
  84 + "vue2": "y",
  85 + "vue3": "y"
  86 + }
  87 + }
  88 + }
  89 + }
  90 +}
... ...
  1 +
  2 +
  3 +> `重要通知:组件升级更新 2.0.0 后,支持日期+时间范围选择,组件 ui 将使用日历选择日期,ui 变化较大,同时支持 PC 和 移动端。此版本不向后兼容,不再支持单独的时间选择(type=time)及相关的 hide-second 属性(时间选可使用内置组件 picker)。若仍需使用旧版本,可在插件市场下载*非uni_modules版本*,旧版本将不再维护`
  4 +
  5 +## DatetimePicker 时间选择器
  6 +
  7 +> **组件名:uni-datetime-picker**
  8 +> 代码块: `uDatetimePicker`
  9 +
  10 +
  11 +该组件的优势是,支持**时间戳**输入和输出(起始时间、终止时间也支持时间戳),可**同时选择**日期和时间。
  12 +
  13 +若只是需要单独选择日期和时间,不需要时间戳输入和输出,可使用原生的 picker 组件。
  14 +
  15 +**_点击 picker 默认值规则:_**
  16 +
  17 +- 若设置初始值 value, 会显示在 picker 显示框中
  18 +- 若无初始值 value,则初始值 value 为当前本地时间 Date.now(), 但不会显示在 picker 显示框中
  19 +
  20 +### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-datetime-picker)
  21 +#### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839
\ No newline at end of file
... ...