date-time-picker.vue 16 KB

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