uni-datetime-picker.vue 25 KB

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