uni-datetime-picker.vue 25 KB

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