Browse Source

fix(calendar): taro h5 scroll-view compatible (#1718)

lkjh3214 3 years ago
parent
commit
3a4eb623d3

+ 148 - 52
src/packages/__VUE/calendar/index.taro.vue

@@ -1,35 +1,157 @@
 <template>
-  <nut-popup
-    v-if="poppable"
-    v-model:visible="show"
-    position="bottom"
-    round
-    closeable
-    @click-overlay="closePopup"
-    @click-close-icon="closePopup"
-    :style="{ height: '85vh' }"
-  >
+  <template v-if="ENV != ENV_TYPE.WEB">
+    <nut-popup
+      v-if="poppable"
+      v-model:visible="show"
+      position="bottom"
+      round
+      closeable
+      @click-overlay="closePopup"
+      @click-close-icon="closePopup"
+      :style="{ height: '85vh' }"
+    >
+      <nut-calendar-item
+        v-if="show"
+        ref="calendarRef"
+        :type="type"
+        :is-auto-back-fill="isAutoBackFill"
+        :poppable="poppable"
+        :title="title"
+        :default-value="defaultValue"
+        :start-date="startDate"
+        :end-date="endDate"
+        @update="update"
+        @close="close"
+        @choose="choose"
+        @select="select"
+        :confirm-text="confirmText"
+        :start-text="startText"
+        :end-text="endText"
+        :show-today="showToday"
+        :show-title="showTitle"
+        :show-sub-title="showSubTitle"
+        :to-date-animation="toDateAnimation"
+      >
+        <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
-      ref="calendarRef"
+      v-else
       :type="type"
       :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"
-      @update="update"
       @close="close"
       @choose="choose"
       @select="select"
-      v-if="show"
+      :show-title="showTitle"
+      :show-sub-title="showSubTitle"
+      :to-date-animation="toDateAnimation"
+      :show-today="showToday"
+    >
+      <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>
+
+  <template v-if="ENV == ENV_TYPE.WEB">
+    <nut-popup
+      v-if="poppable"
+      :visible="visible"
+      position="bottom"
+      round
+      :closeable="true"
+      @click-overlay="closePopup"
+      @click-close-icon="closePopup"
+      :destroy-on-close="true"
+      :style="{ height: '85vh' }"
+    >
+      <nut-calendar-item-h5
+        v-if="show"
+        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"
+        :start-date="startDate"
+        :end-date="endDate"
+        @update="update"
+        @close="close"
+        @choose="choose"
+        @select="select"
+        :show-today="showToday"
+        :show-title="showTitle"
+        :show-sub-title="showSubTitle"
+        :to-date-animation="toDateAnimation"
+      >
+        <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-h5>
+    </nut-popup>
+    <nut-calendar-item-h5
+      v-else
+      :type="type"
+      :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"
+      @update="update"
+      @close="close"
+      @choose="choose"
+      @select="select"
       :show-today="showToday"
       :show-title="showTitle"
-      :to-date-animation="toDateAnimation"
       :show-sub-title="showSubTitle"
+      :to-date-animation="toDateAnimation"
+      ref="calendarRef"
     >
       <template v-slot:btn v-if="showTopBtn">
         <slot name="btn"> </slot>
@@ -43,54 +165,23 @@
       <template v-slot:bottomInfo="date" v-if="bottomInfo">
         <slot name="bottomInfo" :date="date.date"> </slot>
       </template>
-    </nut-calendar-item>
-  </nut-popup>
-
-  <nut-calendar-item
-    v-else
-    :type="type"
-    :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"
-    :to-date-animation="toDateAnimation"
-    :show-today="showToday"
-  >
-    <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-calendar-item-h5>
+  </template>
 </template>
 <script lang="ts">
-import { ref, watch, computed } from 'vue';
+import { ref, watch, computed, reactive, toRefs } from 'vue';
 import { createComponent } from '@/packages/utils/create';
 const { create } = createComponent('calendar');
 import CalendarItem from '../calendaritem/index.taro.vue';
+import CalendarItem2 from '../calendaritem/index.h5.vue';
 import Utils from '@/packages/utils/date';
 import { useExpose } from '@/packages/utils/useExpose/index';
+import Taro from '@tarojs/taro';
 
 export default create({
   components: {
-    [CalendarItem.name]: CalendarItem
+    [CalendarItem.name]: CalendarItem,
+    NutCalendarItemH5: CalendarItem2
   },
   props: {
     type: {
@@ -155,6 +246,10 @@ export default create({
   },
   emits: ['choose', 'close', 'update:visible', 'select'],
   setup(props, { emit, slots }) {
+    const state = reactive({
+      ENV: Taro.getEnv(),
+      ENV_TYPE: Taro.ENV_TYPE
+    });
     const showTopBtn = computed(() => {
       return slots.btn;
     });
@@ -208,6 +303,7 @@ export default create({
     );
 
     return {
+      ...toRefs(state),
       show,
       closePopup,
       update,

+ 835 - 0
src/packages/__VUE/calendaritem/index.h5.vue

@@ -0,0 +1,835 @@
+<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 || translate('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-->
+    <view 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 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(day)">
+                      {{ translate('today') }}</view
+                    >
+                    <view
+                      class="calendar-day-tip"
+                      :class="{ 'calendar-curr-tips-top': rangeTip() }"
+                      v-if="isStartTip(day, month)"
+                    >
+                      {{ startText || translate('start') }}
+                    </view>
+                    <view class="calendar-day-tip" v-if="isEndTip(day, month)">{{ endText || translate('end') }}</view>
+                  </view>
+                </template>
+              </view>
+            </view>
+          </view>
+        </view>
+      </view>
+    </view>
+    <!-- footer-->
+    <view class="nut-calendar-footer" v-if="poppable && !isAutoBackFill">
+      <view class="calendar-confirm-btn" @click="confirm">{{ confirmText || translate('confirm') }}</view>
+    </view>
+  </view>
+</template>
+<script lang="ts">
+import { reactive, ref, watch, toRefs, computed, PropType, onMounted } from 'vue';
+import { createComponent } from '@/packages/utils/create';
+const { create, translate } = createComponent('calendar-item');
+import Utils from '@/packages/utils/date';
+import requestAniFrame from '@/packages/utils/raf';
+import { useExpose } from '@/packages/utils/useExpose/index';
+import Taro from '@tarojs/taro';
+const TARO_ENV = Taro.getEnv();
+
+type InputDate = string | string[];
+type StringArr = string[];
+interface TouchParam {
+  startY: number;
+  endY: number;
+  startTime: number;
+  endTime: number;
+  lastY: number;
+  lastTime: number;
+}
+
+interface CalendarState {
+  yearMonthTitle: string;
+  currDate: string | string[];
+  propStartDate: string;
+  propEndDate: string;
+  currentIndex: number;
+  unLoadPrev: boolean;
+  touchParams: TouchParam;
+  transformY: number;
+  translateY: number;
+  scrollDistance: number;
+  defaultData: InputDate;
+  chooseData: (string | string[])[];
+  monthsData: MonthInfo[];
+  dayPrefix: string;
+  startData: InputDate;
+  endData: InputDate;
+  isRange: boolean;
+  timer: number;
+  avgHeight: number;
+  monthsNum: number;
+  defaultRange: number[];
+}
+interface Day {
+  day: string | number;
+  type: string;
+}
+
+interface MonthInfo {
+  curData: string[] | string;
+  title: string;
+  monthData: Day[];
+  cssHeight: number;
+  cssScrollHeight: number;
+}
+interface Dateprop {
+  year: string;
+  month: string;
+}
+interface DateInfo {
+  year: string;
+  month: string;
+  day: string;
+}
+
+export default create({
+  props: {
+    type: {
+      type: String,
+      default: 'one'
+    },
+    isAutoBackFill: {
+      type: Boolean,
+      default: false
+    },
+    toDateAnimation: {
+      type: Boolean,
+      default: true
+    },
+    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] as PropType<string>,
+      default: ''
+    },
+    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(translate('weekdays'));
+    // element refs
+    const scalePx = ref(2);
+    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;
+    });
+
+    const state: CalendarState = reactive({
+      yearMonthTitle: '',
+      defaultRange: [],
+      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,
+      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 isMultiple = (currDate: string) => {
+      if (state.currDate.length > 0) {
+        return (state.currDate as StringArr).some((item: string) => {
+          return Utils.isEqual(item, currDate);
+        });
+      } else {
+        return false;
+      }
+    };
+    // 获取当前数据
+    const getCurrDate = (day: Day, month: MonthInfo) => {
+      return month.curData[0] + '-' + month.curData[1] + '-' + Utils.getNumTwoBit(+day.day);
+    };
+
+    // 获取样式
+    const getClass = (day: Day, month: MonthInfo) => {
+      const currDate = getCurrDate(day, month);
+      const { type } = props;
+      if (day.type == 'curr') {
+        if (
+          Utils.isEqual(state.currDate as string, currDate) ||
+          (type == 'range' && (isStart(currDate) || isEnd(currDate))) ||
+          (type == 'multiple' && isMultiple(currDate))
+        ) {
+          return `${state.dayPrefix}-active`;
+        } else if (
+          (state.propStartDate && Utils.compareDate(currDate, state.propStartDate)) ||
+          (state.propEndDate && Utils.compareDate(state.propEndDate, currDate))
+        ) {
+          return `${state.dayPrefix}-disabled`;
+        } else if (
+          type == 'range' &&
+          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 = () => {
+      const { type } = props;
+      if ((type == 'range' && state.chooseData.length == 2) || type != 'range') {
+        let chooseData = state.chooseData.slice(0);
+        emit('choose', chooseData);
+        if (props.poppable) {
+          emit('update');
+        }
+      }
+    };
+
+    // 选中数据
+    const chooseDay = (day: Day, month: MonthInfo, isFirst: boolean) => {
+      if (getClass(day, month) != `${state.dayPrefix}-disabled`) {
+        const { type } = props;
+        let days = [...month.curData];
+        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 (type == 'multiple') {
+          if (state.currDate.length > 0) {
+            let hasIndex: number | undefined = undefined;
+            (state.currDate as StringArr).forEach((item: string, index: number) => {
+              if (item == days[3]) {
+                hasIndex = index;
+              }
+            });
+            if (isFirst) {
+              state.chooseData.push([...days]);
+            } else {
+              if (hasIndex !== undefined) {
+                (state.currDate as StringArr).splice(hasIndex, 1);
+                state.chooseData.splice(hasIndex, 1);
+              } else {
+                (state.currDate as StringArr).push(days[3]);
+                state.chooseData.push([...days]);
+              }
+            }
+          } else {
+            state.currDate = [days[3]];
+            state.chooseData = [[...days]];
+          }
+        } else if (type == 'range') {
+          let curDataLength = Object.values(state.currDate).length;
+          if (curDataLength == 2 || curDataLength == 0) {
+            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[0][3], days[3])) {
+              state.chooseData = [...state.chooseData, [...days]];
+            } else {
+              state.chooseData = [[...days], ...state.chooseData];
+            }
+          }
+        } else {
+          state.currDate = days[3];
+          state.chooseData = [...days];
+        }
+        if (!isFirst) {
+          // 点击日期 触发
+          emit('select', state.chooseData);
+          if (props.isAutoBackFill || !props.poppable) {
+            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: Dateprop) => {
+      // 修复:当某个月的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: Dateprop, 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 preMonthDays = Utils.getMonthPreDay(+curData[0], +curData[1]);
+
+      let preMonth = Number(curData[1]) - 1;
+      let preYear = Number(curData[0]);
+      if (preMonth <= 0) {
+        preMonth = 12;
+        preYear += 1;
+      }
+      //当月天数与上个月天数
+      const currMonthDays = Utils.getMonthDays(String(curData[0]), String(curData[1]));
+      const preCurrMonthDays = Utils.getMonthDays(preYear + '', preMonth + '');
+
+      const title = {
+        year: curData[0],
+        month: curData[1]
+      };
+      const monthInfo: MonthInfo = {
+        curData: curData,
+        title: translate('monthTitle', title.year, title.month),
+        monthData: [
+          ...(getPreDaysStatus(
+            preMonthDays,
+            'prev',
+            { month: preMonth + '', year: preYear + '' },
+            preCurrMonthDays
+          ) as Day[]),
+          ...(getDaysStatus(currMonthDays, 'curr', title) as Day[])
+        ],
+        cssHeight: 0,
+        cssScrollHeight: 0
+      };
+      let titleHeight, itemHeight;
+      if (TARO_ENV === Taro.ENV_TYPE.WEB) {
+        titleHeight = 46 * scalePx.value + 16 * scalePx.value * 2;
+        itemHeight = 128 * scalePx.value;
+      } else {
+        titleHeight = Math.floor(46 * scalePx.value) + Math.floor(16 * scalePx.value) * 2;
+        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 = props.type != 'one' ? [...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);
+      }
+      if (monthsNum <= 0) {
+        monthsNum = 1;
+      }
+      // 设置月份数据
+      getMonth(state.startData, 'next');
+
+      let i = 1;
+      do {
+        getMonth(getCurrData('next'), 'next');
+      } while (i++ < monthsNum);
+      state.monthsNum = monthsNum;
+
+      // 日期转化为数组,限制初始日期。判断时间范围
+      if (props.type == 'range' && Array.isArray(state.currDate)) {
+        if (state.currDate.length > 0) {
+          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 (props.type == 'multiple' && Array.isArray(state.currDate)) {
+        if (state.currDate.length > 0) {
+          let defaultArr: string[] = [];
+          let obj: Record<string, unknown> = {};
+          state.currDate.forEach((item: string) => {
+            if (
+              propStartDate &&
+              !Utils.compareDate(item, propStartDate) &&
+              propEndDate &&
+              !Utils.compareDate(propEndDate, item)
+            ) {
+              if (!Object.hasOwnProperty.call(obj, item)) {
+                defaultArr.push(item);
+                obj[item] = item;
+              }
+            }
+          });
+          state.currDate = [...defaultArr];
+          state.defaultData = [...splitDate(defaultArr[0])];
+        }
+      } else {
+        if (state.currDate) {
+          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;
+      if (state.defaultData.length > 0) {
+        state.monthsData.forEach((item, index) => {
+          if (item.title == translate('monthTitle', state.defaultData[0], state.defaultData[1])) {
+            current = index;
+          }
+          if (props.type == 'range') {
+            if (item.title == translate('monthTitle', state.defaultData[3], state.defaultData[4])) {
+              lastCurrent = index;
+            }
+          }
+        });
+      }
+
+      setDefaultRange(monthsNum, current);
+      state.currentIndex = current;
+      state.yearMonthTitle = state.monthsData[state.currentIndex].title;
+      if (state.defaultData.length > 0) {
+        // 设置当前选中日期
+        if (props.type == 'range') {
+          chooseDay({ day: state.defaultData[2], type: 'curr' }, state.monthsData[state.currentIndex], true);
+          chooseDay({ day: state.defaultData[5], type: 'curr' }, state.monthsData[lastCurrent], true);
+        } else if (props.type == 'multiple') {
+          [...state.currDate].forEach((item: string) => {
+            let dateArr = splitDate(item);
+            let current = state.currentIndex;
+            state.monthsData.forEach((item, index) => {
+              if (item.title == translate('monthTitle', dateArr[0], dateArr[1])) {
+                current = index;
+              }
+            });
+            chooseDay({ day: dateArr[2], type: 'curr' }, state.monthsData[current], 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;
+      requestAniFrame(() => {
+        // 初始化 日历位置
+        if (months?.value && monthsPanel?.value && viewArea?.value) {
+          viewHeight.value = months.value.clientHeight;
+          monthsPanel.value.style.height = `${containerHeight}px`;
+          Taro.nextTick(() => {
+            if (months?.value) {
+              months.value.scrollTop = state.monthsData[state.currentIndex].cssScrollHeight;
+            }
+          });
+        }
+      });
+      state.avgHeight = Math.floor(containerHeight / (monthsNum + 1));
+    };
+    const scrollToDate = (date: string) => {
+      if (Utils.compareDate(date, state.propStartDate)) {
+        date = state.propStartDate;
+      } else if (!Utils.compareDate(date, state.propEndDate)) {
+        date = state.propEndDate;
+      }
+      let dateArr = splitDate(date);
+      state.monthsData.forEach((item, index) => {
+        if (item.title == translate('monthTitle', dateArr[0], dateArr[1])) {
+          if (months.value) {
+            let distance = state.monthsData[index].cssScrollHeight - months.value.scrollTop;
+            if (props.toDateAnimation) {
+              let flag = 0;
+              let interval = setInterval(() => {
+                flag++;
+                if (months.value) {
+                  let offset = distance / 10;
+                  months.value.scrollTop = months.value.scrollTop + offset;
+                }
+
+                if (flag >= 10) {
+                  clearInterval(interval);
+                  if (months.value) {
+                    months.value.scrollTop = state.monthsData[index].cssScrollHeight;
+                  }
+                }
+              }, 40);
+            } else {
+              months.value.scrollTop = state.monthsData[index].cssScrollHeight;
+            }
+          }
+        }
+      });
+    };
+    useExpose({
+      scrollToDate
+    });
+    // 设置当前可见月份
+    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 props.type == 'range' && day.type == 'curr' && getClass(day, month) == 'calendar-month-day-active';
+    };
+
+    // 是否有开始提示
+    const isStartTip = (day: Day, month: MonthInfo) => {
+      return isActive(day, month) && isStart(getCurrDate(day, month));
+    };
+
+    // 是否有结束提示
+    const isEndTip = (day: Day, month: MonthInfo) => {
+      if (state.currDate.length >= 2 && isEnd(getCurrDate(day, month))) {
+        return isActive(day, month);
+      }
+      return false;
+    };
+    // 开始结束时间是否相等
+    const rangeTip = () => {
+      if (state.currDate.length >= 2) {
+        return Utils.isEqual(state.currDate[0], state.currDate[1]);
+      }
+    };
+    // 是否有 当前日期
+    const isCurrDay = (dateInfo: DateInfo) => {
+      const date = `${dateInfo.year}-${dateInfo.month}-${
+        Number(dateInfo.day) < 10 ? '0' + dateInfo.day : dateInfo.day
+      }`;
+      return Utils.isEqual(date, Utils.date2Str(new Date()));
+    };
+    // 滚动处理事件
+    const mothsViewScroll = (e: Event) => {
+      if (state.monthsData.length <= 1) {
+        return;
+      }
+      const currentScrollTop = (e.target as Element).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 {
+        const viewPosition = Math.round(currentScrollTop + viewHeight.value);
+        if (
+          viewPosition < state.monthsData[current].cssScrollHeight + state.monthsData[current].cssHeight &&
+          currentScrollTop > state.monthsData[current - 1].cssScrollHeight
+        ) {
+          current -= 1;
+        }
+        if (
+          current + 1 <= state.monthsNum &&
+          viewPosition >= state.monthsData[current + 1].cssScrollHeight + state.monthsData[current + 1].cssHeight
+        ) {
+          current += 1;
+        }
+        if (current >= 1 && 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;
+          let toFixed = 3;
+          if (TARO_ENV === Taro.ENV_TYPE.WEB) {
+            toFixed = 5;
+          }
+          scale = Number((screenWidth / 750).toFixed(toFixed));
+          scalePx.value = scale;
+          initData();
+        }
+      });
+    });
+    // //监听 默认值更改
+    watch(
+      () => props.defaultValue,
+      (val) => {
+        if (val) {
+          if (props.poppable) {
+            resetRender();
+          }
+        }
+      }
+    );
+
+    return {
+      weeks,
+      compConthsData,
+      showTopBtn,
+      topInfo,
+      bottomInfo,
+      rangeTip,
+      mothsViewScroll,
+      getClass,
+      isStartTip,
+      isEndTip,
+      chooseDay,
+      isCurrDay,
+      confirm,
+      monthsPanel,
+      months,
+      weeksPanel,
+      viewArea,
+      ...toRefs(state),
+      ...toRefs(props),
+      translate
+    };
+  }
+});
+</script>

+ 2 - 2
src/packages/__VUE/calendaritem/index.vue

@@ -319,7 +319,7 @@ export default create({
         days[4] = Utils.getWhatDay(+days[0], +days[1], +days[2]);
         if (type == 'multiple') {
           if (state.currDate.length > 0) {
-            let hasIndex: number = NaN;
+            let hasIndex: number | undefined = undefined;
             (state.currDate as StringArr).forEach((item: string, index: number) => {
               if (item == days[3]) {
                 hasIndex = index;
@@ -328,7 +328,7 @@ export default create({
             if (isFirst) {
               state.chooseData.push([...days]);
             } else {
-              if (hasIndex !== NaN) {
+              if (hasIndex !== undefined) {
                 (state.currDate as StringArr).splice(hasIndex, 1);
                 state.chooseData.splice(hasIndex, 1);
               } else {

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

@@ -202,7 +202,7 @@ export default {
       date1: ['2020-01-23', '2020-01-26'],
       date2: '2020-07-08',
       date3: '',
-      date4: ['2021-12-23', '2021-12-26'],
+      date4: ['2022-02-23', '2022-04-26'],
       date5: ['2021-12-23', '2021-12-26'],
       date6: [],
       date7: [],

+ 2 - 2
src/sites/mobile-taro/vue/src/pages/index/index.vue

@@ -33,7 +33,7 @@
 
 <script lang="ts">
 import { reactive, toRefs } from 'vue';
-import { nav } from '../../../../../../config.json';
+import config from '../../../../../../config.json';
 import Taro from '@tarojs/taro';
 export default {
   name: 'NutUI',
@@ -52,7 +52,7 @@ export default {
   // },
   setup() {
     const state = reactive({
-      nav
+      nav: config.nav
     });
 
     const reorder = (packages: any) => {