uni-datetime-picker.vue 25 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012
  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"
  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"
  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="#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"
  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"
  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"
  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"
  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"
  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"
  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" />
  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. }, 20)
  513. },
  514. setEmit(value) {
  515. if (this.returnType === "timestamp" || this.returnType === "date") {
  516. if (!Array.isArray(value)) {
  517. if (!this.hasTime) {
  518. value = value + ' ' + '00:00:00'
  519. }
  520. value = this.createTimestamp(value)
  521. if (this.returnType === "date") {
  522. value = new Date(value)
  523. }
  524. } else {
  525. if (!this.hasTime) {
  526. value[0] = value[0] + ' ' + '00:00:00'
  527. value[1] = value[1] + ' ' + '00:00:00'
  528. }
  529. value[0] = this.createTimestamp(value[0])
  530. value[1] = this.createTimestamp(value[1])
  531. if (this.returnType === "date") {
  532. value[0] = new Date(value[0])
  533. value[1] = new Date(value[1])
  534. }
  535. }
  536. }
  537. this.$emit('change', value)
  538. this.$emit('input', value)
  539. this.$emit('update:modelValue', value)
  540. this.isEmitValue = true
  541. },
  542. createTimestamp(date) {
  543. date = this.fixIosDateFormat(date)
  544. return Date.parse(new Date(date))
  545. },
  546. singleChange(e) {
  547. this.tempSingleDate = e.fulldate
  548. if (this.hasTime) return
  549. this.confirmSingleChange()
  550. },
  551. confirmSingleChange() {
  552. if (!this.tempSingleDate) {
  553. this.popup = false
  554. return
  555. }
  556. if (this.hasTime) {
  557. this.singleVal = this.tempSingleDate + ' ' + (this.time ? this.time : '00:00:00')
  558. } else {
  559. this.singleVal = this.tempSingleDate
  560. }
  561. this.setEmit(this.singleVal)
  562. this.popup = false
  563. },
  564. leftChange(e) {
  565. const {
  566. before,
  567. after
  568. } = e.range
  569. this.rangeChange(before, after)
  570. const obj = {
  571. before: e.range.before,
  572. after: e.range.after,
  573. data: e.range.data,
  574. fulldate: e.fulldate
  575. }
  576. this.startMultipleStatus = Object.assign({}, this.startMultipleStatus, obj)
  577. },
  578. rightChange(e) {
  579. const {
  580. before,
  581. after
  582. } = e.range
  583. this.rangeChange(before, after)
  584. const obj = {
  585. before: e.range.before,
  586. after: e.range.after,
  587. data: e.range.data,
  588. fulldate: e.fulldate
  589. }
  590. this.endMultipleStatus = Object.assign({}, this.endMultipleStatus, obj)
  591. },
  592. mobileChange(e) {
  593. if (this.isRange) {
  594. const {
  595. before,
  596. after
  597. } = e.range
  598. this.handleStartAndEnd(before, after, true)
  599. if (this.hasTime) {
  600. const {
  601. startTime,
  602. endTime
  603. } = e.timeRange
  604. this.tempRange.startTime = startTime
  605. this.tempRange.endTime = endTime
  606. }
  607. this.confirmRangeChange()
  608. } else {
  609. if (this.hasTime) {
  610. this.singleVal = e.fulldate + ' ' + e.time
  611. } else {
  612. this.singleVal = e.fulldate
  613. }
  614. this.setEmit(this.singleVal)
  615. }
  616. this.$refs.mobile.close()
  617. },
  618. rangeChange(before, after) {
  619. if (!(before && after)) return
  620. this.handleStartAndEnd(before, after, true)
  621. if (this.hasTime) return
  622. this.confirmRangeChange()
  623. },
  624. confirmRangeChange() {
  625. if (!this.tempRange.startDate && !this.tempRange.endDate) {
  626. this.popup = false
  627. return
  628. }
  629. let start, end
  630. if (!this.hasTime) {
  631. start = this.range.startDate = this.tempRange.startDate
  632. end = this.range.endDate = this.tempRange.endDate
  633. } else {
  634. start = this.range.startDate = this.tempRange.startDate + ' ' +
  635. (this.tempRange.startTime ? this.tempRange.startTime : '00:00:00')
  636. end = this.range.endDate = this.tempRange.endDate + ' ' +
  637. (this.tempRange.endTime ? this.tempRange.endTime : '00:00:00')
  638. }
  639. const displayRange = [start, end]
  640. this.setEmit(displayRange)
  641. this.popup = false
  642. },
  643. handleStartAndEnd(before, after, temp = false) {
  644. if (!(before && after)) return
  645. const type = temp ? 'tempRange' : 'range'
  646. if (this.dateCompare(before, after)) {
  647. this[type].startDate = before
  648. this[type].endDate = after
  649. } else {
  650. this[type].startDate = after
  651. this[type].endDate = before
  652. }
  653. },
  654. /**
  655. * 比较时间大小
  656. */
  657. dateCompare(startDate, endDate) {
  658. // 计算截止时间
  659. startDate = new Date(startDate.replace('-', '/').replace('-', '/'))
  660. // 计算详细项的截止时间
  661. endDate = new Date(endDate.replace('-', '/').replace('-', '/'))
  662. if (startDate <= endDate) {
  663. return true
  664. } else {
  665. return false
  666. }
  667. },
  668. /**
  669. * 比较时间差
  670. */
  671. diffDate(startDate, endDate) {
  672. // 计算截止时间
  673. startDate = new Date(startDate.replace('-', '/').replace('-', '/'))
  674. // 计算详细项的截止时间
  675. endDate = new Date(endDate.replace('-', '/').replace('-', '/'))
  676. const diff = (endDate - startDate) / (24 * 60 * 60 * 1000)
  677. return Math.abs(diff)
  678. },
  679. clear(needEmit = true) {
  680. if (!this.isRange) {
  681. this.singleVal = ''
  682. this.tempSingleDate = ''
  683. this.time = ''
  684. if (this.isPhone) {
  685. this.$refs.mobile && this.$refs.mobile.clearCalender()
  686. } else {
  687. this.$refs.pcSingle && this.$refs.pcSingle.clearCalender()
  688. }
  689. if (needEmit) {
  690. // 校验规则
  691. // if(this.form && this.formItem){
  692. // const {
  693. // validateTrigger
  694. // } = this.form
  695. // if (validateTrigger === 'blur') {
  696. // this.formItem.onFieldChange()
  697. // }
  698. // }
  699. this.$emit('change', '')
  700. this.$emit('input', '')
  701. this.$emit('update:modelValue', '')
  702. }
  703. } else {
  704. this.range.startDate = ''
  705. this.range.endDate = ''
  706. this.tempRange.startDate = ''
  707. this.tempRange.startTime = ''
  708. this.tempRange.endDate = ''
  709. this.tempRange.endTime = ''
  710. if (this.isPhone) {
  711. this.$refs.mobile && this.$refs.mobile.clearCalender()
  712. } else {
  713. this.$refs.left && this.$refs.left.clearCalender()
  714. this.$refs.right && this.$refs.right.clearCalender()
  715. this.$refs.right && this.$refs.right.next()
  716. }
  717. if (needEmit) {
  718. this.$emit('change', [])
  719. this.$emit('input', [])
  720. this.$emit('update:modelValue', [])
  721. }
  722. }
  723. },
  724. parseDate(date) {
  725. date = this.fixIosDateFormat(date)
  726. const defVal = new Date(date)
  727. const year = defVal.getFullYear()
  728. const month = defVal.getMonth() + 1
  729. const day = defVal.getDate()
  730. const hour = defVal.getHours()
  731. const minute = defVal.getMinutes()
  732. const second = defVal.getSeconds()
  733. const defDate = year + '-' + this.lessTen(month) + '-' + this.lessTen(day)
  734. const defTime = this.lessTen(hour) + ':' + this.lessTen(minute) + (this.hideSecond ? '' : (':' + this
  735. .lessTen(second)))
  736. return {
  737. defDate,
  738. defTime
  739. }
  740. },
  741. lessTen(item) {
  742. return item < 10 ? '0' + item : item
  743. },
  744. //兼容 iOS、safari 日期格式
  745. fixIosDateFormat(value) {
  746. if (typeof value === 'string') {
  747. value = value.replace(/-/g, '/')
  748. }
  749. return value
  750. },
  751. leftMonthSwitch(e) {
  752. // console.log('leftMonthSwitch 返回:', e)
  753. },
  754. rightMonthSwitch(e) {
  755. // console.log('rightMonthSwitch 返回:', e)
  756. }
  757. }
  758. }
  759. </script>
  760. <style>
  761. .uni-date {
  762. /* #ifndef APP-NVUE */
  763. width: 100%;
  764. /* #endif */
  765. flex: 1;
  766. }
  767. .uni-date-x {
  768. display: flex;
  769. flex-direction: row;
  770. align-items: center;
  771. justify-content: center;
  772. padding: 0 10px;
  773. border-radius: 4px;
  774. background-color: #fff;
  775. color: #666;
  776. font-size: 14px;
  777. flex: 1;
  778. }
  779. .uni-date-x--border {
  780. box-sizing: border-box;
  781. border-radius: 4px;
  782. border: 1px solid #e5e5e5;
  783. }
  784. .uni-date-editor--x {
  785. display: flex;
  786. align-items: center;
  787. position: relative;
  788. }
  789. .uni-date-editor--x .uni-date__icon-clear {
  790. padding: 0 5px;
  791. display: flex;
  792. align-items: center;
  793. /* #ifdef H5 */
  794. cursor: pointer;
  795. /* #endif */
  796. }
  797. .uni-date__x-input {
  798. padding: 0 8px;
  799. /* #ifndef APP-NVUE */
  800. width: auto;
  801. /* #endif */
  802. position: relative;
  803. overflow: hidden;
  804. flex: 1;
  805. line-height: 1;
  806. font-size: 14px;
  807. height: 35px;
  808. }
  809. .t-c {
  810. text-align: center;
  811. }
  812. .uni-date__input {
  813. height: 40px;
  814. width: 100%;
  815. line-height: 40px;
  816. font-size: 14px;
  817. }
  818. .uni-date-range__input {
  819. text-align: center;
  820. max-width: 142px;
  821. }
  822. .uni-date-picker__container {
  823. position: relative;
  824. /* position: fixed;
  825. left: 0;
  826. right: 0;
  827. top: 0;
  828. bottom: 0;
  829. box-sizing: border-box;
  830. z-index: 996;
  831. font-size: 14px; */
  832. }
  833. .uni-date-mask {
  834. position: fixed;
  835. bottom: 0px;
  836. top: 0px;
  837. left: 0px;
  838. right: 0px;
  839. background-color: rgba(0, 0, 0, 0);
  840. transition-duration: 0.3s;
  841. z-index: 996;
  842. }
  843. .uni-date-single--x {
  844. /* padding: 0 8px; */
  845. background-color: #fff;
  846. position: absolute;
  847. top: 0;
  848. z-index: 999;
  849. border: 1px solid #EBEEF5;
  850. box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
  851. border-radius: 4px;
  852. }
  853. .uni-date-range--x {
  854. /* padding: 0 8px; */
  855. background-color: #fff;
  856. position: absolute;
  857. top: 0;
  858. z-index: 999;
  859. border: 1px solid #EBEEF5;
  860. box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
  861. border-radius: 4px;
  862. }
  863. .uni-date-editor--x__disabled {
  864. opacity: 0.4;
  865. cursor: default;
  866. }
  867. .uni-date-editor--logo {
  868. width: 16px;
  869. height: 16px;
  870. vertical-align: middle;
  871. }
  872. /* 添加时间 */
  873. .popup-x-header {
  874. /* #ifndef APP-NVUE */
  875. display: flex;
  876. /* #endif */
  877. flex-direction: row;
  878. /* justify-content: space-between; */
  879. }
  880. .popup-x-header--datetime {
  881. /* #ifndef APP-NVUE */
  882. display: flex;
  883. /* #endif */
  884. flex-direction: row;
  885. flex: 1;
  886. }
  887. .popup-x-body {
  888. display: flex;
  889. }
  890. .popup-x-footer {
  891. padding: 0 15px;
  892. border-top-color: #F1F1F1;
  893. border-top-style: solid;
  894. border-top-width: 1px;
  895. /* background-color: #fff; */
  896. line-height: 40px;
  897. text-align: right;
  898. color: #666;
  899. }
  900. .popup-x-footer text:hover {
  901. color: #007aff;
  902. cursor: pointer;
  903. opacity: 0.8;
  904. }
  905. .popup-x-footer .confirm {
  906. margin-left: 20px;
  907. color: #007aff;
  908. }
  909. .uni-date-changed {
  910. /* background-color: #fff; */
  911. text-align: center;
  912. color: #333;
  913. border-bottom-color: #F1F1F1;
  914. border-bottom-style: solid;
  915. border-bottom-width: 1px;
  916. /* padding: 0 50px; */
  917. }
  918. .uni-date-changed--time text {
  919. /* padding: 0 20px; */
  920. height: 50px;
  921. line-height: 50px;
  922. }
  923. .uni-date-changed .uni-date-changed--time {
  924. /* display: flex; */
  925. flex: 1;
  926. }
  927. .uni-date-changed--time-date {
  928. color: #333;
  929. opacity: 0.6;
  930. }
  931. .mr-50 {
  932. margin-right: 50px;
  933. }
  934. /* picker 弹出层通用的指示小三角, todo:扩展至上下左右方向定位 */
  935. .uni-popper__arrow,
  936. .uni-popper__arrow::after {
  937. position: absolute;
  938. display: block;
  939. width: 0;
  940. height: 0;
  941. border-color: transparent;
  942. border-style: solid;
  943. border-width: 6px;
  944. }
  945. .uni-popper__arrow {
  946. filter: drop-shadow(0 2px 12px rgba(0, 0, 0, 0.03));
  947. top: -6px;
  948. left: 10%;
  949. margin-right: 3px;
  950. border-top-width: 0;
  951. border-bottom-color: #EBEEF5;
  952. }
  953. .uni-popper__arrow::after {
  954. content: " ";
  955. top: 1px;
  956. margin-left: -6px;
  957. border-top-width: 0;
  958. border-bottom-color: #fff;
  959. }
  960. </style>