date-time-picker.vue 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562
  1. <template>
  2. <view class="date-time">
  3. <picker mode="multiSelector" :range="range" range-key="text" @change="change" @columnchange="columnchange" :value="value" :disabled="disabled">
  4. <view class="content" :class="{ placeholder: !dateStr }" :style="{height: pickHeight}">
  5. <text>{{ dateStr ? dateStr : placeholder }}</text>
  6. <slot></slot>
  7. </view>
  8. </picker>
  9. </view>
  10. </template>
  11. <script>
  12. /**
  13. * 日期时间选择器
  14. */
  15. import utils from "./utils.js";
  16. import moment from "moment";
  17. export default {
  18. /**
  19. * 数据
  20. */
  21. props: {
  22. /**
  23. * picker类型 start: 开始时间;end:结束时间
  24. */
  25. pickType: {
  26. type: String,
  27. default: ""
  28. },
  29. // 是否禁用
  30. disabled: {
  31. type: Boolean,
  32. default: false
  33. },
  34. // 占位符
  35. placeholder: {
  36. type: String,
  37. default: "请选择日期时间"
  38. },
  39. // 表示有效日期时间范围的开始,
  40. // 字符串格式为 "YYYY-MM-DD hh:mm"
  41. start: {
  42. type: String,
  43. default: "1970-01-01 00:00"
  44. },
  45. // 表示有效日期时间范围的结束
  46. // 字符串格式为 "YYYY-MM-DD hh:mm"
  47. end: {
  48. type: String,
  49. default: "2300-01-01 00:00"
  50. },
  51. // 表示选择器的粒度,有效值:year | month | day | hour | minute
  52. fields: {
  53. type: String,
  54. default: "minute"
  55. },
  56. // 默认值
  57. // 字符串格式为 "YYYY-MM-DD hh:mm"
  58. defaultValue: {
  59. type: String,
  60. default: ""
  61. },
  62. // 高度
  63. height: {
  64. type: Number,
  65. default: 40
  66. },
  67. // 下标
  68. pickIndex: {
  69. type: Number,
  70. default: 0
  71. },
  72. },
  73. /**
  74. * 数据
  75. */
  76. data() {
  77. return {
  78. range: [],
  79. value: [],
  80. dateStr: "", // 最终显示的字符串
  81. dtStart: null, // 有效范围开始
  82. dtEnd: null // 有效范围结束
  83. };
  84. },
  85. /**
  86. * 监听数据
  87. */
  88. watch: {
  89. // 默认值
  90. defaultValue() {
  91. // 设置默认值
  92. this.setDefaultValue();
  93. },
  94. start(newVal, oldVal){
  95. if(newVal != oldVal){
  96. this.setDisabled()
  97. }
  98. },
  99. end(newVal, oldVal){
  100. if(newVal != oldVal){
  101. this.setDisabled()
  102. }
  103. }
  104. },
  105. computed: {
  106. pickHeight() {
  107. return (this.height * 750 / 1200).toFixed(2) + 'rpx';
  108. }
  109. },
  110. /**
  111. * 组件初次加载完成
  112. */
  113. mounted() {
  114. this.setDisabled();
  115. // 设置默认值
  116. this.setDefaultValue();
  117. },
  118. /**
  119. * 方法
  120. */
  121. methods: {
  122. // 设置有效日期开始和结束
  123. setDisabled() {
  124. let start = this.start;
  125. let end = this.end;
  126. // 验证是否是有效的开始和结束日期
  127. if (!utils.isString(this.start)) {
  128. // console.log('开始日期需为String类型,格式为 "YYYY-MM-DD hh:mm"');
  129. start = "1970-1-1 00:00";
  130. }
  131. if (!utils.isString(this.end)) {
  132. // console.log('结束日期需为String类型,格式为 "YYYY-MM-DD hh:mm"');
  133. end = "2300-1-1 00:00";
  134. }
  135. // 将开始日期和结束日期转为 Date
  136. let dtStart = utils.formatDate(start).dt;
  137. let dtEnd = utils.formatDate(end).dt;
  138. // 判断有效日期结束是否大于有效日期开始,如果不是,则将有效日期结束修改为有效日期开始往后300年
  139. if (dtEnd <= dtStart) {
  140. dtEnd = utils.formatDate(start).dt;
  141. dtEnd.setFullYear(dtStart.getFullYear() + 300);
  142. dtEnd.setDate(dtEnd.getDate() - 1);
  143. }
  144. // 更新开始日期和结束日期
  145. this.dtStart = dtStart;
  146. this.dtEnd = dtEnd;
  147. },
  148. /**
  149. * 确认选择
  150. */
  151. change(event) {
  152. let year, month, day, hour, minute;
  153. if (this.fields == "year") {
  154. year = this.range[0][this.value[0]].number; // 年
  155. let dtStr = `${year}`;
  156. this.setDateStr(dtStr);
  157. this.$emit("change", utils.formatDate(dtStr), this.pickType, this.pickIndex);
  158. return;
  159. } else if (this.fields == "month") {
  160. year = this.range[0][this.value[0]].number; // 年
  161. month = this.range[1][this.value[1]].number; // 月
  162. let dtStr = `${year}-${month}`;
  163. this.setDateStr(dtStr);
  164. this.$emit("change", utils.formatDate(dtStr), this.pickType, this.pickIndex);
  165. return;
  166. } else if (this.fields == "day") {
  167. year = this.range[0][this.value[0]].number; // 年
  168. month = this.range[1][this.value[1]].number; // 月
  169. day = this.range[2][this.value[2]].number; // 日
  170. let dtStr = `${year}-${month}-${day}`;
  171. this.setDateStr(dtStr);
  172. this.$emit("change", utils.formatDate(dtStr), this.pickType, this.pickIndex);
  173. return;
  174. } else if (this.fields == "hour") {
  175. year = this.range[0][this.value[0]].number; // 年
  176. month = this.range[1][this.value[1]].number; // 月
  177. day = this.range[2][this.value[2]].number; // 日
  178. hour = this.range[3][this.value[3]].number; // 时
  179. day = this.range[2][this.value[2]].number; // 日
  180. let dtStr = `${year}-${month}-${day} ${hour}`;
  181. this.setDateStr(dtStr);
  182. this.$emit("change", utils.formatDate(dtStr), this.pickType, this.pickIndex);
  183. return;
  184. } else if (this.fields == "minute") {
  185. year = this.range[0][this.value[0]].number; // 年
  186. month = this.range[1][this.value[1]].number; // 月
  187. day = this.range[2][this.value[2]].number; // 日
  188. hour = this.range[3][this.value[3]].number; // 时
  189. minute = this.range[4][this.value[4]].number; // 分
  190. let dtStr = `${year}-${month}-${day} ${hour}:${minute}`;
  191. this.setDateStr(dtStr);
  192. this.$emit("change", utils.formatDate(dtStr), this.pickType, this.pickIndex);
  193. return;
  194. }
  195. },
  196. /**
  197. * 设置显示的值
  198. * @param {Date|String} date 日期字符串或日期对象
  199. */
  200. setDateStr(date) {
  201. let dt = utils.formatDate(date);
  202. if (this.fields == "year") {
  203. this.dateStr = `${dt.YYYY}`;
  204. return;
  205. }
  206. if (this.fields == "month") {
  207. this.dateStr = `${dt.YYYY}-${dt.MM}`;
  208. return;
  209. }
  210. if (this.fields == "day") {
  211. this.dateStr = `${dt.YYYY}-${dt.MM}-${dt.DD}`;
  212. return;
  213. }
  214. if (this.fields == "hour") {
  215. this.dateStr = `${dt.YYYY}-${dt.MM}-${dt.DD} ${dt.hh}`;
  216. return;
  217. }
  218. this.dateStr = `${dt.YYYY}-${dt.MM}-${dt.DD} ${dt.hh}:${dt.mm}`;
  219. },
  220. /**
  221. * 设置年数据
  222. */
  223. setYearData() {
  224. // 有效日期
  225. let yearStart = this.dtStart.getFullYear();
  226. let yearEnd = this.dtEnd.getFullYear();
  227. // 年
  228. let years = [];
  229. for (let year = yearStart; year <= yearEnd; year++) {
  230. let item = {
  231. number: year,
  232. text: `${year}年`
  233. };
  234. years.push(item);
  235. }
  236. this.range.splice(0, 1, years);
  237. },
  238. /**
  239. * 设置月数据
  240. * @param {Number} year 年
  241. */
  242. setMonthData(year) {
  243. // 有效日期
  244. let yearStart = this.dtStart.getFullYear();
  245. let monthStart = this.dtStart.getMonth() + 1;
  246. let yearEnd = this.dtEnd.getFullYear();
  247. let monthEnd = this.dtEnd.getMonth() + 1;
  248. // 月
  249. let months = [];
  250. let monthStartIndex = year == yearStart ? monthStart : 1;
  251. let monthEndIndex = year == yearEnd ? monthEnd : 12;
  252. for (let month = monthStartIndex; month <= monthEndIndex; month++) {
  253. let item = {
  254. number: month,
  255. text: `${month}月`
  256. };
  257. months.push(item);
  258. }
  259. this.range.splice(1, 1, months);
  260. },
  261. /**
  262. * 设置日数据
  263. * @param {Number} year 年
  264. * @param {Number} month 月
  265. */
  266. setDayData(year, month) {
  267. // 有效日期
  268. let yearStart = this.dtStart.getFullYear();
  269. let monthStart = this.dtStart.getMonth() + 1;
  270. let dayStart = this.dtStart.getDate();
  271. let yearEnd = this.dtEnd.getFullYear();
  272. let monthEnd = this.dtEnd.getMonth() + 1;
  273. let dayEnd = this.dtEnd.getDate();
  274. // 日
  275. let days = [];
  276. let dayStartIndex =
  277. year == yearStart && month == monthStart ? dayStart : 1;
  278. let dayEndIndex;
  279. if (year == yearEnd && month == monthEnd) {
  280. dayEndIndex = dayEnd;
  281. } else {
  282. dayEndIndex = new Date(year, month, 0).getDate();
  283. }
  284. for (let day = dayStartIndex; day <= dayEndIndex; day++) {
  285. let item = {
  286. number: day,
  287. text: `${day}日`
  288. };
  289. days.push(item);
  290. }
  291. this.range.splice(2, 1, days);
  292. },
  293. /**
  294. * 设置时数据
  295. * @param {Number} year 年
  296. * @param {Number} month 月
  297. * @param {Number} day 日
  298. */
  299. setHourData(year, month, day) {
  300. // 有效日期
  301. let yearStart = this.dtStart.getFullYear();
  302. let monthStart = this.dtStart.getMonth() + 1;
  303. let dayStart = this.dtStart.getDate();
  304. let hourStart = this.dtStart.getHours();
  305. let yearEnd = this.dtEnd.getFullYear();
  306. let monthEnd = this.dtEnd.getMonth() + 1;
  307. let dayEnd = this.dtEnd.getDate();
  308. let hourEnd = this.dtEnd.getHours();
  309. // 时
  310. let hours = [];
  311. let hourStartIndex =
  312. year == yearStart && month == monthStart && day == dayStart
  313. ? hourStart
  314. : 0;
  315. let hourEndIndex =
  316. year == yearEnd && month == monthEnd && day == dayEnd ? hourEnd : 23;
  317. for (let hour = hourStartIndex; hour <= hourEndIndex; hour++) {
  318. let item = {
  319. number: hour,
  320. text: `${hour}时`
  321. };
  322. hours.push(item);
  323. }
  324. this.range.splice(3, 1, hours);
  325. },
  326. /**
  327. * 设置分数据
  328. * @param {Number} year 年
  329. * @param {Number} month 月
  330. * @param {Number} day 日
  331. * @param {Number} hour 时
  332. */
  333. setMinuteData(year, month, day, hour) {
  334. // 有效日期
  335. let yearStart = this.dtStart.getFullYear();
  336. let monthStart = this.dtStart.getMonth() + 1;
  337. let dayStart = this.dtStart.getDate();
  338. let hourStart = this.dtStart.getHours();
  339. let minuteStart = this.dtStart.getMinutes();
  340. let yearEnd = this.dtEnd.getFullYear();
  341. let monthEnd = this.dtEnd.getMonth() + 1;
  342. let dayEnd = this.dtEnd.getDate();
  343. let hourEnd = this.dtEnd.getHours();
  344. let minuteEnd = this.dtEnd.getMinutes();
  345. // 分
  346. let minutes = [];
  347. let minuteStartIndex =
  348. year == yearStart &&
  349. month == monthStart &&
  350. day == dayStart &&
  351. hour == hourStart
  352. ? minuteStart
  353. : 0;
  354. let minuteEndIndex =
  355. year == yearEnd && month == monthEnd && day == dayEnd && hour == hourEnd
  356. ? minuteEnd
  357. : 59;
  358. for (let minute = minuteStartIndex; minute <= minuteEndIndex; minute++) {
  359. let item = {
  360. number: minute,
  361. text: `${minute}分`
  362. };
  363. minutes.push(item);
  364. }
  365. this.range.splice(4, 1, minutes);
  366. },
  367. /**
  368. * 设置默认值
  369. */
  370. setDefaultValue() {
  371. // 默认日期
  372. let dtDefault;
  373. // 开始日期和结束日期
  374. let dtStart = this.dtStart;
  375. let dtEnd = this.dtEnd;
  376. // 判断是否传了默认日期
  377. // 传了默认日期,格式化默认日期为日期对象
  378. if (this.defaultValue) {
  379. dtDefault = utils.formatDate(this.defaultValue).dt;
  380. } else {// 如果没有传默认日期,将默认日期设置为当前日期
  381. dtDefault = new Date();
  382. }
  383. // 如果默认日期不在有效日期范围内,设置默认日期为有效日期开始值
  384. if (dtDefault < dtStart || dtDefault > dtEnd) {
  385. dtDefault = dtStart;
  386. }
  387. // 更新 dateStr
  388. if (this.defaultValue) this.setDateStr(dtDefault);
  389. // 默认值相关数据
  390. let dfYear = dtDefault.getFullYear();
  391. let dfMonth = dtDefault.getMonth() + 1;
  392. let dfDay = dtDefault.getDate();
  393. let dfHour = dtDefault.getHours();
  394. let dfMinute = dtDefault.getMinutes();
  395. // 设置年数据
  396. this.setYearData();
  397. // 设置 Year 这一列的 value 值
  398. let yearIndex = this.range[0].findIndex(year => {
  399. return dfYear == year.number;
  400. });
  401. this.value.splice(0, 1, yearIndex >= 0 ? yearIndex : 0);
  402. // 设置月数据
  403. if (this.fields == "year") return;
  404. this.setMonthData(dfYear);
  405. // 设置 Month 这一列的 value 值
  406. let monthIndex = this.range[1].findIndex(month => {
  407. return dfMonth == month.number;
  408. });
  409. this.value.splice(1, 1, monthIndex >= 0 ? monthIndex : 0);
  410. // 设置日数据
  411. if (this.fields == "month") return;
  412. this.setDayData(dfYear, dfMonth);
  413. // 设置 Day 这一列的 value 值
  414. let dayIndex = this.range[2].findIndex(day => {
  415. return dfDay == day.number;
  416. });
  417. this.value.splice(2, 1, dayIndex >= 0 ? dayIndex : 0);
  418. // 设置时数据
  419. if (this.fields == "day") return;
  420. this.setHourData(dfYear, dfMonth, dfDay);
  421. // 设置 Hour 这一列的 value 值
  422. let hourIndex = this.range[3].findIndex(hour => {
  423. return dfHour == hour.number;
  424. });
  425. this.value.splice(3, 1, hourIndex >= 0 ? hourIndex : 0);
  426. // 设置分数据
  427. if (this.fields == "hour") return;
  428. this.setMinuteData(dfYear, dfMonth, dfDay, dfHour);
  429. // 设置 Minute 这一列的 value 值
  430. let minuteIndex = this.range[4].findIndex(minute => {
  431. return dfMinute == minute.number;
  432. });
  433. this.value.splice(4, 1, minuteIndex >= 0 ? minuteIndex : 0);
  434. },
  435. /**
  436. * 某一列的值改变时触发
  437. * @param {Number} event.detail.column 表示改变了第几列(下标从0开始)
  438. * @param {Number} event.detail.value 表示变更值的下标
  439. */
  440. columnchange(event) {
  441. let columnIndex = event.detail.column; // 改变的列的下标
  442. let valueIndex = event.detail.value; // 变更值的下标
  443. // 更新改变列的 value
  444. this.value.splice(columnIndex, 1, valueIndex);
  445. // 改变年要更新月数据
  446. if (this.fields == "year") return;
  447. if (columnIndex == 0) {
  448. // 当前选择的月
  449. let monthBeforeUpdate = this.range[1][this.value[1]];
  450. // 更新月数据
  451. this.setMonthData(this.range[0][this.value[0]].number);
  452. // 更新 Month Value
  453. let monthIndex = this.range[1].findIndex(month => {
  454. return month.number == monthBeforeUpdate.number;
  455. });
  456. this.value.splice(1, 1, monthIndex >= 0 ? monthIndex : 0);
  457. }
  458. // 改变年、月都要更新日数据
  459. if (this.fields == "month") return;
  460. if (columnIndex == 0 || columnIndex == 1) {
  461. // 当前选择的日
  462. let dayBeforeUpdate = this.range[2][this.value[2]];
  463. // 更新日数据
  464. this.setDayData(
  465. this.range[0][this.value[0]].number,
  466. this.range[1][this.value[1]].number
  467. );
  468. // 更新 Day Value
  469. let dayIndex = this.range[2].findIndex(day => {
  470. return day.number == dayBeforeUpdate.number;
  471. });
  472. this.value.splice(2, 1, dayIndex >= 0 ? dayIndex : 0);
  473. }
  474. // 改变年、月、日都要更新时数据
  475. if (this.fields == "day") return;
  476. if (columnIndex == 0 || columnIndex == 1 || columnIndex == 2) {
  477. // 当前选择的时
  478. let hourBeforeUpdate = this.range[3][this.value[3]];
  479. // 更新时数据
  480. this.setHourData(
  481. this.range[0][this.value[0]].number,
  482. this.range[1][this.value[1]].number,
  483. this.range[2][this.value[2]].number
  484. );
  485. // 更新 Hour Value
  486. let hourIndex = this.range[3].findIndex(hour => {
  487. return hour.number == hourBeforeUpdate.number;
  488. });
  489. this.value.splice(3, 1, hourIndex >= 0 ? hourIndex : 0);
  490. }
  491. // 当前选择的分
  492. if (this.fields == "hour") return;
  493. let minuteBeforeUpdate = this.range[4][this.value[4]];
  494. // 更新分数据
  495. this.setMinuteData(
  496. this.range[0][this.value[0]].number,
  497. this.range[1][this.value[1]].number,
  498. this.range[2][this.value[2]].number,
  499. this.range[3][this.value[3]].number
  500. );
  501. // 更新 Minute Value
  502. let minuteIndex = this.range[4].findIndex(minute => {
  503. return minute.number == minuteBeforeUpdate.number;
  504. });
  505. this.value.splice(4, 1, minuteIndex >= 0 ? minuteIndex : 0);
  506. }
  507. }
  508. };
  509. </script>
  510. <style lang="less" scoped>
  511. .date-time {
  512. width: 100%;
  513. height: 100%;
  514. .content {
  515. padding: 0 3rpx;
  516. display: flex;
  517. align-items: center;
  518. justify-content: space-between;
  519. height: 25rpx;
  520. }
  521. .placeholder {
  522. color: #a1a7b3;
  523. }
  524. }
  525. </style>