uni-datetime-picker.vue 25 KB

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