Explorar o código

日历功能补充

y595705120 %!s(int64=3) %!d(string=hai) anos
pai
achega
6e101b5e77
Modificáronse 51 ficheiros con 9460 adicións e 2366 borrados
  1. 3 1
      app.js
  2. BIN=BIN
      assets/icon/1.jpg
  3. BIN=BIN
      assets/loading.gif
  4. 33 0
      component/calendar/func/config.js
  5. 1036 0
      component/calendar/func/convertSolarLunar.js
  6. 543 0
      component/calendar/func/day.js
  7. 375 0
      component/calendar/func/render.js
  8. 182 0
      component/calendar/func/todo.js
  9. 367 0
      component/calendar/func/utils.js
  10. 601 0
      component/calendar/func/week.js
  11. 26 0
      component/calendar/func/wxData.js
  12. 244 2277
      component/calendar/index.js
  13. 26 21
      component/calendar/index.wxml
  14. 11 1
      component/calendar/index.wxss
  15. 877 0
      component/calendar/main.js
  16. 144 0
      component/v2/core.js
  17. 12 0
      component/v2/helper.js
  18. 257 0
      component/v2/index.js
  19. 3 0
      component/v2/index.json
  20. 60 0
      component/v2/index.wxml
  21. 215 0
      component/v2/index.wxss
  22. 212 0
      component/v2/plugins/holidays/holidays-map.js
  23. 201 0
      component/v2/plugins/holidays/index.js
  24. 18 0
      component/v2/plugins/index.js
  25. 277 0
      component/v2/plugins/preset/base.js
  26. 69 0
      component/v2/plugins/preset/get-calendar-data.js
  27. 9 0
      component/v2/plugins/preset/index.js
  28. 219 0
      component/v2/plugins/selectable.js
  29. 1036 0
      component/v2/plugins/solarLunar/convertSolarLunar.js
  30. 59 0
      component/v2/plugins/solarLunar/index.js
  31. 305 0
      component/v2/plugins/time-range.js
  32. 135 0
      component/v2/plugins/todo.js
  33. 432 0
      component/v2/plugins/week.js
  34. 51 0
      component/v2/render.js
  35. 0 1
      component/v2/theme/iconfont.wxss
  36. 61 0
      component/v2/theme/theme-default.wxss
  37. 58 0
      component/v2/theme/theme-elegant.wxss
  38. 285 0
      component/v2/utils/index.js
  39. 23 0
      component/v2/utils/logger.js
  40. 30 0
      component/v2/utils/wxData.js
  41. 318 0
      pages/calendarComponent/index.js
  42. 6 0
      pages/calendarComponent/index.json
  43. 26 0
      pages/calendarComponent/index.wxml
  44. 161 0
      pages/calendarComponent/index.wxss
  45. 389 51
      pages/rili/index.js
  46. 2 1
      pages/rili/index.json
  47. 27 9
      pages/rili/index.wxml
  48. 1 1
      pages/rili/index.wxss
  49. 6 2
      pages/user/index/index.wxml
  50. 1 1
      project.config.json
  51. 28 0
      utils/util.js

+ 3 - 1
app.js

@@ -110,6 +110,7 @@ App({
         method: 'POST',
         data,
         success(res) {
+          
           if (res.statusCode !== 200 || typeof res.data !== 'object') {
             reject('网络出错')
             return false;
@@ -121,7 +122,8 @@ App({
             _this.gotoLogin()
             return
           }
-          resolve(res.data);
+          console.log("res", res.data)
+          resolve( res.data );
           return true;
         },
         fail(res) {

BIN=BIN
assets/icon/1.jpg


BIN=BIN
assets/loading.gif


+ 33 - 0
component/calendar/func/config.js

@@ -0,0 +1,33 @@
+import WxData from './wxData'
+
+class Config extends WxData {
+  constructor(component) {
+    super(component)
+    this.Component = component
+  }
+  getCalendarConfig() {
+    if (!this.Component || !this.Component.config) return {}
+    return this.Component.config
+  }
+  setCalendarConfig(config) {
+    return new Promise((resolve, reject) => {
+      if (!this.Component || !this.Component.config) {
+        reject('异常:未找到组件配置信息')
+        return
+      }
+      let conf = { ...this.Component.config, ...config }
+
+      this.Component.config = conf
+      this.setData(
+        {
+          calendarConfig: conf
+        },
+        () => {
+          resolve(conf)
+        }
+      )
+    })
+  }
+}
+
+export default component => new Config(component)

+ 1036 - 0
component/calendar/func/convertSolarLunar.js

@@ -0,0 +1,1036 @@
+/**
+ * @1900-2100区间内的公历、农历互转
+ * @Version 1.0.3
+ * @公历转农历:calendar.solar2lunar(1987,11,01); //[you can ignore params of prefix 0]
+ * @农历转公历:calendar.lunar2solar(1987,09,10); //[you can ignore params of prefix 0]
+ */
+/* 公历年月日转农历数据 返回json */
+// calendar.solar2lunar(1987,11,01);
+/** 农历年月日转公历年月日 **/
+// calendar.lunar2solar(1987,9,10);
+// 调用以上方法后返回类似如下object(json)具体以上就不需要解释了吧!
+// c开头的是公历各属性值 l开头的自然就是农历咯 gz开头的就是天干地支纪年的数据啦~
+// {
+//  Animal: "兔",
+//  IDayCn: "初十",
+//  IMonthCn: "九月",
+//  Term: null,
+//  astro: "天蝎座",
+//  cDay: 1,
+//  cMonth: 11,
+//  cYear: 1987,
+//  gzDay: "甲寅",
+//  gzMonth: "庚戌",
+//  gzYear: "丁卯",
+//  isLeap: false,
+//  isTerm: false,
+//  isToday: false,
+//  lDay: 10,
+//  lMonth: 9,
+//  lYear: 1987,
+//  nWeek: 7,
+//  ncWeek: "星期日"
+// }
+// 该代码还有其他可以调用的方法,请自己查看代码中的详细注释
+const calendar = {
+  /**
+   * 农历1900-2100的润大小信息表
+   * @Array Of Property
+   * @return Hex
+   */
+  lunarInfo: [
+    0x04bd8,
+    0x04ae0,
+    0x0a570,
+    0x054d5,
+    0x0d260,
+    0x0d950,
+    0x16554,
+    0x056a0,
+    0x09ad0,
+    0x055d2, // 1900-1909
+    0x04ae0,
+    0x0a5b6,
+    0x0a4d0,
+    0x0d250,
+    0x1d255,
+    0x0b540,
+    0x0d6a0,
+    0x0ada2,
+    0x095b0,
+    0x14977, // 1910-1919
+    0x04970,
+    0x0a4b0,
+    0x0b4b5,
+    0x06a50,
+    0x06d40,
+    0x1ab54,
+    0x02b60,
+    0x09570,
+    0x052f2,
+    0x04970, // 1920-1929
+    0x06566,
+    0x0d4a0,
+    0x0ea50,
+    0x06e95,
+    0x05ad0,
+    0x02b60,
+    0x186e3,
+    0x092e0,
+    0x1c8d7,
+    0x0c950, // 1930-1939
+    0x0d4a0,
+    0x1d8a6,
+    0x0b550,
+    0x056a0,
+    0x1a5b4,
+    0x025d0,
+    0x092d0,
+    0x0d2b2,
+    0x0a950,
+    0x0b557, // 1940-1949
+    0x06ca0,
+    0x0b550,
+    0x15355,
+    0x04da0,
+    0x0a5b0,
+    0x14573,
+    0x052b0,
+    0x0a9a8,
+    0x0e950,
+    0x06aa0, // 1950-1959
+    0x0aea6,
+    0x0ab50,
+    0x04b60,
+    0x0aae4,
+    0x0a570,
+    0x05260,
+    0x0f263,
+    0x0d950,
+    0x05b57,
+    0x056a0, // 1960-1969
+    0x096d0,
+    0x04dd5,
+    0x04ad0,
+    0x0a4d0,
+    0x0d4d4,
+    0x0d250,
+    0x0d558,
+    0x0b540,
+    0x0b6a0,
+    0x195a6, // 1970-1979
+    0x095b0,
+    0x049b0,
+    0x0a974,
+    0x0a4b0,
+    0x0b27a,
+    0x06a50,
+    0x06d40,
+    0x0af46,
+    0x0ab60,
+    0x09570, // 1980-1989
+    0x04af5,
+    0x04970,
+    0x064b0,
+    0x074a3,
+    0x0ea50,
+    0x06b58,
+    0x055c0,
+    0x0ab60,
+    0x096d5,
+    0x092e0, // 1990-1999
+    0x0c960,
+    0x0d954,
+    0x0d4a0,
+    0x0da50,
+    0x07552,
+    0x056a0,
+    0x0abb7,
+    0x025d0,
+    0x092d0,
+    0x0cab5, // 2000-2009
+    0x0a950,
+    0x0b4a0,
+    0x0baa4,
+    0x0ad50,
+    0x055d9,
+    0x04ba0,
+    0x0a5b0,
+    0x15176,
+    0x052b0,
+    0x0a930, // 2010-2019
+    0x07954,
+    0x06aa0,
+    0x0ad50,
+    0x05b52,
+    0x04b60,
+    0x0a6e6,
+    0x0a4e0,
+    0x0d260,
+    0x0ea65,
+    0x0d530, // 2020-2029
+    0x05aa0,
+    0x076a3,
+    0x096d0,
+    0x04afb,
+    0x04ad0,
+    0x0a4d0,
+    0x1d0b6,
+    0x0d250,
+    0x0d520,
+    0x0dd45, // 2030-2039
+    0x0b5a0,
+    0x056d0,
+    0x055b2,
+    0x049b0,
+    0x0a577,
+    0x0a4b0,
+    0x0aa50,
+    0x1b255,
+    0x06d20,
+    0x0ada0, // 2040-2049
+    /** Add By JJonline@JJonline.Cn **/
+    0x14b63,
+    0x09370,
+    0x049f8,
+    0x04970,
+    0x064b0,
+    0x168a6,
+    0x0ea50,
+    0x06b20,
+    0x1a6c4,
+    0x0aae0, // 2050-2059
+    0x0a2e0,
+    0x0d2e3,
+    0x0c960,
+    0x0d557,
+    0x0d4a0,
+    0x0da50,
+    0x05d55,
+    0x056a0,
+    0x0a6d0,
+    0x055d4, // 2060-2069
+    0x052d0,
+    0x0a9b8,
+    0x0a950,
+    0x0b4a0,
+    0x0b6a6,
+    0x0ad50,
+    0x055a0,
+    0x0aba4,
+    0x0a5b0,
+    0x052b0, // 2070-2079
+    0x0b273,
+    0x06930,
+    0x07337,
+    0x06aa0,
+    0x0ad50,
+    0x14b55,
+    0x04b60,
+    0x0a570,
+    0x054e4,
+    0x0d160, // 2080-2089
+    0x0e968,
+    0x0d520,
+    0x0daa0,
+    0x16aa6,
+    0x056d0,
+    0x04ae0,
+    0x0a9d4,
+    0x0a2d0,
+    0x0d150,
+    0x0f252, // 2090-2099
+    0x0d520
+  ], // 2100
+
+  /**
+   * 公历每个月份的天数普通表
+   * @Array Of Property
+   * @return Number
+   */
+  solarMonth: [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31],
+
+  /**
+   * 天干地支之天干速查表
+   * @Array Of Property trans["甲","乙","丙","丁","戊","己","庚","辛","壬","癸"]
+   * @return Cn string
+   */
+  Gan: [
+    '\u7532',
+    '\u4e59',
+    '\u4e19',
+    '\u4e01',
+    '\u620a',
+    '\u5df1',
+    '\u5e9a',
+    '\u8f9b',
+    '\u58ec',
+    '\u7678'
+  ],
+
+  /**
+   * 天干地支之地支速查表
+   * @Array Of Property
+   * @trans["子","丑","寅","卯","辰","巳","午","未","申","酉","戌","亥"]
+   * @return Cn string
+   */
+  Zhi: [
+    '\u5b50',
+    '\u4e11',
+    '\u5bc5',
+    '\u536f',
+    '\u8fb0',
+    '\u5df3',
+    '\u5348',
+    '\u672a',
+    '\u7533',
+    '\u9149',
+    '\u620c',
+    '\u4ea5'
+  ],
+
+  /**
+   * 天干地支之地支速查表<=>生肖
+   * @Array Of Property
+   * @trans["鼠","牛","虎","兔","龙","蛇","马","羊","猴","鸡","狗","猪"]
+   * @return Cn string
+   */
+  Animals: [
+    '\u9f20',
+    '\u725b',
+    '\u864e',
+    '\u5154',
+    '\u9f99',
+    '\u86c7',
+    '\u9a6c',
+    '\u7f8a',
+    '\u7334',
+    '\u9e21',
+    '\u72d7',
+    '\u732a'
+  ],
+
+  /**
+   * 24节气速查表
+   * @Array Of Property
+   * @trans["小寒","大寒","立春","雨水","惊蛰","春分","清明","谷雨","立夏","小满","芒种","夏至","小暑","大暑","立秋","处暑","白露","秋分","寒露","霜降","立冬","小雪","大雪","冬至"]
+   * @return Cn string
+   */
+  solarTerm: [
+    '\u5c0f\u5bd2',
+    '\u5927\u5bd2',
+    '\u7acb\u6625',
+    '\u96e8\u6c34',
+    '\u60ca\u86f0',
+    '\u6625\u5206',
+    '\u6e05\u660e',
+    '\u8c37\u96e8',
+    '\u7acb\u590f',
+    '\u5c0f\u6ee1',
+    '\u8292\u79cd',
+    '\u590f\u81f3',
+    '\u5c0f\u6691',
+    '\u5927\u6691',
+    '\u7acb\u79cb',
+    '\u5904\u6691',
+    '\u767d\u9732',
+    '\u79cb\u5206',
+    '\u5bd2\u9732',
+    '\u971c\u964d',
+    '\u7acb\u51ac',
+    '\u5c0f\u96ea',
+    '\u5927\u96ea',
+    '\u51ac\u81f3'
+  ],
+
+  /**
+   * 1900-2100各年的24节气日期速查表
+   * @Array Of Property
+   * @return 0x string For splice
+   */
+  sTermInfo: [
+    '9778397bd097c36b0b6fc9274c91aa',
+    '97b6b97bd19801ec9210c965cc920e',
+    '97bcf97c3598082c95f8c965cc920f',
+    '97bd0b06bdb0722c965ce1cfcc920f',
+    'b027097bd097c36b0b6fc9274c91aa',
+    '97b6b97bd19801ec9210c965cc920e',
+    '97bcf97c359801ec95f8c965cc920f',
+    '97bd0b06bdb0722c965ce1cfcc920f',
+    'b027097bd097c36b0b6fc9274c91aa',
+    '97b6b97bd19801ec9210c965cc920e',
+    '97bcf97c359801ec95f8c965cc920f',
+    '97bd0b06bdb0722c965ce1cfcc920f',
+    'b027097bd097c36b0b6fc9274c91aa',
+    '9778397bd19801ec9210c965cc920e',
+    '97b6b97bd19801ec95f8c965cc920f',
+    '97bd09801d98082c95f8e1cfcc920f',
+    '97bd097bd097c36b0b6fc9210c8dc2',
+    '9778397bd197c36c9210c9274c91aa',
+    '97b6b97bd19801ec95f8c965cc920e',
+    '97bd09801d98082c95f8e1cfcc920f',
+    '97bd097bd097c36b0b6fc9210c8dc2',
+    '9778397bd097c36c9210c9274c91aa',
+    '97b6b97bd19801ec95f8c965cc920e',
+    '97bcf97c3598082c95f8e1cfcc920f',
+    '97bd097bd097c36b0b6fc9210c8dc2',
+    '9778397bd097c36c9210c9274c91aa',
+    '97b6b97bd19801ec9210c965cc920e',
+    '97bcf97c3598082c95f8c965cc920f',
+    '97bd097bd097c35b0b6fc920fb0722',
+    '9778397bd097c36b0b6fc9274c91aa',
+    '97b6b97bd19801ec9210c965cc920e',
+    '97bcf97c3598082c95f8c965cc920f',
+    '97bd097bd097c35b0b6fc920fb0722',
+    '9778397bd097c36b0b6fc9274c91aa',
+    '97b6b97bd19801ec9210c965cc920e',
+    '97bcf97c359801ec95f8c965cc920f',
+    '97bd097bd097c35b0b6fc920fb0722',
+    '9778397bd097c36b0b6fc9274c91aa',
+    '97b6b97bd19801ec9210c965cc920e',
+    '97bcf97c359801ec95f8c965cc920f',
+    '97bd097bd097c35b0b6fc920fb0722',
+    '9778397bd097c36b0b6fc9274c91aa',
+    '97b6b97bd19801ec9210c965cc920e',
+    '97bcf97c359801ec95f8c965cc920f',
+    '97bd097bd07f595b0b6fc920fb0722',
+    '9778397bd097c36b0b6fc9210c8dc2',
+    '9778397bd19801ec9210c9274c920e',
+    '97b6b97bd19801ec95f8c965cc920f',
+    '97bd07f5307f595b0b0bc920fb0722',
+    '7f0e397bd097c36b0b6fc9210c8dc2',
+    '9778397bd097c36c9210c9274c920e',
+    '97b6b97bd19801ec95f8c965cc920f',
+    '97bd07f5307f595b0b0bc920fb0722',
+    '7f0e397bd097c36b0b6fc9210c8dc2',
+    '9778397bd097c36c9210c9274c91aa',
+    '97b6b97bd19801ec9210c965cc920e',
+    '97bd07f1487f595b0b0bc920fb0722',
+    '7f0e397bd097c36b0b6fc9210c8dc2',
+    '9778397bd097c36b0b6fc9274c91aa',
+    '97b6b97bd19801ec9210c965cc920e',
+    '97bcf7f1487f595b0b0bb0b6fb0722',
+    '7f0e397bd097c35b0b6fc920fb0722',
+    '9778397bd097c36b0b6fc9274c91aa',
+    '97b6b97bd19801ec9210c965cc920e',
+    '97bcf7f1487f595b0b0bb0b6fb0722',
+    '7f0e397bd097c35b0b6fc920fb0722',
+    '9778397bd097c36b0b6fc9274c91aa',
+    '97b6b97bd19801ec9210c965cc920e',
+    '97bcf7f1487f531b0b0bb0b6fb0722',
+    '7f0e397bd097c35b0b6fc920fb0722',
+    '9778397bd097c36b0b6fc9274c91aa',
+    '97b6b97bd19801ec9210c965cc920e',
+    '97bcf7f1487f531b0b0bb0b6fb0722',
+    '7f0e397bd07f595b0b6fc920fb0722',
+    '9778397bd097c36b0b6fc9274c91aa',
+    '97b6b97bd19801ec9210c9274c920e',
+    '97bcf7f0e47f531b0b0bb0b6fb0722',
+    '7f0e397bd07f595b0b0bc920fb0722',
+    '9778397bd097c36b0b6fc9210c91aa',
+    '97b6b97bd197c36c9210c9274c920e',
+    '97bcf7f0e47f531b0b0bb0b6fb0722',
+    '7f0e397bd07f595b0b0bc920fb0722',
+    '9778397bd097c36b0b6fc9210c8dc2',
+    '9778397bd097c36c9210c9274c920e',
+    '97b6b7f0e47f531b0723b0b6fb0722',
+    '7f0e37f5307f595b0b0bc920fb0722',
+    '7f0e397bd097c36b0b6fc9210c8dc2',
+    '9778397bd097c36b0b70c9274c91aa',
+    '97b6b7f0e47f531b0723b0b6fb0721',
+    '7f0e37f1487f595b0b0bb0b6fb0722',
+    '7f0e397bd097c35b0b6fc9210c8dc2',
+    '9778397bd097c36b0b6fc9274c91aa',
+    '97b6b7f0e47f531b0723b0b6fb0721',
+    '7f0e27f1487f595b0b0bb0b6fb0722',
+    '7f0e397bd097c35b0b6fc920fb0722',
+    '9778397bd097c36b0b6fc9274c91aa',
+    '97b6b7f0e47f531b0723b0b6fb0721',
+    '7f0e27f1487f531b0b0bb0b6fb0722',
+    '7f0e397bd097c35b0b6fc920fb0722',
+    '9778397bd097c36b0b6fc9274c91aa',
+    '97b6b7f0e47f531b0723b0b6fb0721',
+    '7f0e27f1487f531b0b0bb0b6fb0722',
+    '7f0e397bd097c35b0b6fc920fb0722',
+    '9778397bd097c36b0b6fc9274c91aa',
+    '97b6b7f0e47f531b0723b0b6fb0721',
+    '7f0e27f1487f531b0b0bb0b6fb0722',
+    '7f0e397bd07f595b0b0bc920fb0722',
+    '9778397bd097c36b0b6fc9274c91aa',
+    '97b6b7f0e47f531b0723b0787b0721',
+    '7f0e27f0e47f531b0b0bb0b6fb0722',
+    '7f0e397bd07f595b0b0bc920fb0722',
+    '9778397bd097c36b0b6fc9210c91aa',
+    '97b6b7f0e47f149b0723b0787b0721',
+    '7f0e27f0e47f531b0723b0b6fb0722',
+    '7f0e397bd07f595b0b0bc920fb0722',
+    '9778397bd097c36b0b6fc9210c8dc2',
+    '977837f0e37f149b0723b0787b0721',
+    '7f07e7f0e47f531b0723b0b6fb0722',
+    '7f0e37f5307f595b0b0bc920fb0722',
+    '7f0e397bd097c35b0b6fc9210c8dc2',
+    '977837f0e37f14998082b0787b0721',
+    '7f07e7f0e47f531b0723b0b6fb0721',
+    '7f0e37f1487f595b0b0bb0b6fb0722',
+    '7f0e397bd097c35b0b6fc9210c8dc2',
+    '977837f0e37f14998082b0787b06bd',
+    '7f07e7f0e47f531b0723b0b6fb0721',
+    '7f0e27f1487f531b0b0bb0b6fb0722',
+    '7f0e397bd097c35b0b6fc920fb0722',
+    '977837f0e37f14998082b0787b06bd',
+    '7f07e7f0e47f531b0723b0b6fb0721',
+    '7f0e27f1487f531b0b0bb0b6fb0722',
+    '7f0e397bd097c35b0b6fc920fb0722',
+    '977837f0e37f14998082b0787b06bd',
+    '7f07e7f0e47f531b0723b0b6fb0721',
+    '7f0e27f1487f531b0b0bb0b6fb0722',
+    '7f0e397bd07f595b0b0bc920fb0722',
+    '977837f0e37f14998082b0787b06bd',
+    '7f07e7f0e47f531b0723b0b6fb0721',
+    '7f0e27f1487f531b0b0bb0b6fb0722',
+    '7f0e397bd07f595b0b0bc920fb0722',
+    '977837f0e37f14998082b0787b06bd',
+    '7f07e7f0e47f149b0723b0787b0721',
+    '7f0e27f0e47f531b0b0bb0b6fb0722',
+    '7f0e397bd07f595b0b0bc920fb0722',
+    '977837f0e37f14998082b0723b06bd',
+    '7f07e7f0e37f149b0723b0787b0721',
+    '7f0e27f0e47f531b0723b0b6fb0722',
+    '7f0e397bd07f595b0b0bc920fb0722',
+    '977837f0e37f14898082b0723b02d5',
+    '7ec967f0e37f14998082b0787b0721',
+    '7f07e7f0e47f531b0723b0b6fb0722',
+    '7f0e37f1487f595b0b0bb0b6fb0722',
+    '7f0e37f0e37f14898082b0723b02d5',
+    '7ec967f0e37f14998082b0787b0721',
+    '7f07e7f0e47f531b0723b0b6fb0722',
+    '7f0e37f1487f531b0b0bb0b6fb0722',
+    '7f0e37f0e37f14898082b0723b02d5',
+    '7ec967f0e37f14998082b0787b06bd',
+    '7f07e7f0e47f531b0723b0b6fb0721',
+    '7f0e37f1487f531b0b0bb0b6fb0722',
+    '7f0e37f0e37f14898082b072297c35',
+    '7ec967f0e37f14998082b0787b06bd',
+    '7f07e7f0e47f531b0723b0b6fb0721',
+    '7f0e27f1487f531b0b0bb0b6fb0722',
+    '7f0e37f0e37f14898082b072297c35',
+    '7ec967f0e37f14998082b0787b06bd',
+    '7f07e7f0e47f531b0723b0b6fb0721',
+    '7f0e27f1487f531b0b0bb0b6fb0722',
+    '7f0e37f0e366aa89801eb072297c35',
+    '7ec967f0e37f14998082b0787b06bd',
+    '7f07e7f0e47f149b0723b0787b0721',
+    '7f0e27f1487f531b0b0bb0b6fb0722',
+    '7f0e37f0e366aa89801eb072297c35',
+    '7ec967f0e37f14998082b0723b06bd',
+    '7f07e7f0e47f149b0723b0787b0721',
+    '7f0e27f0e47f531b0723b0b6fb0722',
+    '7f0e37f0e366aa89801eb072297c35',
+    '7ec967f0e37f14998082b0723b06bd',
+    '7f07e7f0e37f14998083b0787b0721',
+    '7f0e27f0e47f531b0723b0b6fb0722',
+    '7f0e37f0e366aa89801eb072297c35',
+    '7ec967f0e37f14898082b0723b02d5',
+    '7f07e7f0e37f14998082b0787b0721',
+    '7f07e7f0e47f531b0723b0b6fb0722',
+    '7f0e36665b66aa89801e9808297c35',
+    '665f67f0e37f14898082b0723b02d5',
+    '7ec967f0e37f14998082b0787b0721',
+    '7f07e7f0e47f531b0723b0b6fb0722',
+    '7f0e36665b66a449801e9808297c35',
+    '665f67f0e37f14898082b0723b02d5',
+    '7ec967f0e37f14998082b0787b06bd',
+    '7f07e7f0e47f531b0723b0b6fb0721',
+    '7f0e36665b66a449801e9808297c35',
+    '665f67f0e37f14898082b072297c35',
+    '7ec967f0e37f14998082b0787b06bd',
+    '7f07e7f0e47f531b0723b0b6fb0721',
+    '7f0e26665b66a449801e9808297c35',
+    '665f67f0e37f1489801eb072297c35',
+    '7ec967f0e37f14998082b0787b06bd',
+    '7f07e7f0e47f531b0723b0b6fb0721',
+    '7f0e27f1487f531b0b0bb0b6fb0722'
+  ],
+
+  /**
+   * 数字转中文速查表
+   * @Array Of Property
+   * @trans ['日','一','二','三','四','五','六','七','八','九','十']
+   * @return Cn string
+   */
+  nStr1: [
+    '\u65e5',
+    '\u4e00',
+    '\u4e8c',
+    '\u4e09',
+    '\u56db',
+    '\u4e94',
+    '\u516d',
+    '\u4e03',
+    '\u516b',
+    '\u4e5d',
+    '\u5341'
+  ],
+
+  /**
+   * 日期转农历称呼速查表
+   * @Array Of Property
+   * @trans ['初','十','廿','卅']
+   * @return Cn string
+   */
+  nStr2: ['\u521d', '\u5341', '\u5eff', '\u5345'],
+
+  /**
+   * 月份转农历称呼速查表
+   * @Array Of Property
+   * @trans ['正','一','二','三','四','五','六','七','八','九','十','冬','腊']
+   * @return Cn string
+   */
+  nStr3: [
+    '\u6b63',
+    '\u4e8c',
+    '\u4e09',
+    '\u56db',
+    '\u4e94',
+    '\u516d',
+    '\u4e03',
+    '\u516b',
+    '\u4e5d',
+    '\u5341',
+    '\u51ac',
+    '\u814a'
+  ],
+
+  /**
+   * 返回农历y年一整年的总天数
+   * @param lunar Year
+   * @return Number
+   * @eg:var count = calendar.lYearDays(1987) ;//count=387
+   */
+  lYearDays: function(y) {
+    let i
+    let sum = 348
+    for (i = 0x8000; i > 0x8; i >>= 1) {
+      sum += calendar.lunarInfo[y - 1900] & i ? 1 : 0
+    }
+    return sum + calendar.leapDays(y)
+  },
+
+  /**
+   * 返回农历y年闰月是哪个月;若y年没有闰月 则返回0
+   * @param lunar Year
+   * @return Number (0-12)
+   * @eg:var leapMonth = calendar.leapMonth(1987) ;//leapMonth=6
+   */
+  leapMonth: function(y) {
+    // 闰字编码 \u95f0
+    return calendar.lunarInfo[y - 1900] & 0xf
+  },
+
+  /**
+   * 返回农历y年闰月的天数 若该年没有闰月则返回0
+   * @param lunar Year
+   * @return Number (0、29、30)
+   * @eg:var leapMonthDay = calendar.leapDays(1987) ;//leapMonthDay=29
+   */
+  leapDays: function(y) {
+    if (calendar.leapMonth(y)) {
+      return calendar.lunarInfo[y - 1900] & 0x10000 ? 30 : 29
+    }
+    return 0
+  },
+
+  /**
+   * 返回农历y年m月(非闰月)的总天数,计算m为闰月时的天数请使用leapDays方法
+   * @param lunar Year
+   * @return Number (-1、29、30)
+   * @eg:var MonthDay = calendar.monthDays(1987,9) ;//MonthDay=29
+   */
+  monthDays: function(y, m) {
+    if (m > 12 || m < 1) return -1 // 月份参数从1至12,参数错误返回-1
+    return calendar.lunarInfo[y - 1900] & (0x10000 >> m) ? 30 : 29
+  },
+
+  /**
+   * 返回公历(!)y年m月的天数
+   * @param solar Year
+   * @return Number (-1、28、29、30、31)
+   * @eg:var solarMonthDay = calendar.leapDays(1987) ;//solarMonthDay=30
+   */
+  solarDays: function(y, m) {
+    if (m > 12 || m < 1) return -1 // 若参数错误 返回-1
+    const ms = m - 1
+    if (+ms === 1) {
+      // 2月份的闰平规律测算后确认返回28或29
+      return (y % 4 === 0 && y % 100 !== 0) || y % 400 === 0 ? 29 : 28
+    } else {
+      return calendar.solarMonth[ms]
+    }
+  },
+
+  /**
+   * 农历年份转换为干支纪年
+   * @param  lYear 农历年的年份数
+   * @return Cn string
+   */
+  toGanZhiYear: function(lYear) {
+    let ganKey = (lYear - 3) % 10
+    let zhiKey = (lYear - 3) % 12
+    if (+ganKey === 0) ganKey = 10 // 如果余数为0则为最后一个天干
+    if (+zhiKey === 0) zhiKey = 12 // 如果余数为0则为最后一个地支
+    return calendar.Gan[ganKey - 1] + calendar.Zhi[zhiKey - 1]
+  },
+
+  /**
+   * 公历月、日判断所属星座
+   * @param  cMonth [description]
+   * @param  cDay [description]
+   * @return Cn string
+   */
+  toAstro: function(cMonth, cDay) {
+    const s =
+      '\u9b54\u7faf\u6c34\u74f6\u53cc\u9c7c\u767d\u7f8a\u91d1\u725b\u53cc\u5b50\u5de8\u87f9\u72ee\u5b50\u5904\u5973\u5929\u79e4\u5929\u874e\u5c04\u624b\u9b54\u7faf'
+    const arr = [20, 19, 21, 21, 21, 22, 23, 23, 23, 23, 22, 22]
+    return s.substr(cMonth * 2 - (cDay < arr[cMonth - 1] ? 2 : 0), 2) + '\u5ea7' // 座
+  },
+
+  /**
+   * 传入offset偏移量返回干支
+   * @param offset 相对甲子的偏移量
+   * @return Cn string
+   */
+  toGanZhi: function(offset) {
+    return calendar.Gan[offset % 10] + calendar.Zhi[offset % 12]
+  },
+
+  /**
+   * 传入公历(!)y年获得该年第n个节气的公历日期
+   * @param y公历年(1900-2100);n二十四节气中的第几个节气(1~24);从n=1(小寒)算起
+   * @return day Number
+   * @eg:var _24 = calendar.getTerm(1987,3) ;//_24=4;意即1987年2月4日立春
+   */
+  getTerm: function(y, n) {
+    if (y < 1900 || y > 2100) return -1
+    if (n < 1 || n > 24) return -1
+    const _table = calendar.sTermInfo[y - 1900]
+    const _info = [
+      parseInt('0x' + _table.substr(0, 5)).toString(),
+      parseInt('0x' + _table.substr(5, 5)).toString(),
+      parseInt('0x' + _table.substr(10, 5)).toString(),
+      parseInt('0x' + _table.substr(15, 5)).toString(),
+      parseInt('0x' + _table.substr(20, 5)).toString(),
+      parseInt('0x' + _table.substr(25, 5)).toString()
+    ]
+    const _calday = [
+      _info[0].substr(0, 1),
+      _info[0].substr(1, 2),
+      _info[0].substr(3, 1),
+      _info[0].substr(4, 2),
+
+      _info[1].substr(0, 1),
+      _info[1].substr(1, 2),
+      _info[1].substr(3, 1),
+      _info[1].substr(4, 2),
+
+      _info[2].substr(0, 1),
+      _info[2].substr(1, 2),
+      _info[2].substr(3, 1),
+      _info[2].substr(4, 2),
+
+      _info[3].substr(0, 1),
+      _info[3].substr(1, 2),
+      _info[3].substr(3, 1),
+      _info[3].substr(4, 2),
+
+      _info[4].substr(0, 1),
+      _info[4].substr(1, 2),
+      _info[4].substr(3, 1),
+      _info[4].substr(4, 2),
+
+      _info[5].substr(0, 1),
+      _info[5].substr(1, 2),
+      _info[5].substr(3, 1),
+      _info[5].substr(4, 2)
+    ]
+    return parseInt(_calday[n - 1])
+  },
+
+  /**
+   * 传入农历数字月份返回汉语通俗表示法
+   * @param lunar month
+   * @return Cn string
+   * @eg:var cnMonth = calendar.toChinaMonth(12) ;//cnMonth='腊月'
+   */
+  toChinaMonth: function(m) {
+    // 月 => \u6708
+    if (m > 12 || m < 1) return -1 // 若参数错误 返回-1
+    let s = calendar.nStr3[m - 1]
+    s += '\u6708' // 加上月字
+    return s
+  },
+
+  /**
+   * 传入农历日期数字返回汉字表示法
+   * @param lunar day
+   * @return Cn string
+   * @eg:var cnDay = calendar.toChinaDay(21) ;//cnMonth='廿一'
+   */
+  toChinaDay: function(d) {
+    // 日 => \u65e5
+    let s
+    switch (d) {
+      case 10:
+        s = '\u521d\u5341'
+        break
+      case 20:
+        s = '\u4e8c\u5341'
+        break
+      case 30:
+        s = '\u4e09\u5341'
+        break
+      default:
+        s = calendar.nStr2[Math.floor(d / 10)]
+        s += calendar.nStr1[d % 10]
+    }
+    return s
+  },
+
+  /**
+   * 年份转生肖[!仅能大致转换] => 精确划分生肖分界线是“立春”
+   * @param y year
+   * @return Cn string
+   * @eg:var animal = calendar.getAnimal(1987) ;//animal='兔'
+   */
+  getAnimal: function(y) {
+    return calendar.Animals[(y - 4) % 12]
+  },
+
+  /**
+   * 传入阳历年月日获得详细的公历、农历object信息 <=>JSON
+   * @param y  solar year
+   * @param m  solar month
+   * @param d  solar day
+   * @return JSON object
+   * @eg:console.log(calendar.solar2lunar(1987,11,01));
+   */
+  solar2lunar: function(y, m, d) {
+    // 参数区间1900.1.31~2100.12.31
+    // 年份限定、上限
+    if (y < 1900 || y > 2100) {
+      return -1 // undefined转换为数字变为NaN
+    }
+    // 公历传参最下限
+    if (+y === 1900 && +m === 1 && +d < 31) {
+      return -1
+    }
+    // 未传参 获得当天
+    let objDate
+    if (!y) {
+      objDate = new Date()
+    } else {
+      objDate = new Date(y, parseInt(m) - 1, d)
+    }
+    let i
+    let leap = 0
+    let temp = 0
+    // 修正ymd参数
+    y = objDate.getFullYear()
+    m = objDate.getMonth() + 1
+    d = objDate.getDate()
+    let offset =
+      (Date.UTC(objDate.getFullYear(), objDate.getMonth(), objDate.getDate()) -
+        Date.UTC(1900, 0, 31)) /
+      86400000
+    for (i = 1900; i < 2101 && offset > 0; i++) {
+      temp = calendar.lYearDays(i)
+      offset -= temp
+    }
+    if (offset < 0) {
+      offset += temp
+      i--
+    }
+
+    // 是否今天
+    const isTodayObj = new Date()
+    let isToday = false
+    if (
+      isTodayObj.getFullYear() === +y &&
+      isTodayObj.getMonth() + 1 === +m &&
+      isTodayObj.getDate() === +d
+    ) {
+      isToday = true
+    }
+    // 星期几
+    let nWeek = objDate.getDay()
+    const cWeek = calendar.nStr1[nWeek]
+    // 数字表示周几顺应天朝周一开始的惯例
+    if (+nWeek === 0) {
+      nWeek = 7
+    }
+    // 农历年
+    const year = i
+    leap = calendar.leapMonth(i) // 闰哪个月
+    let isLeap = false
+
+    // 效验闰月
+    for (i = 1; i < 13 && offset > 0; i++) {
+      // 闰月
+      if (leap > 0 && i === leap + 1 && isLeap === false) {
+        --i
+        isLeap = true
+        temp = calendar.leapDays(year) // 计算农历闰月天数
+      } else {
+        temp = calendar.monthDays(year, i) // 计算农历普通月天数
+      }
+      // 解除闰月
+      if (isLeap === true && i === leap + 1) isLeap = false
+      offset -= temp
+    }
+    // 闰月导致数组下标重叠取反
+    if (offset === 0 && leap > 0 && i === leap + 1) {
+      if (isLeap) {
+        isLeap = false
+      } else {
+        isLeap = true
+        --i
+      }
+    }
+    if (offset < 0) {
+      offset += temp
+      --i
+    }
+    // 农历月
+    const month = i
+    // 农历日
+    const day = offset + 1
+    // 天干地支处理
+    const sm = m - 1
+    const gzY = calendar.toGanZhiYear(year)
+
+    // 当月的两个节气
+    // bugfix-2017-7-24 11:03:38 use lunar Year Param `y` Not `year`
+    const firstNode = calendar.getTerm(y, m * 2 - 1) // 返回当月「节」为几日开始
+    const secondNode = calendar.getTerm(y, m * 2) // 返回当月「节」为几日开始
+
+    // 依据12节气修正干支月
+    let gzM = calendar.toGanZhi((y - 1900) * 12 + m + 11)
+    if (d >= firstNode) {
+      gzM = calendar.toGanZhi((y - 1900) * 12 + m + 12)
+    }
+
+    // 传入的日期的节气与否
+    let isTerm = false
+    let Term = null
+    if (+firstNode === d) {
+      isTerm = true
+      Term = calendar.solarTerm[m * 2 - 2]
+    }
+    if (+secondNode === d) {
+      isTerm = true
+      Term = calendar.solarTerm[m * 2 - 1]
+    }
+    // 日柱 当月一日与 1900/1/1 相差天数
+    const dayCyclical = Date.UTC(y, sm, 1, 0, 0, 0, 0) / 86400000 + 25567 + 10
+    const gzD = calendar.toGanZhi(dayCyclical + d - 1)
+    // 该日期所属的星座
+    const astro = calendar.toAstro(m, d)
+
+    return {
+      lYear: year,
+      lMonth: month,
+      lDay: day,
+      Animal: calendar.getAnimal(year),
+      IMonthCn: (isLeap ? '\u95f0' : '') + calendar.toChinaMonth(month),
+      IDayCn: calendar.toChinaDay(day),
+      cYear: y,
+      cMonth: m,
+      cDay: d,
+      gzYear: gzY,
+      gzMonth: gzM,
+      gzDay: gzD,
+      isToday: isToday,
+      isLeap: isLeap,
+      nWeek: nWeek,
+      ncWeek: '\u661f\u671f' + cWeek,
+      isTerm: isTerm,
+      Term: Term,
+      astro: astro
+    }
+  },
+
+  /**
+   * 传入农历年月日以及传入的月份是否闰月获得详细的公历、农历object信息 <=>JSON
+   * @param y  lunar year
+   * @param m  lunar month
+   * @param d  lunar day
+   * @param isLeapMonth  lunar month is leap or not.[如果是农历闰月第四个参数赋值true即可]
+   * @return JSON object
+   * @eg:console.log(calendar.lunar2solar(1987,9,10));
+   */
+  lunar2solar: function(y, m, d, isLeapMonth) {
+    // 参数区间1900.1.31~2100.12.1
+    isLeapMonth = !!isLeapMonth
+    // let leapOffset = 0;
+    const leapMonth = calendar.leapMonth(y)
+    // let leapDay = calendar.leapDays(y);
+    if (isLeapMonth && leapMonth !== m) return -1 // 传参要求计算该闰月公历 但该年得出的闰月与传参的月份并不同
+    if (
+      (+y === 2100 && +m === 12 && +d > 1) ||
+      (+y === 1900 && +m === 1 && +d < 31)
+    )
+      return -1 // 超出了最大极限值
+    const day = calendar.monthDays(y, m)
+    let _day = day
+    // bugFix 2016-9-25
+    // if month is leap, _day use leapDays method
+    if (isLeapMonth) {
+      _day = calendar.leapDays(y, m)
+    }
+    if (y < 1900 || y > 2100 || d > _day) return -1 // 参数合法性效验
+
+    // 计算农历的时间差
+    let offset = 0
+    for (let i = 1900; i < y; i++) {
+      offset += calendar.lYearDays(i)
+    }
+    let leap = 0
+    let isAdd = false
+    for (let i = 1; i < m; i++) {
+      leap = calendar.leapMonth(y)
+      if (!isAdd) {
+        // 处理闰月
+        if (leap <= i && leap > 0) {
+          offset += calendar.leapDays(y)
+          isAdd = true
+        }
+      }
+      offset += calendar.monthDays(y, i)
+    }
+    // 转换闰月农历 需补充该年闰月的前一个月的时差
+    if (isLeapMonth) offset += day
+    // 1900年农历正月一日的公历时间为1900年1月30日0时0分0秒(该时间也是本农历的最开始起始点)
+    const stmap = Date.UTC(1900, 1, 30, 0, 0, 0)
+    const calObj = new Date((offset + d - 31) * 86400000 + stmap)
+    const cY = calObj.getUTCFullYear()
+    const cM = calObj.getUTCMonth() + 1
+    const cD = calObj.getUTCDate()
+
+    return calendar.solar2lunar(cY, cM, cD)
+  }
+}
+
+const {
+  Gan,
+  Zhi,
+  nStr1,
+  nStr2,
+  nStr3,
+  Animals,
+  solarTerm,
+  lunarInfo,
+  sTermInfo,
+  solarMonth,
+  ...rest
+} = calendar
+
+export default rest

+ 543 - 0
component/calendar/func/day.js

@@ -0,0 +1,543 @@
+import WxData from './wxData'
+import CalendarConfig from './config'
+import convertSolarLunar from './convertSolarLunar'
+import {
+  Logger,
+  GetDate,
+  getDateTimeStamp,
+  uniqueArrayByDate,
+  delRepeatedEnableDay,
+  convertEnableAreaToTimestamp,
+  converEnableDaysToTimestamp
+} from './utils'
+
+const logger = new Logger()
+const getDate = new GetDate()
+const toString = Object.prototype.toString
+
+class Day extends WxData {
+  constructor(component) {
+    super(component)
+    this.Component = component
+  }
+  getCalendarConfig() {
+    return this.Component.config
+  }
+  /**
+   *
+   * @param {number} year
+   * @param {number} month
+   */
+  buildDate(year, month) {
+    const today = getDate.todayDate()
+    const thisMonthDays = getDate.thisMonthDays(year, month)
+    const dates = []
+    for (let i = 1; i <= thisMonthDays; i++) {
+      const isToday =
+        +today.year === +year && +today.month === +month && i === +today.date
+      const config = this.getCalendarConfig()
+      const date = {
+        year,
+        month,
+        day: i,
+        choosed: false,
+        week: getDate.dayOfWeek(year, month, i),
+        isToday: isToday && config.highlightToday,
+        lunar: convertSolarLunar.solar2lunar(+year, +month, +i)
+      }
+      dates.push(date)
+    }
+    return dates
+  }
+  /**
+   * 指定可选日期范围
+   * @param {array} area 日期访问数组
+   */
+  enableArea(dateArea = []) {
+    if (dateArea.length === 2) {
+      const isRight = this.__judgeParam(dateArea)
+      if (isRight) {
+        let { days = [], selectedDay = [] } = this.getData('calendar')
+        const { startTimestamp, endTimestamp } = convertEnableAreaToTimestamp(
+          dateArea
+        )
+        const dataAfterHandle = this.__handleEnableArea(
+          {
+            dateArea,
+            days,
+            startTimestamp,
+            endTimestamp
+          },
+          selectedDay
+        )
+        this.setData({
+          'calendar.enableArea': dateArea,
+          'calendar.days': dataAfterHandle.dates,
+          'calendar.selectedDay': dataAfterHandle.selectedDay,
+          'calendar.enableAreaTimestamp': [startTimestamp, endTimestamp]
+        })
+      }
+    } else {
+      logger.warn(
+        'enableArea()参数需为时间范围数组,形如:["2018-8-4" , "2018-8-24"]'
+      )
+    }
+  }
+  /**
+   * 指定特定日期可选
+   * @param {array} days 指定日期数组
+   */
+  enableDays(dates = []) {
+    const { enableArea = [] } = this.getData('calendar')
+    let expectEnableDaysTimestamp = []
+    if (enableArea.length) {
+      expectEnableDaysTimestamp = delRepeatedEnableDay(dates, enableArea)
+    } else {
+      expectEnableDaysTimestamp = converEnableDaysToTimestamp(dates)
+    }
+    let { days = [], selectedDay = [] } = this.getData('calendar')
+    const dataAfterHandle = this.__handleEnableDays(
+      {
+        days,
+        expectEnableDaysTimestamp
+      },
+      selectedDay
+    )
+    this.setData({
+      'calendar.days': dataAfterHandle.dates,
+      'calendar.selectedDay': dataAfterHandle.selectedDay,
+      'calendar.enableDays': dates,
+      'calendar.enableDaysTimestamp': expectEnableDaysTimestamp
+    })
+  }
+  /**
+   * 设置多个日期选中
+   * @param {array} selected 需选中日期
+   */
+  setSelectedDays(selected) {
+    const config = CalendarConfig(this.Component).getCalendarConfig()
+    if (!config.multi) {
+      return logger.warn('单选模式下不能设置多日期选中,请配置 multi')
+    }
+    let { days } = this.getData('calendar')
+    let newSelectedDay = []
+    if (!selected) {
+      days.map(item => {
+        item.choosed = true
+        item.showTodoLabel = false
+      })
+      newSelectedDay = days
+    } else if (selected && selected.length) {
+      const { dates, selectedDates } = this.__handleSelectedDays(
+        days,
+        newSelectedDay,
+        selected
+      )
+      days = dates
+      newSelectedDay = selectedDates
+    }
+    CalendarConfig(this.Component).setCalendarConfig('multi', true)
+    this.setData({
+      'calendar.days': days,
+      'calendar.selectedDay': newSelectedDay
+    })
+  }
+  /**
+   * 禁用指定日期
+   * @param {array} dates  禁用
+   */
+  disableDays(dates) {
+    const { disableDays = [], days } = this.getData('calendar')
+    if (Object.prototype.toString.call(dates) !== '[object Array]') {
+      return logger.warn('disableDays 参数为数组')
+    }
+    let _disableDays = []
+    if (dates.length) {
+      _disableDays = uniqueArrayByDate(dates.concat(disableDays))
+      const disableDaysCol = _disableDays.map(d => getDate.toTimeStr(d))
+      days.forEach(item => {
+        const cur = getDate.toTimeStr(item)
+        if (disableDaysCol.includes(cur)) item.disable = true
+      })
+    } else {
+      days.forEach(item => {
+        item.disable = false
+      })
+    }
+    this.setData({
+      'calendar.days': days,
+      'calendar.disableDays': _disableDays
+    })
+  }
+  /**
+   * 设置连续日期选择区域
+   * @param {array} dateArea 区域开始结束日期数组
+   */
+  chooseArea(dateArea = []) {
+    return new Promise((resolve, reject) => {
+      if (dateArea.length === 1) {
+        dateArea = dateArea.concat(dateArea)
+      }
+      if (dateArea.length === 2) {
+        const isRight = this.__judgeParam(dateArea)
+        if (isRight) {
+          const config = CalendarConfig(this.Component).getCalendarConfig()
+          const { startTimestamp, endTimestamp } = convertEnableAreaToTimestamp(
+            dateArea
+          )
+          this.setData(
+            {
+              calendarConfig: {
+                ...config,
+                chooseAreaMode: true,
+                mulit: true
+              },
+              'calendar.chooseAreaTimestamp': [startTimestamp, endTimestamp]
+            },
+            () => {
+              this.__chooseContinuousDates(startTimestamp, endTimestamp)
+                .then(resolve)
+                .catch(reject)
+            }
+          )
+        }
+      }
+    })
+  }
+  __pusheNextMonthDateArea(item, startTimestamp, endTimestamp, selectedDates) {
+    const days = this.buildDate(item.year, item.month)
+    let daysLen = days.length
+    for (let i = 0; i < daysLen; i++) {
+      const item = days[i]
+      const timeStamp = getDateTimeStamp(item)
+      if (timeStamp <= endTimestamp && timeStamp >= startTimestamp) {
+        selectedDates.push({
+          ...item,
+          choosed: true
+        })
+      }
+      if (i === daysLen - 1 && timeStamp < endTimestamp) {
+        this.__pusheNextMonthDateArea(
+          getDate.nextMonth(item),
+          startTimestamp,
+          endTimestamp,
+          selectedDates
+        )
+      }
+    }
+  }
+  __pushPrevMonthDateArea(item, startTimestamp, endTimestamp, selectedDates) {
+    const days = getDate.sortDates(
+      this.buildDate(item.year, item.month),
+      'desc'
+    )
+    let daysLen = days.length
+    let firstDate = getDateTimeStamp(days[0])
+    for (let i = 0; i < daysLen; i++) {
+      const item = days[i]
+      const timeStamp = getDateTimeStamp(item)
+      if (timeStamp >= startTimestamp && timeStamp <= endTimestamp) {
+        selectedDates.push({
+          ...item,
+          choosed: true
+        })
+      }
+      if (i === daysLen - 1 && firstDate > startTimestamp) {
+        this.__pushPrevMonthDateArea(
+          getDate.prevMonth(item),
+          startTimestamp,
+          endTimestamp,
+          selectedDates
+        )
+      }
+    }
+  }
+  /**
+   * 当设置日期区域非当前时保存其他月份的日期至已选日期数组
+   * @param {object} info
+   */
+  __calcDateWhenNotInOneMonth(info) {
+    const {
+      firstDate,
+      lastDate,
+      startTimestamp,
+      endTimestamp,
+      filterSelectedDate
+    } = info
+    if (getDateTimeStamp(firstDate) > startTimestamp) {
+      this.__pushPrevMonthDateArea(
+        getDate.prevMonth(firstDate),
+        startTimestamp,
+        endTimestamp,
+        filterSelectedDate
+      )
+    }
+    if (getDateTimeStamp(lastDate) < endTimestamp) {
+      this.__pusheNextMonthDateArea(
+        getDate.nextMonth(lastDate),
+        startTimestamp,
+        endTimestamp,
+        filterSelectedDate
+      )
+    }
+    const newSelectedDates = [...getDate.sortDates(filterSelectedDate)]
+    return newSelectedDates
+  }
+  /**
+   * 设置连续日期段
+   * @param {number} startTimestamp 连续日期段开始日期时间戳
+   * @param {number} endTimestamp 连续日期段结束日期时间戳
+   */
+  __chooseContinuousDates(startTimestamp, endTimestamp) {
+    return new Promise((resolve, reject) => {
+      const { days, selectedDay = [] } = this.getData('calendar')
+      const selectedDateStr = []
+      let filterSelectedDate = []
+      selectedDay.forEach(item => {
+        const timeStamp = getDateTimeStamp(item)
+        if (timeStamp >= startTimestamp && timeStamp <= endTimestamp) {
+          filterSelectedDate.push(item)
+          selectedDateStr.push(getDate.toTimeStr(item))
+        }
+      })
+      days.forEach(item => {
+        const timeStamp = getDateTimeStamp(item)
+        const dateInSelecedArray = selectedDateStr.includes(
+          getDate.toTimeStr(item)
+        )
+        if (timeStamp >= startTimestamp && timeStamp <= endTimestamp) {
+          if (dateInSelecedArray) {
+            return
+          }
+          item.choosed = true
+          filterSelectedDate.push(item)
+        } else {
+          item.choosed = false
+          if (dateInSelecedArray) {
+            const idx = filterSelectedDate.findIndex(
+              selectedDate =>
+                getDate.toTimeStr(selectedDate) === getDate.toTimeStr(item)
+            )
+            if (idx > -1) {
+              filterSelectedDate.splice(idx, 1)
+            }
+          }
+        }
+      })
+      const firstDate = days[0]
+      const lastDate = days[days.length - 1]
+      const newSelectedDates = this.__calcDateWhenNotInOneMonth({
+        firstDate,
+        lastDate,
+        startTimestamp,
+        endTimestamp,
+        filterSelectedDate
+      })
+      try {
+        this.setData(
+          {
+            'calendar.days': [...days],
+            'calendar.selectedDay': newSelectedDates
+          },
+          () => {
+            resolve(newSelectedDates)
+          }
+        )
+      } catch (err) {
+        reject(err)
+      }
+    })
+  }
+  /**
+   * 设置指定日期样式
+   * @param {array} dates 待设置特殊样式的日期
+   */
+  setDateStyle(dates) {
+    if (toString.call(dates) !== '[object Array]') return
+    const { days, specialStyleDates } = this.getData('calendar')
+    if (toString.call(specialStyleDates) === '[object Array]') {
+      dates = uniqueArrayByDate([...specialStyleDates, ...dates])
+    }
+    const _specialStyleDates = dates.map(
+      item => `${item.year}_${item.month}_${item.day}`
+    )
+    const _days = days.map(item => {
+      const idx = _specialStyleDates.indexOf(
+        `${item.year}_${item.month}_${item.day}`
+      )
+      if (idx > -1) {
+        return {
+          ...item,
+          class: dates[idx].class
+        }
+      } else {
+        return { ...item }
+      }
+    })
+    this.setData({
+      'calendar.days': _days,
+      'calendar.specialStyleDates': dates
+    })
+  }
+  __judgeParam(dateArea) {
+    const {
+      start,
+      end,
+      startTimestamp,
+      endTimestamp
+    } = convertEnableAreaToTimestamp(dateArea)
+    if (!start || !end) return
+    const startMonthDays = getDate.thisMonthDays(start[0], start[1])
+    const endMonthDays = getDate.thisMonthDays(end[0], end[1])
+    if (start[2] > startMonthDays || start[2] < 1) {
+      logger.warn('enableArea() 开始日期错误,指定日期不在当前月份天数范围内')
+      return false
+    } else if (start[1] > 12 || start[1] < 1) {
+      logger.warn('enableArea() 开始日期错误,月份超出1-12月份')
+      return false
+    } else if (end[2] > endMonthDays || end[2] < 1) {
+      logger.warn('enableArea() 截止日期错误,指定日期不在当前月份天数范围内')
+      return false
+    } else if (end[1] > 12 || end[1] < 1) {
+      logger.warn('enableArea() 截止日期错误,月份超出1-12月份')
+      return false
+    } else if (startTimestamp > endTimestamp) {
+      logger.warn('enableArea()参数最小日期大于了最大日期')
+      return false
+    } else {
+      return true
+    }
+  }
+  __getDisableDateTimestamp() {
+    let disableDateTimestamp
+    const { date, type } = this.getCalendarConfig().disableMode || {}
+    if (date) {
+      const t = date.split('-')
+      if (t.length < 3) {
+        logger.warn('配置 disableMode.date 格式错误')
+        return {}
+      }
+      disableDateTimestamp = getDateTimeStamp({
+        year: +t[0],
+        month: +t[1],
+        day: +t[2]
+      })
+    }
+    return {
+      disableDateTimestamp,
+      disableType: type
+    }
+  }
+  __handleEnableArea(data = {}, selectedDay = []) {
+    const { area, days, startTimestamp, endTimestamp } = data
+    const enableDays = this.getData('calendar.enableDays') || []
+    let expectEnableDaysTimestamp = []
+    if (enableDays.length) {
+      expectEnableDaysTimestamp = delRepeatedEnableDay(enableDays, area)
+    }
+    const {
+      disableDateTimestamp,
+      disableType
+    } = this.__getDisableDateTimestamp()
+    const dates = [...days]
+    dates.forEach(item => {
+      const timestamp = +getDate
+        .newDate(item.year, item.month, item.day)
+        .getTime()
+      const ifOutofArea =
+        (+startTimestamp > timestamp || timestamp > +endTimestamp) &&
+        !expectEnableDaysTimestamp.includes(timestamp)
+      if (
+        ifOutofArea ||
+        (disableType === 'before' &&
+          disableDateTimestamp &&
+          timestamp < disableDateTimestamp) ||
+        (disableType === 'after' &&
+          disableDateTimestamp &&
+          timestamp > disableDateTimestamp)
+      ) {
+        item.disable = true
+        if (item.choosed) {
+          item.choosed = false
+          selectedDay = selectedDay.filter(
+            d => getDate.toTimeStr(item) !== getDate.toTimeStr(d)
+          )
+        }
+      } else if (item.disable) {
+        item.disable = false
+      }
+    })
+    return {
+      dates,
+      selectedDay
+    }
+  }
+  __handleEnableDays(data = {}, selectedDay = []) {
+    const { days, expectEnableDaysTimestamp } = data
+    const { enableAreaTimestamp = [] } = this.getData('calendar')
+    const dates = [...days]
+    dates.forEach(item => {
+      const timestamp = getDate
+        .newDate(item.year, item.month, item.day)
+        .getTime()
+      let setDisable = false
+      if (enableAreaTimestamp.length) {
+        if (
+          (+enableAreaTimestamp[0] > +timestamp ||
+            +timestamp > +enableAreaTimestamp[1]) &&
+          !expectEnableDaysTimestamp.includes(+timestamp)
+        ) {
+          setDisable = true
+        }
+      } else if (!expectEnableDaysTimestamp.includes(+timestamp)) {
+        setDisable = true
+      }
+      if (setDisable) {
+        item.disable = true
+        if (item.choosed) {
+          item.choosed = false
+          selectedDay = selectedDay.filter(
+            d => getDate.toTimeStr(item) !== getDate.toTimeStr(d)
+          )
+        }
+      } else {
+        item.disable = false
+      }
+    })
+    return {
+      dates,
+      selectedDay
+    }
+  }
+  __handleSelectedDays(days = [], newSelectedDay = [], selected) {
+    const { selectedDay, showLabelAlways } = this.getData('calendar')
+    if (selectedDay && selectedDay.length) {
+      newSelectedDay = uniqueArrayByDate(selectedDay.concat(selected))
+    } else {
+      newSelectedDay = selected
+    }
+    const { year: curYear, month: curMonth } = days[0]
+    const currentSelectedDays = []
+    newSelectedDay.forEach(item => {
+      if (+item.year === +curYear && +item.month === +curMonth) {
+        currentSelectedDays.push(getDate.toTimeStr(item))
+      }
+    })
+    ;[...days].map(item => {
+      if (currentSelectedDays.includes(getDate.toTimeStr(item))) {
+        item.choosed = true
+        if (showLabelAlways && item.showTodoLabel) {
+          item.showTodoLabel = true
+        } else {
+          item.showTodoLabel = false
+        }
+      }
+    })
+    return {
+      dates: days,
+      selectedDates: newSelectedDay
+    }
+  }
+}
+
+export default component => new Day(component)

+ 375 - 0
component/calendar/func/render.js

@@ -0,0 +1,375 @@
+import Day from './day'
+import Todo from './todo'
+import WxData from './wxData'
+import convertSolarLunar from './convertSolarLunar'
+import {
+  Logger,
+  GetDate,
+  delRepeatedEnableDay,
+  getDateTimeStamp,
+  converEnableDaysToTimestamp
+} from './utils'
+
+const getDate = new GetDate()
+const logger = new Logger()
+
+class Calendar extends WxData {
+  constructor(component) {
+    super(component)
+    this.Component = component
+  }
+  getCalendarConfig() {
+    return this.Component.config
+  }
+  /**
+   * 渲染日历
+   * @param {number} curYear 年份
+   * @param {number} curMonth  月份
+   * @param {number} curDate  日期
+   * @param {boolean} disableSelect 是否禁用选中
+   */
+  renderCalendar(curYear, curMonth, curDate, disableSelect) {
+    return new Promise(resolve => {
+      const config = this.getCalendarConfig()
+      this.calculateEmptyGrids(curYear, curMonth)
+      this.calculateDays(curYear, curMonth, curDate, disableSelect).then(() => {
+        const { todoLabels, specialStyleDates, enableDays, selectedDay } =
+          this.getData('calendar') || {}
+        if (
+          todoLabels &&
+          todoLabels.find(
+            item => +item.month === +curMonth && +item.year === +curYear
+          )
+        ) {
+          Todo(this.Component).setTodoLabels()
+        }
+        if (
+          specialStyleDates &&
+          specialStyleDates.length &&
+          specialStyleDates.find(
+            item => +item.month === +curMonth && +item.year === +curYear
+          )
+        ) {
+          Day(this.Component).setDateStyle(specialStyleDates)
+        }
+
+        if (
+          enableDays &&
+          enableDays.length &&
+          enableDays.find(item => {
+            let ymd = item.split('-')
+            return +ymd[1] === +curMonth && +ymd[0] === +curYear
+          })
+        ) {
+          Day(this.Component).enableDays(enableDays)
+        }
+
+        if (
+          selectedDay &&
+          selectedDay.length &&
+          selectedDay.find(
+            item => +item.month === +curMonth && +item.year === +curYear
+          ) &&
+          config.mulit
+        ) {
+          Day(this.Component).setSelectedDays(selectedDay)
+        }
+
+        if (!this.Component.firstRender) {
+          resolve({
+            firstRender: true
+          })
+        } else {
+          resolve({
+            firstRender: false
+          })
+        }
+      })
+    })
+  }
+  /**
+   * 计算当前月份前后两月应占的格子
+   * @param {number} year 年份
+   * @param {number} month 月份
+   */
+  calculateEmptyGrids(year, month) {
+    this.calculatePrevMonthGrids(year, month)
+    this.calculateNextMonthGrids(year, month)
+  }
+  /**
+   * 计算上月应占的格子
+   * @param {number} year 年份
+   * @param {number} month 月份
+   */
+  calculatePrevMonthGrids(year, month) {
+    let empytGrids = []
+    const prevMonthDays = getDate.thisMonthDays(year, month - 1)
+    let firstDayOfWeek = getDate.firstDayOfWeek(year, month)
+    const config = this.getCalendarConfig() || {}
+    if (config.firstDayOfWeek === 'Mon') {
+      if (firstDayOfWeek === 0) {
+        firstDayOfWeek = 6
+      } else {
+        firstDayOfWeek -= 1
+      }
+    }
+    if (firstDayOfWeek > 0) {
+      const len = prevMonthDays - firstDayOfWeek
+      const { onlyShowCurrentMonth } = config
+      const { showLunar } = this.getCalendarConfig()
+      for (let i = prevMonthDays; i > len; i--) {
+        if (onlyShowCurrentMonth) {
+          empytGrids.push('')
+        } else {
+          empytGrids.push({
+            day: i,
+            lunar: showLunar
+              ? convertSolarLunar.solar2lunar(year, month - 1, i)
+              : null
+          })
+        }
+      }
+      this.setData({
+        'calendar.empytGrids': empytGrids.reverse()
+      })
+    } else {
+      this.setData({
+        'calendar.empytGrids': null
+      })
+    }
+  }
+  /**
+   * 计算下一月日期是否需要多展示的日期
+   * 某些月份日期为5排,某些月份6排,统一为6排
+   * @param {number} year
+   * @param {number} month
+   * @param {object} config
+   */
+  calculateExtraEmptyDate(year, month, config) {
+    let extDate = 0
+    if (+month === 2) {
+      extDate += 7
+      let firstDayofMonth = getDate.dayOfWeek(year, month, 1)
+      if (config.firstDayOfWeek === 'Mon') {
+        if (+firstDayofMonth === 1) extDate += 7
+      } else {
+        if (+firstDayofMonth === 0) extDate += 7
+      }
+    } else {
+      let firstDayofMonth = getDate.dayOfWeek(year, month, 1)
+      if (config.firstDayOfWeek === 'Mon') {
+        if (firstDayofMonth !== 0 && firstDayofMonth < 6) {
+          extDate += 7
+        }
+      } else {
+        if (firstDayofMonth <= 5) {
+          extDate += 7
+        }
+      }
+    }
+    return extDate
+  }
+  /**
+   * 计算下月应占的格子
+   * @param {number} year 年份
+   * @param {number} month  月份
+   */
+  calculateNextMonthGrids(year, month) {
+    let lastEmptyGrids = []
+    const thisMonthDays = getDate.thisMonthDays(year, month)
+    let lastDayWeek = getDate.dayOfWeek(year, month, thisMonthDays)
+    const config = this.getCalendarConfig() || {}
+    if (config.firstDayOfWeek === 'Mon') {
+      if (lastDayWeek === 0) {
+        lastDayWeek = 6
+      } else {
+        lastDayWeek -= 1
+      }
+    }
+    let len = 7 - (lastDayWeek + 1)
+    const { onlyShowCurrentMonth, showLunar } = config
+    if (!onlyShowCurrentMonth) {
+      len = len + this.calculateExtraEmptyDate(year, month, config)
+    }
+    for (let i = 1; i <= len; i++) {
+      if (onlyShowCurrentMonth) {
+        lastEmptyGrids.push('')
+      } else {
+        lastEmptyGrids.push({
+          day: i,
+          lunar: showLunar
+            ? convertSolarLunar.solar2lunar(year, month + 1, i)
+            : null
+        })
+      }
+    }
+    this.setData({
+      'calendar.lastEmptyGrids': lastEmptyGrids
+    })
+  }
+  /**
+   * 日历初始化将默认值写入 selectDay
+   * @param {number} year
+   * @param {number} month
+   * @param {number} curDate
+   */
+  setSelectedDay(year, month, curDate) {
+    let selectedDay = []
+    const config = this.getCalendarConfig()
+    if (config.noDefault) {
+      selectedDay = []
+      config.noDefault = false
+    } else {
+      const data = this.getData('calendar') || {}
+      const { showLunar } = this.getCalendarConfig()
+      selectedDay = curDate
+        ? [
+            {
+              year,
+              month,
+              day: curDate,
+              choosed: true,
+              week: getDate.dayOfWeek(year, month, curDate),
+              lunar: showLunar
+                ? convertSolarLunar.solar2lunar(year, month, curDate)
+                : null
+            }
+          ]
+        : data.selectedDay
+    }
+    return selectedDay
+  }
+  __getDisableDateTimestamp() {
+    let disableDateTimestamp
+    const { date, type } = this.getCalendarConfig().disableMode || {}
+    if (date) {
+      const t = date.split('-')
+      if (t.length < 3) {
+        logger.warn('配置 disableMode.date 格式错误')
+        return {}
+      }
+      disableDateTimestamp = getDateTimeStamp({
+        year: +t[0],
+        month: +t[1],
+        day: +t[2]
+      })
+    }
+    return {
+      disableDateTimestamp,
+      disableType: type
+    }
+  }
+  resetDates() {
+    this.setData({
+      'calendar.days': []
+    })
+  }
+  /**
+   * 设置日历面板数据
+   * @param {number} year 年份
+   * @param {number} month  月份
+   * @param {number} curDate  日期
+   * @param {boolean} disableSelect 是否禁用选中
+   */
+  calculateDays(year, month, curDate, disableSelect) {
+    return new Promise(resolve => {
+      // 避免切换日期时样式残影
+      this.resetDates()
+      let days = []
+      const {
+        disableDays = [],
+        chooseAreaTimestamp = [],
+        selectedDay: selectedDates = []
+      } = this.getData('calendar')
+      days = Day(this.Component).buildDate(year, month)
+      let selectedDay = selectedDates
+      if (!disableSelect) {
+        selectedDay = this.setSelectedDay(year, month, curDate)
+      }
+      const selectedDayStr = selectedDay.map(d => getDate.toTimeStr(d))
+      const disableDaysStr = disableDays.map(d => getDate.toTimeStr(d))
+      const [areaStart, areaEnd] = chooseAreaTimestamp
+      days.forEach(item => {
+        const cur = getDate.toTimeStr(item)
+        const timestamp = getDateTimeStamp(item)
+        if (selectedDayStr.includes(cur) && !disableSelect) {
+          item.choosed = true
+          if (timestamp > areaEnd || timestamp < areaStart) {
+            const idx = selectedDay.findIndex(
+              selectedDate =>
+                getDate.toTimeStr(selectedDate) === getDate.toTimeStr(item)
+            )
+            selectedDay.splice(idx, 1)
+          }
+        } else if (
+          areaStart &&
+          areaEnd &&
+          timestamp >= areaStart &&
+          timestamp <= areaEnd &&
+          !disableSelect
+        ) {
+          item.choosed = true
+          selectedDay.push(item)
+        }
+        if (disableDaysStr.includes(cur)) item.disable = true
+
+        const {
+          disableDateTimestamp,
+          disableType
+        } = this.__getDisableDateTimestamp()
+        let disabelByConfig = false
+        if (disableDateTimestamp) {
+          if (
+            (disableType === 'before' && timestamp < disableDateTimestamp) ||
+            (disableType === 'after' && timestamp > disableDateTimestamp)
+          ) {
+            disabelByConfig = true
+          }
+        }
+        const isDisable = disabelByConfig || this.__isDisable(timestamp)
+        if (isDisable) {
+          item.disable = true
+          item.choosed = false
+        }
+      })
+      this.setData(
+        {
+          'calendar.days': days,
+          'calendar.selectedDay': [...selectedDay] || []
+        },
+        () => {
+          resolve()
+        }
+      )
+    })
+  }
+  __isDisable(timestamp) {
+    const {
+      enableArea = [],
+      enableDays = [],
+      enableAreaTimestamp = []
+    } = this.getData('calendar')
+    let setDisable = false
+    let expectEnableDaysTimestamp = converEnableDaysToTimestamp(enableDays)
+    if (enableArea.length) {
+      expectEnableDaysTimestamp = delRepeatedEnableDay(enableDays, enableArea)
+    }
+    if (enableAreaTimestamp.length) {
+      if (
+        (+enableAreaTimestamp[0] > +timestamp ||
+          +timestamp > +enableAreaTimestamp[1]) &&
+        !expectEnableDaysTimestamp.includes(+timestamp)
+      ) {
+        setDisable = true
+      }
+    } else if (
+      expectEnableDaysTimestamp.length &&
+      !expectEnableDaysTimestamp.includes(+timestamp)
+    ) {
+      setDisable = true
+    }
+    return setDisable
+  }
+}
+
+export default component => new Calendar(component)

+ 182 - 0
component/calendar/func/todo.js

@@ -0,0 +1,182 @@
+import WxData from './wxData'
+import { Logger, uniqueArrayByDate, GetDate } from './utils'
+
+const logger = new Logger()
+const getDate = new GetDate()
+
+class Todo extends WxData {
+  constructor(component) {
+    super(component)
+    this.Component = component
+  }
+  /**
+   * 设置待办事项标志
+   * @param {object} options 待办事项配置
+   */
+  setTodoLabels(options) {
+    if (options) this.Component.todoConfig = options
+    const calendar = this.getData('calendar')
+    if (!calendar || !calendar.days) {
+      return logger.warn('请等待日历初始化完成后再调用该方法')
+    }
+    const dates = [...calendar.days]
+    const { curYear, curMonth } = calendar
+    const {
+      circle,
+      dotColor = '',
+      pos = 'bottom',
+      showLabelAlways,
+      days: todoDays = []
+    } = options || this.Component.todoConfig || {}
+    const { todoLabels = [] } = calendar
+    const currentMonthTodoLabels = this.getTodoLabels({
+      year: curYear,
+      month: curMonth
+    })
+    let newTodoLabels = todoDays.filter(
+      item => +item.year === +curYear && +item.month === +curMonth
+    )
+    if (this.Component.weekMode) {
+      newTodoLabels = todoDays
+    }
+    const allTodos = currentMonthTodoLabels.concat(newTodoLabels)
+    for (let todo of allTodos) {
+      let target
+      if (this.Component.weekMode) {
+        target = dates.find(
+          date =>
+            +todo.year === +date.year &&
+            +todo.month === +date.month &&
+            +todo.day === +date.day
+        )
+      } else {
+        target = dates[todo.day - 1]
+      }
+      if (!target) continue
+      if (showLabelAlways) {
+        target.showTodoLabel = true
+      } else {
+        target.showTodoLabel = !target.choosed
+      }
+      if (target.showTodoLabel) {
+        target.todoText = todo.todoText
+      }
+      target.color = todo.color
+    }
+    const o = {
+      'calendar.days': dates,
+      'calendar.todoLabels': uniqueArrayByDate(todoLabels.concat(todoDays))
+    }
+    if (!circle) {
+      o['calendar.todoLabelPos'] = pos
+      o['calendar.todoLabelColor'] = dotColor
+    }
+    o['calendar.todoLabelCircle'] = circle || false
+    o['calendar.showLabelAlways'] = showLabelAlways || false
+    this.setData(o)
+  }
+  /**
+   *  删除指定日期的待办事项
+   * @param {array} todos 需要删除待办事项的日期
+   */
+  deleteTodoLabels(todos) {
+    if (!(todos instanceof Array) || !todos.length) return
+    const todoLabels = this.filterTodos(todos)
+    const { days: dates, curYear, curMonth } = this.getData('calendar')
+    const currentMonthTodoLabels = todoLabels.filter(
+      item => curYear === +item.year && curMonth === +item.month
+    )
+    dates.forEach(item => {
+      item.showTodoLabel = false
+    })
+    currentMonthTodoLabels.forEach(item => {
+      dates[item.day - 1].showTodoLabel = !dates[item.day - 1].choosed
+    })
+    this.setData({
+      'calendar.days': dates,
+      'calendar.todoLabels': todoLabels
+    })
+  }
+  /**
+   * 清空所有待办事项
+   */
+  clearTodoLabels() {
+    const { days = [] } = this.getData('calendar')
+    const dates = [].concat(days)
+    dates.forEach(item => {
+      item.showTodoLabel = false
+    })
+    this.setData({
+      'calendar.days': dates,
+      'calendar.todoLabels': []
+    })
+  }
+  /**
+   * 获取所有待办事项
+   * @param {object} target 指定年月
+   * @param {number} [target.year] 年
+   * @param {number} [target.month] 月
+   */
+  getTodoLabels(target) {
+    const { todoLabels = [] } = this.getData('calendar')
+    if (target) {
+      const { year, month } = target
+      const _todoLabels = todoLabels.filter(
+        item => +item.year === +year && +item.month === +month
+      )
+      return _todoLabels
+    }
+    return todoLabels
+  }
+  /**
+   * 过滤将删除的待办事项
+   * @param {array} todos 需要删除待办事项
+   */
+  filterTodos(todos) {
+    const todoLabels = this.getData('calendar.todoLabels') || []
+    const deleteTodo = todos.map(item => getDate.toTimeStr(item))
+    return todoLabels.filter(
+      item => !deleteTodo.includes(getDate.toTimeStr(item))
+    )
+  }
+  /**
+   * 单选时显示待办事项
+   * @param {array} todoDays
+   * @param {array} days
+   * @param {array} selectedDays
+   */
+  showTodoLabels(todoDays, days, selectedDays) {
+    todoDays.forEach(item => {
+      if (this.Component.weekMode) {
+        days.forEach((_item, idx) => {
+          if (+_item.day === +item.day) {
+            const day = days[idx]
+            day.hasTodo = true
+            day.todoText = item.todoText
+            if (
+              selectedDays &&
+              selectedDays.length &&
+              +selectedDays[0].day === +item.day
+            ) {
+              day.showTodoLabel = true
+            }
+          }
+        })
+      } else {
+        const day = days[item.day - 1]
+        if (!day) return
+        day.hasTodo = true
+        day.todoText = item.todoText
+        if (
+          selectedDays &&
+          selectedDays.length &&
+          +selectedDays[0].day === +item.day
+        ) {
+          days[selectedDays[0].day - 1].showTodoLabel = true
+        }
+      }
+    })
+  }
+}
+
+export default component => new Todo(component)

+ 367 - 0
component/calendar/func/utils.js

@@ -0,0 +1,367 @@
+import convertSolarLunar from './convertSolarLunar'
+
+let systemInfo
+export function getSystemInfo() {
+  if (systemInfo) return systemInfo
+  systemInfo = wx.getSystemInfoSync()
+  return systemInfo
+}
+
+export function isComponent(target) {
+  return (
+    target &&
+    target.__wxExparserNodeId__ !== void 0 &&
+    typeof target.setData === 'function'
+  )
+}
+
+export class Logger {
+  info(msg) {
+    console.log(
+      '%cInfo: %c' + msg,
+      'color:#FF0080;font-weight:bold',
+      'color: #FF509B'
+    )
+  }
+  warn(msg) {
+    console.log(
+      '%cWarn: %c' + msg,
+      'color:#FF6600;font-weight:bold',
+      'color: #FF9933'
+    )
+  }
+  tips(msg) {
+    console.log(
+      '%cTips: %c' + msg,
+      'color:#00B200;font-weight:bold',
+      'color: #00CC33'
+    )
+  }
+}
+
+export class Slide {
+  /**
+   * 上滑
+   * @param {object} e 事件对象
+   * @returns {boolean} 布尔值
+   */
+  isUp(gesture = {}, touche = {}) {
+    const { startX, startY } = gesture
+    const deltaX = touche.clientX - startX
+    const deltaY = touche.clientY - startY
+    if (deltaY < -60 && deltaX < 20 && deltaX > -20) {
+      this.slideLock = false
+      return true
+    } else {
+      return false
+    }
+  }
+  /**
+   * 下滑
+   * @param {object} e 事件对象
+   * @returns {boolean} 布尔值
+   */
+  isDown(gesture = {}, touche = {}) {
+    const { startX, startY } = gesture
+    const deltaX = touche.clientX - startX
+    const deltaY = touche.clientY - startY
+    if (deltaY > 60 && deltaX < 20 && deltaX > -20) {
+      return true
+    } else {
+      return false
+    }
+  }
+  /**
+   * 左滑
+   * @param {object} e 事件对象
+   * @returns {boolean} 布尔值
+   */
+  isLeft(gesture = {}, touche = {}) {
+    const { startX, startY } = gesture
+    const deltaX = touche.clientX - startX
+    const deltaY = touche.clientY - startY
+    if (deltaX < -60 && deltaY < 20 && deltaY > -20) {
+      return true
+    } else {
+      return false
+    }
+  }
+  /**
+   * 右滑
+   * @param {object} e 事件对象
+   * @returns {boolean} 布尔值
+   */
+  isRight(gesture = {}, touche = {}) {
+    const { startX, startY } = gesture
+    const deltaX = touche.clientX - startX
+    const deltaY = touche.clientY - startY
+
+    if (deltaX > 60 && deltaY < 20 && deltaY > -20) {
+      return true
+    } else {
+      return false
+    }
+  }
+}
+
+export class GetDate {
+  /**
+   * new Date 区分平台
+   * @param {number} year
+   * @param {number} month
+   * @param {number} day
+   */
+  newDate(year, month, day) {
+    let cur = `${+year}-${+month}-${+day}`
+    if (isIos()) {
+      cur = `${+year}/${+month}/${+day}`
+    }
+    return new Date(cur)
+  }
+  /**
+   * 计算指定月份共多少天
+   * @param {number} year 年份
+   * @param {number} month  月份
+   */
+  thisMonthDays(year, month) {
+    return new Date(Date.UTC(year, month, 0)).getUTCDate()
+  }
+  /**
+   * 计算指定月份第一天星期几
+   * @param {number} year 年份
+   * @param {number} month  月份
+   */
+  firstDayOfWeek(year, month) {
+    return new Date(Date.UTC(year, month - 1, 1)).getUTCDay()
+  }
+  /**
+   * 计算指定日期星期几
+   * @param {number} year 年份
+   * @param {number} month  月份
+   * @param {number} date 日期
+   */
+  dayOfWeek(year, month, date) {
+    return new Date(Date.UTC(year, month - 1, date)).getUTCDay()
+  }
+  todayDate() {
+    const _date = new Date()
+    const year = _date.getFullYear()
+    const month = _date.getMonth() + 1
+    const date = _date.getDate()
+    return {
+      year,
+      month,
+      date
+    }
+  }
+  todayTimestamp() {
+    const { year, month, date } = this.todayDate()
+    const timestamp = this.newDate(year, month, date).getTime()
+    return timestamp
+  }
+  toTimeStr(dateInfo) {
+    if (dateInfo.day) {
+      dateInfo.date = dateInfo.day
+    }
+    return `${+dateInfo.year}-${+dateInfo.month}-${+dateInfo.date}`
+  }
+  sortDates(dates, sortType) {
+    return dates.sort(function(a, b) {
+      const at = getDateTimeStamp(a)
+      const bt = getDateTimeStamp(b)
+      if (at < bt && sortType !== 'desc') {
+        return -1
+      } else {
+        return 1
+      }
+    })
+  }
+  prevMonth(dataInfo) {
+    const prevMonthInfo =
+      +dataInfo.month > 1
+        ? {
+            year: dataInfo.year,
+            month: dataInfo.month - 1
+          }
+        : {
+            year: dataInfo.year - 1,
+            month: 12
+          }
+    return prevMonthInfo
+  }
+  nextMonth(dataInfo) {
+    const nextMonthInfo =
+      +dataInfo.month < 12
+        ? {
+            year: dataInfo.year,
+            month: dataInfo.month + 1
+          }
+        : {
+            year: dataInfo.year + 1,
+            month: 1
+          }
+    return nextMonthInfo
+  }
+  convertLunar(dates = []) {
+    const datesWithLunar = dates.map(date => {
+      if (date) {
+        date.lunar = convertSolarLunar.solar2lunar(
+          +date.year,
+          +date.month,
+          +date.day
+        )
+      }
+      return date
+    })
+    return datesWithLunar
+  }
+}
+
+export function isIos() {
+  const sys = getSystemInfo()
+  return /iphone|ios/i.test(sys.platform)
+}
+
+/**
+ * 浅比较对象是否相等
+ * @param {Object} origin 对比源
+ * @param {Object} target 对比目标
+ * @return {Boolean} true 为相等,false 为不等
+ */
+export function shallowEqual(origin, target) {
+  if (origin === target) {
+    return true
+  } else if (
+    typeof origin === 'object' &&
+    origin != null &&
+    typeof target === 'object' &&
+    target != null
+  ) {
+    if (Object.keys(origin).length !== Object.keys(target).length) return false
+    for (var prop in origin) {
+      if (target.hasOwnProperty(prop)) {
+        if (!shallowEqual(origin[prop], target[prop])) return false
+      } else return false
+    }
+    return true
+  } else return false
+}
+
+/**
+ * 获取当前页面实例
+ */
+export function getCurrentPage() {
+  const pages = getCurrentPages()
+  const last = pages.length - 1
+  return pages[last]
+}
+
+export function getComponent(componentId) {
+  const logger = new Logger()
+  let page = getCurrentPage() || {}
+  if (page.selectComponent && typeof page.selectComponent === 'function') {
+    if (componentId) {
+      return page.selectComponent(componentId)
+    } else {
+      logger.warn('请传入组件ID')
+    }
+  } else {
+    logger.warn('该基础库暂不支持多个小程序日历组件')
+  }
+}
+
+/**
+ * 日期数组根据日期去重
+ * @param {array} array 数组
+ */
+export function uniqueArrayByDate(array = []) {
+  let uniqueObject = {}
+  let uniqueArray = []
+  array.forEach(item => {
+    uniqueObject[`${item.year}-${item.month}-${item.day}`] = item
+  })
+  for (let i in uniqueObject) {
+    uniqueArray.push(uniqueObject[i])
+  }
+  return uniqueArray
+}
+
+/**
+ * 指定可选日期及可选日期数组去重
+ * @param {array} enableDays 特定可选日期数组
+ * @param {array} enableArea 可选日期区域数组
+ */
+export function delRepeatedEnableDay(enableDays = [], enableArea = []) {
+  let _startTimestamp
+  let _endTimestamp
+  if (enableArea.length === 2) {
+    const { startTimestamp, endTimestamp } = convertEnableAreaToTimestamp(
+      enableArea
+    )
+    _startTimestamp = startTimestamp
+    _endTimestamp = endTimestamp
+  }
+  const enableDaysTimestamp = converEnableDaysToTimestamp(enableDays)
+  const tmp = enableDaysTimestamp.filter(
+    item => item < _startTimestamp || item > _endTimestamp
+  )
+  return tmp
+}
+
+/**
+ *  指定日期区域转时间戳
+ * @param {array} timearea 时间区域
+ */
+export function convertEnableAreaToTimestamp(timearea = []) {
+  const getDate = new GetDate()
+  const start = timearea[0].split('-')
+  const end = timearea[1].split('-')
+  const logger = new Logger()
+  if (start.length !== 3 || end.length !== 3) {
+    logger.warn('enableArea() 参数格式为: ["2018-2-1", "2018-3-1"]')
+    return {}
+  }
+  const startTimestamp = getDate.newDate(start[0], start[1], start[2]).getTime()
+  const endTimestamp = getDate.newDate(end[0], end[1], end[2]).getTime()
+  return {
+    start,
+    end,
+    startTimestamp,
+    endTimestamp
+  }
+}
+
+/**
+ * 计算指定日期时间戳
+ * @param {object} dateInfo
+ */
+export function getDateTimeStamp(dateInfo) {
+  if (Object.prototype.toString.call(dateInfo) !== '[object Object]') return
+  const getDate = new GetDate()
+  return getDate.newDate(dateInfo.year, dateInfo.month, dateInfo.day).getTime()
+}
+
+/**
+ *  指定特定日期数组转时间戳
+ * @param {array} enableDays 指定时间数组
+ */
+export function converEnableDaysToTimestamp(enableDays = []) {
+  const logger = new Logger()
+  const getDate = new GetDate()
+  const enableDaysTimestamp = []
+  enableDays.forEach(item => {
+    if (typeof item !== 'string')
+      return logger.warn('enableDays()入参日期格式错误')
+    const tmp = item.split('-')
+    if (tmp.length !== 3) return logger.warn('enableDays()入参日期格式错误')
+    const timestamp = getDate.newDate(tmp[0], tmp[1], tmp[2]).getTime()
+    enableDaysTimestamp.push(timestamp)
+  })
+  return enableDaysTimestamp
+}
+
+// 同一页面多个日历组件按先后顺序渲染
+export const initialTasks = {
+  flag: 'finished', // process 处理中,finished 处理完成
+  tasks: []
+}

+ 601 - 0
component/calendar/func/week.js

@@ -0,0 +1,601 @@
+import Day from './day'
+import WxData from './wxData'
+import Render from './render'
+import CalendarConfig from './config'
+import convertSolarLunar from './convertSolarLunar'
+import { GetDate, Logger, getDateTimeStamp } from './utils'
+
+const getDate = new GetDate()
+const logger = new Logger()
+
+class WeekMode extends WxData {
+  constructor(component) {
+    super(component)
+    this.Component = component
+    this.getCalendarConfig = CalendarConfig(this.Component).getCalendarConfig
+  }
+  /**
+   * 周、月视图切换
+   * @param {string} view  视图 [week, month]
+   * @param {object} date  {year: 2017, month: 11, day: 1}
+   */
+  switchWeek(view, date) {
+    return new Promise((resolve, reject) => {
+      const config = CalendarConfig(this.Component).getCalendarConfig()
+      if (config.multi) return logger.warn('多选模式不能切换周月视图')
+      const { selectedDay = [], curYear, curMonth } = this.getData('calendar')
+      let currentDate = []
+      let disableSelected = false
+      if (!selectedDay.length) {
+        currentDate = getDate.todayDate()
+        currentDate.day = currentDate.date
+        disableSelected = true
+        // return this.__tipsWhenCanNotSwtich();
+      } else {
+        currentDate = selectedDay[0]
+      }
+      let selectedDate = date || currentDate
+      const { year, month } = selectedDate
+      const notInCurrentMonth = curYear !== year || curMonth !== month
+      if (view === 'week') {
+        if (this.Component.weekMode) return
+        if ((selectedDay.length && notInCurrentMonth) || !selectedDay.length) {
+          // return this.__tipsWhenCanNotSwtich();
+          disableSelected = true
+          selectedDate = {
+            year: curYear,
+            month: curMonth,
+            day: selectedDate.day
+          }
+        }
+        this.Component.weekMode = true
+        this.setData({
+          'calendarConfig.weekMode': true
+        })
+        this.jump(selectedDate, disableSelected)
+          .then(resolve)
+          .catch(reject)
+      } else {
+        this.Component.weekMode = false
+        this.setData({
+          'calendarConfig.weekMode': false
+        })
+        const disableSelected =
+          (selectedDay.length && notInCurrentMonth) || !selectedDay.length
+        Render(this.Component)
+          .renderCalendar(curYear, curMonth, selectedDate.day, disableSelected)
+          .then(resolve)
+          .catch(reject)
+      }
+    })
+  }
+  /**
+   * 更新当前年月
+   */
+  updateCurrYearAndMonth(type) {
+    let { days, curYear, curMonth } = this.getData('calendar')
+    const { month: firstMonth } = days[0]
+    const { month: lastMonth } = days[days.length - 1]
+    const lastDayOfThisMonth = getDate.thisMonthDays(curYear, curMonth)
+    const lastDayOfThisWeek = days[days.length - 1]
+    const firstDayOfThisWeek = days[0]
+    if (
+      (lastDayOfThisWeek.day + 7 > lastDayOfThisMonth ||
+        (curMonth === firstMonth && firstMonth !== lastMonth)) &&
+      type === 'next'
+    ) {
+      curMonth = curMonth + 1
+      if (curMonth > 12) {
+        curYear = curYear + 1
+        curMonth = 1
+      }
+    } else if (
+      (+firstDayOfThisWeek.day <= 7 ||
+        (curMonth === lastMonth && firstMonth !== lastMonth)) &&
+      type === 'prev'
+    ) {
+      curMonth = curMonth - 1
+      if (curMonth <= 0) {
+        curYear = curYear - 1
+        curMonth = 12
+      }
+    }
+    return {
+      Uyear: curYear,
+      Umonth: curMonth
+    }
+  }
+  /**
+   * 计算周视图下当前这一周和当月的最后一天
+   */
+  calculateLastDay() {
+    const { days = [], curYear, curMonth } = this.getData('calendar')
+    const lastDayInThisWeek = days[days.length - 1].day
+    const lastDayInThisMonth = getDate.thisMonthDays(curYear, curMonth)
+    return { lastDayInThisWeek, lastDayInThisMonth }
+  }
+  /**
+   * 计算周视图下当前这一周第一天
+   */
+  calculateFirstDay() {
+    const { days } = this.getData('calendar')
+    const firstDayInThisWeek = days[0].day
+    return { firstDayInThisWeek }
+  }
+  /**
+   * 当月第一周所有日期范围
+   * @param {number} year
+   * @param {number} month
+   * @param {boolean} firstDayOfWeekIsMon 每周是否配置为以周一开始
+   */
+  firstWeekInMonth(year, month, firstDayOfWeekIsMon) {
+    let firstDay = getDate.dayOfWeek(year, month, 1)
+    if (firstDayOfWeekIsMon && firstDay === 0) {
+      firstDay = 7
+    }
+    const [, end] = [0, 7 - firstDay]
+    let days = this.getData('calendar.days') || []
+    if (this.Component.weekMode) {
+      days = Day(this.Component).buildDate(year, month)
+    }
+    const daysCut = days.slice(0, firstDayOfWeekIsMon ? end + 1 : end)
+    return daysCut
+  }
+  /**
+   * 当月最后一周所有日期范围
+   * @param {number} year
+   * @param {number} month
+   * @param {boolean} firstDayOfWeekIsMon 每周是否配置为以周一开始
+   */
+  lastWeekInMonth(year, month, firstDayOfWeekIsMon) {
+    const lastDay = getDate.thisMonthDays(year, month)
+    const lastDayWeek = getDate.dayOfWeek(year, month, lastDay)
+    const [start, end] = [lastDay - lastDayWeek, lastDay]
+    let days = this.getData('calendar.days') || []
+    if (this.Component.weekMode) {
+      days = Day(this.Component).buildDate(year, month)
+    }
+    const daysCut = days.slice(firstDayOfWeekIsMon ? start : start - 1, end)
+    return daysCut
+  }
+  __getDisableDateTimestamp(config) {
+    const { date, type } = config.disableMode || {}
+    let disableDateTimestamp
+    if (date) {
+      const t = date.split('-')
+      if (t.length < 3) {
+        logger.warn('配置 disableMode.date 格式错误')
+        return {}
+      }
+      disableDateTimestamp = getDateTimeStamp({
+        year: +t[0],
+        month: +t[1],
+        day: +t[2]
+      })
+    }
+    return {
+      disableDateTimestamp,
+      disableType: type
+    }
+  }
+  /**
+   * 渲染日期之前初始化已选日期
+   * @param {array} dates 当前日期数组
+   */
+  initSelectedDay(dates) {
+    let datesCopy = [...dates]
+    const { selectedDay = [] } = this.getData('calendar')
+    const selectedDayStr = selectedDay.map(
+      item => `${+item.year}-${+item.month}-${+item.day}`
+    )
+    const config = this.getCalendarConfig()
+    const {
+      disableDateTimestamp,
+      disableType
+    } = this.__getDisableDateTimestamp(config)
+    datesCopy = datesCopy.map(item => {
+      if (!item) return {}
+      const dateTimestamp = getDateTimeStamp(item)
+      let date = { ...item }
+      if (
+        selectedDayStr.includes(`${+date.year}-${+date.month}-${+date.day}`)
+      ) {
+        date.choosed = true
+      } else {
+        date.choosed = false
+      }
+      if (
+        (disableType === 'after' && dateTimestamp > disableDateTimestamp) ||
+        (disableType === 'before' && dateTimestamp < disableDateTimestamp)
+      ) {
+        date.disable = true
+      }
+      date = this.__setTodoWhenJump(date, config)
+      if (config.showLunar) {
+        date = this.__setSolarLunar(date)
+      }
+      if (config.highlightToday) {
+        date = this.__highlightToday(date)
+      }
+      return date
+    })
+    return datesCopy
+  }
+  /**
+   * 周视图下设置可选日期范围
+   * @param {object} days 当前展示的日期
+   */
+  setEnableAreaOnWeekMode(dates = []) {
+    let { enableAreaTimestamp = [], enableDaysTimestamp = [] } = this.getData(
+      'calendar'
+    )
+    dates.forEach(item => {
+      const timestamp = getDate
+        .newDate(item.year, item.month, item.day)
+        .getTime()
+
+      let setDisable = false
+      if (enableAreaTimestamp.length) {
+        if (
+          (+enableAreaTimestamp[0] > +timestamp ||
+            +timestamp > +enableAreaTimestamp[1]) &&
+          !enableDaysTimestamp.includes(+timestamp)
+        ) {
+          setDisable = true
+        }
+      } else if (
+        enableDaysTimestamp.length &&
+        !enableDaysTimestamp.includes(+timestamp)
+      ) {
+        setDisable = true
+      }
+      if (setDisable) {
+        item.disable = true
+        item.choosed = false
+      }
+      const config = CalendarConfig(this.Component).getCalendarConfig()
+      const {
+        disableDateTimestamp,
+        disableType
+      } = this.__getDisableDateTimestamp(config)
+      if (
+        (disableType === 'before' && timestamp < disableDateTimestamp) ||
+        (disableType === 'after' && timestamp > disableDateTimestamp)
+      ) {
+        item.disable = true
+      }
+    })
+  }
+  updateYMWhenSwipeCalendarHasSelected(dates) {
+    const hasSelectedDate = dates.filter(date => date.choosed)
+    if (hasSelectedDate && hasSelectedDate.length) {
+      const { year, month } = hasSelectedDate[0]
+      return {
+        year,
+        month
+      }
+    }
+    return {}
+  }
+  /**
+   * 计算下一周的日期
+   */
+  calculateNextWeekDays() {
+    let { lastDayInThisWeek, lastDayInThisMonth } = this.calculateLastDay()
+    let { curYear, curMonth } = this.getData('calendar')
+    let days = []
+    if (lastDayInThisMonth - lastDayInThisWeek >= 7) {
+      const { Uyear, Umonth } = this.updateCurrYearAndMonth('next')
+      curYear = Uyear
+      curMonth = Umonth
+      for (let i = lastDayInThisWeek + 1; i <= lastDayInThisWeek + 7; i++) {
+        days.push({
+          year: curYear,
+          month: curMonth,
+          day: i,
+          week: getDate.dayOfWeek(curYear, curMonth, i)
+        })
+      }
+    } else {
+      for (let i = lastDayInThisWeek + 1; i <= lastDayInThisMonth; i++) {
+        days.push({
+          year: curYear,
+          month: curMonth,
+          day: i,
+          week: getDate.dayOfWeek(curYear, curMonth, i)
+        })
+      }
+      const { Uyear, Umonth } = this.updateCurrYearAndMonth('next')
+      curYear = Uyear
+      curMonth = Umonth
+      for (let i = 1; i <= 7 - (lastDayInThisMonth - lastDayInThisWeek); i++) {
+        days.push({
+          year: curYear,
+          month: curMonth,
+          day: i,
+          week: getDate.dayOfWeek(curYear, curMonth, i)
+        })
+      }
+    }
+    days = this.initSelectedDay(days)
+    const {
+      year: updateYear,
+      month: updateMonth
+    } = this.updateYMWhenSwipeCalendarHasSelected(days)
+    if (updateYear && updateMonth) {
+      curYear = updateYear
+      curMonth = updateMonth
+    }
+    this.setEnableAreaOnWeekMode(days)
+    this.setData(
+      {
+        'calendar.curYear': curYear,
+        'calendar.curMonth': curMonth,
+        'calendar.days': days
+      },
+      () => {
+        Day(this.Component).setDateStyle()
+      }
+    )
+  }
+  /**
+   * 计算上一周的日期
+   */
+  calculatePrevWeekDays() {
+    let { firstDayInThisWeek } = this.calculateFirstDay()
+    let { curYear, curMonth } = this.getData('calendar')
+    let days = []
+
+    if (firstDayInThisWeek - 7 > 0) {
+      const { Uyear, Umonth } = this.updateCurrYearAndMonth('prev')
+      curYear = Uyear
+      curMonth = Umonth
+      for (let i = firstDayInThisWeek - 7; i < firstDayInThisWeek; i++) {
+        days.push({
+          year: curYear,
+          month: curMonth,
+          day: i,
+          week: getDate.dayOfWeek(curYear, curMonth, i)
+        })
+      }
+    } else {
+      let temp = []
+      for (let i = 1; i < firstDayInThisWeek; i++) {
+        temp.push({
+          year: curYear,
+          month: curMonth,
+          day: i,
+          week: getDate.dayOfWeek(curYear, curMonth, i)
+        })
+      }
+      const { Uyear, Umonth } = this.updateCurrYearAndMonth('prev')
+      curYear = Uyear
+      curMonth = Umonth
+      const prevMonthDays = getDate.thisMonthDays(curYear, curMonth)
+      for (
+        let i = prevMonthDays - Math.abs(firstDayInThisWeek - 7);
+        i <= prevMonthDays;
+        i++
+      ) {
+        days.push({
+          year: curYear,
+          month: curMonth,
+          day: i,
+          week: getDate.dayOfWeek(curYear, curMonth, i)
+        })
+      }
+      days = days.concat(temp)
+    }
+    days = this.initSelectedDay(days)
+    const {
+      year: updateYear,
+      month: updateMonth
+    } = this.updateYMWhenSwipeCalendarHasSelected(days)
+    if (updateYear && updateMonth) {
+      curYear = updateYear
+      curMonth = updateMonth
+    }
+    this.setEnableAreaOnWeekMode(days)
+    this.setData(
+      {
+        'calendar.curYear': curYear,
+        'calendar.curMonth': curMonth,
+        'calendar.days': days
+      },
+      () => {
+        Day(this.Component).setDateStyle()
+      }
+    )
+  }
+  calculateDatesWhenJump(
+    { year, month, day },
+    { firstWeekDays, lastWeekDays },
+    firstDayOfWeekIsMon
+  ) {
+    const inFirstWeek = this.__dateIsInWeek({ year, month, day }, firstWeekDays)
+    const inLastWeek = this.__dateIsInWeek({ year, month, day }, lastWeekDays)
+    let dates = []
+    if (inFirstWeek) {
+      dates = this.__calculateDatesWhenInFirstWeek(
+        firstWeekDays,
+        firstDayOfWeekIsMon
+      )
+    } else if (inLastWeek) {
+      dates = this.__calculateDatesWhenInLastWeek(
+        lastWeekDays,
+        firstDayOfWeekIsMon
+      )
+    } else {
+      dates = this.__calculateDates({ year, month, day }, firstDayOfWeekIsMon)
+    }
+    return dates
+  }
+  jump({ year, month, day }, disableSelected) {
+    return new Promise(resolve => {
+      if (!day) return
+      const config = this.getCalendarConfig()
+      const firstDayOfWeekIsMon = config.firstDayOfWeek === 'Mon'
+      const firstWeekDays = this.firstWeekInMonth(
+        year,
+        month,
+        firstDayOfWeekIsMon
+      )
+      let lastWeekDays = this.lastWeekInMonth(year, month, firstDayOfWeekIsMon)
+      let dates = this.calculateDatesWhenJump(
+        { year, month, day },
+        {
+          firstWeekDays,
+          lastWeekDays
+        },
+        firstDayOfWeekIsMon
+      )
+      dates = dates.map(d => {
+        let date = { ...d }
+        if (
+          +date.year === +year &&
+          +date.month === +month &&
+          +date.day === +day &&
+          !disableSelected
+        ) {
+          date.choosed = true
+        }
+        date = this.__setTodoWhenJump(date, config)
+        if (config.showLunar) {
+          date = this.__setSolarLunar(date)
+        }
+        if (config.highlightToday) {
+          date = this.__highlightToday(date)
+        }
+        return date
+      })
+      this.setEnableAreaOnWeekMode(dates)
+      const tmpData = {
+        'calendar.days': dates,
+        'calendar.curYear': year,
+        'calendar.curMonth': month,
+        'calendar.empytGrids': [],
+        'calendar.lastEmptyGrids': []
+      }
+      if (!disableSelected) {
+        tmpData['calendar.selectedDay'] = dates.filter(item => item.choosed)
+      }
+      this.setData(tmpData, () => {
+        Day(this.Component).setDateStyle()
+        resolve({ year, month, date: day })
+      })
+    })
+  }
+  __setTodoWhenJump(dateInfo) {
+    const date = { ...dateInfo }
+    const { todoLabels = [], showLabelAlways } = this.getData('calendar')
+    const todosStr = todoLabels.map(d => `${+d.year}-${+d.month}-${+d.day}`)
+    const idx = todosStr.indexOf(`${+date.year}-${+date.month}-${+date.day}`)
+    if (idx !== -1) {
+      if (showLabelAlways) {
+        date.showTodoLabel = true
+      } else {
+        date.showTodoLabel = !date.choosed
+      }
+      const todo = todoLabels[idx] || {}
+      if (date.showTodoLabel && todo.todoText) date.todoText = todo.todoText
+      if (todo.color) date.color = todo.color
+    }
+    return date
+  }
+  __setSolarLunar(dateInfo) {
+    const date = { ...dateInfo }
+    date.lunar = convertSolarLunar.solar2lunar(
+      +date.year,
+      +date.month,
+      +date.day
+    )
+    return date
+  }
+  __highlightToday(dateInfo) {
+    const date = { ...dateInfo }
+    const today = getDate.todayDate()
+    const isToday =
+      +today.year === +date.year &&
+      +today.month === +date.month &&
+      +date.day === +today.date
+    date.isToday = isToday
+    return date
+  }
+  __calculateDatesWhenInFirstWeek(firstWeekDays) {
+    const dates = [...firstWeekDays]
+    if (dates.length < 7) {
+      let { year, month } = dates[0]
+      let len = 7 - dates.length
+      let lastDate
+      if (month > 1) {
+        month -= 1
+        lastDate = getDate.thisMonthDays(year, month)
+      } else {
+        month = 12
+        year -= 1
+        lastDate = getDate.thisMonthDays(year, month)
+      }
+      while (len) {
+        dates.unshift({
+          year,
+          month,
+          day: lastDate,
+          week: getDate.dayOfWeek(year, month, lastDate)
+        })
+        lastDate -= 1
+        len -= 1
+      }
+    }
+    return dates
+  }
+  __calculateDatesWhenInLastWeek(lastWeekDays) {
+    const dates = [...lastWeekDays]
+    if (dates.length < 7) {
+      let { year, month } = dates[0]
+      let len = 7 - dates.length
+      let firstDate = 1
+      if (month > 11) {
+        month = 1
+        year += 1
+      } else {
+        month += 1
+      }
+      while (len) {
+        dates.push({
+          year,
+          month,
+          day: firstDate,
+          week: getDate.dayOfWeek(year, month, firstDate)
+        })
+        firstDate += 1
+        len -= 1
+      }
+    }
+    return dates
+  }
+  __calculateDates({ year, month, day }, firstDayOfWeekIsMon) {
+    const week = getDate.dayOfWeek(year, month, day)
+    let range = [day - week, day + (6 - week)]
+    if (firstDayOfWeekIsMon) {
+      range = [day + 1 - week, day + (7 - week)]
+    }
+    const dates = Day(this.Component).buildDate(year, month)
+    const weekDates = dates.slice(range[0] - 1, range[1])
+    return weekDates
+  }
+  __dateIsInWeek(date, week) {
+    return week.find(
+      item =>
+        +item.year === +date.year &&
+        +item.month === +date.month &&
+        +item.day === +date.day
+    )
+  }
+  __tipsWhenCanNotSwtich() {
+    logger.info(
+      '当前月份未选中日期下切换为周视图,不能明确该展示哪一周的日期,故此情况不允许切换'
+    )
+  }
+}
+
+export default component => new WeekMode(component)

+ 26 - 0
component/calendar/func/wxData.js

@@ -0,0 +1,26 @@
+class WxData {
+  constructor(component) {
+    this.Component = component
+  }
+  getData(key) {
+    const data = this.Component.data
+    if (!key) return data
+    if (key.includes('.')) {
+      let keys = key.split('.')
+      const tmp = keys.reduce((prev, next) => {
+        return prev[next]
+      }, data)
+      return tmp
+    } else {
+      return this.Component.data[key]
+    }
+  }
+  setData(data, cb = () => {}) {
+    if (!data) return
+    if (typeof data === 'object') {
+      this.Component.setData(data, cb)
+    }
+  }
+}
+
+export default WxData

+ 244 - 2277
component/calendar/index.js

@@ -1,2287 +1,254 @@
-!(function(e) {
-  var t = {};
-  function a(n) {
-    if (t[n]) return t[n].exports;
-    var s = (t[n] = { i: n, l: !1, exports: {} });
-    return e[n].call(s.exports, s, s.exports, a), (s.l = !0), s.exports;
-  }
-  (a.m = e),
-    (a.c = t),
-    (a.d = function(e, t, n) {
-      a.o(e, t) || Object.defineProperty(e, t, { enumerable: !0, get: n });
-    }),
-    (a.r = function(e) {
-      'undefined' != typeof Symbol &&
-        Symbol.toStringTag &&
-        Object.defineProperty(e, Symbol.toStringTag, { value: 'Module' }),
-        Object.defineProperty(e, '__esModule', { value: !0 });
-    }),
-    (a.t = function(e, t) {
-      if ((1 & t && (e = a(e)), 8 & t)) return e;
-      if (4 & t && 'object' == typeof e && e && e.__esModule) return e;
-      var n = Object.create(null);
-      if (
-        (a.r(n),
-        Object.defineProperty(n, 'default', { enumerable: !0, value: e }),
-        2 & t && 'string' != typeof e)
-      )
-        for (var s in e)
-          a.d(
-            n,
-            s,
-            function(t) {
-              return e[t];
-            }.bind(null, s)
-          );
-      return n;
-    }),
-    (a.n = function(e) {
-      var t =
-        e && e.__esModule
-          ? function() {
-              return e.default;
-            }
-          : function() {
-              return e;
-            };
-      return a.d(t, 'a', t), t;
-    }),
-    (a.o = function(e, t) {
-      return Object.prototype.hasOwnProperty.call(e, t);
-    }),
-    (a.p = ''),
-    a((a.s = 7));
-})([
-  function(e, t, a) {
-    'use strict';
-    let n;
-    function s() {
-      return n || (n = wx.getSystemInfoSync());
-    }
-    Object.defineProperty(t, '__esModule', { value: !0 }),
-      (t.getSystemInfo = s),
-      (t.isComponent = function(e) {
-        return (
-          e &&
-          void 0 !== e.__wxExparserNodeId__ &&
-          'function' == typeof e.setData
-        );
-      }),
-      (t.isIos = c),
-      (t.getCurrentPage = l),
-      (t.getComponent = function(e) {
-        const t = new r();
-        let a = l() || {};
-        if (a.selectComponent && 'function' == typeof a.selectComponent) {
-          if (e) return a.selectComponent(e);
-          t.warn('请传入组件ID');
-        } else t.warn('该基础库暂不支持多个小程序日历组件');
-      }),
-      (t.uniqueArrayByDate = function(e = []) {
-        let t = {},
-          a = [];
-        e.forEach(e => {
-          t[`${e.year}-${e.month}-${e.day}`] = e;
-        });
-        for (let e in t) a.push(t[e]);
-        return a;
-      }),
-      (t.delRepeatedEnableDay = function(e = [], t = []) {
-        let a, n;
-        if (2 === t.length) {
-          const { startTimestamp: e, endTimestamp: s } = b(t);
-          (a = e), (n = s);
-        }
-        return d(e).filter(e => e < a || e > n);
-      }),
-      (t.convertEnableAreaToTimestamp = b),
-      (t.converEnableDaysToTimestamp = d),
-      (t.initialTasks = t.GetDate = t.Slide = t.Logger = void 0);
-    class r {
-      info(e) {
-        console.log(
-          '%cInfo: %c' + e,
-          'color:#FF0080;font-weight:bold',
-          'color: #FF509B'
-        );
-      }
-      warn(e) {
-        console.log(
-          '%cWarn: %c' + e,
-          'color:#FF6600;font-weight:bold',
-          'color: #FF9933'
-        );
-      }
-      tips(e) {
-        console.log(
-          '%cTips: %c' + e,
-          'color:#00B200;font-weight:bold',
-          'color: #00CC33'
-        );
-      }
-    }
-    t.Logger = r;
-    t.Slide = class {
-      isUp(e = {}, t = {}) {
-        const { startX: a, startY: n } = e,
-          s = t.clientX - a;
-        return (
-          t.clientY - n < -60 &&
-          s < 20 &&
-          s > -20 &&
-          ((this.slideLock = !1), !0)
-        );
-      }
-      isDown(e = {}, t = {}) {
-        const { startX: a, startY: n } = e,
-          s = t.clientX - a;
-        return t.clientY - n > 60 && s < 20 && s > -20;
-      }
-      isLeft(e = {}, t = {}) {
-        const { startX: a, startY: n } = e,
-          s = t.clientX - a,
-          r = t.clientY - n;
-        return s < -60 && r < 20 && r > -20;
-      }
-      isRight(e = {}, t = {}) {
-        const { startX: a, startY: n } = e,
-          s = t.clientX - a,
-          r = t.clientY - n;
-        return s > 60 && r < 20 && r > -20;
-      }
-    };
-    class o {
-      newDate(e, t, a) {
-        let n = `${+e}-${+t}-${+a}`;
-        return c() && (n = `${+e}/${+t}/${+a}`), new Date(n);
-      }
-      thisMonthDays(e, t) {
-        return new Date(Date.UTC(e, t, 0)).getUTCDate();
-      }
-      firstDayOfWeek(e, t) {
-        return new Date(Date.UTC(e, t - 1, 1)).getUTCDay();
-      }
-      dayOfWeek(e, t, a) {
-        return new Date(Date.UTC(e, t - 1, a)).getUTCDay();
-      }
-      todayDate() {
-        const e = new Date();
-        return {
-          year: e.getFullYear(),
-          month: e.getMonth() + 1,
-          date: e.getDate()
-        };
-      }
-      todayTimestamp() {
-        const { year: e, month: t, date: a } = this.todayDate();
-        return this.newDate(e, t, a).getTime();
-      }
-    }
-    function c() {
-      const e = s();
-      return /iphone|ios/i.test(e.platform);
-    }
-    function l() {
-      const e = getCurrentPages();
-      return e[e.length - 1];
-    }
-    function b(e = []) {
-      const t = new o(),
-        a = e[0].split('-'),
-        n = e[1].split('-'),
-        s = new r();
-      return 3 !== a.length || 3 !== n.length
-        ? (s.warn('enableArea() 参数格式为: ["2018-2-1", "2018-3-1"]'), {})
-        : {
-            start: a,
-            end: n,
-            startTimestamp: t.newDate(a[0], a[1], a[2]).getTime(),
-            endTimestamp: t.newDate(n[0], n[1], n[2]).getTime()
-          };
-    }
-    function d(e = []) {
-      const t = new r(),
-        a = new o(),
-        n = [];
-      return (
-        e.forEach(e => {
-          if ('string' != typeof e)
-            return t.warn('enableDays()入参日期格式错误');
-          const s = e.split('-');
-          if (3 !== s.length) return t.warn('enableDays()入参日期格式错误');
-          const r = a.newDate(s[0], s[1], s[2]).getTime();
-          n.push(r);
-        }),
-        n
-      );
-    }
-    t.GetDate = o;
-    t.initialTasks = { flag: 'finished', tasks: [] };
-  },
-  function(e, t, a) {
-    'use strict';
-    Object.defineProperty(t, '__esModule', { value: !0 }), (t.default = void 0);
-    var n = class {
-      constructor(e) {
-        this.Component = e;
-      }
-      getData(e) {
-        const t = this.Component.data;
-        if (!e) return t;
-        if (e.includes('.')) {
-          return e.split('.').reduce((e, t) => e[t], t);
-        }
-        return this.Component.data[e];
-      }
-      setData(e, t = () => {}) {
-        e && 'object' == typeof e && this.Component.setData(e, t);
-      }
-    };
-    t.default = n;
-  },
-  function(e, t, a) {
-    'use strict';
-    Object.defineProperty(t, '__esModule', { value: !0 }), (t.default = void 0);
-    const n = {
-        lunarInfo: [
-          19416,
-          19168,
-          42352,
-          21717,
-          53856,
-          55632,
-          91476,
-          22176,
-          39632,
-          21970,
-          19168,
-          42422,
-          42192,
-          53840,
-          119381,
-          46400,
-          54944,
-          44450,
-          38320,
-          84343,
-          18800,
-          42160,
-          46261,
-          27216,
-          27968,
-          109396,
-          11104,
-          38256,
-          21234,
-          18800,
-          25958,
-          54432,
-          59984,
-          28309,
-          23248,
-          11104,
-          100067,
-          37600,
-          116951,
-          51536,
-          54432,
-          120998,
-          46416,
-          22176,
-          107956,
-          9680,
-          37584,
-          53938,
-          43344,
-          46423,
-          27808,
-          46416,
-          86869,
-          19872,
-          42416,
-          83315,
-          21168,
-          43432,
-          59728,
-          27296,
-          44710,
-          43856,
-          19296,
-          43748,
-          42352,
-          21088,
-          62051,
-          55632,
-          23383,
-          22176,
-          38608,
-          19925,
-          19152,
-          42192,
-          54484,
-          53840,
-          54616,
-          46400,
-          46752,
-          103846,
-          38320,
-          18864,
-          43380,
-          42160,
-          45690,
-          27216,
-          27968,
-          44870,
-          43872,
-          38256,
-          19189,
-          18800,
-          25776,
-          29859,
-          59984,
-          27480,
-          21952,
-          43872,
-          38613,
-          37600,
-          51552,
-          55636,
-          54432,
-          55888,
-          30034,
-          22176,
-          43959,
-          9680,
-          37584,
-          51893,
-          43344,
-          46240,
-          47780,
-          44368,
-          21977,
-          19360,
-          42416,
-          86390,
-          21168,
-          43312,
-          31060,
-          27296,
-          44368,
-          23378,
-          19296,
-          42726,
-          42208,
-          53856,
-          60005,
-          54576,
-          23200,
-          30371,
-          38608,
-          19195,
-          19152,
-          42192,
-          118966,
-          53840,
-          54560,
-          56645,
-          46496,
-          22224,
-          21938,
-          18864,
-          42359,
-          42160,
-          43600,
-          111189,
-          27936,
-          44448,
-          84835,
-          37744,
-          18936,
-          18800,
-          25776,
-          92326,
-          59984,
-          27424,
-          108228,
-          43744,
-          41696,
-          53987,
-          51552,
-          54615,
-          54432,
-          55888,
-          23893,
-          22176,
-          42704,
-          21972,
-          21200,
-          43448,
-          43344,
-          46240,
-          46758,
-          44368,
-          21920,
-          43940,
-          42416,
-          21168,
-          45683,
-          26928,
-          29495,
-          27296,
-          44368,
-          84821,
-          19296,
-          42352,
-          21732,
-          53600,
-          59752,
-          54560,
-          55968,
-          92838,
-          22224,
-          19168,
-          43476,
-          41680,
-          53584,
-          62034,
-          54560
-        ],
-        solarMonth: [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31],
-        Gan: ['甲', '乙', '丙', '丁', '戊', '己', '庚', '辛', '壬', '癸'],
-        Zhi: [
-          '子',
-          '丑',
-          '寅',
-          '卯',
-          '辰',
-          '巳',
-          '午',
-          '未',
-          '申',
-          '酉',
-          '戌',
-          '亥'
-        ],
-        Animals: [
-          '鼠',
-          '牛',
-          '虎',
-          '兔',
-          '龙',
-          '蛇',
-          '马',
-          '羊',
-          '猴',
-          '鸡',
-          '狗',
-          '猪'
-        ],
-        solarTerm: [
-          '小寒',
-          '大寒',
-          '立春',
-          '雨水',
-          '惊蛰',
-          '春分',
-          '清明',
-          '谷雨',
-          '立夏',
-          '小满',
-          '芒种',
-          '夏至',
-          '小暑',
-          '大暑',
-          '立秋',
-          '处暑',
-          '白露',
-          '秋分',
-          '寒露',
-          '霜降',
-          '立冬',
-          '小雪',
-          '大雪',
-          '冬至'
-        ],
-        sTermInfo: [
-          '9778397bd097c36b0b6fc9274c91aa',
-          '97b6b97bd19801ec9210c965cc920e',
-          '97bcf97c3598082c95f8c965cc920f',
-          '97bd0b06bdb0722c965ce1cfcc920f',
-          'b027097bd097c36b0b6fc9274c91aa',
-          '97b6b97bd19801ec9210c965cc920e',
-          '97bcf97c359801ec95f8c965cc920f',
-          '97bd0b06bdb0722c965ce1cfcc920f',
-          'b027097bd097c36b0b6fc9274c91aa',
-          '97b6b97bd19801ec9210c965cc920e',
-          '97bcf97c359801ec95f8c965cc920f',
-          '97bd0b06bdb0722c965ce1cfcc920f',
-          'b027097bd097c36b0b6fc9274c91aa',
-          '9778397bd19801ec9210c965cc920e',
-          '97b6b97bd19801ec95f8c965cc920f',
-          '97bd09801d98082c95f8e1cfcc920f',
-          '97bd097bd097c36b0b6fc9210c8dc2',
-          '9778397bd197c36c9210c9274c91aa',
-          '97b6b97bd19801ec95f8c965cc920e',
-          '97bd09801d98082c95f8e1cfcc920f',
-          '97bd097bd097c36b0b6fc9210c8dc2',
-          '9778397bd097c36c9210c9274c91aa',
-          '97b6b97bd19801ec95f8c965cc920e',
-          '97bcf97c3598082c95f8e1cfcc920f',
-          '97bd097bd097c36b0b6fc9210c8dc2',
-          '9778397bd097c36c9210c9274c91aa',
-          '97b6b97bd19801ec9210c965cc920e',
-          '97bcf97c3598082c95f8c965cc920f',
-          '97bd097bd097c35b0b6fc920fb0722',
-          '9778397bd097c36b0b6fc9274c91aa',
-          '97b6b97bd19801ec9210c965cc920e',
-          '97bcf97c3598082c95f8c965cc920f',
-          '97bd097bd097c35b0b6fc920fb0722',
-          '9778397bd097c36b0b6fc9274c91aa',
-          '97b6b97bd19801ec9210c965cc920e',
-          '97bcf97c359801ec95f8c965cc920f',
-          '97bd097bd097c35b0b6fc920fb0722',
-          '9778397bd097c36b0b6fc9274c91aa',
-          '97b6b97bd19801ec9210c965cc920e',
-          '97bcf97c359801ec95f8c965cc920f',
-          '97bd097bd097c35b0b6fc920fb0722',
-          '9778397bd097c36b0b6fc9274c91aa',
-          '97b6b97bd19801ec9210c965cc920e',
-          '97bcf97c359801ec95f8c965cc920f',
-          '97bd097bd07f595b0b6fc920fb0722',
-          '9778397bd097c36b0b6fc9210c8dc2',
-          '9778397bd19801ec9210c9274c920e',
-          '97b6b97bd19801ec95f8c965cc920f',
-          '97bd07f5307f595b0b0bc920fb0722',
-          '7f0e397bd097c36b0b6fc9210c8dc2',
-          '9778397bd097c36c9210c9274c920e',
-          '97b6b97bd19801ec95f8c965cc920f',
-          '97bd07f5307f595b0b0bc920fb0722',
-          '7f0e397bd097c36b0b6fc9210c8dc2',
-          '9778397bd097c36c9210c9274c91aa',
-          '97b6b97bd19801ec9210c965cc920e',
-          '97bd07f1487f595b0b0bc920fb0722',
-          '7f0e397bd097c36b0b6fc9210c8dc2',
-          '9778397bd097c36b0b6fc9274c91aa',
-          '97b6b97bd19801ec9210c965cc920e',
-          '97bcf7f1487f595b0b0bb0b6fb0722',
-          '7f0e397bd097c35b0b6fc920fb0722',
-          '9778397bd097c36b0b6fc9274c91aa',
-          '97b6b97bd19801ec9210c965cc920e',
-          '97bcf7f1487f595b0b0bb0b6fb0722',
-          '7f0e397bd097c35b0b6fc920fb0722',
-          '9778397bd097c36b0b6fc9274c91aa',
-          '97b6b97bd19801ec9210c965cc920e',
-          '97bcf7f1487f531b0b0bb0b6fb0722',
-          '7f0e397bd097c35b0b6fc920fb0722',
-          '9778397bd097c36b0b6fc9274c91aa',
-          '97b6b97bd19801ec9210c965cc920e',
-          '97bcf7f1487f531b0b0bb0b6fb0722',
-          '7f0e397bd07f595b0b6fc920fb0722',
-          '9778397bd097c36b0b6fc9274c91aa',
-          '97b6b97bd19801ec9210c9274c920e',
-          '97bcf7f0e47f531b0b0bb0b6fb0722',
-          '7f0e397bd07f595b0b0bc920fb0722',
-          '9778397bd097c36b0b6fc9210c91aa',
-          '97b6b97bd197c36c9210c9274c920e',
-          '97bcf7f0e47f531b0b0bb0b6fb0722',
-          '7f0e397bd07f595b0b0bc920fb0722',
-          '9778397bd097c36b0b6fc9210c8dc2',
-          '9778397bd097c36c9210c9274c920e',
-          '97b6b7f0e47f531b0723b0b6fb0722',
-          '7f0e37f5307f595b0b0bc920fb0722',
-          '7f0e397bd097c36b0b6fc9210c8dc2',
-          '9778397bd097c36b0b70c9274c91aa',
-          '97b6b7f0e47f531b0723b0b6fb0721',
-          '7f0e37f1487f595b0b0bb0b6fb0722',
-          '7f0e397bd097c35b0b6fc9210c8dc2',
-          '9778397bd097c36b0b6fc9274c91aa',
-          '97b6b7f0e47f531b0723b0b6fb0721',
-          '7f0e27f1487f595b0b0bb0b6fb0722',
-          '7f0e397bd097c35b0b6fc920fb0722',
-          '9778397bd097c36b0b6fc9274c91aa',
-          '97b6b7f0e47f531b0723b0b6fb0721',
-          '7f0e27f1487f531b0b0bb0b6fb0722',
-          '7f0e397bd097c35b0b6fc920fb0722',
-          '9778397bd097c36b0b6fc9274c91aa',
-          '97b6b7f0e47f531b0723b0b6fb0721',
-          '7f0e27f1487f531b0b0bb0b6fb0722',
-          '7f0e397bd097c35b0b6fc920fb0722',
-          '9778397bd097c36b0b6fc9274c91aa',
-          '97b6b7f0e47f531b0723b0b6fb0721',
-          '7f0e27f1487f531b0b0bb0b6fb0722',
-          '7f0e397bd07f595b0b0bc920fb0722',
-          '9778397bd097c36b0b6fc9274c91aa',
-          '97b6b7f0e47f531b0723b0787b0721',
-          '7f0e27f0e47f531b0b0bb0b6fb0722',
-          '7f0e397bd07f595b0b0bc920fb0722',
-          '9778397bd097c36b0b6fc9210c91aa',
-          '97b6b7f0e47f149b0723b0787b0721',
-          '7f0e27f0e47f531b0723b0b6fb0722',
-          '7f0e397bd07f595b0b0bc920fb0722',
-          '9778397bd097c36b0b6fc9210c8dc2',
-          '977837f0e37f149b0723b0787b0721',
-          '7f07e7f0e47f531b0723b0b6fb0722',
-          '7f0e37f5307f595b0b0bc920fb0722',
-          '7f0e397bd097c35b0b6fc9210c8dc2',
-          '977837f0e37f14998082b0787b0721',
-          '7f07e7f0e47f531b0723b0b6fb0721',
-          '7f0e37f1487f595b0b0bb0b6fb0722',
-          '7f0e397bd097c35b0b6fc9210c8dc2',
-          '977837f0e37f14998082b0787b06bd',
-          '7f07e7f0e47f531b0723b0b6fb0721',
-          '7f0e27f1487f531b0b0bb0b6fb0722',
-          '7f0e397bd097c35b0b6fc920fb0722',
-          '977837f0e37f14998082b0787b06bd',
-          '7f07e7f0e47f531b0723b0b6fb0721',
-          '7f0e27f1487f531b0b0bb0b6fb0722',
-          '7f0e397bd097c35b0b6fc920fb0722',
-          '977837f0e37f14998082b0787b06bd',
-          '7f07e7f0e47f531b0723b0b6fb0721',
-          '7f0e27f1487f531b0b0bb0b6fb0722',
-          '7f0e397bd07f595b0b0bc920fb0722',
-          '977837f0e37f14998082b0787b06bd',
-          '7f07e7f0e47f531b0723b0b6fb0721',
-          '7f0e27f1487f531b0b0bb0b6fb0722',
-          '7f0e397bd07f595b0b0bc920fb0722',
-          '977837f0e37f14998082b0787b06bd',
-          '7f07e7f0e47f149b0723b0787b0721',
-          '7f0e27f0e47f531b0b0bb0b6fb0722',
-          '7f0e397bd07f595b0b0bc920fb0722',
-          '977837f0e37f14998082b0723b06bd',
-          '7f07e7f0e37f149b0723b0787b0721',
-          '7f0e27f0e47f531b0723b0b6fb0722',
-          '7f0e397bd07f595b0b0bc920fb0722',
-          '977837f0e37f14898082b0723b02d5',
-          '7ec967f0e37f14998082b0787b0721',
-          '7f07e7f0e47f531b0723b0b6fb0722',
-          '7f0e37f1487f595b0b0bb0b6fb0722',
-          '7f0e37f0e37f14898082b0723b02d5',
-          '7ec967f0e37f14998082b0787b0721',
-          '7f07e7f0e47f531b0723b0b6fb0722',
-          '7f0e37f1487f531b0b0bb0b6fb0722',
-          '7f0e37f0e37f14898082b0723b02d5',
-          '7ec967f0e37f14998082b0787b06bd',
-          '7f07e7f0e47f531b0723b0b6fb0721',
-          '7f0e37f1487f531b0b0bb0b6fb0722',
-          '7f0e37f0e37f14898082b072297c35',
-          '7ec967f0e37f14998082b0787b06bd',
-          '7f07e7f0e47f531b0723b0b6fb0721',
-          '7f0e27f1487f531b0b0bb0b6fb0722',
-          '7f0e37f0e37f14898082b072297c35',
-          '7ec967f0e37f14998082b0787b06bd',
-          '7f07e7f0e47f531b0723b0b6fb0721',
-          '7f0e27f1487f531b0b0bb0b6fb0722',
-          '7f0e37f0e366aa89801eb072297c35',
-          '7ec967f0e37f14998082b0787b06bd',
-          '7f07e7f0e47f149b0723b0787b0721',
-          '7f0e27f1487f531b0b0bb0b6fb0722',
-          '7f0e37f0e366aa89801eb072297c35',
-          '7ec967f0e37f14998082b0723b06bd',
-          '7f07e7f0e47f149b0723b0787b0721',
-          '7f0e27f0e47f531b0723b0b6fb0722',
-          '7f0e37f0e366aa89801eb072297c35',
-          '7ec967f0e37f14998082b0723b06bd',
-          '7f07e7f0e37f14998083b0787b0721',
-          '7f0e27f0e47f531b0723b0b6fb0722',
-          '7f0e37f0e366aa89801eb072297c35',
-          '7ec967f0e37f14898082b0723b02d5',
-          '7f07e7f0e37f14998082b0787b0721',
-          '7f07e7f0e47f531b0723b0b6fb0722',
-          '7f0e36665b66aa89801e9808297c35',
-          '665f67f0e37f14898082b0723b02d5',
-          '7ec967f0e37f14998082b0787b0721',
-          '7f07e7f0e47f531b0723b0b6fb0722',
-          '7f0e36665b66a449801e9808297c35',
-          '665f67f0e37f14898082b0723b02d5',
-          '7ec967f0e37f14998082b0787b06bd',
-          '7f07e7f0e47f531b0723b0b6fb0721',
-          '7f0e36665b66a449801e9808297c35',
-          '665f67f0e37f14898082b072297c35',
-          '7ec967f0e37f14998082b0787b06bd',
-          '7f07e7f0e47f531b0723b0b6fb0721',
-          '7f0e26665b66a449801e9808297c35',
-          '665f67f0e37f1489801eb072297c35',
-          '7ec967f0e37f14998082b0787b06bd',
-          '7f07e7f0e47f531b0723b0b6fb0721',
-          '7f0e27f1487f531b0b0bb0b6fb0722'
-        ],
-        nStr1: [
-          '日',
-          '一',
-          '二',
-          '三',
-          '四',
-          '五',
-          '六',
-          '七',
-          '八',
-          '九',
-          '十'
-        ],
-        nStr2: ['初', '十', '廿', '卅'],
-        nStr3: [
-          '正',
-          '二',
-          '三',
-          '四',
-          '五',
-          '六',
-          '七',
-          '八',
-          '九',
-          '十',
-          '冬',
-          '腊'
-        ],
-        lYearDays: function(e) {
-          let t,
-            a = 348;
-          for (t = 32768; t > 8; t >>= 1)
-            a += n.lunarInfo[e - 1900] & t ? 1 : 0;
-          return a + n.leapDays(e);
-        },
-        leapMonth: function(e) {
-          return 15 & n.lunarInfo[e - 1900];
-        },
-        leapDays: function(e) {
-          return n.leapMonth(e) ? (65536 & n.lunarInfo[e - 1900] ? 30 : 29) : 0;
-        },
-        monthDays: function(e, t) {
-          return t > 12 || t < 1
-            ? -1
-            : n.lunarInfo[e - 1900] & (65536 >> t)
-            ? 30
-            : 29;
-        },
-        solarDays: function(e, t) {
-          if (t > 12 || t < 1) return -1;
-          const a = t - 1;
-          return 1 == +a
-            ? (e % 4 == 0 && e % 100 != 0) || e % 400 == 0
-              ? 29
-              : 28
-            : n.solarMonth[a];
-        },
-        toGanZhiYear: function(e) {
-          let t = (e - 3) % 10,
-            a = (e - 3) % 12;
-          return (
-            0 == +t && (t = 10),
-            0 == +a && (a = 12),
-            n.Gan[t - 1] + n.Zhi[a - 1]
-          );
-        },
-        toAstro: function(e, t) {
-          return (
-            '魔羯水瓶双鱼白羊金牛双子巨蟹狮子处女天秤天蝎射手魔羯'.substr(
-              2 * e -
-                (t < [20, 19, 21, 21, 21, 22, 23, 23, 23, 23, 22, 22][e - 1]
-                  ? 2
-                  : 0),
-              2
-            ) + '座'
-          );
-        },
-        toGanZhi: function(e) {
-          return n.Gan[e % 10] + n.Zhi[e % 12];
-        },
-        getTerm: function(e, t) {
-          if (e < 1900 || e > 2100) return -1;
-          if (t < 1 || t > 24) return -1;
-          const a = n.sTermInfo[e - 1900],
-            s = [
-              parseInt('0x' + a.substr(0, 5)).toString(),
-              parseInt('0x' + a.substr(5, 5)).toString(),
-              parseInt('0x' + a.substr(10, 5)).toString(),
-              parseInt('0x' + a.substr(15, 5)).toString(),
-              parseInt('0x' + a.substr(20, 5)).toString(),
-              parseInt('0x' + a.substr(25, 5)).toString()
-            ],
-            r = [
-              s[0].substr(0, 1),
-              s[0].substr(1, 2),
-              s[0].substr(3, 1),
-              s[0].substr(4, 2),
-              s[1].substr(0, 1),
-              s[1].substr(1, 2),
-              s[1].substr(3, 1),
-              s[1].substr(4, 2),
-              s[2].substr(0, 1),
-              s[2].substr(1, 2),
-              s[2].substr(3, 1),
-              s[2].substr(4, 2),
-              s[3].substr(0, 1),
-              s[3].substr(1, 2),
-              s[3].substr(3, 1),
-              s[3].substr(4, 2),
-              s[4].substr(0, 1),
-              s[4].substr(1, 2),
-              s[4].substr(3, 1),
-              s[4].substr(4, 2),
-              s[5].substr(0, 1),
-              s[5].substr(1, 2),
-              s[5].substr(3, 1),
-              s[5].substr(4, 2)
-            ];
-          return parseInt(r[t - 1]);
-        },
-        toChinaMonth: function(e) {
-          if (e > 12 || e < 1) return -1;
-          let t = n.nStr3[e - 1];
-          return (t += '月');
-        },
-        toChinaDay: function(e) {
-          let t;
-          switch (e) {
-            case 10:
-              t = '初十';
-              break;
-            case 20:
-              t = '二十';
-              break;
-            case 30:
-              t = '三十';
-              break;
-            default:
-              (t = n.nStr2[Math.floor(e / 10)]), (t += n.nStr1[e % 10]);
-          }
-          return t;
-        },
-        getAnimal: function(e) {
-          return n.Animals[(e - 4) % 12];
-        },
-        solar2lunar: function(e, t, a) {
-          if (e < 1900 || e > 2100) return -1;
-          if (1900 == +e && 1 == +t && +a < 31) return -1;
-          let s,
-            r,
-            o = 0,
-            c = 0;
-          (e = (s = e
-            ? new Date(e, parseInt(t) - 1, a)
-            : new Date()).getFullYear()),
-            (t = s.getMonth() + 1),
-            (a = s.getDate());
-          let l =
-            (Date.UTC(s.getFullYear(), s.getMonth(), s.getDate()) -
-              Date.UTC(1900, 0, 31)) /
-            864e5;
-          for (r = 1900; r < 2101 && l > 0; r++) l -= c = n.lYearDays(r);
-          l < 0 && ((l += c), r--);
-          const b = new Date();
-          let d = !1;
-          b.getFullYear() === +e &&
-            b.getMonth() + 1 === +t &&
-            b.getDate() === +a &&
-            (d = !0);
-          let f = s.getDay();
-          const i = n.nStr1[f];
-          0 == +f && (f = 7);
-          const h = r;
-          o = n.leapMonth(r);
-          let u = !1;
-          for (r = 1; r < 13 && l > 0; r++)
-            o > 0 && r === o + 1 && !1 === u
-              ? (--r, (u = !0), (c = n.leapDays(h)))
-              : (c = n.monthDays(h, r)),
-              !0 === u && r === o + 1 && (u = !1),
-              (l -= c);
-          0 === l && o > 0 && r === o + 1 && (u ? (u = !1) : ((u = !0), --r)),
-            l < 0 && ((l += c), --r);
-          const y = r,
-            m = l + 1,
-            D = t - 1,
-            g = n.toGanZhiYear(h),
-            p = n.getTerm(e, 2 * t - 1),
-            w = n.getTerm(e, 2 * t);
-          let C = n.toGanZhi(12 * (e - 1900) + t + 11);
-          a >= p && (C = n.toGanZhi(12 * (e - 1900) + t + 12));
-          let T = !1,
-            M = null;
-          +p === a && ((T = !0), (M = n.solarTerm[2 * t - 2])),
-            +w === a && ((T = !0), (M = n.solarTerm[2 * t - 1]));
-          const k = Date.UTC(e, D, 1, 0, 0, 0, 0) / 864e5 + 25567 + 10,
-            _ = n.toGanZhi(k + a - 1),
-            L = n.toAstro(t, a);
-          return {
-            lYear: h,
-            lMonth: y,
-            lDay: m,
-            Animal: n.getAnimal(h),
-            IMonthCn: (u ? '闰' : '') + n.toChinaMonth(y),
-            IDayCn: n.toChinaDay(m),
-            cYear: e,
-            cMonth: t,
-            cDay: a,
-            gzYear: g,
-            gzMonth: C,
-            gzDay: _,
-            isToday: d,
-            isLeap: u,
-            nWeek: f,
-            ncWeek: '星期' + i,
-            isTerm: T,
-            Term: M,
-            astro: L
-          };
-        },
-        lunar2solar: function(e, t, a, s) {
-          s = !!s;
-          const r = n.leapMonth(e);
-          if (s && r !== t) return -1;
-          if (
-            (2100 == +e && 12 == +t && +a > 1) ||
-            (1900 == +e && 1 == +t && +a < 31)
-          )
-            return -1;
-          const o = n.monthDays(e, t);
-          let c = o;
-          if ((s && (c = n.leapDays(e, t)), e < 1900 || e > 2100 || a > c))
-            return -1;
-          let l = 0;
-          for (let t = 1900; t < e; t++) l += n.lYearDays(t);
-          let b = 0,
-            d = !1;
-          for (let a = 1; a < t; a++)
-            (b = n.leapMonth(e)),
-              d || (b <= a && b > 0 && ((l += n.leapDays(e)), (d = !0))),
-              (l += n.monthDays(e, a));
-          s && (l += o);
-          const f = Date.UTC(1900, 1, 30, 0, 0, 0),
-            i = new Date(864e5 * (l + a - 31) + f),
-            h = i.getUTCFullYear(),
-            u = i.getUTCMonth() + 1,
-            y = i.getUTCDate();
-          return n.solar2lunar(h, u, y);
-        }
-      },
-      {
-        Gan: s,
-        Zhi: r,
-        nStr1: o,
-        nStr2: c,
-        nStr3: l,
-        Animals: b,
-        solarTerm: d,
-        lunarInfo: f,
-        sTermInfo: i,
-        solarMonth: h,
-        ...u
-      } = n;
-    var y = u;
-    t.default = y;
-  },
-  function(e, t, a) {
-    'use strict';
-    Object.defineProperty(t, '__esModule', { value: !0 }), (t.default = void 0);
-    class n {
-      constructor(e) {
-        this.Component = e;
-      }
-      getCalendarConfig() {
-        return this.Component && this.Component.config
-          ? this.Component.config
-          : {};
-      }
-      setCalendarConfig(e, t) {
-        this.Component &&
-          this.Component.config &&
-          (this.Component.config[e] = t);
-      }
-    }
-    t.default = e => new n(e);
-  },
-  function(e, t, a) {
-    'use strict';
-    Object.defineProperty(t, '__esModule', { value: !0 }), (t.default = void 0);
-    var n = l(a(1)),
-      s = l(a(5)),
-      r = l(a(3)),
-      o = l(a(2)),
-      c = a(0);
-    function l(e) {
-      return e && e.__esModule ? e : { default: e };
-    }
-    const b = new c.GetDate(),
-      d = new c.Logger();
-    class f extends n.default {
-      constructor(e) {
-        super(e),
-          (this.Component = e),
-          (this.getCalendarConfig = (0, r.default)(
-            this.Component
-          ).getCalendarConfig);
-      }
-      switchWeek(e, t) {
-        return new Promise((a, n) => {
-          if ((0, r.default)(this.Component).getCalendarConfig().multi)
-            return d.warn('多选模式不能切换周月视图');
-          const { selectedDay: o = [], curYear: c, curMonth: l } = this.getData(
-            'calendar'
-          );
-          if (!o.length) return this.__tipsWhenCanNotSwtich();
-          const b = o[0];
-          if ('week' === e) {
-            if (this.Component.weekMode) return;
-            const e = t || b,
-              { year: s, month: r } = e;
-            if (c !== s || l !== r) return this.__tipsWhenCanNotSwtich();
-            (this.Component.weekMode = !0),
-              this.setData({ 'calendar.weekMode': !0 }),
-              this.jump(e)
-                .then(a)
-                .catch(n);
-          } else
-            (this.Component.weekMode = !1),
-              this.setData({ 'calendar.weekMode': !1 }),
-              (0, s.default)(this.Component)
-                .renderCalendar(c, l, t)
-                .then(a)
-                .catch(n);
-        });
-      }
-      updateCurrYearAndMonth(e) {
-        let { days: t, curYear: a, curMonth: n } = this.getData('calendar');
-        const { month: s } = t[0],
-          { month: r } = t[t.length - 1],
-          o = b.thisMonthDays(a, n),
-          c = t[t.length - 1],
-          l = t[0];
-        return (
-          (c.day + 7 > o || (n === s && s !== r)) && 'next' === e
-            ? (n += 1) > 12 && ((a += 1), (n = 1))
-            : (+l.day <= 7 || (n === r && s !== r)) &&
-              'prev' === e &&
-              (n -= 1) <= 0 &&
-              ((a -= 1), (n = 12)),
-          { Uyear: a, Umonth: n }
-        );
-      }
-      calculateLastDay() {
-        const { days: e, curYear: t, curMonth: a } = this.getData('calendar');
-        return {
-          lastDayInThisWeek: e[e.length - 1].day,
-          lastDayInThisMonth: b.thisMonthDays(t, a)
-        };
-      }
-      calculateFirstDay() {
-        const { days: e } = this.getData('calendar');
-        return { firstDayInThisWeek: e[0].day };
-      }
-      firstWeekInMonth(e, t, a) {
-        let n = b.dayOfWeek(e, t, 1);
-        const [, r] = [0, 7 - n];
-        let o = this.getData('calendar.days') || [];
-        return (
-          this.Component.weekMode &&
-            (o = (0, s.default)(this.Component).buildDate(e, t)),
-          o.slice(0, a ? r + 1 : r)
-        );
-      }
-      lastWeekInMonth(e, t, a) {
-        const n = b.thisMonthDays(e, t),
-          r = b.dayOfWeek(e, t, n),
-          [o, c] = [n - r, n];
-        let l = this.getData('calendar.days') || [];
-        return (
-          this.Component.weekMode &&
-            (l = (0, s.default)(this.Component).buildDate(e, t)),
-          l.slice(a ? o : o - 1, c)
-        );
-      }
-      initSelectedDay(e) {
-        const t = [...e],
-          {
-            selectedDay: a = [],
-            todoLabels: n = [],
-            showLabelAlways: s
-          } = this.getData('calendar'),
-          r = a.map(e => `${+e.year}-${+e.month}-${+e.day}`),
-          c = n.map(e => `${+e.year}-${+e.month}-${+e.day}`),
-          l = this.getCalendarConfig();
-        return (
-          t.forEach(e => {
-            r.includes(`${+e.year}-${+e.month}-${+e.day}`)
-              ? (e.choosed = !0)
-              : (e.choosed = !1);
-            const t = c.indexOf(`${+e.year}-${+e.month}-${+e.day}`);
-            if (-1 !== t) {
-              e.showTodoLabel = !!s || !e.choosed;
-              const a = n[t] || {};
-              e.showTodoLabel && a.todoText && (e.todoText = a.todoText);
-            }
-            if (
-              (l.showLunar &&
-                (e.lunar = o.default.solar2lunar(+e.year, +e.month, +e.day)),
-              l.highlightToday)
-            ) {
-              const t = b.todayDate(),
-                a =
-                  +t.year == +e.year &&
-                  +t.month == +e.month &&
-                  +e.day == +t.date;
-              e.isToday = a;
-            }
-          }),
-          t
-        );
-      }
-      setEnableAreaOnWeekMode(e) {
-        let {
-          todayTimestamp: t,
-          enableAreaTimestamp: a = [],
-          enableDaysTimestamp: n = []
-        } = this.getData('calendar');
-        e.forEach(e => {
-          const s = b.newDate(e.year, e.month, e.day).getTime();
-          let o = !1;
-          a.length
-            ? (+a[0] > +s || +s > +a[1]) && !n.includes(+s) && (o = !0)
-            : n.length && !n.includes(+s) && (o = !0),
-            o && ((e.disable = !0), (e.choosed = !1));
-          const { disablePastDay: c } =
-            (0, r.default)(this.Component).getCalendarConfig() || {};
-          c && s - t < 0 && !e.disable && (e.disable = !0);
-        });
-      }
-      calculateNextWeekDays() {
-        let {
-            lastDayInThisWeek: e,
-            lastDayInThisMonth: t
-          } = this.calculateLastDay(),
-          { curYear: a, curMonth: n } = this.getData('calendar'),
-          s = [];
-        if (t - e >= 7) {
-          const { Uyear: t, Umonth: r } = this.updateCurrYearAndMonth('next');
-          (a = t), (n = r);
-          for (let t = e + 1; t <= e + 7; t++)
-            s.push({ year: a, month: n, day: t, week: b.dayOfWeek(a, n, t) });
-        } else {
-          for (let r = e + 1; r <= t; r++)
-            s.push({ year: a, month: n, day: r, week: b.dayOfWeek(a, n, r) });
-          const { Uyear: r, Umonth: o } = this.updateCurrYearAndMonth('next');
-          (a = r), (n = o);
-          for (let r = 1; r <= 7 - (t - e); r++)
-            s.push({ year: a, month: n, day: r, week: b.dayOfWeek(a, n, r) });
-        }
-        (s = this.initSelectedDay(s)),
-          this.setEnableAreaOnWeekMode(s),
-          this.setData({
-            'calendar.curYear': a,
-            'calendar.curMonth': n,
-            'calendar.days': s
-          });
-      }
-      calculatePrevWeekDays() {
-        let { firstDayInThisWeek: e } = this.calculateFirstDay(),
-          { curYear: t, curMonth: a } = this.getData('calendar'),
-          n = [];
-        if (e - 7 > 0) {
-          const { Uyear: s, Umonth: r } = this.updateCurrYearAndMonth('prev');
-          (t = s), (a = r);
-          for (let s = e - 7; s < e; s++)
-            n.push({ year: t, month: a, day: s, week: b.dayOfWeek(t, a, s) });
-        } else {
-          let s = [];
-          for (let n = 1; n < e; n++)
-            s.push({ year: t, month: a, day: n, week: b.dayOfWeek(t, a, n) });
-          const { Uyear: r, Umonth: o } = this.updateCurrYearAndMonth('prev');
-          (t = r), (a = o);
-          const c = b.thisMonthDays(t, a);
-          for (let s = c - Math.abs(e - 7); s <= c; s++)
-            n.push({ year: t, month: a, day: s, week: b.dayOfWeek(t, a, s) });
-          n = n.concat(s);
-        }
-        (n = this.initSelectedDay(n)),
-          this.setEnableAreaOnWeekMode(n),
-          this.setData({
-            'calendar.curYear': t,
-            'calendar.curMonth': a,
-            'calendar.days': n
-          });
-      }
-      calculateDatesWhenJump(
-        { year: e, month: t, day: a },
-        { firstWeekDays: n, lastWeekDays: s },
-        r
-      ) {
-        const o = this.__dateIsInWeek({ year: e, month: t, day: a }, n),
-          c = this.__dateIsInWeek({ year: e, month: t, day: a }, s);
-        let l = [];
-        return (l = o
-          ? this.__calculateDatesWhenInFirstWeek(n, r)
-          : c
-          ? this.__calculateDatesWhenInLastWeek(s, r)
-          : this.__calculateDates({ year: e, month: t, day: a }));
-      }
-      jump({ year: e, month: t, day: a }) {
-        return new Promise(n => {
-          if (!a) return;
-          const s = 'Mon' === this.getCalendarConfig().firstDayOfWeek,
-            r = this.firstWeekInMonth(e, t, s);
-          let o = this.lastWeekInMonth(e, t, s);
-          const c = this.calculateDatesWhenJump(
-            { year: e, month: t, day: a },
-            { firstWeekDays: r, lastWeekDays: o },
-            s
-          );
-          c.map(n => {
-            +n.year == +e && +n.month == +t && +n.day == +a && (n.choosed = !0);
-          }),
-            this.initSelectedDay(c),
-            this.setEnableAreaOnWeekMode(c),
-            this.setData(
-              {
-                'calendar.days': c,
-                'calendar.curYear': e,
-                'calendar.curMonth': t,
-                'calendar.empytGrids': [],
-                'calendar.lastEmptyGrids': []
-              },
-              n
-            );
-        });
-      }
-      __calculateDatesWhenInFirstWeek(e, t) {
-        const a = [...e];
-        if (a.length < 7) {
-          let e,
-            { year: t, month: n } = a[0],
-            s = 7 - a.length;
-          for (
-            n > 1
-              ? ((n -= 1), (e = b.thisMonthDays(t, n)))
-              : ((n = 12), (t -= 1), (e = b.thisMonthDays(t, n)));
-            s;
+import Week from './func/week'
+import { Logger, Slide, GetDate, initialTasks } from './func/utils'
+import initCalendar, {
+  jump,
+  getCurrentYM,
+  whenChangeDate,
+  renderCalendar,
+  whenMulitSelect,
+  whenSingleSelect,
+  whenChooseArea,
+  getCalendarDates
+} from './main.js'
 
-          )
-            a.unshift({
-              year: t,
-              month: n,
-              day: e,
-              week: b.dayOfWeek(t, n, e)
-            }),
-              (e -= 1),
-              (s -= 1);
-        }
-        return a;
-      }
-      __calculateDatesWhenInLastWeek(e, t) {
-        const a = [...e];
-        if (t && a.length < 7) {
-          let { year: e, month: t } = a[0],
-            n = 7 - a.length,
-            s = 1;
-          for (t > 11 ? ((t = 1), (e += 1)) : (t += 1); n; )
-            a.push({ year: e, month: t, day: s, week: b.dayOfWeek(e, t, s) }),
-              (s += 1),
-              (n -= 1);
-        }
-        return a;
-      }
-      __calculateDates({ year: e, month: t, day: a }, n) {
-        const r = b.dayOfWeek(e, t, a);
-        let o = [a - r, a + (6 - r)];
-        return (
-          n && (o = [a + 1 - r, a + (7 - r)]),
-          (0, s.default)(this.Component)
-            .buildDate(e, t)
-            .slice(o[0] - 1, o[1])
-        );
-      }
-      __dateIsInWeek(e, t) {
-        return t.find(
-          t => +t.year == +e.year && +t.month == +e.month && +t.day == +e.day
-        );
-      }
-      __tipsWhenCanNotSwtich() {
-        d.info(
-          '当前月份未选中日期下切换为周视图,不能明确该展示哪一周的日期,故此情况不允许切换'
-        );
-      }
-    }
-    t.default = e => new f(e);
+const slide = new Slide()
+const logger = new Logger()
+const getDate = new GetDate()
+
+Component({
+  options: {
+    styleIsolation: 'apply-shared',
+    multipleSlots: true // 在组件定义时的选项中启用多slot支持
   },
-  function(e, t, a) {
-    'use strict';
-    Object.defineProperty(t, '__esModule', { value: !0 }), (t.default = void 0);
-    var n = c(a(6)),
-      s = c(a(1)),
-      r = c(a(2)),
-      o = a(0);
-    function c(e) {
-      return e && e.__esModule ? e : { default: e };
+  properties: {
+    calendarConfig: {
+      type: Object,
+      value: {}
     }
-    const l = new o.GetDate();
-    class b extends s.default {
-      constructor(e) {
-        super(e), (this.Component = e);
-      }
-      getCalendarConfig() {
-        return this.Component.config;
-      }
-      renderCalendar(e, t, a) {
-        return new Promise(s => {
-          this.calculateEmptyGrids(e, t), this.calculateDays(e, t, a);
-          const { todoLabels: r } = this.getData('calendar') || {};
-          r &&
-            r instanceof Array &&
-            r.find(e => +e.month == +t) &&
-            (0, n.default)(this.Component).setTodoLabels(),
-            this.Component.firstRender || s();
-        });
-      }
-      calculateEmptyGrids(e, t) {
-        this.calculatePrevMonthGrids(e, t), this.calculateNextMonthGrids(e, t);
-      }
-      calculatePrevMonthGrids(e, t) {
-        let a = [];
-        const n = l.thisMonthDays(e, t - 1);
-        let s = l.firstDayOfWeek(e, t);
-        const o = this.getCalendarConfig() || {};
-        if (
-          ('Mon' === o.firstDayOfWeek && (0 === s ? (s = 6) : (s -= 1)), s > 0)
-        ) {
-          const c = n - s,
-            { onlyShowCurrentMonth: l } = o,
-            { showLunar: b } = this.getCalendarConfig();
-          for (let s = n; s > c; s--)
-            l
-              ? a.push('')
-              : a.push({
-                  day: s,
-                  lunar: b ? r.default.solar2lunar(e, t - 1, s) : null
-                });
-          this.setData({ 'calendar.empytGrids': a.reverse() });
-        } else this.setData({ 'calendar.empytGrids': null });
-      }
-      calculateExtraEmptyDate(e, t, a) {
-        let n = 0;
-        if (2 == +t) {
-          n += 7;
-          let s = l.dayOfWeek(e, t, 1);
-          'Mon' === a.firstDayOfWeek
-            ? 1 == +s && (n += 7)
-            : 0 == +s && (n += 7);
-        } else {
-          let s = l.dayOfWeek(e, t, 1);
-          'Mon' === a.firstDayOfWeek
-            ? 0 !== s && s < 6 && (n += 7)
-            : s < 6 && (n += 7);
-        }
-        return n;
-      }
-      calculateNextMonthGrids(e, t) {
-        let a = [];
-        const n = l.thisMonthDays(e, t);
-        let s = l.dayOfWeek(e, t, n);
-        const o = this.getCalendarConfig() || {};
-        'Mon' === o.firstDayOfWeek && (0 === s ? (s = 6) : (s -= 1));
-        let c = 7 - (s + 1);
-        const { onlyShowCurrentMonth: b, showLunar: d } = o;
-        b || (c += this.calculateExtraEmptyDate(e, t, o));
-        for (let n = 1; n <= c; n++)
-          b
-            ? a.push('')
-            : a.push({
-                day: n,
-                lunar: d ? r.default.solar2lunar(e, t + 1, n) : null
-              });
-        this.setData({ 'calendar.lastEmptyGrids': a });
-      }
-      setSelectedDay(e, t, a) {
-        let n = [];
-        const s = this.getCalendarConfig();
-        if (s.noDefault) (n = []), (s.noDefault = !1);
-        else {
-          const s = this.getData('calendar') || {},
-            { showLunar: o } = this.getCalendarConfig();
-          n = a
-            ? [
-                {
-                  year: e,
-                  month: t,
-                  day: a,
-                  choosed: !0,
-                  week: l.dayOfWeek(e, t, a),
-                  lunar: o ? r.default.solar2lunar(e, t, a) : null
-                }
-              ]
-            : s.selectedDay;
-        }
-        return n;
-      }
-      buildDate(e, t) {
-        const a = l.todayDate(),
-          n = l.thisMonthDays(e, t),
-          s = [];
-        for (let r = 1; r <= n; r++) {
-          const n = +a.year == +e && +a.month == +t && r === +a.date,
-            o = this.getCalendarConfig();
-          s.push({
-            year: e,
-            month: t,
-            day: r,
-            choosed: !1,
-            week: l.dayOfWeek(e, t, r),
-            isToday: n && o.highlightToday
-          });
-        }
-        return s;
-      }
-      calculateDays(e, t, a) {
-        let n = [];
-        const { todayTimestamp: s, disableDays: o = [] } = this.getData(
-          'calendar'
-        );
-        n = this.buildDate(e, t);
-        const c = this.setSelectedDay(e, t, a),
-          b = c.map(e => `${+e.year}-${+e.month}-${+e.day}`),
-          d = o.map(e => `${+e.year}-${+e.month}-${+e.day}`);
-        n.forEach(e => {
-          const t = `${+e.year}-${+e.month}-${+e.day}`;
-          b.includes(t) && (e.choosed = !0), d.includes(t) && (e.disable = !0);
-          const a = l.newDate(e.year, e.month, e.day).getTime(),
-            {
-              showLunar: n,
-              disablePastDay: o,
-              disableLaterDay: c
-            } = this.getCalendarConfig();
-          n && (e.lunar = r.default.solar2lunar(+e.year, +e.month, +e.day));
-          let f = !1;
-          o
-            ? (f = o && a - s < 0 && !e.disable)
-            : c && (f = c && a - s > 0 && !e.disable),
-            (f || this.__isDisable(a)) && ((e.disable = !0), (e.choosed = !1));
-        }),
-          this.setData({ 'calendar.days': n, 'calendar.selectedDay': c || [] });
-      }
-      __isDisable(e) {
-        const {
-          enableArea: t = [],
-          enableDays: a = [],
-          enableAreaTimestamp: n = []
-        } = this.getData('calendar');
-        let s = !1,
-          r = (0, o.converEnableDaysToTimestamp)(a);
-        return (
-          t.length && (r = (0, o.delRepeatedEnableDay)(a, t)),
-          n.length
-            ? (+n[0] > +e || +e > +n[1]) && !r.includes(+e) && (s = !0)
-            : r.length && !r.includes(+e) && (s = !0),
-          s
-        );
-      }
-    }
-    t.default = e => new b(e);
   },
-  function(e, t, a) {
-    'use strict';
-    Object.defineProperty(t, '__esModule', { value: !0 }), (t.default = void 0);
-    var n,
-      s = (n = a(1)) && n.__esModule ? n : { default: n },
-      r = a(0);
-    const o = new r.Logger();
-    class c extends s.default {
-      constructor(e) {
-        super(e), (this.Component = e);
-      }
-      setTodoLabels(e) {
-        e && (this.Component.todoConfig = e);
-        const t = this.getData('calendar');
-        if (!t || !t.days) return o.warn('请等待日历初始化完成后再调用该方法');
-        const a = [...t.days],
-          { curYear: n, curMonth: s } = t,
-          {
-            circle: c,
-            dotColor: l = '',
-            pos: b = 'bottom',
-            showLabelAlways: d,
-            days: f = []
-          } = e || this.Component.todoConfig || {},
-          { todoLabels: i = [], todoLabelPos: h, todoLabelColor: u } = t,
-          y = this.getTodoLabels({ year: n, month: s });
-        let m = f.filter(e => +e.year == +n && +e.month == +s);
-        this.Component.weekMode && (m = f);
-        const D = y.concat(m);
-        for (let e of D) {
-          let t;
-          (t = this.Component.weekMode
-            ? a.find(
-                t =>
-                  +e.year == +t.year && +e.month == +t.month && +e.day == +t.day
-              )
-            : a[e.day - 1]) &&
-            ((t.showTodoLabel = !!d || !t.choosed),
-            t.showTodoLabel && e.todoText && (t.todoText = e.todoText));
-        }
-        const g = {
-          'calendar.days': a,
-          'calendar.todoLabels': (0, r.uniqueArrayByDate)(f.concat(i))
-        };
-        c ||
-          (b && b !== h && (g['calendar.todoLabelPos'] = b),
-          l && l !== u && (g['calendar.todoLabelColor'] = l)),
-          (g['calendar.todoLabelCircle'] = c || !1),
-          (g['calendar.showLabelAlways'] = d || !1),
-          this.setData(g);
-      }
-      deleteTodoLabels(e) {
-        if (!(e instanceof Array && e.length)) return;
-        const t = this.filterTodos(e),
-          { days: a, curYear: n, curMonth: s } = this.getData('calendar'),
-          r = t.filter(e => n === +e.year && s === +e.month);
-        a.forEach(e => {
-          e.showTodoLabel = !1;
-        }),
-          r.forEach(e => {
-            a[e.day - 1].showTodoLabel = !a[e.day - 1].choosed;
-          }),
-          this.setData({ 'calendar.days': a, 'calendar.todoLabels': t });
-      }
-      clearTodoLabels() {
-        const { days: e = [] } = this.getData('calendar'),
-          t = [].concat(e);
-        t.forEach(e => {
-          e.showTodoLabel = !1;
-        }),
-          this.setData({ 'calendar.days': t, 'calendar.todoLabels': [] });
-      }
-      getTodoLabels(e) {
-        const { todoLabels: t = [] } = this.getData('calendar');
-        if (e) {
-          const { year: a, month: n } = e;
-          return t.filter(e => +e.year == +a && +e.month == +n);
-        }
-        return t;
-      }
-      filterTodos(e) {
-        const t = this.getData('calendar.todoLabels') || [],
-          a = e.map(e => `${e.year}-${e.month}-${e.day}`);
-        return t.filter(e => !a.includes(`${e.year}-${e.month}-${e.day}`));
-      }
-      showTodoLabels(e, t, a) {
-        e.forEach(e => {
-          if (this.Component.weekMode)
-            t.forEach((n, s) => {
-              if (+n.day == +e.day) {
-                const n = t[s];
-                (n.hasTodo = !0),
-                  (n.todoText = e.todoText),
-                  a &&
-                    a.length &&
-                    +a[0].day == +e.day &&
-                    (n.showTodoLabel = !0);
-              }
-            });
-          else {
-            const n = t[e.day - 1];
-            if (!n) return;
-            (n.hasTodo = !0),
-              (n.todoText = e.todoText),
-              a &&
-                a.length &&
-                +a[0].day == +e.day &&
-                (t[a[0].day - 1].showTodoLabel = !0);
-          }
-        });
-      }
+  data: {
+    handleMap: {
+      prev_year: 'chooseYear',
+      prev_month: 'chooseMonth',
+      next_month: 'chooseMonth',
+      next_year: 'chooseYear'
     }
-    t.default = e => new c(e);
   },
-  function(e, t, a) {
-    'use strict';
-    var n,
-      s = (n = a(4)) && n.__esModule ? n : { default: n },
-      r = a(0),
-      o = (function(e) {
-        if (e && e.__esModule) return e;
-        var t = {};
-        if (null != e)
-          for (var a in e)
-            if (Object.prototype.hasOwnProperty.call(e, a)) {
-              var n =
-                Object.defineProperty && Object.getOwnPropertyDescriptor
-                  ? Object.getOwnPropertyDescriptor(e, a)
-                  : {};
-              n.get || n.set ? Object.defineProperty(t, a, n) : (t[a] = e[a]);
-            }
-        return (t.default = e), t;
-      })(a(8));
-    const c = new r.Slide(),
-      l = new r.Logger();
-    Component({
-      options: { multipleSlots: !0 },
-      properties: { calendarConfig: { type: Object, value: {} } },
-      data: {
-        handleMap: {
-          prev_year: 'chooseYear',
-          prev_month: 'chooseMonth',
-          next_month: 'chooseMonth',
-          next_year: 'chooseYear'
-        }
-      },
-      lifetimes: {
-        attached: function() {
-          this.initComp();
-        }
-      },
-      attached: function() {
-        this.initComp();
-      },
-      methods: {
-        initComp() {
-          const e = this.properties.calendarConfig || {};
-          this.setTheme(e.theme), (0, o.default)(this, e);
-        },
-        setTheme(e) {
-          this.setData({ 'calendarConfig.theme': e || 'default' });
-        },
-        chooseDate(e) {
-          const { type: t } = e.currentTarget.dataset;
-          t && this[this.data.handleMap[t]](t);
-        },
-        chooseYear(e) {
-          const { curYear: t, curMonth: a } = this.data.calendar;
-          if (!t || !a) return l.warn('异常:未获取到当前年月');
-          if (this.weekMode) return console.warn('周视图下不支持点击切换年月');
-          let n = +t,
-            s = +a;
-          'prev_year' === e ? (n -= 1) : 'next_year' === e && (n += 1),
-            this.render(t, a, n, s);
-        },
-        chooseMonth(e) {
-          const { curYear: t, curMonth: a } = this.data.calendar;
-          if (!t || !a) return l.warn('异常:未获取到当前年月');
-          if (this.weekMode) return console.warn('周视图下不支持点击切换年月');
-          let n = +t,
-            s = +a;
-          'prev_month' === e
-            ? (s -= 1) < 1 && ((n -= 1), (s = 12))
-            : 'next_month' === e && (s += 1) > 12 && ((n += 1), (s = 1)),
-            this.render(t, a, n, s);
-        },
-        render(e, t, a, n) {
-          o.whenChangeDate.call(this, {
-            curYear: e,
-            curMonth: t,
-            newYear: a,
-            newMonth: n
-          }),
-            this.setData({ 'calendar.curYear': a, 'calendar.curMonth': n }),
-            o.renderCalendar.call(this, a, n);
-        },
-        tapDayItem(e) {
-          const { idx: t, disable: a } = e.currentTarget.dataset;
-          if (a) return;
-          const n = this.config || {},
-            { multi: s } = n;
-          s
-            ? o.whenMulitSelect.call(this, t)
-            : o.whenSingleSelect.call(this, t);
-        },
-        doubleClickToToday() {
-          if (!this.config.multi && !this.weekMode)
-            if (
-              (void 0 === this.count ? (this.count = 1) : (this.count += 1),
-              this.lastClick)
-            ) {
-              new Date().getTime() - this.lastClick < 500 &&
-                this.count >= 2 &&
-                o.jump.call(this),
-                (this.count = void 0),
-                (this.lastClick = void 0);
-            } else this.lastClick = new Date().getTime();
-        },
-        calendarTouchstart(e) {
-          const t = e.touches[0],
-            a = t.clientX,
-            n = t.clientY;
-          (this.slideLock = !0),
-            this.setData({ 'gesture.startX': a, 'gesture.startY': n });
-        },
-        calendarTouchmove(e) {
-          const { gesture: t } = this.data;
-          if (this.slideLock) {
-            if (c.isLeft(t, e.touches[0])) {
-              if ((this.setData({ 'calendar.leftSwipe': 1 }), this.weekMode))
-                return (
-                  (this.slideLock = !1),
-                  (this.currentDates = (0, o.getCalendarDates)()),
-                  (this.currentYM = (0, o.getCurrentYM)()),
-                  (0, s.default)(this).calculateNextWeekDays(),
-                  this.onSwipeCalendar('next_week'),
-                  void this.onWeekChange('next_week')
-                );
-              this.chooseMonth('next_month'),
-                this.onSwipeCalendar('next_month'),
-                (this.slideLock = !1);
-            }
-            if (c.isRight(t, e.touches[0])) {
-              if ((this.setData({ 'calendar.rightSwipe': 1 }), this.weekMode))
-                return (
-                  (this.slideLock = !1),
-                  (this.currentDates = (0, o.getCalendarDates)()),
-                  (this.currentYM = (0, o.getCurrentYM)()),
-                  (0, s.default)(this).calculatePrevWeekDays(),
-                  this.onSwipeCalendar('prev_week'),
-                  void this.onWeekChange('prev_week')
-                );
-              this.chooseMonth('prev_month'),
-                this.onSwipeCalendar('prev_month'),
-                (this.slideLock = !1);
-            }
-          }
-        },
-        calendarTouchend(e) {
-          this.setData({ 'calendar.leftSwipe': 0, 'calendar.rightSwipe': 0 });
-        },
-        onSwipeCalendar(e) {
-          this.triggerEvent('onSwipe', { directionType: e });
-        },
-        onWeekChange(e) {
-          this.triggerEvent('whenChangeWeek', {
-            current: {
-              currentYM: this.currentYM,
-              dates: [...this.currentDates]
-            },
-            next: {
-              currentYM: (0, o.getCurrentYM)(),
-              dates: (0, o.getCalendarDates)()
-            },
-            directionType: e
-          }),
-            (this.currentDates = null),
-            (this.currentYM = null);
-        }
-      }
-    });
-  },
-  function(e, t, a) {
-    'use strict';
-    Object.defineProperty(t, '__esModule', { value: !0 }),
-      (t.getCurrentYM = L),
-      (t.getSelectedDay = v),
-      (t.cancelAllSelectedDay = W),
-      (t.jump = S),
-      (t.setTodoLabels = $),
-      (t.deleteTodoLabels = Y),
-      (t.clearTodoLabels = O),
-      (t.getTodoLabels = x),
-      (t.disableDay = A),
-      (t.enableArea = E),
-      (t.enableDays = I),
-      (t.setSelectedDays = P),
-      (t.getCalendarConfig = j),
-      (t.setCalendarConfig = G),
-      (t.getCalendarDates = U),
-      (t.switchView = F),
-      (t.default = t.calculateNextWeekDays = t.calculatePrevWeekDays = t.whenMulitSelect = t.whenSingleSelect = t.renderCalendar = t.whenChangeDate = void 0);
-    var n = f(a(9)),
-      s = f(a(4)),
-      r = f(a(6)),
-      o = f(a(1)),
-      c = f(a(5)),
-      l = f(a(3)),
-      b = f(a(2)),
-      d = a(0);
-    function f(e) {
-      return e && e.__esModule ? e : { default: e };
-    }
-    let i = {},
-      h = new d.Logger(),
-      u = new d.GetDate(),
-      y = null;
-    function m(e) {
-      e && (i = (0, d.getComponent)(e));
-    }
-    function D(e, t) {
-      return m(t), (y = new o.default(i)).getData(e);
+  lifetimes: {
+    attached: function() {
+      this.initComp()
+    },
+    detached: function() {
+      initialTasks.flag = 'finished'
+      initialTasks.tasks.length = 0
     }
-    function g(e, t = () => {}) {
-      return new o.default(i).setData(e, t);
-    }
-    const p = {
-        renderCalendar(e, t, a) {
-          return (
-            (0, d.isComponent)(this) && (i = this),
-            new Promise((n, s) => {
-              (0, c.default)(i)
-                .renderCalendar(e, t, a)
-                .then(() => {
-                  !(function(e) {
-                    e.calendar = {
-                      jump: S,
-                      switchView: F,
-                      disableDay: A,
-                      enableArea: E,
-                      enableDays: I,
-                      getCurrentYM: L,
-                      getSelectedDay: v,
-                      cancelAllSelectedDay: W,
-                      setTodoLabels: $,
-                      getTodoLabels: x,
-                      deleteTodoLabels: Y,
-                      clearTodoLabels: O,
-                      setSelectedDays: P,
-                      getCalendarConfig: j,
-                      setCalendarConfig: G,
-                      getCalendarDates: U
-                    };
-                  })((0, d.getCurrentPage)()),
-                    i.triggerEvent('afterCalendarRender', i),
-                    (i.firstRender = !0),
-                    (d.initialTasks.flag = 'finished'),
-                    d.initialTasks.tasks.length &&
-                      d.initialTasks.tasks.shift()(),
-                    n();
-                });
-            })
-          );
-        },
-        whenChangeDate({ curYear: e, curMonth: t, newYear: a, newMonth: n }) {
-          i.triggerEvent('whenChangeMonth', {
-            current: { year: e, month: t },
-            next: { year: a, month: n }
-          });
-        },
-        whenMulitSelect(e) {
-          (0, d.isComponent)(this) && (i = this);
-          const { calendar: t = {} } = D(),
-            { days: a, todoLabels: n } = t;
-          let { selectedDay: s = [] } = t;
-          const r = a[e];
-          if (r) {
-            if (((r.choosed = !r.choosed), r.choosed)) {
-              r.cancel = !1;
-              const { showLabelAlways: e } = D('calendar');
-              e && r.showTodoLabel
-                ? (r.showTodoLabel = !0)
-                : (r.showTodoLabel = !1),
-                s.push(r);
-            } else {
-              r.cancel = !0;
-              const e = `${r.year}-${r.month}-${r.day}`;
-              (s = s.filter(t => e !== `${t.year}-${t.month}-${t.day}`)),
-                n &&
-                  n.forEach(t => {
-                    e === `${t.year}-${t.month}-${t.day}` &&
-                      (r.showTodoLabel = !0);
-                  });
-            }
-            if ((0, l.default)(i).getCalendarConfig().takeoverTap)
-              return i.triggerEvent('onTapDay', r);
-            g({ 'calendar.days': a, 'calendar.selectedDay': s }),
-              p.afterTapDay(r, s);
-          }
-        },
-        whenSingleSelect(e) {
-          (0, d.isComponent)(this) && (i = this);
-          const { calendar: t = {} } = D(),
-            { days: a, selectedDay: n = [], todoLabels: s } = t;
-          let o = [];
-          const c = a[e];
-          if (!c) return;
-          const b = (n[0] || {}).day,
-            f = (b && a[b - 1]) || {},
-            { month: h, year: u } = a[0] || {},
-            y = (0, l.default)(i).getCalendarConfig();
-          if (y.takeoverTap) return i.triggerEvent('onTapDay', c);
-          if ((p.afterTapDay(c), !y.inverse && f.day === c.day)) return;
-          i.weekMode &&
-            a.forEach((e, t) => {
-              e.day === b && (a[t].choosed = !1);
-            }),
-            s && (o = s.filter(e => +e.year === u && +e.month === h)),
-            (0, r.default)(i).showTodoLabels(o, a, n);
-          const m = { 'calendar.days': a };
-          f.day !== c.day
-            ? ((f.choosed = !1),
-              (c.choosed = !0),
-              (t.showLabelAlways && c.showTodoLabel) || (c.showTodoLabel = !1),
-              (m['calendar.selectedDay'] = [c]))
-            : y.inverse &&
-              ((c.choosed = !c.choosed),
-              c.choosed &&
-                (c.showTodoLabel && t.showLabelAlways
-                  ? (c.showTodoLabel = !0)
-                  : (c.showTodoLabel = !1)),
-              (m['calendar.selectedDay'] = [])),
-            g(m);
-        },
-        afterTapDay(e, t) {
-          const a = (0, l.default)(i).getCalendarConfig(),
-            { multi: n } = a;
-          n
-            ? i.triggerEvent('afterTapDay', {
-                currentSelected: e,
-                selectedDays: t
-              })
-            : i.triggerEvent('afterTapDay', e);
-        },
-        jumpToToday() {
-          const { year: e, month: t, date: a } = u.todayDate(),
-            n = u.todayTimestamp();
-          g({
-            'calendar.curYear': e,
-            'calendar.curMonth': t,
-            'calendar.selectedDay': [
-              {
-                year: e,
-                day: a,
-                month: t,
-                choosed: !0,
-                lunar: (0, l.default)(i).getCalendarConfig().showLunar
-                  ? b.default.solar2lunar(e, t, a)
-                  : null
-              }
-            ],
-            'calendar.todayTimestamp': n
-          }),
-            p.renderCalendar(e, t, a);
-        }
-      },
-      w = p.whenChangeDate;
-    t.whenChangeDate = w;
-    const C = p.renderCalendar;
-    t.renderCalendar = C;
-    const T = p.whenSingleSelect;
-    t.whenSingleSelect = T;
-    const M = p.whenMulitSelect;
-    t.whenMulitSelect = M;
-    const k = p.calculatePrevWeekDays;
-    t.calculatePrevWeekDays = k;
-    const _ = p.calculateNextWeekDays;
-    function L(e) {
-      return (
-        m(e), { year: D('calendar.curYear'), month: D('calendar.curMonth') }
-      );
-    }
-    function v(e) {
-      return m(e), D('calendar.selectedDay');
-    }
-    function W(e) {
-      m(e);
-      const t = [...D('calendar.days')];
-      t.map(e => {
-        e.choosed = !1;
-      }),
-        g({ 'calendar.days': t, 'calendar.selectedDay': [] });
-    }
-    function S(e, t, a, n) {
-      m(n);
-      const { selectedDay: r = [], weekMode: o } = D('calendar') || {},
-        { year: c, month: l, day: b } = r[0] || {};
-      if (+c != +e || +l != +t || +b != +a) {
-        if (o) return (0, s.default)(i).jump({ year: e, month: t, day: a });
-        if (e && t) {
-          if ('number' != typeof +e || 'number' != typeof +t)
-            return h.warn('jump 函数年月日参数必须为数字');
-          const n = u.todayTimestamp();
-          g(
-            {
-              'calendar.curYear': e,
-              'calendar.curMonth': t,
-              'calendar.todayTimestamp': n
-            },
-            () => {
-              if ('number' == typeof +a) return p.renderCalendar(e, t, a);
-              p.renderCalendar(e, t);
-            }
-          );
-        } else p.jumpToToday();
-      }
-    }
-    function $(e, t) {
-      m(t), (0, r.default)(i).setTodoLabels(e);
-    }
-    function Y(e, t) {
-      m(t), (0, r.default)(i).deleteTodoLabels(e);
-    }
-    function O(e) {
-      m(e), (0, r.default)(i).clearTodoLabels();
-    }
-    function x(e) {
-      return m(e), (0, r.default)(i).getTodoLabels();
-    }
-    function A(e = [], t) {
-      m(t), (0, n.default)(i).disableDays(e);
-    }
-    function E(e = [], t) {
-      m(t), (0, n.default)(i).enableArea(e);
-    }
-    function I(e = [], t) {
-      m(t), (0, n.default)(i).enableDays(e);
-    }
-    function P(e, t) {
-      m(t), (0, n.default)(i).setSelectedDays(e);
-    }
-    function j(e) {
-      m(e), (0, l.default)(i).getCalendarConfig();
-    }
-    function G(e, t, a) {
-      m(a), (0, l.default)(i).setCalendarConfig(e, t);
-    }
-    function U(e) {
-      return m(e), D('calendar.days', e);
-    }
-    function F(...e) {
-      return new Promise((t, a) => {
-        const n = e[0];
-        if (!e[1])
-          return (0, s.default)(i)
-            .switchWeek(n)
-            .then(t)
-            .catch(a);
-        'string' == typeof e[1]
-          ? (m(e[1]),
-            (0, s.default)(i)
-              .switchWeek(n, e[2])
-              .then(t)
-              .catch(a))
-          : 'object' == typeof e[1] &&
-            ('string' == typeof e[2] && m(e[1]),
-            (0, s.default)(i)
-              .switchWeek(n, e[1])
-              .then(t)
-              .catch(a));
-      });
-    }
-    function N(e, t) {
-      (d.initialTasks.flag = 'process'),
-        ((i = e).config = t),
-        (function(e) {
-          let t = ['日', '一', '二', '三', '四', '五', '六'];
-          'Mon' === e && (t = ['一', '二', '三', '四', '五', '六', '日']),
-            g({ 'calendar.weeksCh': t });
-        })(t.firstDayOfWeek),
-        (function(e) {
-          if (e && 'string' == typeof e) {
-            const t = e.split('-');
-            if (t.length < 3)
-              return h.warn('配置 jumpTo 格式应为: 2018-4-2 或 2018-04-02');
-            S(+t[0], +t[1], +t[2]);
-          } else e ? S() : ((i.config.noDefault = !0), S());
-        })(t.defaultDay),
-        h.tips(
-          '使用中若遇问题请反馈至 https://github.com/treadpit/wx_calendar/issues ✍️'
-        );
-    }
-    t.calculateNextWeekDays = _;
-    t.default = (e, t = {}) => {
-      if ('process' === d.initialTasks.flag)
-        return d.initialTasks.tasks.push(function() {
-          N(e, t);
-        });
-      N(e, t);
-    };
   },
-  function(e, t, a) {
-    'use strict';
-    Object.defineProperty(t, '__esModule', { value: !0 }), (t.default = void 0);
-    var n = o(a(1)),
-      s = o(a(3)),
-      r = a(0);
-    function o(e) {
-      return e && e.__esModule ? e : { default: e };
-    }
-    const c = new r.Logger(),
-      l = new r.GetDate();
-    class b extends n.default {
-      constructor(e) {
-        super(e), (this.Component = e);
-      }
-      enableArea(e = []) {
-        if (2 === e.length) {
-          const { start: t, end: a, startTimestamp: n, endTimestamp: s } = (0,
-          r.convertEnableAreaToTimestamp)(e);
-          if (!t || !a) return;
-          const o = l.thisMonthDays(t[0], t[1]),
-            c = l.thisMonthDays(a[0], a[1]);
-          if (
-            this.__judgeParam({
-              start: t,
-              end: a,
-              startMonthDays: o,
-              endMonthDays: c,
-              startTimestamp: n,
-              endTimestamp: s
-            })
-          ) {
-            let { days: t = [], selectedDay: a = [] } = this.getData(
-              'calendar'
-            );
-            const r = this.__handleEnableArea(
-              { area: e, days: t, startTimestamp: n, endTimestamp: s },
-              a
-            );
-            this.setData({
-              'calendar.enableArea': e,
-              'calendar.days': r.dates,
-              'calendar.selectedDay': r.selectedDay,
-              'calendar.enableAreaTimestamp': [n, s]
-            });
-          }
-        } else
-          c.warn(
-            'enableArea()参数需为时间范围数组,形如:["2018-8-4" , "2018-8-24"]'
-          );
-      }
-      enableDays(e = []) {
-        const { enableArea: t = [] } = this.getData('calendar');
-        let a = [];
-        a = t.length
-          ? (0, r.delRepeatedEnableDay)(e, t)
-          : (0, r.converEnableDaysToTimestamp)(e);
-        let { days: n = [], selectedDay: s = [] } = this.getData('calendar');
-        const o = this.__handleEnableDays(
-          { days: n, expectEnableDaysTimestamp: a },
-          s
-        );
-        this.setData({
-          'calendar.days': o.dates,
-          'calendar.selectedDay': o.selectedDay,
-          'calendar.enableDays': e,
-          'calendar.enableDaysTimestamp': a
-        });
-      }
-      setSelectedDays(e) {
-        if (!(0, s.default)(this.Component).getCalendarConfig().multi)
-          return c.warn('单选模式下不能设置多日期选中,请配置 multi');
-        let { days: t } = this.getData('calendar'),
-          a = [];
-        if (e) {
-          if (e && e.length) {
-            const { dates: n, selectedDates: s } = this.__handleSelectedDays(
-              t,
-              a,
-              e
-            );
-            (t = n), (a = s);
-          }
-        } else
-          t.map(e => {
-            (e.choosed = !0), (e.showTodoLabel = !1);
-          }),
-            (a = t);
-        (0, s.default)(this.Component).setCalendarConfig('multi', !0),
-          this.setData({ 'calendar.days': t, 'calendar.selectedDay': a });
-      }
-      disableDays(e) {
-        const { disableDays: t = [], days: a } = this.getData('calendar');
-        if ('[object Array]' !== Object.prototype.toString.call(e))
-          return c.warn('disableDays 参数为数组');
-        let n = [];
-        if (e.length) {
-          const s = (n = (0, r.uniqueArrayByDate)(e.concat(t))).map(
-            e => `${e.year}-${e.month}-${e.day}`
-          );
-          a.forEach(e => {
-            const t = `${e.year}-${e.month}-${e.day}`;
-            s.includes(t) && (e.disable = !0);
-          });
-        } else
-          a.forEach(e => {
-            e.disable = !1;
-          });
-        this.setData({ 'calendar.days': a, 'calendar.disableDays': n });
-      }
-      __judgeParam(e) {
-        const {
-          start: t,
-          end: a,
-          startMonthDays: n,
-          endMonthDays: s,
-          startTimestamp: r,
-          endTimestamp: o
-        } = e;
-        return t[2] > n || t[2] < 1
-          ? (c.warn(
-              'enableArea() 开始日期错误,指定日期不在当前月份天数范围内'
-            ),
-            !1)
-          : t[1] > 12 || t[1] < 1
-          ? (c.warn('enableArea() 开始日期错误,月份超出1-12月份'), !1)
-          : a[2] > s || a[2] < 1
-          ? (c.warn(
-              'enableArea() 截止日期错误,指定日期不在当前月份天数范围内'
-            ),
-            !1)
-          : a[1] > 12 || a[1] < 1
-          ? (c.warn('enableArea() 截止日期错误,月份超出1-12月份'), !1)
-          : !(r > o) || (c.warn('enableArea()参数最小日期大于了最大日期'), !1);
-      }
-      __handleEnableArea(e = {}, t = []) {
-        const { area: a, days: n, startTimestamp: s, endTimestamp: o } = e,
-          c = this.getData('calendar.enableDays') || [];
-        let b = [];
-        c.length && (b = (0, r.delRepeatedEnableDay)(c, a));
-        const d = [...n];
-        return (
-          d.forEach(e => {
-            const a = l.newDate(e.year, e.month, e.day).getTime();
-            (+s > +a || +a > +o) && !b.includes(+a)
-              ? ((e.disable = !0),
-                e.choosed &&
-                  ((e.choosed = !1),
-                  (t = t.filter(
-                    t =>
-                      `${e.year}-${e.month}-${e.day}` !==
-                      `${t.year}-${t.month}-${t.day}`
-                  ))))
-              : e.disable && (e.disable = !1);
-          }),
-          { dates: d, selectedDay: t }
-        );
-      }
-      __handleEnableDays(e = {}, t = []) {
-        const { days: a, expectEnableDaysTimestamp: n } = e,
-          { enableAreaTimestamp: s = [] } = this.getData('calendar'),
-          r = [...a];
-        return (
-          r.forEach(e => {
-            const a = l.newDate(e.year, e.month, e.day).getTime();
-            let r = !1;
-            s.length
-              ? (+s[0] > +a || +a > +s[1]) && !n.includes(+a) && (r = !0)
-              : n.includes(+a) || (r = !0),
-              r
-                ? ((e.disable = !0),
-                  e.choosed &&
-                    ((e.choosed = !1),
-                    (t = t.filter(
-                      t =>
-                        `${e.year}-${e.month}-${e.day}` !==
-                        `${t.year}-${t.month}-${t.day}`
-                    ))))
-                : (e.disable = !1);
-          }),
-          { dates: r, selectedDay: t }
-        );
-      }
-      __handleSelectedDays(e = [], t = [], a) {
-        const { selectedDay: n, showLabelAlways: s } = this.getData('calendar');
-        t = n && n.length ? (0, r.uniqueArrayByDate)(n.concat(a)) : a;
-        const { year: o, month: c } = e[0],
-          l = [];
-        return (
-          t.forEach(e => {
-            +e.year == +o &&
-              +e.month == +c &&
-              l.push(`${e.year}-${e.month}-${e.day}`);
-          }),
-          [...e].map(e => {
-            l.includes(`${e.year}-${e.month}-${e.day}`) &&
-              ((e.choosed = !0),
-              s && e.showTodoLabel
-                ? (e.showTodoLabel = !0)
-                : (e.showTodoLabel = !1));
-          }),
-          { dates: e, selectedDates: t }
-        );
-      }
+  methods: {
+    initComp() {
+      const calendarConfig = this.setDefaultDisableDate()
+      this.setConfig(calendarConfig)
+    },
+    setDefaultDisableDate() {
+      const calendarConfig = this.properties.calendarConfig || {}
+      if (calendarConfig.disableMode && !calendarConfig.disableMode.date) {
+        calendarConfig.disableMode.date = getDate.toTimeStr(getDate.todayDate())
+      }
+      return calendarConfig
+    },
+    setConfig(config) {
+      if (config.markToday && typeof config.markToday === 'string') {
+        config.highlightToday = true
+      }
+      config.theme = config.theme || 'default'
+      this.weekMode = config.weekMode
+      this.setData(
+        {
+          calendarConfig: config
+        },
+        () => {
+          initCalendar(this, config)
+        }
+      )
+    },
+    chooseDate(e) {
+      const { type } = e.currentTarget.dataset
+      if (!type) return
+      const methodName = this.data.handleMap[type]
+      this[methodName](type)
+    },
+    chooseYear(type) {
+      const { curYear, curMonth } = this.data.calendar
+      if (!curYear || !curMonth) return logger.warn('异常:未获取到当前年月')
+      if (this.weekMode) {
+        return console.warn('周视图下不支持点击切换年月')
+      }
+      let newYear = +curYear
+      let newMonth = +curMonth
+      if (type === 'prev_year') {
+        newYear -= 1
+      } else if (type === 'next_year') {
+        newYear += 1
+      }
+      this.render(curYear, curMonth, newYear, newMonth)
+    },
+    chooseMonth(type) {
+      const { curYear, curMonth } = this.data.calendar
+      if (!curYear || !curMonth) return logger.warn('异常:未获取到当前年月')
+      if (this.weekMode) return console.warn('周视图下不支持点击切换年月')
+      let newYear = +curYear
+      let newMonth = +curMonth
+      if (type === 'prev_month') {
+        newMonth = newMonth - 1
+        if (newMonth < 1) {
+          newYear -= 1
+          newMonth = 12
+        }
+      } else if (type === 'next_month') {
+        newMonth += 1
+        if (newMonth > 12) {
+          newYear += 1
+          newMonth = 1
+        }
+      }
+      this.render(curYear, curMonth, newYear, newMonth)
+    },
+    render(curYear, curMonth, newYear, newMonth) {
+      whenChangeDate.call(this, {
+        curYear,
+        curMonth,
+        newYear,
+        newMonth
+      })
+      this.setData({
+        'calendar.curYear': newYear,
+        'calendar.curMonth': newMonth
+      })
+      renderCalendar.call(this, newYear, newMonth)
+    },
+    /**
+     * 日期点击事件
+     * @param {!object} e 事件对象
+     */
+    tapDayItem(e) {
+      const { idx, date = {} } = e.currentTarget.dataset
+      const { day, disable } = date
+      if (disable || !day) return
+      const config = this.data.calendarConfig || this.config || {}
+      const { multi, chooseAreaMode } = config
+      if (multi) {
+        whenMulitSelect.call(this, idx)
+      } else if (chooseAreaMode) {
+        whenChooseArea.call(this, idx)
+      } else {
+        whenSingleSelect.call(this, idx)
+      }
+      this.setData({
+        'calendar.noDefault': false
+      })
+    },
+    doubleClickToToday() {
+      if (this.config.multi || this.weekMode) return
+      if (this.count === undefined) {
+        this.count = 1
+      } else {
+        this.count += 1
+      }
+      if (this.lastClick) {
+        const difference = new Date().getTime() - this.lastClick
+        if (difference < 500 && this.count >= 2) {
+          jump.call(this)
+        }
+        this.count = undefined
+        this.lastClick = undefined
+      } else {
+        this.lastClick = new Date().getTime()
+      }
+    },
+    /**
+     * 日历滑动开始
+     * @param {object} e
+     */
+    calendarTouchstart(e) {
+      const t = e.touches[0]
+      const startX = t.clientX
+      const startY = t.clientY
+      this.slideLock = true // 滑动事件加锁
+      this.setData({
+        'gesture.startX': startX,
+        'gesture.startY': startY
+      })
+    },
+    /**
+     * 日历滑动中
+     * @param {object} e
+     */
+    calendarTouchmove(e) {
+      const { gesture } = this.data
+      const { preventSwipe } = this.properties.calendarConfig
+      if (!this.slideLock || preventSwipe) return
+      if (slide.isLeft(gesture, e.touches[0])) {
+        this.handleSwipe('left')
+        this.slideLock = false
+      }
+      if (slide.isRight(gesture, e.touches[0])) {
+        this.handleSwipe('right')
+        this.slideLock = false
+      }
+    },
+    calendarTouchend(e) {
+      this.setData({
+        'calendar.leftSwipe': 0,
+        'calendar.rightSwipe': 0
+      })
+    },
+    handleSwipe(direction) {
+      let swipeKey = 'calendar.leftSwipe'
+      let swipeCalendarType = 'next_month'
+      let weekChangeType = 'next_week'
+      if (direction === 'right') {
+        swipeKey = 'calendar.rightSwipe'
+        swipeCalendarType = 'prev_month'
+        weekChangeType = 'prev_week'
+      }
+      this.setData({
+        [swipeKey]: 1
+      })
+      this.currentYM = getCurrentYM()
+      if (this.weekMode) {
+        this.slideLock = false
+        this.currentDates = getCalendarDates()
+        if (weekChangeType === 'prev_week') {
+          Week(this).calculatePrevWeekDays()
+        } else if (weekChangeType === 'next_week') {
+          Week(this).calculateNextWeekDays()
+        }
+        this.onSwipeCalendar(weekChangeType)
+        this.onWeekChange(weekChangeType)
+        return
+      }
+      this.chooseMonth(swipeCalendarType)
+      this.onSwipeCalendar(swipeCalendarType)
+    },
+    onSwipeCalendar(direction) {
+      this.triggerEvent('onSwipe', {
+        directionType: direction,
+        currentYM: this.currentYM
+      })
+    },
+    onWeekChange(direction) {
+      this.triggerEvent('whenChangeWeek', {
+        current: {
+          currentYM: this.currentYM,
+          dates: [...this.currentDates]
+        },
+        next: {
+          currentYM: getCurrentYM(),
+          dates: getCalendarDates()
+        },
+        directionType: direction
+      })
+      this.currentDates = null
+      this.currentYM = null
     }
-    t.default = e => new b(e);
   }
-]);
-
+})

+ 26 - 21
component/calendar/index.wxml

@@ -2,52 +2,55 @@
   <view class="calendar b tb">
     <!-- 头部操作栏 -->
     <view wx:if="{{!calendarConfig.hideHeadOnWeekMode}}" class="handle {{calendarConfig.theme}}_handle-color fs28 b lr ac pc">
-      <view class="prev fs36" wx:if="{{calendarConfig.showHandlerOnWeekMode || !calendar.weekMode}}">
+      <view class="prev fs36" wx:if="{{calendarConfig.showHandlerOnWeekMode || !calendarConfig.weekMode}}">
         <text class="prev-handle iconfont icon-doubleleft" bindtap="chooseDate" data-type="prev_year"></text>
         <text class="prev-handle iconfont icon-left" bindtap="chooseDate" data-type="prev_month"></text>
       </view>
       <view class="flex date-in-handle b lr cc" bindtap="doubleClickToToday">{{calendar.curYear || "--"}} 年 {{calendar.curMonth || "--"}} 月</view>
-      <view class="next fs36" wx:if="{{calendarConfig.showHandlerOnWeekMode || !calendar.weekMode}}">
+      <view class="next fs36" wx:if="{{calendarConfig.showHandlerOnWeekMode || !calendarConfig.weekMode}}">
         <text class="next-handle iconfont icon-right" bindtap="chooseDate" data-type="next_month"></text>
         <text class="next-handle iconfont icon-doubleright" bindtap="chooseDate" data-type="next_year"></text>
       </view>
     </view>
     <!-- 星期栏 -->
     <view class="weeks b lr ac {{calendarConfig.theme}}_week-color">
-      <view class="week fs28" wx:for="{{calendar.weeksCh}}" wx:key="{{index}}" data-idx="{{index}}">{{item}}</view>
+      <view class="week fs28" wx:for="{{calendar.weeksCh}}" wx:key="index" data-idx="{{index}}">{{item}}</view>
     </view>
     <!-- 日历面板主体 -->
     <view class="b lr wrap"
         bindtouchstart="calendarTouchstart"
-        bindtouchmove="calendarTouchmove"
-        bindtouchend="calendarTouchend">
+        catchtouchmove="calendarTouchmove"
+        catchtouchend="calendarTouchend">
         <!-- 上月日期格子 -->
         <view
           class="grid b ac pc {{calendarConfig.theme}}_prev-month-date"
           wx:if="{{calendar.empytGrids}}"
           wx:for="{{calendar.empytGrids}}"
-          wx:key="{{index}}"
+          wx:key="index"
           data-idx="{{index}}">
-            <view class="date">
-              {{item.day}}
-              <view
-                wx:if="{{calendarConfig.showLunar && item.lunar}}"
-                class="date-desc date-desc-bottom">
-                  {{item.lunar.Term || item.lunar.IDayCn}}
+            <view class="date-wrap b cc">
+              <view class="date">
+                {{item.day}}
+                <view
+                  wx:if="{{calendarConfig.showLunar && item.lunar}}"
+                  class="date-desc date-desc-bottom">
+                    {{item.lunar.Term || item.lunar.IDayCn}}
+                </view>
               </view>
             </view>
         </view>
         <!-- 本月日期格子 -->
-        <view class="grid {{calendarConfig.theme}}_normal-date b ac pc"
+        <view
           wx:for="{{calendar.days}}"
-          wx:key="{{index}}"
-          data-disable="{{item.disable}}"
+          wx:key="index"
           data-idx="{{index}}"
-          bindtap="tapDayItem">
+          data-date="{{item}}"
+          bindtap="tapDayItem"
+          class="grid {{item.class ? item.class  : ''}} {{calendarConfig.theme}}_normal-date b ac pc">
             <view
-              class="date-wrap b cc">
-              <view class="date b ac pc {{(item.week === 0 || item.week === 6) ? calendarConfig.theme + '_weekend-color' : ''}} {{calendar.todoLabelCircle && item.showTodoLabel && !item.choosed ? calendarConfig.theme + '_todo-circle todo-circle' : '' }} {{item.isToday ? calendarConfig.theme + '_today' : ''}} {{item.choosed ? calendarConfig.theme + '_choosed' : ''}} {{item.disable ? calendarConfig.theme + '_date-disable' : ''}}">
-                {{item.day}}
+              class="date-wrap b cc {{(item.week === 0 || item.week === 6) ? calendarConfig.theme + '_weekend-color' : ''}}">
+              <view class="date b ac pc {{item.class ? item.class  : ''}} {{calendarConfig.chooseAreaMode ? 'date-area-mode' : ''}} {{calendar.todoLabelCircle && item.showTodoLabel && !item.choosed ? calendarConfig.theme + '_todo-circle todo-circle' : '' }} {{item.isToday ? calendarConfig.theme + '_today' : ''}} {{item.choosed ? calendarConfig.theme + '_choosed' : ''}} {{item.disable ? calendarConfig.theme + '_date-disable' : ''}}">
+                {{calendarConfig.markToday && item.isToday ? calendarConfig.markToday : item.day}}
                 <view
                   wx:if="{{(calendarConfig.showLunar && item.lunar && !item.showTodoLabel) || (item.showTodoLabel && calendar.todoLabelPos !== 'bottom')}}"
                   class="date-desc {{calendarConfig.theme}}_date-desc date-desc-bottom {{(item.choosed || item.isToday) ? 'date-desc-bottom-always' : ''}} {{item.disable ? calendarConfig.theme + '_date-desc-disable' : ''}}">
@@ -56,7 +59,7 @@
                 <view
                   wx:if="{{item.showTodoLabel && !calendar.todoLabelCircle}}"
                   class="{{item.todoText ? 'date-desc' : calendarConfig.theme + '_todo-dot todo-dot'}} {{calendarConfig.showLunar ? calendarConfig.theme + '_date-desc-lunar' : ''}} {{calendar.todoLabelPos === 'bottom' ? 'date-desc-bottom todo-dot-bottom' : 'date-desc-top todo-dot-top'}} {{calendar.showLabelAlways && item.choosed && calendar.todoLabelPos === 'bottom' ? 'date-desc-bottom-always todo-dot-bottom-always' : ''}} {{calendar.showLabelAlways && item.choosed && calendar.todoLabelPos === 'top' ? 'date-desc-top-always todo-dot-top-always' : ''}}"
-                  style="background-color: {{calendar.todoLabelColor}};">
+                  style="background-color: {{item.todoText ? '' : item.color || calendar.todoLabelColor}}; color: {{item.color}}">
                     {{item.todoText}}
                 </view>
               </view>
@@ -66,8 +69,9 @@
         <view
           class="grid b ac pc {{calendarConfig.theme}}_next-month-date"
           wx:for="{{calendar.lastEmptyGrids}}"
-          wx:key="{{index}}"
+          wx:key="index"
           data-idx="{{index}}">
+          <view class="date-wrap b cc">
             <view class="date">
               {{item.day}}
               <view
@@ -76,6 +80,7 @@
                   {{item.lunar.Term || item.lunar.IDayCn}}
               </view>
             </view>
+          </view>
         </view>
       </view>
   </view>

+ 11 - 1
component/calendar/index.wxss

@@ -98,7 +98,8 @@
 }
 
 .date-wrap {
-    height: 72rpx;
+    width: 100%;
+    height: 80rpx;
     position: relative;
     left: 0;
     top: 0;
@@ -122,6 +123,11 @@
     animation-iteration-count: 1;
 }
 
+.date-area-mode {
+    width: 100%;
+    border-radius: 0;
+}
+
 .date-desc {
     width: 150%;
     height: 32rpx;
@@ -199,6 +205,10 @@
     bottom: -14rpx;
 }
 
+.todo-circle .date-desc.date-desc-bottom {
+    bottom: -30rpx;
+}
+
 .date-desc.date-desc-bottom-always {
     bottom: -28rpx;
 }

+ 877 - 0
component/calendar/main.js

@@ -0,0 +1,877 @@
+import Day from './func/day'
+import Week from './func/week'
+import Todo from './func/todo'
+import WxData from './func/wxData'
+import Calendar from './func/render'
+import CalendarConfig from './func/config'
+import convertSolarLunar from './func/convertSolarLunar'
+import {
+  Logger,
+  GetDate,
+  isComponent,
+  initialTasks,
+  getCurrentPage,
+  getComponent,
+  getDateTimeStamp
+} from './func/utils'
+
+let Component = {}
+let logger = new Logger()
+let getDate = new GetDate()
+let dataInstance = null
+
+/**
+ * 全局赋值正在操作的组件实例,方便读/写各自的 data
+ * @param {string} componentId 要操作的日历组件ID
+ */
+function bindCurrentComponent(componentId) {
+  if (componentId) {
+    Component = getComponent(componentId)
+  }
+  return Component
+}
+/**
+ * 获取日历内部数据
+ * @param {string} key 获取值的键名
+ * @param {string} componentId 要操作的日历组件ID
+ */
+function getData(key, componentId) {
+  bindCurrentComponent(componentId)
+  dataInstance = new WxData(Component)
+  return dataInstance.getData(key)
+}
+/**
+ * 设置日历内部数据
+ * @param {object}} data 待设置的数据
+ * @param {function} callback 设置成功回调函数
+ */
+function setData(data, callback = () => {}) {
+  const dataInstance = new WxData(Component)
+  return dataInstance.setData(data, callback)
+}
+
+const conf = {
+  /**
+   * 渲染日历
+   * @param {number} curYear
+   * @param {number} curMonth
+   * @param {number} curDate
+   */
+  renderCalendar(curYear, curMonth, curDate) {
+    if (isComponent(this)) Component = this
+    return new Promise((resolve, reject) => {
+      Calendar(Component)
+        .renderCalendar(curYear, curMonth, curDate)
+        .then((info = {}) => {
+          if (!info.firstRender) {
+            return resolve({
+              year: curYear,
+              month: curMonth,
+              date: curDate
+            })
+          }
+          mountEventsOnPage(getCurrentPage())
+          Component.triggerEvent('afterCalendarRender', Component)
+          Component.firstRender = true
+          initialTasks.flag = 'finished'
+          if (initialTasks.tasks.length) {
+            initialTasks.tasks.shift()()
+          }
+          resolve({
+            year: curYear,
+            month: curMonth,
+            date: curDate
+          })
+        })
+        .catch(err => {
+          reject(err)
+        })
+    })
+  },
+  /**
+   * 当改变月份时触发
+   * @param {object} param
+   */
+  whenChangeDate({ curYear, curMonth, newYear, newMonth }) {
+    Component.triggerEvent('whenChangeMonth', {
+      current: {
+        year: curYear,
+        month: curMonth
+      },
+      next: {
+        year: newYear,
+        month: newMonth
+      }
+    })
+  },
+  /**
+   * 多选
+   * @param {number} dateIdx 当前选中日期索引值
+   */
+  whenMulitSelect(dateIdx) {
+    if (isComponent(this)) Component = this
+    const { calendar = {} } = getData()
+    const { days, todoLabels } = calendar
+    const config = CalendarConfig(Component).getCalendarConfig()
+    let { selectedDay: selectedDays = [] } = calendar
+    const currentDay = days[dateIdx]
+    if (!currentDay) return
+    currentDay.choosed = !currentDay.choosed
+    if (!currentDay.choosed) {
+      currentDay.cancel = true // 该次点击是否为取消日期操作
+      const currentDayStr = getDate.toTimeStr(currentDay)
+      selectedDays = selectedDays.filter(
+        item => currentDayStr !== getDate.toTimeStr(item)
+      )
+      if (todoLabels) {
+        todoLabels.forEach(item => {
+          if (currentDayStr === getDate.toTimeStr(item)) {
+            currentDay.showTodoLabel = true
+          }
+        })
+      }
+    } else {
+      currentDay.cancel = false
+      const { showLabelAlways } = getData('calendar')
+      if (showLabelAlways && currentDay.showTodoLabel) {
+        currentDay.showTodoLabel = true
+      } else {
+        currentDay.showTodoLabel = false
+      }
+      if (!config.takeoverTap) {
+        selectedDays.push(currentDay)
+      }
+    }
+    if (config.takeoverTap) {
+      return Component.triggerEvent('onTapDay', currentDay)
+    }
+    setData({
+      'calendar.days': days,
+      'calendar.selectedDay': selectedDays
+    })
+    conf.afterTapDay(currentDay, selectedDays)
+  },
+  /**
+   * 单选
+   * @param {number} dateIdx 当前选中日期索引值
+   */
+  whenSingleSelect(dateIdx) {
+    if (isComponent(this)) Component = this
+    const { calendar = {} } = getData()
+    const { days, selectedDay: selectedDays = [], todoLabels } = calendar
+    let shouldMarkerTodoDay = []
+    const currentDay = days[dateIdx]
+    if (!currentDay) return
+    const preSelectedDate = [...selectedDays].pop() || {}
+    const { month: dMonth, year: dYear } = days[0] || {}
+    const config = CalendarConfig(Component).getCalendarConfig()
+    if (config.takeoverTap) {
+      return Component.triggerEvent('onTapDay', currentDay)
+    }
+    conf.afterTapDay(currentDay)
+    if (!config.inverse && preSelectedDate.day === currentDay.day) return
+    days.forEach((item, idx) => {
+      if (+item.day === +preSelectedDate.day) days[idx].choosed = false
+    })
+    if (todoLabels) {
+      // 筛选当月待办事项的日期
+      shouldMarkerTodoDay = todoLabels.filter(
+        item => +item.year === dYear && +item.month === dMonth
+      )
+    }
+    Todo(Component).showTodoLabels(shouldMarkerTodoDay, days, selectedDays)
+    const tmp = {
+      'calendar.days': days
+    }
+    if (preSelectedDate.day !== currentDay.day) {
+      preSelectedDate.choosed = false
+      currentDay.choosed = true
+      if (!calendar.showLabelAlways || !currentDay.showTodoLabel) {
+        currentDay.showTodoLabel = false
+      }
+      tmp['calendar.selectedDay'] = [currentDay]
+    } else if (config.inverse) {
+      if (currentDay.choosed) {
+        if (currentDay.showTodoLabel && calendar.showLabelAlways) {
+          currentDay.showTodoLabel = true
+        } else {
+          currentDay.showTodoLabel = false
+        }
+      }
+      tmp['calendar.selectedDay'] = []
+    }
+    if (config.weekMode) {
+      tmp['calendar.curYear'] = currentDay.year
+      tmp['calendar.curMonth'] = currentDay.month
+    }
+    setData(tmp)
+  },
+  gotoSetContinuousDates(start, end) {
+    return chooseDateArea([
+      `${getDate.toTimeStr(start)}`,
+      `${getDate.toTimeStr(end)}`
+    ])
+  },
+  timeRangeHelper(currentDate, selectedDay) {
+    const currentDateTimestamp = getDateTimeStamp(currentDate)
+    const startDate = selectedDay[0]
+    let endDate
+    let endDateTimestamp
+    let selectedLen = selectedDay.length
+    if (selectedLen > 1) {
+      endDate = selectedDay[selectedLen - 1]
+      endDateTimestamp = getDateTimeStamp(endDate)
+    }
+    const startTimestamp = getDateTimeStamp(startDate)
+    return {
+      endDate,
+      startDate,
+      currentDateTimestamp,
+      endDateTimestamp,
+      startTimestamp
+    }
+  },
+  /**
+   * 计算连续日期选择的开始及结束日期
+   * @param {object} currentDate 当前选择日期
+   * @param {array} selectedDay 已选择的的日期
+   */
+  calculateDateRange(currentDate, selectedDay) {
+    const {
+      endDate,
+      startDate,
+      currentDateTimestamp,
+      endDateTimestamp,
+      startTimestamp
+    } = this.timeRangeHelper(currentDate, selectedDay)
+    let range = []
+    let selectedLen = selectedDay.length
+    const isWantToChooseOneDate = selectedDay.filter(
+      item => getDate.toTimeStr(item) === getDate.toTimeStr(currentDate)
+    )
+    if (selectedLen === 2 && isWantToChooseOneDate.length) {
+      range = [currentDate, currentDate]
+      return range
+    }
+    if (
+      currentDateTimestamp >= startTimestamp &&
+      endDateTimestamp &&
+      currentDateTimestamp <= endDateTimestamp
+    ) {
+      const currentDateIdxInChoosedDateArea = selectedDay.findIndex(
+        item => getDate.toTimeStr(item) === getDate.toTimeStr(currentDate)
+      )
+      if (selectedLen / 2 > currentDateIdxInChoosedDateArea) {
+        range = [currentDate, endDate]
+      } else {
+        range = [startDate, currentDate]
+      }
+    } else if (currentDateTimestamp < startTimestamp) {
+      range = [currentDate, endDate]
+    } else if (currentDateTimestamp > startTimestamp) {
+      range = [startDate, currentDate]
+    }
+    return range
+  },
+  chooseAreaWhenExistArea(currentDate, selectedDay) {
+    return new Promise((resolve, reject) => {
+      const range = conf.calculateDateRange(
+        currentDate,
+        getDate.sortDates(selectedDay)
+      )
+      conf
+        .gotoSetContinuousDates(...range)
+        .then(data => {
+          resolve(data)
+          conf.afterTapDay(currentDate)
+        })
+        .catch(err => {
+          reject(err)
+          conf.afterTapDay(currentDate)
+        })
+    })
+  },
+  chooseAreaWhenHasOneDate(currentDate, selectedDay, lastChoosedDate) {
+    return new Promise((resolve, reject) => {
+      const startDate = lastChoosedDate || selectedDay[0]
+      let range = [startDate, currentDate]
+      const currentDateTimestamp = getDateTimeStamp(currentDate)
+      const lastChoosedDateTimestamp = getDateTimeStamp(startDate)
+      if (lastChoosedDateTimestamp > currentDateTimestamp) {
+        range = [currentDate, startDate]
+      }
+      conf
+        .gotoSetContinuousDates(...range)
+        .then(data => {
+          resolve(data)
+          conf.afterTapDay(currentDate)
+        })
+        .catch(err => {
+          reject(err)
+          conf.afterTapDay(currentDate)
+        })
+    })
+  },
+  /**
+   * 日期范围选择模式
+   * @param {number} dateIdx 当前选中日期索引值
+   */
+  whenChooseArea(dateIdx) {
+    return new Promise((resolve, reject) => {
+      if (isComponent(this)) Component = this
+      if (Component.weekMode) return
+      const { days = [], selectedDay, lastChoosedDate } = getData('calendar')
+      const currentDate = days[dateIdx]
+      if (currentDate.disable) return
+      const config = CalendarConfig(Component).getCalendarConfig()
+      if (config.takeoverTap) {
+        return Component.triggerEvent('onTapDay', currentDate)
+      }
+      if (selectedDay && selectedDay.length > 1) {
+        conf
+          .chooseAreaWhenExistArea(currentDate, selectedDay)
+          .then(dates => {
+            resolve(dates)
+          })
+          .catch(err => {
+            reject(err)
+          })
+      } else if (lastChoosedDate || (selectedDay && selectedDay.length === 1)) {
+        conf
+          .chooseAreaWhenHasOneDate(currentDate, selectedDay, lastChoosedDate)
+          .then(dates => {
+            resolve(dates)
+          })
+          .catch(err => {
+            reject(err)
+          })
+      } else {
+        days.forEach(date => {
+          if (+date.day === +currentDate.day) {
+            date.choosed = true
+          } else {
+            date.choosed = false
+          }
+        })
+
+        const dataInstance = new WxData(Component)
+        dataInstance.setData({
+          'calendar.days': [...days],
+          'calendar.lastChoosedDate': currentDate
+        })
+      }
+    })
+  },
+  /**
+   * 点击日期后触发事件
+   * @param {object} currentSelected 当前选择的日期
+   * @param {array} selectedDates  多选状态下选中的日期
+   */
+  afterTapDay(currentSelected, selectedDates) {
+    const config = CalendarConfig(Component).getCalendarConfig()
+    const { multi } = config
+    if (!multi) {
+      Component.triggerEvent('afterTapDay', currentSelected)
+    } else {
+      Component.triggerEvent('afterTapDay', {
+        currentSelected,
+        selectedDates
+      })
+    }
+  },
+  /**
+   * 跳转至今天
+   */
+  jumpToToday() {
+    return new Promise((resolve, reject) => {
+      const { year, month, date } = getDate.todayDate()
+      const timestamp = getDate.todayTimestamp()
+      const config = CalendarConfig(Component).getCalendarConfig()
+      setData({
+        'calendar.curYear': year,
+        'calendar.curMonth': month,
+        'calendar.selectedDay': [
+          {
+            year: year,
+            day: date,
+            month: month,
+            choosed: true,
+            lunar: config.showLunar
+              ? convertSolarLunar.solar2lunar(year, month, date)
+              : null
+          }
+        ],
+        'calendar.todayTimestamp': timestamp
+      })
+      conf
+        .renderCalendar(year, month, date)
+        .then(() => {
+          resolve({ year, month, date })
+        })
+        .catch(() => {
+          reject('jump failed')
+        })
+    })
+  }
+}
+
+export const whenChangeDate = conf.whenChangeDate
+export const renderCalendar = conf.renderCalendar
+export const whenSingleSelect = conf.whenSingleSelect
+export const whenChooseArea = conf.whenChooseArea
+export const whenMulitSelect = conf.whenMulitSelect
+export const calculatePrevWeekDays = conf.calculatePrevWeekDays
+export const calculateNextWeekDays = conf.calculateNextWeekDays
+
+/**
+ * 获取当前年月
+ * @param {string} componentId 要操作的日历组件ID
+ */
+export function getCurrentYM(componentId) {
+  bindCurrentComponent(componentId)
+  return {
+    year: getData('calendar.curYear'),
+    month: getData('calendar.curMonth')
+  }
+}
+
+/**
+ * 获取已选择的日期
+ * @param {object } options 日期配置选项 {lunar} 是否返回农历信息
+ * @param {string} componentId 要操作的日历组件ID
+ */
+export function getSelectedDay(options = {}, componentId) {
+  bindCurrentComponent(componentId)
+  const config = getCalendarConfig()
+  const dates = getData('calendar.selectedDay') || []
+  if (options.lunar && !config.showLunar) {
+    const datesWithLunar = getDate.convertLunar(dates)
+    return datesWithLunar
+  } else {
+    return dates
+  }
+}
+
+/**
+ * 取消选中日期
+ * @param {array} dates 需要取消的日期,不传则取消所有已选择的日期
+ * @param {string} componentId 要操作的日历组件ID
+ */
+export function cancelSelectedDates(dates, componentId) {
+  bindCurrentComponent(componentId)
+  const { days = [], selectedDay = [] } = getData('calendar') || {}
+  if (!dates || !dates.length) {
+    days.forEach(item => {
+      item.choosed = false
+    })
+    setData({
+      'calendar.days': days,
+      'calendar.selectedDay': []
+    })
+  } else {
+    const cancelDatesStr = dates.map(
+      date => `${+date.year}-${+date.month}-${+date.day}`
+    )
+    const filterSelectedDates = selectedDay.filter(
+      date =>
+        !cancelDatesStr.includes(`${+date.year}-${+date.month}-${+date.day}`)
+    )
+    days.forEach(date => {
+      if (
+        cancelDatesStr.includes(`${+date.year}-${+date.month}-${+date.day}`)
+      ) {
+        date.choosed = false
+      }
+    })
+    setData({
+      'calendar.days': days,
+      'calendar.selectedDay': filterSelectedDates
+    })
+  }
+}
+/**
+ * 周视图跳转
+ * @param {object} date info
+ * @param {boolean} disableSelected 跳转时是否需要选中,周视图切换调用该方法,如未选择日期时不选中日期
+ */
+function jumpWhenWeekMode({ year, month, day }, disableSelected) {
+  return new Promise((resolve, reject) => {
+    Week(Component)
+      .jump(
+        {
+          year: +year,
+          month: +month,
+          day: +day
+        },
+        disableSelected
+      )
+      .then(date => {
+        resolve(date)
+        Component.triggerEvent('afterCalendarRender', Component)
+      })
+      .catch(err => {
+        reject(err)
+        Component.triggerEvent('afterCalendarRender', Component)
+      })
+  })
+}
+
+/**
+ * 月视图跳转
+ * @param {object} date info
+ */
+function jumpWhenNormalMode({ year, month, day }) {
+  return new Promise((resolve, reject) => {
+    if (typeof +year !== 'number' || typeof +month !== 'number') {
+      return logger.warn('jump 函数年月日参数必须为数字')
+    }
+    const timestamp = getDate.todayTimestamp()
+    let tmp = {
+      'calendar.curYear': +year,
+      'calendar.curMonth': +month,
+      'calendar.todayTimestamp': timestamp
+    }
+    setData(tmp, () => {
+      conf
+        .renderCalendar(+year, +month, +day)
+        .then(date => {
+          resolve(date)
+        })
+        .catch(err => {
+          reject(err)
+        })
+    })
+  })
+}
+
+/**
+ * 跳转至指定日期
+ * @param {number} year
+ * @param {number} month
+ * @param {number} day
+ * @param {string} componentId 要操作的日历组件ID
+ */
+export function jump(year, month, day, componentId) {
+  return new Promise((resolve, reject) => {
+    bindCurrentComponent(componentId)
+    const { selectedDay = [] } = getData('calendar') || {}
+    const { weekMode } = getData('calendarConfig') || {}
+    const { year: y, month: m, day: d } = selectedDay[0] || {}
+    if (+y === +year && +m === +month && +d === +day) {
+      return
+    }
+    if (weekMode) {
+      let disableSelected = false
+      if (!year || !month || !day) {
+        const today = getDate.todayDate()
+        year = today.year
+        month = today.month
+        day = today.date
+        disableSelected = true
+      }
+      jumpWhenWeekMode({ year, month, day }, disableSelected)
+        .then(date => {
+          resolve(date)
+        })
+        .catch(err => {
+          reject(err)
+        })
+      mountEventsOnPage(getCurrentPage())
+      return
+    }
+    if (year && month) {
+      jumpWhenNormalMode({ year, month, day })
+        .then(date => {
+          resolve(date)
+        })
+        .catch(err => {
+          reject(err)
+        })
+    } else {
+      conf
+        .jumpToToday()
+        .then(date => {
+          resolve(date)
+        })
+        .catch(err => {
+          reject(err)
+        })
+    }
+  })
+}
+
+/**
+ * 设置待办事项日期标记
+ * @param {object} todos  待办事项配置
+ * @param {string} [todos.pos] 标记显示位置,默认值'bottom' ['bottom', 'top']
+ * @param {string} [todos.dotColor] 标记点颜色,backgroundColor 支持的值都行
+ * @param {object[]} [todos.days] 需要标记的所有日期,如:[{year: 2015, month: 5, day: 12}],其中年月日字段必填
+ * @param {string} componentId 要操作的日历组件ID
+ */
+export function setTodoLabels(todos, componentId) {
+  bindCurrentComponent(componentId)
+  Todo(Component).setTodoLabels(todos)
+}
+
+/**
+ * 删除指定日期待办事项
+ * @param {array} todos 需要删除的待办日期数组
+ * @param {string} componentId 要操作的日历组件ID
+ */
+export function deleteTodoLabels(todos, componentId) {
+  bindCurrentComponent(componentId)
+  Todo(Component).deleteTodoLabels(todos)
+}
+
+/**
+ * 清空所有待办事项
+ * @param {string} componentId 要操作的日历组件ID
+ */
+export function clearTodoLabels(componentId) {
+  bindCurrentComponent(componentId)
+  Todo(Component).clearTodoLabels()
+}
+
+/**
+ * 获取所有待办事项
+ * @param {object } options 日期配置选项 {lunar} 是否返回农历信息
+ * @param {string} componentId 要操作的日历组件ID
+ */
+export function getTodoLabels(options = {}, componentId) {
+  bindCurrentComponent(componentId)
+  const config = getCalendarConfig()
+  const todoDates = Todo(Component).getTodoLabels() || []
+  if (options.lunar && !config.showLunar) {
+    const todoDatesWithLunar = getDate.convertLunar(todoDates)
+    return todoDatesWithLunar
+  } else {
+    return todoDates
+  }
+}
+
+/**
+ * 禁用指定日期
+ * @param {array} days 日期
+ * @param {number} [days.year]
+ * @param {number} [days.month]
+ * @param {number} [days.day]
+ * @param {string} componentId 要操作的日历组件ID
+ */
+export function disableDay(days = [], componentId) {
+  bindCurrentComponent(componentId)
+  Day(Component).disableDays(days)
+}
+
+/**
+ * 指定可选日期范围
+ * @param {array} area 日期访问数组
+ * @param {string} componentId 要操作的日历组件ID
+ */
+export function enableArea(area = [], componentId) {
+  bindCurrentComponent(componentId)
+  Day(Component).enableArea(area)
+}
+
+/**
+ * 指定特定日期可选
+ * @param {array} days 指定日期数组
+ * @param {string} componentId 要操作的日历组件ID
+ */
+export function enableDays(days = [], componentId) {
+  bindCurrentComponent(componentId)
+  Day(Component).enableDays(days)
+}
+
+/**
+ * 设置选中日期(多选模式下)
+ * @param {array} selected 需选中日期
+ * @param {string} componentId 要操作的日历组件ID
+ */
+export function setSelectedDays(selected, componentId) {
+  bindCurrentComponent(componentId)
+  Day(Component).setSelectedDays(selected)
+}
+
+/**
+ * 获取当前日历配置
+ * @param {string} componentId 要操作的日历组件ID
+ */
+export function getCalendarConfig(componentId) {
+  bindCurrentComponent(componentId)
+  return CalendarConfig(Component).getCalendarConfig()
+}
+
+/**
+ * 设置日历配置
+ * @param {object} config
+ * @param {string} componentId 要操作的日历组件ID
+ */
+export function setCalendarConfig(config, componentId) {
+  bindCurrentComponent(componentId)
+  if (!config || Object.keys(config).length === 0) {
+    return logger.warn('setCalendarConfig 参数必须为非空对象')
+  }
+  const existConfig = getCalendarConfig()
+  return new Promise((resolve, reject) => {
+    CalendarConfig(Component)
+      .setCalendarConfig(config)
+      .then(conf => {
+        resolve(conf)
+        const { date, type } = existConfig.disableMode || {}
+        const { _date, _type } = config.disableMode || {}
+        if (type !== _type || date !== _date) {
+          const { year, month } = getCurrentYM()
+          jump(year, month)
+        }
+      })
+      .catch(err => {
+        reject(err)
+      })
+  })
+}
+
+/**
+ * 获取当前日历面板日期
+ * @param {object } options 日期配置选项 {lunar} 是否返回农历信息
+ * @param {string} componentId 要操作的日历组件ID
+ */
+export function getCalendarDates(options = {}, componentId) {
+  bindCurrentComponent(componentId)
+  const config = getCalendarConfig()
+  const dates = getData('calendar.days', componentId) || []
+  if (options.lunar && !config.showLunar) {
+    const datesWithLunar = getDate.convertLunar(dates)
+    return datesWithLunar
+  } else {
+    return dates
+  }
+}
+
+/**
+ * 选择连续日期范围
+ * @param {string} componentId 要操作的日历组件ID
+ */
+export function chooseDateArea(dateArea, componentId) {
+  bindCurrentComponent(componentId)
+  return Day(Component).chooseArea(dateArea)
+}
+
+/**
+ * 设置指定日期样式
+ * @param {array} dates 待设置特殊样式的日期
+ * @param {string} componentId 要操作的日历组件ID
+ */
+export function setDateStyle(dates, componentId) {
+  if (!dates) return
+  bindCurrentComponent(componentId)
+  Day(Component).setDateStyle(dates)
+}
+
+/**
+ * 切换周月视图
+ * 切换视图时可传入指定日期,如: {year: 2019, month: 1, day: 3}
+ * args[0] view 视图模式[week, month]
+ * args[1]|args[2]为day object或者 componentId
+ */
+export function switchView(...args) {
+  return new Promise((resolve, reject) => {
+    const view = args[0]
+    if (!args[1]) {
+      return Week(Component)
+        .switchWeek(view)
+        .then(resolve)
+        .catch(reject)
+    }
+    if (typeof args[1] === 'string') {
+      bindCurrentComponent(args[1], this)
+      Week(Component)
+        .switchWeek(view, args[2])
+        .then(resolve)
+        .catch(reject)
+    } else if (typeof args[1] === 'object') {
+      if (typeof args[2] === 'string') {
+        bindCurrentComponent(args[1], this)
+      }
+      Week(Component)
+        .switchWeek(view, args[1])
+        .then(resolve)
+        .catch(reject)
+    }
+  })
+}
+
+/**
+ * 绑定日历事件至当前页面实例
+ * @param {object} page 当前页面实例
+ */
+function mountEventsOnPage(page) {
+  page.calendar = {
+    jump,
+    switchView,
+    disableDay,
+    enableArea,
+    enableDays,
+    chooseDateArea,
+    getCurrentYM,
+    getSelectedDay,
+    cancelSelectedDates,
+    setDateStyle,
+    setTodoLabels,
+    getTodoLabels,
+    deleteTodoLabels,
+    clearTodoLabels,
+    setSelectedDays,
+    getCalendarConfig,
+    setCalendarConfig,
+    getCalendarDates
+  }
+}
+
+function setWeekHeader(firstDayOfWeek) {
+  let weeksCh = ['日', '一', '二', '三', '四', '五', '六']
+  if (firstDayOfWeek === 'Mon') {
+    weeksCh = ['一', '二', '三', '四', '五', '六', '日']
+  }
+  setData({
+    'calendar.weeksCh': weeksCh
+  })
+}
+
+function autoSelectDay(defaultDay) {
+  Component.firstRenderWeekMode = true
+  if (defaultDay && typeof defaultDay === 'string') {
+    const day = defaultDay.split('-')
+    if (day.length < 3) {
+      return logger.warn('配置 jumpTo 格式应为: 2018-4-2 或 2018-04-02')
+    }
+    jump(+day[0], +day[1], +day[2])
+  } else {
+    if (!defaultDay) {
+      Component.config.noDefault = true
+      setData({
+        'config.noDefault': true
+      })
+    }
+    jump()
+  }
+}
+
+function init(component, config) {
+  initialTasks.flag = 'process'
+  Component = component
+  Component.config = config
+  setWeekHeader(config.firstDayOfWeek)
+  autoSelectDay(config.defaultDay)
+  logger.tips(
+    '使用中若遇问题请反馈至 https://github.com/treadpit/wx_calendar/issues ✍️'
+  )
+}
+
+export default (component, config = {}) => {
+  if (initialTasks.flag === 'process') {
+    return initialTasks.tasks.push(function() {
+      init(component, config)
+    })
+  }
+  init(component, config)
+}

+ 144 - 0
component/v2/core.js

@@ -0,0 +1,144 @@
+import { dateUtil, getCalendarConfig } from './utils/index'
+
+/**
+ * 计算当前月份前后两月应占的格子
+ * @param {number} year 年份
+ * @param {number} month 月份
+ */
+function calculateEmptyGrids(year, month, config) {
+  const prevMonthGrids = calculatePrevMonthGrids(year, month, config)
+  const nextMonthGrids = calculateNextMonthGrids(year, month, config)
+  return {
+    prevMonthGrids,
+    nextMonthGrids
+  }
+}
+
+/**
+ * 计算上月应占的格子
+ * @param {number} year 年份
+ * @param {number} month 月份
+ */
+function calculatePrevMonthGrids(year, month, config) {
+  let emptyGrids = []
+  const prevMonthDays = dateUtil.getDatesCountOfMonth(year, month - 1)
+  let firstDayOfWeek = dateUtil.firstDayOfWeek(year, month)
+  if (config.firstDayOfWeek === 'Mon') {
+    if (firstDayOfWeek === 0) {
+      firstDayOfWeek = 6
+    } else {
+      firstDayOfWeek -= 1
+    }
+  }
+  if (firstDayOfWeek > 0) {
+    const len = prevMonthDays - firstDayOfWeek
+    const { onlyShowCurrentMonth } = config
+    const YMInfo = dateUtil.getPrevMonthInfo({ year, month })
+    for (let i = prevMonthDays; i > len; i--) {
+      if (onlyShowCurrentMonth) {
+        emptyGrids.push('')
+      } else {
+        const week = dateUtil.getDayOfWeek(+year, +month, i)
+        emptyGrids.push({
+          ...YMInfo,
+          date: i,
+          week
+        })
+      }
+    }
+    emptyGrids.reverse()
+  }
+  return emptyGrids
+}
+/**
+ * 计算下一月日期是否需要多展示的日期
+ * 某些月份日期为5排,某些月份6排,统一为6排
+ * @param {number} year
+ * @param {number} month
+ * @param {object} config
+ */
+function calculateExtraEmptyDate(year, month, config) {
+  let extDate = 0
+  if (+month === 2) {
+    extDate += 7
+    let firstDayofMonth = dateUtil.getDayOfWeek(year, month, 1)
+    if (config.firstDayOfWeek === 'Mon') {
+      if (+firstDayofMonth === 1) extDate += 7
+    } else {
+      if (+firstDayofMonth === 0) extDate += 7
+    }
+  } else {
+    let firstDayofMonth = dateUtil.getDayOfWeek(year, month, 1)
+    if (config.firstDayOfWeek === 'Mon') {
+      if (firstDayofMonth !== 0 && firstDayofMonth < 6) {
+        extDate += 7
+      }
+    } else {
+      if (firstDayofMonth <= 5) {
+        extDate += 7
+      }
+    }
+  }
+  return extDate
+}
+/**
+ * 计算下月应占的格子
+ * @param {number} year 年份
+ * @param {number} month  月份
+ */
+function calculateNextMonthGrids(year, month, config) {
+  let emptyGrids = []
+  const datesCount = dateUtil.getDatesCountOfMonth(year, month)
+  let lastDayWeek = dateUtil.getDayOfWeek(year, month, datesCount)
+  if (config.firstDayOfWeek === 'Mon') {
+    if (lastDayWeek === 0) {
+      lastDayWeek = 6
+    } else {
+      lastDayWeek -= 1
+    }
+  }
+  let len = 7 - (lastDayWeek + 1)
+  const { onlyShowCurrentMonth } = config
+  if (!onlyShowCurrentMonth) {
+    len = len + calculateExtraEmptyDate(year, month, config)
+  }
+  const YMInfo = dateUtil.getNextMonthInfo({ year, month })
+  for (let i = 1; i <= len; i++) {
+    const week = dateUtil.getDayOfWeek(+year, +month, i)
+    if (onlyShowCurrentMonth) {
+      emptyGrids.push('')
+    } else {
+      emptyGrids.push({
+        id: i - 1,
+        ...YMInfo,
+        date: i,
+        week: week || 7
+      })
+    }
+  }
+  return emptyGrids
+}
+/**
+ * 设置日历面板数据
+ * @param {number} year 年份
+ * @param {number} month  月份
+ * @param {number} curDate  日期
+ */
+function calculateCurrentMonthDates(year, month) {
+  return dateUtil.calcDates(year, month)
+}
+
+export function calcJumpData({ dateInfo, config, component }) {
+  dateInfo = dateInfo || dateUtil.todayFMD()
+  const { year, month, date } = dateInfo
+  const calendarConfig = config || getCalendarConfig(component)
+  const emptyGrids = calculateEmptyGrids(year, month, calendarConfig)
+  const calendar = {
+    curYear: year,
+    curMonth: month,
+    curDate: date,
+    dates: calculateCurrentMonthDates(year, month),
+    ...emptyGrids
+  }
+  return calendar
+}

+ 12 - 0
component/v2/helper.js

@@ -0,0 +1,12 @@
+import { dateUtil } from './utils/index'
+
+export function calcTargetYMInfo() {
+  return {
+    right: dateUtil.getPrevMonthInfo,
+    left: dateUtil.getNextMonthInfo,
+    prev_month: dateUtil.getPrevMonthInfo,
+    next_month: dateUtil.getNextMonthInfo,
+    prev_year: dateUtil.getPrevYearInfo,
+    next_year: dateUtil.getNextYearInfo
+  }
+}

+ 257 - 0
component/v2/index.js

@@ -0,0 +1,257 @@
+import plugins from './plugins/index'
+import { calcJumpData } from './core'
+import { renderCalendar } from './render'
+import { calcTargetYMInfo } from './helper'
+import { dateUtil, calendarGesture, logger } from './utils/index'
+
+Component({
+  options: {
+    styleIsolation: 'apply-shared',
+    multipleSlots: true // 在组件定义时的选项中启用多slot支持
+  },
+  properties: {
+    config: {
+      type: Object,
+      value: {}
+    }
+  },
+  lifetimes: {
+    attached: function() {
+      this.initComp()
+    }
+  },
+  methods: {
+    initComp() {
+      const calendarConfig = this.setDefaultDisableDate()
+      this.setConfig(calendarConfig)
+    },
+    // 禁用某天日期配置默认为今天
+    setDefaultDisableDate() {
+      const calendarConfig = this.properties.config || {}
+      if (calendarConfig.disableMode && !calendarConfig.disableMode.date) {
+        calendarConfig.disableMode.date = dateUtil.toTimeStr(
+          dateUtil.todayFMD()
+        )
+      }
+      return calendarConfig
+    },
+    initCalendar(config) {
+      const { defaultDate } = config
+      let date = dateUtil.todayFMD()
+      if (defaultDate && typeof defaultDate === 'string') {
+        const dateInfo = defaultDate.split('-')
+        if (dateInfo.length < 3) {
+          return logger.warn('defaultDate配置格式应为: 2018-4-2 或 2018-04-02')
+        } else {
+          date = {
+            year: +dateInfo[0],
+            month: +dateInfo[1],
+            date: +dateInfo[2]
+          }
+        }
+      }
+      const waitRenderData = calcJumpData({
+        dateInfo: date,
+        config
+      })
+      const timestamp = dateUtil.todayTimestamp()
+      if (config.autoChoosedWhenJump) {
+        const target = waitRenderData.dates.filter(
+          item => dateUtil.toTimeStr(item) === dateUtil.toTimeStr(date)
+        )
+        if (target && target.length) {
+          if (!waitRenderData.selectedDates) {
+            waitRenderData.selectedDates = target
+          } else {
+            waitRenderData.selectedDates.push(target[0])
+          }
+        }
+      }
+      return {
+        ...waitRenderData,
+        todayTimestamp: timestamp,
+        weeksCh: dateUtil.getWeekHeader(config.firstDayOfWeek)
+      }
+    },
+    setConfig(config) {
+      if (config.markToday && typeof config.markToday === 'string') {
+        config.highlightToday = true
+      }
+      config.theme = config.theme || 'default'
+      this.setData(
+        {
+          config
+        },
+        () => {
+          for (let plugin of plugins.installed) {
+            const [, p] = plugin
+            if (typeof p.install === 'function') {
+              p.install(this)
+            }
+            if (typeof p.methods === 'function') {
+              const methods = p.methods(this)
+              for (let fnName in methods) {
+                if (fnName.startsWith('__')) continue
+                const fn = methods[fnName]
+                if (typeof fn === 'function') {
+                  if (!this.calendar) this.calendar = {}
+                  this.calendar[fnName] = fn
+                }
+              }
+            }
+          }
+          const initData = this.initCalendar(config)
+          renderCalendar.call(this, initData, config)
+        }
+      )
+    },
+    tapDate(e) {
+      const { info } = e.currentTarget.dataset
+      const { date, disable } = info || {}
+      if (disable || !date) return
+      const { calendar, config } = this.data
+      let calendarData = calendar
+      let calendarConfig = config
+      if (config.takeoverTap) {
+        return this.triggerEvent('takeoverTap', info)
+      }
+      for (let plugin of plugins.installed) {
+        const [, p] = plugin
+        if (typeof p.onTapDate === 'function') {
+          const {
+            calendarData: __calendarData,
+            calendarConfig: __calendarConfig
+          } = p.onTapDate(info, calendarData, calendarConfig)
+          calendarData = __calendarData
+          calendarConfig = __calendarConfig
+        }
+      }
+      renderCalendar.call(this, calendarData, calendarConfig).then(() => {
+        this.triggerEvent('afterTapDate', info)
+      })
+    },
+    /**
+     * 日历滑动开始
+     * @param {object} e
+     */
+    calendarTouchstart(e) {
+      const t = e.touches[0]
+      const startX = t.clientX
+      const startY = t.clientY
+      this.swipeLock = true
+      this.setData({
+        'gesture.startX': startX,
+        'gesture.startY': startY
+      })
+    },
+    /**
+     * 日历滑动中
+     * @param {object} e
+     */
+    calendarTouchmove(e) {
+      const { gesture } = this.data
+      const { preventSwipe } = this.properties.config
+      if (!this.swipeLock || preventSwipe) return
+      if (calendarGesture.isLeft(gesture, e.touches[0])) {
+        this.handleSwipe('left')
+        this.swipeLock = false
+      }
+      if (calendarGesture.isRight(gesture, e.touches[0])) {
+        this.handleSwipe('right')
+        this.swipeLock = false
+      }
+    },
+    calendarTouchend(e) {
+      this.setData({
+        'calendar.leftSwipe': 0,
+        'calendar.rightSwipe': 0
+      })
+    },
+    handleSwipe(direction) {
+      let swipeKey = 'calendar.leftSwipe'
+      if (direction === 'right') {
+        swipeKey = 'calendar.rightSwipe'
+      }
+      this.setData({
+        [swipeKey]: 1
+      })
+      const { calendar } = this.data
+      let calendarData = calendar
+      const { curYear, curMonth } = calendarData
+      const getMonthInfo = calcTargetYMInfo()[direction]
+      const target = getMonthInfo({
+        year: +curYear,
+        month: +curMonth
+      })
+      target.direction = direction
+      this.renderCalendar(target)
+    },
+    changeDate(e) {
+      const { type } = e.currentTarget.dataset
+      const { calendar: calendarData } = this.data
+      const { curYear, curMonth } = calendarData
+      const getMonthInfo = calcTargetYMInfo()[type]
+      const target = getMonthInfo({
+        year: +curYear,
+        month: +curMonth
+      })
+      target.direction = type
+      this.renderCalendar(target)
+    },
+    renderCalendar(target) {
+      let { calendar: calendarData, config } = this.data
+      const { curYear, curMonth } = calendarData || {}
+      for (let plugin of plugins.installed) {
+        const [, p] = plugin
+        if (typeof p.onSwitchCalendar === 'function') {
+          calendarData = p.onSwitchCalendar(target, calendarData, this)
+        }
+      }
+      return renderCalendar.call(this, calendarData, config).then(() => {
+        let triggerEventName = 'whenChangeMonth'
+        if (config.weekMode) {
+          triggerEventName = 'whenChangeWeek'
+        }
+        this.triggerEvent(triggerEventName, {
+          current: {
+            year: +curYear,
+            month: +curMonth
+          },
+          next: target
+        })
+        this.triggerEvent('onSwipe', {
+          current: {
+            year: +curYear,
+            month: +curMonth
+          },
+          next: target,
+          type: triggerEventName
+        })
+      })
+    },
+    doubleClickJumpToToday() {
+      const { multi, weekMode } = this.calendar.getCalendarConfig() || {}
+      if (multi || weekMode) return
+      if (this.count === undefined) {
+        this.count = 1
+      } else {
+        this.count += 1
+      }
+      if (this.lastClick) {
+        const difference = new Date().getTime() - this.lastClick
+        if (
+          difference < 500 &&
+          this.count >= 2 &&
+          typeof this.calendar.jump === 'function'
+        ) {
+          const today = dateUtil.todayFMD()
+          this.calendar.jump(today)
+        }
+        this.count = undefined
+        this.lastClick = undefined
+      } else {
+        this.lastClick = new Date().getTime()
+      }
+    }
+  }
+})

+ 3 - 0
component/v2/index.json

@@ -0,0 +1,3 @@
+{
+  "component": true
+}

+ 60 - 0
component/v2/index.wxml

@@ -0,0 +1,60 @@
+<view class="flex b tb ac" wx:if="{{calendar}}">
+  <view class="calendar b tb">
+    <!-- 头部操作栏 -->
+    <view
+      wx:if="{{!config.hideHeader}}"
+      class="handle {{config.theme}}_handle-color fs28 b lr ac pc">
+      <view class="prev fs36" wx:if="{{!config.weekMode}}">
+        <text class="prev-handle iconfont icon-doubleleft" bindtap="changeDate" data-type="prev_year"></text>
+        <text class="prev-handle iconfont icon-left" bindtap="changeDate" data-type="prev_month"></text>
+      </view>
+      <view class="flex date-in-handle b lr cc" bindtap="doubleClickJumpToToday">{{calendar.curYear || "--"}} 年 {{calendar.curMonth || "--"}} 月</view>
+      <view class="next fs36" wx:if="{{!config.weekMode}}">
+        <text class="next-handle iconfont icon-right" bindtap="changeDate" data-type="next_month"></text>
+        <text class="next-handle iconfont icon-doubleright" bindtap="changeDate" data-type="next_year"></text>
+      </view>
+    </view>
+    <!-- 星期栏 -->
+    <view class="weeks b lr ac {{config.theme}}_week-color">
+      <view class="week fs28" wx:for="{{calendar.weeksCh}}" wx:key="index" data-idx="{{index}}">{{item}}</view>
+    </view>
+    <!-- 日历面板主体 -->
+    <view class="b lr wrap" bindtouchstart="calendarTouchstart" catchtouchmove="calendarTouchmove" catchtouchend="calendarTouchend">
+      <!-- 上月日期格子 -->
+      <view class="grid b ac pc {{config.theme}}_prev-month-date" wx:for="{{calendar.prevMonthGrids}}" wx:key="index" data-idx="{{index}}">
+        <view class="date-wrap b cc">
+          <view class="date">
+                {{item.date}}
+          </view>
+        </view>
+      </view>
+      <!-- 本月日期格子 -->
+      <view wx:for="{{calendar.dates}}" wx:key="index" data-idx="{{index}}" data-info="{{item}}" bindtap="tapDate" class="grid {{item.class ? item.class  : ''}} {{config.theme}}_normal-date b ac pc">
+        <view class="date-wrap b cc {{config.emphasisWeek && (item.week === 0 || item.week === 6) ? config.theme + '_weekend-color' : ''}}">
+          <view class="date b ac pc {{item.class ? item.class  : ''}} {{item.isToday && config.highlightToday ? config.theme + '_today' : ''}} {{item.choosed ? config.theme + '_choosed' : ''}} {{item.disable ? config.theme + '_date-disable' : ''}} {{config.chooseAreaMode ? 'date-area-mode' : ''}}  {{calendar.todoLabelCircle && item.showTodoLabel && !item.choosed ? config.theme + '_todo-circle todo-circle' : '' }}">
+                {{config.markToday && item.isToday ? config.markToday : item.date}}
+                <view
+                  wx:if="{{(config.showLunar && item.lunar && !item.showTodoLabel) || (item.showTodoLabel && calendar.todoLabelPos !== 'bottom') || config.showHolidays}}"
+                  class="date-desc {{config.theme}}_date-desc date-desc-bottom {{(item.choosed || item.isToday) ? 'date-desc-bottom-always' : ''}} {{item.disable ? config.theme + '_date-desc-disable' : ''}}">
+                  <text class="{{config.showHolidays && !item.showTodoLabel && item.label && !item.choosed ? config.theme + '_date-desc-lunar' : ''}} {{item.type === 'festival' ? config.theme + '_festival' : ''}}">{{item.label || item.lunar.Term || item.lunar.IDayCn}}</text>
+                </view>
+                <view
+                  wx:if="{{item.showTodoLabel && !calendar.todoLabelCircle}}"
+                  class="{{item.todoText ? 'date-desc' : config.theme + '_todo-dot todo-dot'}} {{config.showLunar ? config.theme + '_date-desc-lunar' : ''}} {{calendar.todoLabelPos === 'bottom' ? 'date-desc-bottom todo-dot-bottom' : 'date-desc-top todo-dot-top'}} {{calendar.showLabelAlways && item.choosed && calendar.todoLabelPos === 'bottom' ? 'date-desc-bottom-always todo-dot-bottom-always' : ''}} {{calendar.showLabelAlways && item.choosed && calendar.todoLabelPos === 'top' ? 'date-desc-top-always todo-dot-top-always' : ''}}"
+                  style="background-color: {{item.todoText ? '' : item.color || calendar.todoLabelColor}}; color: {{item.color}}">
+                    {{item.todoText}}
+                </view>
+          </view>
+        </view>
+      </view>
+      <!-- 下月日期格子 -->
+      <view class="grid b ac pc {{config.theme}}_next-month-date" wx:for="{{calendar.nextMonthGrids}}" wx:key="index" data-idx="{{index}}">
+        <view class="date-wrap b cc">
+          <view class="date">
+              {{item.date}}
+          </view>
+        </view>
+      </view>
+    </view>
+  </view>
+</view>

+ 215 - 0
component/v2/index.wxss

@@ -0,0 +1,215 @@
+@import './theme/iconfont.wxss';
+@import './theme/theme-default.wxss';
+@import './theme/theme-elegant.wxss';
+
+.b {
+    display: flex;
+}
+
+.lr {
+    flex-direction: row;
+}
+
+.tb {
+    flex-direction: column;
+}
+
+.pc {
+    justify-content: center;
+}
+
+.ac {
+    align-items: center;
+}
+
+.cc {
+    align-items: center;
+    justify-content: center;
+}
+
+.wrap {
+    flex-wrap: wrap;
+}
+
+.flex {
+    flex-grow: 1;
+}
+
+.bg {
+    background-image: linear-gradient(to bottom, #faefe7, #ffcbd7);
+    overflow: hidden;
+}
+
+.white-color {
+    color: #fff;
+}
+
+.fs24 {
+    font-size: 24rpx;
+}
+
+.fs28 {
+    font-size: 28rpx;
+}
+
+.fs32 {
+    font-size: 32rpx;
+}
+
+.fs36 {
+    font-size: 36rpx;
+}
+
+.calendar {
+    width: 100%;
+    box-sizing: border-box;
+}
+
+/* 日历操作栏 */
+
+.handle {
+    height: 80rpx;
+}
+
+.prev-handle,
+.next-handle {
+    padding: 20rpx;
+}
+
+.date-in-handle {
+    height: 80rpx;
+}
+
+/* 星期栏 */
+
+.weeks {
+    height: 50rpx;
+    line-height: 50rpx;
+    opacity: 0.5;
+}
+
+.week {
+    text-align: center;
+}
+
+.grid,
+.week {
+    width: 14.286014285714286%;
+}
+
+.date-wrap {
+    width: 100%;
+    height: 80rpx;
+    position: relative;
+    left: 0;
+    top: 0;
+}
+
+.date {
+    position: relative;
+    left: 0;
+    top: 0;
+    width: 55rpx;
+    height: 55rpx;
+    text-align: center;
+    line-height: 55rpx;
+    font-size: 38rpx;
+    padding: 10rpx;
+    font-weight: 200;
+    border-radius: 50%;
+    transition: all 0.3s;
+    animation-name: choosed;
+    animation-duration: 0.5s;
+    animation-timing-function: linear;
+    animation-iteration-count: 1;
+}
+
+.date-area-mode {
+    width: 100%;
+    border-radius: 0;
+}
+
+.date-desc {
+    width: 150%;
+    height: 32rpx;
+    font-size: 20rpx;
+    line-height: 32rpx;
+    position: absolute;
+    left: 50%;
+    transform: translateX(-50%);
+    overflow: hidden;
+    word-break: break-all;
+    text-overflow: ellipsis;
+    white-space: nowrap;
+    -webkit-line-clamp: 1;
+    text-align: center;
+}
+
+@keyframes choosed {
+    from {
+        transform: scale(1);
+    }
+
+    50% {
+        transform: scale(0.9);
+    }
+
+    to {
+        transform: scale(1);
+    }
+}
+
+/* 日期圆圈标记 */
+.todo-circle {
+    border-width: 1rpx;
+    border-style: solid;
+    box-sizing: border-box;
+}
+
+/* 待办点标记相关样式 */
+.todo-dot {
+    width: 10rpx;
+    height: 10rpx;
+    border-radius: 50%;
+    position: absolute;
+    left: 50%;
+    transform: translateX(-50%);
+}
+
+.todo-dot-top {
+    top: 3rpx;
+}
+
+.todo-dot.todo-dot-top-always {
+    top: -8rpx;
+}
+
+.todo-dot.todo-dot-bottom {
+    bottom: 0;
+}
+
+.todo-dot.todo-dot-bottom-always {
+    bottom: -10rpx;
+}
+
+/* 日期描述文字(待办文字/农历)相关样式 */
+
+.date-desc.date-desc-top {
+    top: -6rpx;
+}
+
+.date-desc.date-desc-top-always {
+    top: -20rpx;
+}
+
+.date-desc.date-desc-bottom {
+    bottom: -14rpx;
+}
+
+.todo-circle .date-desc.date-desc-bottom {
+    bottom: -30rpx;
+}
+
+.date-desc.date-desc-bottom-always {
+    bottom: -28rpx;
+}

+ 212 - 0
component/v2/plugins/holidays/holidays-map.js

@@ -0,0 +1,212 @@
+/* *
+  @Author: drfu*
+  @Description: 数据来源于国务院办公厅关于2020年部分节假日安排的通知(国办发明电〔2019〕16号)_政府信息公开专栏,http://www.gov.cn/zhengce/content/2019-11/21/content_5454164.htm
+  @Date: 2020-10-12 14:29:45*
+ * @Last Modified by: drfu
+ * @Last Modified time: 2020-10-16 17:38:08
+*/
+
+// 节日列表
+export const festival = {
+  // 农历固定日期节日
+  lunar: {
+    1: {
+      1: {
+        type: 'festival',
+        name: '春节',
+        label: '春节'
+      },
+      8: {
+        type: 'festival',
+        name: '腊八节',
+        label: '腊八'
+      },
+      15: {
+        type: 'festival',
+        name: '元宵节',
+        label: '元宵'
+      }
+    },
+    7: {
+      7: {
+        type: 'festival',
+        name: '七夕节',
+        label: '七夕'
+      },
+      15: {
+        type: 'festival',
+        name: '中元节',
+        label: '中元节'
+      }
+    },
+    9: {
+      9: {
+        type: 'festival',
+        name: '重阳节',
+        label: '重阳节'
+      }
+    }
+  },
+  // 阳历固定日期节日
+  solar: {
+    2: {
+      14: {
+        type: 'festival',
+        name: '情人节',
+        label: '情人节'
+      }
+    },
+    3: {
+      12: {
+        type: 'festival',
+        name: '植树节',
+        label: '植树节'
+      }
+    },
+    4: {
+      1: {
+        type: 'festival',
+        name: '愚人节',
+        label: '愚人节'
+      },
+      5: {
+        type: 'festival',
+        name: '清明节',
+        label: '清明节'
+      }
+    },
+    5: {
+      1: {
+        type: 'festival',
+        name: '劳动节',
+        label: '劳动节'
+      }
+    },
+    6: {
+      1: {
+        type: 'festival',
+        name: '儿童节',
+        label: '儿童节'
+      }
+    },
+    7: {
+      1: {
+        type: 'festival',
+        name: '建党节',
+        label: '建党节'
+      }
+    },
+    8: {
+      1: {
+        type: 'festival',
+        name: '建军节',
+        label: '建军节'
+      }
+    },
+    9: {
+      10: {
+        type: 'festival',
+        name: '教师节',
+        label: '教师节'
+      }
+    },
+    10: {
+      1: {
+        type: 'festival',
+        name: '国庆节',
+        label: '国庆节'
+      }
+    },
+    12: {
+      25: {
+        type: 'festival',
+        name: '圣诞节',
+        label: '圣诞节'
+      }
+    }
+  }
+}
+
+export const holidays = {
+  2020: {
+    1: {
+      1: {
+        type: 'holiday',
+        name: '元旦',
+        label: '休'
+      },
+      19: {
+        type: 'work',
+        name: '调班',
+        label: '班'
+      },
+      '24-30': {
+        type: 'holiday',
+        name: '春节',
+        label: '休'
+      }
+    },
+    2: {
+      1: {
+        type: 'work',
+        name: '调班',
+        label: '班'
+      }
+    },
+    4: {
+      '4-6': {
+        type: 'holiday',
+        name: '清明节',
+        label: '休'
+      },
+      26: {
+        type: 'work',
+        name: '调班',
+        label: '班'
+      }
+    },
+    5: {
+      '1-5': {
+        type: 'holiday',
+        name: '劳动节',
+        label: '休'
+      },
+      9: {
+        type: 'work',
+        name: '调班',
+        label: '班'
+      }
+    },
+    6: {
+      '25-27': {
+        type: 'holiday',
+        name: '端午节',
+        label: '休'
+      },
+      28: {
+        type: 'work',
+        name: '调班',
+        label: '班'
+      }
+    },
+    9: {
+      27: {
+        type: 'work',
+        name: '调班',
+        label: '班'
+      }
+    },
+    10: {
+      '1-8': {
+        type: 'holiday',
+        name: '国庆节/中秋节',
+        label: '休'
+      },
+      10: {
+        type: 'work',
+        name: '调班',
+        label: '班'
+      }
+    }
+  }
+}

+ 201 - 0
component/v2/plugins/holidays/index.js

@@ -0,0 +1,201 @@
+/* *
+  @Author: drfu*
+  @Description: 显示法定节假日班/休情况
+  @Date: 2020-10-12 14:29:45*
+ * @Last Modified by: drfu
+ * @Last Modified time: 2020-10-16 17:34:13
+*/
+
+import { holidays, festival } from './holidays-map'
+import { dateUtil, getCalendarData, logger } from '../../utils/index'
+
+/**
+ * 当前是否在休假期内
+ * @param {object} { year, month }
+ * @param {object} { start, end, current }
+ * @returns
+ */
+function inHolidays({ year, month }, { start, end, current }) {
+  const getTimeStamp = dateUtil.getTimeStamp
+  const startTimestamp = getTimeStamp({
+    year,
+    month,
+    date: start
+  })
+  const endTimestamp = getTimeStamp({
+    year,
+    month,
+    date: end
+  })
+  const currentDateTimestamp = getTimeStamp({
+    year,
+    month,
+    date: current
+  })
+  if (
+    currentDateTimestamp >= startTimestamp &&
+    currentDateTimestamp <= endTimestamp
+  ) {
+    return true
+  }
+  return false
+}
+
+function addSpecialFestival(date, component) {
+  const { convertlLunar2Solar, convertSolarLunar } = component.calendar || {}
+  const lunarDateInfo = convertSolarLunar(date)
+  const { lYear, lMonth } = lunarDateInfo || {}
+  // 春节
+  const info = {
+    type: 'festival',
+    name: '除夕',
+    label: '除夕'
+  }
+  if (lMonth === 12) {
+    if (!festival.lunar['12']) festival.lunar['12'] = {}
+    if (convertlLunar2Solar(`${lYear}-12-30`) === -1) {
+      festival.lunar['12']['29'] = info
+    } else {
+      festival.lunar['12']['30'] = info
+    }
+  }
+}
+
+/**
+ * 是否匹配到节日
+ * @param {object} [dateInfo={}]
+ * @param {object} [component={}]
+ * @returns {object|boolean} 匹配到的节日数据或者false
+ */
+function hasFestivalDate(dateInfo = {}, component = {}) {
+  const { month, date } = dateInfo
+  let festivalDate = festival.solar[month] && festival.solar[month][date]
+  if (!festivalDate) {
+    const { convertSolarLunar } = component.calendar || {}
+    const lunarDateInfo = convertSolarLunar(dateInfo)
+    const { lMonth, lDay } = lunarDateInfo
+    festivalDate = festival.lunar[lMonth] && festival.lunar[lMonth][lDay]
+    if (!festivalDate) {
+      const festivalOfMonth = festival.lunar[lMonth] || {}
+      const festivalDateKey = Object.keys(festivalOfMonth).find(item =>
+        item.match(new RegExp(`\\b${lDay}\\b`))
+      )
+      if (!festivalDateKey) {
+        festivalDate = false
+      } else {
+        const festivalInfo = festival.lunar[lMonth][festivalDateKey]
+        if (!festivalInfo) {
+          festivalDate = false
+        } else {
+          const { condition } = festivalInfo
+          if (typeof condition === 'function') {
+            festivalDate = condition(lunarDateInfo)
+          } else {
+            festivalDate = false
+          }
+        }
+      }
+    }
+  }
+  return festivalDate
+}
+
+export default () => {
+  return {
+    name: 'holidays',
+    beforeRender(calendarData = {}, calendarConfig = {}, component) {
+      let { dates = [] } = calendarData
+      if (calendarConfig.showHolidays || calendarConfig.showFestival) {
+        dates = dates.map(d => {
+          let item = { ...d }
+          const { year, month, date } = item
+          const holidaysOfMonth =
+            (holidays[year] && holidays[year][month]) || {}
+          const holidayDate = holidaysOfMonth[date]
+          if (holidayDate) {
+            item = {
+              ...item,
+              ...holidayDate
+            }
+          } else {
+            const holidayKeys = Object.keys(holidaysOfMonth).filter(item =>
+              item.includes('-')
+            )
+            let target = ''
+            for (let v of holidayKeys) {
+              const [start, end] = v.split('-')
+              if (+d.date >= +start && +d.date <= +end) {
+                target = v
+                break
+              }
+            }
+            const [start, end] = target.split('-')
+            const isInHolidays = inHolidays(
+              {
+                year,
+                month
+              },
+              {
+                start,
+                end,
+                current: date
+              }
+            )
+            if (isInHolidays) {
+              item = {
+                ...item,
+                ...holidaysOfMonth[target]
+              }
+            } else if (calendarConfig.showFestival) {
+              const { convertSolarLunar, convertlLunar2Solar } =
+                component.calendar || {}
+              if (
+                typeof convertSolarLunar !== 'function' ||
+                typeof convertlLunar2Solar !== 'function'
+              ) {
+                return logger.warn(
+                  '农历节日显示需要引入农历插件(/component/v2/plugins/solarLunar)'
+                )
+              }
+              addSpecialFestival(item, component)
+              const festivalDate = hasFestivalDate(item, component)
+              if (festivalDate) {
+                item = {
+                  ...item,
+                  ...festivalDate
+                }
+              }
+            }
+          }
+          return item
+        })
+      }
+      return {
+        calendarData: {
+          ...calendarData,
+          dates: dates
+        },
+        calendarConfig
+      }
+    },
+    methods(component) {
+      return {
+        getHolidaysOfCurrentYear() {
+          const calendar = getCalendarData('calendar', component)
+          const { curYear } = calendar
+          return this.methods(component).getHolidaysOfYear(curYear)
+        },
+        getHolidaysOfYear(year) {
+          if (!year) return logger.warn('getHolidaysOfCurrentYear() 入参错误')
+          if (!holidays[year]) {
+            logger.warn('未匹配到当前年份节假日信息,请自行补充')
+            return {
+              err: 'not match'
+            }
+          }
+          return holidays[year]
+        }
+      }
+    }
+  }
+}

+ 18 - 0
component/v2/plugins/index.js

@@ -0,0 +1,18 @@
+import preset from './preset/index'
+
+export default {
+  installed: [...preset],
+  use(plugin) {
+    if (typeof plugin !== 'function') return
+    const info = plugin() || {}
+    const { name } = info
+    if (
+      name &&
+      name !== 'methods' &&
+      !this.installed.some(p => p[0] === name)
+    ) {
+      this.installed.unshift([name, info])
+    }
+    return this
+  }
+}

+ 277 - 0
component/v2/plugins/preset/base.js

@@ -0,0 +1,277 @@
+/**
+ * @Author: drfu*
+ * @Description: 基础功能
+ * @Date: 2020-10-08 21:22:09*
+ * @Last Modified by: drfu
+ * @Last Modified time: 2020-10-11 13:28:52
+ * */
+
+import { calcJumpData } from '../../core'
+import { renderCalendar } from '../../render'
+import {
+  dateUtil,
+  getCalendarData,
+  setCalendarData,
+  getCalendarConfig
+} from '../../utils/index'
+
+export default () => {
+  return {
+    name: 'base',
+    beforeRender(calendarData = {}, calendarConfig) {
+      const calendar = calendarData
+      const { selectedDates = [], dates } = calendar
+      let _dates = [...dates]
+      if (selectedDates.length) {
+        const selectedDatesStr = selectedDates.map(date =>
+          dateUtil.toTimeStr(date)
+        )
+        _dates.forEach(date => {
+          const dateStr = dateUtil.toTimeStr(date)
+          if (selectedDatesStr.includes(dateStr)) {
+            date.choosed = true
+          }
+        })
+      }
+      return {
+        calendarData: {
+          ...calendarData,
+          dates: _dates
+        },
+        calendarConfig
+      }
+    },
+    onTapDate(tapedDate, calendarData = {}, calendarConfig = {}) {
+      const calendar = {
+        ...calendarData
+      }
+      const dateIndex = dateUtil.findDateIndexInArray(
+        tapedDate,
+        calendarData.dates
+      )
+      const { multi, inverse } = calendarConfig
+      let dates = [...calendar.dates]
+      const { selectedDates = [] } = calendar
+      if (!multi) {
+        let preSelectedDate = {}
+        if (selectedDates.length) {
+          preSelectedDate = [...selectedDates].pop() || {}
+        }
+        const timeStr = dateUtil.toTimeStr
+        if (!inverse && timeStr(preSelectedDate) === timeStr(tapedDate)) {
+          return calendar
+        }
+        let _tapedDate = { ...tapedDate, choosed: !tapedDate.choosed }
+
+        dates[dateIndex] = _tapedDate
+        if (preSelectedDate.date) {
+          const idx = dateUtil.findDateIndexInArray(preSelectedDate, dates)
+          const date = dates[idx]
+          if (date) {
+            date.choosed = false
+          }
+        }
+        if (dates[dateIndex].choosed) {
+          calendar.selectedDates = [dates[dateIndex]]
+        } else {
+          calendar.selectedDates = []
+        }
+      } else {
+        dates[dateIndex] = {
+          ...dates[dateIndex],
+          choosed: !dates[dateIndex].choosed
+        }
+        if (!calendar.selectedDates) {
+          calendar.selectedDates = []
+        }
+        if (dates[dateIndex].choosed) {
+          calendar.selectedDates.push(dates[dateIndex])
+        } else {
+          calendar.selectedDates = calendar.selectedDates.filter(
+            date =>
+              dateUtil.toTimeStr(date) !== dateUtil.toTimeStr(dates[dateIndex])
+          )
+        }
+      }
+      return {
+        calendarData: {
+          ...calendar,
+          dates
+        },
+        calendarConfig
+      }
+    },
+    onSwitchCalendar(date, calendarData = {}, component) {
+      const calendarConfig = getCalendarConfig(component)
+      if (calendarConfig.weekMode) {
+        return calendarData
+      }
+      const updatedRenderData = calcJumpData({
+        dateInfo: date,
+        config: calendarConfig
+      })
+      return {
+        ...calendarData,
+        ...updatedRenderData
+      }
+    },
+    methods(component) {
+      return {
+        jump: dateInfo => {
+          if (Object.prototype.toString.call(dateInfo) !== '[object Object]')
+            return
+          const updatedRenderData = calcJumpData({
+            dateInfo,
+            component
+          })
+          const existCalendarData = getCalendarData('calendar', component)
+          const config = getCalendarConfig(component)
+          if (config.autoChoosedWhenJump) {
+            const target = updatedRenderData.dates[dateInfo.date - 1]
+            if (!updatedRenderData.selectedDates) {
+              updatedRenderData.selectedDates = [target]
+            } else {
+              updatedRenderData.selectedDates.push(target)
+            }
+          }
+          return renderCalendar.call(component, {
+            ...existCalendarData,
+            ...updatedRenderData
+          })
+        },
+        getCalendarConfig() {
+          return getCalendarConfig(component)
+        },
+        setCalendarConfig(config) {
+          return new Promise((resolve, reject) => {
+            if (!component || !component.data.config) {
+              reject('异常:未找到组件配置信息')
+              return
+            }
+            let conf = { ...component.config, ...config }
+            component.config = conf
+            setCalendarData({ config: conf }, component)
+              .then(resolve)
+              .catch(reject)
+          })
+        },
+        cancelSelectedDates(cancelDates = []) {
+          const existCalendarData = getCalendarData('calendar', component) || {}
+          const { dates = [], selectedDates = [] } = existCalendarData
+          let updatedRenderData = {}
+          const config = getCalendarConfig(component)
+          let chooseAreaData = {}
+          if (config.chooseAreaMode) {
+            chooseAreaData = {
+              chooseAreaTimestamp: [],
+              tempChooseAreaTimestamp: []
+            }
+          }
+          if (!cancelDates.length) {
+            dates.forEach(item => {
+              item.choosed = false
+            })
+            updatedRenderData = {
+              dates,
+              selectedDates: []
+            }
+          } else {
+            const cancelDatesStr = cancelDates.map(date =>
+              dateUtil.toTimeStr(date)
+            )
+            const filterSelectedDates = selectedDates.filter(
+              date => !cancelDatesStr.includes(dateUtil.toTimeStr(date))
+            )
+            dates.forEach(date => {
+              if (cancelDatesStr.includes(dateUtil.toTimeStr(date))) {
+                date.choosed = false
+              }
+            })
+            updatedRenderData = {
+              dates,
+              selectedDates: filterSelectedDates
+            }
+          }
+
+          return renderCalendar.call(component, {
+            ...existCalendarData,
+            ...updatedRenderData,
+            ...chooseAreaData
+          })
+        },
+        setSelectedDates: targetDates => {
+          const existCalendarData = getCalendarData('calendar', component)
+          let { dates, selectedDates = [] } = existCalendarData || {}
+          let __selectedDates = []
+          let __dates = dates
+          if (!targetDates) {
+            __dates = dates.map(item => {
+              const date = { ...item }
+              date.choosed = true
+              if (existCalendarData.showLabelAlways && date.showTodoLabel) {
+                date.showTodoLabel = true
+              } else {
+                date.showTodoLabel = false
+              }
+              return date
+            })
+            __selectedDates = dates
+          } else if (targetDates && targetDates.length) {
+            const allSelected = dateUtil.uniqueArrayByDate(
+              selectedDates.concat(targetDates)
+            )
+            const allSelectedDateStr = allSelected.map(d =>
+              dateUtil.toTimeStr(d)
+            )
+            __dates = dates.map(item => {
+              const date = { ...item }
+              if (allSelectedDateStr.includes(dateUtil.toTimeStr(date))) {
+                date.choosed = true
+                __selectedDates.push(date)
+              }
+              if (existCalendarData.showLabelAlways && date.showTodoLabel) {
+                date.showTodoLabel = true
+              } else {
+                date.showTodoLabel = false
+              }
+              return date
+            })
+          }
+          return renderCalendar.call(component, {
+            ...existCalendarData,
+            dates: __dates,
+            selectedDates: __selectedDates
+          })
+        },
+        setDateStyle: toSetDates => {
+          if (!Array.isArray(toSetDates)) return Promise.reject()
+          const existCalendarData = getCalendarData('calendar', component)
+          const { dates = [], specialStyleDates } = existCalendarData || {}
+          if (Array.isArray(specialStyleDates)) {
+            toSetDates = dateUtil.uniqueArrayByDate([
+              ...specialStyleDates,
+              ...toSetDates
+            ])
+          }
+          const toSetDatesStr = toSetDates.map(item => dateUtil.toTimeStr(item))
+          const _dates = dates.map(item => {
+            const idx = toSetDatesStr.indexOf(dateUtil.toTimeStr(item))
+            if (idx > -1) {
+              return {
+                ...item,
+                class: toSetDates[idx].class
+              }
+            } else {
+              return item
+            }
+          })
+          return renderCalendar.call(component, {
+            ...existCalendarData,
+            dates: _dates,
+            specialStyleDates: toSetDates
+          })
+        }
+      }
+    }
+  }
+}

+ 69 - 0
component/v2/plugins/preset/get-calendar-data.js

@@ -0,0 +1,69 @@
+/**
+ * @Author: drfu*
+ * @Description: 获取日历数据
+ * @Date: 2020-10-08 21:22:09*
+ * @Last Modified by: drfu
+ * @Last Modified time: 2020-10-11 13:42:37
+ * */
+
+import { getCalendarData, logger, getCalendarConfig } from '../../utils/index'
+
+function wrapDateWithLunar(dates = [], convertFn) {
+  const datesWithLunar = JSON.parse(JSON.stringify(dates)).map(date => ({
+    ...date,
+    lunar: convertFn(date)
+  }))
+  return datesWithLunar
+}
+
+export default () => {
+  return {
+    name: 'getData',
+    methods(component) {
+      return {
+        getCurrentYM: () => {
+          const { curYear, curMonth } = getCalendarData('calendar', component)
+          return {
+            year: curYear,
+            month: curMonth
+          }
+        },
+        getSelectedDates: (options = {}) => {
+          const dates =
+            getCalendarData('calendar.selectedDates', component) || []
+          const config = getCalendarConfig(component) || {}
+          if (options.lunar && !config.showLunar) {
+            const injectedFns = component.calendar || {}
+            if (typeof injectedFns.convertSolarLunar === 'function') {
+              return wrapDateWithLunar(dates, injectedFns.convertSolarLunar)
+            } else {
+              logger.warn('获取农历信息需引入农历插件')
+            }
+          } else {
+            return dates
+          }
+        },
+        getCalendarDates: (options = {}) => {
+          const config = getCalendarConfig(component) || {}
+          const dates = getCalendarData('calendar.dates', component)
+          if (options.lunar && !config.showLunar) {
+            const injectedFns = component.calendar || {}
+            if (typeof injectedFns.convertSolarLunar === 'function') {
+              return wrapDateWithLunar(dates, injectedFns.convertSolarLunar)
+            } else {
+              logger.warn('获取农历信息需引入农历插件')
+            }
+          } else {
+            return dates
+          }
+        },
+        getCalendarAllData: () => {
+          return {
+            data: getCalendarData('calendar', component) || {},
+            config: getCalendarConfig(component) || {}
+          }
+        }
+      }
+    }
+  }
+}

+ 9 - 0
component/v2/plugins/preset/index.js

@@ -0,0 +1,9 @@
+import base from './base'
+import getCalendarData from './get-calendar-data'
+
+const preset = [
+  ['base', base()],
+  ['get-calendar-data', getCalendarData()]
+]
+
+export default preset

+ 219 - 0
component/v2/plugins/selectable.js

@@ -0,0 +1,219 @@
+/**
+ * @Author: drfu*
+ * @Description: 禁用、启用日期选择
+ * @Date: 2020-10-08 21:22:09*
+ * @Last Modified by: drfu
+ * @Last Modified time: 2020-10-08 21:25:00
+ * */
+
+import { getCalendarData, dateUtil, logger } from '../utils/index'
+import { renderCalendar } from '../render'
+
+function convertEnableAreaToTimestamp(timearea = []) {
+  const start = timearea[0].split('-')
+  const end = timearea[1].split('-')
+  if (start.length !== 3 || end.length !== 3) {
+    logger.warn('enableArea() 参数格式为: ["2018-2-1", "2018-3-1"]')
+    return {}
+  }
+  const startTimestamp = dateUtil
+    .newDate(start[0], start[1], start[2])
+    .getTime()
+  const endTimestamp = dateUtil.newDate(end[0], end[1], end[2]).getTime()
+  return {
+    start,
+    end,
+    startTimestamp,
+    endTimestamp
+  }
+}
+
+function isValiditeOfDateArea(dateArea) {
+  const {
+    start,
+    end,
+    startTimestamp,
+    endTimestamp
+  } = convertEnableAreaToTimestamp(dateArea)
+  if (!start || !end) return
+  const datesCountOfStart = dateUtil.getDatesCountOfMonth(start[0], start[1])
+  const datesCountOfEnd = dateUtil.getDatesCountOfMonth(end[0], end[1])
+  if (start[2] > datesCountOfStart || start[2] < 1) {
+    logger.warn('enableArea() 开始日期错误,指定日期不在指定月份天数范围内')
+    return false
+  } else if (start[1] > 12 || start[1] < 1) {
+    logger.warn('enableArea() 开始日期错误,月份超出1-12月份')
+    return false
+  } else if (end[2] > datesCountOfEnd || end[2] < 1) {
+    logger.warn('enableArea() 截止日期错误,指定日期不在指定月份天数范围内')
+    return false
+  } else if (end[1] > 12 || end[1] < 1) {
+    logger.warn('enableArea() 截止日期错误,月份超出1-12月份')
+    return false
+  } else if (startTimestamp > endTimestamp) {
+    logger.warn('enableArea()参数最小日期大于了最大日期')
+    return false
+  } else {
+    return true
+  }
+}
+
+function handleDisableMode(calendarConfig) {
+  const { disableMode } = calendarConfig
+  if (!disableMode) return {}
+  const disableBound =
+    dateUtil.getTimeStamp(disableMode.date) || dateUtil.todayTimestamp()
+  return {
+    disableBound,
+    disableType: disableMode.type
+  }
+}
+
+function disabledByConfig(dateInfo, currentDate, calendarConfig) {
+  const date = { ...dateInfo }
+  const { disableType, disableBound } = handleDisableMode(calendarConfig)
+  if (
+    (disableType === 'before' && disableBound && currentDate < disableBound) ||
+    (disableType === 'after' && disableBound && currentDate > disableBound)
+  ) {
+    date.disable = true
+  } else {
+    date.disable = false
+  }
+  return date
+}
+
+export default () => {
+  return {
+    name: 'enable',
+    beforeRender(calendarData = {}, calendarConfig = {}) {
+      const {
+        dates,
+        enableArea,
+        enableDates,
+        disableDates,
+        renderCausedBy
+      } = calendarData
+      const _dates = [...dates].map(date => {
+        let item = { ...date }
+        const timeStr = dateUtil.toTimeStr(date)
+        const timestamp = +dateUtil.getTimeStamp(item)
+        if (renderCausedBy === 'enableDates') {
+          if (enableDates && enableDates.length) {
+            if (enableDates.includes(timeStr)) {
+              item.disable = false
+            } else {
+              item.disable = true
+            }
+            return item
+          }
+        } else if (renderCausedBy === 'enableArea') {
+          if (enableArea && enableArea.length) {
+            const [startTimestamp, endTimestamp] = enableArea || []
+            const ifOutofArea =
+              +startTimestamp > timestamp || timestamp > +endTimestamp
+            item.disable = ifOutofArea
+            return item
+          }
+        } else if (renderCausedBy === 'disableDates') {
+          if (disableDates && disableDates.length) {
+            if (disableDates && disableDates.includes(timeStr)) {
+              item.disable = true
+            }
+            return item
+          }
+        }
+        return disabledByConfig(item, timestamp, calendarConfig)
+      })
+
+      return {
+        calendarData: {
+          ...calendarData,
+          dates: _dates
+        },
+        calendarConfig
+      }
+    },
+    methods(component) {
+      return {
+        enableArea: (dateArea = []) => {
+          if (dateArea.length === 2) {
+            const validate = isValiditeOfDateArea(dateArea)
+            if (validate) {
+              const existCalendarData = getCalendarData('calendar', component)
+              const {
+                startTimestamp,
+                endTimestamp
+              } = convertEnableAreaToTimestamp(dateArea)
+
+              return renderCalendar.call(component, {
+                ...existCalendarData,
+                renderCausedBy: 'enableArea',
+                enableArea: [startTimestamp, endTimestamp]
+              })
+            }
+          } else {
+            return Promise.inject(
+              'enableArea()参数需为时间范围数组,形如:["2018-8-4" , "2018-8-24"]'
+            )
+          }
+        },
+        enableDates: (toSet = []) => {
+          if (!toSet.length) return
+          const existCalendarData = getCalendarData('calendar', component)
+          const { enableDates = [] } = existCalendarData || {}
+          let toSetDates = toSet.map(item => {
+            if (typeof item === 'string') {
+              return dateUtil.transformDateRow2Dict(item)
+            }
+            return item
+          })
+          if (enableDates.length) {
+            toSetDates = dateUtil.uniqueArrayByDate([
+              ...toSetDates,
+              ...enableDates.map(d => dateUtil.transformDateRow2Dict(d))
+            ])
+          }
+          return renderCalendar.call(component, {
+            ...existCalendarData,
+            renderCausedBy: 'enableDates',
+            enableDates: toSetDates.map(date => {
+              if (typeof date !== 'string') {
+                return dateUtil.toTimeStr(date)
+              }
+              return date
+            })
+          })
+        },
+        disableDates: toSet => {
+          const existCalendarData = getCalendarData('calendar', component)
+          const { disableDates = [], dates = [] } = existCalendarData || {}
+          let toSetDates = toSet.map(item => {
+            let date = { ...item }
+            if (typeof date === 'string') {
+              return dateUtil.transformDateRow2Dict(item)
+            }
+            return item
+          })
+          if (disableDates && disableDates.length) {
+            toSetDates = dateUtil.uniqueArrayByDate([
+              ...toSetDates,
+              ...disableDates.map(d => dateUtil.transformDateRow2Dict(d))
+            ])
+          }
+          return renderCalendar.call(component, {
+            ...existCalendarData,
+            renderCausedBy: 'disableDates',
+            dates,
+            disableDates: toSetDates.map(date => {
+              if (typeof date !== 'string') {
+                return dateUtil.toTimeStr(date)
+              }
+              return date
+            })
+          })
+        }
+      }
+    }
+  }
+}

+ 1036 - 0
component/v2/plugins/solarLunar/convertSolarLunar.js

@@ -0,0 +1,1036 @@
+/**
+ * @1900-2100区间内的公历、农历互转
+ * @Version 1.0.3
+ * @公历转农历:calendar.solar2lunar(1987,11,01); //[you can ignore params of prefix 0]
+ * @农历转公历:calendar.lunar2solar(1987,09,10); //[you can ignore params of prefix 0]
+ */
+/* 公历年月日转农历数据 返回json */
+// calendar.solar2lunar(1987,11,01);
+/** 农历年月日转公历年月日 **/
+// calendar.lunar2solar(1987,9,10);
+// 调用以上方法后返回类似如下object(json)具体以上就不需要解释了吧!
+// c开头的是公历各属性值 l开头的自然就是农历咯 gz开头的就是天干地支纪年的数据啦~
+// {
+//  Animal: "兔",
+//  IDayCn: "初十",
+//  IMonthCn: "九月",
+//  Term: null,
+//  astro: "天蝎座",
+//  cDay: 1,
+//  cMonth: 11,
+//  cYear: 1987,
+//  gzDay: "甲寅",
+//  gzMonth: "庚戌",
+//  gzYear: "丁卯",
+//  isLeap: false,
+//  isTerm: false,
+//  isToday: false,
+//  lDay: 10,
+//  lMonth: 9,
+//  lYear: 1987,
+//  nWeek: 7,
+//  ncWeek: "星期日"
+// }
+// 该代码还有其他可以调用的方法,请自己查看代码中的详细注释
+const calendar = {
+  /**
+   * 农历1900-2100的润大小信息表
+   * @Array Of Property
+   * @return Hex
+   */
+  lunarInfo: [
+    0x04bd8,
+    0x04ae0,
+    0x0a570,
+    0x054d5,
+    0x0d260,
+    0x0d950,
+    0x16554,
+    0x056a0,
+    0x09ad0,
+    0x055d2, // 1900-1909
+    0x04ae0,
+    0x0a5b6,
+    0x0a4d0,
+    0x0d250,
+    0x1d255,
+    0x0b540,
+    0x0d6a0,
+    0x0ada2,
+    0x095b0,
+    0x14977, // 1910-1919
+    0x04970,
+    0x0a4b0,
+    0x0b4b5,
+    0x06a50,
+    0x06d40,
+    0x1ab54,
+    0x02b60,
+    0x09570,
+    0x052f2,
+    0x04970, // 1920-1929
+    0x06566,
+    0x0d4a0,
+    0x0ea50,
+    0x06e95,
+    0x05ad0,
+    0x02b60,
+    0x186e3,
+    0x092e0,
+    0x1c8d7,
+    0x0c950, // 1930-1939
+    0x0d4a0,
+    0x1d8a6,
+    0x0b550,
+    0x056a0,
+    0x1a5b4,
+    0x025d0,
+    0x092d0,
+    0x0d2b2,
+    0x0a950,
+    0x0b557, // 1940-1949
+    0x06ca0,
+    0x0b550,
+    0x15355,
+    0x04da0,
+    0x0a5b0,
+    0x14573,
+    0x052b0,
+    0x0a9a8,
+    0x0e950,
+    0x06aa0, // 1950-1959
+    0x0aea6,
+    0x0ab50,
+    0x04b60,
+    0x0aae4,
+    0x0a570,
+    0x05260,
+    0x0f263,
+    0x0d950,
+    0x05b57,
+    0x056a0, // 1960-1969
+    0x096d0,
+    0x04dd5,
+    0x04ad0,
+    0x0a4d0,
+    0x0d4d4,
+    0x0d250,
+    0x0d558,
+    0x0b540,
+    0x0b6a0,
+    0x195a6, // 1970-1979
+    0x095b0,
+    0x049b0,
+    0x0a974,
+    0x0a4b0,
+    0x0b27a,
+    0x06a50,
+    0x06d40,
+    0x0af46,
+    0x0ab60,
+    0x09570, // 1980-1989
+    0x04af5,
+    0x04970,
+    0x064b0,
+    0x074a3,
+    0x0ea50,
+    0x06b58,
+    0x055c0,
+    0x0ab60,
+    0x096d5,
+    0x092e0, // 1990-1999
+    0x0c960,
+    0x0d954,
+    0x0d4a0,
+    0x0da50,
+    0x07552,
+    0x056a0,
+    0x0abb7,
+    0x025d0,
+    0x092d0,
+    0x0cab5, // 2000-2009
+    0x0a950,
+    0x0b4a0,
+    0x0baa4,
+    0x0ad50,
+    0x055d9,
+    0x04ba0,
+    0x0a5b0,
+    0x15176,
+    0x052b0,
+    0x0a930, // 2010-2019
+    0x07954,
+    0x06aa0,
+    0x0ad50,
+    0x05b52,
+    0x04b60,
+    0x0a6e6,
+    0x0a4e0,
+    0x0d260,
+    0x0ea65,
+    0x0d530, // 2020-2029
+    0x05aa0,
+    0x076a3,
+    0x096d0,
+    0x04afb,
+    0x04ad0,
+    0x0a4d0,
+    0x1d0b6,
+    0x0d250,
+    0x0d520,
+    0x0dd45, // 2030-2039
+    0x0b5a0,
+    0x056d0,
+    0x055b2,
+    0x049b0,
+    0x0a577,
+    0x0a4b0,
+    0x0aa50,
+    0x1b255,
+    0x06d20,
+    0x0ada0, // 2040-2049
+    /** Add By JJonline@JJonline.Cn **/
+    0x14b63,
+    0x09370,
+    0x049f8,
+    0x04970,
+    0x064b0,
+    0x168a6,
+    0x0ea50,
+    0x06b20,
+    0x1a6c4,
+    0x0aae0, // 2050-2059
+    0x0a2e0,
+    0x0d2e3,
+    0x0c960,
+    0x0d557,
+    0x0d4a0,
+    0x0da50,
+    0x05d55,
+    0x056a0,
+    0x0a6d0,
+    0x055d4, // 2060-2069
+    0x052d0,
+    0x0a9b8,
+    0x0a950,
+    0x0b4a0,
+    0x0b6a6,
+    0x0ad50,
+    0x055a0,
+    0x0aba4,
+    0x0a5b0,
+    0x052b0, // 2070-2079
+    0x0b273,
+    0x06930,
+    0x07337,
+    0x06aa0,
+    0x0ad50,
+    0x14b55,
+    0x04b60,
+    0x0a570,
+    0x054e4,
+    0x0d160, // 2080-2089
+    0x0e968,
+    0x0d520,
+    0x0daa0,
+    0x16aa6,
+    0x056d0,
+    0x04ae0,
+    0x0a9d4,
+    0x0a2d0,
+    0x0d150,
+    0x0f252, // 2090-2099
+    0x0d520
+  ], // 2100
+
+  /**
+   * 公历每个月份的天数普通表
+   * @Array Of Property
+   * @return Number
+   */
+  solarMonth: [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31],
+
+  /**
+   * 天干地支之天干速查表
+   * @Array Of Property trans["甲","乙","丙","丁","戊","己","庚","辛","壬","癸"]
+   * @return Cn string
+   */
+  Gan: [
+    '\u7532',
+    '\u4e59',
+    '\u4e19',
+    '\u4e01',
+    '\u620a',
+    '\u5df1',
+    '\u5e9a',
+    '\u8f9b',
+    '\u58ec',
+    '\u7678'
+  ],
+
+  /**
+   * 天干地支之地支速查表
+   * @Array Of Property
+   * @trans["子","丑","寅","卯","辰","巳","午","未","申","酉","戌","亥"]
+   * @return Cn string
+   */
+  Zhi: [
+    '\u5b50',
+    '\u4e11',
+    '\u5bc5',
+    '\u536f',
+    '\u8fb0',
+    '\u5df3',
+    '\u5348',
+    '\u672a',
+    '\u7533',
+    '\u9149',
+    '\u620c',
+    '\u4ea5'
+  ],
+
+  /**
+   * 天干地支之地支速查表<=>生肖
+   * @Array Of Property
+   * @trans["鼠","牛","虎","兔","龙","蛇","马","羊","猴","鸡","狗","猪"]
+   * @return Cn string
+   */
+  Animals: [
+    '\u9f20',
+    '\u725b',
+    '\u864e',
+    '\u5154',
+    '\u9f99',
+    '\u86c7',
+    '\u9a6c',
+    '\u7f8a',
+    '\u7334',
+    '\u9e21',
+    '\u72d7',
+    '\u732a'
+  ],
+
+  /**
+   * 24节气速查表
+   * @Array Of Property
+   * @trans["小寒","大寒","立春","雨水","惊蛰","春分","清明","谷雨","立夏","小满","芒种","夏至","小暑","大暑","立秋","处暑","白露","秋分","寒露","霜降","立冬","小雪","大雪","冬至"]
+   * @return Cn string
+   */
+  solarTerm: [
+    '\u5c0f\u5bd2',
+    '\u5927\u5bd2',
+    '\u7acb\u6625',
+    '\u96e8\u6c34',
+    '\u60ca\u86f0',
+    '\u6625\u5206',
+    '\u6e05\u660e',
+    '\u8c37\u96e8',
+    '\u7acb\u590f',
+    '\u5c0f\u6ee1',
+    '\u8292\u79cd',
+    '\u590f\u81f3',
+    '\u5c0f\u6691',
+    '\u5927\u6691',
+    '\u7acb\u79cb',
+    '\u5904\u6691',
+    '\u767d\u9732',
+    '\u79cb\u5206',
+    '\u5bd2\u9732',
+    '\u971c\u964d',
+    '\u7acb\u51ac',
+    '\u5c0f\u96ea',
+    '\u5927\u96ea',
+    '\u51ac\u81f3'
+  ],
+
+  /**
+   * 1900-2100各年的24节气日期速查表
+   * @Array Of Property
+   * @return 0x string For splice
+   */
+  sTermInfo: [
+    '9778397bd097c36b0b6fc9274c91aa',
+    '97b6b97bd19801ec9210c965cc920e',
+    '97bcf97c3598082c95f8c965cc920f',
+    '97bd0b06bdb0722c965ce1cfcc920f',
+    'b027097bd097c36b0b6fc9274c91aa',
+    '97b6b97bd19801ec9210c965cc920e',
+    '97bcf97c359801ec95f8c965cc920f',
+    '97bd0b06bdb0722c965ce1cfcc920f',
+    'b027097bd097c36b0b6fc9274c91aa',
+    '97b6b97bd19801ec9210c965cc920e',
+    '97bcf97c359801ec95f8c965cc920f',
+    '97bd0b06bdb0722c965ce1cfcc920f',
+    'b027097bd097c36b0b6fc9274c91aa',
+    '9778397bd19801ec9210c965cc920e',
+    '97b6b97bd19801ec95f8c965cc920f',
+    '97bd09801d98082c95f8e1cfcc920f',
+    '97bd097bd097c36b0b6fc9210c8dc2',
+    '9778397bd197c36c9210c9274c91aa',
+    '97b6b97bd19801ec95f8c965cc920e',
+    '97bd09801d98082c95f8e1cfcc920f',
+    '97bd097bd097c36b0b6fc9210c8dc2',
+    '9778397bd097c36c9210c9274c91aa',
+    '97b6b97bd19801ec95f8c965cc920e',
+    '97bcf97c3598082c95f8e1cfcc920f',
+    '97bd097bd097c36b0b6fc9210c8dc2',
+    '9778397bd097c36c9210c9274c91aa',
+    '97b6b97bd19801ec9210c965cc920e',
+    '97bcf97c3598082c95f8c965cc920f',
+    '97bd097bd097c35b0b6fc920fb0722',
+    '9778397bd097c36b0b6fc9274c91aa',
+    '97b6b97bd19801ec9210c965cc920e',
+    '97bcf97c3598082c95f8c965cc920f',
+    '97bd097bd097c35b0b6fc920fb0722',
+    '9778397bd097c36b0b6fc9274c91aa',
+    '97b6b97bd19801ec9210c965cc920e',
+    '97bcf97c359801ec95f8c965cc920f',
+    '97bd097bd097c35b0b6fc920fb0722',
+    '9778397bd097c36b0b6fc9274c91aa',
+    '97b6b97bd19801ec9210c965cc920e',
+    '97bcf97c359801ec95f8c965cc920f',
+    '97bd097bd097c35b0b6fc920fb0722',
+    '9778397bd097c36b0b6fc9274c91aa',
+    '97b6b97bd19801ec9210c965cc920e',
+    '97bcf97c359801ec95f8c965cc920f',
+    '97bd097bd07f595b0b6fc920fb0722',
+    '9778397bd097c36b0b6fc9210c8dc2',
+    '9778397bd19801ec9210c9274c920e',
+    '97b6b97bd19801ec95f8c965cc920f',
+    '97bd07f5307f595b0b0bc920fb0722',
+    '7f0e397bd097c36b0b6fc9210c8dc2',
+    '9778397bd097c36c9210c9274c920e',
+    '97b6b97bd19801ec95f8c965cc920f',
+    '97bd07f5307f595b0b0bc920fb0722',
+    '7f0e397bd097c36b0b6fc9210c8dc2',
+    '9778397bd097c36c9210c9274c91aa',
+    '97b6b97bd19801ec9210c965cc920e',
+    '97bd07f1487f595b0b0bc920fb0722',
+    '7f0e397bd097c36b0b6fc9210c8dc2',
+    '9778397bd097c36b0b6fc9274c91aa',
+    '97b6b97bd19801ec9210c965cc920e',
+    '97bcf7f1487f595b0b0bb0b6fb0722',
+    '7f0e397bd097c35b0b6fc920fb0722',
+    '9778397bd097c36b0b6fc9274c91aa',
+    '97b6b97bd19801ec9210c965cc920e',
+    '97bcf7f1487f595b0b0bb0b6fb0722',
+    '7f0e397bd097c35b0b6fc920fb0722',
+    '9778397bd097c36b0b6fc9274c91aa',
+    '97b6b97bd19801ec9210c965cc920e',
+    '97bcf7f1487f531b0b0bb0b6fb0722',
+    '7f0e397bd097c35b0b6fc920fb0722',
+    '9778397bd097c36b0b6fc9274c91aa',
+    '97b6b97bd19801ec9210c965cc920e',
+    '97bcf7f1487f531b0b0bb0b6fb0722',
+    '7f0e397bd07f595b0b6fc920fb0722',
+    '9778397bd097c36b0b6fc9274c91aa',
+    '97b6b97bd19801ec9210c9274c920e',
+    '97bcf7f0e47f531b0b0bb0b6fb0722',
+    '7f0e397bd07f595b0b0bc920fb0722',
+    '9778397bd097c36b0b6fc9210c91aa',
+    '97b6b97bd197c36c9210c9274c920e',
+    '97bcf7f0e47f531b0b0bb0b6fb0722',
+    '7f0e397bd07f595b0b0bc920fb0722',
+    '9778397bd097c36b0b6fc9210c8dc2',
+    '9778397bd097c36c9210c9274c920e',
+    '97b6b7f0e47f531b0723b0b6fb0722',
+    '7f0e37f5307f595b0b0bc920fb0722',
+    '7f0e397bd097c36b0b6fc9210c8dc2',
+    '9778397bd097c36b0b70c9274c91aa',
+    '97b6b7f0e47f531b0723b0b6fb0721',
+    '7f0e37f1487f595b0b0bb0b6fb0722',
+    '7f0e397bd097c35b0b6fc9210c8dc2',
+    '9778397bd097c36b0b6fc9274c91aa',
+    '97b6b7f0e47f531b0723b0b6fb0721',
+    '7f0e27f1487f595b0b0bb0b6fb0722',
+    '7f0e397bd097c35b0b6fc920fb0722',
+    '9778397bd097c36b0b6fc9274c91aa',
+    '97b6b7f0e47f531b0723b0b6fb0721',
+    '7f0e27f1487f531b0b0bb0b6fb0722',
+    '7f0e397bd097c35b0b6fc920fb0722',
+    '9778397bd097c36b0b6fc9274c91aa',
+    '97b6b7f0e47f531b0723b0b6fb0721',
+    '7f0e27f1487f531b0b0bb0b6fb0722',
+    '7f0e397bd097c35b0b6fc920fb0722',
+    '9778397bd097c36b0b6fc9274c91aa',
+    '97b6b7f0e47f531b0723b0b6fb0721',
+    '7f0e27f1487f531b0b0bb0b6fb0722',
+    '7f0e397bd07f595b0b0bc920fb0722',
+    '9778397bd097c36b0b6fc9274c91aa',
+    '97b6b7f0e47f531b0723b0787b0721',
+    '7f0e27f0e47f531b0b0bb0b6fb0722',
+    '7f0e397bd07f595b0b0bc920fb0722',
+    '9778397bd097c36b0b6fc9210c91aa',
+    '97b6b7f0e47f149b0723b0787b0721',
+    '7f0e27f0e47f531b0723b0b6fb0722',
+    '7f0e397bd07f595b0b0bc920fb0722',
+    '9778397bd097c36b0b6fc9210c8dc2',
+    '977837f0e37f149b0723b0787b0721',
+    '7f07e7f0e47f531b0723b0b6fb0722',
+    '7f0e37f5307f595b0b0bc920fb0722',
+    '7f0e397bd097c35b0b6fc9210c8dc2',
+    '977837f0e37f14998082b0787b0721',
+    '7f07e7f0e47f531b0723b0b6fb0721',
+    '7f0e37f1487f595b0b0bb0b6fb0722',
+    '7f0e397bd097c35b0b6fc9210c8dc2',
+    '977837f0e37f14998082b0787b06bd',
+    '7f07e7f0e47f531b0723b0b6fb0721',
+    '7f0e27f1487f531b0b0bb0b6fb0722',
+    '7f0e397bd097c35b0b6fc920fb0722',
+    '977837f0e37f14998082b0787b06bd',
+    '7f07e7f0e47f531b0723b0b6fb0721',
+    '7f0e27f1487f531b0b0bb0b6fb0722',
+    '7f0e397bd097c35b0b6fc920fb0722',
+    '977837f0e37f14998082b0787b06bd',
+    '7f07e7f0e47f531b0723b0b6fb0721',
+    '7f0e27f1487f531b0b0bb0b6fb0722',
+    '7f0e397bd07f595b0b0bc920fb0722',
+    '977837f0e37f14998082b0787b06bd',
+    '7f07e7f0e47f531b0723b0b6fb0721',
+    '7f0e27f1487f531b0b0bb0b6fb0722',
+    '7f0e397bd07f595b0b0bc920fb0722',
+    '977837f0e37f14998082b0787b06bd',
+    '7f07e7f0e47f149b0723b0787b0721',
+    '7f0e27f0e47f531b0b0bb0b6fb0722',
+    '7f0e397bd07f595b0b0bc920fb0722',
+    '977837f0e37f14998082b0723b06bd',
+    '7f07e7f0e37f149b0723b0787b0721',
+    '7f0e27f0e47f531b0723b0b6fb0722',
+    '7f0e397bd07f595b0b0bc920fb0722',
+    '977837f0e37f14898082b0723b02d5',
+    '7ec967f0e37f14998082b0787b0721',
+    '7f07e7f0e47f531b0723b0b6fb0722',
+    '7f0e37f1487f595b0b0bb0b6fb0722',
+    '7f0e37f0e37f14898082b0723b02d5',
+    '7ec967f0e37f14998082b0787b0721',
+    '7f07e7f0e47f531b0723b0b6fb0722',
+    '7f0e37f1487f531b0b0bb0b6fb0722',
+    '7f0e37f0e37f14898082b0723b02d5',
+    '7ec967f0e37f14998082b0787b06bd',
+    '7f07e7f0e47f531b0723b0b6fb0721',
+    '7f0e37f1487f531b0b0bb0b6fb0722',
+    '7f0e37f0e37f14898082b072297c35',
+    '7ec967f0e37f14998082b0787b06bd',
+    '7f07e7f0e47f531b0723b0b6fb0721',
+    '7f0e27f1487f531b0b0bb0b6fb0722',
+    '7f0e37f0e37f14898082b072297c35',
+    '7ec967f0e37f14998082b0787b06bd',
+    '7f07e7f0e47f531b0723b0b6fb0721',
+    '7f0e27f1487f531b0b0bb0b6fb0722',
+    '7f0e37f0e366aa89801eb072297c35',
+    '7ec967f0e37f14998082b0787b06bd',
+    '7f07e7f0e47f149b0723b0787b0721',
+    '7f0e27f1487f531b0b0bb0b6fb0722',
+    '7f0e37f0e366aa89801eb072297c35',
+    '7ec967f0e37f14998082b0723b06bd',
+    '7f07e7f0e47f149b0723b0787b0721',
+    '7f0e27f0e47f531b0723b0b6fb0722',
+    '7f0e37f0e366aa89801eb072297c35',
+    '7ec967f0e37f14998082b0723b06bd',
+    '7f07e7f0e37f14998083b0787b0721',
+    '7f0e27f0e47f531b0723b0b6fb0722',
+    '7f0e37f0e366aa89801eb072297c35',
+    '7ec967f0e37f14898082b0723b02d5',
+    '7f07e7f0e37f14998082b0787b0721',
+    '7f07e7f0e47f531b0723b0b6fb0722',
+    '7f0e36665b66aa89801e9808297c35',
+    '665f67f0e37f14898082b0723b02d5',
+    '7ec967f0e37f14998082b0787b0721',
+    '7f07e7f0e47f531b0723b0b6fb0722',
+    '7f0e36665b66a449801e9808297c35',
+    '665f67f0e37f14898082b0723b02d5',
+    '7ec967f0e37f14998082b0787b06bd',
+    '7f07e7f0e47f531b0723b0b6fb0721',
+    '7f0e36665b66a449801e9808297c35',
+    '665f67f0e37f14898082b072297c35',
+    '7ec967f0e37f14998082b0787b06bd',
+    '7f07e7f0e47f531b0723b0b6fb0721',
+    '7f0e26665b66a449801e9808297c35',
+    '665f67f0e37f1489801eb072297c35',
+    '7ec967f0e37f14998082b0787b06bd',
+    '7f07e7f0e47f531b0723b0b6fb0721',
+    '7f0e27f1487f531b0b0bb0b6fb0722'
+  ],
+
+  /**
+   * 数字转中文速查表
+   * @Array Of Property
+   * @trans ['日','一','二','三','四','五','六','七','八','九','十']
+   * @return Cn string
+   */
+  nStr1: [
+    '\u65e5',
+    '\u4e00',
+    '\u4e8c',
+    '\u4e09',
+    '\u56db',
+    '\u4e94',
+    '\u516d',
+    '\u4e03',
+    '\u516b',
+    '\u4e5d',
+    '\u5341'
+  ],
+
+  /**
+   * 日期转农历称呼速查表
+   * @Array Of Property
+   * @trans ['初','十','廿','卅']
+   * @return Cn string
+   */
+  nStr2: ['\u521d', '\u5341', '\u5eff', '\u5345'],
+
+  /**
+   * 月份转农历称呼速查表
+   * @Array Of Property
+   * @trans ['正','一','二','三','四','五','六','七','八','九','十','冬','腊']
+   * @return Cn string
+   */
+  nStr3: [
+    '\u6b63',
+    '\u4e8c',
+    '\u4e09',
+    '\u56db',
+    '\u4e94',
+    '\u516d',
+    '\u4e03',
+    '\u516b',
+    '\u4e5d',
+    '\u5341',
+    '\u51ac',
+    '\u814a'
+  ],
+
+  /**
+   * 返回农历y年一整年的总天数
+   * @param lunar Year
+   * @return Number
+   * @eg:var count = calendar.lYearDays(1987) ;//count=387
+   */
+  lYearDays: function(y) {
+    let i
+    let sum = 348
+    for (i = 0x8000; i > 0x8; i >>= 1) {
+      sum += calendar.lunarInfo[y - 1900] & i ? 1 : 0
+    }
+    return sum + calendar.leapDays(y)
+  },
+
+  /**
+   * 返回农历y年闰月是哪个月;若y年没有闰月 则返回0
+   * @param lunar Year
+   * @return Number (0-12)
+   * @eg:var leapMonth = calendar.leapMonth(1987) ;//leapMonth=6
+   */
+  leapMonth: function(y) {
+    // 闰字编码 \u95f0
+    return calendar.lunarInfo[y - 1900] & 0xf
+  },
+
+  /**
+   * 返回农历y年闰月的天数 若该年没有闰月则返回0
+   * @param lunar Year
+   * @return Number (0、29、30)
+   * @eg:var leapMonthDay = calendar.leapDays(1987) ;//leapMonthDay=29
+   */
+  leapDays: function(y) {
+    if (calendar.leapMonth(y)) {
+      return calendar.lunarInfo[y - 1900] & 0x10000 ? 30 : 29
+    }
+    return 0
+  },
+
+  /**
+   * 返回农历y年m月(非闰月)的总天数,计算m为闰月时的天数请使用leapDays方法
+   * @param lunar Year
+   * @return Number (-1、29、30)
+   * @eg:var MonthDay = calendar.monthDays(1987,9) ;//MonthDay=29
+   */
+  monthDays: function(y, m) {
+    if (m > 12 || m < 1) return -1 // 月份参数从1至12,参数错误返回-1
+    return calendar.lunarInfo[y - 1900] & (0x10000 >> m) ? 30 : 29
+  },
+
+  /**
+   * 返回公历(!)y年m月的天数
+   * @param solar Year
+   * @return Number (-1、28、29、30、31)
+   * @eg:var solarMonthDay = calendar.leapDays(1987) ;//solarMonthDay=30
+   */
+  solarDays: function(y, m) {
+    if (m > 12 || m < 1) return -1 // 若参数错误 返回-1
+    const ms = m - 1
+    if (+ms === 1) {
+      // 2月份的闰平规律测算后确认返回28或29
+      return (y % 4 === 0 && y % 100 !== 0) || y % 400 === 0 ? 29 : 28
+    } else {
+      return calendar.solarMonth[ms]
+    }
+  },
+
+  /**
+   * 农历年份转换为干支纪年
+   * @param  lYear 农历年的年份数
+   * @return Cn string
+   */
+  toGanZhiYear: function(lYear) {
+    let ganKey = (lYear - 3) % 10
+    let zhiKey = (lYear - 3) % 12
+    if (+ganKey === 0) ganKey = 10 // 如果余数为0则为最后一个天干
+    if (+zhiKey === 0) zhiKey = 12 // 如果余数为0则为最后一个地支
+    return calendar.Gan[ganKey - 1] + calendar.Zhi[zhiKey - 1]
+  },
+
+  /**
+   * 公历月、日判断所属星座
+   * @param  cMonth [description]
+   * @param  cDay [description]
+   * @return Cn string
+   */
+  toAstro: function(cMonth, cDay) {
+    const s =
+      '\u9b54\u7faf\u6c34\u74f6\u53cc\u9c7c\u767d\u7f8a\u91d1\u725b\u53cc\u5b50\u5de8\u87f9\u72ee\u5b50\u5904\u5973\u5929\u79e4\u5929\u874e\u5c04\u624b\u9b54\u7faf'
+    const arr = [20, 19, 21, 21, 21, 22, 23, 23, 23, 23, 22, 22]
+    return s.substr(cMonth * 2 - (cDay < arr[cMonth - 1] ? 2 : 0), 2) + '\u5ea7' // 座
+  },
+
+  /**
+   * 传入offset偏移量返回干支
+   * @param offset 相对甲子的偏移量
+   * @return Cn string
+   */
+  toGanZhi: function(offset) {
+    return calendar.Gan[offset % 10] + calendar.Zhi[offset % 12]
+  },
+
+  /**
+   * 传入公历(!)y年获得该年第n个节气的公历日期
+   * @param y公历年(1900-2100);n二十四节气中的第几个节气(1~24);从n=1(小寒)算起
+   * @return day Number
+   * @eg:var _24 = calendar.getTerm(1987,3) ;//_24=4;意即1987年2月4日立春
+   */
+  getTerm: function(y, n) {
+    if (y < 1900 || y > 2100) return -1
+    if (n < 1 || n > 24) return -1
+    const _table = calendar.sTermInfo[y - 1900]
+    const _info = [
+      parseInt('0x' + _table.substr(0, 5)).toString(),
+      parseInt('0x' + _table.substr(5, 5)).toString(),
+      parseInt('0x' + _table.substr(10, 5)).toString(),
+      parseInt('0x' + _table.substr(15, 5)).toString(),
+      parseInt('0x' + _table.substr(20, 5)).toString(),
+      parseInt('0x' + _table.substr(25, 5)).toString()
+    ]
+    const _calday = [
+      _info[0].substr(0, 1),
+      _info[0].substr(1, 2),
+      _info[0].substr(3, 1),
+      _info[0].substr(4, 2),
+
+      _info[1].substr(0, 1),
+      _info[1].substr(1, 2),
+      _info[1].substr(3, 1),
+      _info[1].substr(4, 2),
+
+      _info[2].substr(0, 1),
+      _info[2].substr(1, 2),
+      _info[2].substr(3, 1),
+      _info[2].substr(4, 2),
+
+      _info[3].substr(0, 1),
+      _info[3].substr(1, 2),
+      _info[3].substr(3, 1),
+      _info[3].substr(4, 2),
+
+      _info[4].substr(0, 1),
+      _info[4].substr(1, 2),
+      _info[4].substr(3, 1),
+      _info[4].substr(4, 2),
+
+      _info[5].substr(0, 1),
+      _info[5].substr(1, 2),
+      _info[5].substr(3, 1),
+      _info[5].substr(4, 2)
+    ]
+    return parseInt(_calday[n - 1])
+  },
+
+  /**
+   * 传入农历数字月份返回汉语通俗表示法
+   * @param lunar month
+   * @return Cn string
+   * @eg:var cnMonth = calendar.toChinaMonth(12) ;//cnMonth='腊月'
+   */
+  toChinaMonth: function(m) {
+    // 月 => \u6708
+    if (m > 12 || m < 1) return -1 // 若参数错误 返回-1
+    let s = calendar.nStr3[m - 1]
+    s += '\u6708' // 加上月字
+    return s
+  },
+
+  /**
+   * 传入农历日期数字返回汉字表示法
+   * @param lunar day
+   * @return Cn string
+   * @eg:var cnDay = calendar.toChinaDay(21) ;//cnMonth='廿一'
+   */
+  toChinaDay: function(d) {
+    // 日 => \u65e5
+    let s
+    switch (d) {
+      case 10:
+        s = '\u521d\u5341'
+        break
+      case 20:
+        s = '\u4e8c\u5341'
+        break
+      case 30:
+        s = '\u4e09\u5341'
+        break
+      default:
+        s = calendar.nStr2[Math.floor(d / 10)]
+        s += calendar.nStr1[d % 10]
+    }
+    return s
+  },
+
+  /**
+   * 年份转生肖[!仅能大致转换] => 精确划分生肖分界线是“立春”
+   * @param y year
+   * @return Cn string
+   * @eg:var animal = calendar.getAnimal(1987) ;//animal='兔'
+   */
+  getAnimal: function(y) {
+    return calendar.Animals[(y - 4) % 12]
+  },
+
+  /**
+   * 传入阳历年月日获得详细的公历、农历object信息 <=>JSON
+   * @param y  solar year
+   * @param m  solar month
+   * @param d  solar day
+   * @return JSON object
+   * @eg:console.log(calendar.solar2lunar(1987,11,01));
+   */
+  solar2lunar: function(y, m, d) {
+    // 参数区间1900.1.31~2100.12.31
+    // 年份限定、上限
+    if (y < 1900 || y > 2100) {
+      return -1 // undefined转换为数字变为NaN
+    }
+    // 公历传参最下限
+    if (+y === 1900 && +m === 1 && +d < 31) {
+      return -1
+    }
+    // 未传参 获得当天
+    let objDate
+    if (!y) {
+      objDate = new Date()
+    } else {
+      objDate = new Date(y, parseInt(m) - 1, d)
+    }
+    let i
+    let leap = 0
+    let temp = 0
+    // 修正ymd参数
+    y = objDate.getFullYear()
+    m = objDate.getMonth() + 1
+    d = objDate.getDate()
+    let offset =
+      (Date.UTC(objDate.getFullYear(), objDate.getMonth(), objDate.getDate()) -
+        Date.UTC(1900, 0, 31)) /
+      86400000
+    for (i = 1900; i < 2101 && offset > 0; i++) {
+      temp = calendar.lYearDays(i)
+      offset -= temp
+    }
+    if (offset < 0) {
+      offset += temp
+      i--
+    }
+
+    // 是否今天
+    const isTodayObj = new Date()
+    let isToday = false
+    if (
+      isTodayObj.getFullYear() === +y &&
+      isTodayObj.getMonth() + 1 === +m &&
+      isTodayObj.getDate() === +d
+    ) {
+      isToday = true
+    }
+    // 星期几
+    let nWeek = objDate.getDay()
+    const cWeek = calendar.nStr1[nWeek]
+    // 数字表示周几顺应天朝周一开始的惯例
+    if (+nWeek === 0) {
+      nWeek = 7
+    }
+    // 农历年
+    const year = i
+    leap = calendar.leapMonth(i) // 闰哪个月
+    let isLeap = false
+
+    // 效验闰月
+    for (i = 1; i < 13 && offset > 0; i++) {
+      // 闰月
+      if (leap > 0 && i === leap + 1 && isLeap === false) {
+        --i
+        isLeap = true
+        temp = calendar.leapDays(year) // 计算农历闰月天数
+      } else {
+        temp = calendar.monthDays(year, i) // 计算农历普通月天数
+      }
+      // 解除闰月
+      if (isLeap === true && i === leap + 1) isLeap = false
+      offset -= temp
+    }
+    // 闰月导致数组下标重叠取反
+    if (offset === 0 && leap > 0 && i === leap + 1) {
+      if (isLeap) {
+        isLeap = false
+      } else {
+        isLeap = true
+        --i
+      }
+    }
+    if (offset < 0) {
+      offset += temp
+      --i
+    }
+    // 农历月
+    const month = i
+    // 农历日
+    const day = offset + 1
+    // 天干地支处理
+    const sm = m - 1
+    const gzY = calendar.toGanZhiYear(year)
+
+    // 当月的两个节气
+    // bugfix-2017-7-24 11:03:38 use lunar Year Param `y` Not `year`
+    const firstNode = calendar.getTerm(y, m * 2 - 1) // 返回当月「节」为几日开始
+    const secondNode = calendar.getTerm(y, m * 2) // 返回当月「节」为几日开始
+
+    // 依据12节气修正干支月
+    let gzM = calendar.toGanZhi((y - 1900) * 12 + m + 11)
+    if (d >= firstNode) {
+      gzM = calendar.toGanZhi((y - 1900) * 12 + m + 12)
+    }
+
+    // 传入的日期的节气与否
+    let isTerm = false
+    let Term = null
+    if (+firstNode === d) {
+      isTerm = true
+      Term = calendar.solarTerm[m * 2 - 2]
+    }
+    if (+secondNode === d) {
+      isTerm = true
+      Term = calendar.solarTerm[m * 2 - 1]
+    }
+    // 日柱 当月一日与 1900/1/1 相差天数
+    const dayCyclical = Date.UTC(y, sm, 1, 0, 0, 0, 0) / 86400000 + 25567 + 10
+    const gzD = calendar.toGanZhi(dayCyclical + d - 1)
+    // 该日期所属的星座
+    const astro = calendar.toAstro(m, d)
+
+    return {
+      lYear: year,
+      lMonth: month,
+      lDay: day,
+      Animal: calendar.getAnimal(year),
+      IMonthCn: (isLeap ? '\u95f0' : '') + calendar.toChinaMonth(month),
+      IDayCn: calendar.toChinaDay(day),
+      cYear: y,
+      cMonth: m,
+      cDay: d,
+      gzYear: gzY,
+      gzMonth: gzM,
+      gzDay: gzD,
+      isToday: isToday,
+      isLeap: isLeap,
+      nWeek: nWeek,
+      ncWeek: '\u661f\u671f' + cWeek,
+      isTerm: isTerm,
+      Term: Term,
+      astro: astro
+    }
+  },
+
+  /**
+   * 传入农历年月日以及传入的月份是否闰月获得详细的公历、农历object信息 <=>JSON
+   * @param y  lunar year
+   * @param m  lunar month
+   * @param d  lunar day
+   * @param isLeapMonth  lunar month is leap or not.[如果是农历闰月第四个参数赋值true即可]
+   * @return JSON object
+   * @eg:console.log(calendar.lunar2solar(1987,9,10));
+   */
+  lunar2solar: function(y, m, d, isLeapMonth) {
+    // 参数区间1900.1.31~2100.12.1
+    isLeapMonth = !!isLeapMonth
+    // let leapOffset = 0;
+    const leapMonth = calendar.leapMonth(y)
+    // let leapDay = calendar.leapDays(y);
+    if (isLeapMonth && leapMonth !== m) return -1 // 传参要求计算该闰月公历 但该年得出的闰月与传参的月份并不同
+    if (
+      (+y === 2100 && +m === 12 && +d > 1) ||
+      (+y === 1900 && +m === 1 && +d < 31)
+    )
+      return -1 // 超出了最大极限值
+    const day = calendar.monthDays(y, m)
+    let _day = day
+    // bugFix 2016-9-25
+    // if month is leap, _day use leapDays method
+    if (isLeapMonth) {
+      _day = calendar.leapDays(y, m)
+    }
+    if (y < 1900 || y > 2100 || d > _day) return -1 // 参数合法性效验
+
+    // 计算农历的时间差
+    let offset = 0
+    for (let i = 1900; i < y; i++) {
+      offset += calendar.lYearDays(i)
+    }
+    let leap = 0
+    let isAdd = false
+    for (let i = 1; i < m; i++) {
+      leap = calendar.leapMonth(y)
+      if (!isAdd) {
+        // 处理闰月
+        if (leap <= i && leap > 0) {
+          offset += calendar.leapDays(y)
+          isAdd = true
+        }
+      }
+      offset += calendar.monthDays(y, i)
+    }
+    // 转换闰月农历 需补充该年闰月的前一个月的时差
+    if (isLeapMonth) offset += day
+    // 1900年农历正月一日的公历时间为1900年1月30日0时0分0秒(该时间也是本农历的最开始起始点)
+    const stmap = Date.UTC(1900, 1, 30, 0, 0, 0)
+    const calObj = new Date((offset + d - 31) * 86400000 + stmap)
+    const cY = calObj.getUTCFullYear()
+    const cM = calObj.getUTCMonth() + 1
+    const cD = calObj.getUTCDate()
+
+    return calendar.solar2lunar(cY, cM, cD)
+  }
+}
+
+const {
+  Gan,
+  Zhi,
+  nStr1,
+  nStr2,
+  nStr3,
+  Animals,
+  solarTerm,
+  lunarInfo,
+  sTermInfo,
+  solarMonth,
+  ...rest
+} = calendar
+
+export default rest

+ 59 - 0
component/v2/plugins/solarLunar/index.js

@@ -0,0 +1,59 @@
+import { dateUtil } from '../../utils/index'
+import convertSolarLunar from './convertSolarLunar'
+
+function getDateRow2Dict(dateInfo) {
+  if (!dateInfo) return dateInfo
+  if (typeof dateInfo === 'string' && dateInfo.includes('-')) {
+    dateInfo = dateUtil.transformDateRow2Dict(dateInfo)
+  }
+  return dateInfo
+}
+
+export default () => {
+  return {
+    name: 'convertSolarLunar',
+    beforeRender(calendarData = {}, calendarConfig = {}) {
+      let { dates = [], selectedDates = [] } = calendarData
+      if (calendarConfig.showLunar) {
+        dates = dates.map(dataInfo => {
+          const { year, month, date } = dataInfo
+          return {
+            ...dataInfo,
+            lunar: convertSolarLunar.solar2lunar(year, month, date)
+          }
+        })
+        selectedDates = selectedDates.map(dataInfo => {
+          const { year, month, date } = dataInfo
+          return {
+            ...dataInfo,
+            lunar: convertSolarLunar.solar2lunar(year, month, date)
+          }
+        })
+      }
+      return {
+        calendarData: {
+          ...calendarData,
+          dates: dates,
+          selectedDates: selectedDates
+        },
+        calendarConfig
+      }
+    },
+    methods() {
+      return {
+        convertSolarLunar: dateInfo => {
+          dateInfo = getDateRow2Dict(dateInfo)
+          if (!dateInfo) return dateInfo
+          const { year, month, date } = dateInfo
+          return convertSolarLunar.solar2lunar(year, month, date)
+        },
+        convertlLunar2Solar: (dateInfo, isLeapMonth) => {
+          dateInfo = getDateRow2Dict(dateInfo)
+          if (!dateInfo) return dateInfo
+          const { year, month, date } = dateInfo
+          return convertSolarLunar.lunar2solar(year, month, date, isLeapMonth)
+        }
+      }
+    }
+  }
+}

+ 305 - 0
component/v2/plugins/time-range.js

@@ -0,0 +1,305 @@
+/**
+ * @Author: drfu*
+ * @Description: 时间区域选择
+ * @Date: 2020-10-08 21:22:09*
+ * @Last Modified by: drfu
+ * @Last Modified time: 2020-10-11 13:56:32
+ * */
+
+import { renderCalendar } from '../render'
+import {
+  logger,
+  dateUtil,
+  getCalendarConfig,
+  getCalendarData
+} from '../utils/index'
+
+function pusheNextMonthDateArea(
+  dateInfo = {},
+  startTimestamp,
+  endTimestamp,
+  selectedDates = []
+) {
+  let tempOfSelectedDate = [...selectedDates]
+  const dates = dateUtil.calcDates(dateInfo.year, dateInfo.month)
+  let datesLen = dates.length
+  for (let i = 0; i < datesLen; i++) {
+    const date = dates[i]
+    const timeStamp = dateUtil.getTimeStamp(date)
+    if (timeStamp <= endTimestamp && timeStamp >= startTimestamp) {
+      tempOfSelectedDate.push({
+        ...date,
+        choosed: true
+      })
+    }
+    if (i === datesLen - 1 && timeStamp < endTimestamp) {
+      pusheNextMonthDateArea(
+        dateUtil.getNextMonthInfo(date),
+        startTimestamp,
+        endTimestamp,
+        tempOfSelectedDate
+      )
+    }
+  }
+  return tempOfSelectedDate
+}
+function pushPrevMonthDateArea(
+  dateInfo = {},
+  startTimestamp,
+  endTimestamp,
+  selectedDates = []
+) {
+  let tempOfSelectedDate = [...selectedDates]
+  const dates = dateUtil.sortDatesByTime(
+    dateUtil.calcDates(dateInfo.year, dateInfo.month),
+    'desc'
+  )
+  let datesLen = dates.length
+  let firstDate = dateUtil.getTimeStamp(dates[0])
+  for (let i = 0; i < datesLen; i++) {
+    const date = dates[i]
+    const timeStamp = dateUtil.getTimeStamp(date)
+    if (timeStamp >= startTimestamp && timeStamp <= endTimestamp) {
+      tempOfSelectedDate.push({
+        ...date,
+        choosed: true
+      })
+    }
+    if (i === datesLen - 1 && firstDate > startTimestamp) {
+      pushPrevMonthDateArea(
+        dateUtil.getPrevMonthInfo(date),
+        startTimestamp,
+        endTimestamp,
+        tempOfSelectedDate
+      )
+    }
+  }
+  return tempOfSelectedDate
+}
+/**
+ * 当设置日期区域非当前时保存其他月份的日期至已选日期数组
+ * @param {object} info
+ */
+function calcDateWhenNotInOneMonth(info) {
+  const { firstDate, lastDate, startTimestamp, endTimestamp } = info
+  let { selectedDate } = info
+  if (dateUtil.getTimeStamp(firstDate) > startTimestamp) {
+    selectedDate = pushPrevMonthDateArea(
+      dateUtil.getPrevMonthInfo(firstDate),
+      startTimestamp,
+      endTimestamp,
+      selectedDate
+    )
+  }
+  if (dateUtil.getTimeStamp(lastDate) < endTimestamp) {
+    selectedDate = pusheNextMonthDateArea(
+      dateUtil.getNextMonthInfo(lastDate),
+      startTimestamp,
+      endTimestamp,
+      selectedDate
+    )
+  }
+  return [...selectedDate]
+}
+
+/**
+ *  指定日期区域转时间戳
+ * @param {array} timearea 时间区域
+ */
+export function convertTimeRangeToTimestamp(timearea = []) {
+  const start = timearea[0].split('-')
+  const end = timearea[1].split('-')
+  if (start.length !== 3 || end.length !== 3) {
+    logger.warn('enableArea() 参数格式为: ["2018-2-1", "2018-3-1"]')
+    return {}
+  }
+  const startTimestamp = dateUtil
+    .newDate(start[0], start[1], start[2])
+    .getTime()
+  const endTimestamp = dateUtil.newDate(end[0], end[1], end[2]).getTime()
+  return {
+    start,
+    end,
+    startTimestamp,
+    endTimestamp
+  }
+}
+
+/**
+ * 校验时间区域是否合法
+ * @param {array} dateArea 时间区域
+ */
+function validateTimeRange(dateArea) {
+  const {
+    start,
+    end,
+    startTimestamp,
+    endTimestamp
+  } = convertTimeRangeToTimestamp(dateArea)
+  if (!start || !end) return
+  const startMonthDays = dateUtil.getDatesCountOfMonth(start[0], start[1])
+  const endMonthDays = dateUtil.getDatesCountOfMonth(end[0], end[1])
+  if (start[2] > startMonthDays || start[2] < 1) {
+    logger.warn('enableArea() 开始日期错误,指定日期不在当前月份天数范围内')
+    return false
+  } else if (start[1] > 12 || start[1] < 1) {
+    logger.warn('enableArea() 开始日期错误,月份超出1-12月份')
+    return false
+  } else if (end[2] > endMonthDays || end[2] < 1) {
+    logger.warn('enableArea() 截止日期错误,指定日期不在当前月份天数范围内')
+    return false
+  } else if (end[1] > 12 || end[1] < 1) {
+    logger.warn('enableArea() 截止日期错误,月份超出1-12月份')
+    return false
+  } else if (startTimestamp > endTimestamp) {
+    logger.warn('enableArea()参数最小日期大于了最大日期')
+    return false
+  } else {
+    return true
+  }
+}
+
+export default () => {
+  return {
+    name: 'timeRange',
+    beforeRender(calendarData = {}, calendarConfig = {}) {
+      const {
+        chooseAreaTimestamp = [],
+        dates = [],
+        selectedDates = []
+      } = calendarData
+      let __dates = dates
+      let __selectedDates = selectedDates
+      const [startDateTimestamp, endDateTimestamp] = chooseAreaTimestamp
+      if (chooseAreaTimestamp.length === 2) {
+        __selectedDates = []
+        __dates = dates.map(d => {
+          const date = { ...d }
+          const dateTimeStamp = dateUtil.getTimeStamp(date)
+          if (
+            dateTimeStamp >= startDateTimestamp &&
+            endDateTimestamp >= dateTimeStamp
+          ) {
+            date.choosed = true
+            __selectedDates.push(date)
+          } else {
+            date.choosed = false
+            __selectedDates = __selectedDates.filter(
+              item => dateUtil.getTimeStamp(item) !== dateTimeStamp
+            )
+          }
+          return date
+        })
+        const monthOfStartDate = new Date(startDateTimestamp).getMonth()
+        const monthOfEndDate = new Date(endDateTimestamp).getMonth()
+        if (monthOfStartDate !== monthOfEndDate) {
+          __selectedDates = calcDateWhenNotInOneMonth({
+            firstDate: __dates[0],
+            lastDate: __dates[__dates.length - 1],
+            startTimestamp: startDateTimestamp,
+            endTimestamp: endDateTimestamp,
+            selectedDate: __selectedDates
+          })
+        }
+      }
+      return {
+        calendarData: {
+          ...calendarData,
+          dates: __dates,
+          selectedDates: dateUtil.sortDatesByTime(
+            dateUtil.uniqueArrayByDate(__selectedDates)
+          )
+        },
+        calendarConfig
+      }
+    },
+    onTapDate(tapedDate, calendarData = {}, calendarConfig = {}) {
+      if (!calendarConfig.chooseAreaMode) {
+        return {
+          calendarData,
+          calendarConfig
+        }
+      }
+      let {
+        tempChooseAreaTimestamp = [],
+        chooseAreaTimestamp: existChooseAreaTimestamp = [],
+        selectedDates = [],
+        dates = []
+      } = calendarData
+      const timestamp = dateUtil.getTimeStamp(tapedDate)
+      let __dates = [...dates]
+      let __selectedDates = [...selectedDates]
+      if (
+        tempChooseAreaTimestamp.length === 2 ||
+        existChooseAreaTimestamp.length === 2
+      ) {
+        tempChooseAreaTimestamp = [tapedDate]
+        __selectedDates = []
+        __dates.forEach(d => (d.choosed = false))
+      } else if (tempChooseAreaTimestamp.length === 1) {
+        const preChoosedDate = tempChooseAreaTimestamp[0]
+        const preTimestamp = dateUtil.getTimeStamp(preChoosedDate)
+        if (preTimestamp <= timestamp) {
+          tempChooseAreaTimestamp.push(tapedDate)
+        } else if (preTimestamp > timestamp) {
+          tempChooseAreaTimestamp.unshift(tapedDate)
+        }
+      } else {
+        tempChooseAreaTimestamp = [tapedDate]
+      }
+      let chooseAreaTimestamp = []
+      if (tempChooseAreaTimestamp.length === 2) {
+        const [startDate, endDate] = tempChooseAreaTimestamp
+        const startDateTimestamp = dateUtil.getTimeStamp(startDate)
+        const endDateTimestamp = dateUtil.getTimeStamp(endDate)
+        chooseAreaTimestamp = [startDateTimestamp, endDateTimestamp]
+      }
+      return {
+        calendarData: {
+          ...calendarData,
+          chooseAreaTimestamp,
+          tempChooseAreaTimestamp,
+          dates: __dates,
+          selectedDates: __selectedDates
+        },
+        calendarConfig: {
+          ...calendarConfig,
+          multi: true
+        }
+      }
+    },
+    methods(component) {
+      return {
+        /**
+         * 设置连续日期选择区域
+         * @param {array} dateArea 区域开始结束日期数组
+         */
+        chooseDateArea: (dateArea = []) => {
+          if (dateArea.length === 1) {
+            dateArea = dateArea.concat(dateArea)
+          }
+          if (dateArea.length !== 2) return
+          const isRight = validateTimeRange(dateArea)
+          if (!isRight) return
+          const config = getCalendarConfig(component) || {}
+          const { startTimestamp, endTimestamp } = convertTimeRangeToTimestamp(
+            dateArea
+          )
+          const existCalendarData = getCalendarData('calendar', component)
+          return renderCalendar.call(
+            component,
+            {
+              ...existCalendarData,
+              chooseAreaTimestamp: [startTimestamp, endTimestamp]
+            },
+            {
+              ...config,
+              multi: true,
+              chooseAreaMode: true
+            }
+          )
+        }
+      }
+    }
+  }
+}

+ 135 - 0
component/v2/plugins/todo.js

@@ -0,0 +1,135 @@
+/**
+ * @Author: drfu*
+ * @Description: 代办事项
+ * @Date: 2020-10-08 21:22:09*
+ * @Last Modified by: drfu
+ * @Last Modified time: 2020-10-11 14:23:02
+ * */
+
+import { getCalendarData, dateUtil } from '../utils/index'
+import { renderCalendar } from '../render'
+
+function updateDatePropertyOfTodoLabel(todos, dates, showLabelAlways) {
+  const datesInfo = [...dates]
+  for (let todo of todos) {
+    let targetIdx = datesInfo.findIndex(
+      item => dateUtil.toTimeStr(item) === dateUtil.toTimeStr(todo)
+    )
+    let target = datesInfo[targetIdx]
+    if (!target) continue
+    if (showLabelAlways) {
+      target.showTodoLabel = true
+    } else {
+      target.showTodoLabel = !target.choosed
+    }
+    if (target.showTodoLabel) {
+      target.todoText = todo.todoText
+    }
+    target.color = todo.color
+  }
+  return datesInfo
+}
+
+export default () => {
+  return {
+    name: 'todo',
+    beforeRender(calendarData = {}, calendarConfig = {}, component) {
+      const { todos = [], dates = [], showLabelAlways } = calendarData
+      const dateWithTodoInfo = updateDatePropertyOfTodoLabel(
+        todos,
+        dates,
+        showLabelAlways
+      )
+      return {
+        calendarData: {
+          ...calendarData,
+          dates: dateWithTodoInfo
+        },
+        calendarConfig
+      }
+    },
+    methods(component) {
+      return {
+        setTodos: (options = {}) => {
+          const calendar = getCalendarData('calendar', component)
+          if (!calendar || !calendar.dates) {
+            return Promise.reject('请等待日历初始化完成后再调用该方法')
+          }
+          const {
+            circle,
+            dotColor = '',
+            pos = 'bottom',
+            showLabelAlways,
+            dates: todoDates = []
+          } = options
+          const { todos = [] } = calendar
+          const tranformStr2NumOfTodo = todoDates.map(date =>
+            dateUtil.tranformStr2NumOfDate(date)
+          )
+          const calendarData = {
+            dates: calendar.dates,
+            todos: dateUtil.uniqueArrayByDate(
+              todos.concat(tranformStr2NumOfTodo)
+            )
+          }
+          if (!circle) {
+            calendarData.todoLabelPos = pos
+            calendarData.todoLabelColor = dotColor
+          }
+          calendarData.todoLabelCircle = circle || false
+          calendarData.showLabelAlways = showLabelAlways || false
+          const existCalendarData = getCalendarData('calendar', component)
+          return renderCalendar.call(component, {
+            ...existCalendarData,
+            ...calendarData
+          })
+        },
+        deleteTodos(todos = []) {
+          if (!(todos instanceof Array) || !todos.length)
+            return Promise.reject('deleteTodos()应为入参为非空数组')
+          const existCalendarData = getCalendarData('calendar', component)
+          const allTodos = existCalendarData.todos || []
+          const toDeleteTodos = todos.map(item => dateUtil.toTimeStr(item))
+          const remainTodos = allTodos.filter(
+            item => !toDeleteTodos.includes(dateUtil.toTimeStr(item))
+          )
+          const { dates, curYear, curMonth } = existCalendarData
+          const _dates = [...dates]
+          const currentMonthTodos = dateUtil.filterDatesByYM(
+            {
+              year: curYear,
+              month: curMonth
+            },
+            remainTodos
+          )
+          _dates.forEach(item => {
+            item.showTodoLabel = false
+          })
+          currentMonthTodos.forEach(item => {
+            _dates[item.date - 1].showTodoLabel = !_dates[item.date - 1].choosed
+          })
+          return renderCalendar.call(component, {
+            ...existCalendarData,
+            dates: _dates,
+            todos: remainTodos
+          })
+        },
+        clearTodos() {
+          const existCalendarData = getCalendarData('calendar', component)
+          const _dates = [...existCalendarData.dates]
+          _dates.forEach(item => {
+            item.showTodoLabel = false
+          })
+          return renderCalendar.call(component, {
+            ...existCalendarData,
+            dates: _dates,
+            todos: []
+          })
+        },
+        getTodos() {
+          return getCalendarData('calendar.todos', component) || []
+        }
+      }
+    }
+  }
+}

+ 432 - 0
component/v2/plugins/week.js

@@ -0,0 +1,432 @@
+/**
+ * @Author: drfu*
+ * @Description: 周视图
+ * @Date: 2020-10-08 21:22:09*
+ * @Last Modified by: drfu
+ * @Last Modified time: 2020-10-12 14:39:45
+ * */
+
+import { renderCalendar } from '../render'
+import {
+  getCalendarConfig,
+  getCalendarData,
+  logger,
+  dateUtil
+} from '../utils/index'
+import { calcJumpData } from '../core'
+
+/**
+ * 当月第一周所有日期
+ */
+function firstWeekInMonth(
+  target = {},
+  calendarDates = [],
+  calendarConfig = {}
+) {
+  const { firstDayOfWeek } = calendarConfig
+  const firstDayOfWeekIsMon = firstDayOfWeek === 'Mon'
+  const { year, month } = target
+  let firstDay = dateUtil.getDayOfWeek(year, month, 1)
+  if (firstDayOfWeekIsMon && firstDay === 0) {
+    firstDay = 7
+  }
+  const [, end] = [0, 7 - firstDay]
+  return calendarDates.slice(0, firstDayOfWeekIsMon ? end + 1 : end)
+}
+
+/**
+ * 当月最后一周所有日期
+ */
+function lastWeekInMonth(target = {}, calendarDates = [], calendarConfig = {}) {
+  const { firstDayOfWeek } = calendarConfig
+  const firstDayOfWeekIsMon = firstDayOfWeek === 'Mon'
+  const { year, month } = target
+  const lastDay = dateUtil.getDatesCountOfMonth(year, month)
+  let lastDayWeek = dateUtil.getDayOfWeek(year, month, lastDay)
+  if (firstDayOfWeekIsMon && lastDayWeek === 0) {
+    lastDayWeek = 7
+  }
+  const [start, end] = [lastDay - lastDayWeek, lastDay]
+  return calendarDates.slice(firstDayOfWeekIsMon ? start : start - 1, end)
+}
+
+/**
+ * 判断目标日期是否在某些指定日历内
+ */
+function dateIsInDatesRange(target, dates) {
+  if (!target || !dates || !dates.length) return false
+  const targetDateStr = dateUtil.toTimeStr(target)
+  let rst = false
+  for (let date of dates) {
+    const dateStr = dateUtil.toTimeStr(date)
+    if (dateStr === targetDateStr) {
+      rst = true
+      return rst
+    }
+    rst = false
+  }
+  return rst
+}
+
+function getDatesWhenTargetInFirstWeek(target, firstWeekDates) {
+  const { year, month } = target
+  const prevMonthInfo = dateUtil.getPrevMonthInfo({ year, month })
+  let lastMonthDatesCount = dateUtil.getDatesCountOfMonth(
+    prevMonthInfo.year,
+    prevMonthInfo.month
+  )
+  let dates = firstWeekDates
+  let firstWeekCount = firstWeekDates.length
+  for (let i = 0; i < 7 - firstWeekCount; i++) {
+    const week = dateUtil.getDayOfWeek(+year, +month, lastMonthDatesCount)
+    dates.unshift({
+      year: prevMonthInfo.year,
+      month: prevMonthInfo.month,
+      date: lastMonthDatesCount,
+      week
+    })
+    lastMonthDatesCount -= 1
+  }
+  return dates
+}
+
+function getDatesWhenTargetInLastWeek(target, lastWeekDates) {
+  const { year, month } = target
+  const prevMonthInfo = dateUtil.getNextMonthInfo({ year, month })
+  let dates = lastWeekDates
+  let lastWeekCount = lastWeekDates.length
+  for (let i = 0; i < 7 - lastWeekCount; i++) {
+    const week = dateUtil.getDayOfWeek(+year, +month, i + 1)
+    dates.push({
+      year: prevMonthInfo.year,
+      month: prevMonthInfo.month,
+      date: i + 1,
+      week
+    })
+  }
+  return dates
+}
+
+function getDates(target, calendarDates = [], calendarConfig = {}) {
+  const { year, month, date } = target
+  const targetDay = dateUtil.getDayOfWeek(year, month, date)
+  const { firstDayOfWeek } = calendarConfig
+  const firstDayOfWeekIsMon = firstDayOfWeek === 'Mon'
+  if (firstDayOfWeekIsMon) {
+    const startIdx = date - (targetDay || 7)
+    return calendarDates.splice(startIdx, 7)
+  } else {
+    const startIdx = date - targetDay - 1
+    return calendarDates.splice(startIdx, 7)
+  }
+}
+
+function getTargetWeekDates(target, calendarConfig) {
+  if (!target) return
+  const { year, month } = target
+  const calendarDates = dateUtil.calcDates(year, month)
+  const firstWeekDates = firstWeekInMonth(target, calendarDates, calendarConfig)
+  const lastWeekDates = lastWeekInMonth(target, calendarDates, calendarConfig)
+  if (dateIsInDatesRange(target, firstWeekDates)) {
+    return getDatesWhenTargetInFirstWeek(target, firstWeekDates)
+  } else if (dateIsInDatesRange(target, lastWeekDates)) {
+    return getDatesWhenTargetInLastWeek(target, lastWeekDates)
+  } else {
+    return getDates(target, calendarDates, calendarConfig)
+  }
+}
+
+/**
+ * 计算周视图下当前这一周最后一天
+ */
+function calculateLastDateOfCurrentWeek(calendarData = {}) {
+  const { dates = [] } = calendarData
+  return dates[dates.length - 1]
+}
+/**
+ * 计算周视图下当前这一周第一天
+ */
+function calculateFirstDateOfCurrentWeek(calendarData = {}) {
+  const { dates } = calendarData
+  return dates[0]
+}
+
+/**
+ * 计算下一周的日期
+ */
+function calculateNextWeekDates(calendarData = {}) {
+  let { curYear, curMonth } = calendarData
+  let calendarDates = []
+  let lastDateInThisWeek = calculateLastDateOfCurrentWeek(calendarData)
+  const { year: LYear, month: LMonth } = lastDateInThisWeek
+  if (curYear !== LYear || curMonth !== LMonth) {
+    calendarDates = dateUtil.calcDates(LYear, LMonth)
+    curYear = LYear
+    curMonth = LMonth
+  } else {
+    calendarDates = dateUtil.calcDates(curYear, curMonth)
+  }
+  const lastDateInThisMonth = dateUtil.getDatesCountOfMonth(curYear, curMonth)
+  const count = lastDateInThisMonth - lastDateInThisWeek.date
+  const lastDateIdx = calendarDates.findIndex(
+    date => dateUtil.toTimeStr(date) === dateUtil.toTimeStr(lastDateInThisWeek)
+  )
+  const startIdx = lastDateIdx + 1
+  if (count >= 7) {
+    return {
+      dates: calendarDates.splice(startIdx, 7),
+      year: curYear,
+      month: curMonth
+    }
+  } else {
+    const nextMonth = dateUtil.getNextMonthInfo({
+      year: curYear,
+      month: curMonth
+    })
+    const { year, month } = nextMonth || {}
+    const calendarDatesOfNextMonth = dateUtil.calcDates(year, month)
+    const remainDatesOfThisMonth = calendarDates.splice(startIdx)
+    const patchDatesOfNextMonth = calendarDatesOfNextMonth.splice(
+      0,
+      7 - remainDatesOfThisMonth.length
+    )
+    return {
+      dates: [...remainDatesOfThisMonth, ...patchDatesOfNextMonth],
+      ...nextMonth
+    }
+  }
+}
+
+/**
+ * 计算上一周的日期
+ */
+function calculatePrevWeekDates(calendarData = {}) {
+  let { curYear, curMonth } = calendarData
+  let firstDateInThisWeek = calculateFirstDateOfCurrentWeek(calendarData)
+  let calendarDates = []
+  const { year: FYear, month: FMonth } = firstDateInThisWeek
+  if (curYear !== FYear || curMonth !== FMonth) {
+    calendarDates = dateUtil.calcDates(FYear, FMonth)
+    curYear = FYear
+    curMonth = FMonth
+  } else {
+    calendarDates = dateUtil.calcDates(curYear, curMonth)
+  }
+  const firstDateIdx = calendarDates.findIndex(
+    date => dateUtil.toTimeStr(date) === dateUtil.toTimeStr(firstDateInThisWeek)
+  )
+  if (firstDateIdx - 7 >= 0) {
+    const startIdx = firstDateIdx - 7
+    return {
+      dates: calendarDates.splice(startIdx, 7),
+      year: curYear,
+      month: curMonth
+    }
+  } else {
+    const prevMonth = dateUtil.getPrevMonthInfo({
+      year: curYear,
+      month: curMonth
+    })
+    const { year, month } = prevMonth || {}
+    const calendarDatesOfPrevMonth = dateUtil.calcDates(year, month)
+    const remainDatesOfThisMonth = calendarDates.splice(
+      0,
+      firstDateInThisWeek.date - 1
+    )
+    const patchDatesOfPrevMonth = calendarDatesOfPrevMonth.splice(
+      -(7 - remainDatesOfThisMonth.length)
+    )
+    return {
+      dates: [...patchDatesOfPrevMonth, ...remainDatesOfThisMonth],
+      ...prevMonth
+    }
+  }
+}
+
+export default () => {
+  return {
+    name: 'week',
+    beforeRender(calendarData = {}, calendarConfig = {}, component) {
+      const { initializedWeekMode, selectedDates } = calendarData
+      if (calendarConfig.weekMode && !initializedWeekMode) {
+        const { defaultDate } = calendarConfig
+        const target =
+          (selectedDates && selectedDates[0]) ||
+          (defaultDate && dateUtil.transformDateRow2Dict(defaultDate)) ||
+          dateUtil.todayFMD()
+        const waitRenderData = this.methods(
+          component
+        ).__calcDatesWhenSwitchView('week', target)
+        const { data, config } = waitRenderData || {}
+        const setSelectDates = this.methods(
+          component
+        ).__selectTargetDateWhenJump(target, data.dates, config)
+        return {
+          calendarData: {
+            ...data,
+            ...setSelectDates,
+            weeksCh: dateUtil.getWeekHeader(calendarConfig.firstDayOfWeek),
+            initializedWeekMode: true
+          },
+          calendarConfig
+        }
+      }
+      return {
+        calendarData,
+        calendarConfig
+      }
+    },
+    onSwitchCalendar(target = {}, calendarData = {}, component) {
+      const { direction } = target
+      const { curYear, curMonth } = calendarData
+      const calendarConfig = getCalendarConfig(component)
+      let waitRenderData = {}
+      if (calendarConfig.weekMode) {
+        if (direction === 'left') {
+          waitRenderData = calculateNextWeekDates(calendarData)
+        } else {
+          waitRenderData = calculatePrevWeekDates(calendarData)
+        }
+        const { dates, year, month } = waitRenderData
+        return {
+          ...calendarData,
+          dates,
+          curYear: year || curYear,
+          curMonth: month || curMonth
+        }
+      }
+      return calendarData
+    },
+    methods(component) {
+      return {
+        __selectTargetDateWhenJump: (target = {}, dates = [], config = {}) => {
+          let selectedDate = target
+          const weekDates = dates.map((date, idx) => {
+            const tmp = { ...date }
+            tmp.id = idx
+            const isTarget =
+              dateUtil.toTimeStr(target) === dateUtil.toTimeStr(tmp)
+            if (isTarget && !target.choosed && config.autoChoosedWhenJump) {
+              tmp.choosed = true
+              selectedDate = tmp
+            }
+            return tmp
+          })
+          return {
+            dates: weekDates,
+            selectedDates: [selectedDate]
+          }
+        },
+        __calcDatesForWeekMode(target, config = {}, calendarData = {}) {
+          const { year, month } = target || {}
+          const weekDates = getTargetWeekDates(target, config)
+          weekDates.forEach((date, idx) => (date.id = idx))
+          return {
+            data: {
+              ...calendarData,
+              prevMonthGrids: null,
+              nextMonthGrids: null,
+              dates: weekDates,
+              curYear: year,
+              curMonth: month
+            },
+            config: {
+              ...config,
+              weekMode: true
+            }
+          }
+        },
+        __calcDatesForMonthMode(target, config = {}, calendarData = {}) {
+          const { year, month } = target || {}
+          const waitRenderData = calcJumpData({
+            dateInfo: target,
+            config
+          })
+          return {
+            data: {
+              ...calendarData,
+              ...waitRenderData,
+              curYear: year,
+              curMonth: month
+            },
+            config: {
+              ...config,
+              weekMode: false
+            }
+          }
+        },
+        /**
+         * 周、月视图切换
+         * @param {string} view  视图 [week, month]
+         * @param {object} target
+         */
+        __calcDatesWhenSwitchView: (view, target) => {
+          const calendarConfig = getCalendarConfig(component)
+          if (calendarConfig.multi)
+            return logger.warn('多选模式不能切换周月视图')
+          const existCalendarData = getCalendarData('calendar', component) || {}
+          const {
+            selectedDates = [],
+            dates = [],
+            curYear,
+            curMonth
+          } = existCalendarData
+          const currentMonthSelected = selectedDates.filter(
+            item => curYear === +item.year || curMonth === +item.month
+          )
+          let jumpTarget = {}
+          if (target) {
+            jumpTarget = target
+          } else {
+            if (currentMonthSelected.length) {
+              jumpTarget = currentMonthSelected.pop()
+            } else {
+              jumpTarget = dates[0]
+            }
+          }
+          if (view === 'week') {
+            return this.methods(component).__calcDatesForWeekMode(
+              jumpTarget,
+              calendarConfig,
+              existCalendarData
+            )
+          } else {
+            return this.methods(component).__calcDatesForMonthMode(
+              jumpTarget,
+              calendarConfig,
+              existCalendarData
+            )
+          }
+        },
+        weekModeJump: dateInfo => {
+          const target = dateInfo || dateUtil.todayFMD()
+          const existCalendarData = getCalendarData('calendar', component) || {}
+          const waitRenderData = this.methods(
+            component
+          ).__calcDatesWhenSwitchView('week', target)
+          const { data, config } = waitRenderData || {}
+          const setSelectDates = this.methods(
+            component
+          ).__selectTargetDateWhenJump(target, data.dates, config)
+          return renderCalendar.call(
+            component,
+            {
+              ...existCalendarData,
+              ...data,
+              ...setSelectDates
+            },
+            config
+          )
+        },
+        switchView: (view, target) => {
+          const waitRenderData = this.methods(
+            component
+          ).__calcDatesWhenSwitchView(view, target)
+          const { data, config } = waitRenderData || {}
+          if (!data) return logger.warn('当前状态不能切换为周视图')
+          return renderCalendar.call(component, data, config)
+        }
+      }
+    }
+  }
+}

+ 51 - 0
component/v2/render.js

@@ -0,0 +1,51 @@
+import plugins from './plugins/index'
+import { getCalendarConfig } from './utils/index'
+
+/**
+ * 渲染日历
+ */
+export function renderCalendar(calendarData, calendarConfig) {
+  return new Promise(resolve => {
+    const Component = this
+    if (Component.firstRender === void 0) {
+      Component.firstRender = true
+    } else {
+      Component.firstRender = false
+    }
+    const exitData = Component.data.calendar || {}
+    for (let plugin of plugins.installed) {
+      const [, p] = plugin
+      if (typeof p.beforeRender === 'function') {
+        const {
+          calendarData: newData,
+          calendarConfig: newConfig
+        } = p.beforeRender(
+          { ...exitData, ...calendarData },
+          calendarConfig || getCalendarConfig(Component),
+          Component
+        )
+        calendarData = newData
+        calendarConfig = newConfig
+      }
+    }
+
+    Component.setData(
+      {
+        config: calendarConfig,
+        calendar: calendarData
+      },
+      () => {
+        const rst = {
+          calendar: calendarData,
+          config: calendarConfig,
+          firstRender: Component.firstRender
+        }
+        resolve(rst)
+        if (Component.firstRender) {
+          Component.triggerEvent('afterCalendarRender', rst)
+          Component.firstRender = false
+        }
+      }
+    )
+  })
+}

+ 0 - 1
component/calendar/iconfont/iconfont.wxss → component/v2/theme/iconfont.wxss

@@ -27,4 +27,3 @@
 .icon-doubleright::before {
     content: "\e7ee";
 }
-

+ 61 - 0
component/v2/theme/theme-default.wxss

@@ -0,0 +1,61 @@
+
+/* 日历主要颜色相关样式 */
+
+.default_color,
+.default_weekend-color,
+.default_handle-color,
+.default_week-color {
+    color: #ff629a;
+}
+
+.default_today {
+    color: #fff;
+    background-color: #874fb4;
+}
+
+.default_choosed {
+    color: #fff;
+    background-color: #ff629a;
+}
+
+.default_date-disable {
+    color: #c7c7c7;
+}
+
+.default_choosed.default_date-disable {
+    color: #e2e2e2;
+    background-color: #c2afb6;
+}
+
+.default_prev-month-date,
+.default_next-month-date {
+    color: #e2e2e2;
+}
+
+.default_normal-date {
+    color: #88d2ac;
+}
+
+.default_todo-circle {
+    border-color: #88d2ac;
+}
+
+.default_todo-dot {
+    background-color: #e54d42;
+}
+
+.default_date-desc {
+    color: #c2c2c2;
+}
+
+.default_date-desc-lunar {
+    color: #e54d42;
+}
+
+.default_date-desc-disable {
+    color: #e2e2e2;
+}
+
+.default_festival {
+    color: #c2c2c2;
+}

+ 58 - 0
component/v2/theme/theme-elegant.wxss

@@ -0,0 +1,58 @@
+.elegant_color,
+.elegant_weekend-color,
+.elegant_handle-color,
+.elegant_week-color {
+    color: #333;
+}
+
+.elegant_today {
+    color: #000;
+    background-color: #e1e7f5;
+}
+
+.elegant_choosed {
+    color: #000;
+    background-color: #e2e2e2;
+}
+
+.elegant_date-disable {
+    color: #c7c7c7;
+}
+
+.elegant_choosed.elegant_date-disable {
+    color: #999;
+    background-color: #ebebeb;
+}
+
+.elegant_prev-month-date,
+.elegant_next-month-date {
+    color: #e2e2e2;
+}
+
+.elegant_normal-date {
+    color: #777;
+}
+
+.elegant_todo-circle {
+    border-color: #161035;
+}
+
+.elegant_todo-dot {
+    background-color: #161035;
+}
+
+.elegant_date-desc {
+    color: #c2c2c2;
+}
+
+.elegant_date-desc-lunar {
+    color: #161035;
+}
+
+.elegant_date-desc-disable {
+    color: #e2e2e2;
+}
+
+.elegant_festival {
+    color: #c2c2c2;
+}

+ 285 - 0
component/v2/utils/index.js

@@ -0,0 +1,285 @@
+import Logger from './logger'
+import WxData from './wxData'
+
+let systemInfo
+export function getSystemInfo() {
+  if (systemInfo) return systemInfo
+  systemInfo = wx.getSystemInfoSync()
+  return systemInfo
+}
+
+export function isIos() {
+  const sys = getSystemInfo()
+  return /iphone|ios/i.test(sys.platform)
+}
+
+class Gesture {
+  /**
+   * 左滑
+   * @param {object} e 事件对象
+   * @returns {boolean} 布尔值
+   */
+  isLeft(gesture = {}, touche = {}) {
+    const { startX, startY } = gesture
+    const deltaX = touche.clientX - startX
+    const deltaY = touche.clientY - startY
+    if (deltaX < -60 && deltaY < 20 && deltaY > -20) {
+      return true
+    } else {
+      return false
+    }
+  }
+  /**
+   * 右滑
+   * @param {object} e 事件对象
+   * @returns {boolean} 布尔值
+   */
+  isRight(gesture = {}, touche = {}) {
+    const { startX, startY } = gesture
+    const deltaX = touche.clientX - startX
+    const deltaY = touche.clientY - startY
+
+    if (deltaX > 60 && deltaY < 20 && deltaY > -20) {
+      return true
+    } else {
+      return false
+    }
+  }
+}
+
+class DateUtil {
+  newDate(year, month, date) {
+    let cur = `${+year}-${+month}-${+date}`
+    if (isIos()) {
+      cur = `${+year}/${+month}/${+date}`
+    }
+    return new Date(cur)
+  }
+  /**
+   * 计算指定日期时间戳
+   * @param {object} date
+   */
+  getTimeStamp(dateInfo) {
+    if (typeof dateInfo === 'string') {
+      dateInfo = this.transformDateRow2Dict(dateInfo)
+    }
+    if (Object.prototype.toString.call(dateInfo) !== '[object Object]') return
+    const dateUtil = new DateUtil()
+    return dateUtil
+      .newDate(dateInfo.year, dateInfo.month, dateInfo.date)
+      .getTime()
+  }
+  /**
+   * 计算指定月份共多少天
+   * @param {number} year 年份
+   * @param {number} month  月份
+   */
+  getDatesCountOfMonth(year, month) {
+    return new Date(Date.UTC(year, month, 0)).getUTCDate()
+  }
+  /**
+   * 计算指定月份第一天星期几
+   * @param {number} year 年份
+   * @param {number} month  月份
+   */
+  firstDayOfWeek(year, month) {
+    return new Date(Date.UTC(year, month - 1, 1)).getUTCDay()
+  }
+  /**
+   * 计算指定日期星期几
+   * @param {number} year 年份
+   * @param {number} month  月份
+   * @param {number} date 日期
+   */
+  getDayOfWeek(year, month, date) {
+    return new Date(Date.UTC(year, month - 1, date)).getUTCDay()
+  }
+  todayFMD() {
+    const _date = new Date()
+    const year = _date.getFullYear()
+    const month = _date.getMonth() + 1
+    const date = _date.getDate()
+    return {
+      year: +year,
+      month: +month,
+      date: +date
+    }
+  }
+  todayTimestamp() {
+    const { year, month, date } = this.todayFMD()
+    const timestamp = this.newDate(year, month, date).getTime()
+    return timestamp
+  }
+  toTimeStr(dateInfo = {}) {
+    return `${+dateInfo.year}-${+dateInfo.month}-${+dateInfo.date}`
+  }
+  transformDateRow2Dict(dateStr) {
+    if (typeof dateStr === 'string' && dateStr.includes('-')) {
+      const [year, month, date] = dateStr.split('-')
+      return this.tranformStr2NumOfDate({
+        year,
+        month,
+        date
+      })
+    }
+    return {}
+  }
+  tranformStr2NumOfDate(date = {}) {
+    const target = { ...date }
+    // 可能传入字符串
+    target.year = +target.year
+    target.month = +target.month
+    target.date = +target.date
+    return target
+  }
+  sortDatesByTime(dates = [], sortType) {
+    return dates.sort((a, b) => {
+      const at = this.getTimeStamp(a)
+      const bt = this.getTimeStamp(b)
+      if (at < bt && sortType !== 'desc') {
+        return -1
+      } else {
+        return 1
+      }
+    })
+  }
+  getPrevMonthInfo(date = {}) {
+    const prevMonthInfo =
+      Number(date.month) > 1
+        ? {
+            year: +date.year,
+            month: Number(date.month) - 1
+          }
+        : {
+            year: Number(date.year) - 1,
+            month: 12
+          }
+    return prevMonthInfo
+  }
+  getNextMonthInfo(date = {}) {
+    const nextMonthInfo =
+      Number(date.month) < 12
+        ? {
+            year: +date.year,
+            month: Number(date.month) + 1
+          }
+        : {
+            year: Number(date.year) + 1,
+            month: 1
+          }
+    return nextMonthInfo
+  }
+  getPrevYearInfo(date = {}) {
+    return {
+      year: Number(date.year) - 1,
+      month: +date.month
+    }
+  }
+  getNextYearInfo(date = {}) {
+    return {
+      year: Number(date.year) + 1,
+      month: +date.month
+    }
+  }
+  findDateIndexInArray(target, dates) {
+    return dates.findIndex(
+      item => dateUtil.toTimeStr(item) === dateUtil.toTimeStr(target)
+    )
+  }
+  calcDates(year, month) {
+    const datesCount = this.getDatesCountOfMonth(year, month)
+    const dates = []
+    const today = dateUtil.todayFMD()
+    for (let i = 1; i <= datesCount; i++) {
+      const week = dateUtil.getDayOfWeek(+year, +month, i)
+      const date = {
+        year: +year,
+        id: i - 1,
+        month: +month,
+        date: i,
+        week,
+        isToday:
+          +today.year === +year && +today.month === +month && i === +today.date
+      }
+      dates.push(date)
+    }
+    return dates
+  }
+  /**
+   * 日期数组根据日期去重
+   * @param {array} array 数组
+   */
+  uniqueArrayByDate(array = []) {
+    let uniqueObject = {}
+    let uniqueArray = []
+    array.forEach(item => {
+      uniqueObject[dateUtil.toTimeStr(item)] = item
+    })
+    for (let i in uniqueObject) {
+      uniqueArray.push(uniqueObject[i])
+    }
+    return uniqueArray
+  }
+  /**
+   * 筛选指定年月日期
+   * @param {object} target 指定年月
+   * @param {array} dates 待筛选日期
+   */
+  filterDatesByYM(target, dates) {
+    if (target) {
+      const { year, month } = target
+      const _dates = dates.filter(
+        item => +item.year === +year && +item.month === +month
+      )
+      return _dates
+    }
+    return dates
+  }
+  getWeekHeader(firstDayOfWeek) {
+    let weeksCh = ['日', '一', '二', '三', '四', '五', '六']
+    if (firstDayOfWeek === 'Mon') {
+      weeksCh = ['一', '二', '三', '四', '五', '六', '日']
+    }
+    return weeksCh
+  }
+}
+
+/**
+ * 获取当前页面实例
+ */
+export function getCurrentPage() {
+  const pages = getCurrentPages() || []
+  const last = pages.length - 1
+  return pages[last] || {}
+}
+
+export function getComponentById(componentId) {
+  const logger = new Logger()
+  let page = getCurrentPage() || {}
+  if (page.selectComponent && typeof page.selectComponent === 'function') {
+    if (componentId) {
+      return page.selectComponent(componentId)
+    } else {
+      logger.warn('请传入组件ID')
+    }
+  } else {
+    logger.warn('该基础库暂不支持多个小程序日历组件')
+  }
+}
+
+export const logger = new Logger()
+export const calendarGesture = new Gesture()
+export const dateUtil = new DateUtil()
+export const getCalendarData = (key, component) =>
+  new WxData(component).getData(key)
+export const setCalendarData = (data, component) =>
+  new WxData(component).setData(data)
+export const getCalendarConfig = component =>
+  getCalendarData('config', component)
+export const setCalendarConfig = (config, component) =>
+  setCalendarData(
+    {
+      config
+    },
+    component
+  )

+ 23 - 0
component/v2/utils/logger.js

@@ -0,0 +1,23 @@
+export default class Logger {
+  info(msg) {
+    console.log(
+      '%cInfo: %c' + msg,
+      'color:#FF0080;font-weight:bold',
+      'color: #FF509B'
+    )
+  }
+  warn(msg) {
+    console.log(
+      '%cWarn: %c' + msg,
+      'color:#FF6600;font-weight:bold',
+      'color: #FF9933'
+    )
+  }
+  tips(msg) {
+    console.log(
+      '%cTips: %c' + msg,
+      'color:#00B200;font-weight:bold',
+      'color: #00CC33'
+    )
+  }
+}

+ 30 - 0
component/v2/utils/wxData.js

@@ -0,0 +1,30 @@
+class WxData {
+  constructor(component) {
+    this.Component = component
+  }
+  getData(key) {
+    const data = this.Component.data
+    if (!key) return data
+    if (key.includes('.')) {
+      let keys = key.split('.')
+      const tmp = keys.reduce((prev, next) => {
+        return prev[next]
+      }, data)
+      return tmp
+    } else {
+      return this.Component.data[key]
+    }
+  }
+  setData(data) {
+    return new Promise((resolve, reject) => {
+      if (!data) return reject('no data to set')
+      if (typeof data === 'object') {
+        this.Component.setData(data, () => {
+          resolve(data)
+        })
+      }
+    })
+  }
+}
+
+export default WxData

+ 318 - 0
pages/calendarComponent/index.js

@@ -0,0 +1,318 @@
+const conf = {
+  data: {
+    calendarConfig: {
+      showLunar: true
+      // chooseAreaMode: true,
+      // firstDayOfWeek: 'Mon',
+      // disableMode: {
+      //   type: 'after',
+      //   date: '2020-03-9'
+      // },
+      // defaultDay: '2020-3-6'
+      // multi: true
+    },
+    actionBtn: [
+      {
+        text: '跳转指定日期',
+        action: 'jump',
+        color: 'olive'
+      },
+      {
+        text: '获取当前已选',
+        action: 'getSelectedDay',
+        color: 'red'
+      },
+      {
+        text: '取消选中日期',
+        action: 'cancelSelectedDates',
+        color: 'mauve'
+      },
+      {
+        text: '设置待办事项',
+        action: 'setTodoLabels',
+        color: 'cyan'
+      },
+      {
+        text: '删除指定代办',
+        action: 'deleteTodoLabels',
+        color: 'pink'
+      },
+      {
+        text: '清空待办事项',
+        action: 'clearTodoLabels',
+        color: 'red'
+      },
+      {
+        text: '获取所有代办',
+        action: 'getTodoLabels',
+        color: 'purple'
+      },
+      {
+        text: '禁选指定日期',
+        action: 'disableDay',
+        color: 'olive'
+      },
+      {
+        text: '指定可选区域',
+        action: 'enableArea',
+        color: 'pink'
+      },
+      {
+        text: '指定特定可选',
+        action: 'enableDays',
+        color: 'red'
+      },
+      {
+        text: '选中指定日期',
+        action: 'setSelectedDays',
+        color: 'cyan'
+      },
+      {
+        text: '周月视图切换',
+        action: 'switchView',
+        color: 'orange'
+      },
+      {
+        text: '自定义配置',
+        action: 'config',
+        color: 'pink'
+      },
+      {
+        text: '获取自定义配置',
+        action: 'getConfig',
+        color: 'olive'
+      },
+      {
+        text: '获取日历面板日期',
+        action: 'getCalendarDates',
+        color: 'purple'
+      }
+    ]
+  },
+  afterTapDay(e) {
+    console.log('afterTapDay', e.detail)
+  },
+  whenChangeMonth(e) {
+    console.log('whenChangeMonth', e.detail)
+  },
+  whenChangeWeek(e) {
+    console.log('whenChangeWeek', e.detail)
+  },
+  onTapDay(e) {
+    console.log('onTapDay', e.detail)
+  },
+  afterCalendarRender(e) {
+    console.log('afterCalendarRender', e)
+    // this.calendar.switchView('week').then(() => {
+    //   this.calendar.jump(2020, 3, 1).then(date => {}); // 跳转至某日
+    // });
+  },
+  onSwipe(e) {
+    console.log('onSwipe', e)
+  },
+  showToast(msg) {
+    if (!msg || typeof msg !== 'string') return
+    wx.showToast({
+      title: msg,
+      icon: 'none',
+      duration: 1500
+    })
+  },
+  generateRandomDate(type) {
+    let random = ~~(Math.random() * 10)
+    switch (type) {
+      case 'year':
+        random = 201 * 10 + ~~(Math.random() * 10)
+        break
+      case 'month':
+        random = (~~(Math.random() * 10) % 9) + 1
+        break
+      case 'date':
+        random = (~~(Math.random() * 100) % 27) + 1
+        break
+      default:
+        break
+    }
+    return random
+  },
+  handleAction(e) {
+    const { action, disable } = e.currentTarget.dataset
+    if (disable) {
+      this.showToast('抱歉,还不支持~😂')
+    }
+    this.setData({
+      rst: []
+    })
+    const calendar = this.calendar
+    const { year, month } = calendar.getCurrentYM()
+    switch (action) {
+      case 'config':
+        calendar
+          .setCalendarConfig({
+            showLunar: false,
+            theme: 'elegant',
+            multi: true
+          })
+          .then(conf => {
+            console.log('设置成功:', conf)
+          })
+        break
+      case 'getConfig':
+        const config = calendar.getCalendarConfig()
+        this.showToast('请在控制台查看结果')
+        console.log('自定义配置: ', config)
+        break
+      case 'jump': {
+        const year = this.generateRandomDate('year')
+        const month = this.generateRandomDate('month')
+        const date = this.generateRandomDate('date')
+        calendar[action](year, month, date)
+        break
+      }
+      case 'getSelectedDay': {
+        const selected = calendar[action]()
+        if (!selected || !selected.length)
+          return this.showToast('当前未选择任何日期')
+        this.showToast('请在控制台查看结果')
+        console.log('get selected days: ', selected)
+        const rst = selected.map(item => JSON.stringify(item))
+        this.setData({
+          rst
+        })
+        break
+      }
+      case 'cancelSelectedDates':
+        calendar[action]([
+          {
+            year: 2020,
+            month: 3,
+            day: 3
+          }
+        ])
+        break
+      case 'setTodoLabels': {
+        const days = [
+          {
+            year,
+            month,
+            day: this.generateRandomDate('date'),
+            todoText: Math.random() * 10 > 5 ? '领奖日' : ''
+          }
+        ]
+        calendar[action]({
+          showLabelAlways: true,
+          days
+        })
+        console.log('set todo labels: ', days)
+        break
+      }
+      case 'deleteTodoLabels': {
+        const todos = [...calendar.getTodoLabels()]
+        if (todos && todos.length) {
+          todos.length = 1
+          calendar[action](todos)
+          const _todos = [...calendar.getTodoLabels()]
+          setTimeout(() => {
+            const rst = _todos.map(item => JSON.stringify(item))
+            this.setData(
+              {
+                rst
+              },
+              () => {
+                console.log('set todo labels: ', todos)
+              }
+            )
+          })
+        } else {
+          this.showToast('没有待办事项')
+        }
+        break
+      }
+      case 'clearTodoLabels':
+        const todos = [...calendar.getTodoLabels()]
+        if (!todos || !todos.length) {
+          return this.showToast('没有待办事项')
+        }
+        calendar[action]()
+        break
+      case 'getTodoLabels': {
+        const selected = calendar[action]()
+        if (!selected || !selected.length)
+          return this.showToast('未设置待办事项')
+        const rst = selected.map(item => JSON.stringify(item))
+        rst.map(item => JSON.stringify(item))
+        this.setData({
+          rst
+        })
+        break
+      }
+      case 'disableDay':
+        calendar[action]([
+          {
+            year,
+            month,
+            day: this.generateRandomDate('date')
+          }
+        ])
+        break
+      case 'enableArea': {
+        let sDate = this.generateRandomDate('date')
+        let eDate = this.generateRandomDate('date')
+        if (sDate > eDate) {
+          ;[eDate, sDate] = [sDate, eDate]
+        }
+        const area = [`${year}-${month}-${sDate}`, `${year}-${month}-${eDate}`]
+        calendar[action](area)
+        this.setData({
+          rstStr: JSON.stringify(area)
+        })
+        break
+      }
+      case 'enableDays':
+        const days = [
+          `${year}-${month}-${this.generateRandomDate('date')}`,
+          `${year}-${month}-${this.generateRandomDate('date')}`,
+          `${year}-${month}-${this.generateRandomDate('date')}`,
+          `${year}-${month}-${this.generateRandomDate('date')}`,
+          `${year}-${month}-${this.generateRandomDate('date')}`
+        ]
+        calendar[action](days)
+        this.setData({
+          rstStr: JSON.stringify(days)
+        })
+        break
+      case 'switchView':
+        if (!this.week) {
+          calendar[action]('week')
+          this.week = true
+        } else {
+          calendar[action]()
+          this.week = false
+        }
+        break
+      case 'setSelectedDays':
+        const toSet = [
+          {
+            year,
+            month,
+            day: this.generateRandomDate('date')
+          },
+          {
+            year,
+            month,
+            day: this.generateRandomDate('date')
+          }
+        ]
+        calendar[action](toSet)
+        break
+      case 'getCalendarDates':
+        this.showToast('请在控制台查看结果')
+        console.log(calendar.getCalendarDates())
+        break
+      default:
+        break
+    }
+  }
+}
+
+Page(conf)

+ 6 - 0
pages/calendarComponent/index.json

@@ -0,0 +1,6 @@
+{
+  "navigationBarTitleText": "小历-calendar 组件示例",
+  "usingComponents": {
+    "calendar": "../../component/calendar/index"
+  }
+}

+ 26 - 0
pages/calendarComponent/index.wxml

@@ -0,0 +1,26 @@
+<view style="width: 90%;height:640rpx;overflow:hidden;margin: 0 auto 40rpx;">
+  <calendar
+    calendarConfig="{{calendarConfig}}"
+    bind:onSwipe="onSwipe"
+    bind:onTapDay="onTapDay"
+    bind:afterTapDay="afterTapDay"
+    bind:whenChangeWeek="whenChangeWeek"
+    bind:whenChangeMonth="whenChangeMonth"
+    bind:afterCalendarRender="afterCalendarRender"
+  ></calendar>
+</view>
+
+<view class='b cc wrap btn-wrap'>
+    <button
+      class='b cc btn btn-{{item.color}} btn-action-{{item.action}}'
+      wx:for="{{actionBtn}}"
+      wx:key="action"
+      data-action="{{item.action}}"
+      data-disable="{{item.disable}}"
+      bindtap="handleAction">{{item.text}}</button>
+</view>
+
+<view class="show-rst-wrap">
+  <view wx:if="{{rst}}" wx:for="{{rst}}" wx:key="index" class="show-rst">{{item}}</view>
+  <view wx:if="{{rstStr}}" wx:key="index" class="show-rst">{{rstStr}}</view>
+</view>

+ 161 - 0
pages/calendarComponent/index.wxss

@@ -0,0 +1,161 @@
+
+.b {
+    display: flex;
+}
+
+.lr {
+    flex-direction: row;
+}
+
+.tb {
+    flex-direction: column;
+}
+
+.ps {
+    justify-content: flex-start;
+}
+
+.pb {
+    justify-content: space-between;
+}
+
+.pc {
+    justify-content: center;
+}
+
+.ac {
+    align-items: center;
+}
+
+.cc {
+    align-items: center;
+    justify-content: center;
+}
+
+.wrap {
+    flex-wrap: wrap;
+}
+
+.flex {
+    flex-grow: 1;
+}
+
+.btn-wrap {
+    width: 90%;
+    margin: 0 auto;
+}
+
+.btn {
+    box-sizing: border-box;
+    padding: 0 20rpx;
+    font-size: 28rpx;
+    height: 60rpx;
+    line-height: 1;
+    background: transparent;
+    margin-bottom: 20rpx;
+}
+
+.btn:active {
+    filter: grayscale(100%);
+}
+
+.btn-red {
+    color: #e54d42;
+}
+
+.btn-red::after {
+    border-color: #e54d42;
+    border-radius: 60rpx;
+}
+
+.btn-olive {
+    color: #8dc63f;
+}
+
+.btn-olive::after {
+    border-color: #8dc63f;
+    border-radius: 60rpx;
+}
+
+.btn-orange {
+    color: #f37b1d;
+}
+
+.btn-orange::after {
+    border-color: #f37b1d;
+    border-radius: 60rpx;
+}
+
+.btn-cyan {
+    color: #1cbbb4;
+}
+
+.btn-cyan::after {
+    border-color: #1cbbb4;
+    border-radius: 60rpx;
+}
+
+.btn-mauve {
+    color: #9c26b0;
+}
+
+.btn-mauve::after {
+    border-color: #9c26b0;
+    border-radius: 60rpx;
+}
+
+.btn-pink {
+    color: #e03997;
+}
+
+.btn-pink::after {
+    border-color: #e03997;
+    border-radius: 60rpx;
+}
+
+.btn-purple {
+    color: #6739b6;
+}
+
+.btn-purple::after {
+    border-color: #6739b6;
+    border-radius: 60rpx;
+}
+
+.btn-brown {
+    color: #a5673f;
+}
+
+.btn-brown::after {
+    border-color: #a5673f;
+    border-radius: 60rpx;
+}
+
+.btn-grey {
+    color: #8799a3;
+}
+
+.btn-grey::after {
+    border-color: #8799a3;
+    border-radius: 60rpx;
+}
+
+.show-rst-wrap {
+    padding: 20rpx 30rpx 50rpx;
+}
+
+.show-rst {
+    font-size: 26rpx;
+    color: #a3a3a3;
+    line-height: 26rpx;
+    margin-bottom: 15rpx;
+    word-wrap: break-word;
+}
+
+.blue-date {
+    color: blue;
+}
+
+.orange-date {
+    color: #f40;
+}

+ 389 - 51
pages/rili/index.js

@@ -1,25 +1,107 @@
-import { jump } from '../../component/calendar/index.js';
+import todo from '../../component/v2/plugins/todo'
+import selectable from '../../component/v2/plugins/selectable'
+import solarLunar from '../../component/v2/plugins/solarLunar/index'
+import timeRange from '../../component/v2/plugins/time-range'
+import week from '../../component/v2/plugins/week'
+import holidays from '../../component/v2/plugins/holidays/index'
+import plugin from '../../component/v2/plugins/index'
+import util, {formatDate, sec2Date} from '../../utils/util.js'
 const app = getApp()
+plugin
+  .use(todo)
+  .use(solarLunar)
+  .use(selectable)
+  .use(week)
+  .use(timeRange)
+  .use(holidays)
+
 const conf = {
   data: {
+    outList:[],
+    visitList:[],
     calendarConfig: {
-      // 配置内置主题
-      theme: 'default'
-    }
+      theme: 'elegant',
+      // showHolidays: true,
+      // emphasisWeek: true,
+      // chooseAreaMode: true
+      defaultDate: formatDate(new Date()),
+      // autoChoosedWhenJump: true
+    },
+    actionBtn: [
+      {
+        text: '跳转指定日期',
+        action: 'jump',
+        color: 'olive'
+      },
+      {
+        text: '获取当前已选',
+        action: 'getSelectedDates',
+        color: 'red'
+      },
+      {
+        text: '取消选中日期',
+        action: 'cancelSelectedDates',
+        color: 'mauve'
+      },
+      {
+        text: '设置待办事项',
+        action: 'setTodos',
+        color: 'cyan'
+      },
+      {
+        text: '删除指定代办',
+        action: 'deleteTodos',
+        color: 'pink'
+      },
+      {
+        text: '清空待办事项',
+        action: 'clearTodos',
+        color: 'red'
+      },
+      {
+        text: '获取所有代办',
+        action: 'getTodos',
+        color: 'purple'
+      },
+      {
+        text: '禁选指定日期',
+        action: 'disableDates',
+        color: 'olive'
+      },
+      {
+        text: '指定可选区域',
+        action: 'enableArea',
+        color: 'pink'
+      },
+      {
+        text: '指定特定可选',
+        action: 'enableDates',
+        color: 'red'
+      },
+      {
+        text: '选中指定日期',
+        action: 'setSelectedDates',
+        color: 'cyan'
+      },
+      {
+        text: '周月视图切换',
+        action: 'switchView',
+        color: 'orange'
+      },
+      {
+        text: '获取自定义配置',
+        action: 'getConfig',
+        color: 'olive'
+      },
+      {
+        text: '获取日历面板日期',
+        action: 'getCalendarDates',
+        color: 'purple'
+      }
+    ]
   },
-  doSomeThing() {
-    // 调用日历方法
-    this.calendar.enableArea(['2022-3-7', '2022-4-7']);
-  }
-};
-Page({
-  /**
-   * 选择日期后执行的事件
-   * currentSelect 当前点击的日期
-   * allSelectedDays 选择的所有日期(当mulit为true时,allSelectedDays有值)
-   */
-  afterTapDay(e) {
-    console.log('afterTapDay', e.detail); // => { currentSelect: {}, allSelectedDays: [] }
+  afterTapDate(e) {
+    console.log('afterTapDate', e.detail)
   },
   onLoad: function(){
     app.checkLogin(res=>{
@@ -27,48 +109,304 @@ Page({
         wx.navigateTo({
           url: '/pages/index/index',
         })
+      }else{
+        console.log("loadData", res )
+        this.loadData()
       }
     })
   },
-  /**
-   * 当日历滑动时触发(适用于周/月视图)
-   * 可在滑动时按需在该方法内获取当前日历的一些数据
-   */
-  onSwipe(e) {
-    console.log('onSwipe', e.detail);
-    const dates = this.calendar.getCalendarDates();
+  loadData(){
+    let curDate = this.data.calendarConfig.defaultDate;
+    let fromDate = curDate.substr(0,8)+ "01"
+    let toDate = curDate.substr(0,8)+ "31"
+    
+    app.formPost("User.loadData", {fromDate, toDate}).then( res=>{
+      console.log("loadData", res )
+      if( res.code == 200){
+        let outList = res.data.outList||[];
+        let visitList = res.data.visitList||[];
+        this.setData({outList, visitList});
+        this.loadMarks()
+      }
+    })
+  },
+  loadMarks(){
+    const calendar = this.selectComponent('#calendar').calendar
+    let {outList, visitList} = this.data;
+    console.log("loadMarks", outList, visitList )
+    var dateMap = {};
+    // 外出
+    for(let i in outList ){
+      let item = outList[i];
+      let {fromDate,toDate} = item;
+      if( toDate < fromDate) continue;
+      while( fromDate <= toDate){
+        dateMap[fromDate] = 1
+        fromDate = util.nextDate( fromDate);
+      }
+    }
+    // 
+    for(let i in visitList ){
+      let item = visitList[i];
+      let {fromDate,toDate} = item;
+      if( toDate < fromDate) continue;
+      while( fromDate <= toDate){
+        dateMap[fromDate] = dateMap[fromDate]?3:2
+        fromDate = util.nextDate( fromDate);
+      }
+    }
+    var dates = [];
+    for( let date in dateMap ){
+      let d = new Date(date);
+      let item = {year: d.getFullYear(), month: d.getMonth()+1, date: d.getDate()};
+      switch( dateMap[date]){
+        case 1:
+          item.todoText = "出";
+          break;
+        case 2:
+         item.todoText = "临";
+         break;
+        default:
+          item.todoText = "临&出";
+      }
+      dates.push( item );
+    }
+    console.log("dates", dates );
+    calendar.setTodos({
+      showLabelAlways: true,
+      dates
+    })
   },
-  /**
-   * 当改变月份时触发
-   * => current 当前年月 / next 切换后的年月
-   */
   whenChangeMonth(e) {
-    console.log('whenChangeMonth', e.detail);
-    // => { current: { month: 3, ... }, next: { month: 4, ... }}
+    console.log('whenChangeMonth', e.detail)
   },
-  /**
-   * 周视图下当改变周时触发
-   * => current 当前周信息 / next 切换后周信息
-   */
   whenChangeWeek(e) {
-    console.log('whenChangeWeek', e.detail);
-    // {
-    //    current: { currentYM: {year: 2019, month: 1 }, dates: [{}] },
-    //    next: { currentYM: {year: 2019, month: 1}, dates: [{}] },
-    //    directionType: 'next_week'
-    // }
+    console.log('whenChangeWeek', e.detail)
   },
-  /**
-   * 日期点击事件(此事件会完全接管点击事件),需自定义配置 takeoverTap 值为真才能生效
-   * currentSelect 当前点击的日期
-   */
-  onTapDay(e) {
-    console.log('onTapDay', e.detail); // => { year: 2019, month: 12, day: 3, ...}
+  takeoverTap(e) {
+    console.log('takeoverTap', e.detail)
   },
-  /**
-   * 日历初次渲染完成后触发事件,如设置事件标记
-   */
   afterCalendarRender(e) {
-    console.log('afterCalendarRender', e);
+    console.log('afterCalendarRender', e)
+    // 获取日历组件上的 calendar 对象
+    // const calendar = this.selectComponent('#calendar').calendar
+    // console.log('afterCalendarRender -> calendar', calendar)
+  },
+  onSwipe(e) {
+    console.log('onSwipe', e)
+  },
+  showToast(msg) {
+    if (!msg || typeof msg !== 'string') return
+    wx.showToast({
+      title: msg,
+      icon: 'none',
+      duration: 1500
+    })
+  },
+  generateRandomDate(type) {
+    let random = ~~(Math.random() * 10)
+    switch (type) {
+      case 'year':
+        random = 201 * 10 + ~~(Math.random() * 10)
+        break
+      case 'month':
+        random = (~~(Math.random() * 10) % 9) + 1
+        break
+      case 'date':
+        random = (~~(Math.random() * 100) % 27) + 1
+        break
+      default:
+        break
+    }
+    return random
+  },
+  handleAction(e) {
+    const { action, disable } = e.currentTarget.dataset
+    if (disable) {
+      this.showToast('抱歉,还不支持~😂')
+    }
+    this.setData({
+      rst: []
+    })
+    const calendar = this.selectComponent('#calendar').calendar
+    const { year, month } = calendar.getCurrentYM()
+    switch (action) {
+      case 'config':
+        calendar
+          .setCalendarConfig({
+            showLunar: false,
+            theme: 'elegant',
+            multi: true
+          })
+          .then(conf => {
+            console.log('设置成功:', conf)
+          })
+        break
+      case 'getConfig':
+        const config = calendar.getCalendarConfig()
+        this.showToast('请在控制台查看结果')
+        console.log('自定义配置: ', config)
+        break
+      case 'jump': {
+        const year = this.generateRandomDate('year')
+        const month = this.generateRandomDate('month')
+        const date = this.generateRandomDate('date')
+        const config = calendar.getCalendarConfig()
+        if (config.weekMode) {
+          calendar['weekModeJump']({ year, month, date })
+        } else {
+          calendar[action]({ year, month, date })
+        }
+        break
+      }
+      case 'getSelectedDates': {
+        const selected = calendar[action]()
+        if (!selected || !selected.length)
+          return this.showToast('当前未选择任何日期')
+        this.showToast('请在控制台查看结果')
+        console.log('get selected dates: ', selected)
+        const rst = selected.map(item => JSON.stringify(item))
+        this.setData({
+          rst
+        })
+        break
+      }
+      case 'cancelSelectedDates':
+        const selected = calendar.getSelectedDates()
+        calendar[action](selected)
+        break
+      case 'setTodos': {
+        const dates = [
+          {
+            year,
+            month,
+            date: this.generateRandomDate('date'),
+            todoText: Math.random() * 10 > 5 ? '领奖日' : ''
+          }
+        ]
+        calendar[action]({
+          showLabelAlways: true,
+          dates
+        })
+        console.log('set todo: ', dates)
+        break
+      }
+      case 'deleteTodos': {
+        const todos = [...calendar.getTodos()]
+        if (todos.length) {
+          calendar[action]([todos[0]]).then(() => {
+            const _todos = [...calendar.getTodos()]
+            setTimeout(() => {
+              const rst = _todos.map(item => JSON.stringify(item))
+              this.setData(
+                {
+                  rst
+                },
+                () => {
+                  console.log('delete todo: ', todos[0])
+                }
+              )
+            })
+          })
+        } else {
+          this.showToast('没有待办事项')
+        }
+        break
+      }
+      case 'clearTodos':
+        const todos = [...calendar.getTodos()]
+        if (!todos || !todos.length) {
+          return this.showToast('没有待办事项')
+        }
+        calendar[action]()
+        break
+      case 'getTodos': {
+        const selected = calendar[action]()
+        if (!selected || !selected.length)
+          return this.showToast('未设置待办事项')
+        const rst = selected.map(item => JSON.stringify(item))
+        rst.map(item => JSON.stringify(item))
+        console.log("rst", rst)
+        this.setData({
+          rst
+        })
+        break
+      }
+      case 'disableDates':
+        calendar[action]([
+          {
+            year,
+            month,
+            date: this.generateRandomDate('date')
+          }
+        ])
+        break
+      case 'enableArea': {
+        let sDate = this.generateRandomDate('date')
+        let eDate = this.generateRandomDate('date')
+        if (sDate > eDate) {
+          ;[eDate, sDate] = [sDate, eDate]
+        }
+        const area = [`${year}-${month}-${sDate}`, `${year}-${month}-${eDate}`]
+        calendar[action](area)
+        this.setData({
+          rstStr: JSON.stringify(area)
+        })
+        break
+      }
+      case 'enableDates':
+        const dates = [
+          `${year}-${month}-${this.generateRandomDate('date')}`,
+          `${year}-${month}-${this.generateRandomDate('date')}`,
+          `${year}-${month}-${this.generateRandomDate('date')}`,
+          `${year}-${month}-${this.generateRandomDate('date')}`,
+          `${year}-${month}-${this.generateRandomDate('date')}`
+        ]
+        calendar[action](dates)
+        this.setData({
+          rstStr: JSON.stringify(dates)
+        })
+        break
+      case 'switchView':
+        if (!this.week) {
+          calendar[action]('week').then(calendarData => {
+            console.log('switch success!', calendarData)
+          })
+          this.week = true
+        } else {
+          calendar[action]().then(calendarData => {
+            console.log('switch success!', calendarData)
+          })
+          this.week = false
+        }
+        break
+      case 'setSelectedDates':
+        const toSet = [
+          {
+            year,
+            month,
+            date: this.generateRandomDate('date')
+          },
+          {
+            year,
+            month,
+            date: this.generateRandomDate('date')
+          }
+        ]
+        calendar[action](toSet)
+        break
+      case 'getCalendarDates':
+        this.showToast('请在控制台查看结果')
+        console.log(
+          calendar.getCalendarDates({
+            lunar: true
+          })
+        )
+        break
+      default:
+        break
+    }
   }
-});
+}
+
+Page(conf)

+ 2 - 1
pages/rili/index.json

@@ -1,5 +1,6 @@
 {
+  "navigationBarTitleText": "莅临&外出",
   "usingComponents": {
-        "calendar": "../../component/calendar/index"
+    "calendar": "../../component/v2/index"
   }
 }

+ 27 - 9
pages/rili/index.wxml

@@ -1,9 +1,27 @@
-<calendar
-  calendarConfig="{{calendarConfig}}"
-  bind:onTapDay="onTapDay"
-  bind:afterTapDay="afterTapDay"
-  bind:onSwipe="onSwipe"
-  bind:whenChangeWeek="whenChangeWeek"
-  bind:whenChangeMonth="whenChangeMonth"
-  bind:afterCalendarRender="afterCalendarRender"
-></calendar>
+<view style="width: 90%;height:640rpx;overflow:hidden;margin: 0 auto 40rpx;">
+  <calendar
+    id="calendar"
+    config="{{calendarConfig}}"
+    bind:onSwipe="onSwipe"
+    bind:whenChangeWeek="whenChangeWeek"
+    bind:whenChangeMonth="whenChangeMonth"
+    bind:takeoverTap="takeoverTap"
+    bind:afterTapDate="afterTapDate"
+    bind:afterCalendarRender="afterCalendarRender"
+  ></calendar>
+</view>
+
+<!-- <view class='b cc wrap btn-wrap'>
+    <button
+      class='b cc btn btn-{{item.color}} btn-action-{{item.action}}'
+      wx:for="{{actionBtn}}"
+      wx:key="action"
+      data-action="{{item.action}}"
+      data-disable="{{item.disable}}"
+      bindtap="handleAction">{{item.text}}</button>
+</view> -->
+
+<!-- <view class="show-rst-wrap">
+  <view wx:if="{{rst}}" wx:for="{{rst}}" wx:key="index" class="show-rst">{{item}}</view>
+  <view wx:if="{{rstStr}}" wx:key="index" class="show-rst">{{rstStr}}</view>
+</view> -->

+ 1 - 1
pages/rili/index.wxss

@@ -1 +1 @@
-/* pages/rili/index.wxss */
+@import '../calendarComponent/index.wxss'

+ 6 - 2
pages/user/index/index.wxml

@@ -1,6 +1,6 @@
 <view class='userinfo' wx:if="{{userInfo.userId}}">
   <view class='userinfo-avatar'>
-    <image src="{{userInfo.headerImg}}" mode="widthFix" style="width:100%"></image>
+    <image src="/assets/icon/1.jpg" mode="widthFix" style="width:100%"></image>
   </view>
   <view class='userinfo-name'>
     <view> {{userInfo.nickname}}</view>
@@ -21,10 +21,14 @@
 <view>
   <i-cell-group i-class="my-group-margin">
 
-    <i-cell title="历史记录" is-link bindtap="gotoFileList" data-api="getViewArticalList">
+    <i-cell title="预留功能1" is-link bindtap="gotoFileList" data-api="getViewArticalList">
       <i-icon type="mine_fill" slot="icon" size="20" />
     </i-cell>
 
+    <i-cell title="预留功能2" is-link bindtap="gotoFileList" data-api="getViewArticalList">
+      <i-icon type="shop_fill" slot="icon" size="20" />
+    </i-cell>
+
     <i-cell title="退出登入" bind:click="logOut">
       <i-icon type="flashlight_fill" slot="icon" size="20" />
     </i-cell>

+ 1 - 1
project.config.json

@@ -42,7 +42,7 @@
   "compileType": "miniprogram",
   "libVersion": "2.8.0",
   "appid": "wx147197ea9d0b992b",
-  "projectname": "%E5%86%85%E4%B8%9A%E7%AE%A1%E7%90%86%E7%B3%BB%E7%BB%9F",
+  "projectname": "莅临&外出",
   "debugOptions": {
     "hidedInDevtools": []
   },

+ 28 - 0
utils/util.js

@@ -13,6 +13,31 @@ const formatTime = date => {
   return [year, month, day].map(formatNumber).join('/') + ' ' + [hour, minute, second].map(formatNumber).join(':')
 }
 
+const formatDate = date => {
+  const year = date.getFullYear()
+  const month = date.getMonth() + 1
+  const day = date.getDate()
+  return [year, month, day].map(formatNumber).join('-') 
+}
+
+const sec2Date = sec => {
+  let date = new Date( sec * 1000 );
+  const year = date.getFullYear()
+  const month = date.getMonth() + 1
+  const day = date.getDate()
+  return [year, month, day].map(formatNumber).join('-') 
+}
+
+const nextDate = datestr => {
+  let date = new Date( datestr );
+  date = new Date(+date + 86400*1000)
+  const year = date.getFullYear()
+  const month = date.getMonth() + 1
+  const day = date.getDate()
+  return [year, month, day].map(formatNumber).join('-') 
+}
+
+
 const formatNumber = n => {
   n = n.toString()
   return n[1] ? n : '0' + n
@@ -159,5 +184,8 @@ module.exports = {
   showSuccess,
   showToast,
   showMsg,
+  sec2Date,
+  nextDate,
+  formatDate,
   showBusy
 }