render.js 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375
  1. import Day from './day'
  2. import Todo from './todo'
  3. import WxData from './wxData'
  4. import convertSolarLunar from './convertSolarLunar'
  5. import {
  6. Logger,
  7. GetDate,
  8. delRepeatedEnableDay,
  9. getDateTimeStamp,
  10. converEnableDaysToTimestamp
  11. } from './utils'
  12. const getDate = new GetDate()
  13. const logger = new Logger()
  14. class Calendar extends WxData {
  15. constructor(component) {
  16. super(component)
  17. this.Component = component
  18. }
  19. getCalendarConfig() {
  20. return this.Component.config
  21. }
  22. /**
  23. * 渲染日历
  24. * @param {number} curYear 年份
  25. * @param {number} curMonth 月份
  26. * @param {number} curDate 日期
  27. * @param {boolean} disableSelect 是否禁用选中
  28. */
  29. renderCalendar(curYear, curMonth, curDate, disableSelect) {
  30. return new Promise(resolve => {
  31. const config = this.getCalendarConfig()
  32. this.calculateEmptyGrids(curYear, curMonth)
  33. this.calculateDays(curYear, curMonth, curDate, disableSelect).then(() => {
  34. const { todoLabels, specialStyleDates, enableDays, selectedDay } =
  35. this.getData('calendar') || {}
  36. if (
  37. todoLabels &&
  38. todoLabels.find(
  39. item => +item.month === +curMonth && +item.year === +curYear
  40. )
  41. ) {
  42. Todo(this.Component).setTodoLabels()
  43. }
  44. if (
  45. specialStyleDates &&
  46. specialStyleDates.length &&
  47. specialStyleDates.find(
  48. item => +item.month === +curMonth && +item.year === +curYear
  49. )
  50. ) {
  51. Day(this.Component).setDateStyle(specialStyleDates)
  52. }
  53. if (
  54. enableDays &&
  55. enableDays.length &&
  56. enableDays.find(item => {
  57. let ymd = item.split('-')
  58. return +ymd[1] === +curMonth && +ymd[0] === +curYear
  59. })
  60. ) {
  61. Day(this.Component).enableDays(enableDays)
  62. }
  63. if (
  64. selectedDay &&
  65. selectedDay.length &&
  66. selectedDay.find(
  67. item => +item.month === +curMonth && +item.year === +curYear
  68. ) &&
  69. config.mulit
  70. ) {
  71. Day(this.Component).setSelectedDays(selectedDay)
  72. }
  73. if (!this.Component.firstRender) {
  74. resolve({
  75. firstRender: true
  76. })
  77. } else {
  78. resolve({
  79. firstRender: false
  80. })
  81. }
  82. })
  83. })
  84. }
  85. /**
  86. * 计算当前月份前后两月应占的格子
  87. * @param {number} year 年份
  88. * @param {number} month 月份
  89. */
  90. calculateEmptyGrids(year, month) {
  91. this.calculatePrevMonthGrids(year, month)
  92. this.calculateNextMonthGrids(year, month)
  93. }
  94. /**
  95. * 计算上月应占的格子
  96. * @param {number} year 年份
  97. * @param {number} month 月份
  98. */
  99. calculatePrevMonthGrids(year, month) {
  100. let empytGrids = []
  101. const prevMonthDays = getDate.thisMonthDays(year, month - 1)
  102. let firstDayOfWeek = getDate.firstDayOfWeek(year, month)
  103. const config = this.getCalendarConfig() || {}
  104. if (config.firstDayOfWeek === 'Mon') {
  105. if (firstDayOfWeek === 0) {
  106. firstDayOfWeek = 6
  107. } else {
  108. firstDayOfWeek -= 1
  109. }
  110. }
  111. if (firstDayOfWeek > 0) {
  112. const len = prevMonthDays - firstDayOfWeek
  113. const { onlyShowCurrentMonth } = config
  114. const { showLunar } = this.getCalendarConfig()
  115. for (let i = prevMonthDays; i > len; i--) {
  116. if (onlyShowCurrentMonth) {
  117. empytGrids.push('')
  118. } else {
  119. empytGrids.push({
  120. day: i,
  121. lunar: showLunar
  122. ? convertSolarLunar.solar2lunar(year, month - 1, i)
  123. : null
  124. })
  125. }
  126. }
  127. this.setData({
  128. 'calendar.empytGrids': empytGrids.reverse()
  129. })
  130. } else {
  131. this.setData({
  132. 'calendar.empytGrids': null
  133. })
  134. }
  135. }
  136. /**
  137. * 计算下一月日期是否需要多展示的日期
  138. * 某些月份日期为5排,某些月份6排,统一为6排
  139. * @param {number} year
  140. * @param {number} month
  141. * @param {object} config
  142. */
  143. calculateExtraEmptyDate(year, month, config) {
  144. let extDate = 0
  145. if (+month === 2) {
  146. extDate += 7
  147. let firstDayofMonth = getDate.dayOfWeek(year, month, 1)
  148. if (config.firstDayOfWeek === 'Mon') {
  149. if (+firstDayofMonth === 1) extDate += 7
  150. } else {
  151. if (+firstDayofMonth === 0) extDate += 7
  152. }
  153. } else {
  154. let firstDayofMonth = getDate.dayOfWeek(year, month, 1)
  155. if (config.firstDayOfWeek === 'Mon') {
  156. if (firstDayofMonth !== 0 && firstDayofMonth < 6) {
  157. extDate += 7
  158. }
  159. } else {
  160. if (firstDayofMonth <= 5) {
  161. extDate += 7
  162. }
  163. }
  164. }
  165. return extDate
  166. }
  167. /**
  168. * 计算下月应占的格子
  169. * @param {number} year 年份
  170. * @param {number} month 月份
  171. */
  172. calculateNextMonthGrids(year, month) {
  173. let lastEmptyGrids = []
  174. const thisMonthDays = getDate.thisMonthDays(year, month)
  175. let lastDayWeek = getDate.dayOfWeek(year, month, thisMonthDays)
  176. const config = this.getCalendarConfig() || {}
  177. if (config.firstDayOfWeek === 'Mon') {
  178. if (lastDayWeek === 0) {
  179. lastDayWeek = 6
  180. } else {
  181. lastDayWeek -= 1
  182. }
  183. }
  184. let len = 7 - (lastDayWeek + 1)
  185. const { onlyShowCurrentMonth, showLunar } = config
  186. if (!onlyShowCurrentMonth) {
  187. len = len + this.calculateExtraEmptyDate(year, month, config)
  188. }
  189. for (let i = 1; i <= len; i++) {
  190. if (onlyShowCurrentMonth) {
  191. lastEmptyGrids.push('')
  192. } else {
  193. lastEmptyGrids.push({
  194. day: i,
  195. lunar: showLunar
  196. ? convertSolarLunar.solar2lunar(year, month + 1, i)
  197. : null
  198. })
  199. }
  200. }
  201. this.setData({
  202. 'calendar.lastEmptyGrids': lastEmptyGrids
  203. })
  204. }
  205. /**
  206. * 日历初始化将默认值写入 selectDay
  207. * @param {number} year
  208. * @param {number} month
  209. * @param {number} curDate
  210. */
  211. setSelectedDay(year, month, curDate) {
  212. let selectedDay = []
  213. const config = this.getCalendarConfig()
  214. if (config.noDefault) {
  215. selectedDay = []
  216. config.noDefault = false
  217. } else {
  218. const data = this.getData('calendar') || {}
  219. const { showLunar } = this.getCalendarConfig()
  220. selectedDay = curDate
  221. ? [
  222. {
  223. year,
  224. month,
  225. day: curDate,
  226. choosed: true,
  227. week: getDate.dayOfWeek(year, month, curDate),
  228. lunar: showLunar
  229. ? convertSolarLunar.solar2lunar(year, month, curDate)
  230. : null
  231. }
  232. ]
  233. : data.selectedDay
  234. }
  235. return selectedDay
  236. }
  237. __getDisableDateTimestamp() {
  238. let disableDateTimestamp
  239. const { date, type } = this.getCalendarConfig().disableMode || {}
  240. if (date) {
  241. const t = date.split('-')
  242. if (t.length < 3) {
  243. logger.warn('配置 disableMode.date 格式错误')
  244. return {}
  245. }
  246. disableDateTimestamp = getDateTimeStamp({
  247. year: +t[0],
  248. month: +t[1],
  249. day: +t[2]
  250. })
  251. }
  252. return {
  253. disableDateTimestamp,
  254. disableType: type
  255. }
  256. }
  257. resetDates() {
  258. this.setData({
  259. 'calendar.days': []
  260. })
  261. }
  262. /**
  263. * 设置日历面板数据
  264. * @param {number} year 年份
  265. * @param {number} month 月份
  266. * @param {number} curDate 日期
  267. * @param {boolean} disableSelect 是否禁用选中
  268. */
  269. calculateDays(year, month, curDate, disableSelect) {
  270. return new Promise(resolve => {
  271. // 避免切换日期时样式残影
  272. this.resetDates()
  273. let days = []
  274. const {
  275. disableDays = [],
  276. chooseAreaTimestamp = [],
  277. selectedDay: selectedDates = []
  278. } = this.getData('calendar')
  279. days = Day(this.Component).buildDate(year, month)
  280. let selectedDay = selectedDates
  281. if (!disableSelect) {
  282. selectedDay = this.setSelectedDay(year, month, curDate)
  283. }
  284. const selectedDayStr = selectedDay.map(d => getDate.toTimeStr(d))
  285. const disableDaysStr = disableDays.map(d => getDate.toTimeStr(d))
  286. const [areaStart, areaEnd] = chooseAreaTimestamp
  287. days.forEach(item => {
  288. const cur = getDate.toTimeStr(item)
  289. const timestamp = getDateTimeStamp(item)
  290. if (selectedDayStr.includes(cur) && !disableSelect) {
  291. item.choosed = true
  292. if (timestamp > areaEnd || timestamp < areaStart) {
  293. const idx = selectedDay.findIndex(
  294. selectedDate =>
  295. getDate.toTimeStr(selectedDate) === getDate.toTimeStr(item)
  296. )
  297. selectedDay.splice(idx, 1)
  298. }
  299. } else if (
  300. areaStart &&
  301. areaEnd &&
  302. timestamp >= areaStart &&
  303. timestamp <= areaEnd &&
  304. !disableSelect
  305. ) {
  306. item.choosed = true
  307. selectedDay.push(item)
  308. }
  309. if (disableDaysStr.includes(cur)) item.disable = true
  310. const {
  311. disableDateTimestamp,
  312. disableType
  313. } = this.__getDisableDateTimestamp()
  314. let disabelByConfig = false
  315. if (disableDateTimestamp) {
  316. if (
  317. (disableType === 'before' && timestamp < disableDateTimestamp) ||
  318. (disableType === 'after' && timestamp > disableDateTimestamp)
  319. ) {
  320. disabelByConfig = true
  321. }
  322. }
  323. const isDisable = disabelByConfig || this.__isDisable(timestamp)
  324. if (isDisable) {
  325. item.disable = true
  326. item.choosed = false
  327. }
  328. })
  329. this.setData(
  330. {
  331. 'calendar.days': days,
  332. 'calendar.selectedDay': [...selectedDay] || []
  333. },
  334. () => {
  335. resolve()
  336. }
  337. )
  338. })
  339. }
  340. __isDisable(timestamp) {
  341. const {
  342. enableArea = [],
  343. enableDays = [],
  344. enableAreaTimestamp = []
  345. } = this.getData('calendar')
  346. let setDisable = false
  347. let expectEnableDaysTimestamp = converEnableDaysToTimestamp(enableDays)
  348. if (enableArea.length) {
  349. expectEnableDaysTimestamp = delRepeatedEnableDay(enableDays, enableArea)
  350. }
  351. if (enableAreaTimestamp.length) {
  352. if (
  353. (+enableAreaTimestamp[0] > +timestamp ||
  354. +timestamp > +enableAreaTimestamp[1]) &&
  355. !expectEnableDaysTimestamp.includes(+timestamp)
  356. ) {
  357. setDisable = true
  358. }
  359. } else if (
  360. expectEnableDaysTimestamp.length &&
  361. !expectEnableDaysTimestamp.includes(+timestamp)
  362. ) {
  363. setDisable = true
  364. }
  365. return setDisable
  366. }
  367. }
  368. export default component => new Calendar(component)