uni-datetime-picker.vue 26 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021
  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="#c0c4cc" size="22"></uni-icons>
  9. <input class="uni-date__x-input" type="text" v-model="singleVal" placeholder-style="color:#aaa;"
  10. :placeholder="singlePlaceholderText" :disabled="true" />
  11. </view>
  12. <view v-else class="uni-date-x uni-date-range">
  13. <uni-icons type="calendar" color="#c0c4cc" size="22"></uni-icons>
  14. <input class="uni-date__x-input t-c" type="text" v-model="range.startDate" placeholder-style="color:#aaa;"
  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" placeholder-style="color:#aaa;"
  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="#c0c4cc" size="24"></uni-icons>
  24. </view>
  25. </view>
  26. </slot>
  27. </view>
  28. <view v-show="popup" class="uni-date-mask" @click="close"></view>
  29. <view v-if="!isPhone" ref="datePicker" v-show="popup" class="uni-date-picker__container">
  30. <view v-if="!isRange" class="uni-date-single--x" :style="popover">
  31. <view class="uni-popper__arrow"></view>
  32. <view v-if="hasTime" class="uni-date-changed popup-x-header">
  33. <input class="uni-date__input t-c" type="text" v-model="tempSingleDate" placeholder-style="color:#aaa;"
  34. :placeholder="selectDateText" />
  35. <time-picker type="time" v-model="time" :border="false" :disabled="!tempSingleDate"
  36. :start="reactStartTime" :end="reactEndTime" :hideSecond="hideSecond" style="width: 100%;">
  37. <input class="uni-date__input t-c" type="text" v-model="time" :placeholder="selectTimeText" placeholder-style="color:#aaa;"
  38. :disabled="!tempSingleDate" />
  39. </time-picker>
  40. </view>
  41. <calendar ref="pcSingle" :showMonth="false" :start-date="caleRange.startDate"
  42. :end-date="caleRange.endDate" :date="defSingleDate" @change="singleChange"
  43. style="padding: 0 8px;" />
  44. <view v-if="hasTime" class="popup-x-footer">
  45. <!-- <text class="">此刻</text> -->
  46. <text class="confirm" @click="confirmSingleChange">{{okText}}</text>
  47. </view>
  48. <view class="uni-date-popper__arrow"></view>
  49. </view>
  50. <view v-else class="uni-date-range--x" :style="popover">
  51. <view class="uni-popper__arrow"></view>
  52. <view v-if="hasTime" class="popup-x-header uni-date-changed">
  53. <view class="popup-x-header--datetime">
  54. <input class="uni-date__input uni-date-range__input" type="text" v-model="tempRange.startDate" placeholder-style="color:#aaa;"
  55. :placeholder="startDateText" />
  56. <time-picker type="time" v-model="tempRange.startTime" :start="reactStartTime" :border="false"
  57. :disabled="!tempRange.startDate" :hideSecond="hideSecond">
  58. <input class="uni-date__input uni-date-range__input" type="text" placeholder-style="color:#aaa;"
  59. v-model="tempRange.startTime" :placeholder="startTimeText"
  60. :disabled="!tempRange.startDate" />
  61. </time-picker>
  62. </view>
  63. <uni-icons type="arrowthinright" color="#999" style="line-height: 40px;"></uni-icons>
  64. <view class="popup-x-header--datetime">
  65. <input class="uni-date__input uni-date-range__input" type="text" v-model="tempRange.endDate" placeholder-style="color:#aaa;"
  66. :placeholder="endDateText" />
  67. <time-picker type="time" v-model="tempRange.endTime" :end="reactEndTime" :border="false"
  68. :disabled="!tempRange.endDate" :hideSecond="hideSecond">
  69. <input class="uni-date__input uni-date-range__input" type="text" v-model="tempRange.endTime" placeholder-style="color:#aaa;"
  70. :placeholder="endTimeText" :disabled="!tempRange.endDate" />
  71. </time-picker>
  72. </view>
  73. </view>
  74. <view class="popup-x-body">
  75. <calendar ref="left" :showMonth="false" :start-date="caleRange.startDate"
  76. :end-date="caleRange.endDate" :range="true" @change="leftChange" :pleStatus="endMultipleStatus"
  77. @firstEnterCale="updateRightCale" @monthSwitch="leftMonthSwitch" style="padding: 0 8px;" />
  78. <calendar ref="right" :showMonth="false" :start-date="caleRange.startDate"
  79. :end-date="caleRange.endDate" :range="true" @change="rightChange"
  80. :pleStatus="startMultipleStatus" @firstEnterCale="updateLeftCale"
  81. @monthSwitch="rightMonthSwitch" style="padding: 0 8px;border-left: 1px solid #F1F1F1;" />
  82. </view>
  83. <view v-if="hasTime" class="popup-x-footer">
  84. <text class="" @click="clear">{{clearText}}</text>
  85. <text class="confirm" @click="confirmRangeChange">{{okText}}</text>
  86. </view>
  87. </view>
  88. </view>
  89. <calendar v-show="isPhone" ref="mobile" :clearDate="false" :date="defSingleDate" :defTime="reactMobDefTime"
  90. :start-date="caleRange.startDate" :end-date="caleRange.endDate" :selectableTimes="mobSelectableTime"
  91. :pleStatus="endMultipleStatus" :showMonth="false" :range="isRange" :typeHasTime="hasTime" :insert="false"
  92. :hideSecond="hideSecond" @confirm="mobileChange" @maskClose="close" />
  93. </view>
  94. </template>
  95. <script>
  96. /**
  97. * DatetimePicker 时间选择器
  98. * @description 同时支持 PC 和移动端使用日历选择日期和日期范围
  99. * @tutorial https://ext.dcloud.net.cn/plugin?id=3962
  100. * @property {String} type 选择器类型
  101. * @property {String|Number|Array|Date} value 绑定值
  102. * @property {String} placeholder 单选择时的占位内容
  103. * @property {String} start 起始时间
  104. * @property {String} end 终止时间
  105. * @property {String} start-placeholder 范围选择时开始日期的占位内容
  106. * @property {String} end-placeholder 范围选择时结束日期的占位内容
  107. * @property {String} range-separator 选择范围时的分隔符
  108. * @property {Boolean} border = [true|false] 是否有边框
  109. * @property {Boolean} disabled = [true|false] 是否禁用
  110. * @property {Boolean} clearIcon = [true|false] 是否显示清除按钮(仅PC端适用)
  111. * @event {Function} change 确定日期时触发的事件
  112. * @event {Function} show 打开弹出层
  113. * @event {Function} close 关闭弹出层
  114. * @event {Function} clear 清除上次选中的状态和值
  115. **/
  116. import calendar from './calendar.vue'
  117. import timePicker from './time-picker.vue'
  118. import {
  119. initVueI18n
  120. } from '@dcloudio/uni-i18n'
  121. import messages from './i18n/index.js'
  122. const {
  123. t
  124. } = initVueI18n(messages)
  125. export default {
  126. name: 'UniDatetimePicker',
  127. options: {
  128. virtualHost: true
  129. },
  130. components: {
  131. calendar,
  132. timePicker
  133. },
  134. inject: {
  135. form: {
  136. from: 'uniForm',
  137. default: null
  138. },
  139. formItem: {
  140. from: 'uniFormItem',
  141. default: null
  142. },
  143. },
  144. data() {
  145. return {
  146. isRange: false,
  147. hasTime: false,
  148. mobileRange: false,
  149. // 单选
  150. singleVal: '',
  151. tempSingleDate: '',
  152. defSingleDate: '',
  153. time: '',
  154. // 范围选
  155. caleRange: {
  156. startDate: '',
  157. startTime: '',
  158. endDate: '',
  159. endTime: ''
  160. },
  161. range: {
  162. startDate: '',
  163. // startTime: '',
  164. endDate: '',
  165. // endTime: ''
  166. },
  167. tempRange: {
  168. startDate: '',
  169. startTime: '',
  170. endDate: '',
  171. endTime: ''
  172. },
  173. // 左右日历同步数据
  174. startMultipleStatus: {
  175. before: '',
  176. after: '',
  177. data: [],
  178. fulldate: ''
  179. },
  180. endMultipleStatus: {
  181. before: '',
  182. after: '',
  183. data: [],
  184. fulldate: ''
  185. },
  186. visible: false,
  187. popup: false,
  188. popover: null,
  189. isEmitValue: false,
  190. isPhone: false,
  191. isFirstShow: true,
  192. }
  193. },
  194. props: {
  195. type: {
  196. type: String,
  197. default: 'datetime'
  198. },
  199. value: {
  200. type: [String, Number, Array, Date],
  201. default: ''
  202. },
  203. modelValue: {
  204. type: [String, Number, Array, Date],
  205. default: ''
  206. },
  207. start: {
  208. type: [Number, String],
  209. default: ''
  210. },
  211. end: {
  212. type: [Number, String],
  213. default: ''
  214. },
  215. returnType: {
  216. type: String,
  217. default: 'string'
  218. },
  219. placeholder: {
  220. type: String,
  221. default: ''
  222. },
  223. startPlaceholder: {
  224. type: String,
  225. default: ''
  226. },
  227. endPlaceholder: {
  228. type: String,
  229. default: ''
  230. },
  231. rangeSeparator: {
  232. type: String,
  233. default: '-'
  234. },
  235. border: {
  236. type: [Boolean],
  237. default: true
  238. },
  239. disabled: {
  240. type: [Boolean],
  241. default: false
  242. },
  243. clearIcon: {
  244. type: [Boolean],
  245. default: true
  246. },
  247. hideSecond: {
  248. type: [Boolean],
  249. default: false
  250. }
  251. },
  252. watch: {
  253. type: {
  254. immediate: true,
  255. handler(newVal, oldVal) {
  256. if (newVal.indexOf('time') !== -1) {
  257. this.hasTime = true
  258. } else {
  259. this.hasTime = false
  260. }
  261. if (newVal.indexOf('range') !== -1) {
  262. this.isRange = true
  263. } else {
  264. this.isRange = false
  265. }
  266. }
  267. },
  268. // #ifndef VUE3
  269. value: {
  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. // #endif
  280. // #ifdef VUE3
  281. modelValue: {
  282. immediate: true,
  283. handler(newVal, oldVal) {
  284. if (this.isEmitValue) {
  285. this.isEmitValue = false
  286. return
  287. }
  288. this.initPicker(newVal)
  289. }
  290. },
  291. // #endif
  292. start: {
  293. immediate: true,
  294. handler(newVal, oldVal) {
  295. if (!newVal) return
  296. const {
  297. defDate,
  298. defTime
  299. } = this.parseDate(newVal)
  300. this.caleRange.startDate = defDate
  301. if (this.hasTime) {
  302. this.caleRange.startTime = defTime
  303. }
  304. }
  305. },
  306. end: {
  307. immediate: true,
  308. handler(newVal, oldVal) {
  309. if (!newVal) return
  310. const {
  311. defDate,
  312. defTime
  313. } = this.parseDate(newVal)
  314. this.caleRange.endDate = defDate
  315. if (this.hasTime) {
  316. this.caleRange.endTime = defTime
  317. }
  318. }
  319. },
  320. },
  321. computed: {
  322. reactStartTime() {
  323. const activeDate = this.isRange ? this.tempRange.startDate : this.tempSingleDate
  324. const res = activeDate === this.caleRange.startDate ? this.caleRange.startTime : ''
  325. return res
  326. },
  327. reactEndTime() {
  328. const activeDate = this.isRange ? this.tempRange.endDate : this.tempSingleDate
  329. const res = activeDate === this.caleRange.endDate ? this.caleRange.endTime : ''
  330. return res
  331. },
  332. reactMobDefTime() {
  333. const times = {
  334. start: this.tempRange.startTime,
  335. end: this.tempRange.endTime
  336. }
  337. return this.isRange ? times : this.time
  338. },
  339. mobSelectableTime() {
  340. return {
  341. start: this.caleRange.startTime,
  342. end: this.caleRange.endTime
  343. }
  344. },
  345. datePopupWidth() {
  346. // todo
  347. return this.isRange ? 653 : 301
  348. },
  349. /**
  350. * for i18n
  351. */
  352. singlePlaceholderText() {
  353. return this.placeholder || (this.type === 'date' ? this.selectDateText : t(
  354. "uni-datetime-picker.selectDateTime"))
  355. },
  356. startPlaceholderText() {
  357. return this.startPlaceholder || this.startDateText
  358. },
  359. endPlaceholderText() {
  360. return this.endPlaceholder || this.endDateText
  361. },
  362. selectDateText() {
  363. return t("uni-datetime-picker.selectDate")
  364. },
  365. selectTimeText() {
  366. return t("uni-datetime-picker.selectTime")
  367. },
  368. startDateText() {
  369. return this.startPlaceholder || t("uni-datetime-picker.startDate")
  370. },
  371. startTimeText() {
  372. return t("uni-datetime-picker.startTime")
  373. },
  374. endDateText() {
  375. return this.endPlaceholder || t("uni-datetime-picker.endDate")
  376. },
  377. endTimeText() {
  378. return t("uni-datetime-picker.endTime")
  379. },
  380. okText() {
  381. return t("uni-datetime-picker.ok")
  382. },
  383. clearText() {
  384. return t("uni-datetime-picker.clear")
  385. },
  386. showClearIcon() {
  387. const {
  388. clearIcon,
  389. disabled,
  390. singleVal,
  391. range
  392. } = this
  393. const bool = clearIcon && !disabled && (singleVal || (range.startDate && range.endDate))
  394. return bool
  395. }
  396. },
  397. created() {
  398. // if (this.form && this.formItem) {
  399. // this.$watch('formItem.errMsg', (newVal) => {
  400. // this.localMsg = newVal
  401. // })
  402. // }
  403. },
  404. mounted() {
  405. this.platform()
  406. },
  407. methods: {
  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. if (this.hasTime) {
  437. this.range.startDate = defBefore.defDate + ' ' + defBefore.defTime
  438. this.range.endDate = defAfter.defDate + ' ' + defAfter.defTime
  439. this.tempRange.startTime = defBefore.defTime
  440. this.tempRange.endTime = defAfter.defTime
  441. }
  442. const defaultRange = {
  443. before: defBefore.defDate,
  444. after: defAfter.defDate
  445. }
  446. this.startMultipleStatus = Object.assign({}, this.startMultipleStatus, defaultRange, {
  447. which: 'right'
  448. })
  449. this.endMultipleStatus = Object.assign({}, this.endMultipleStatus, defaultRange, {
  450. which: 'left'
  451. })
  452. }
  453. },
  454. updateLeftCale(e) {
  455. const left = this.$refs.left
  456. // 设置范围选
  457. left.cale.setHoverMultiple(e.after)
  458. left.setDate(this.$refs.left.nowDate.fullDate)
  459. },
  460. updateRightCale(e) {
  461. const right = this.$refs.right
  462. // 设置范围选
  463. right.cale.setHoverMultiple(e.after)
  464. right.setDate(this.$refs.right.nowDate.fullDate)
  465. },
  466. platform() {
  467. const systemInfo = uni.getSystemInfoSync()
  468. this.isPhone = systemInfo.windowWidth <= 500
  469. this.windowWidth = systemInfo.windowWidth
  470. },
  471. show(event) {
  472. if (this.disabled) {
  473. return
  474. }
  475. this.platform()
  476. if (this.isPhone) {
  477. this.$refs.mobile.open()
  478. return
  479. }
  480. this.popover = {
  481. top: '10px'
  482. }
  483. const dateEditor = uni.createSelectorQuery().in(this).select(".uni-date-editor")
  484. dateEditor.boundingClientRect(rect => {
  485. if (this.windowWidth - rect.left < this.datePopupWidth) {
  486. this.popover.right = 0
  487. }
  488. }).exec()
  489. setTimeout(() => {
  490. this.popup = !this.popup
  491. if (!this.isPhone && this.isRange && this.isFirstShow) {
  492. this.isFirstShow = false
  493. const {
  494. startDate,
  495. endDate
  496. } = this.range
  497. if (startDate && endDate) {
  498. if (this.diffDate(startDate, endDate) < 30) {
  499. this.$refs.right.next()
  500. }
  501. } else {
  502. this.$refs.right.next()
  503. this.$refs.right.cale.lastHover = false
  504. }
  505. }
  506. }, 50)
  507. },
  508. close() {
  509. setTimeout(() => {
  510. this.popup = false
  511. this.$emit('maskClick', this.value)
  512. this.$refs.mobile.close()
  513. }, 20)
  514. },
  515. setEmit(value) {
  516. if (this.returnType === "timestamp" || this.returnType === "date") {
  517. if (!Array.isArray(value)) {
  518. if (!this.hasTime) {
  519. value = value + ' ' + '00:00:00'
  520. }
  521. value = this.createTimestamp(value)
  522. if (this.returnType === "date") {
  523. value = new Date(value)
  524. }
  525. } else {
  526. if (!this.hasTime) {
  527. value[0] = value[0] + ' ' + '00:00:00'
  528. value[1] = value[1] + ' ' + '00:00:00'
  529. }
  530. value[0] = this.createTimestamp(value[0])
  531. value[1] = this.createTimestamp(value[1])
  532. if (this.returnType === "date") {
  533. value[0] = new Date(value[0])
  534. value[1] = new Date(value[1])
  535. }
  536. }
  537. }
  538. this.$emit('change', value)
  539. this.$emit('input', value)
  540. this.$emit('update:modelValue', value)
  541. this.isEmitValue = true
  542. },
  543. createTimestamp(date) {
  544. date = this.fixIosDateFormat(date)
  545. return Date.parse(new Date(date))
  546. },
  547. singleChange(e) {
  548. this.tempSingleDate = e.fulldate
  549. if (this.hasTime) return
  550. this.confirmSingleChange()
  551. },
  552. confirmSingleChange() {
  553. if (!this.tempSingleDate) {
  554. this.popup = false
  555. return
  556. }
  557. if (this.hasTime) {
  558. this.singleVal = this.tempSingleDate + ' ' + (this.time ? this.time : '00:00:00')
  559. } else {
  560. this.singleVal = this.tempSingleDate
  561. }
  562. this.setEmit(this.singleVal)
  563. this.popup = false
  564. },
  565. leftChange(e) {
  566. const {
  567. before,
  568. after
  569. } = e.range
  570. this.rangeChange(before, after)
  571. const obj = {
  572. before: e.range.before,
  573. after: e.range.after,
  574. data: e.range.data,
  575. fulldate: e.fulldate
  576. }
  577. this.startMultipleStatus = Object.assign({}, this.startMultipleStatus, obj)
  578. },
  579. rightChange(e) {
  580. const {
  581. before,
  582. after
  583. } = e.range
  584. this.rangeChange(before, after)
  585. const obj = {
  586. before: e.range.before,
  587. after: e.range.after,
  588. data: e.range.data,
  589. fulldate: e.fulldate
  590. }
  591. this.endMultipleStatus = Object.assign({}, this.endMultipleStatus, obj)
  592. },
  593. mobileChange(e) {
  594. if (this.isRange) {
  595. const {
  596. before,
  597. after
  598. } = e.range
  599. this.handleStartAndEnd(before, after, true)
  600. if (this.hasTime) {
  601. const {
  602. startTime,
  603. endTime
  604. } = e.timeRange
  605. this.tempRange.startTime = startTime
  606. this.tempRange.endTime = endTime
  607. }
  608. this.confirmRangeChange()
  609. } else {
  610. if (this.hasTime) {
  611. var time=e.time
  612. if(e.fulldate){
  613. time=e.time||"00:00:00"
  614. }
  615. if(e.fulldate||time){
  616. this.singleVal = e.fulldate + ' ' + time
  617. }
  618. } else {
  619. this.singleVal = e.fulldate
  620. }
  621. this.setEmit(this.singleVal)
  622. }
  623. this.$refs.mobile.close()
  624. },
  625. rangeChange(before, after) {
  626. if (!(before && after)) return
  627. this.handleStartAndEnd(before, after, true)
  628. if (this.hasTime) return
  629. this.confirmRangeChange()
  630. },
  631. confirmRangeChange() {
  632. if (!this.tempRange.startDate && !this.tempRange.endDate) {
  633. this.popup = false
  634. return
  635. }
  636. let start, end
  637. if (!this.hasTime) {
  638. start = this.range.startDate = this.tempRange.startDate
  639. end = this.range.endDate = this.tempRange.endDate
  640. } else {
  641. start = this.range.startDate = this.tempRange.startDate + ' ' +
  642. (this.tempRange.startTime ? this.tempRange.startTime : '00:00:00')
  643. end = this.range.endDate = this.tempRange.endDate + ' ' +
  644. (this.tempRange.endTime ? this.tempRange.endTime : '00:00:00')
  645. }
  646. const displayRange = [start, end]
  647. this.setEmit(displayRange)
  648. this.popup = false
  649. },
  650. handleStartAndEnd(before, after, temp = false) {
  651. if (!(before && after)) return
  652. const type = temp ? 'tempRange' : 'range'
  653. if (this.dateCompare(before, after)) {
  654. this[type].startDate = before
  655. this[type].endDate = after
  656. } else {
  657. this[type].startDate = after
  658. this[type].endDate = before
  659. }
  660. },
  661. /**
  662. * 比较时间大小
  663. */
  664. dateCompare(startDate, endDate) {
  665. // 计算截止时间
  666. startDate = new Date(startDate.replace('-', '/').replace('-', '/'))
  667. // 计算详细项的截止时间
  668. endDate = new Date(endDate.replace('-', '/').replace('-', '/'))
  669. if (startDate <= endDate) {
  670. return true
  671. } else {
  672. return false
  673. }
  674. },
  675. /**
  676. * 比较时间差
  677. */
  678. diffDate(startDate, endDate) {
  679. // 计算截止时间
  680. startDate = new Date(startDate.replace('-', '/').replace('-', '/'))
  681. // 计算详细项的截止时间
  682. endDate = new Date(endDate.replace('-', '/').replace('-', '/'))
  683. const diff = (endDate - startDate) / (24 * 60 * 60 * 1000)
  684. return Math.abs(diff)
  685. },
  686. clear(needEmit = true) {
  687. if (!this.isRange) {
  688. this.singleVal = ''
  689. this.tempSingleDate = ''
  690. this.time = ''
  691. if (this.isPhone) {
  692. this.$refs.mobile && this.$refs.mobile.clearCalender()
  693. } else {
  694. this.$refs.pcSingle && this.$refs.pcSingle.clearCalender()
  695. }
  696. if (needEmit) {
  697. // 校验规则
  698. // if(this.form && this.formItem){
  699. // const {
  700. // validateTrigger
  701. // } = this.form
  702. // if (validateTrigger === 'blur') {
  703. // this.formItem.onFieldChange()
  704. // }
  705. // }
  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.$emit('change', [])
  726. this.$emit('input', [])
  727. this.$emit('update:modelValue', [])
  728. }
  729. }
  730. },
  731. parseDate(date) {
  732. date = this.fixIosDateFormat(date)
  733. const defVal = new Date(date)
  734. const year = defVal.getFullYear()
  735. const month = defVal.getMonth() + 1
  736. const day = defVal.getDate()
  737. const hour = defVal.getHours()
  738. const minute = defVal.getMinutes()
  739. const second = defVal.getSeconds()
  740. const defDate = year + '-' + this.lessTen(month) + '-' + this.lessTen(day)
  741. const defTime = this.lessTen(hour) + ':' + this.lessTen(minute) + (this.hideSecond ? '' : (':' + this
  742. .lessTen(second)))
  743. return {
  744. defDate,
  745. defTime
  746. }
  747. },
  748. lessTen(item) {
  749. return item < 10 ? '0' + item : item
  750. },
  751. //兼容 iOS、safari 日期格式
  752. fixIosDateFormat(value) {
  753. if (typeof value === 'string') {
  754. value = value.replace(/-/g, '/')
  755. }
  756. return value
  757. },
  758. leftMonthSwitch(e) {
  759. // console.log('leftMonthSwitch 返回:', e)
  760. },
  761. rightMonthSwitch(e) {
  762. // console.log('rightMonthSwitch 返回:', e)
  763. }
  764. }
  765. }
  766. </script>
  767. <style lang="scss">
  768. $uni-primary: #007aff !default;
  769. .uni-date {
  770. /* #ifndef APP-NVUE */
  771. width: 100%;
  772. /* #endif */
  773. flex: 1;
  774. }
  775. .uni-date-x {
  776. display: flex;
  777. flex-direction: row;
  778. align-items: center;
  779. justify-content: center;
  780. padding: 0 10px;
  781. border-radius: 4px;
  782. background-color: #fff;
  783. color: #666;
  784. font-size: 14px;
  785. flex: 1;
  786. }
  787. .uni-date-x--border {
  788. box-sizing: border-box;
  789. border-radius: 4px;
  790. border: 1px solid #e5e5e5;
  791. }
  792. .uni-date-editor--x {
  793. display: flex;
  794. align-items: center;
  795. position: relative;
  796. }
  797. .uni-date-editor--x .uni-date__icon-clear {
  798. padding: 0 5px;
  799. display: flex;
  800. align-items: center;
  801. /* #ifdef H5 */
  802. cursor: pointer;
  803. /* #endif */
  804. }
  805. .uni-date__x-input {
  806. padding: 0 8px;
  807. /* #ifndef APP-NVUE */
  808. width: auto;
  809. /* #endif */
  810. position: relative;
  811. overflow: hidden;
  812. flex: 1;
  813. line-height: 1;
  814. font-size: 14px;
  815. height: 35px;
  816. }
  817. .t-c {
  818. text-align: center;
  819. }
  820. .uni-date__input {
  821. height: 40px;
  822. width: 100%;
  823. line-height: 40px;
  824. font-size: 14px;
  825. }
  826. .uni-date-range__input {
  827. text-align: center;
  828. max-width: 142px;
  829. }
  830. .uni-date-picker__container {
  831. position: relative;
  832. /* position: fixed;
  833. left: 0;
  834. right: 0;
  835. top: 0;
  836. bottom: 0;
  837. box-sizing: border-box;
  838. z-index: 996;
  839. font-size: 14px; */
  840. }
  841. .uni-date-mask {
  842. position: fixed;
  843. bottom: 0px;
  844. top: 0px;
  845. left: 0px;
  846. right: 0px;
  847. background-color: rgba(0, 0, 0, 0);
  848. transition-duration: 0.3s;
  849. z-index: 996;
  850. }
  851. .uni-date-single--x {
  852. /* padding: 0 8px; */
  853. background-color: #fff;
  854. position: absolute;
  855. top: 0;
  856. z-index: 999;
  857. border: 1px solid #EBEEF5;
  858. box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
  859. border-radius: 4px;
  860. }
  861. .uni-date-range--x {
  862. /* padding: 0 8px; */
  863. background-color: #fff;
  864. position: absolute;
  865. top: 0;
  866. z-index: 999;
  867. border: 1px solid #EBEEF5;
  868. box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
  869. border-radius: 4px;
  870. }
  871. .uni-date-editor--x__disabled {
  872. opacity: 0.4;
  873. cursor: default;
  874. }
  875. .uni-date-editor--logo {
  876. width: 16px;
  877. height: 16px;
  878. vertical-align: middle;
  879. }
  880. /* 添加时间 */
  881. .popup-x-header {
  882. /* #ifndef APP-NVUE */
  883. display: flex;
  884. /* #endif */
  885. flex-direction: row;
  886. /* justify-content: space-between; */
  887. }
  888. .popup-x-header--datetime {
  889. /* #ifndef APP-NVUE */
  890. display: flex;
  891. /* #endif */
  892. flex-direction: row;
  893. flex: 1;
  894. }
  895. .popup-x-body {
  896. display: flex;
  897. }
  898. .popup-x-footer {
  899. padding: 0 15px;
  900. border-top-color: #F1F1F1;
  901. border-top-style: solid;
  902. border-top-width: 1px;
  903. /* background-color: #fff; */
  904. line-height: 40px;
  905. text-align: right;
  906. color: #666;
  907. }
  908. .popup-x-footer text:hover {
  909. color: $uni-primary;
  910. cursor: pointer;
  911. opacity: 0.8;
  912. }
  913. .popup-x-footer .confirm {
  914. margin-left: 20px;
  915. color: $uni-primary;
  916. }
  917. .uni-date-changed {
  918. /* background-color: #fff; */
  919. text-align: center;
  920. color: #333;
  921. border-bottom-color: #F1F1F1;
  922. border-bottom-style: solid;
  923. border-bottom-width: 1px;
  924. /* padding: 0 50px; */
  925. }
  926. .uni-date-changed--time text {
  927. /* padding: 0 20px; */
  928. height: 50px;
  929. line-height: 50px;
  930. }
  931. .uni-date-changed .uni-date-changed--time {
  932. /* display: flex; */
  933. flex: 1;
  934. }
  935. .uni-date-changed--time-date {
  936. color: #333;
  937. opacity: 0.6;
  938. }
  939. .mr-50 {
  940. margin-right: 50px;
  941. }
  942. /* picker 弹出层通用的指示小三角, todo:扩展至上下左右方向定位 */
  943. .uni-popper__arrow,
  944. .uni-popper__arrow::after {
  945. position: absolute;
  946. display: block;
  947. width: 0;
  948. height: 0;
  949. border-color: transparent;
  950. border-style: solid;
  951. border-width: 6px;
  952. }
  953. .uni-popper__arrow {
  954. filter: drop-shadow(0 2px 12px rgba(0, 0, 0, 0.03));
  955. top: -6px;
  956. left: 10%;
  957. margin-right: 3px;
  958. border-top-width: 0;
  959. border-bottom-color: #EBEEF5;
  960. }
  961. .uni-popper__arrow::after {
  962. content: " ";
  963. top: 1px;
  964. margin-left: -6px;
  965. border-top-width: 0;
  966. border-bottom-color: #fff;
  967. }
  968. </style>