lic-uni-datetime-picker.vue 28 KB

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