babe498c085649b7b554ad15c9907e82a40ee12f8ac37db7424c8e0fab7cc538dd459e666a50e7edb40b99988158d1f4d02827e83496e2555156a38c88eede 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280
  1. <template>
  2. <div class="el-calendar">
  3. <div class="el-calendar__header">
  4. <div class="el-calendar__title">
  5. {{ i18nDate }}
  6. </div>
  7. <div
  8. class="el-calendar__button-group"
  9. v-if="validatedRange.length === 0">
  10. <el-button-group>
  11. <el-button
  12. type="plain"
  13. size="mini"
  14. @click="selectDate('prev-month')">
  15. {{ t('el.datepicker.prevMonth') }}
  16. </el-button>
  17. <el-button
  18. type="plain"
  19. size="mini"
  20. @click="selectDate('today')">
  21. {{ t('el.datepicker.today') }}
  22. </el-button>
  23. <el-button
  24. type="plain"
  25. size="mini"
  26. @click="selectDate('next-month')">
  27. {{ t('el.datepicker.nextMonth') }}
  28. </el-button>
  29. </el-button-group>
  30. </div>
  31. </div>
  32. <div
  33. class="el-calendar__body"
  34. v-if="validatedRange.length === 0"
  35. key="no-range">
  36. <date-table
  37. :date="date"
  38. :selected-day="realSelectedDay"
  39. :first-day-of-week="realFirstDayOfWeek"
  40. @pick="pickDay" />
  41. </div>
  42. <div
  43. v-else
  44. class="el-calendar__body"
  45. key="has-range">
  46. <date-table
  47. v-for="(range, index) in validatedRange"
  48. :key="index"
  49. :date="range[0]"
  50. :selected-day="realSelectedDay"
  51. :range="range"
  52. :hide-header="index !== 0"
  53. :first-day-of-week="realFirstDayOfWeek"
  54. @pick="pickDay" />
  55. </div>
  56. </div>
  57. </template>
  58. <script>
  59. import Locale from 'element-ui/src/mixins/locale';
  60. import fecha from 'element-ui/src/utils/date';
  61. import ElButton from 'element-ui/packages/button';
  62. import ElButtonGroup from 'element-ui/packages/button-group';
  63. import DateTable from './date-table';
  64. import { validateRangeInOneMonth } from 'element-ui/src/utils/date-util';
  65. const validTypes = ['prev-month', 'today', 'next-month'];
  66. const weekDays = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'];
  67. const oneDay = 86400000;
  68. export default {
  69. name: 'ElCalendar',
  70. mixins: [Locale],
  71. components: {
  72. DateTable,
  73. ElButton,
  74. ElButtonGroup
  75. },
  76. props: {
  77. value: [Date, String, Number],
  78. range: {
  79. type: Array,
  80. validator(range) {
  81. if (Array.isArray(range)) {
  82. return range.length === 2 && range.every(
  83. item => typeof item === 'string' ||
  84. typeof item === 'number' ||
  85. item instanceof Date);
  86. } else {
  87. return true;
  88. }
  89. }
  90. },
  91. firstDayOfWeek: {
  92. type: Number,
  93. default: 1
  94. }
  95. },
  96. provide() {
  97. return {
  98. elCalendar: this
  99. };
  100. },
  101. methods: {
  102. pickDay(day) {
  103. this.realSelectedDay = day;
  104. },
  105. selectDate(type) {
  106. if (validTypes.indexOf(type) === -1) {
  107. throw new Error(`invalid type ${type}`);
  108. }
  109. let day = '';
  110. if (type === 'prev-month') {
  111. day = `${this.prevMonthDatePrefix}-01`;
  112. } else if (type === 'next-month') {
  113. day = `${this.nextMonthDatePrefix}-01`;
  114. } else {
  115. day = this.formatedToday;
  116. }
  117. if (day === this.formatedDate) return;
  118. this.pickDay(day);
  119. },
  120. toDate(val) {
  121. if (!val) {
  122. throw new Error('invalid val');
  123. }
  124. return val instanceof Date ? val : new Date(val);
  125. },
  126. rangeValidator(date, isStart) {
  127. const firstDayOfWeek = this.realFirstDayOfWeek;
  128. const expected = isStart ? firstDayOfWeek : (firstDayOfWeek === 0 ? 6 : firstDayOfWeek - 1);
  129. const message = `${isStart ? 'start' : 'end'} of range should be ${weekDays[expected]}.`;
  130. if (date.getDay() !== expected) {
  131. console.warn('[ElementCalendar]', message, 'Invalid range will be ignored.');
  132. return false;
  133. }
  134. return true;
  135. }
  136. },
  137. computed: {
  138. prevMonthDatePrefix() {
  139. const temp = new Date(this.date.getTime());
  140. temp.setDate(0);
  141. return fecha.format(temp, 'yyyy-MM');
  142. },
  143. curMonthDatePrefix() {
  144. return fecha.format(this.date, 'yyyy-MM');
  145. },
  146. nextMonthDatePrefix() {
  147. const temp = new Date(this.date.getFullYear(), this.date.getMonth() + 1, 1);
  148. return fecha.format(temp, 'yyyy-MM');
  149. },
  150. formatedDate() {
  151. return fecha.format(this.date, 'yyyy-MM-dd');
  152. },
  153. i18nDate() {
  154. const year = this.date.getFullYear();
  155. const month = this.date.getMonth() + 1;
  156. return `${year} ${this.t('el.datepicker.year')} ${this.t('el.datepicker.month' + month)}`;
  157. },
  158. formatedToday() {
  159. return fecha.format(this.now, 'yyyy-MM-dd');
  160. },
  161. realSelectedDay: {
  162. get() {
  163. if (!this.value) return this.selectedDay;
  164. return this.formatedDate;
  165. },
  166. set(val) {
  167. this.selectedDay = val;
  168. const date = new Date(val);
  169. this.$emit('input', date);
  170. }
  171. },
  172. date() {
  173. if (!this.value) {
  174. if (this.realSelectedDay) {
  175. const d = this.selectedDay.split('-');
  176. return new Date(d[0], d[1] - 1, d[2]);
  177. } else if (this.validatedRange.length) {
  178. return this.validatedRange[0][0];
  179. }
  180. return this.now;
  181. } else {
  182. return this.toDate(this.value);
  183. }
  184. },
  185. // if range is valid, we get a two-digit array
  186. validatedRange() {
  187. let range = this.range;
  188. if (!range) return [];
  189. range = range.reduce((prev, val, index) => {
  190. const date = this.toDate(val);
  191. if (this.rangeValidator(date, index === 0)) {
  192. prev = prev.concat(date);
  193. }
  194. return prev;
  195. }, []);
  196. if (range.length === 2) {
  197. const [start, end] = range;
  198. if (start > end) {
  199. console.warn('[ElementCalendar]end time should be greater than start time');
  200. return [];
  201. }
  202. // start time and end time in one month
  203. if (validateRangeInOneMonth(start, end)) {
  204. return [
  205. [start, end]
  206. ];
  207. }
  208. const data = [];
  209. let startDay = new Date(start.getFullYear(), start.getMonth() + 1, 1);
  210. const lastDay = this.toDate(startDay.getTime() - oneDay);
  211. if (!validateRangeInOneMonth(startDay, end)) {
  212. console.warn('[ElementCalendar]start time and end time interval must not exceed two months');
  213. return [];
  214. }
  215. // 第一个月的时间范围
  216. data.push([
  217. start,
  218. lastDay
  219. ]);
  220. // 下一月的时间范围,需要计算一下该月的第一个周起始日
  221. const firstDayOfWeek = this.realFirstDayOfWeek;
  222. const nextMontFirstDay = startDay.getDay();
  223. let interval = 0;
  224. if (nextMontFirstDay !== firstDayOfWeek) {
  225. if (firstDayOfWeek === 0) {
  226. interval = 7 - nextMontFirstDay;
  227. } else {
  228. interval = firstDayOfWeek - nextMontFirstDay;
  229. interval = interval > 0 ? interval : 7 + interval;
  230. }
  231. }
  232. startDay = this.toDate(startDay.getTime() + interval * oneDay);
  233. if (startDay.getDate() < end.getDate()) {
  234. data.push([
  235. startDay,
  236. end
  237. ]);
  238. }
  239. return data;
  240. }
  241. return [];
  242. },
  243. realFirstDayOfWeek() {
  244. if (this.firstDayOfWeek < 1 || this.firstDayOfWeek > 6) {
  245. return 0;
  246. }
  247. return Math.floor(this.firstDayOfWeek);
  248. }
  249. },
  250. data() {
  251. return {
  252. selectedDay: '',
  253. now: new Date()
  254. };
  255. }
  256. };
  257. </script>