tui-datetime.vue 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654
  1. <template>
  2. <view class="tui-datetime-picker">
  3. <view class="tui-mask" :class="{ 'tui-mask-show': isShow }" @touchmove.stop.prevent="stop" catchtouchmove="stop"
  4. @tap="maskClick"></view>
  5. <view class="tui-header" :class="{ 'tui-show': isShow }">
  6. <view class="tui-picker-header" :class="{ 'tui-date-radius': radius }"
  7. :style="{ backgroundColor: headerBackground }" @touchmove.stop.prevent="stop" catchtouchmove="stop">
  8. <view class="tui-btn-picker" :style="{ color: cancelColor }" hover-class="tui-opacity"
  9. :hover-stay-time="150" @tap="hide">取消</view>
  10. <view class="tui-pickerdate__title" :style="{fontSize:titleSize+'rpx',color:titleColor}">{{title}}
  11. </view>
  12. <view class="tui-btn-picker" :style="{ color: color }" hover-class="tui-opacity" :hover-stay-time="150"
  13. @tap="btnFix">确定</view>
  14. </view>
  15. <view class="tui-date-header" :style="{ backgroundColor: unitBackground }" v-if="unitTop">
  16. <view class="tui-date-unit" v-if="type < 4 || type == 7 || type==8">年</view>
  17. <view class="tui-date-unit" v-if="type < 4 || type == 7 || type==8">月</view>
  18. <view class="tui-date-unit" v-if="type == 1 || type == 2 || type == 7 || type==8">日</view>
  19. <view class="tui-date-unit" v-if="type == 1 || type == 4 || type == 5 || type == 7 || type==8">时</view>
  20. <view class="tui-date-unit" v-if="(type == 1 || type > 3) && type!=8">分</view>
  21. <view class="tui-date-unit" v-if="type > 4 && type !=8">秒</view>
  22. </view>
  23. <view @touchstart.stop="pickerstart" class="tui-date__picker-body"
  24. :style="{ backgroundColor: bodyBackground,height:height+'rpx' }">
  25. <picker-view :value="value" @change="change" class="tui-picker-view">
  26. <picker-view-column v-if="!reset && (type < 4 || type == 7 || type==8)">
  27. <view class="tui-date__column-item" :class="{ 'tui-font-size_32': !unitTop && type == 7 }"
  28. v-for="(item, index) in years" :key="index">
  29. {{ item }}
  30. <text class="tui-date__unit-text" v-if="!unitTop">年</text>
  31. </view>
  32. </picker-view-column>
  33. <picker-view-column v-if="!reset && (type < 4 || type == 7 || type==8)">
  34. <view class="tui-date__column-item" :class="{ 'tui-font-size_32': !unitTop && type == 7 }"
  35. v-for="(item, index) in months" :key="index">
  36. {{ formatNum(item) }}
  37. <text class="tui-date__unit-text" v-if="!unitTop">月</text>
  38. </view>
  39. </picker-view-column>
  40. <picker-view-column v-if="!reset && (type == 1 || type == 2 || type == 7 || type==8)">
  41. <view class="tui-date__column-item" :class="{ 'tui-font-size_32': !unitTop && type == 7 }"
  42. v-for="(item, index) in days" :key="index">
  43. {{ formatNum(item) }}
  44. <text class="tui-date__unit-text" v-if="!unitTop">日</text>
  45. </view>
  46. </picker-view-column>
  47. <picker-view-column v-if="!reset && (type == 1 || type == 4 || type == 5 || type == 7 || type==8)">
  48. <view class="tui-date__column-item" :class="{ 'tui-font-size_32': !unitTop && type == 7 }"
  49. v-for="(item, index) in hours" :key="index">
  50. {{ formatNum(item) }}
  51. <text class="tui-date__unit-text" v-if="!unitTop">时</text>
  52. </view>
  53. </picker-view-column>
  54. <picker-view-column v-if="!reset && (type == 1 || type > 3) && type!=8">
  55. <view class="tui-date__column-item" :class="{ 'tui-font-size_32': !unitTop && type == 7 }"
  56. v-for="(item, index) in minutes" :key="index">
  57. {{ formatNum(item) }}
  58. <text class="tui-date__unit-text" v-if="!unitTop">分</text>
  59. </view>
  60. </picker-view-column>
  61. <picker-view-column v-if="!reset && type > 4 && type!=8">
  62. <view class="tui-date__column-item" :class="{ 'tui-font-size_32': !unitTop && type == 7 }"
  63. v-for="(item, index) in seconds" :key="index">
  64. {{ formatNum(item) }}
  65. <text class="tui-date__unit-text" v-if="!unitTop">秒</text>
  66. </view>
  67. </picker-view-column>
  68. </picker-view>
  69. </view>
  70. </view>
  71. </view>
  72. </template>
  73. <script>
  74. export default {
  75. name: 'tuiDatetime',
  76. emits: ['cancel', 'confirm'],
  77. props: {
  78. //1-日期+时间(年月日+时分) 2-日期(年月日) 3-日期(年月) 4-时间(时分) 5-时分秒 6-分秒 7-年月日 时分秒 8-年月日+小时
  79. type: {
  80. type: Number,
  81. default: 1
  82. },
  83. //年份区间
  84. startYear: {
  85. type: Number,
  86. default: 1980
  87. },
  88. //年份区间
  89. endYear: {
  90. type: Number,
  91. default: 2050
  92. },
  93. //显示标题
  94. title: {
  95. type: String,
  96. default: ''
  97. },
  98. //标题字体大小
  99. titleSize: {
  100. type: [Number, String],
  101. default: 34
  102. },
  103. //标题字体颜色
  104. titleColor: {
  105. type: String,
  106. default: '#333'
  107. },
  108. //"取消"字体颜色
  109. cancelColor: {
  110. type: String,
  111. default: '#888'
  112. },
  113. //"确定"字体颜色
  114. color: {
  115. type: String,
  116. default: '#5677fc'
  117. },
  118. //设置默认显示日期 2019-08-01 || 2019-08-01 17:01 || 2019/08/01
  119. setDateTime: {
  120. type: String,
  121. default: ''
  122. },
  123. //单位置顶
  124. unitTop: {
  125. type: Boolean,
  126. default: false
  127. },
  128. //圆角设置
  129. radius: {
  130. type: Boolean,
  131. default: false
  132. },
  133. //头部背景色
  134. headerBackground: {
  135. type: String,
  136. default: '#fff'
  137. },
  138. //根据实际调整,不建议使用深颜色
  139. bodyBackground: {
  140. type: String,
  141. default: '#fff'
  142. },
  143. //单位置顶时,单位条背景色
  144. unitBackground: {
  145. type: String,
  146. default: '#fff'
  147. },
  148. height: {
  149. type: [Number, String],
  150. default: 520
  151. },
  152. //点击遮罩 是否可关闭
  153. maskClosable: {
  154. type: Boolean,
  155. default: true
  156. }
  157. },
  158. data() {
  159. return {
  160. isShow: false,
  161. years: [],
  162. months: [],
  163. days: [],
  164. hours: [],
  165. minutes: [],
  166. seconds: [],
  167. year: 0,
  168. month: 0,
  169. day: 0,
  170. hour: 0,
  171. minute: 0,
  172. second: 0,
  173. startDate: '',
  174. endDate: '',
  175. value: [0, 0, 0, 0, 0, 0],
  176. reset: false,
  177. isEnd: true
  178. };
  179. },
  180. mounted() {
  181. setTimeout(() => {
  182. this.initData();
  183. }, 20)
  184. },
  185. computed: {
  186. yearOrMonth() {
  187. return `${this.year}-${this.month}`;
  188. },
  189. propsChange() {
  190. return `${this.setDateTime}-${this.type}-${this.startYear}-${this.endYear}`;
  191. }
  192. },
  193. watch: {
  194. yearOrMonth() {
  195. this.setDays();
  196. },
  197. propsChange() {
  198. this.reset = true;
  199. setTimeout(() => {
  200. this.initData();
  201. }, 20);
  202. }
  203. },
  204. methods: {
  205. stop() {},
  206. formatNum: function(num) {
  207. return num < 10 ? '0' + num : num + '';
  208. },
  209. generateArray: function(start, end) {
  210. return Array.from(new Array(end + 1).keys()).slice(start);
  211. },
  212. getIndex: function(arr, val) {
  213. let index = arr.indexOf(val);
  214. return ~index ? index : 0;
  215. },
  216. getCharCount(str) {
  217. let regex = new RegExp('/', 'g');
  218. let result = str.match(regex);
  219. return !result ? 0 : result.length;
  220. },
  221. //日期时间处理
  222. initSelectValue() {
  223. let fdate = this.setDateTime.replace(/\-/g, '/');
  224. if (this.type == 3 && this.getCharCount(fdate) === 1) {
  225. fdate += '/01'
  226. }
  227. fdate = fdate && fdate.indexOf('/') == -1 ? `2020/01/01 ${fdate}` : fdate;
  228. let time = null;
  229. if (fdate) time = new Date(fdate);
  230. else time = new Date();
  231. this.year = time.getFullYear();
  232. this.month = time.getMonth() + 1;
  233. this.day = time.getDate();
  234. this.hour = time.getHours();
  235. this.minute = time.getMinutes();
  236. this.second = time.getSeconds();
  237. },
  238. initData() {
  239. this.initSelectValue();
  240. this.reset = false;
  241. switch (this.type) {
  242. case 1:
  243. this.value = [0, 0, 0, 0, 0];
  244. this.setYears();
  245. this.setMonths();
  246. this.setDays();
  247. this.setHours();
  248. this.setMinutes();
  249. break;
  250. case 2:
  251. this.value = [0, 0, 0];
  252. this.setYears();
  253. this.setMonths();
  254. this.setDays();
  255. break;
  256. case 3:
  257. this.value = [0, 0];
  258. this.setYears();
  259. this.setMonths();
  260. break;
  261. case 4:
  262. this.value = [0, 0];
  263. this.setHours();
  264. this.setMinutes();
  265. break;
  266. case 5:
  267. this.value = [0, 0, 0];
  268. this.setHours();
  269. this.setMinutes();
  270. this.setSeconds();
  271. break;
  272. case 6:
  273. this.value = [0, 0];
  274. this.setMinutes();
  275. this.setSeconds();
  276. break;
  277. case 7:
  278. this.value = [0, 0, 0, 0, 0, 0];
  279. this.setYears();
  280. this.setMonths();
  281. this.setDays();
  282. this.setHours();
  283. this.setMinutes();
  284. this.setSeconds();
  285. break;
  286. case 8:
  287. this.value = [0, 0, 0, 0];
  288. this.setYears();
  289. this.setMonths();
  290. this.setDays();
  291. this.setHours();
  292. break;
  293. default:
  294. break;
  295. }
  296. },
  297. setYears() {
  298. this.years = this.generateArray(this.startYear, this.endYear);
  299. setTimeout(() => {
  300. this.$set(this.value, 0, this.getIndex(this.years, this.year));
  301. }, 8);
  302. },
  303. setMonths() {
  304. this.months = this.generateArray(1, 12);
  305. setTimeout(() => {
  306. this.$set(this.value, 1, this.getIndex(this.months, this.month));
  307. }, 8);
  308. },
  309. setDays() {
  310. if (this.type == 3 || this.type == 4) return;
  311. let totalDays = new Date(this.year, this.month, 0).getDate();
  312. totalDays = !totalDays || totalDays < 1 ? 1 : totalDays
  313. this.days = this.generateArray(1, totalDays);
  314. setTimeout(() => {
  315. this.$set(this.value, 2, this.getIndex(this.days, this.day));
  316. }, 8);
  317. },
  318. setHours() {
  319. this.hours = this.generateArray(0, 23);
  320. setTimeout(() => {
  321. let index = 0
  322. if (this.type == 8) {
  323. index = this.value.length - 1
  324. } else {
  325. index = this.type == 5 || this.type == 7 ? this.value.length - 3 : this.value.length - 2;
  326. }
  327. this.$set(this.value, index, this.getIndex(this.hours, this.hour));
  328. }, 8);
  329. },
  330. setMinutes() {
  331. this.minutes = this.generateArray(0, 59);
  332. setTimeout(() => {
  333. let index = this.type > 4 ? this.value.length - 2 : this.value.length - 1;
  334. this.$set(this.value, index, this.getIndex(this.minutes, this.minute));
  335. }, 8);
  336. },
  337. setSeconds() {
  338. this.seconds = this.generateArray(0, 59);
  339. setTimeout(() => {
  340. this.$set(this.value, this.value.length - 1, this.getIndex(this.seconds, this.second));
  341. }, 8);
  342. },
  343. show() {
  344. setTimeout(() => {
  345. this.isShow = true;
  346. }, 50);
  347. },
  348. hide() {
  349. this.isShow = false;
  350. this.$emit('cancel', {});
  351. },
  352. maskClick() {
  353. if (!this.maskClosable) return;
  354. this.hide()
  355. },
  356. change(e) {
  357. this.value = e.detail.value;
  358. switch (this.type) {
  359. case 1:
  360. this.year = this.years[this.value[0]];
  361. this.month = this.months[this.value[1]];
  362. this.day = this.days[this.value[2]];
  363. this.hour = this.hours[this.value[3]];
  364. this.minute = this.minutes[this.value[4]];
  365. break;
  366. case 2:
  367. this.year = this.years[this.value[0]];
  368. this.month = this.months[this.value[1]];
  369. this.day = this.days[this.value[2]];
  370. break;
  371. case 3:
  372. this.year = this.years[this.value[0]];
  373. this.month = this.months[this.value[1]];
  374. break;
  375. case 4:
  376. this.hour = this.hours[this.value[0]];
  377. this.minute = this.minutes[this.value[1]];
  378. break;
  379. case 5:
  380. this.hour = this.hours[this.value[0]];
  381. this.minute = this.minutes[this.value[1]];
  382. this.second = this.seconds[this.value[2]];
  383. break;
  384. case 6:
  385. this.minute = this.minutes[this.value[0]];
  386. this.second = this.seconds[this.value[1]];
  387. break;
  388. case 7:
  389. this.year = this.years[this.value[0]];
  390. this.month = this.months[this.value[1]];
  391. this.day = this.days[this.value[2]];
  392. this.hour = this.hours[this.value[3]];
  393. this.minute = this.minutes[this.value[4]];
  394. this.second = this.seconds[this.value[5]];
  395. break;
  396. case 8:
  397. this.year = this.years[this.value[0]];
  398. this.month = this.months[this.value[1]];
  399. this.day = this.days[this.value[2]];
  400. this.hour = this.hours[this.value[3]];
  401. break;
  402. default:
  403. break;
  404. }
  405. this.isEnd = true
  406. },
  407. selectResult() {
  408. let result = {};
  409. let year = this.year;
  410. let month = this.formatNum(this.month || 0);
  411. let day = this.formatNum(this.day || 0);
  412. let hour = this.formatNum(this.hour || 0);
  413. let minute = this.formatNum(this.minute || 0);
  414. let second = this.formatNum(this.second || 0);
  415. switch (this.type) {
  416. case 1:
  417. result = {
  418. year: year,
  419. month: month,
  420. day: day,
  421. hour: hour,
  422. minute: minute,
  423. result: `${year}-${month}-${day} ${hour}:${minute}`
  424. };
  425. break;
  426. case 2:
  427. result = {
  428. year: year,
  429. month: month,
  430. day: day,
  431. result: `${year}-${month}-${day}`
  432. };
  433. break;
  434. case 3:
  435. result = {
  436. year: year,
  437. month: month,
  438. result: `${year}-${month}`
  439. };
  440. break;
  441. case 4:
  442. result = {
  443. hour: hour,
  444. minute: minute,
  445. result: `${hour}:${minute}`
  446. };
  447. break;
  448. case 5:
  449. result = {
  450. hour: hour,
  451. minute: minute,
  452. second: second,
  453. result: `${hour}:${minute}:${second}`
  454. };
  455. break;
  456. case 6:
  457. result = {
  458. minute: minute,
  459. second: second,
  460. result: `${minute}:${second}`
  461. };
  462. break;
  463. case 7:
  464. result = {
  465. year: year,
  466. month: month,
  467. day: day,
  468. hour: hour,
  469. minute: minute,
  470. second: second,
  471. result: `${year}-${month}-${day} ${hour}:${minute}:${second}`
  472. };
  473. break;
  474. case 8:
  475. result = {
  476. year: year,
  477. month: month,
  478. day: day,
  479. hour: hour,
  480. result: `${year}-${month}-${day} ${hour}:00`
  481. };
  482. break;
  483. default:
  484. break;
  485. }
  486. this.$emit('confirm', result);
  487. },
  488. waitFix() {
  489. if (this.isEnd) {
  490. this.selectResult()
  491. } else {
  492. setTimeout(() => {
  493. this.waitFix()
  494. }, 50)
  495. }
  496. },
  497. btnFix() {
  498. setTimeout(() => {
  499. this.waitFix()
  500. this.hide();
  501. }, 80);
  502. },
  503. pickerstart() {
  504. this.isEnd = false
  505. }
  506. }
  507. };
  508. </script>
  509. <style scoped>
  510. .tui-datetime-picker {
  511. position: relative;
  512. z-index: 996;
  513. }
  514. .tui-picker-view {
  515. height: 100%;
  516. box-sizing: border-box;
  517. }
  518. .tui-mask {
  519. position: fixed;
  520. z-index: 997;
  521. top: 0;
  522. right: 0;
  523. bottom: 0;
  524. left: 0;
  525. background-color: rgba(0, 0, 0, 0.6);
  526. visibility: hidden;
  527. opacity: 0;
  528. transition: all 0.3s ease-in-out;
  529. }
  530. .tui-mask-show {
  531. visibility: visible !important;
  532. opacity: 1 !important;
  533. }
  534. .tui-header {
  535. z-index: 998;
  536. position: fixed;
  537. bottom: 0;
  538. left: 0;
  539. width: 100%;
  540. transition: all 0.3s ease-in-out;
  541. transform: translateY(100%);
  542. }
  543. .tui-date-header {
  544. width: 100%;
  545. height: 52rpx;
  546. display: flex;
  547. align-items: center;
  548. justify-content: space-between;
  549. font-size: 26rpx;
  550. line-height: 26rpx;
  551. /* #ifdef MP */
  552. box-shadow: 0 15rpx 10rpx -15rpx #efefef;
  553. /* #endif */
  554. /* #ifndef MP */
  555. box-shadow: 0 15rpx 10rpx -15rpx #888;
  556. /* #endif */
  557. position: relative;
  558. z-index: 2;
  559. }
  560. .tui-date-unit {
  561. flex: 1;
  562. text-align: center;
  563. }
  564. .tui-show {
  565. transform: translateY(0);
  566. }
  567. .tui-picker-header {
  568. width: 100%;
  569. height: 90rpx;
  570. padding: 0 40rpx;
  571. display: flex;
  572. justify-content: space-between;
  573. align-items: center;
  574. box-sizing: border-box;
  575. font-size: 32rpx;
  576. position: relative;
  577. }
  578. .tui-date-radius {
  579. border-top-left-radius: 20rpx;
  580. border-top-right-radius: 20rpx;
  581. overflow: hidden;
  582. }
  583. .tui-picker-header::after {
  584. content: '';
  585. position: absolute;
  586. border-bottom: 1rpx solid #eaeef1;
  587. -webkit-transform: scaleY(0.5);
  588. transform: scaleY(0.5);
  589. bottom: 0;
  590. right: 0;
  591. left: 0;
  592. }
  593. .tui-date__picker-body {
  594. width: 100%;
  595. /* height: 520rpx; */
  596. overflow: hidden;
  597. }
  598. .tui-date__column-item {
  599. display: flex;
  600. align-items: center;
  601. justify-content: center;
  602. font-size: 36rpx;
  603. color: #333;
  604. }
  605. .tui-font-size_32 {
  606. font-size: 32rpx !important;
  607. }
  608. .tui-date__unit-text {
  609. font-size: 24rpx !important;
  610. padding-left: 8rpx;
  611. }
  612. .tui-btn-picker {
  613. padding: 16rpx;
  614. box-sizing: border-box;
  615. text-align: center;
  616. text-decoration: none;
  617. flex-shrink: 0;
  618. /* #ifdef H5 */
  619. cursor: pointer;
  620. /* #endif */
  621. }
  622. .tui-opacity {
  623. opacity: 0.5;
  624. }
  625. .tui-pickerdate__title {
  626. white-space: nowrap;
  627. overflow: hidden;
  628. text-overflow: ellipsis;
  629. flex: 1;
  630. padding: 0 30rpx;
  631. box-sizing: border-box;
  632. text-align: center;
  633. }
  634. </style>