uni-datetime-picker.vue 27 KB

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