uni-datetime-picker.vue 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989
  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. modelValue: {
  267. immediate: true,
  268. handler(newVal, oldVal) {
  269. if (this.isEmitValue) {
  270. this.isEmitValue = false
  271. return
  272. }
  273. this.initPicker(newVal)
  274. }
  275. },
  276. start: {
  277. immediate: true,
  278. handler(newVal, oldVal) {
  279. if (!newVal) return
  280. const {
  281. defDate,
  282. defTime
  283. } = this.parseDate(newVal)
  284. this.caleRange.startDate = defDate
  285. if (this.hasTime) {
  286. this.caleRange.startTime = defTime
  287. }
  288. }
  289. },
  290. end: {
  291. immediate: true,
  292. handler(newVal, oldVal) {
  293. if (!newVal) return
  294. const {
  295. defDate,
  296. defTime
  297. } = this.parseDate(newVal)
  298. this.caleRange.endDate = defDate
  299. if (this.hasTime) {
  300. this.caleRange.endTime = defTime
  301. }
  302. }
  303. },
  304. },
  305. computed: {
  306. reactStartTime() {
  307. const activeDate = this.isRange ? this.tempRange.startDate : this.tempSingleDate
  308. const res = activeDate === this.caleRange.startDate ? this.caleRange.startTime : ''
  309. return res
  310. },
  311. reactEndTime() {
  312. const activeDate = this.isRange ? this.tempRange.endDate : this.tempSingleDate
  313. const res = activeDate === this.caleRange.endDate ? this.caleRange.endTime : ''
  314. return res
  315. },
  316. reactMobDefTime() {
  317. const times = {
  318. start: this.tempRange.startTime,
  319. end: this.tempRange.endTime
  320. }
  321. return this.isRange ? times : this.time
  322. },
  323. mobSelectableTime() {
  324. return {
  325. start: this.caleRange.startTime,
  326. end: this.caleRange.endTime
  327. }
  328. },
  329. datePopupWidth() {
  330. // todo
  331. return this.isRange ? 653 : 301
  332. },
  333. /**
  334. * for i18n
  335. */
  336. singlePlaceholderText() {
  337. return this.placeholder || (this.type === 'date' ? this.selectDateText : t(
  338. "uni-datetime-picker.selectDateTime"))
  339. },
  340. startPlaceholderText() {
  341. return this.startPlaceholder || this.startDateText
  342. },
  343. endPlaceholderText() {
  344. return this.endPlaceholder || this.endDateText
  345. },
  346. selectDateText() {
  347. return t("uni-datetime-picker.selectDate")
  348. },
  349. selectTimeText() {
  350. return t("uni-datetime-picker.selectTime")
  351. },
  352. startDateText() {
  353. return this.startPlaceholder || t("uni-datetime-picker.startDate")
  354. },
  355. startTimeText() {
  356. return t("uni-datetime-picker.startTime")
  357. },
  358. endDateText() {
  359. return this.endPlaceholder || t("uni-datetime-picker.endDate")
  360. },
  361. endTimeText() {
  362. return t("uni-datetime-picker.endTime")
  363. },
  364. okText() {
  365. return t("uni-datetime-picker.ok")
  366. },
  367. clearText() {
  368. return t("uni-datetime-picker.clear")
  369. },
  370. showClearIcon() {
  371. const { clearIcon, disabled, singleVal, range } = this
  372. const bool = clearIcon && !disabled && (singleVal || (range.startDate && range.endDate))
  373. return bool
  374. }
  375. },
  376. created() {
  377. this.form = this.getForm('uniForms')
  378. this.formItem = this.getForm('uniFormsItem')
  379. // if (this.formItem) {
  380. // if (this.formItem.name) {
  381. // this.rename = this.formItem.name
  382. // this.form.inputChildrens.push(this)
  383. // }
  384. // }
  385. },
  386. mounted() {
  387. this.platform()
  388. },
  389. methods: {
  390. /**
  391. * 获取父元素实例
  392. */
  393. getForm(name = 'uniForms') {
  394. let parent = this.$parent;
  395. let parentName = parent.$options.name;
  396. while (parentName !== name) {
  397. parent = parent.$parent;
  398. if (!parent) return false
  399. parentName = parent.$options.name;
  400. }
  401. return parent;
  402. },
  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. return
  474. }
  475. this.popover = {
  476. top: '10px'
  477. }
  478. const dateEditor = uni.createSelectorQuery().in(this).select(".uni-date-editor")
  479. dateEditor.boundingClientRect(rect => {
  480. if (this.windowWidth - rect.left < this.datePopupWidth) {
  481. this.popover.right = 0
  482. }
  483. }).exec()
  484. setTimeout(() => {
  485. this.popup = !this.popup
  486. if (!this.isPhone && this.isRange && this.isFirstShow) {
  487. this.isFirstShow = false
  488. const {
  489. startDate,
  490. endDate
  491. } = this.range
  492. if (startDate && endDate) {
  493. if (this.diffDate(startDate, endDate) < 30) {
  494. this.$refs.right.next()
  495. }
  496. } else {
  497. this.$refs.right.next()
  498. this.$refs.right.cale.lastHover = false
  499. }
  500. }
  501. }, 50)
  502. },
  503. close() {
  504. setTimeout(() => {
  505. this.popup = false
  506. this.$emit('maskClick', this.value)
  507. }, 20)
  508. },
  509. setEmit(value) {
  510. if (this.returnType === "timestamp" || this.returnType === "date") {
  511. if (!Array.isArray(value)) {
  512. if (!this.hasTime) {
  513. value = value + ' ' + '00:00:00'
  514. }
  515. value = this.createTimestamp(value)
  516. if (this.returnType === "date") {
  517. value = new Date(value)
  518. }
  519. } else {
  520. if (!this.hasTime) {
  521. value[0] = value[0] + ' ' + '00:00:00'
  522. value[1] = value[1] + ' ' + '00:00:00'
  523. }
  524. value[0] = this.createTimestamp(value[0])
  525. value[1] = this.createTimestamp(value[1])
  526. if (this.returnType === "date") {
  527. value[0] = new Date(value[0])
  528. value[1] = new Date(value[1])
  529. }
  530. }
  531. }
  532. this.formItem && this.formItem.setValue(value)
  533. this.$emit('change', value)
  534. this.$emit('input', value)
  535. this.$emit('update:modelValue', value)
  536. this.isEmitValue = true
  537. },
  538. createTimestamp(date) {
  539. date = this.fixIosDateFormat(date)
  540. return Date.parse(new Date(date))
  541. },
  542. singleChange(e) {
  543. this.tempSingleDate = e.fulldate
  544. if (this.hasTime) return
  545. this.confirmSingleChange()
  546. },
  547. confirmSingleChange() {
  548. if (!this.tempSingleDate) {
  549. this.popup = false
  550. return
  551. }
  552. if (this.hasTime) {
  553. this.singleVal = this.tempSingleDate + ' ' + (this.time ? this.time : '00:00:00')
  554. } else {
  555. this.singleVal = this.tempSingleDate
  556. }
  557. this.setEmit(this.singleVal)
  558. this.popup = false
  559. },
  560. leftChange(e) {
  561. const {
  562. before,
  563. after
  564. } = e.range
  565. this.rangeChange(before, after)
  566. const obj = {
  567. before: e.range.before,
  568. after: e.range.after,
  569. data: e.range.data,
  570. fulldate: e.fulldate
  571. }
  572. this.startMultipleStatus = Object.assign({}, this.startMultipleStatus, obj)
  573. },
  574. rightChange(e) {
  575. const {
  576. before,
  577. after
  578. } = e.range
  579. this.rangeChange(before, after)
  580. const obj = {
  581. before: e.range.before,
  582. after: e.range.after,
  583. data: e.range.data,
  584. fulldate: e.fulldate
  585. }
  586. this.endMultipleStatus = Object.assign({}, this.endMultipleStatus, obj)
  587. },
  588. mobileChange(e) {
  589. if (this.isRange) {
  590. const {
  591. before,
  592. after
  593. } = e.range
  594. this.handleStartAndEnd(before, after, true)
  595. if (this.hasTime) {
  596. const {
  597. startTime,
  598. endTime
  599. } = e.timeRange
  600. this.tempRange.startTime = startTime
  601. this.tempRange.endTime = endTime
  602. }
  603. this.confirmRangeChange()
  604. } else {
  605. if (this.hasTime) {
  606. this.singleVal = e.fulldate + ' ' + e.time
  607. } else {
  608. this.singleVal = e.fulldate
  609. }
  610. this.setEmit(this.singleVal)
  611. }
  612. this.$refs.mobile.close()
  613. },
  614. rangeChange(before, after) {
  615. if (!(before && after)) return
  616. this.handleStartAndEnd(before, after, true)
  617. if (this.hasTime) return
  618. this.confirmRangeChange()
  619. },
  620. confirmRangeChange() {
  621. if (!this.tempRange.startDate && !this.tempRange.endDate) {
  622. this.popup = false
  623. return
  624. }
  625. let start, end
  626. if (!this.hasTime) {
  627. start = this.range.startDate = this.tempRange.startDate
  628. end = this.range.endDate = this.tempRange.endDate
  629. } else {
  630. start = this.range.startDate = this.tempRange.startDate + ' ' +
  631. (this.tempRange.startTime ? this.tempRange.startTime : '00:00:00')
  632. end = this.range.endDate = this.tempRange.endDate + ' ' +
  633. (this.tempRange.endTime ? this.tempRange.endTime : '00:00:00')
  634. }
  635. const displayRange = [start, end]
  636. this.setEmit(displayRange)
  637. this.popup = false
  638. },
  639. handleStartAndEnd(before, after, temp = false) {
  640. if (!(before && after)) return
  641. const type = temp ? 'tempRange' : 'range'
  642. if (this.dateCompare(before, after)) {
  643. this[type].startDate = before
  644. this[type].endDate = after
  645. } else {
  646. this[type].startDate = after
  647. this[type].endDate = before
  648. }
  649. },
  650. /**
  651. * 比较时间大小
  652. */
  653. dateCompare(startDate, endDate) {
  654. // 计算截止时间
  655. startDate = new Date(startDate.replace('-', '/').replace('-', '/'))
  656. // 计算详细项的截止时间
  657. endDate = new Date(endDate.replace('-', '/').replace('-', '/'))
  658. if (startDate <= endDate) {
  659. return true
  660. } else {
  661. return false
  662. }
  663. },
  664. /**
  665. * 比较时间差
  666. */
  667. diffDate(startDate, endDate) {
  668. // 计算截止时间
  669. startDate = new Date(startDate.replace('-', '/').replace('-', '/'))
  670. // 计算详细项的截止时间
  671. endDate = new Date(endDate.replace('-', '/').replace('-', '/'))
  672. const diff = (endDate - startDate) / (24 * 60 * 60 * 1000)
  673. return Math.abs(diff)
  674. },
  675. clear(needEmit = true) {
  676. if (!this.isRange) {
  677. this.singleVal = ''
  678. this.tempSingleDate = ''
  679. this.time = ''
  680. if (this.isPhone) {
  681. this.$refs.mobile && this.$refs.mobile.clearCalender()
  682. } else {
  683. this.$refs.pcSingle && this.$refs.pcSingle.clearCalender()
  684. }
  685. if (needEmit) {
  686. this.formItem && this.formItem.setValue('')
  687. this.$emit('change', '')
  688. this.$emit('input', '')
  689. this.$emit('update:modelValue', '')
  690. }
  691. } else {
  692. this.range.startDate = ''
  693. this.range.endDate = ''
  694. this.tempRange.startDate = ''
  695. this.tempRange.startTime = ''
  696. this.tempRange.endDate = ''
  697. this.tempRange.endTime = ''
  698. if (this.isPhone) {
  699. this.$refs.mobile && this.$refs.mobile.clearCalender()
  700. } else {
  701. this.$refs.left && this.$refs.left.clearCalender()
  702. this.$refs.right && this.$refs.right.clearCalender()
  703. this.$refs.right && this.$refs.right.next()
  704. }
  705. if (needEmit) {
  706. this.formItem && this.formItem.setValue([])
  707. this.$emit('change', [])
  708. this.$emit('input', [])
  709. this.$emit('update:modelValue', [])
  710. }
  711. }
  712. },
  713. parseDate(date) {
  714. date = this.fixIosDateFormat(date)
  715. const defVal = new Date(date)
  716. const year = defVal.getFullYear()
  717. const month = defVal.getMonth() + 1
  718. const day = defVal.getDate()
  719. const hour = defVal.getHours()
  720. const minute = defVal.getMinutes()
  721. const second = defVal.getSeconds()
  722. const defDate = year + '-' + this.lessTen(month) + '-' + this.lessTen(day)
  723. const defTime = this.lessTen(hour) + ':' + this.lessTen(minute) + (this.hideSecond ? '' : (':' + this
  724. .lessTen(second)))
  725. return {
  726. defDate,
  727. defTime
  728. }
  729. },
  730. lessTen(item) {
  731. return item < 10 ? '0' + item : item
  732. },
  733. //兼容 iOS、safari 日期格式
  734. fixIosDateFormat(value) {
  735. if (typeof value === 'string') {
  736. value = value.replace(/-/g, '/')
  737. }
  738. return value
  739. },
  740. leftMonthSwitch(e) {
  741. // console.log('leftMonthSwitch 返回:', e)
  742. },
  743. rightMonthSwitch(e) {
  744. // console.log('rightMonthSwitch 返回:', e)
  745. }
  746. }
  747. }
  748. </script>
  749. <style>
  750. .uni-date-x {
  751. display: flex;
  752. flex-direction: row;
  753. align-items: center;
  754. justify-content: center;
  755. padding: 0 10px;
  756. border-radius: 4px;
  757. background-color: #fff;
  758. color: #666;
  759. font-size: 14px;
  760. }
  761. .uni-date-x--border {
  762. box-sizing: border-box;
  763. border-radius: 4px;
  764. border: 1px solid #dcdfe6;
  765. }
  766. .uni-date-editor--x {
  767. position: relative;
  768. }
  769. .uni-date-editor--x .uni-date__icon-clear {
  770. position: absolute;
  771. top: 0;
  772. right: 0;
  773. display: inline-block;
  774. box-sizing: border-box;
  775. border: 9px solid transparent;
  776. /* #ifdef H5 */
  777. cursor: pointer;
  778. /* #endif */
  779. }
  780. .uni-date__x-input {
  781. padding: 0 8px;
  782. height: 40px;
  783. width: 100%;
  784. line-height: 40px;
  785. font-size: 14px;
  786. }
  787. .t-c {
  788. text-align: center;
  789. }
  790. .uni-date__input {
  791. height: 40px;
  792. width: 100%;
  793. line-height: 40px;
  794. font-size: 14px;
  795. }
  796. .uni-date-range__input {
  797. text-align: center;
  798. max-width: 142px;
  799. }
  800. .uni-date-picker__container {
  801. position: relative;
  802. /* position: fixed;
  803. left: 0;
  804. right: 0;
  805. top: 0;
  806. bottom: 0;
  807. box-sizing: border-box;
  808. z-index: 996;
  809. font-size: 14px; */
  810. }
  811. .uni-date-mask {
  812. position: fixed;
  813. bottom: 0px;
  814. top: 0px;
  815. left: 0px;
  816. right: 0px;
  817. background-color: rgba(0, 0, 0, 0);
  818. transition-duration: 0.3s;
  819. z-index: 996;
  820. }
  821. .uni-date-single--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-range--x {
  832. /* padding: 0 8px; */
  833. background-color: #fff;
  834. position: absolute;
  835. top: 0;
  836. z-index: 999;
  837. border: 1px solid #EBEEF5;
  838. box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
  839. border-radius: 4px;
  840. }
  841. .uni-date-editor--x__disabled {
  842. opacity: 0.4;
  843. cursor: default;
  844. }
  845. .uni-date-editor--logo {
  846. width: 16px;
  847. height: 16px;
  848. vertical-align: middle;
  849. }
  850. /* 添加时间 */
  851. .popup-x-header {
  852. /* #ifndef APP-NVUE */
  853. display: flex;
  854. /* #endif */
  855. flex-direction: row;
  856. /* justify-content: space-between; */
  857. }
  858. .popup-x-header--datetime {
  859. /* #ifndef APP-NVUE */
  860. display: flex;
  861. /* #endif */
  862. flex-direction: row;
  863. flex: 1;
  864. }
  865. .popup-x-body {
  866. display: flex;
  867. }
  868. .popup-x-footer {
  869. padding: 0 15px;
  870. border-top-color: #F1F1F1;
  871. border-top-style: solid;
  872. border-top-width: 1px;
  873. /* background-color: #fff; */
  874. line-height: 40px;
  875. text-align: right;
  876. color: #666;
  877. }
  878. .popup-x-footer text:hover {
  879. color: #007aff;
  880. cursor: pointer;
  881. opacity: 0.8;
  882. }
  883. .popup-x-footer .confirm {
  884. margin-left: 20px;
  885. color: #007aff;
  886. }
  887. .uni-date-changed {
  888. /* background-color: #fff; */
  889. text-align: center;
  890. color: #333;
  891. border-bottom-color: #F1F1F1;
  892. border-bottom-style: solid;
  893. border-bottom-width: 1px;
  894. /* padding: 0 50px; */
  895. }
  896. .uni-date-changed--time text {
  897. /* padding: 0 20px; */
  898. height: 50px;
  899. line-height: 50px;
  900. }
  901. .uni-date-changed .uni-date-changed--time {
  902. /* display: flex; */
  903. flex: 1;
  904. }
  905. .uni-date-changed--time-date {
  906. color: #333;
  907. opacity: 0.6;
  908. }
  909. .mr-50 {
  910. margin-right: 50px;
  911. }
  912. /* picker 弹出层通用的指示小三角, todo:扩展至上下左右方向定位 */
  913. .uni-popper__arrow,
  914. .uni-popper__arrow::after {
  915. position: absolute;
  916. display: block;
  917. width: 0;
  918. height: 0;
  919. border-color: transparent;
  920. border-style: solid;
  921. border-width: 6px;
  922. }
  923. .uni-popper__arrow {
  924. filter: drop-shadow(0 2px 12px rgba(0, 0, 0, 0.03));
  925. top: -6px;
  926. left: 10%;
  927. margin-right: 3px;
  928. border-top-width: 0;
  929. border-bottom-color: #EBEEF5;
  930. }
  931. .uni-popper__arrow::after {
  932. content: " ";
  933. top: 1px;
  934. margin-left: -6px;
  935. border-top-width: 0;
  936. border-bottom-color: #fff;
  937. }
  938. </style>