uni-datetime-picker.vue 26 KB

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