day.js 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543
  1. import WxData from './wxData'
  2. import CalendarConfig from './config'
  3. import convertSolarLunar from './convertSolarLunar'
  4. import {
  5. Logger,
  6. GetDate,
  7. getDateTimeStamp,
  8. uniqueArrayByDate,
  9. delRepeatedEnableDay,
  10. convertEnableAreaToTimestamp,
  11. converEnableDaysToTimestamp
  12. } from './utils'
  13. const logger = new Logger()
  14. const getDate = new GetDate()
  15. const toString = Object.prototype.toString
  16. class Day extends WxData {
  17. constructor(component) {
  18. super(component)
  19. this.Component = component
  20. }
  21. getCalendarConfig() {
  22. return this.Component.config
  23. }
  24. /**
  25. *
  26. * @param {number} year
  27. * @param {number} month
  28. */
  29. buildDate(year, month) {
  30. const today = getDate.todayDate()
  31. const thisMonthDays = getDate.thisMonthDays(year, month)
  32. const dates = []
  33. for (let i = 1; i <= thisMonthDays; i++) {
  34. const isToday =
  35. +today.year === +year && +today.month === +month && i === +today.date
  36. const config = this.getCalendarConfig()
  37. const date = {
  38. year,
  39. month,
  40. day: i,
  41. choosed: false,
  42. week: getDate.dayOfWeek(year, month, i),
  43. isToday: isToday && config.highlightToday,
  44. lunar: convertSolarLunar.solar2lunar(+year, +month, +i)
  45. }
  46. dates.push(date)
  47. }
  48. return dates
  49. }
  50. /**
  51. * 指定可选日期范围
  52. * @param {array} area 日期访问数组
  53. */
  54. enableArea(dateArea = []) {
  55. if (dateArea.length === 2) {
  56. const isRight = this.__judgeParam(dateArea)
  57. if (isRight) {
  58. let { days = [], selectedDay = [] } = this.getData('calendar')
  59. const { startTimestamp, endTimestamp } = convertEnableAreaToTimestamp(
  60. dateArea
  61. )
  62. const dataAfterHandle = this.__handleEnableArea(
  63. {
  64. dateArea,
  65. days,
  66. startTimestamp,
  67. endTimestamp
  68. },
  69. selectedDay
  70. )
  71. this.setData({
  72. 'calendar.enableArea': dateArea,
  73. 'calendar.days': dataAfterHandle.dates,
  74. 'calendar.selectedDay': dataAfterHandle.selectedDay,
  75. 'calendar.enableAreaTimestamp': [startTimestamp, endTimestamp]
  76. })
  77. }
  78. } else {
  79. logger.warn(
  80. 'enableArea()参数需为时间范围数组,形如:["2018-8-4" , "2018-8-24"]'
  81. )
  82. }
  83. }
  84. /**
  85. * 指定特定日期可选
  86. * @param {array} days 指定日期数组
  87. */
  88. enableDays(dates = []) {
  89. const { enableArea = [] } = this.getData('calendar')
  90. let expectEnableDaysTimestamp = []
  91. if (enableArea.length) {
  92. expectEnableDaysTimestamp = delRepeatedEnableDay(dates, enableArea)
  93. } else {
  94. expectEnableDaysTimestamp = converEnableDaysToTimestamp(dates)
  95. }
  96. let { days = [], selectedDay = [] } = this.getData('calendar')
  97. const dataAfterHandle = this.__handleEnableDays(
  98. {
  99. days,
  100. expectEnableDaysTimestamp
  101. },
  102. selectedDay
  103. )
  104. this.setData({
  105. 'calendar.days': dataAfterHandle.dates,
  106. 'calendar.selectedDay': dataAfterHandle.selectedDay,
  107. 'calendar.enableDays': dates,
  108. 'calendar.enableDaysTimestamp': expectEnableDaysTimestamp
  109. })
  110. }
  111. /**
  112. * 设置多个日期选中
  113. * @param {array} selected 需选中日期
  114. */
  115. setSelectedDays(selected) {
  116. const config = CalendarConfig(this.Component).getCalendarConfig()
  117. if (!config.multi) {
  118. return logger.warn('单选模式下不能设置多日期选中,请配置 multi')
  119. }
  120. let { days } = this.getData('calendar')
  121. let newSelectedDay = []
  122. if (!selected) {
  123. days.map(item => {
  124. item.choosed = true
  125. item.showTodoLabel = false
  126. })
  127. newSelectedDay = days
  128. } else if (selected && selected.length) {
  129. const { dates, selectedDates } = this.__handleSelectedDays(
  130. days,
  131. newSelectedDay,
  132. selected
  133. )
  134. days = dates
  135. newSelectedDay = selectedDates
  136. }
  137. CalendarConfig(this.Component).setCalendarConfig('multi', true)
  138. this.setData({
  139. 'calendar.days': days,
  140. 'calendar.selectedDay': newSelectedDay
  141. })
  142. }
  143. /**
  144. * 禁用指定日期
  145. * @param {array} dates 禁用
  146. */
  147. disableDays(dates) {
  148. const { disableDays = [], days } = this.getData('calendar')
  149. if (Object.prototype.toString.call(dates) !== '[object Array]') {
  150. return logger.warn('disableDays 参数为数组')
  151. }
  152. let _disableDays = []
  153. if (dates.length) {
  154. _disableDays = uniqueArrayByDate(dates.concat(disableDays))
  155. const disableDaysCol = _disableDays.map(d => getDate.toTimeStr(d))
  156. days.forEach(item => {
  157. const cur = getDate.toTimeStr(item)
  158. if (disableDaysCol.includes(cur)) item.disable = true
  159. })
  160. } else {
  161. days.forEach(item => {
  162. item.disable = false
  163. })
  164. }
  165. this.setData({
  166. 'calendar.days': days,
  167. 'calendar.disableDays': _disableDays
  168. })
  169. }
  170. /**
  171. * 设置连续日期选择区域
  172. * @param {array} dateArea 区域开始结束日期数组
  173. */
  174. chooseArea(dateArea = []) {
  175. return new Promise((resolve, reject) => {
  176. if (dateArea.length === 1) {
  177. dateArea = dateArea.concat(dateArea)
  178. }
  179. if (dateArea.length === 2) {
  180. const isRight = this.__judgeParam(dateArea)
  181. if (isRight) {
  182. const config = CalendarConfig(this.Component).getCalendarConfig()
  183. const { startTimestamp, endTimestamp } = convertEnableAreaToTimestamp(
  184. dateArea
  185. )
  186. this.setData(
  187. {
  188. calendarConfig: {
  189. ...config,
  190. chooseAreaMode: true,
  191. mulit: true
  192. },
  193. 'calendar.chooseAreaTimestamp': [startTimestamp, endTimestamp]
  194. },
  195. () => {
  196. this.__chooseContinuousDates(startTimestamp, endTimestamp)
  197. .then(resolve)
  198. .catch(reject)
  199. }
  200. )
  201. }
  202. }
  203. })
  204. }
  205. __pusheNextMonthDateArea(item, startTimestamp, endTimestamp, selectedDates) {
  206. const days = this.buildDate(item.year, item.month)
  207. let daysLen = days.length
  208. for (let i = 0; i < daysLen; i++) {
  209. const item = days[i]
  210. const timeStamp = getDateTimeStamp(item)
  211. if (timeStamp <= endTimestamp && timeStamp >= startTimestamp) {
  212. selectedDates.push({
  213. ...item,
  214. choosed: true
  215. })
  216. }
  217. if (i === daysLen - 1 && timeStamp < endTimestamp) {
  218. this.__pusheNextMonthDateArea(
  219. getDate.nextMonth(item),
  220. startTimestamp,
  221. endTimestamp,
  222. selectedDates
  223. )
  224. }
  225. }
  226. }
  227. __pushPrevMonthDateArea(item, startTimestamp, endTimestamp, selectedDates) {
  228. const days = getDate.sortDates(
  229. this.buildDate(item.year, item.month),
  230. 'desc'
  231. )
  232. let daysLen = days.length
  233. let firstDate = getDateTimeStamp(days[0])
  234. for (let i = 0; i < daysLen; i++) {
  235. const item = days[i]
  236. const timeStamp = getDateTimeStamp(item)
  237. if (timeStamp >= startTimestamp && timeStamp <= endTimestamp) {
  238. selectedDates.push({
  239. ...item,
  240. choosed: true
  241. })
  242. }
  243. if (i === daysLen - 1 && firstDate > startTimestamp) {
  244. this.__pushPrevMonthDateArea(
  245. getDate.prevMonth(item),
  246. startTimestamp,
  247. endTimestamp,
  248. selectedDates
  249. )
  250. }
  251. }
  252. }
  253. /**
  254. * 当设置日期区域非当前时保存其他月份的日期至已选日期数组
  255. * @param {object} info
  256. */
  257. __calcDateWhenNotInOneMonth(info) {
  258. const {
  259. firstDate,
  260. lastDate,
  261. startTimestamp,
  262. endTimestamp,
  263. filterSelectedDate
  264. } = info
  265. if (getDateTimeStamp(firstDate) > startTimestamp) {
  266. this.__pushPrevMonthDateArea(
  267. getDate.prevMonth(firstDate),
  268. startTimestamp,
  269. endTimestamp,
  270. filterSelectedDate
  271. )
  272. }
  273. if (getDateTimeStamp(lastDate) < endTimestamp) {
  274. this.__pusheNextMonthDateArea(
  275. getDate.nextMonth(lastDate),
  276. startTimestamp,
  277. endTimestamp,
  278. filterSelectedDate
  279. )
  280. }
  281. const newSelectedDates = [...getDate.sortDates(filterSelectedDate)]
  282. return newSelectedDates
  283. }
  284. /**
  285. * 设置连续日期段
  286. * @param {number} startTimestamp 连续日期段开始日期时间戳
  287. * @param {number} endTimestamp 连续日期段结束日期时间戳
  288. */
  289. __chooseContinuousDates(startTimestamp, endTimestamp) {
  290. return new Promise((resolve, reject) => {
  291. const { days, selectedDay = [] } = this.getData('calendar')
  292. const selectedDateStr = []
  293. let filterSelectedDate = []
  294. selectedDay.forEach(item => {
  295. const timeStamp = getDateTimeStamp(item)
  296. if (timeStamp >= startTimestamp && timeStamp <= endTimestamp) {
  297. filterSelectedDate.push(item)
  298. selectedDateStr.push(getDate.toTimeStr(item))
  299. }
  300. })
  301. days.forEach(item => {
  302. const timeStamp = getDateTimeStamp(item)
  303. const dateInSelecedArray = selectedDateStr.includes(
  304. getDate.toTimeStr(item)
  305. )
  306. if (timeStamp >= startTimestamp && timeStamp <= endTimestamp) {
  307. if (dateInSelecedArray) {
  308. return
  309. }
  310. item.choosed = true
  311. filterSelectedDate.push(item)
  312. } else {
  313. item.choosed = false
  314. if (dateInSelecedArray) {
  315. const idx = filterSelectedDate.findIndex(
  316. selectedDate =>
  317. getDate.toTimeStr(selectedDate) === getDate.toTimeStr(item)
  318. )
  319. if (idx > -1) {
  320. filterSelectedDate.splice(idx, 1)
  321. }
  322. }
  323. }
  324. })
  325. const firstDate = days[0]
  326. const lastDate = days[days.length - 1]
  327. const newSelectedDates = this.__calcDateWhenNotInOneMonth({
  328. firstDate,
  329. lastDate,
  330. startTimestamp,
  331. endTimestamp,
  332. filterSelectedDate
  333. })
  334. try {
  335. this.setData(
  336. {
  337. 'calendar.days': [...days],
  338. 'calendar.selectedDay': newSelectedDates
  339. },
  340. () => {
  341. resolve(newSelectedDates)
  342. }
  343. )
  344. } catch (err) {
  345. reject(err)
  346. }
  347. })
  348. }
  349. /**
  350. * 设置指定日期样式
  351. * @param {array} dates 待设置特殊样式的日期
  352. */
  353. setDateStyle(dates) {
  354. if (toString.call(dates) !== '[object Array]') return
  355. const { days, specialStyleDates } = this.getData('calendar')
  356. if (toString.call(specialStyleDates) === '[object Array]') {
  357. dates = uniqueArrayByDate([...specialStyleDates, ...dates])
  358. }
  359. const _specialStyleDates = dates.map(
  360. item => `${item.year}_${item.month}_${item.day}`
  361. )
  362. const _days = days.map(item => {
  363. const idx = _specialStyleDates.indexOf(
  364. `${item.year}_${item.month}_${item.day}`
  365. )
  366. if (idx > -1) {
  367. return {
  368. ...item,
  369. class: dates[idx].class
  370. }
  371. } else {
  372. return { ...item }
  373. }
  374. })
  375. this.setData({
  376. 'calendar.days': _days,
  377. 'calendar.specialStyleDates': dates
  378. })
  379. }
  380. __judgeParam(dateArea) {
  381. const {
  382. start,
  383. end,
  384. startTimestamp,
  385. endTimestamp
  386. } = convertEnableAreaToTimestamp(dateArea)
  387. if (!start || !end) return
  388. const startMonthDays = getDate.thisMonthDays(start[0], start[1])
  389. const endMonthDays = getDate.thisMonthDays(end[0], end[1])
  390. if (start[2] > startMonthDays || start[2] < 1) {
  391. logger.warn('enableArea() 开始日期错误,指定日期不在当前月份天数范围内')
  392. return false
  393. } else if (start[1] > 12 || start[1] < 1) {
  394. logger.warn('enableArea() 开始日期错误,月份超出1-12月份')
  395. return false
  396. } else if (end[2] > endMonthDays || end[2] < 1) {
  397. logger.warn('enableArea() 截止日期错误,指定日期不在当前月份天数范围内')
  398. return false
  399. } else if (end[1] > 12 || end[1] < 1) {
  400. logger.warn('enableArea() 截止日期错误,月份超出1-12月份')
  401. return false
  402. } else if (startTimestamp > endTimestamp) {
  403. logger.warn('enableArea()参数最小日期大于了最大日期')
  404. return false
  405. } else {
  406. return true
  407. }
  408. }
  409. __getDisableDateTimestamp() {
  410. let disableDateTimestamp
  411. const { date, type } = this.getCalendarConfig().disableMode || {}
  412. if (date) {
  413. const t = date.split('-')
  414. if (t.length < 3) {
  415. logger.warn('配置 disableMode.date 格式错误')
  416. return {}
  417. }
  418. disableDateTimestamp = getDateTimeStamp({
  419. year: +t[0],
  420. month: +t[1],
  421. day: +t[2]
  422. })
  423. }
  424. return {
  425. disableDateTimestamp,
  426. disableType: type
  427. }
  428. }
  429. __handleEnableArea(data = {}, selectedDay = []) {
  430. const { area, days, startTimestamp, endTimestamp } = data
  431. const enableDays = this.getData('calendar.enableDays') || []
  432. let expectEnableDaysTimestamp = []
  433. if (enableDays.length) {
  434. expectEnableDaysTimestamp = delRepeatedEnableDay(enableDays, area)
  435. }
  436. const {
  437. disableDateTimestamp,
  438. disableType
  439. } = this.__getDisableDateTimestamp()
  440. const dates = [...days]
  441. dates.forEach(item => {
  442. const timestamp = +getDate
  443. .newDate(item.year, item.month, item.day)
  444. .getTime()
  445. const ifOutofArea =
  446. (+startTimestamp > timestamp || timestamp > +endTimestamp) &&
  447. !expectEnableDaysTimestamp.includes(timestamp)
  448. if (
  449. ifOutofArea ||
  450. (disableType === 'before' &&
  451. disableDateTimestamp &&
  452. timestamp < disableDateTimestamp) ||
  453. (disableType === 'after' &&
  454. disableDateTimestamp &&
  455. timestamp > disableDateTimestamp)
  456. ) {
  457. item.disable = true
  458. if (item.choosed) {
  459. item.choosed = false
  460. selectedDay = selectedDay.filter(
  461. d => getDate.toTimeStr(item) !== getDate.toTimeStr(d)
  462. )
  463. }
  464. } else if (item.disable) {
  465. item.disable = false
  466. }
  467. })
  468. return {
  469. dates,
  470. selectedDay
  471. }
  472. }
  473. __handleEnableDays(data = {}, selectedDay = []) {
  474. const { days, expectEnableDaysTimestamp } = data
  475. const { enableAreaTimestamp = [] } = this.getData('calendar')
  476. const dates = [...days]
  477. dates.forEach(item => {
  478. const timestamp = getDate
  479. .newDate(item.year, item.month, item.day)
  480. .getTime()
  481. let setDisable = false
  482. if (enableAreaTimestamp.length) {
  483. if (
  484. (+enableAreaTimestamp[0] > +timestamp ||
  485. +timestamp > +enableAreaTimestamp[1]) &&
  486. !expectEnableDaysTimestamp.includes(+timestamp)
  487. ) {
  488. setDisable = true
  489. }
  490. } else if (!expectEnableDaysTimestamp.includes(+timestamp)) {
  491. setDisable = true
  492. }
  493. if (setDisable) {
  494. item.disable = true
  495. if (item.choosed) {
  496. item.choosed = false
  497. selectedDay = selectedDay.filter(
  498. d => getDate.toTimeStr(item) !== getDate.toTimeStr(d)
  499. )
  500. }
  501. } else {
  502. item.disable = false
  503. }
  504. })
  505. return {
  506. dates,
  507. selectedDay
  508. }
  509. }
  510. __handleSelectedDays(days = [], newSelectedDay = [], selected) {
  511. const { selectedDay, showLabelAlways } = this.getData('calendar')
  512. if (selectedDay && selectedDay.length) {
  513. newSelectedDay = uniqueArrayByDate(selectedDay.concat(selected))
  514. } else {
  515. newSelectedDay = selected
  516. }
  517. const { year: curYear, month: curMonth } = days[0]
  518. const currentSelectedDays = []
  519. newSelectedDay.forEach(item => {
  520. if (+item.year === +curYear && +item.month === +curMonth) {
  521. currentSelectedDays.push(getDate.toTimeStr(item))
  522. }
  523. })
  524. ;[...days].map(item => {
  525. if (currentSelectedDays.includes(getDate.toTimeStr(item))) {
  526. item.choosed = true
  527. if (showLabelAlways && item.showTodoLabel) {
  528. item.showTodoLabel = true
  529. } else {
  530. item.showTodoLabel = false
  531. }
  532. }
  533. })
  534. return {
  535. dates: days,
  536. selectedDates: newSelectedDay
  537. }
  538. }
  539. }
  540. export default component => new Day(component)