week.js 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601
  1. import Day from './day'
  2. import WxData from './wxData'
  3. import Render from './render'
  4. import CalendarConfig from './config'
  5. import convertSolarLunar from './convertSolarLunar'
  6. import { GetDate, Logger, getDateTimeStamp } from './utils'
  7. const getDate = new GetDate()
  8. const logger = new Logger()
  9. class WeekMode extends WxData {
  10. constructor(component) {
  11. super(component)
  12. this.Component = component
  13. this.getCalendarConfig = CalendarConfig(this.Component).getCalendarConfig
  14. }
  15. /**
  16. * 周、月视图切换
  17. * @param {string} view 视图 [week, month]
  18. * @param {object} date {year: 2017, month: 11, day: 1}
  19. */
  20. switchWeek(view, date) {
  21. return new Promise((resolve, reject) => {
  22. const config = CalendarConfig(this.Component).getCalendarConfig()
  23. if (config.multi) return logger.warn('多选模式不能切换周月视图')
  24. const { selectedDay = [], curYear, curMonth } = this.getData('calendar')
  25. let currentDate = []
  26. let disableSelected = false
  27. if (!selectedDay.length) {
  28. currentDate = getDate.todayDate()
  29. currentDate.day = currentDate.date
  30. disableSelected = true
  31. // return this.__tipsWhenCanNotSwtich();
  32. } else {
  33. currentDate = selectedDay[0]
  34. }
  35. let selectedDate = date || currentDate
  36. const { year, month } = selectedDate
  37. const notInCurrentMonth = curYear !== year || curMonth !== month
  38. if (view === 'week') {
  39. if (this.Component.weekMode) return
  40. if ((selectedDay.length && notInCurrentMonth) || !selectedDay.length) {
  41. // return this.__tipsWhenCanNotSwtich();
  42. disableSelected = true
  43. selectedDate = {
  44. year: curYear,
  45. month: curMonth,
  46. day: selectedDate.day
  47. }
  48. }
  49. this.Component.weekMode = true
  50. this.setData({
  51. 'calendarConfig.weekMode': true
  52. })
  53. this.jump(selectedDate, disableSelected)
  54. .then(resolve)
  55. .catch(reject)
  56. } else {
  57. this.Component.weekMode = false
  58. this.setData({
  59. 'calendarConfig.weekMode': false
  60. })
  61. const disableSelected =
  62. (selectedDay.length && notInCurrentMonth) || !selectedDay.length
  63. Render(this.Component)
  64. .renderCalendar(curYear, curMonth, selectedDate.day, disableSelected)
  65. .then(resolve)
  66. .catch(reject)
  67. }
  68. })
  69. }
  70. /**
  71. * 更新当前年月
  72. */
  73. updateCurrYearAndMonth(type) {
  74. let { days, curYear, curMonth } = this.getData('calendar')
  75. const { month: firstMonth } = days[0]
  76. const { month: lastMonth } = days[days.length - 1]
  77. const lastDayOfThisMonth = getDate.thisMonthDays(curYear, curMonth)
  78. const lastDayOfThisWeek = days[days.length - 1]
  79. const firstDayOfThisWeek = days[0]
  80. if (
  81. (lastDayOfThisWeek.day + 7 > lastDayOfThisMonth ||
  82. (curMonth === firstMonth && firstMonth !== lastMonth)) &&
  83. type === 'next'
  84. ) {
  85. curMonth = curMonth + 1
  86. if (curMonth > 12) {
  87. curYear = curYear + 1
  88. curMonth = 1
  89. }
  90. } else if (
  91. (+firstDayOfThisWeek.day <= 7 ||
  92. (curMonth === lastMonth && firstMonth !== lastMonth)) &&
  93. type === 'prev'
  94. ) {
  95. curMonth = curMonth - 1
  96. if (curMonth <= 0) {
  97. curYear = curYear - 1
  98. curMonth = 12
  99. }
  100. }
  101. return {
  102. Uyear: curYear,
  103. Umonth: curMonth
  104. }
  105. }
  106. /**
  107. * 计算周视图下当前这一周和当月的最后一天
  108. */
  109. calculateLastDay() {
  110. const { days = [], curYear, curMonth } = this.getData('calendar')
  111. const lastDayInThisWeek = days[days.length - 1].day
  112. const lastDayInThisMonth = getDate.thisMonthDays(curYear, curMonth)
  113. return { lastDayInThisWeek, lastDayInThisMonth }
  114. }
  115. /**
  116. * 计算周视图下当前这一周第一天
  117. */
  118. calculateFirstDay() {
  119. const { days } = this.getData('calendar')
  120. const firstDayInThisWeek = days[0].day
  121. return { firstDayInThisWeek }
  122. }
  123. /**
  124. * 当月第一周所有日期范围
  125. * @param {number} year
  126. * @param {number} month
  127. * @param {boolean} firstDayOfWeekIsMon 每周是否配置为以周一开始
  128. */
  129. firstWeekInMonth(year, month, firstDayOfWeekIsMon) {
  130. let firstDay = getDate.dayOfWeek(year, month, 1)
  131. if (firstDayOfWeekIsMon && firstDay === 0) {
  132. firstDay = 7
  133. }
  134. const [, end] = [0, 7 - firstDay]
  135. let days = this.getData('calendar.days') || []
  136. if (this.Component.weekMode) {
  137. days = Day(this.Component).buildDate(year, month)
  138. }
  139. const daysCut = days.slice(0, firstDayOfWeekIsMon ? end + 1 : end)
  140. return daysCut
  141. }
  142. /**
  143. * 当月最后一周所有日期范围
  144. * @param {number} year
  145. * @param {number} month
  146. * @param {boolean} firstDayOfWeekIsMon 每周是否配置为以周一开始
  147. */
  148. lastWeekInMonth(year, month, firstDayOfWeekIsMon) {
  149. const lastDay = getDate.thisMonthDays(year, month)
  150. const lastDayWeek = getDate.dayOfWeek(year, month, lastDay)
  151. const [start, end] = [lastDay - lastDayWeek, lastDay]
  152. let days = this.getData('calendar.days') || []
  153. if (this.Component.weekMode) {
  154. days = Day(this.Component).buildDate(year, month)
  155. }
  156. const daysCut = days.slice(firstDayOfWeekIsMon ? start : start - 1, end)
  157. return daysCut
  158. }
  159. __getDisableDateTimestamp(config) {
  160. const { date, type } = config.disableMode || {}
  161. let disableDateTimestamp
  162. if (date) {
  163. const t = date.split('-')
  164. if (t.length < 3) {
  165. logger.warn('配置 disableMode.date 格式错误')
  166. return {}
  167. }
  168. disableDateTimestamp = getDateTimeStamp({
  169. year: +t[0],
  170. month: +t[1],
  171. day: +t[2]
  172. })
  173. }
  174. return {
  175. disableDateTimestamp,
  176. disableType: type
  177. }
  178. }
  179. /**
  180. * 渲染日期之前初始化已选日期
  181. * @param {array} dates 当前日期数组
  182. */
  183. initSelectedDay(dates) {
  184. let datesCopy = [...dates]
  185. const { selectedDay = [] } = this.getData('calendar')
  186. const selectedDayStr = selectedDay.map(
  187. item => `${+item.year}-${+item.month}-${+item.day}`
  188. )
  189. const config = this.getCalendarConfig()
  190. const {
  191. disableDateTimestamp,
  192. disableType
  193. } = this.__getDisableDateTimestamp(config)
  194. datesCopy = datesCopy.map(item => {
  195. if (!item) return {}
  196. const dateTimestamp = getDateTimeStamp(item)
  197. let date = { ...item }
  198. if (
  199. selectedDayStr.includes(`${+date.year}-${+date.month}-${+date.day}`)
  200. ) {
  201. date.choosed = true
  202. } else {
  203. date.choosed = false
  204. }
  205. if (
  206. (disableType === 'after' && dateTimestamp > disableDateTimestamp) ||
  207. (disableType === 'before' && dateTimestamp < disableDateTimestamp)
  208. ) {
  209. date.disable = true
  210. }
  211. date = this.__setTodoWhenJump(date, config)
  212. if (config.showLunar) {
  213. date = this.__setSolarLunar(date)
  214. }
  215. if (config.highlightToday) {
  216. date = this.__highlightToday(date)
  217. }
  218. return date
  219. })
  220. return datesCopy
  221. }
  222. /**
  223. * 周视图下设置可选日期范围
  224. * @param {object} days 当前展示的日期
  225. */
  226. setEnableAreaOnWeekMode(dates = []) {
  227. let { enableAreaTimestamp = [], enableDaysTimestamp = [] } = this.getData(
  228. 'calendar'
  229. )
  230. dates.forEach(item => {
  231. const timestamp = getDate
  232. .newDate(item.year, item.month, item.day)
  233. .getTime()
  234. let setDisable = false
  235. if (enableAreaTimestamp.length) {
  236. if (
  237. (+enableAreaTimestamp[0] > +timestamp ||
  238. +timestamp > +enableAreaTimestamp[1]) &&
  239. !enableDaysTimestamp.includes(+timestamp)
  240. ) {
  241. setDisable = true
  242. }
  243. } else if (
  244. enableDaysTimestamp.length &&
  245. !enableDaysTimestamp.includes(+timestamp)
  246. ) {
  247. setDisable = true
  248. }
  249. if (setDisable) {
  250. item.disable = true
  251. item.choosed = false
  252. }
  253. const config = CalendarConfig(this.Component).getCalendarConfig()
  254. const {
  255. disableDateTimestamp,
  256. disableType
  257. } = this.__getDisableDateTimestamp(config)
  258. if (
  259. (disableType === 'before' && timestamp < disableDateTimestamp) ||
  260. (disableType === 'after' && timestamp > disableDateTimestamp)
  261. ) {
  262. item.disable = true
  263. }
  264. })
  265. }
  266. updateYMWhenSwipeCalendarHasSelected(dates) {
  267. const hasSelectedDate = dates.filter(date => date.choosed)
  268. if (hasSelectedDate && hasSelectedDate.length) {
  269. const { year, month } = hasSelectedDate[0]
  270. return {
  271. year,
  272. month
  273. }
  274. }
  275. return {}
  276. }
  277. /**
  278. * 计算下一周的日期
  279. */
  280. calculateNextWeekDays() {
  281. let { lastDayInThisWeek, lastDayInThisMonth } = this.calculateLastDay()
  282. let { curYear, curMonth } = this.getData('calendar')
  283. let days = []
  284. if (lastDayInThisMonth - lastDayInThisWeek >= 7) {
  285. const { Uyear, Umonth } = this.updateCurrYearAndMonth('next')
  286. curYear = Uyear
  287. curMonth = Umonth
  288. for (let i = lastDayInThisWeek + 1; i <= lastDayInThisWeek + 7; i++) {
  289. days.push({
  290. year: curYear,
  291. month: curMonth,
  292. day: i,
  293. week: getDate.dayOfWeek(curYear, curMonth, i)
  294. })
  295. }
  296. } else {
  297. for (let i = lastDayInThisWeek + 1; i <= lastDayInThisMonth; i++) {
  298. days.push({
  299. year: curYear,
  300. month: curMonth,
  301. day: i,
  302. week: getDate.dayOfWeek(curYear, curMonth, i)
  303. })
  304. }
  305. const { Uyear, Umonth } = this.updateCurrYearAndMonth('next')
  306. curYear = Uyear
  307. curMonth = Umonth
  308. for (let i = 1; i <= 7 - (lastDayInThisMonth - lastDayInThisWeek); i++) {
  309. days.push({
  310. year: curYear,
  311. month: curMonth,
  312. day: i,
  313. week: getDate.dayOfWeek(curYear, curMonth, i)
  314. })
  315. }
  316. }
  317. days = this.initSelectedDay(days)
  318. const {
  319. year: updateYear,
  320. month: updateMonth
  321. } = this.updateYMWhenSwipeCalendarHasSelected(days)
  322. if (updateYear && updateMonth) {
  323. curYear = updateYear
  324. curMonth = updateMonth
  325. }
  326. this.setEnableAreaOnWeekMode(days)
  327. this.setData(
  328. {
  329. 'calendar.curYear': curYear,
  330. 'calendar.curMonth': curMonth,
  331. 'calendar.days': days
  332. },
  333. () => {
  334. Day(this.Component).setDateStyle()
  335. }
  336. )
  337. }
  338. /**
  339. * 计算上一周的日期
  340. */
  341. calculatePrevWeekDays() {
  342. let { firstDayInThisWeek } = this.calculateFirstDay()
  343. let { curYear, curMonth } = this.getData('calendar')
  344. let days = []
  345. if (firstDayInThisWeek - 7 > 0) {
  346. const { Uyear, Umonth } = this.updateCurrYearAndMonth('prev')
  347. curYear = Uyear
  348. curMonth = Umonth
  349. for (let i = firstDayInThisWeek - 7; i < firstDayInThisWeek; i++) {
  350. days.push({
  351. year: curYear,
  352. month: curMonth,
  353. day: i,
  354. week: getDate.dayOfWeek(curYear, curMonth, i)
  355. })
  356. }
  357. } else {
  358. let temp = []
  359. for (let i = 1; i < firstDayInThisWeek; i++) {
  360. temp.push({
  361. year: curYear,
  362. month: curMonth,
  363. day: i,
  364. week: getDate.dayOfWeek(curYear, curMonth, i)
  365. })
  366. }
  367. const { Uyear, Umonth } = this.updateCurrYearAndMonth('prev')
  368. curYear = Uyear
  369. curMonth = Umonth
  370. const prevMonthDays = getDate.thisMonthDays(curYear, curMonth)
  371. for (
  372. let i = prevMonthDays - Math.abs(firstDayInThisWeek - 7);
  373. i <= prevMonthDays;
  374. i++
  375. ) {
  376. days.push({
  377. year: curYear,
  378. month: curMonth,
  379. day: i,
  380. week: getDate.dayOfWeek(curYear, curMonth, i)
  381. })
  382. }
  383. days = days.concat(temp)
  384. }
  385. days = this.initSelectedDay(days)
  386. const {
  387. year: updateYear,
  388. month: updateMonth
  389. } = this.updateYMWhenSwipeCalendarHasSelected(days)
  390. if (updateYear && updateMonth) {
  391. curYear = updateYear
  392. curMonth = updateMonth
  393. }
  394. this.setEnableAreaOnWeekMode(days)
  395. this.setData(
  396. {
  397. 'calendar.curYear': curYear,
  398. 'calendar.curMonth': curMonth,
  399. 'calendar.days': days
  400. },
  401. () => {
  402. Day(this.Component).setDateStyle()
  403. }
  404. )
  405. }
  406. calculateDatesWhenJump(
  407. { year, month, day },
  408. { firstWeekDays, lastWeekDays },
  409. firstDayOfWeekIsMon
  410. ) {
  411. const inFirstWeek = this.__dateIsInWeek({ year, month, day }, firstWeekDays)
  412. const inLastWeek = this.__dateIsInWeek({ year, month, day }, lastWeekDays)
  413. let dates = []
  414. if (inFirstWeek) {
  415. dates = this.__calculateDatesWhenInFirstWeek(
  416. firstWeekDays,
  417. firstDayOfWeekIsMon
  418. )
  419. } else if (inLastWeek) {
  420. dates = this.__calculateDatesWhenInLastWeek(
  421. lastWeekDays,
  422. firstDayOfWeekIsMon
  423. )
  424. } else {
  425. dates = this.__calculateDates({ year, month, day }, firstDayOfWeekIsMon)
  426. }
  427. return dates
  428. }
  429. jump({ year, month, day }, disableSelected) {
  430. return new Promise(resolve => {
  431. if (!day) return
  432. const config = this.getCalendarConfig()
  433. const firstDayOfWeekIsMon = config.firstDayOfWeek === 'Mon'
  434. const firstWeekDays = this.firstWeekInMonth(
  435. year,
  436. month,
  437. firstDayOfWeekIsMon
  438. )
  439. let lastWeekDays = this.lastWeekInMonth(year, month, firstDayOfWeekIsMon)
  440. let dates = this.calculateDatesWhenJump(
  441. { year, month, day },
  442. {
  443. firstWeekDays,
  444. lastWeekDays
  445. },
  446. firstDayOfWeekIsMon
  447. )
  448. dates = dates.map(d => {
  449. let date = { ...d }
  450. if (
  451. +date.year === +year &&
  452. +date.month === +month &&
  453. +date.day === +day &&
  454. !disableSelected
  455. ) {
  456. date.choosed = true
  457. }
  458. date = this.__setTodoWhenJump(date, config)
  459. if (config.showLunar) {
  460. date = this.__setSolarLunar(date)
  461. }
  462. if (config.highlightToday) {
  463. date = this.__highlightToday(date)
  464. }
  465. return date
  466. })
  467. this.setEnableAreaOnWeekMode(dates)
  468. const tmpData = {
  469. 'calendar.days': dates,
  470. 'calendar.curYear': year,
  471. 'calendar.curMonth': month,
  472. 'calendar.empytGrids': [],
  473. 'calendar.lastEmptyGrids': []
  474. }
  475. if (!disableSelected) {
  476. tmpData['calendar.selectedDay'] = dates.filter(item => item.choosed)
  477. }
  478. this.setData(tmpData, () => {
  479. Day(this.Component).setDateStyle()
  480. resolve({ year, month, date: day })
  481. })
  482. })
  483. }
  484. __setTodoWhenJump(dateInfo) {
  485. const date = { ...dateInfo }
  486. const { todoLabels = [], showLabelAlways } = this.getData('calendar')
  487. const todosStr = todoLabels.map(d => `${+d.year}-${+d.month}-${+d.day}`)
  488. const idx = todosStr.indexOf(`${+date.year}-${+date.month}-${+date.day}`)
  489. if (idx !== -1) {
  490. if (showLabelAlways) {
  491. date.showTodoLabel = true
  492. } else {
  493. date.showTodoLabel = !date.choosed
  494. }
  495. const todo = todoLabels[idx] || {}
  496. if (date.showTodoLabel && todo.todoText) date.todoText = todo.todoText
  497. if (todo.color) date.color = todo.color
  498. }
  499. return date
  500. }
  501. __setSolarLunar(dateInfo) {
  502. const date = { ...dateInfo }
  503. date.lunar = convertSolarLunar.solar2lunar(
  504. +date.year,
  505. +date.month,
  506. +date.day
  507. )
  508. return date
  509. }
  510. __highlightToday(dateInfo) {
  511. const date = { ...dateInfo }
  512. const today = getDate.todayDate()
  513. const isToday =
  514. +today.year === +date.year &&
  515. +today.month === +date.month &&
  516. +date.day === +today.date
  517. date.isToday = isToday
  518. return date
  519. }
  520. __calculateDatesWhenInFirstWeek(firstWeekDays) {
  521. const dates = [...firstWeekDays]
  522. if (dates.length < 7) {
  523. let { year, month } = dates[0]
  524. let len = 7 - dates.length
  525. let lastDate
  526. if (month > 1) {
  527. month -= 1
  528. lastDate = getDate.thisMonthDays(year, month)
  529. } else {
  530. month = 12
  531. year -= 1
  532. lastDate = getDate.thisMonthDays(year, month)
  533. }
  534. while (len) {
  535. dates.unshift({
  536. year,
  537. month,
  538. day: lastDate,
  539. week: getDate.dayOfWeek(year, month, lastDate)
  540. })
  541. lastDate -= 1
  542. len -= 1
  543. }
  544. }
  545. return dates
  546. }
  547. __calculateDatesWhenInLastWeek(lastWeekDays) {
  548. const dates = [...lastWeekDays]
  549. if (dates.length < 7) {
  550. let { year, month } = dates[0]
  551. let len = 7 - dates.length
  552. let firstDate = 1
  553. if (month > 11) {
  554. month = 1
  555. year += 1
  556. } else {
  557. month += 1
  558. }
  559. while (len) {
  560. dates.push({
  561. year,
  562. month,
  563. day: firstDate,
  564. week: getDate.dayOfWeek(year, month, firstDate)
  565. })
  566. firstDate += 1
  567. len -= 1
  568. }
  569. }
  570. return dates
  571. }
  572. __calculateDates({ year, month, day }, firstDayOfWeekIsMon) {
  573. const week = getDate.dayOfWeek(year, month, day)
  574. let range = [day - week, day + (6 - week)]
  575. if (firstDayOfWeekIsMon) {
  576. range = [day + 1 - week, day + (7 - week)]
  577. }
  578. const dates = Day(this.Component).buildDate(year, month)
  579. const weekDates = dates.slice(range[0] - 1, range[1])
  580. return weekDates
  581. }
  582. __dateIsInWeek(date, week) {
  583. return week.find(
  584. item =>
  585. +item.year === +date.year &&
  586. +item.month === +date.month &&
  587. +item.day === +date.day
  588. )
  589. }
  590. __tipsWhenCanNotSwtich() {
  591. logger.info(
  592. '当前月份未选中日期下切换为周视图,不能明确该展示哪一周的日期,故此情况不允许切换'
  593. )
  594. }
  595. }
  596. export default component => new WeekMode(component)