tui-datetime.vue 13 KB

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