index.js 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322
  1. import { use, range } from '../utils';
  2. import Picker from '../picker';
  3. import { pickerProps } from '../picker/shared';
  4. import {
  5. times,
  6. padZero,
  7. isValidDate,
  8. getTrueValue,
  9. getMonthEndDay
  10. } from './utils';
  11. const [sfc, bem] = use('datetime-picker');
  12. const currentYear = new Date().getFullYear();
  13. export default sfc({
  14. props: {
  15. ...pickerProps,
  16. value: null,
  17. minHour: Number,
  18. minMinute: Number,
  19. type: {
  20. type: String,
  21. default: 'datetime'
  22. },
  23. showToolbar: {
  24. type: Boolean,
  25. default: true
  26. },
  27. format: {
  28. type: String,
  29. default: 'YYYY.MM.DD HH时 mm分'
  30. },
  31. formatter: {
  32. type: Function,
  33. default: (type, value) => value
  34. },
  35. minDate: {
  36. type: Date,
  37. default: () => new Date(currentYear - 10, 0, 1),
  38. validator: isValidDate
  39. },
  40. maxDate: {
  41. type: Date,
  42. default: () => new Date(currentYear + 10, 11, 31),
  43. validator: isValidDate
  44. },
  45. maxHour: {
  46. type: Number,
  47. default: 23
  48. },
  49. maxMinute: {
  50. type: Number,
  51. default: 59
  52. }
  53. },
  54. data() {
  55. return {
  56. innerValue: this.correctValue(this.value)
  57. };
  58. },
  59. watch: {
  60. value(val) {
  61. val = this.correctValue(val);
  62. const isEqual =
  63. this.type === 'time'
  64. ? val === this.innerValue
  65. : val.valueOf() === this.innerValue.valueOf();
  66. if (!isEqual) {
  67. this.innerValue = val;
  68. if (this.type === 'time') {
  69. this.updateColumnValue(val);
  70. }
  71. }
  72. },
  73. innerValue(val) {
  74. this.$emit('input', val);
  75. },
  76. columns() {
  77. this.updateColumnValue(this.innerValue);
  78. }
  79. },
  80. computed: {
  81. ranges() {
  82. if (this.type === 'time') {
  83. return [
  84. {
  85. type: 'hour',
  86. range: [this.minHour, this.maxHour]
  87. },
  88. {
  89. type: 'minute',
  90. range: [this.minMinute, this.maxMinute]
  91. }
  92. ];
  93. }
  94. const {
  95. maxYear,
  96. maxDate,
  97. maxMonth,
  98. maxHour,
  99. maxMinute
  100. } = this.getBoundary('max', this.innerValue);
  101. const {
  102. minYear,
  103. minDate,
  104. minMonth,
  105. minHour,
  106. minMinute
  107. } = this.getBoundary('min', this.innerValue);
  108. const result = [
  109. {
  110. type: 'year',
  111. range: [minYear, maxYear]
  112. },
  113. {
  114. type: 'month',
  115. range: [minMonth, maxMonth]
  116. },
  117. {
  118. type: 'day',
  119. range: [minDate, maxDate]
  120. },
  121. {
  122. type: 'hour',
  123. range: [minHour, maxHour]
  124. },
  125. {
  126. type: 'minute',
  127. range: [minMinute, maxMinute]
  128. }
  129. ];
  130. if (this.type === 'date') result.splice(3, 2);
  131. if (this.type === 'year-month') result.splice(2, 3);
  132. return result;
  133. },
  134. columns() {
  135. const results = this.ranges.map(({ type, range: rangeArr }) => {
  136. const values = times(rangeArr[1] - rangeArr[0] + 1, index => {
  137. let value = rangeArr[0] + index;
  138. value = value < 10 ? `0${value}` : `${value}`;
  139. return this.formatter(type, value);
  140. });
  141. return {
  142. values
  143. };
  144. });
  145. return results;
  146. }
  147. },
  148. mounted() {
  149. this.updateColumnValue(this.innerValue);
  150. },
  151. methods: {
  152. correctValue(value) {
  153. // validate value
  154. const isDateType = this.type !== 'time';
  155. if (isDateType && !isValidDate(value)) {
  156. value = this.minDate;
  157. } else if (!value) {
  158. const { minHour } = this;
  159. value = `${minHour > 10 ? minHour : '0' + minHour}:00`;
  160. }
  161. // time type
  162. if (!isDateType) {
  163. let [hour, minute] = value.split(':');
  164. hour = padZero(range(hour, this.minHour, this.maxHour));
  165. minute = padZero(range(minute, this.minMinute, this.maxMinute));
  166. return `${hour}:${minute}`;
  167. }
  168. // date type
  169. value = Math.max(value, this.minDate.getTime());
  170. value = Math.min(value, this.maxDate.getTime());
  171. return new Date(value);
  172. },
  173. getBoundary(type, value) {
  174. const boundary = this[`${type}Date`];
  175. const year = boundary.getFullYear();
  176. let month = 1;
  177. let date = 1;
  178. let hour = 0;
  179. let minute = 0;
  180. if (type === 'max') {
  181. month = 12;
  182. date = getMonthEndDay(value.getFullYear(), value.getMonth() + 1);
  183. hour = 23;
  184. minute = 59;
  185. }
  186. if (value.getFullYear() === year) {
  187. month = boundary.getMonth() + 1;
  188. if (value.getMonth() + 1 === month) {
  189. date = boundary.getDate();
  190. if (value.getDate() === date) {
  191. hour = boundary.getHours();
  192. if (value.getHours() === hour) {
  193. minute = boundary.getMinutes();
  194. }
  195. }
  196. }
  197. }
  198. return {
  199. [`${type}Year`]: year,
  200. [`${type}Month`]: month,
  201. [`${type}Date`]: date,
  202. [`${type}Hour`]: hour,
  203. [`${type}Minute`]: minute
  204. };
  205. },
  206. onConfirm() {
  207. this.$emit('confirm', this.innerValue);
  208. },
  209. onChange(picker) {
  210. let value;
  211. if (this.type === 'time') {
  212. const indexes = picker.getIndexes();
  213. value = `${indexes[0] + this.minHour}:${indexes[1] + this.minMinute}`;
  214. } else {
  215. const values = picker.getValues();
  216. const year = getTrueValue(values[0]);
  217. const month = getTrueValue(values[1]);
  218. const maxDate = getMonthEndDay(year, month);
  219. let date = getTrueValue(values[2]);
  220. if (this.type === 'year-month') {
  221. date = 1;
  222. }
  223. date = date > maxDate ? maxDate : date;
  224. let hour = 0;
  225. let minute = 0;
  226. if (this.type === 'datetime') {
  227. hour = getTrueValue(values[3]);
  228. minute = getTrueValue(values[4]);
  229. }
  230. value = new Date(year, month - 1, date, hour, minute);
  231. }
  232. this.innerValue = this.correctValue(value);
  233. this.$nextTick(() => {
  234. this.$nextTick(() => {
  235. this.$emit('change', picker);
  236. });
  237. });
  238. },
  239. updateColumnValue(value) {
  240. let values = [];
  241. const { formatter } = this;
  242. if (this.type === 'time') {
  243. const pair = value.split(':');
  244. values = [formatter('hour', pair[0]), formatter('minute', pair[1])];
  245. } else {
  246. values = [
  247. formatter('year', `${value.getFullYear()}`),
  248. formatter('month', padZero(value.getMonth() + 1)),
  249. formatter('day', padZero(value.getDate()))
  250. ];
  251. if (this.type === 'datetime') {
  252. values.push(
  253. formatter('hour', padZero(value.getHours())),
  254. formatter('minute', padZero(value.getMinutes()))
  255. );
  256. }
  257. if (this.type === 'year-month') {
  258. values = values.slice(0, 2);
  259. }
  260. }
  261. this.$nextTick(() => {
  262. this.$refs.picker.setValues(values);
  263. });
  264. }
  265. },
  266. render(h) {
  267. const props = {};
  268. Object.keys(pickerProps).forEach(key => {
  269. props[key] = this[key];
  270. });
  271. return (
  272. <Picker
  273. class={bem()}
  274. ref="picker"
  275. columns={this.columns}
  276. onChange={this.onChange}
  277. onConfirm={this.onConfirm}
  278. onCancel={() => {
  279. this.$emit('cancel');
  280. }}
  281. {...{ props }}
  282. />
  283. );
  284. }
  285. });