浏览代码

chore: 日历组件重构,新功能添加 (#1027)

* feat: 添加range组件、calendar组件在线文档

* fix: 文档调整

* fix: 重构calendar组件

* feat: 日历组件重构,文档修改,功能完善

* fix: 格式化

* fix: 代码格式化调整。

* fix: 去除无用代码

* fix: 文档调整

* fix:  文档调整

* fix: taro  demo 样式修改

Co-authored-by: lkjh3214 <13121007159@163.com>
lkjh3214 3 年之前
父节点
当前提交
9879206416

+ 156 - 19
src/packages/__VUE/calendar/demo.vue

@@ -14,8 +14,8 @@
         :default-value="date"
         @close="closeSwitch('isVisible')"
         @choose="setChooseValue"
-        :start-date="`2019-10-11`"
-        :end-date="`2029-11-11`"
+        :start-date="`2022-01-11`"
+        :end-date="`2022-11-11`"
       >
       </nut-calendar>
     </div>
@@ -36,15 +36,16 @@
         :end-date="`2021-01-08`"
         @close="closeSwitch('isVisible1')"
         @choose="setChooseValue1"
+        @select="select"
       >
       </nut-calendar>
     </div>
 
-    <h2>自定义日历-自动回填</h2>
+    <h2>快捷选择</h2>
     <div>
       <nut-cell
         :show-icon="true"
-        title="选择日期"
+        title="选择单个日期"
         :desc="date3 ? date3 : '请选择'"
         @click="openSwitch('isVisible3')"
       >
@@ -60,23 +61,98 @@
       >
       </nut-calendar>
     </div>
-
-    <h2>平铺展示</h2>
-    <div class="test-calendar-wrapper">
+    <div>
+      <nut-cell
+        :show-icon="true"
+        title="选择日期范围"
+        @click="openSwitch('isVisible4')"
+        :desc="date4 ? `${date4[0]}至${date4[1]}` : '请选择'"
+      >
+      </nut-cell>
       <nut-calendar
-        :poppable="false"
-        :default-value="date2"
+        v-model:visible="isVisible4"
+        :default-value="date4"
+        type="range"
+        :start-date="`2022-01-01`"
+        :end-date="`2022-12-31`"
+        @close="closeSwitch('isVisible4')"
+        @choose="setChooseValue4"
         :is-auto-back-fill="true"
-        @choose="setChooseValue2"
       >
       </nut-calendar>
     </div>
+    <h2>自定义日历</h2>
+    <div>
+      <nut-cell
+        :show-icon="true"
+        title="自定义按钮"
+        :desc="date5 && date5[0] ? `${date5[0]}至${date5[1]}` : '请选择'"
+        @click="openSwitch('isVisible5')"
+      >
+      </nut-cell>
+      <nut-calendar
+        v-model:visible="isVisible5"
+        :default-value="date5"
+        type="range"
+        :start-date="`2021-12-22`"
+        :end-date="`2022-12-31`"
+        @close="closeSwitch('isVisible5')"
+        @choose="setChooseValue5"
+      >
+        <template v-slot:btn>
+          <div class="wrapper">
+            <div class="d_div"> <span class="d_btn" @click="clickBtn">最近七天</span></div>
+            <div class="d_div"> <span class="d_btn" @click="clickBtn1">当月</span></div>
+          </div>
+        </template>
+        <template v-slot:day="date">
+          <span>{{ date.date.day }}</span>
+        </template>
+      </nut-calendar>
+    </div>
+    <div>
+      <nut-cell
+        :show-icon="true"
+        title="自定义时间文案"
+        :desc="date6 && date6[0] ? `${date6[0]}至${date6[1]}` : '请选择'"
+        @click="openSwitch('isVisible6')"
+      >
+      </nut-cell>
+      <nut-calendar
+        v-model:visible="isVisible6"
+        :default-value="date6"
+        type="range"
+        @close="closeSwitch('isVisible6')"
+        @choose="setChooseValue6"
+        :start-date="`2022-01-01`"
+        :end-date="`2022-12-31`"
+        confirm-text="submit"
+        start-text="入店"
+        end-text="离店"
+        title="日期选择"
+      >
+        <template v-slot:day="date">
+          <span>{{ date.date.day <= 9 ? '0' + date.date.day : date.date.day }}</span>
+        </template>
+        <template v-slot:bottomInfo="date">
+          <span class="info">{{
+            date.date ? (date.date.day <= 10 ? '上旬' : date.date.day <= 20 ? '中旬' : '下旬') : ''
+          }}</span>
+        </template>
+      </nut-calendar>
+    </div>
+    <h2>平铺展示</h2>
+    <div class="test-calendar-wrapper">
+      <nut-calendar :poppable="false" :default-value="date2" :is-auto-back-fill="true" @choose="setChooseValue2">
+      </nut-calendar>
+    </div>
   </div>
 </template>
 
 <script lang="ts">
 import { reactive, toRefs } from 'vue';
 import { createComponent } from '../../utils/create';
+import Utils from '../../utils/date';
 
 const { createDemo } = createComponent('calendar');
 
@@ -85,10 +161,17 @@ interface TestCalendarState {
   date: string;
   dateWeek: string;
   isVisible1: boolean;
+  isVisible2: boolean;
+  isVisible3: boolean;
+  isVisible4: boolean;
+  isVisible5: boolean;
+  isVisible6: boolean;
   date1: string[];
   date2: string;
-  isVisible3: boolean;
   date3: string;
+  date4: string[];
+  date5: string[];
+  date6: string[];
 }
 export default createDemo({
   props: {},
@@ -97,14 +180,18 @@ export default createDemo({
       isVisible: false,
       date: '',
       dateWeek: '',
-
-      isVisible1: false,
       date1: ['2019-12-23', '2019-12-26'],
-
       date2: '2020-07-08',
-
+      date3: '',
+      date4: ['2021-12-23', '2021-12-26'],
+      date5: ['2021-12-23', '2021-12-26'],
+      date6: [],
+      isVisible1: false,
+      isVisible2: false,
       isVisible3: false,
-      date3: ''
+      isVisible4: false,
+      isVisible5: false,
+      isVisible6: false
     });
     const openSwitch = (param: string) => {
       state[`${param}`] = true;
@@ -119,19 +206,43 @@ export default createDemo({
       state.dateWeek = param[4];
     };
 
+    const select = (param: string) => {
+      console.log(param);
+    };
     const setChooseValue1 = (param: string) => {
       state.date1 = [...[param[0][3], param[1][3]]];
     };
 
     const setChooseValue2 = (param: string) => {
       state.date2 = param[3];
-      console.log(state.date2);
     };
 
     const setChooseValue3 = (param: string) => {
       state.date3 = param[3];
     };
+    const setChooseValue4 = (param: string) => {
+      state.date4 = [...[param[0][3], param[1][3]]];
+    };
 
+    const setChooseValue5 = (param: string) => {
+      state.date5 = [...[param[0][3], param[1][3]]];
+    };
+    const setChooseValue6 = (param: string) => {
+      state.date6 = [...[param[0][3], param[1][3]]];
+    };
+    const clickBtn = (param: string) => {
+      let date = [Utils.date2Str(new Date()), Utils.getDay(6)];
+      state.date5 = date;
+    };
+    const clickBtn1 = (param: string) => {
+      let date = new Date();
+      let year = date.getFullYear();
+      let month: any = date.getMonth() + 1;
+      month = month < 10 ? '0' + month : month + '';
+      let yearMonth = `${year}-${month}`;
+      let currMonthDays = Utils.getMonthDays(year + '', month + '');
+      state.date5 = [`${yearMonth}-01`, `${yearMonth}-${currMonthDays}`];
+    };
     return {
       ...toRefs(state),
       openSwitch,
@@ -139,7 +250,13 @@ export default createDemo({
       setChooseValue,
       setChooseValue1,
       setChooseValue2,
-      setChooseValue3
+      setChooseValue3,
+      setChooseValue4,
+      setChooseValue5,
+      setChooseValue6,
+      clickBtn,
+      clickBtn1,
+      select
     };
   }
 });
@@ -149,7 +266,27 @@ export default createDemo({
 .test-calendar-wrapper {
   display: flex;
   width: 100%;
-  height: 613px;
+  height: 560px;
   overflow: hidden;
 }
+.wrapper {
+  display: flex;
+  padding: 0 40px;
+}
+.d_div {
+  margin: 0px 5px;
+  .d_btn {
+    background: #fa3f19;
+    color: #fff;
+    font-size: 12px;
+    padding: 2px 8px;
+    border-radius: 4px;
+    display: inline-block;
+    height: 16px;
+  }
+}
+.info {
+  font-size: 12px;
+  line-height: 14px;
+}
 </style>

+ 289 - 3
src/packages/__VUE/calendar/doc.md

@@ -91,6 +91,7 @@ export default {
     :end-date="`2021-01-08`"
     @close="closeSwitch('isVisible')"
     @choose="setChooseValue"
+    @select="select"
   >
   </nut-calendar>
 </template>
@@ -111,24 +112,28 @@ export default {
     const setChooseValue= param => {
       state.date = [...[param[0][3], param[1][3]]];
     };
+    const select = (param: string) => {
+      console.log(param);
+    };
     return {
       ...toRefs(state),
       openSwitch,
       closeSwitch,
       setChooseValue,
+      select,
     };
   }  
 };
 </script>
 ```
 :::
-### 自定义日历-自动回填
+### 快捷选择-单选
 :::demo
 ```html
 <template>
   <nut-cell
     :showIcon="true"
-    title="选择日期"
+    title="选择单个日期"
     :desc="date ? date : '请选择'"
     @click="openSwitch('isVisible')"
   >
@@ -137,6 +142,7 @@ export default {
     v-model:visible="isVisible"
     @close="closeSwitch('isVisible')"
     @choose="setChooseValue"
+    :default-value="date"
     :start-date="null"
     :end-date="null"
     :is-auto-back-fill="true"
@@ -171,11 +177,264 @@ export default {
 </script>
 ```
 :::
+
+### 快捷选择-范围选择
+:::demo
+```html
+<template>
+  <nut-cell
+    :showIcon="true"
+    title="选择日期范围"
+    :desc="date ? `${date[0]}至${date[1]}` : '请选择'"
+    @click="openSwitch('isVisible')"
+  >
+  </nut-cell>
+  <nut-calendar
+    v-model:visible="isVisible"
+    :default-value="date"
+    type="range"
+    :start-date="`2022-01-01`"
+    :end-date="`2022-12-31`"
+    @close="closeSwitch('isVisible')"
+    @choose="setChooseValue"
+    :is-auto-back-fill="true"
+  >
+  </nut-calendar>
+</template>
+<script lang="ts">
+import { reactive, toRefs } from 'vue';
+export default {
+  setup() {
+    const state = reactive({
+      date: ['2021-12-23', '2021-12-26'],
+      isVisible: false
+    });
+    const openSwitch = param => {
+      state[`${param}`] = true;
+    };
+    const closeSwitch = param => {
+      state[`${param}`] = false;
+    };
+     const setChooseValue = param => {
+      state.date= param[3];
+    };
+    return {
+      ...toRefs(state),
+      setChooseValue,
+      openSwitch,
+      closeSwitch
+    };
+  }
+}
+</script>
+```
+:::
+
+### 自定义日历按钮
+:::demo
+```html
+<template>
+  <nut-cell
+    :showIcon="true"
+    title="自定义按钮"
+    :desc="date ? `${date[0]}至${date[1]}` : '请选择'"
+    @click="openSwitch('isVisible')"
+  >
+  </nut-cell>
+  <nut-calendar
+    v-model:visible="isVisible"
+    :default-value="date"
+    type="range"
+    :start-date="`2021-12-22`"
+    :end-date="`2022-12-31`"
+    @close="closeSwitch('isVisible')"
+    @choose="setChooseValue"
+  >
+    <template v-slot:btn>
+      <div class="wrapper">
+        <div class="d_div"> <span class="d_btn" @click="clickBtn">最近七天</span></div>
+        <div class="d_div"> <span class="d_btn" @click="clickBtn1">当月</span></div>
+      </div>
+    </template>
+    <template v-slot:day="date">
+      <span>{{ date.date.day }}</span>
+    </template>
+  </nut-calendar>
+</template>
+
+<script lang="ts">
+import { reactive, toRefs } from 'vue';
+export default {
+  setup() {
+    const state = reactive({
+      date: ['2021-12-23', '2021-12-26'],
+      isVisible: false
+    });
+    const getNumTwoBit = function(n: number): string {
+      n = Number(n);
+      return (n > 9 ? '' : '0') + n;
+    };
+    const date2Str =  function(date: Date, split?: string): string {
+      split = split || '-';
+      const y = date.getFullYear();
+      const m = getNumTwoBit(date.getMonth() + 1);
+      const d = getNumTwoBit(date.getDate());
+      return [y, m, d].join(split);
+    };
+    const getDay = function(i: number): string {
+      i = i || 0;
+      let date = new Date();
+      const diff = i * (1000 * 60 * 60 * 24);
+      date = new Date(date.getTime() + diff);
+      return date2Str(date);
+    };
+    const isLeapYear= function(y: number): boolean {
+      return (y % 4 == 0 && y % 100 != 0) || y % 400 == 0;
+    };
+    const getMonthDays= function(year: string, month: string): number {
+      if (/^0/.test(month)) {
+        month = month.split('')[1];
+      }
+      return ([
+        0,
+        31,
+        isLeapYear(Number(year)) ? 29 : 28,
+        31,
+        30,
+        31,
+        30,
+        31,
+        31,
+        30,
+        31,
+        30,
+        31
+      ] as number[])[month as any];
+    };
+    const openSwitch = param => {
+      state[`${param}`] = true;
+    };
+    const closeSwitch = param => {
+      state[`${param}`] = false;
+    };
+     const setChooseValue = param => {
+      state.date= param[3];
+    };
+    const clickBtn = (param: string) => {
+      let date = [date2Str(new Date()), getDay(6)];
+      state.date5 = date;
+    };
+    const clickBtn1 = (param: string) => {
+      let date = new Date();
+      let year = date.getFullYear();
+      let month: any = date.getMonth() + 1;
+      month = month < 10 ? '0' + month : month + '';
+      let yearMonth = `${year}-${month}`;
+      let currMonthDays = getMonthDays(year + '', month + '');
+      state.date5 = [`${yearMonth}-01`, `${yearMonth}-${currMonthDays}`];
+    };
+    return {
+      ...toRefs(state),
+      setChooseValue,
+      openSwitch,
+      closeSwitch,
+      clickBtn,
+      clickBtn1
+    };
+  }
+}
+</script>
+<style lang="scss" scoped>
+.wrapper {
+  display: flex;
+  padding: 0 40px;
+  justify-content: center;
+}
+.d_div {
+  margin: 0px 5px;
+  .d_btn {
+    background: #fa3f19;
+    color: #fff;
+    font-size: 12px;
+    padding: 2px 8px;
+    border-radius: 4px;
+    display: inline-block;
+    height: 16px;
+  }
+}
+
+</style>
+```
+:::
+
+### 自定义时间文案
+:::demo
+```html
+<template>
+  <nut-cell
+    :showIcon="true"
+    title="自定义时间文案"
+    :desc="date && date[0] ? `${date[0]}至${date[1]}` : '请选择'"
+    @click="openSwitch('isVisible')"
+  >
+  </nut-cell>
+  <nut-calendar
+    v-model:visible="isVisible"
+    :default-value="date"
+    type="range"
+    @close="closeSwitch('isVisible')"
+    @choose="setChooseValue"
+    :start-date="`2022-01-01`"
+    :end-date="`2022-12-31`"
+    confirm-text="提交"
+    start-text="入店"
+    end-text="离店"
+    title="日期选择"
+  >
+    <template v-slot:day="date">
+      <span>{{ date.date.day <= 9 ? '0' + date.date.day : date.date.day }}</span>
+    </template>
+    <template v-slot:bottomInfo="date">
+      <span class="info" style="fontSize:12px;lineHeight:14px">{{
+        date.date ? (date.date.day == 10 ? '十' :  '') : ''
+      }}</span>
+    </template>
+  </nut-calendar>
+</template>
+<script lang="ts">
+import { reactive, toRefs } from 'vue';
+export default {
+  setup() {
+    const state = reactive({
+      date: [],
+      isVisible: false
+    });
+    const openSwitch = param => {
+      state[`${param}`] = true;
+    };
+    const closeSwitch = param => {
+      state[`${param}`] = false;
+    };
+     const setChooseValue = param => {
+      state.date = param[3];
+    };
+    return {
+      ...toRefs(state),
+      setChooseValue,
+      openSwitch,
+      closeSwitch
+    };
+  }
+}
+</script>
+
+```
+:::
 ### 平铺展示
 :::demo
 ```html
 <template>
-  <div class="test-calendar-wrapper">
+  <div class="test-calendar-wrapper" >
     <nut-calendar
         :poppable="false"
         :default-value="date"
@@ -202,6 +461,15 @@ export default {
   }
 }
 </script>
+<style lang="scss" scoped>
+.test-calendar-wrapper {
+  display: flex;
+  width: 100%;
+  height: 560px;
+  overflow: hidden;
+}
+</style>
+
 ```
 :::
 
@@ -220,6 +488,12 @@ export default {
 | default-value     | 默认值,日期选择 String 格式,区间选择 Array 格式 | String 、 Array | null            |
 | start-date        | 开始日期, 如果不限制开始日期传 null              | String          | 今天            |
 | end-date          | 结束日期,如果不限制结束日期传 null               | String          | 距离今天 365 天 |
+| show-today          | 是否展示今天标记               | Boolean          | true |
+| start-text         | 范围选择,开始信息文案               | String          | ’开始‘ |
+| end-text         | 范围选择,结束信息文案               | String          | ‘结束’ |
+| confirm-text          | 底部确认按钮文案               | String          | ’确认‘ |
+| show-title          | 是否在展示日历标题               | Boolean          | true |
+| show-sub-title          | 是否展示日期标题              | Boolean          | true |
 
 ### Events
 
@@ -227,3 +501,15 @@ export default {
 |--------|------------------------------|------------------------------|
 | choose | 选择之后或是点击确认按钮触发 | 日期数组(包含年月日和星期) |
 | close  | 关闭时触发                   | -                            |
+| select  | 点击/选择后触发              |  Day:object                          |
+
+
+
+### Slots
+
+| 名称    | 说明         |
+|---------|--------------|
+| btn | 	自定义日历标题下部,可用以添加自定义操作 |
+| day | 	日期信息 |
+| topInfo | 	日期顶部信息 |
+| bottomInfo | 	日期底部信息 |

+ 47 - 41
src/packages/__VUE/calendar/index.scss

@@ -2,30 +2,24 @@
   position: relative;
   display: flex;
   flex: 1;
-  height: 518px;
-  padding-top: 132px;
-  padding-bottom: 78px;
-  color: $calendar-base-color;
-  font-size: $calendar-base-font;
-  background-color: $white;
+  color: #333333;
+  font-size: 16px;
+  background-color: #fff;
   overflow: hidden;
-
+  height: 100%;
+  flex-direction: column;
   &.nut-calendar-tile {
-    padding-top: 46px;
-    padding-bottom: 0;
-
     .nut-calendar-header {
       .calendar-title {
         font-size: $calendar-base-font;
       }
     }
   }
-
   .nut-calendar-taro {
     height: 60vh;
   }
   .popup-box {
-    height: 518px;
+    height: 100%;
   }
   .nut-calendar-content {
     overflow-y: auto;
@@ -36,26 +30,21 @@
 
   // 头部导航
   .nut-calendar-header {
-    position: absolute;
-    top: -1px;
-    left: 0;
-    right: 0;
     display: flex;
     flex-direction: column;
     text-align: center;
     padding-top: 1px;
     background-color: $white;
-    z-index: 1;
 
     .calendar-title {
-      padding-top: 22px;
       font-size: $calendar-title-font;
-      line-height: 25px;
-      border-radius: 12px 12px 0 0;
+      line-height: 44px;
+    }
+    .calendar-top-slot {
+      height: 24px;
     }
-
     .calendar-curr-month {
-      padding: 10px 0 7px;
+      padding: 7px 0;
       line-height: 22px;
     }
 
@@ -63,7 +52,7 @@
       display: flex;
       align-items: center;
       justify-content: space-around;
-      height: 46px;
+      height: 36px;
       border-radius: 0px 0px 12px 12px;
       box-shadow: 0px 4px 10px 0px rgba($color: #000000, $alpha: 0.06);
 
@@ -79,13 +68,17 @@
   // 月份
   .nut-calendar-content {
     flex: 1;
-
+    width: 100%;
+    display: block;
     .calendar-months-panel {
       position: relative;
       width: 100%;
       height: auto;
       display: block;
-
+      box-sizing: border-box;
+      .viewArea {
+        display: block;
+      }
       .calendar-month {
         display: flex;
         flex-direction: column;
@@ -118,14 +111,12 @@
 
       .calendar-month-con {
         overflow: hidden;
-
         .calendar-month-item {
           .calendar-month-day:nth-child(7n + 0),
           .calendar-month-day:nth-child(7n + 1) {
             color: $calendar-primary-color;
           }
         }
-
         .calendar-month-day {
           float: left;
           width: 14.28%;
@@ -136,24 +127,43 @@
           flex-direction: column;
           position: relative;
 
-          .curr-tips,
+          .calendar-curr-tips {
+            position: absolute;
+            width: 100%;
+          }
+
+          .calendar-curr-tip-curr {
+            position: absolute;
+            bottom: 6px;
+            width: 100%;
+            font-size: 12px;
+            line-height: 14px;
+          }
           .calendar-day-tip {
             position: absolute;
-            top: 10px;
+            bottom: 6px;
             width: 100%;
-            font-size: 11px;
-            line-height: 12px;
+            font-size: 12px;
+            line-height: 14px;
             color: $calendar-primary-color;
           }
-
+          .calendar-curr-tips-top {
+            top: 6px;
+          }
+          .calendar-curr-tips-bottom {
+            bottom: 6px;
+          }
           &-active {
             background-color: $calendar-primary-color;
             color: $white !important;
 
-            .curr-tips {
+            .calendar-curr-tips {
+              // color: $white !important;
+              visibility: hidden;
+            }
+            .calendar-curr-tip-curr {
               visibility: hidden;
             }
-
             .calendar-day-tip {
               color: $white;
             }
@@ -168,7 +178,7 @@
           }
 
           .calendar-day {
-            padding: 4px 0;
+            padding: 2px 0;
             font-size: $calendar-day-font;
           }
         }
@@ -178,19 +188,15 @@
 
   // 底部导航
   .nut-calendar-footer {
-    position: absolute;
-    left: 0;
-    right: 0;
-    bottom: -1px;
     display: flex;
-    height: 78px;
+    height: 62px;
     width: 100%;
     background-color: $white;
 
     .calendar-confirm-btn {
       height: 44px;
       width: 100%;
-      margin: 14px 18px;
+      margin: 10px 18px;
       border-radius: 22px;
       background: $button-primary-background-color;
       color: $white;

+ 90 - 8
src/packages/__VUE/calendar/index.taro.vue

@@ -7,6 +7,7 @@
     closeable
     @click-overlay="closePopup"
     @click-close-icon="closePopup"
+    :style="{ height: '85vh' }"
   >
     <nut-calendar-item
       ref="calendarRef"
@@ -20,7 +21,27 @@
       @update="update"
       @close="close"
       @choose="choose"
+      @select="select"
+      v-if="show"
+      :confirm-text="confirmText"
+      :start-text="startText"
+      :end-text="endText"
+      :show-today="showToday"
+      :show-title="showTitle"
+      :show-sub-title="showSubTitle"
     >
+      <template v-slot:btn v-if="showTopBtn">
+        <slot name="btn"> </slot>
+      </template>
+      <template v-slot:day="date" v-if="dayInfo">
+        <slot name="day" :date="date.date"> </slot>
+      </template>
+      <template v-slot:topInfo="date" v-if="topInfo">
+        <slot name="topInfo" :date="date.date"> </slot>
+      </template>
+      <template v-slot:bottomInfo="date" v-if="bottomInfo">
+        <slot name="bottomInfo" :date="date.date"> </slot>
+      </template>
     </nut-calendar-item>
   </nut-popup>
 
@@ -30,21 +51,38 @@
     :is-auto-back-fill="isAutoBackFill"
     :poppable="poppable"
     :title="title"
+    :confirm-text="confirmText"
+    :start-text="startText"
+    :end-text="endText"
     :default-value="defaultValue"
     :start-date="startDate"
     :end-date="endDate"
     @close="close"
     @choose="choose"
+    @select="select"
+    :show-title="showTitle"
+    :show-sub-title="showSubTitle"
   >
+    <template v-slot:btn v-if="showTopBtn">
+      <slot name="btn"> </slot>
+    </template>
+    <template v-slot:day="date" v-if="dayInfo">
+      <slot name="day" :date="date.date"> </slot>
+    </template>
+    <template v-slot:topInfo="date" v-if="topInfo">
+      <slot name="topInfo" :date="date.date"> </slot>
+    </template>
+    <template v-slot:bottomInfo="date" v-if="bottomInfo">
+      <slot name="bottomInfo" :date="date.date"> </slot>
+    </template>
   </nut-calendar-item>
 </template>
 <script lang="ts">
-import { PropType, ref, watch } from 'vue';
+import { ref, watch, computed } from 'vue';
 import { createComponent } from '../../utils/create';
 const { create } = createComponent('calendar');
-import CalendarItem from '../calendaritem/index.vue';
+import CalendarItem from '../calendaritem/index.taro.vue';
 import Utils from '../../utils/date';
-type InputDate = string | string[];
 export default create({
   components: {
     [CalendarItem.name]: CalendarItem
@@ -62,16 +100,40 @@ export default create({
       type: Boolean,
       default: true
     },
+    showTitle: {
+      type: Boolean,
+      default: true
+    },
+    showSubTitle: {
+      type: Boolean,
+      default: true
+    },
     visible: {
       type: Boolean,
+      default: false
+    },
+    showToday: {
+      type: Boolean,
       default: true
     },
     title: {
       type: String,
       default: '日历选择'
     },
+    confirmText: {
+      type: String,
+      default: '确认'
+    },
+    startText: {
+      type: String,
+      default: '开始'
+    },
+    endText: {
+      type: String,
+      default: '结束'
+    },
     defaultValue: {
-      type: String as PropType<InputDate>
+      type: [String, Array]
     },
     startDate: {
       type: String,
@@ -82,8 +144,20 @@ export default create({
       default: Utils.getDay(365)
     }
   },
-  emits: ['choose', 'close', 'update:visible'],
-  setup(props, { emit }) {
+  emits: ['choose', 'close', 'update:visible', 'select'],
+  setup(props, { emit, slots }) {
+    const showTopBtn = computed(() => {
+      return slots.btn;
+    });
+    const topInfo = computed(() => {
+      return slots.topInfo;
+    });
+    const dayInfo = computed(() => {
+      return slots.day;
+    });
+    const bottomInfo = computed(() => {
+      return slots.bottomInfo;
+    });
     let show = ref(props.visible);
     // element refs
     const calendarRef = ref<null | HTMLElement>(null);
@@ -108,7 +182,10 @@ export default create({
     const closePopup = () => {
       close();
     };
-
+    const select = (param: string) => {
+      // close();
+      emit('select', param);
+    };
     watch(
       () => props.visible,
       (value: boolean) => {
@@ -121,8 +198,13 @@ export default create({
       closePopup,
       update,
       close,
+      select,
       choose,
-      calendarRef
+      calendarRef,
+      showTopBtn,
+      topInfo,
+      dayInfo,
+      bottomInfo
     };
   }
 });

+ 90 - 5
src/packages/__VUE/calendar/index.vue

@@ -7,21 +7,43 @@
     :closeable="true"
     @click-overlay="closePopup"
     @click-close-icon="closePopup"
+    :destroy-on-close="true"
+    :style="{ height: '85vh' }"
   >
     <nut-calendar-item
+      v-if="visible"
       props
       ref="calendarRef"
       :type="type"
       :is-auto-back-fill="isAutoBackFill"
       :poppable="poppable"
       :title="title"
+      :confirm-text="confirmText"
+      :start-text="startText"
+      :end-text="endText"
       :default-value="defaultValue"
+      :show-today="showToday"
       :start-date="startDate"
       :end-date="endDate"
       @update="update"
       @close="close"
       @choose="choose"
+      @select="select"
+      :show-title="showTitle"
+      :show-sub-title="showSubTitle"
     >
+      <template v-slot:btn v-if="showTopBtn">
+        <slot name="btn"> </slot>
+      </template>
+      <template v-slot:day="date" v-if="dayInfo">
+        <slot name="day" :date="date.date"> </slot>
+      </template>
+      <template v-slot:topInfo="date" v-if="topInfo">
+        <slot name="topInfo" :date="date.date"> </slot>
+      </template>
+      <template v-slot:bottomInfo="date" v-if="bottomInfo">
+        <slot name="bottomInfo" :date="date.date"> </slot>
+      </template>
     </nut-calendar-item>
   </nut-popup>
   <nut-calendar-item
@@ -30,16 +52,34 @@
     :is-auto-back-fill="isAutoBackFill"
     :poppable="poppable"
     :title="title"
+    :confirm-text="confirmText"
+    :start-text="startText"
+    :end-text="endText"
     :default-value="defaultValue"
     :start-date="startDate"
     :end-date="endDate"
     @close="close"
     @choose="choose"
+    @select="select"
+    :show-title="showTitle"
+    :show-sub-title="showSubTitle"
   >
+    <template v-slot:btn v-if="showTopBtn">
+      <slot name="btn"> </slot>
+    </template>
+    <template v-slot:day="date" v-if="dayInfo">
+      <slot name="day" :date="date.date"> </slot>
+    </template>
+    <template v-slot:topInfo="date" v-if="topInfo">
+      <slot name="topInfo" :date="date.date"> </slot>
+    </template>
+    <template v-slot:bottomInfo="date" v-if="bottomInfo">
+      <slot name="bottomInfo" :date="date.date"> </slot>
+    </template>
   </nut-calendar-item>
 </template>
 <script lang="ts">
-import { PropType, ref } from 'vue';
+import { PropType, ref, computed } from 'vue';
 import { createComponent } from '../../utils/create';
 const { create } = createComponent('calendar');
 import CalendarItem from '../calendaritem/index.vue';
@@ -62,16 +102,40 @@ export default create({
       type: Boolean,
       default: true
     },
+    showTitle: {
+      type: Boolean,
+      default: true
+    },
+    showSubTitle: {
+      type: Boolean,
+      default: true
+    },
     visible: {
       type: Boolean,
       default: false
     },
+    showToday: {
+      type: Boolean,
+      default: true
+    },
     title: {
       type: String,
       default: '日历选择'
     },
+    confirmText: {
+      type: String,
+      default: '确认'
+    },
+    startText: {
+      type: String,
+      default: '开始'
+    },
+    endText: {
+      type: String,
+      default: '结束'
+    },
     defaultValue: {
-      type: String as PropType<InputDate>
+      type: [String, Array]
     },
     startDate: {
       type: String,
@@ -82,8 +146,20 @@ export default create({
       default: Utils.getDay(365)
     }
   },
-  emits: ['choose', 'close', 'update:visible'],
-  setup(props, { emit }) {
+  emits: ['choose', 'close', 'update:visible', 'select'],
+  setup(props, { emit, slots }) {
+    const showTopBtn = computed(() => {
+      return slots.btn;
+    });
+    const topInfo = computed(() => {
+      return slots.topInfo;
+    });
+    const dayInfo = computed(() => {
+      return slots.day;
+    });
+    const bottomInfo = computed(() => {
+      return slots.bottomInfo;
+    });
     // element refs
     const calendarRef = ref<null | HTMLElement>(null);
 
@@ -101,6 +177,10 @@ export default create({
       close();
       emit('choose', param);
     };
+    const select = (param: string) => {
+      // close();
+      emit('select', param);
+    };
 
     const closePopup = () => {
       close();
@@ -111,7 +191,12 @@ export default create({
       update,
       close,
       choose,
-      calendarRef
+      select,
+      calendarRef,
+      showTopBtn,
+      topInfo,
+      dayInfo,
+      bottomInfo
     };
   }
 });

+ 57 - 42
src/packages/__VUE/calendaritem/index.scss

@@ -2,52 +2,49 @@
   position: relative;
   display: flex;
   flex: 1;
-  height: 518px;
-  padding-top: 132px;
-  padding-bottom: 78px;
-  color: $calendar-base-color;
-  font-size: $calendar-base-font;
-  background-color: $white;
+  color: #333333;
+  font-size: 16px;
+  background-color: #fff;
   overflow: hidden;
-
+  height: 100%;
+  flex-direction: column;
   &.nut-calendar-tile {
-    height: 100%;
-    padding-top: 46px;
-    padding-bottom: 0;
-
     .nut-calendar-header {
       .calendar-title {
         font-size: $calendar-base-font;
       }
     }
   }
-
-  &.nut-calendar-nofooter {
-    padding-bottom: 0;
+  .nut-calendar-taro {
+    height: 60vh;
+  }
+  .popup-box {
+    height: 100%;
+  }
+  .nut-calendar-content {
+    overflow-y: auto;
+  }
+  ::-webkit-scrollbar {
+    display: none;
   }
 
   // 头部导航
   .nut-calendar-header {
-    position: absolute;
-    top: -1px;
-    left: 0;
-    right: 0;
     display: flex;
     flex-direction: column;
     text-align: center;
     padding-top: 1px;
     background-color: $white;
-    z-index: 1;
 
     .calendar-title {
-      padding-top: 22px;
       font-size: $calendar-title-font;
-      line-height: 25px;
-      border-radius: 12px 12px 0 0;
+      line-height: 44px;
+    }
+    .calendar-top-slot {
+      height: 24px;
     }
-
     .calendar-curr-month {
-      padding: 10px 0 7px;
+      padding: 7px 0;
       line-height: 22px;
     }
 
@@ -55,7 +52,8 @@
       display: flex;
       align-items: center;
       justify-content: space-around;
-      height: 46px;
+      height: 36px;
+      border-radius: 0px 0px 12px 12px;
       box-shadow: 0px 4px 10px 0px rgba($color: #000000, $alpha: 0.06);
 
       .calendar-week-item {
@@ -70,13 +68,17 @@
   // 月份
   .nut-calendar-content {
     flex: 1;
-
+    width: 100%;
+    display: block;
     .calendar-months-panel {
       position: relative;
       width: 100%;
       height: auto;
       display: block;
-
+      box-sizing: border-box;
+      .viewArea {
+        display: block;
+      }
       .calendar-month {
         display: flex;
         flex-direction: column;
@@ -109,14 +111,12 @@
 
       .calendar-month-con {
         overflow: hidden;
-
         .calendar-month-item {
           .calendar-month-day:nth-child(7n + 0),
           .calendar-month-day:nth-child(7n + 1) {
             color: $calendar-primary-color;
           }
         }
-
         .calendar-month-day {
           float: left;
           width: 14.28%;
@@ -127,24 +127,43 @@
           flex-direction: column;
           position: relative;
 
-          .calendar-curr-tips,
+          .calendar-curr-tips {
+            position: absolute;
+            width: 100%;
+          }
+
+          .calendar-curr-tip-curr {
+            position: absolute;
+            bottom: 6px;
+            width: 100%;
+            font-size: 12px;
+            line-height: 14px;
+          }
           .calendar-day-tip {
             position: absolute;
-            top: 10px;
+            bottom: 6px;
             width: 100%;
-            font-size: 11px;
-            line-height: 12px;
+            font-size: 12px;
+            line-height: 14px;
             color: $calendar-primary-color;
           }
-
+          .calendar-curr-tips-top {
+            top: 6px;
+          }
+          .calendar-curr-tips-bottom {
+            bottom: 6px;
+          }
           &-active {
             background-color: $calendar-primary-color;
             color: $white !important;
 
             .calendar-curr-tips {
+              // color: $white !important;
+              visibility: hidden;
+            }
+            .calendar-curr-tip-curr {
               visibility: hidden;
             }
-
             .calendar-day-tip {
               color: $white;
             }
@@ -159,7 +178,7 @@
           }
 
           .calendar-day {
-            padding: 4px 0;
+            padding: 2px 0;
             font-size: $calendar-day-font;
           }
         }
@@ -169,19 +188,15 @@
 
   // 底部导航
   .nut-calendar-footer {
-    position: absolute;
-    left: 0;
-    right: 0;
-    bottom: -1px;
     display: flex;
-    height: 78px;
+    height: 62px;
     width: 100%;
     background-color: $white;
 
     .calendar-confirm-btn {
       height: 44px;
       width: 100%;
-      margin: 14px 18px;
+      margin: 10px 18px;
       border-radius: 22px;
       background: $button-primary-background-color;
       color: $white;

+ 687 - 0
src/packages/__VUE/calendaritem/index.taro.vue

@@ -0,0 +1,687 @@
+<template>
+  <view
+    class="nut-calendar nut-calendar-taro"
+    :class="{
+      'nut-calendar-tile': !poppable,
+      'nut-calendar-nofooter': isAutoBackFill
+    }"
+  >
+    <!-- header -->
+    <view class="nut-calendar-header" :class="{ 'nut-calendar-header-tile': !poppable }">
+      <view class="calendar-title" v-if="showTitle">{{ title }}</view>
+      <view class="calendar-top-slot" v-if="showTopBtn">
+        <slot name="btn"> </slot>
+      </view>
+      <view class="calendar-curr-month" v-if="showSubTitle">{{ yearMonthTitle }}</view>
+      <view class="calendar-weeks" ref="weeksPanel">
+        <view class="calendar-week-item" v-for="(item, index) of weeks" :key="index">{{ item }}</view>
+      </view>
+    </view>
+    <!-- content-->
+    <scroll-view
+      :scroll-top="scrollTop"
+      :scroll-y="true"
+      class="nut-calendar-content"
+      ref="months"
+      @scroll="mothsViewScroll"
+    >
+      <view class="calendar-months-panel" ref="monthsPanel">
+        <view class="viewArea" ref="viewArea" :style="{ transform: `translateY(${translateY}px)` }">
+          <view class="calendar-month" v-for="(month, index) of compConthsDatas" :key="index" :id="month.title">
+            <view class="calendar-month-title">{{ month.title }}</view>
+            <view class="calendar-month-con">
+              <view class="calendar-month-item" :class="type === 'range' ? 'month-item-range' : ''">
+                <template v-for="(day, i) of month.monthData" :key="i">
+                  <view class="calendar-month-day" :class="getClass(day, month)" @click="chooseDay(day, month)">
+                    <!-- 日期显示slot -->
+                    <view class="calendar-day">
+                      <slot name="day" :date="day.type == 'curr' ? day : ''">
+                        {{ day.type == 'curr' ? day.day : '' }}
+                      </slot>
+                    </view>
+                    <view class="calendar-curr-tips calendar-curr-tips-top" v-if="topInfo">
+                      <slot name="topInfo" :date="day.type == 'curr' ? day : ''"> </slot>
+                    </view>
+                    <view class="calendar-curr-tips calendar-curr-tips-bottom" v-if="bottomInfo">
+                      <slot name="bottomInfo" :date="day.type == 'curr' ? day : ''"> </slot>
+                    </view>
+                    <view class="calendar-curr-tip-curr" v-if="!bottomInfo && showToday && isCurrDay(month, day.day)">
+                      今天
+                    </view>
+                    <view
+                      class="calendar-day-tip"
+                      :class="{ 'calendar-curr-tips-top': rangeTip(day, month) }"
+                      v-if="isStartTip(day, month)"
+                    >
+                      {{ startText }}
+                    </view>
+                    <view class="calendar-day-tip" v-if="isEndTip(day, month)">{{ endText }}</view>
+                  </view>
+                </template>
+              </view>
+            </view>
+          </view>
+        </view>
+      </view>
+    </scroll-view>
+    <!-- footer-->
+    <view class="nut-calendar-footer" v-if="poppable && !isAutoBackFill">
+      <view class="calendar-confirm-btn" @click="confirm">{{ confirmText }}</view>
+    </view>
+  </view>
+</template>
+<script lang="ts">
+import { PropType, reactive, ref, watch, toRefs, computed, onMounted } from 'vue';
+import { createComponent } from '../../utils/create';
+const { create } = createComponent('calendar-item');
+import Taro from '@tarojs/taro';
+import Utils from '../../utils/date';
+import requestAniFrame from '../../utils/raf';
+
+type InputDate = string | string[];
+interface CalendarState {
+  yearMonthTitle: string;
+  currDate: any;
+  propStartDate: string;
+  propEndDate: string;
+  currentIndex: number;
+  unLoadPrev: boolean;
+  touchParams: any;
+  transformY: number;
+  translateY: number;
+  scrollDistance: number;
+  defaultData: InputDate;
+  chooseData: any;
+  monthsData: any[];
+  dayPrefix: string;
+  startData: InputDate;
+  endData: InputDate;
+  isRange: boolean;
+  timer: number;
+  avgHeight: number;
+  monthsNum: number;
+  defaultRange: any[];
+  scrollTop: number;
+  compConthsDatas: any[];
+}
+interface Day {
+  day: string | number;
+  type: string;
+}
+
+interface MonthInfo {
+  curData: string[] | string;
+  title: string;
+  monthData: Day[];
+  cssHeight?: Number;
+  cssScrollHeight?: Number;
+}
+
+export default create({
+  props: {
+    type: {
+      type: String,
+      default: 'one'
+    },
+    isAutoBackFill: {
+      type: Boolean,
+      default: false
+    },
+    poppable: {
+      type: Boolean,
+      default: true
+    },
+    showTitle: {
+      type: Boolean,
+      default: true
+    },
+    showSubTitle: {
+      type: Boolean,
+      default: true
+    },
+    showToday: {
+      type: Boolean,
+      default: true
+    },
+    title: {
+      type: String,
+      default: '日历选择'
+    },
+    confirmText: {
+      type: String,
+      default: '确认'
+    },
+    startText: {
+      type: String,
+      default: '开始'
+    },
+    endText: {
+      type: String,
+      default: '结束'
+    },
+    defaultValue: {
+      type: [String, Array],
+      default: null
+    },
+    startDate: {
+      type: String,
+      default: Utils.getDay(0)
+    },
+    endDate: {
+      type: String,
+      default: Utils.getDay(365)
+    }
+  },
+  emits: ['choose', 'update', 'close', 'select'],
+
+  setup(props, { emit, slots }) {
+    const weeks = ref(['日', '一', '二', '三', '四', '五', '六']);
+    // element refs
+    const months = ref<null | HTMLElement>(null);
+    const monthsPanel = ref<null | HTMLElement>(null);
+    const weeksPanel = ref<null | HTMLElement>(null);
+    const viewArea = ref<null | HTMLElement>(null);
+    const scalePx = ref(2);
+    const viewHeight = ref(0);
+
+    const compConthsData = computed(() => {
+      return state.monthsData.slice(state.defaultRange[0], state.defaultRange[1]);
+    });
+    const showTopBtn = computed(() => {
+      return slots.btn;
+    });
+    const topInfo = computed(() => {
+      return slots.topInfo;
+    });
+    const bottomInfo = computed(() => {
+      return slots.bottomInfo;
+    });
+    // state
+    const state: CalendarState = reactive({
+      yearMonthTitle: '',
+      defaultRange: [0, 1],
+      compConthsDatas: [],
+
+      currDate: '',
+      propStartDate: '',
+      propEndDate: '',
+      unLoadPrev: false,
+      touchParams: {
+        startY: 0,
+        endY: 0,
+        startTime: 0,
+        endTime: 0,
+        lastY: 0,
+        lastTime: 0
+      },
+      transformY: 0,
+      translateY: 0,
+      scrollDistance: 0,
+      defaultData: [],
+      chooseData: [],
+      monthsData: [],
+      dayPrefix: 'calendar-month-day',
+      startData: '',
+      endData: '',
+      isRange: props.type === 'range',
+      timer: 0,
+      currentIndex: 0,
+      avgHeight: 0,
+      scrollTop: 0,
+      monthsNum: 0
+    });
+    // 日期转化成数组
+    const splitDate = (date: string) => {
+      return date.split('-');
+    };
+    // 判断是否为开始时间
+    const isStart = (currDate: string) => {
+      return Utils.isEqual(state.currDate[0], currDate);
+    };
+    // 判断是否为结束时间
+    const isEnd = (currDate: string) => {
+      return Utils.isEqual(state.currDate[1], currDate);
+    };
+
+    // 获取当前数据
+    const getCurrDate = (day: Day, month: MonthInfo, isRange?: boolean) => {
+      return month.curData[0] + '-' + month.curData[1] + '-' + Utils.getNumTwoBit(+day.day);
+    };
+
+    // 获取样式
+    const getClass = (day: Day, month: MonthInfo, isRange?: boolean) => {
+      const currDate = getCurrDate(day, month, isRange);
+      if (day.type == 'curr') {
+        if (
+          (!state.isRange && Utils.isEqual(state.currDate as string, currDate)) ||
+          (state.isRange && (isStart(currDate) || isEnd(currDate)))
+        ) {
+          return `${state.dayPrefix}-active`;
+        } else if (
+          (props.startDate && Utils.compareDate(currDate, props.startDate)) ||
+          (props.endDate && Utils.compareDate(props.endDate, currDate))
+        ) {
+          return `${state.dayPrefix}-disabled`;
+        } else if (
+          state.isRange &&
+          Array.isArray(state.currDate) &&
+          Object.values(state.currDate).length == 2 &&
+          Utils.compareDate(state.currDate[0], currDate) &&
+          Utils.compareDate(currDate, state.currDate[1])
+        ) {
+          return `${state.dayPrefix}-choose`;
+        } else {
+          return null;
+        }
+      } else {
+        return `${state.dayPrefix}-disabled`;
+      }
+    };
+
+    const confirm = () => {
+      if ((state.isRange && state.chooseData.length == 2) || !state.isRange) {
+        emit('choose', state.chooseData);
+        if (props.poppable) {
+          emit('update');
+        }
+      }
+    };
+
+    // 选中数据
+    const chooseDay = (day: Day, month: MonthInfo, isFirst: boolean, isRange?: boolean) => {
+      if (getClass(day, month, isRange) != `${state.dayPrefix}-disabled`) {
+        let days = [...month.curData];
+        // days = isRange ? days.splice(3) : days.splice(0, 3);
+        days[2] = typeof day.day == 'number' ? Utils.getNumTwoBit(day.day) : day.day;
+        days[3] = `${days[0]}-${days[1]}-${days[2]}`;
+        days[4] = Utils.getWhatDay(+days[0], +days[1], +days[2]);
+        if (!state.isRange) {
+          state.currDate = days[3];
+          state.chooseData = [...days];
+        } else {
+          if (Object.values(state.currDate).length == 2) {
+            state.currDate = [days[3]];
+          } else {
+            if (Utils.compareDate(state.currDate[0], days[3])) {
+              Array.isArray(state.currDate) && state.currDate.push(days[3]);
+            } else {
+              Array.isArray(state.currDate) && state.currDate.unshift(days[3]);
+            }
+          }
+          if (state.chooseData.length == 2 || !state.chooseData.length) {
+            state.chooseData = [...days];
+          } else {
+            if (Utils.compareDate(state.chooseData[3], days[3])) {
+              state.chooseData = [[...state.chooseData], [...days]];
+            } else {
+              state.chooseData = [[...days], [...state.chooseData]];
+            }
+          }
+        }
+
+        if (!isFirst) {
+          // 点击日期 触发
+          emit('select', state.chooseData);
+          if (props.isAutoBackFill) {
+            confirm();
+          }
+        }
+      }
+    };
+
+    // 获取当前月数据
+    const getCurrData = (type: string) => {
+      const monthData = type == 'prev' ? state.monthsData[0] : state.monthsData[state.monthsData.length - 1];
+      let year = parseInt(monthData.curData[0]);
+      let month = parseInt(monthData.curData[1].toString().replace(/^0/, ''));
+      switch (type) {
+        case 'prev':
+          month == 1 && (year -= 1);
+          month = month == 1 ? 12 : --month;
+          break;
+        case 'next':
+          month == 12 && (year += 1);
+          month = month == 12 ? 1 : ++month;
+          break;
+      }
+      return [year, Utils.getNumTwoBit(month), Utils.getMonthDays(String(year), String(month))];
+    };
+
+    // 获取日期状态
+    const getDaysStatus = (days: number, type: string, dateInfo: any) => {
+      // 修复:当某个月的1号是周日时,月份下方会空出来一行
+      let { year, month } = dateInfo;
+      if (type == 'prev' && days >= 7) {
+        days -= 7;
+      }
+      return Array.from(Array(days), (v, k) => {
+        return {
+          day: k + 1,
+          type: type,
+          year,
+          month
+        };
+      });
+    };
+    // 获取上一个月的最后一周天数,填充当月空白
+    const getPreDaysStatus = (days: number, type: string, dateInfo: any, preCurrMonthDays: number) => {
+      // 修复:当某个月的1号是周日时,月份下方会空出来一行
+      let { year, month } = dateInfo;
+      if (type == 'prev' && days >= 7) {
+        days -= 7;
+      }
+      let months = Array.from(Array(preCurrMonthDays), (v, k) => {
+        return {
+          day: k + 1,
+          type: type,
+          year,
+          month
+        };
+      });
+      return months.slice(preCurrMonthDays - days);
+    };
+    // 获取月数据
+    const getMonth = (curData: any[], type: string) => {
+      // 一号为周几
+      const preMonthDays = Utils.getMonthPreDay(+curData[0], +curData[1]);
+
+      let preMonth = curData[1] - 1;
+      let preYear = curData[0];
+      if (preMonth <= 0) {
+        preMonth = 12;
+        preYear += 1;
+      }
+      //当月天数与上个月天数
+      const currMonthDays = Utils.getMonthDays(curData[0], curData[1]);
+      const preCurrMonthDays = Utils.getMonthDays(preYear + '', preMonth + '');
+
+      const title = {
+        year: curData[0],
+        month: curData[1]
+      };
+      const monthInfo: MonthInfo = {
+        curData: curData,
+        title: `${title.year}年${title.month}月`,
+        monthData: [
+          ...(getPreDaysStatus(preMonthDays, 'prev', { month: preMonth, year: preYear }, preCurrMonthDays) as Day[]),
+          ...(getDaysStatus(currMonthDays, 'curr', title) as Day[])
+        ]
+      };
+
+      let titleHeight = Math.floor(46 * scalePx.value) + Math.floor(16 * scalePx.value) * 2;
+      let itemHeight = Math.floor(128 * scalePx.value);
+      monthInfo.cssHeight = titleHeight + (monthInfo.monthData.length > 35 ? itemHeight * 6 : itemHeight * 5);
+
+      let cssScrollHeight = 0;
+
+      if (state.monthsData.length > 0) {
+        cssScrollHeight =
+          state.monthsData[state.monthsData.length - 1].cssScrollHeight +
+          state.monthsData[state.monthsData.length - 1].cssHeight;
+      }
+      monthInfo.cssScrollHeight = cssScrollHeight;
+      if (type == 'next') {
+        // 判断当前日期 是否大于 最后一天
+        if (
+          !state.endData ||
+          !Utils.compareDate(
+            `${state.endData[0]}-${state.endData[1]}-${Utils.getMonthDays(state.endData[0], state.endData[1])}`,
+            `${curData[0]}-${curData[1]}-${curData[2]}`
+          )
+        ) {
+          state.monthsData.push(monthInfo);
+        }
+      } else {
+        // 判断当前日期 是否小于 第一天
+        if (
+          !state.startData ||
+          !Utils.compareDate(
+            `${curData[0]}-${curData[1]}-${curData[2]}`,
+            `${state.startData[0]}-${state.startData[1]}-01`
+          )
+        ) {
+          state.monthsData.unshift(monthInfo);
+        } else {
+          state.unLoadPrev = true;
+        }
+      }
+    };
+
+    // 初始化数据
+    const initData = () => {
+      // 初始化开始结束数据
+      let propStartDate = props.startDate ? props.startDate : Utils.getDay(0);
+      let propEndDate = props.endDate ? props.endDate : Utils.getDay(365);
+      state.propStartDate = propStartDate;
+      state.propEndDate = propEndDate;
+      state.startData = splitDate(propStartDate);
+      state.endData = splitDate(propEndDate);
+
+      // 根据是否存在默认时间,初始化当前日期,
+      if (!props.defaultValue || !(Array.isArray(props.defaultValue) && props.defaultValue.length > 0)) {
+        state.currDate = state.isRange ? [Utils.date2Str(new Date()), Utils.getDay(1)] : Utils.date2Str(new Date());
+      } else {
+        state.currDate = state.isRange ? [...props.defaultValue] : props.defaultValue;
+      }
+      // 判断时间范围内存在多少个月
+      const startDate = {
+        year: Number(state.startData[0]),
+        month: Number(state.startData[1])
+      };
+      const endDate = {
+        year: Number(state.endData[0]),
+        month: Number(state.endData[1])
+      };
+      let monthsNum = endDate.month - startDate.month;
+      if (endDate.year - startDate.year > 0) {
+        monthsNum = monthsNum + 12 * (endDate.year - startDate.year);
+      }
+
+      // 设置月份数据
+      getMonth(state.startData, 'next');
+
+      let i = 1;
+      do {
+        getMonth(getCurrData('next'), 'next');
+      } while (i++ < monthsNum);
+      state.monthsNum = monthsNum;
+
+      // 日期转化为数组,限制初始日期。判断时间范围
+      if (state.isRange && Array.isArray(state.currDate)) {
+        if (propStartDate && Utils.compareDate(state.currDate[0], propStartDate)) {
+          state.currDate.splice(0, 1, propStartDate);
+        }
+        if (propEndDate && Utils.compareDate(propEndDate, state.currDate[1])) {
+          state.currDate.splice(1, 1, propEndDate);
+        }
+        state.defaultData = [...splitDate(state.currDate[0]), ...splitDate(state.currDate[1])];
+      } else {
+        if (propStartDate && Utils.compareDate(state.currDate as string, propStartDate)) {
+          state.currDate = propStartDate;
+        } else if (propEndDate && !Utils.compareDate(state.currDate as string, propEndDate)) {
+          state.currDate = propEndDate;
+        }
+        state.defaultData = [...splitDate(state.currDate as string)];
+      }
+
+      let current = 0;
+      let lastCurrent = 0;
+      state.monthsData.forEach((item, index) => {
+        if (item.title == `${state.defaultData[0]}年${state.defaultData[1]}月`) {
+          current = index;
+        }
+        if (state.isRange) {
+          if (item.title == `${state.defaultData[3]}年${state.defaultData[4]}月`) {
+            lastCurrent = index;
+          }
+        }
+      });
+      setDefaultRange(monthsNum, current);
+      state.currentIndex = current;
+      state.yearMonthTitle = state.monthsData[state.currentIndex].title;
+
+      // 设置当前选中日期
+      if (state.isRange) {
+        chooseDay({ day: state.defaultData[2], type: 'curr' }, state.monthsData[state.currentIndex], true);
+        chooseDay({ day: state.defaultData[5], type: 'curr' }, state.monthsData[lastCurrent], true, true);
+      } else {
+        chooseDay({ day: state.defaultData[2], type: 'curr' }, state.monthsData[state.currentIndex], true);
+      }
+
+      let lastItem = state.monthsData[state.monthsData.length - 1];
+      let containerHeight = lastItem.cssHeight + lastItem.cssScrollHeight;
+      if (months?.value && monthsPanel?.value && viewArea?.value) {
+        viewHeight.value = months.value.clientHeight;
+        monthsPanel.value.style.height = `${containerHeight}px`;
+
+        state.scrollTop = state.monthsData[state.currentIndex].cssScrollHeight;
+      }
+      state.avgHeight = Math.floor(containerHeight / (monthsNum + 1));
+    };
+    const setDefaultRange = (monthsNum: number, current: number) => {
+      let rangeArr: any[] = [];
+      if (monthsNum >= 3) {
+        if (current > 0 && current < monthsNum) {
+          rangeArr = [current - 1, current + 3];
+        } else if (current == 0) {
+          rangeArr = [current, current + 4];
+        } else if (current == monthsNum) {
+          rangeArr = [current - 2, current + 2];
+        }
+      } else {
+        rangeArr = [0, monthsNum + 1];
+      }
+      if (JSON.stringify(state.defaultRange) !== JSON.stringify(rangeArr)) {
+        state.defaultRange[0] = rangeArr[0];
+        state.defaultRange[1] = rangeArr[1];
+        state.compConthsDatas = state.monthsData.slice(rangeArr[0], rangeArr[1]);
+      }
+      let defaultScrollTop = state.monthsData[state.defaultRange[0]].cssScrollHeight;
+      state.translateY = defaultScrollTop;
+    };
+    // 区间选择&&当前月&&选中态
+    const isActive = (day: Day, month: MonthInfo) => {
+      return state.isRange && day.type == 'curr' && getClass(day, month) == 'calendar-month-day-active';
+    };
+
+    // 是否有开始提示
+    const isStartTip = (day: Day, month: MonthInfo) => {
+      if (isActive(day, month)) {
+        return isStart(getCurrDate(day, month));
+      } else {
+        return false;
+      }
+    };
+
+    // 是否有结束提示
+    const isEndTip = (day: Day, month: MonthInfo) => {
+      if (state.currDate.length >= 2 && isEnd(getCurrDate(day, month))) {
+        return isActive(day, month);
+      }
+      return false;
+    };
+    // 开始结束时间是否相等
+    const rangeTip = (day: Day, month: MonthInfo) => {
+      if (state.currDate.length >= 2) {
+        return Utils.isEqual(state.currDate[0], state.currDate[1]);
+      }
+    };
+    // 是否有是当前日期
+    const isCurrDay = (month: any, day: string) => {
+      const date = `${month.curData[0]}-${month.curData[1]}-${day}`;
+      return Utils.isEqual(date, Utils.date2Str(new Date()));
+    };
+
+    const mothsViewScroll = (e: any) => {
+      var currentScrollTop = e.target.scrollTop;
+      let current = Math.floor(currentScrollTop / state.avgHeight);
+      if (current == 0) {
+        if (currentScrollTop >= state.monthsData[current + 1].cssScrollHeight) {
+          current += 1;
+        }
+      } else if (current > 0 && current < state.monthsNum - 1) {
+        if (currentScrollTop >= state.monthsData[current + 1].cssScrollHeight) {
+          current += 1;
+        }
+        if (currentScrollTop < state.monthsData[current].cssScrollHeight) {
+          current -= 1;
+        }
+      } else {
+        if (
+          currentScrollTop + viewHeight.value <
+          state.monthsData[current].cssScrollHeight + state.monthsData[current].cssHeight
+        ) {
+          current -= 1;
+        }
+        if (
+          current + 1 <= state.monthsNum &&
+          currentScrollTop + viewHeight.value >=
+            state.monthsData[current + 1].cssScrollHeight + state.monthsData[current + 1].cssHeight
+        ) {
+          current += 1;
+        }
+        if (currentScrollTop < state.monthsData[current - 1].cssScrollHeight) {
+          current -= 1;
+        }
+      }
+
+      if (state.currentIndex !== current) {
+        state.currentIndex = current;
+        setDefaultRange(state.monthsNum, current);
+      }
+      state.yearMonthTitle = state.monthsData[current].title;
+    };
+    // 重新渲染
+    const resetRender = () => {
+      state.chooseData.splice(0);
+      state.monthsData.splice(0);
+      initData();
+    };
+    onMounted(() => {
+      // 初始化数据
+      Taro.getSystemInfo({
+        success(res) {
+          let scale = 2;
+          let screenWidth = res.screenWidth;
+          scale = Number((screenWidth / 750).toFixed(3));
+          scalePx.value = scale;
+          initData();
+        }
+      });
+    });
+
+    //监听 默认值更改
+    watch(
+      () => props.defaultValue,
+      (val) => {
+        if (val) {
+          if (props.poppable) {
+            resetRender();
+          }
+        }
+      }
+    );
+
+    return {
+      weeks,
+      showTopBtn,
+      topInfo,
+      bottomInfo,
+      rangeTip,
+      mothsViewScroll,
+      getClass,
+      isStartTip,
+      isEndTip,
+      chooseDay,
+      isCurrDay,
+      confirm,
+      monthsPanel,
+      months,
+      weeksPanel,
+      viewArea,
+      ...toRefs(state),
+      ...toRefs(props)
+    };
+  }
+});
+</script>

+ 306 - 319
src/packages/__VUE/calendaritem/index.vue

@@ -7,72 +7,52 @@
     }"
   >
     <!-- header -->
-    <view
-      class="nut-calendar-header"
-      :class="{ 'nut-calendar-header-tile': !poppable }"
-    >
-      <template v-if="poppable">
-        <view class="calendar-title">{{ title }}</view>
-        <view class="calendar-curr-month">{{ yearMonthTitle }}</view>
-      </template>
+    <view class="nut-calendar-header" :class="{ 'nut-calendar-header-tile': !poppable }">
+      <view class="calendar-title" v-if="showTitle">{{ title }}</view>
+      <view class="calendar-top-slot" v-if="showTopBtn">
+        <slot name="btn"> </slot>
+      </view>
+      <view class="calendar-curr-month" v-if="showSubTitle">{{ yearMonthTitle }}</view>
       <view class="calendar-weeks" ref="weeksPanel">
-        <view
-          class="calendar-week-item"
-          v-for="(item, index) of weeks"
-          :key="index"
-          >{{ item }}</view
-        >
+        <view class="calendar-week-item" v-for="(item, index) of weeks" :key="index">{{ item }}</view>
       </view>
     </view>
     <!-- content-->
-    <view
-      class="nut-calendar-content"
-      ref="months"
-      @touchstart.stop="touchStart"
-      @touchmove.stop.prevent="touchMove"
-      @touchend.stop="touchEnd"
-    >
+    <view class="nut-calendar-content" ref="months" @scroll="mothsViewScroll">
       <view class="calendar-months-panel" ref="monthsPanel">
-        <view class="calendar-loading-tip">{{
-          !unLoadPrev ? '加载上一个月' : '没有更早月份'
-        }}</view>
-        <view
-          class="calendar-month"
-          v-for="(month, index) of monthsData"
-          :key="index"
-        >
-          <view class="calendar-month-title">{{ month.title }}</view>
-          <view class="calendar-month-con">
-            <view
-              class="calendar-month-item"
-              :class="type === 'range' ? 'month-item-range' : ''"
-            >
-              <template v-for="(day, i) of month.monthData" :key="i">
-                <view
-                  class="calendar-month-day"
-                  :class="getClass(day, month)"
-                  @click="chooseDay(day, month)"
-                >
-                  <view class="calendar-day">{{
-                    day.type == 'curr' ? day.day : ''
-                  }}</view>
-                  <view
-                    class="calendar-curr-tips"
-                    v-if="isCurrDay(month, day.day)"
-                    >今天</view
-                  >
-                  <view
-                    class="calendar-day-tip"
-                    v-if="isStartTip(day, month)"
-                    >{{ '开始' }}</view
-                  >
-                  <view
-                    class="calendar-day-tip"
-                    v-else-if="isEndTip(day, month)"
-                    >{{ '结束' }}</view
-                  >
-                </view>
-              </template>
+        <view class="viewArea" ref="viewArea" :style="{ transform: `translateY(${translateY}px)` }">
+          <view class="calendar-month" v-for="(month, index) of compConthsData" :key="index">
+            <view class="calendar-month-title">{{ month.title }}</view>
+            <view class="calendar-month-con">
+              <view class="calendar-month-item" :class="type === 'range' ? 'month-item-range' : ''">
+                <template v-for="(day, i) of month.monthData" :key="i">
+                  <view class="calendar-month-day" :class="getClass(day, month)" @click="chooseDay(day, month)">
+                    <!-- 日期显示slot -->
+                    <view class="calendar-day">
+                      <slot name="day" :date="day.type == 'curr' ? day : ''">
+                        {{ day.type == 'curr' ? day.day : '' }}
+                      </slot>
+                    </view>
+                    <view class="calendar-curr-tips calendar-curr-tips-top" v-if="topInfo">
+                      <slot name="topInfo" :date="day.type == 'curr' ? day : ''"> </slot>
+                    </view>
+                    <view class="calendar-curr-tips calendar-curr-tips-bottom" v-if="bottomInfo">
+                      <slot name="bottomInfo" :date="day.type == 'curr' ? day : ''"> </slot>
+                    </view>
+                    <view class="calendar-curr-tip-curr" v-if="!bottomInfo && showToday && isCurrDay(month, day.day)">
+                      今天
+                    </view>
+                    <view
+                      class="calendar-day-tip"
+                      :class="{ 'calendar-curr-tips-top': rangeTip(day, month) }"
+                      v-if="isStartTip(day, month)"
+                    >
+                      {{ startText }}
+                    </view>
+                    <view class="calendar-day-tip" v-if="isEndTip(day, month)">{{ endText }}</view>
+                  </view>
+                </template>
+              </view>
             </view>
           </view>
         </view>
@@ -80,12 +60,12 @@
     </view>
     <!-- footer-->
     <view class="nut-calendar-footer" v-if="poppable && !isAutoBackFill">
-      <view class="calendar-confirm-btn" @click="confirm">确定</view>
+      <view class="calendar-confirm-btn" @click="confirm">{{ confirmText }}</view>
     </view>
   </view>
 </template>
 <script lang="ts">
-import { PropType, reactive, ref, watch, toRefs } from 'vue';
+import { PropType, reactive, ref, watch, toRefs, computed } from 'vue';
 import { createComponent } from '../../utils/create';
 const { create } = createComponent('calendar-item');
 import Utils from '../../utils/date';
@@ -93,7 +73,10 @@ import requestAniFrame from '../../utils/raf';
 type InputDate = string | string[];
 interface CalendarState {
   yearMonthTitle: string;
-  currDate: InputDate;
+  currDate: any;
+  propStartDate: string;
+  propEndDate: string;
+  currentIndex: number;
   unLoadPrev: boolean;
   touchParams: any;
   transformY: number;
@@ -107,6 +90,9 @@ interface CalendarState {
   endData: InputDate;
   isRange: boolean;
   timer: number;
+  avgHeight: number;
+  monthsNum: number;
+  defaultRange: any[];
 }
 interface Day {
   day: string | number;
@@ -117,6 +103,8 @@ interface MonthInfo {
   curData: string[] | string;
   title: string;
   monthData: Day[];
+  cssHeight?: Number;
+  cssScrollHeight?: Number;
 }
 
 export default create({
@@ -133,12 +121,36 @@ export default create({
       type: Boolean,
       default: true
     },
+    showTitle: {
+      type: Boolean,
+      default: true
+    },
+    showSubTitle: {
+      type: Boolean,
+      default: true
+    },
+    showToday: {
+      type: Boolean,
+      default: true
+    },
     title: {
       type: String,
       default: '日历选择'
     },
+    confirmText: {
+      type: String,
+      default: '确认'
+    },
+    startText: {
+      type: String,
+      default: '开始'
+    },
+    endText: {
+      type: String,
+      default: '结束'
+    },
     defaultValue: {
-      type: String as PropType<InputDate>,
+      type: [String, Array],
       default: null
     },
     startDate: {
@@ -150,19 +162,35 @@ export default create({
       default: Utils.getDay(365)
     }
   },
-  emits: ['choose', 'update', 'close'],
+  emits: ['choose', 'update', 'close', 'select'],
 
-  setup(props, { emit }) {
+  setup(props, { emit, slots }) {
     const weeks = ref(['日', '一', '二', '三', '四', '五', '六']);
     // element refs
     const months = ref<null | HTMLElement>(null);
     const monthsPanel = ref<null | HTMLElement>(null);
     const weeksPanel = ref<null | HTMLElement>(null);
+    const viewArea = ref<null | HTMLElement>(null);
+    const viewHeight = ref(0);
+    const compConthsData = computed(() => {
+      return state.monthsData.slice(state.defaultRange[0], state.defaultRange[1]);
+    });
+    const showTopBtn = computed(() => {
+      return slots.btn;
+    });
+    const topInfo = computed(() => {
+      return slots.topInfo;
+    });
+    const bottomInfo = computed(() => {
+      return slots.bottomInfo;
+    });
 
-    // state
     const state: CalendarState = reactive({
       yearMonthTitle: '',
+      defaultRange: [],
       currDate: '',
+      propStartDate: '',
+      propEndDate: '',
       unLoadPrev: false,
       touchParams: {
         startY: 0,
@@ -182,35 +210,27 @@ export default create({
       startData: '',
       endData: '',
       isRange: props.type === 'range',
-      timer: 0
+      timer: 0,
+      currentIndex: 0,
+      avgHeight: 0,
+      monthsNum: 0
     });
-
     // 日期转化成数组
     const splitDate = (date: string) => {
       return date.split('-');
     };
-
+    // 判断是否为开始时间
     const isStart = (currDate: string) => {
       return Utils.isEqual(state.currDate[0], currDate);
     };
-
+    // 判断是否为结束时间
     const isEnd = (currDate: string) => {
       return Utils.isEqual(state.currDate[1], currDate);
     };
 
     // 获取当前数据
     const getCurrDate = (day: Day, month: MonthInfo, isRange?: boolean) => {
-      return isRange
-        ? month.curData[3] +
-            '-' +
-            month.curData[4] +
-            '-' +
-            Utils.getNumTwoBit(+day.day)
-        : month.curData[0] +
-            '-' +
-            month.curData[1] +
-            '-' +
-            Utils.getNumTwoBit(+day.day);
+      return month.curData[0] + '-' + month.curData[1] + '-' + Utils.getNumTwoBit(+day.day);
     };
 
     // 获取样式
@@ -218,14 +238,13 @@ export default create({
       const currDate = getCurrDate(day, month, isRange);
       if (day.type == 'curr') {
         if (
-          (!state.isRange &&
-            Utils.isEqual(state.currDate as string, currDate)) ||
+          (!state.isRange && Utils.isEqual(state.currDate as string, currDate)) ||
           (state.isRange && (isStart(currDate) || isEnd(currDate)))
         ) {
           return `${state.dayPrefix}-active`;
         } else if (
-          (props.startDate && Utils.compareDate(currDate, props.startDate)) ||
-          (props.endDate && Utils.compareDate(props.endDate, currDate))
+          (state.propStartDate && Utils.compareDate(currDate, state.propStartDate)) ||
+          (state.propEndDate && Utils.compareDate(state.propEndDate, currDate))
         ) {
           return `${state.dayPrefix}-disabled`;
         } else if (
@@ -243,7 +262,7 @@ export default create({
         return `${state.dayPrefix}-disabled`;
       }
     };
-
+    // 确认选择时触发
     const confirm = () => {
       if ((state.isRange && state.chooseData.length == 2) || !state.isRange) {
         emit('choose', state.chooseData);
@@ -254,17 +273,10 @@ export default create({
     };
 
     // 选中数据
-    const chooseDay = (
-      day: Day,
-      month: MonthInfo,
-      isFirst: boolean,
-      isRange?: boolean
-    ) => {
+    const chooseDay = (day: Day, month: MonthInfo, isFirst: boolean, isRange?: boolean) => {
       if (getClass(day, month, isRange) != `${state.dayPrefix}-disabled`) {
         let days = [...month.curData];
-        days = isRange ? days.splice(3) : days.splice(0, 3);
-        days[2] =
-          typeof day.day == 'number' ? Utils.getNumTwoBit(day.day) : day.day;
+        days[2] = typeof day.day == 'number' ? Utils.getNumTwoBit(day.day) : day.day;
         days[3] = `${days[0]}-${days[1]}-${days[2]}`;
         days[4] = Utils.getWhatDay(+days[0], +days[1], +days[2]);
         if (!state.isRange) {
@@ -290,19 +302,19 @@ export default create({
             }
           }
         }
-
-        if (props.isAutoBackFill && !isFirst) {
-          confirm();
+        if (!isFirst) {
+          // 点击日期 触发
+          emit('select', state.chooseData);
+          if (props.isAutoBackFill) {
+            confirm();
+          }
         }
       }
     };
 
     // 获取当前月数据
     const getCurrData = (type: string) => {
-      const monthData =
-        type == 'prev'
-          ? state.monthsData[0]
-          : state.monthsData[state.monthsData.length - 1];
+      const monthData = type == 'prev' ? state.monthsData[0] : state.monthsData[state.monthsData.length - 1];
       let year = parseInt(monthData.curData[0]);
       let month = parseInt(monthData.curData[1].toString().replace(/^0/, ''));
       switch (type) {
@@ -315,31 +327,57 @@ export default create({
           month = month == 12 ? 1 : ++month;
           break;
       }
-      return [
-        year,
-        Utils.getNumTwoBit(month),
-        Utils.getMonthDays(String(year), String(month))
-      ];
+      return [year, Utils.getNumTwoBit(month), Utils.getMonthDays(String(year), String(month))];
     };
 
     // 获取日期状态
-    const getDaysStatus = (days: number, type: string) => {
+    const getDaysStatus = (days: number, type: string, dateInfo: any) => {
       // 修复:当某个月的1号是周日时,月份下方会空出来一行
+      let { year, month } = dateInfo;
       if (type == 'prev' && days >= 7) {
         days -= 7;
       }
       return Array.from(Array(days), (v, k) => {
         return {
           day: k + 1,
-          type: type
+          type: type,
+          year,
+          month
         };
       });
     };
-
+    // 获取上一个月的最后一周天数,填充当月空白
+    const getPreDaysStatus = (days: number, type: string, dateInfo: any, preCurrMonthDays: number) => {
+      // 修复:当某个月的1号是周日时,月份下方会空出来一行
+      let { year, month } = dateInfo;
+      if (type == 'prev' && days >= 7) {
+        days -= 7;
+      }
+      let months = Array.from(Array(preCurrMonthDays), (v, k) => {
+        return {
+          day: k + 1,
+          type: type,
+          year,
+          month
+        };
+      });
+      return months.slice(preCurrMonthDays - days);
+    };
     // 获取月数据
-    const getMonth = (curData: string[], type: string) => {
+    const getMonth = (curData: any[], type: string) => {
+      // 一号为周几
       const preMonthDays = Utils.getMonthPreDay(+curData[0], +curData[1]);
+
+      let preMonth = curData[1] - 1;
+      let preYear = curData[0];
+      if (preMonth <= 0) {
+        preMonth = 12;
+        preYear += 1;
+      }
+      //当月天数与上个月天数
       const currMonthDays = Utils.getMonthDays(curData[0], curData[1]);
+      const preCurrMonthDays = Utils.getMonthDays(preYear + '', preMonth + '');
+
       const title = {
         year: curData[0],
         month: curData[1]
@@ -348,24 +386,32 @@ export default create({
         curData: curData,
         title: `${title.year}年${title.month}月`,
         monthData: [
-          ...(getDaysStatus(preMonthDays, 'prev') as Day[]),
-          ...(getDaysStatus(currMonthDays, 'curr') as Day[])
+          ...(getPreDaysStatus(preMonthDays, 'prev', { month: preMonth, year: preYear }, preCurrMonthDays) as Day[]),
+          ...(getDaysStatus(currMonthDays, 'curr', title) as Day[])
         ]
       };
+      monthInfo.cssHeight = 39 + (monthInfo.monthData.length > 35 ? 384 : 320);
+      let cssScrollHeight = 0;
+
+      if (state.monthsData.length > 0) {
+        cssScrollHeight =
+          state.monthsData[state.monthsData.length - 1].cssScrollHeight +
+          state.monthsData[state.monthsData.length - 1].cssHeight;
+      }
+      monthInfo.cssScrollHeight = cssScrollHeight;
       if (type == 'next') {
+        // 判断当前日期 是否大于 最后一天
         if (
           !state.endData ||
           !Utils.compareDate(
-            `${state.endData[0]}-${state.endData[1]}-${Utils.getMonthDays(
-              state.endData[0],
-              state.endData[1]
-            )}`,
+            `${state.endData[0]}-${state.endData[1]}-${Utils.getMonthDays(state.endData[0], state.endData[1])}`,
             `${curData[0]}-${curData[1]}-${curData[2]}`
           )
         ) {
           state.monthsData.push(monthInfo);
         }
       } else {
+        // 判断当前日期 是否小于 第一天
         if (
           !state.startData ||
           !Utils.compareDate(
@@ -383,90 +429,117 @@ export default create({
     // 初始化数据
     const initData = () => {
       // 初始化开始结束数据
-      state.startData = props.startDate ? splitDate(props.startDate) : '';
-      state.endData = props.endDate ? splitDate(props.endDate) : '';
-
-      // 初始化当前日期
-      if (!props.defaultValue) {
-        state.currDate = state.isRange
-          ? [Utils.date2Str(new Date()), Utils.getDay(1)]
-          : Utils.date2Str(new Date());
+      let propStartDate = props.startDate ? props.startDate : Utils.getDay(0);
+      let propEndDate = props.endDate ? props.endDate : Utils.getDay(365);
+      state.propStartDate = propStartDate;
+      state.propEndDate = propEndDate;
+      state.startData = splitDate(propStartDate);
+      state.endData = splitDate(propEndDate);
+
+      // 根据是否存在默认时间,初始化当前日期,
+      if (!props.defaultValue || !(Array.isArray(props.defaultValue) && props.defaultValue.length > 0)) {
+        state.currDate = state.isRange ? [Utils.date2Str(new Date()), Utils.getDay(1)] : Utils.date2Str(new Date());
       } else {
-        state.currDate = state.isRange
-          ? [...props.defaultValue]
-          : props.defaultValue;
+        state.currDate = state.isRange ? [...props.defaultValue] : props.defaultValue;
+      }
+
+      // 判断时间范围内存在多少个月
+      const startDate = {
+        year: Number(state.startData[0]),
+        month: Number(state.startData[1])
+      };
+      const endDate = {
+        year: Number(state.endData[0]),
+        month: Number(state.endData[1])
+      };
+      let monthsNum = endDate.month - startDate.month;
+      if (endDate.year - startDate.year > 0) {
+        monthsNum = monthsNum + 12 * (endDate.year - startDate.year);
       }
 
-      // 日期转化为数组
+      // 设置月份数据
+      getMonth(state.startData, 'next');
+
+      let i = 1;
+      do {
+        getMonth(getCurrData('next'), 'next');
+      } while (i++ < monthsNum);
+
+      state.monthsNum = monthsNum;
+
+      // 日期转化为数组,限制初始日期。判断时间范围
       if (state.isRange && Array.isArray(state.currDate)) {
-        if (
-          props.startDate &&
-          Utils.compareDate(state.currDate[0], props.startDate)
-        ) {
-          state.currDate.splice(0, 1, props.startDate);
+        if (propStartDate && Utils.compareDate(state.currDate[0], propStartDate)) {
+          state.currDate.splice(0, 1, propStartDate);
         }
-        if (
-          props.endDate &&
-          Utils.compareDate(props.endDate, state.currDate[1])
-        ) {
-          state.currDate.splice(1, 1, props.endDate);
+        if (propEndDate && Utils.compareDate(propEndDate, state.currDate[1])) {
+          state.currDate.splice(1, 1, propEndDate);
         }
-        state.defaultData = [
-          ...splitDate(state.currDate[0]),
-          ...splitDate(state.currDate[1])
-        ];
+        state.defaultData = [...splitDate(state.currDate[0]), ...splitDate(state.currDate[1])];
       } else {
-        if (
-          props.startDate &&
-          Utils.compareDate(state.currDate as string, props.startDate)
-        ) {
-          state.currDate = props.startDate;
-        } else if (
-          props.endDate &&
-          !Utils.compareDate(state.currDate as string, props.endDate)
-        ) {
-          state.currDate = props.endDate;
+        if (propStartDate && Utils.compareDate(state.currDate as string, propStartDate)) {
+          state.currDate = propStartDate;
+        } else if (propEndDate && !Utils.compareDate(state.currDate as string, propEndDate)) {
+          state.currDate = propEndDate;
         }
-
         state.defaultData = [...splitDate(state.currDate as string)];
       }
+      // 设置默认可见区域
+      let current = 0;
+      let lastCurrent = 0;
+      state.monthsData.forEach((item, index) => {
+        if (item.title == `${state.defaultData[0]}年${state.defaultData[1]}月`) {
+          current = index;
+        }
+        if (state.isRange) {
+          if (item.title == `${state.defaultData[3]}年${state.defaultData[4]}月`) {
+            lastCurrent = index;
+          }
+        }
+      });
+      setDefaultRange(monthsNum, current);
+      state.currentIndex = current;
+      state.yearMonthTitle = state.monthsData[state.currentIndex].title;
 
-      getMonth(state.defaultData, 'next');
-      state.yearMonthTitle = state.monthsData[0].title;
-
-      let i = 1;
-      do {
-        getMonth(getCurrData('next'), 'next');
-      } while (i++ < 4);
-
+      // 设置当前选中日期
       if (state.isRange) {
-        chooseDay(
-          { day: state.defaultData[2], type: 'curr' },
-          state.monthsData[0],
-          true
-        );
-        chooseDay(
-          { day: state.defaultData[5], type: 'curr' },
-          state.monthsData[0],
-          true,
-          true
-        );
+        chooseDay({ day: state.defaultData[2], type: 'curr' }, state.monthsData[state.currentIndex], true);
+        chooseDay({ day: state.defaultData[5], type: 'curr' }, state.monthsData[lastCurrent], true, true);
       } else {
-        chooseDay(
-          { day: state.defaultData[2], type: 'curr' },
-          state.monthsData[0],
-          true
-        );
+        chooseDay({ day: state.defaultData[2], type: 'curr' }, state.monthsData[state.currentIndex], true);
       }
-    };
 
+      let lastItem = state.monthsData[state.monthsData.length - 1];
+      let containerHeight = lastItem.cssHeight + lastItem.cssScrollHeight;
+      requestAniFrame(() => {
+        // 初始化 日历位置
+        if (months?.value && monthsPanel?.value && viewArea?.value) {
+          viewHeight.value = months.value.clientHeight;
+          monthsPanel.value.style.height = `${containerHeight}px`;
+          months.value.scrollTop = state.monthsData[state.currentIndex].cssScrollHeight;
+        }
+      });
+      state.avgHeight = Math.floor(containerHeight / (monthsNum + 1));
+    };
+    // 设置当前可见月份
+    const setDefaultRange = (monthsNum: number, current: number) => {
+      if (monthsNum >= 3) {
+        if (current > 0 && current < monthsNum) {
+          state.defaultRange = [current - 1, current + 3];
+        } else if (current == 0) {
+          state.defaultRange = [current, current + 4];
+        } else if (current == monthsNum) {
+          state.defaultRange = [current - 2, current + 2];
+        }
+      } else {
+        state.defaultRange = [0, monthsNum + 2];
+      }
+      let defaultScrollTop = state.monthsData[state.defaultRange[0]].cssScrollHeight;
+      state.translateY = defaultScrollTop;
+    };
     // 区间选择&&当前月&&选中态
     const isActive = (day: Day, month: MonthInfo) => {
-      return (
-        state.isRange &&
-        day.type == 'curr' &&
-        getClass(day, month) == 'calendar-month-day-active'
-      );
+      return state.isRange && day.type == 'curr' && getClass(day, month) == 'calendar-month-day-active';
     };
 
     // 是否有开始提示
@@ -480,179 +553,92 @@ export default create({
 
     // 是否有结束提示
     const isEndTip = (day: Day, month: MonthInfo) => {
-      return isActive(day, month);
+      if (state.currDate.length >= 2 && isEnd(getCurrDate(day, month))) {
+        return isActive(day, month);
+      }
+      return false;
     };
-
-    // 是否有是当前日期
+    // 开始结束时间是否相等
+    const rangeTip = (day: Day, month: MonthInfo) => {
+      if (state.currDate.length >= 2) {
+        return Utils.isEqual(state.currDate[0], state.currDate[1]);
+      }
+    };
+    // 是否有 当前日期
     const isCurrDay = (month: any, day: string) => {
       const date = `${month.curData[0]}-${month.curData[1]}-${day}`;
       return Utils.isEqual(date, Utils.date2Str(new Date()));
     };
-
-    // 监听月份滚动,改变月份标题
-    const loadScroll = () => {
-      if (!props.poppable) {
-        return false;
-      }
-      requestAniFrame(() => {
-        if (weeksPanel?.value && monthsPanel?.value) {
-          const top = weeksPanel?.value.getBoundingClientRect().bottom;
-          const monthsDoms =
-            monthsPanel.value.getElementsByClassName('calendar-month');
-          for (let i = 0; i < monthsDoms.length; i++) {
-            if (
-              monthsDoms[i].getBoundingClientRect().top <= top &&
-              monthsDoms[i].getBoundingClientRect().bottom >= top
-            ) {
-              state.yearMonthTitle = state.monthsData[i].title;
-            } else if (state.scrollDistance === 0) {
-              state.yearMonthTitle = state.monthsData[0].title;
-            }
-          }
-        }
-      });
-    };
-
-    // 设置月份滚动距离和速度
-    const setTransform = (translateY = 0, type?: string, time = 1000) => {
-      if (monthsPanel?.value) {
-        if (type === 'end') {
-          monthsPanel.value.style.webkitTransition = `transform ${time}ms cubic-bezier(0.19, 1, 0.22, 1)`;
-          clearTimeout(state.timer);
-          state.timer = setTimeout(() => {
-            loadScroll();
-          }, time);
-        } else {
-          monthsPanel.value.style.webkitTransition = '';
-          loadScroll();
+    // 滚动处理事件
+    const mothsViewScroll = (e: any) => {
+      var currentScrollTop = e.target.scrollTop;
+      let current = Math.floor(currentScrollTop / state.avgHeight);
+      if (current == 0) {
+        if (currentScrollTop >= state.monthsData[current + 1].cssScrollHeight) {
+          current += 1;
         }
-        monthsPanel.value.style.webkitTransform = `translateY(${translateY}px)`;
-        state.scrollDistance = translateY;
-      }
-    };
-
-    // 计算滚动方向和距离
-    const setMove = (move: number, type?: string, time?: number) => {
-      let updateMove = move + state.transformY;
-      const h = months.value?.offsetHeight || 0;
-      const offsetHeight = monthsPanel.value?.offsetHeight || 0;
-      if (type === 'end') {
-        // 限定滚动距离
-        if (updateMove > 0) {
-          updateMove = 0;
+      } else if (current > 0 && current < state.monthsNum - 1) {
+        if (currentScrollTop >= state.monthsData[current + 1].cssScrollHeight) {
+          current += 1;
         }
-        if (updateMove < 0 && updateMove < -offsetHeight + h) {
-          updateMove = -offsetHeight + h;
+        if (currentScrollTop < state.monthsData[current].cssScrollHeight) {
+          current -= 1;
         }
-        if (offsetHeight <= h && state.monthsData.length == 1) {
-          updateMove = 0;
-        }
-        setTransform(updateMove, type, time);
       } else {
-        if (updateMove > 0 && updateMove > 100) {
-          updateMove = 100;
-        }
         if (
-          updateMove < -offsetHeight + h - 100 &&
-          state.monthsData.length > 1
+          currentScrollTop + viewHeight.value <
+          state.monthsData[current].cssScrollHeight + state.monthsData[current].cssHeight
         ) {
-          updateMove = -offsetHeight + h - 100;
+          current -= 1;
         }
         if (
-          updateMove < 0 &&
-          updateMove < -100 &&
-          state.monthsData.length == 1
+          current + 1 <= state.monthsNum &&
+          currentScrollTop + viewHeight.value >=
+            state.monthsData[current + 1].cssScrollHeight + state.monthsData[current + 1].cssHeight
         ) {
-          updateMove = -100;
+          current += 1;
         }
-        setTransform(updateMove);
-      }
-    };
-
-    // 监听touch开始
-    const touchStart = (event: TouchEvent) => {
-      const changedTouches = event.changedTouches[0];
-      state.touchParams.startY = changedTouches.pageY;
-      state.touchParams.startTime = event.timeStamp || Date.now();
-      state.transformY = state.scrollDistance;
-    };
-
-    // 监听touchmove
-    const touchMove = (event: TouchEvent) => {
-      //event.preventDefault();
-      const changedTouches = event.changedTouches[0];
-      state.touchParams.lastY = changedTouches.pageY;
-      state.touchParams.lastTime = event.timeStamp || Date.now();
-      const move = state.touchParams.lastY - state.touchParams.startY;
-      if (Math.abs(move) < 5) {
-        return false;
-      }
-      setMove(move);
-    };
-
-    // 监听touchend
-    const touchEnd = (event: TouchEvent) => {
-      const changedTouches = event.changedTouches[0];
-      state.touchParams.lastY = changedTouches.pageY;
-      state.touchParams.lastTime = event.timeStamp || Date.now();
-      let move = state.touchParams.lastY - state.touchParams.startY;
-      if (Math.abs(move) < 5) {
-        return false;
-      }
-      const updateMove = move + state.transformY;
-      const h = months.value?.offsetHeight || 0;
-      const offsetHeight = monthsPanel.value?.offsetHeight || 0;
-      if (updateMove > 0) {
-        getMonth(getCurrData('prev'), 'prev');
-      } else if (
-        updateMove < 0 &&
-        updateMove <
-          -offsetHeight + (Math.abs(move) > h ? Math.abs(move) : h) * 5
-      ) {
-        getMonth(getCurrData('next'), 'next');
-        if (Math.abs(move) >= 300) {
-          getMonth(getCurrData('next'), 'next');
+        if (currentScrollTop < state.monthsData[current - 1].cssScrollHeight) {
+          current -= 1;
         }
       }
-
-      let moveTime = state.touchParams.lastTime - state.touchParams.startTime;
-      if (moveTime <= 300) {
-        move = move * 2;
-        moveTime = moveTime + 1000;
-        setMove(move, 'end', moveTime);
-      } else {
-        setMove(move, 'end');
+      if (state.currentIndex !== current) {
+        state.currentIndex = current;
+        setDefaultRange(state.monthsNum, current);
       }
+      state.yearMonthTitle = state.monthsData[current].title;
     };
 
     // 重新渲染
     const resetRender = () => {
       state.chooseData.splice(0);
       state.monthsData.splice(0);
-      state.scrollDistance = 0;
-      state.translateY = 0;
-      setTransform(state.scrollDistance);
       initData();
     };
 
     // 初始化数据
     initData();
 
-    //监听 默认值更改
+    // //监听 默认值更改
     watch(
       () => props.defaultValue,
       (val) => {
         if (val) {
-          resetRender();
+          if (props.poppable) {
+            resetRender();
+          }
         }
       }
     );
 
     return {
       weeks,
-      touchStart,
-      touchMove,
-      touchEnd,
+      compConthsData,
+      showTopBtn,
+      topInfo,
+      bottomInfo,
+      rangeTip,
+      mothsViewScroll,
       getClass,
       isStartTip,
       isEndTip,
@@ -662,6 +648,7 @@ export default create({
       monthsPanel,
       months,
       weeksPanel,
+      viewArea,
       ...toRefs(state),
       ...toRefs(props)
     };

+ 1 - 1
src/packages/styles/variables.scss

@@ -193,7 +193,7 @@ $calendar-disable-color: #d1d0d0 !default;
 $calendar-title-font: $font-size-4 !default;
 $calendar-base-font: $font-size-3 !default;
 $calendar-text-font: $font-size-1 !default;
-$calendar-day-font: 18px !default;
+$calendar-day-font: 16px !default;
 
 //overlay
 $overlay-bg-color: rgba(0, 0, 0, 0.7) !default;

+ 48 - 48
src/sites/mobile-taro/vue/project.config.json

@@ -1,51 +1,51 @@
 {
-  "miniprogramRoot": "dist/",
-  "projectname": "%40nutui%2Fnutui-taro-mobile",
-  "description": "nutui-taro-vue",
-  "appid": "wxf2b976b67dab3882",
-  "setting": {
-    "urlCheck": true,
-    "es6": false,
-    "enhance": true,
-    "postcss": true,
-    "preloadBackgroundData": false,
-    "minified": true,
-    "newFeature": false,
-    "coverView": true,
-    "nodeModules": false,
-    "autoAudits": false,
-    "showShadowRootInWxmlPanel": true,
-    "scopeDataCheck": false,
-    "uglifyFileName": false,
-    "checkInvalidKey": true,
-    "checkSiteMap": true,
-    "uploadWithSourceMap": true,
-    "compileHotReLoad": false,
-    "lazyloadPlaceholderEnable": false,
-    "useMultiFrameRuntime": true,
-    "useApiHook": true,
-    "useApiHostProcess": true,
-    "babelSetting": {
-      "ignore": [],
-      "disablePlugins": [],
-      "outputPath": ""
+    "miniprogramRoot": "dist/",
+    "projectname": "%40nutui%2Fnutui-taro-mobile",
+    "description": "nutui-taro-vue",
+    "appid": "wx7a1156d6d5c48a20",
+    "setting": {
+        "urlCheck": true,
+        "es6": false,
+        "enhance": true,
+        "postcss": true,
+        "preloadBackgroundData": false,
+        "minified": true,
+        "newFeature": false,
+        "coverView": true,
+        "nodeModules": false,
+        "autoAudits": false,
+        "showShadowRootInWxmlPanel": true,
+        "scopeDataCheck": false,
+        "uglifyFileName": false,
+        "checkInvalidKey": true,
+        "checkSiteMap": true,
+        "uploadWithSourceMap": true,
+        "compileHotReLoad": false,
+        "lazyloadPlaceholderEnable": false,
+        "useMultiFrameRuntime": true,
+        "useApiHook": true,
+        "useApiHostProcess": true,
+        "babelSetting": {
+            "ignore": [],
+            "disablePlugins": [],
+            "outputPath": ""
+        },
+        "enableEngineNative": false,
+        "useIsolateContext": false,
+        "userConfirmedBundleSwitch": false,
+        "packNpmManually": false,
+        "packNpmRelationList": [],
+        "minifyWXSS": true,
+        "disableUseStrict": false,
+        "minifyWXML": true,
+        "showES6CompileOption": false,
+        "useCompilerPlugins": false
     },
-    "enableEngineNative": false,
-    "useIsolateContext": false,
-    "userConfirmedBundleSwitch": false,
-    "packNpmManually": false,
-    "packNpmRelationList": [],
-    "minifyWXSS": true,
-    "disableUseStrict": false,
-    "minifyWXML": true,
-    "showES6CompileOption": false,
-    "useCompilerPlugins": false
-  },
-  "compileType": "miniprogram",
-  "simulatorType": "wechat",
-  "simulatorPluginLibVersion": {},
-  "libVersion": "2.17.3",
-  "condition": {
-    "miniprogram": {}
-  }
+    "compileType": "miniprogram",
+    "simulatorType": "wechat",
+    "simulatorPluginLibVersion": {},
+    "libVersion": "2.17.3",
+    "condition": {
+        "miniprogram": {}
+    }
 }

+ 161 - 40
src/sites/mobile-taro/vue/src/dentry/pages/calendar/index.vue

@@ -14,7 +14,7 @@
         :default-value="date"
         @close="closeSwitch('isVisible')"
         @choose="setChooseValue"
-        :start-date="`2019-10-11`"
+        :start-date="`2022-01-11`"
         :end-date="`2022-11-11`"
       >
       </nut-calendar>
@@ -25,7 +25,7 @@
         :show-icon="true"
         title="选择日期区间"
         :desc="date1 ? `${date1[0]}至${date1[1]}` : '请选择'"
-        @click="openSwitch1('isVisible1')"
+        @click="openSwitch('isVisible1')"
       >
       </nut-cell>
       <nut-calendar
@@ -34,24 +34,25 @@
         type="range"
         :start-date="`2019-12-22`"
         :end-date="`2021-01-08`"
-        @close="closeSwitch1('isVisible1')"
+        @close="closeSwitch('isVisible1')"
         @choose="setChooseValue1"
+        @select="select"
       >
       </nut-calendar>
     </div>
 
-    <h2>自定义日历-自动回填</h2>
+    <h2>快捷选择</h2>
     <div>
       <nut-cell
         :show-icon="true"
-        title="选择日期"
+        title="选择单个日期"
         :desc="date3 ? date3 : '请选择'"
-        @click="openSwitch3('isVisible3')"
+        @click="openSwitch('isVisible3')"
       >
       </nut-cell>
       <nut-calendar
         v-model:visible="isVisible3"
-        @close="closeSwitch3('isVisible3')"
+        @close="closeSwitch('isVisible3')"
         @choose="setChooseValue3"
         :default-value="date3"
         :start-date="null"
@@ -60,32 +61,112 @@
       >
       </nut-calendar>
     </div>
-
-    <h2>平铺展示</h2>
-    <div class="test-calendar-wrapper">
+    <div>
+      <nut-cell
+        :show-icon="true"
+        title="选择日期范围"
+        @click="openSwitch('isVisible4')"
+        :desc="date4 ? `${date4[0]}至${date4[1]}` : '请选择'"
+      >
+      </nut-cell>
       <nut-calendar
-        :poppable="false"
-        :default-value="date2"
+        v-model:visible="isVisible4"
+        :default-value="date4"
+        type="range"
+        :start-date="`2022-01-01`"
+        :end-date="`2022-12-31`"
+        @close="closeSwitch('isVisible4')"
+        @choose="setChooseValue4"
         :is-auto-back-fill="true"
-        @choose="setChooseValue2"
       >
       </nut-calendar>
     </div>
+    <h2>自定义日历</h2>
+    <div>
+      <nut-cell
+        :show-icon="true"
+        title="自定义按钮"
+        :desc="date5 && date5[0] ? `${date5[0]}至${date5[1]}` : '请选择'"
+        @click="openSwitch('isVisible5')"
+      >
+      </nut-cell>
+      <nut-calendar
+        v-model:visible="isVisible5"
+        :default-value="date5"
+        type="range"
+        :start-date="`2021-12-22`"
+        :end-date="`2022-12-31`"
+        @close="closeSwitch('isVisible5')"
+        @choose="setChooseValue5"
+      >
+        <template v-slot:btn>
+          <view class="wrapper">
+            <view class="d_div"> <span class="d_btn" @click="clickBtn">最近七天</span></view>
+            <view class="d_div"> <span class="d_btn" @click="clickBtn1">当月</span></view>
+          </view>
+        </template>
+        <template v-slot:day="date">
+          <span>{{ date.date.day }}</span>
+        </template>
+      </nut-calendar>
+    </div>
+    <div>
+      <nut-cell
+        :show-icon="true"
+        title="自定义时间文案"
+        :desc="date6 && date6[0] ? `${date6[0]}至${date6[1]}` : '请选择'"
+        @click="openSwitch('isVisible6')"
+      >
+      </nut-cell>
+      <nut-calendar
+        v-model:visible="isVisible6"
+        :default-value="date6"
+        type="range"
+        @close="closeSwitch('isVisible6')"
+        @choose="setChooseValue6"
+        :start-date="`2022-02-01`"
+        :end-date="`2022-3-30`"
+        confirm-text="submit"
+        start-text="入店"
+        end-text="离店"
+        title="日期选择"
+      >
+        <template v-slot:day="date">
+          <view>{{ date.date.day <= 9 ? '0' + date.date.day : date.date.day }}</view>
+        </template>
+        <template v-slot:bottomInfo="date">
+          <view class="info">{{ date.date ? (date.date.day == 10 ? '十' : '') : '' }}</view>
+        </template>
+      </nut-calendar>
+    </div>
+    <h2>平铺展示</h2>
+    <div class="test-calendar-wrapper">
+      <nut-calendar :poppable="false" :default-value="date2" :is-auto-back-fill="true" @choose="setChooseValue2">
+      </nut-calendar>
+    </div>
   </div>
 </template>
 
 <script lang="ts">
 import { reactive, toRefs } from 'vue';
+import Utils from '../../../../../../../packages/utils/date';
 
 interface TestCalendarState {
   isVisible: boolean;
   date: string;
   dateWeek: string;
   isVisible1: boolean;
+  isVisible2: boolean;
+  isVisible3: boolean;
+  isVisible4: boolean;
+  isVisible5: boolean;
+  isVisible6: boolean;
   date1: string[];
   date2: string;
-  isVisible3: boolean;
   date3: string;
+  date4: string[];
+  date5: string[];
+  date6: string[];
 }
 export default {
   props: {},
@@ -94,63 +175,83 @@ export default {
       isVisible: false,
       date: '',
       dateWeek: '',
-
-      isVisible1: false,
       date1: ['2019-12-23', '2019-12-26'],
-
       date2: '2020-07-08',
-
+      date3: '',
+      date4: ['2021-12-23', '2021-12-26'],
+      date5: ['2021-12-23', '2021-12-26'],
+      date6: [],
+      isVisible1: false,
+      isVisible2: false,
       isVisible3: false,
-      date3: ''
+      isVisible4: false,
+      isVisible5: false,
+      isVisible6: false
     });
     const openSwitch = (param: string) => {
-      state.isVisible = true;
-    };
-    const openSwitch1 = (param: string) => {
-      state.isVisible1 = true;
-    };
-    const openSwitch3 = (param: string) => {
-      state.isVisible3 = true;
+      state[`${param}`] = true;
     };
+
     const closeSwitch = (param: string) => {
-      state.isVisible = false;
-    };
-    const closeSwitch1 = (param: string) => {
-      state.isVisible1 = false;
-    };
-    const closeSwitch3 = (param: string) => {
-      state.isVisible3 = false;
+      state[`${param}`] = false;
     };
+
     const setChooseValue = (param: string) => {
       state.date = param[3];
       state.dateWeek = param[4];
     };
 
+    const select = (param: string) => {
+      console.log(param);
+    };
     const setChooseValue1 = (param: string) => {
       state.date1 = [...[param[0][3], param[1][3]]];
     };
 
     const setChooseValue2 = (param: string) => {
       state.date2 = param[3];
-      console.log(state.date2);
     };
 
     const setChooseValue3 = (param: string) => {
       state.date3 = param[3];
     };
+    const setChooseValue4 = (param: string) => {
+      state.date4 = [...[param[0][3], param[1][3]]];
+    };
 
+    const setChooseValue5 = (param: string) => {
+      state.date5 = [...[param[0][3], param[1][3]]];
+    };
+    const setChooseValue6 = (param: string) => {
+      state.date6 = [...[param[0][3], param[1][3]]];
+    };
+    const clickBtn = (param: string) => {
+      let date = [Utils.date2Str(new Date()), Utils.getDay(6)];
+      state.date5 = date;
+    };
+    const clickBtn1 = (param: string) => {
+      let date = new Date();
+      let year = date.getFullYear();
+      let month: any = date.getMonth() + 1;
+      month = month < 10 ? '0' + month : month + '';
+      let yearMonth = `${year}-${month}`;
+      let currMonthDays = Utils.getMonthDays(year + '', month + '');
+      state.date5 = [`${yearMonth}-01`, `${yearMonth}-${currMonthDays}`];
+    };
     return {
       ...toRefs(state),
       openSwitch,
-      openSwitch1,
-      openSwitch3,
       closeSwitch,
-      closeSwitch1,
-      closeSwitch3,
       setChooseValue,
       setChooseValue1,
       setChooseValue2,
-      setChooseValue3
+      setChooseValue3,
+      setChooseValue4,
+      setChooseValue5,
+      setChooseValue6,
+      clickBtn,
+      clickBtn1,
+      select
     };
   }
 };
@@ -160,7 +261,27 @@ export default {
 .test-calendar-wrapper {
   display: flex;
   width: 100%;
-  height: 613px;
+  height: 560px;
   overflow: hidden;
 }
+.wrapper {
+  display: flex;
+  padding: 0 40px;
+}
+.d_div {
+  margin: 0px 5px;
+  .d_btn {
+    background: #fa3f19;
+    color: #fff;
+    font-size: 12px;
+    padding: 2px 8px;
+    border-radius: 4px;
+    display: inline-block;
+    height: 16px;
+  }
+}
+.info {
+  font-size: 12px;
+  line-height: 14px;
+}
 </style>