1. 程式人生 > 實用技巧 >小程式手寫 的選擇日期 區間

小程式手寫 的選擇日期 區間

參考:https://www.cnblogs.com/bin521/p/10244502.html

專案中實踐

js

// plugin/components/calendar/calendar.js
/**
 * 屬性:
 * 01、year:年份:整型
 * 02、month:月份:整型
 * 03、day:日期:整型
 * 04、startDate:日曆起點:字串[YYYY-MM]
 * 05、endDate:日曆終點:字串[YYYY-MM]
 * 06、header:是否顯示標題:布林型
 * 07、next:是否顯示下個月按鈕:布林型
 * 08、prev:是否顯示上個月按鈕:布林型
 * 09、weeks:是否顯示周標題:布林型
 * 10、showMoreDays:是否顯示上下月份的數字:布林型
 * 11、lunar:是否顯示農曆 布林型
 * 11、weeksType:周標題型別:字串[en、full-en、cn]
 * 12、cellSize: 單元格大小 整型
 * 13、daysColor:設定日期字型、背景顏色
 * 14、activeType: 日期背景效果(正方形、圓形)[rounded, square]
 * 
 * 事件方法:
 * 1、nextMonth:點選下個月
 * 2、prevMonth:點選上個月
 * 3、dateChange: 日期選擇器變化
 * 
 * 樣式:
 * calendar-style 日曆整體樣式
 * header-style 標題樣式
 * board-style 面板樣式
 */

const lunar = require('./lunar.js');
const minYear = 1900;
const maxYear = 2099;

let dateStart, dateEnd;

Component({
  /**
   * 元件的屬性列表
   */
  properties: {
    /**
     * 年份
     */
    year: {
      type: Number,
      value: new Date().getFullYear(),
      observer: '_yearChange'
    },

    /**
     * 月份1~12
     */
    month: {
      type: Number,
      value: new Date().getMonth() + 1,
      observer: '_monthChange'
    },

    /**
     * 日期
     */
    day: {
      type: Number,
      value: new Date().getDate(),
      observer: '_dayChange'
    },

    /**
     * 日曆範圍起點
     */
    startDate: {
      type: String,
      value: '1900-01-01',
      observer: '_setStartDate'
    },

    /**
     * 日曆範圍終點
     */
    endDate: {
      type: String,
      value: '2099-12',
      observer: '_setEndDate'
    },

    /**
     * 是否顯示標題
     */
    header: {
      type: Boolean,
      value: true,
      observer: '_headerChange'
    },

    /**
     * 是否顯示下個月按鈕
     */
    next: {
      type: Boolean,
      value: true
    },

    /**
     * 是否顯示上個月按鈕
     */
    prev: {
      type: Boolean,
      value: true
    },

    /**
     * 顯示額外上下月份日期
     */
    showMoreDays: {
      type: Boolean,
      value: false,
      observer: '_moreChange'
    },

    /**
     * 是否顯示周標題
     */
    weeks: {
      type: Boolean,
      value: true,
      observer: '_showWeeksChange'
    },

    /**
     * 周標題型別
     */
    weeksType: {
      type: String,
      value: 'en',
      observer: '_weeksTypeChange'
    },

    /**
     * 設定日期字型、背景顏色
     */
    daysColor: {
      type: Array,
      value: [],
      observer: '_setDaysColor'
    },

    /**
     * 單元格大小
     */
    cellSize: {
      type: Number,
      value: 30,
      observer: '_setCellSize'
    },

    /**
     * 設定日期背景效果
     */
    activeType: {
      type: String,
      value: 'rounded',
      observer: '_setActiveType'
    },

    /**
     * 是否顯示農曆
     */
    lunar: {
      type: Boolean,
      value: false,
      observer: '_showLunar'
    },

    /**
     * 額外選項
     */
    addon: {
      type: String,
      value: 'none',
      observer: '_setAddon'
    },

    /**
     * 日期附加選項
     */
    daysAddon: {
      type: Array,
      value: [],
      observer: '_setDaysAddon'
    },

    moreDays: {
      type: Boolean,
      value: false,
      observer: '_setMoreDays'
    }
  },

  /**
   * 元件的初始資料
   */
  data: {
    days_array: [], // 日期陣列
    days_color: [], // 日期字型、背景顏色陣列
    days_addon: [], // 日期附件
    weekTitle: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    max_year: 2099, // 最大年份
    max_month: 12, // 最大月份
    max_day: 31,
    min_year: 1900, // 最小年份
    min_month: 1, // 最小月份
    min_day: 1,  // 最小日期
    moreDays: false
  },

  /**
   * 元件的方法列表
   */
  methods: {
    /**
     * 檢查年份
     */
    _checkYear: function(year) {
      if (year < minYear) {
        throw new RangeError('年份不能小於' + minYear + '年');
      } else if (year > maxYear) {
        throw new RangeError('年份不能大於' + maxYear + '年');
      }
      return true;
    },

    /**
     * 檢查月份
     */
    _checkMonth: function(month) {
      if (month < 1) {
        throw new RangeError('月份不能小於1');
      } else if (month > 12) {
        throw new RangeError('月份不能大於12');
      }
      return true;
    },

    /**
     * 檢查日期是否輸入正確
     * @param day int 日期
     */
    _checkDay: function(day) {
      if (day < 1) {
        throw new RangeError('日期不能小於1');
      } else if (day > 31) {
        throw new RangeError('日期不能大於31');
      }
      return true;
    },

    /**
     * 年份屬性改變
     */
    _yearChange: function(newYear, oldYear) {
      if (this._checkYear(newYear)) {
        this.setData({
          year: newYear,
          days_array: this._setCalendarData(newYear, this.data.month)
        });
      }
    },

    /**
     * 月份屬性改變
     */
    _monthChange: function(newMonth, oldMonth) {
      if (this._checkMonth(newMonth)) {
        this.setData({
          month: newMonth,
          days_array: this._setCalendarData(this.data.year, newMonth)
        });
      }
    },

    /**
     * 日期屬性改變
     */
    _dayChange: function(newDay, oldDay) {
      if (this._checkDay(newDay)) {
        this.setData({
          day: newDay
        });
      }
    },

    /**
     * 設定起始日期
     */
    _setStartDate: function(newDate, oldDate) {
      if (newDate.length <= 10 && newDate.indexOf('-') == 4) {
        const year = parseInt(newDate.split('-')[0]);
        const month = parseInt(newDate.split('-')[1]);
        const day = parseInt(newDate.split('-')[2]);
        if (!isNaN(year) && year >= minYear && !isNaN(month) && month >= 1 && month <= 12) {
          this.setData({
            startDate: newDate,
            min_year: year,
            min_month: month,
            min_day: day
          });
          this._setCalendarData();
        } else {
          throw new Error('起始日期必須是YYYY-MM-DD格式,且大於等於1900-01-01');
        }
      } else {
        throw new Error('起始日期必須是YYYY-MM-DD格式');
      }
    },

    /**
     * 設定結束日期
     */
    _setEndDate: function(newDate, oldDate) {
      if (newDate.length <= 10 && newDate.indexOf('-') == 4) {
        const year = parseInt(newDate.split('-')[0]);
        const month = parseInt(newDate.split('-')[1]);
        const day = parseInt(newDate.split('-')[2]);
        if (!isNaN(year) && year <= 2099 && !isNaN(month) && month >= 1 && month <= 12) {
          this.setData({
            endDate: newDate,
            max_year: year,
            max_month: month,
            max_day: day
          });
        } else {
          throw new Error('結束日期必須是YYYY-MM-DD格式,且小於等於2099-12');
        }
      } else {
        throw new Error('結束日期必須是YYYY-MM-DD格式');
      }
    },

    /**
     * 是否顯示標題
     */
    _headerChange: function(newHeader, oldHeader) {
      this.setData({
        header: !!newHeader
      });
    },

    /**
     * 是否顯示額外的月份日期
     */
    _moreChange: function(newMore, oldMore) {
      this.setData({
        showMoreDays: !!newMore,
        days_array: this._setCalendarData(this.data.year, this.data.month)
      });
    },

    /**
     * 周標題型別
     */
    _weeksTypeChange: function(newVal, oldVal) {
      switch (newVal) {
        case 'en':
          this.setData({
            weeksType: 'en',
            weekTitle: ['S', 'M', 'T', 'W', 'T', 'F', 'S']
          });
          break;
        case 'cn':
          this.setData({
            weeksType: 'cn',
            weekTitle: ['日', '一', '二', '三', '四', '五', '六']
          });
          break;
        case 'full-en':
          this.setData({
            weeksType: 'full-en',
            weekTitle: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat']
          });
          break;
        default:
          this.setData({
            weeksType: 'en',
            weekTitle: ['S', 'M', 'T', 'W', 'T', 'F', 'S']
          });
          break;
      }
    },

    /**
     * 是否顯示周標題
     */
    _showWeeksChange: function(newVal, oldVal) {
      this.setData({
        weeks: !!newVal,
      });
    },

    /**
     * 設定單元格寬度
     */
    _setCellSize: function(newSize, oldSize) {
      this.setData({
        cellSize: newSize
      });
    },

    /**
     * 是否顯示農曆
     */
    _showLunar: function(newConfig, oldConfig) {
      this.setData({
        lunar: !!newConfig
      });
    },

    /**
     * 設定日期單元格字型顏色、背景
     */
    _setDaysColor: function(newDaysColor, oldDaysColor) {
      this.setData({
        days_color: newDaysColor
      }, function() {
        this.setData({
          days_array: this._setCalendarData(this.data.year, this.data.month)
        });
      });
    },

    /**
     * 設定日期背景效果
     */
    _setActiveType: function(newType, oldType) {
      switch (newType) {
        case 'rounded':
        case 'square':
          this.setData({
            activeType: newType
          });
          break;
        default:
          this.setData({
            activeType: 'rounded'
          });
      }
    },

    /**
     * 設定日曆
     * @param int year 年份
     * @param int month 月份,取值1~12
     */
    _setCalendarData: function(year, month) {
      const empty_days_count = new Date(year, month - 1, 1).getDay(); // 本月第一天是周幾,0是星期日,6是星期六
      let empty_days = new Array;
      const prev_month = month - 1 == 0 ? 12 : month - 1; // 上個月的月份數
      const prev_year = month - 1 == 0 ? this.data.year - 1 : this.data.year;
      /**
       * 上個月的日期
       */
      for (let i = 0; i < empty_days_count; i++) {
        empty_days.push({
          state: 'inactive',
          day: -1,
          month: prev_month,
          year: prev_year,
          info: 'prev',
          color: '#c3c6d1',
          background: 'transparent'
        });
      }

      /**
       * 下個月的日期
       */
      const last_day = new Date(year, month, 0); // 本月最後一天
      const days_count = last_day.getDate(); // 本月最後一天是幾號
      const last_date = last_day.getDay(); // 本月最後一天是星期幾
      const next_month = month + 1 == 13 ? 1 : month + 1; // 下個月的月份數
      const next_year = month + 1 == 13 ? this.data.year + 1 : this.data.year;
      let empty_days_last = new Array;
      for (let i = 0; i < 6 - last_date; i++) {
        empty_days_last.push({
          state: 'inactive',
          day: -2,
          month: next_month,
          year: next_year,
          info: 'next',
          color: '#c3c6d1',
          background: 'transparent'
        });
      }

      /**
       * 本月的日期
       */
      let temp = new Array;
      for (let i = 1; i <= days_count; i++) {
        temp.push({
          state: 'inactive',
          day: i,
          month: month,
          year: year,
          info: 'current',
          color: '#4a4f74',
          background: 'transparent'
        });
      }
      const days_range = temp; // 本月
      let days = empty_days.concat(days_range, empty_days_last); // 上個月 + 本月 + 下個月            
      // 如果要顯示前後月份的日期
      if (this.data.showMoreDays) {
        // 顯示下月的日期
        let index = days.findIndex(element => {
          return element.day == -2;
        });
        if (index != -1) {
          const length = days.length;
          const count = length - index;
          for (let i = 1; i <= count; i++) {
            days[index + i - 1].day = i;
          }
        }

        // 顯示上月的日期
        index = days.findIndex(element => {
          return element.day == 1;
        }) - 1;
        if (index != -1) {
          const last_month_day = new Date(year, month - 1, 0).getDate();
          for (let i = 0; i <= index; i++) {
            days[i].day = last_month_day - index + i;
          }
        }
      }

      /**
       * 設定日期顏色、背景
       */
      for (let i = 0; i < this.data.days_color.length; i++) {
        const item = this.data.days_color[i];
        const background = item.background ? item.background : 'transparent';
        for (let j = 0; j < days.length; j++) {
          if (days[j].month == item.month && days[j].day == item.day) {
            if (item.color) {
              days[j].color = item.color + '!important';
            }
            if (item.background) {
              days[j].background = item.background + '!important';
            }
          }
        }
      }

      /**
       * 設定農曆
       */
      for (let i = 0; i < days.length; i++) {
        const item = days[i];
        const solarDay = item.day;
        const solarMonth = item.month;
        const solarYear = year;
        if (solarDay > 0) {
          const lunarDate = lunar.solarToLunar(year, solarMonth, solarDay);
          days[i]['lunarMonth'] = lunarDate.monthStr;
          days[i]['lunarDay'] = lunarDate.dayStr;
          if (lunarDate.dayStr == '初一') {
            days[i]['lunarDay'] = lunarDate.monthStr;
          }
        }
      }

      // 設定起止日期
      for (let i = 0; i < days.length; i++) {
        if (days[i].year < this.data.min_year || 
        (days[i].year == this.data.min_year && days[i].month < this.data.min_month) ||
          (days[i].year == this.data.min_year && days[i].month == this.data.min_month && days[i].day < this.data.min_day)) {
          days[i].color = '#c3c6d1';
        }
        if (days[i].year > this.data.max_year ||
          (days[i].year == this.data.max_year && days[i].month > this.data.max_month) ||
          (days[i].year == this.data.max_year && days[i].month == this.data.max_month && days[i].day > this.data.max_day)) {
          days[i].color = '#c3c6d1';
        }
      }

      let days_array = new Array;
      let week = new Array;
      for (let i = 0; i < days.length; i++) {
        week.push(days[i]);
        if (i % 7 == 6) {
          days_array.push(week);
          week = new Array;
        }
      }

      if (week.length > 0) {
        days_array.push(week);
      }

      return days_array;
    },

    _setAddon: function(newAddon, oldAddon) {
      if (newAddon == 'none') {
        this.setData({
          lunar: false,
          daysAddon: [],
          addon: 'none'
        });
      } else if (newAddon == 'lunar') {
        this.setData({
          lunar: true,
          daysAddon: [],
          addon: 'lunar'
        });

      } else if (newAddon == 'custom') {
        this.setData({
          addon: 'custom',
          lunar: false,
        });
      } else if (newAddon == 'mixed') {
        this.setData({
          addon: 'mixed',
          lunar: true
        });
      }
    },

    /**
     * 自定義日期陣列
     */
    _setDaysAddon: function(newAddon, oldAddon) {
      if (typeof(newAddon) == 'object' && newAddon instanceof Array) {
        this.setData({
          days_addon: newAddon
        });
      }
    },

    /**
     * 是否允許選自日期區間
     */
    _setMoreDays: function(newMore, oldMore) {
      this.setData({
        moreDays: !!newMore,
      });
    },

    /**
     * 點選下個月
     */
    nextMonth: function() {
      const eventDetail = {
        prevYear: this.data.year,
        prevMonth: this.data.month
      };

      if (this.data.month == 12) {
        this.setData({
          year: this.data.year + 1,
          month: 1
        });
      } else {
        this.setData({
          month: this.data.month + 1
        });
      }
      this.setData({
        days_array: this._setCalendarData(this.data.year, this.data.month)
      });
      eventDetail['currentYear'] = this.data.year;
      eventDetail['currentMonth'] = this.data.month;
      this.triggerEvent('nextMonth', eventDetail);
    },

    /**
     * 點選上個月
     */
    prevMonth: function() {
      const eventDetail = {
        prevYear: this.data.year,
        prevMonth: this.data.month
      };

      if (this.data.month == 1) {
        this.setData({
          year: this.data.year - 1,
          month: 12
        });
      } else {
        this.setData({
          month: this.data.month - 1
        })
      }
      this.setData({
        days_array: this._setCalendarData(this.data.year, this.data.month)
      });
      eventDetail['currentYear'] = this.data.year;
      eventDetail['currentMonth'] = this.data.month;
      this.triggerEvent('prevMonth', eventDetail);
    },

    /**
     * 日期選擇器變化
     */
    dateChange: function(event) {
      const eventDetail = {
        prevYear: this.data.year,
        prevMonth: this.data.month
      };
      const value = event.detail.value;
      const date = new Date(value);
      const year = date.getFullYear();
      const month = date.getMonth() + 1;
      this.setData({
        year: year,
        month: month,
        days_array: this._setCalendarData(year, month)
      });

      eventDetail['currentYear'] = year;
      eventDetail['currentMonth'] = month;
      this.triggerEvent('dateChange', eventDetail);
    },

    /**
     * 點選具體日期
     */
    dayClick: function(event) {
      const click_day = event.currentTarget.dataset.day;

      // 判斷選中日期是否在範圍內
      if (click_day.year < this.data.min_year ||
        (click_day.year == this.data.min_year && click_day.month < this.data.min_month) ||
        (click_day.year == this.data.min_year && click_day.month == this.data.min_month && click_day.day < this.data.min_day)) {
        return;
      }
      if (click_day.year > this.data.max_year ||
        (click_day.year == this.data.max_year && click_day.month > this.data.max_month) ||
        (click_day.year == this.data.max_year && click_day.month == this.data.max_month && click_day.day > this.data.max_day)) {
        return;
      }

      // 選擇單日期
      if (!this.data.moreDays) {
        this.triggerEvent('dayClick', click_day);
      } else {
        const click_day = event.currentTarget.dataset.day;
        if (dateStart == undefined) {
          dateStart = click_day;
          // 設定選中日期背景色
          let temp = temp = new Array(1);;
          
          temp[0] = {
            background: '#ea9518',
            day: dateStart.day,
            month: dateStart.month
          }
          this.setData({
            days_color: temp
          });
          this.setData({
            days_array: this._setCalendarData(dateStart.year, dateStart.month)
          });
        } else {
          dateEnd = click_day;
        }
        if (dateEnd != undefined) {
          // 選擇同一天返回
          if (dateStart.year == dateEnd.year && dateStart.month == dateEnd.month &&
            dateStart.day == dateEnd.day) {
            return;
          }
          
          // 當開始日期超過結束日期時,開始日期和結束日期對換
          if (dateStart.year > dateEnd.year || 
            (dateStart.year == dateEnd.year && dateStart.month > dateEnd.month) ||
            (dateStart.year == dateEnd.year && dateStart.month == dateStart.month && dateStart.day > dateEnd.day)) {
            let temp = dateEnd;
            dateEnd = dateStart;
            dateStart = temp;
          }

          const eventDetail = {
            dateStart: dateStart,
            dateEnd: dateEnd
          }

          // 設定選中日期背景色
          // 設定選中日期背景色
          let temp = new Array();
          var start = this.parse(dateStart.year + '-' + dateStart.month + '-' + dateStart.day, 'y-m-d');
          var end = this.parse(dateEnd.year + '-' + dateEnd.month + '-' + dateEnd.day, 'y-m-d');
          for (let i = 0; start <= end && i < 20; i++) {
            temp.push({
              day: start.getDate(),
              month: start.getMonth() + 1,
              background: "#ea9518"
            })
            start = this.nextDay(start);
          }
          this.setData({
            days_color: temp
          });
          console.log(this.data.days_color)
          this.setData({
            days_array: this._setCalendarData(click_day.year, click_day.month)
          });

          dateStart = undefined;
          dateEnd = undefined;
          // 睡眠3秒
          let start = new Date().getTime();
          while (true) if (new Date().getTime() - start > 1000) break;

          this.triggerEvent('dayClick', eventDetail);
        }
      }
    },

    /**
     * @description 將字串轉換為日期,支援格式y-m-d ymd (y m r)以及標準的
     * @return {Date} 返回日期物件
     */
    parse: function(dateStr, formatStr) {
      if (typeof dateStr === 'undefined') return null;
      if (typeof formatStr === 'string') {
        var _d = new Date(formatStr);
        //首先取得順序相關字串
        var arrStr = formatStr.replace(/[^ymd]/g, '').split('');
        if (!arrStr && arrStr.length != 3) return null;

        var formatStr = formatStr.replace(/y|m|d/g, function(k) {
          switch (k) {
            case 'y':
              return '(\\d{4})';
            case 'm':
              ;
            case 'd':
              return '(\\d{1,2})';
          }
        });

        var reg = new RegExp(formatStr, 'g');
        var arr = reg.exec(dateStr)

        var dateObj = {};
        for (var i = 0, len = arrStr.length; i < len; i++) {
          dateObj[arrStr[i]] = arr[i + 1];
        }
        return new Date(dateObj['y'], dateObj['m'] - 1, dateObj['d']);
      }
      return null;
    },

    nextDay: function(d) {
      let end_date = new Date(d);
      end_date = +end_date + 1000*60*60*24;
      end_date = new Date(end_date);
      return end_date;
    },
  },

  created: function() {},

  attached: function() {
    const year = this.data.year;
    const month = this.data.month;
    this.setData({
      days_array: this._setCalendarData(year, month)
    });
  },

  ready: function() {},

  externalClasses: [
    'calendar-style', // 日曆整體樣式
    'header-style', // 標題樣式
    'board-style', // 面板樣式        
  ]
})

  wxml

<view class="calendar calendar-style">
    <!--主標題-->
    <view class="calendar-header header-style" wx:if="{{header}}">
        <text wx:if="{{year == min_year && month == min_month}}"></text>
        <text class="cwj-icon cwj-calendar-icon-left" bindtap="prevMonth" wx:elif="{{prev}}"></text>
        <text wx:else></text>
        
        <picker mode="date" value="{{year}}-{{month}}" start="{{startDate}}" end="{{endDate}}" bindchange="dateChange" fields="month">
            <text>{{year}}年{{month}}月</text>
        </picker>
        
        <text wx:if="{{year == max_year && month == max_month}}"></text>
        <text class="cwj-icon cwj-calendar-icon-right" bindtap="nextMonth" wx:elif="{{next}}"></text>
        <text wx:else></text>
    </view>

    <!--日曆面板-->
    <view class="calendar-board board-style">
        <!--周標題-->
        <view class="calendar-weeks" wx:if="{{weeks && weekTitle.length == 7}}">
            <text class="calendar-weekday" wx:for="{{weekTitle}}" wx:key="unique">{{item}}</text>
        </view>

        <!--日期-->
        <view class="calendar-days">
            <block wx:for="{{days_array}}" wx:for-item="item" wx:key="unique" wx:for-index="i">
                <!--日期行-->
                <view class="calendar-row">
                    <block wx:for="{{days_array[i]}}" wx:for-item="day" wx:key="unique">
                        <view class="calendar-cell" style="background: {{day.background}}; width: {{cellSize}}px; height: {{cellSize}}px;" wx:if="{{day.day <= 0}}"></view>
                        <view class="calendar-cell" style="background: {{day.background}}; width: {{cellSize}}px; height: {{cellSize}}px;" wx:elif="{{activeType == 'square'}}" bindtap="dayClick" data-day="{{day}}">
                            <block wx:if="{{day.info == 'prev'}}">
                                <text class="calendar-day" style="color: {{day.color}};">{{day.day}}</text>
                                <block wx:if="{{addon == 'lunar'}}">
                                    <text class="calendar-lunar-day" style="color: {{day.color}};" wx:if="{{lunar}}">{{day.lunarDay}}</text>
                                </block>
                                <block wx:elif="{{addon == 'custom'}}">
                                    <text class="calendar-lunar-day" style="color: {{day.color}};">{{days_addon[c]}}</text>
                                </block>
                                <block wx:elif="{{addon == 'mixed'}}">
                                    <text class="calendar-lunar-day" style="color: {{day.color}};" wx:if="{{days_addon[i] != ''}}">{{days_addon[i]}}</text>
                                    <text class="calendar-lunar-day" style="color: {{day.color}};" wx:else>{{day.lunarDay}}</text>
                                </block>
                            </block>
                            <block wx:elif="{{day.info == 'next'}}">
                                <text class="calendar-day" style="color: {{day.color}};">{{day.day}}</text>
                                <block wx:if="{{addon == 'lunar'}}">
                                    <text class="calendar-lunar-day" style="color: {{day.color}};" wx:if="{{lunar}}">{{day.lunarDay}}</text>
                                </block>
                                <block wx:elif="{{addon == 'custom'}}">
                                    <text class="calendar-lunar-day" style="color: {{day.color}};">{{days_addon[i]}}</text>
                                </block>
                                <block wx:elif="{{addon == 'mixed'}}">
                                    <text class="calendar-lunar-day" style="color: {{day.color}};" wx:if="{{days_addon[i] != ''}}">{{days_addon[i]}}</text>
                                    <text class="calendar-lunar-day" style="color: {{day.color}};" wx:else>{{day.lunarDay}}</text>
                                </block>
                            </block>
                            <block wx:else>
                                <text class="calendar-day" style="color: {{day.color}};">{{day.day}}</text>
                                <block wx:if="{{addon == 'lunar'}}">
                                    <text class="calendar-lunar-day" style="color: {{day.color}};" wx:if="{{lunar}}">{{day.lunarDay}}</text>
                                </block>
                                <block wx:elif="{{addon == 'custom'}}">
                                    <text class="calendar-lunar-day" style="color: {{day.color}};">{{days_addon[i]}}</text>
                                </block>
                                <block wx:elif="{{addon == 'mixed'}}">
                                    <text class="calendar-lunar-day" style="color: {{day.color}};" wx:if="{{days_addon[i] != ''}}">{{days_addon[i]}}</text>
                                    <text class="calendar-lunar-day" style="color: {{day.color}};" wx:else>{{day.lunarDay}}</text>
                                </block>                                
                            </block>                            
                        </view>
                        <view class="calendar-cell cell-rounded" style="background: {{day.background}}; width: {{cellSize}}px; height: {{cellSize}}px;" wx:else bindtap="dayClick" data-day="{{day}}">
                            <block wx:if="{{day.info == 'prev'}}">
                                <text class="calendar-day" style="color: {{day.color}};">{{day.day}}</text>
                                <block wx:if="{{addon == 'lunar'}}">
                                    <text class="calendar-lunar-day" style="color: {{day.color}};" wx:if="{{lunar}}">{{day.lunarDay}}</text>
                                </block>
                                <block wx:elif="{{addon == 'custom'}}">
                                    <text class="calendar-lunar-day" style="color: {{day.color}};">{{days_addon[i]}}</text>
                                </block>
                                <block wx:elif="{{addon == 'mixed'}}">
                                    <text class="calendar-lunar-day" style="color: {{day.color}};" wx:if="{{days_addon[i] != ''}}">{{days_addon[i]}}</text>
                                    <text class="calendar-lunar-day" style="color: {{day.color}};" wx:else>{{day.lunarDay}}</text>
                                </block>
                            </block>
                            <block wx:elif="{{day.info == 'next'}}">
                                <text class="calendar-day" style="color: {{day.color}};">{{day.day}}</text>
                                <block wx:if="{{addon == 'lunar'}}">
                                    <text class="calendar-lunar-day" style="color: {{day.color}};" wx:if="{{lunar}}">{{day.lunarDay}}</text>
                                </block>
                                <block wx:elif="{{addon == 'custom'}}">
                                    <text class="calendar-lunar-day" style="color: {{day.color}};">{{days_addon[i]}}</text>
                                </block>
                                <block wx:elif="{{addon == 'mixed'}}">
                                    <text class="calendar-lunar-day" style="color: {{day.color}};" wx:if="{{days_addon[i] != ''}}">{{days_addon[i]}}</text>
                                    <text class="calendar-lunar-day" style="color: {{day.color}};" wx:else>{{day.lunarDay}}</text>
                                </block>
                            </block>
                            <block wx:else>
                                <text class="calendar-day" style="color: {{day.color}};">{{day.day}}</text>
                                <block wx:if="{{addon == 'lunar'}}">
                                    <text class="calendar-lunar-day" style="color: {{day.color}};" wx:if="{{lunar}}">{{day.lunarDay}}</text>
                                </block>
                                <block wx:elif="{{addon == 'custom'}}">
                                    <text class="calendar-lunar-day" style="color: {{day.color}};">{{days_addon[i]}}</text>
                                </block>
                                <block wx:elif="{{addon == 'mixed'}}">
                                    <text class="calendar-lunar-day" style="color: {{day.color}};" wx:if="{{days_addon[i] != ''}}">{{days_addon[i]}}</text>
                                    <text class="calendar-lunar-day" style="color: {{day.color}};" wx:else>{{day.lunarDay}}</text>
                                </block>                                
                            </block>                          
                        </view>
                    </block>
                </view>
            </block>
        </view>
    </view>
</view>

  wxss

/* plugin/components/calendar/calendar.wxss */

/*
 * 字型
 */


@font-face {font-family: "cwj-icon";
  src: url('//at.alicdn.com/t/font_601455_q8aev4obbmon7b9.eot?t=1522928336476'); /* IE9*/
  src: url('//at.alicdn.com/t/font_601455_q8aev4obbmon7b9.eot?t=1522928336476#iefix') format('embedded-opentype'), /* IE6-IE8 */
  url('data:application/x-font-woff;charset=utf-8;base64,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') format('woff'),
  url('//at.alicdn.com/t/font_601455_q8aev4obbmon7b9.ttf?t=1522928336476') format('truetype'), /* chrome, firefox, opera, Safari, Android, iOS 4.2+*/
  url('//at.alicdn.com/t/font_601455_q8aev4obbmon7b9.svg?t=1522928336476#cwj-icon') format('svg'); /* iOS 4.1- */
}

.cwj-icon {
  font-family:"cwj-icon" !important;
  font-size:16px;
  font-style:normal;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

.cwj-calendar-icon-left:before { content: "\e697"; }

.cwj-calendar-icon-up:before { content: "\e6a5"; }

.cwj-calendar-icon-down:before { content: "\e6a6"; }

.cwj-calendar-icon-right:before { content: "\e6a7"; }

.cwj-calendar-icon-dot:before { content: "\e608"; }


/**
 * 日曆元件
 */

.calendar {
    display: block;
    margin: 0rpx;
    font-size: 28rpx;
}

/**
 * 日曆主標題
 */

.calendar-header {
    display: flex;
    align-items: center;
    justify-content: space-around;    
    margin: 0rpx 20rpx 20rpx 20rpx;
    text-align: center;
    font-weight: bold;
}

/**
 * 日曆周標題
 */

.calendar-weeks {
    display: flex;
    justify-content: space-around;
    font-weight: bold;
}

/**
 * 日曆周標題單個專案
 */

.calendar-weekday {
    display: flex;
    justify-content: center;
    align-items: center;
    width: 40rpx;
    height: 40rpx;
    margin-top: 10rpx;
    margin-bottom: 10rpx;    
    text-align: center;
}

/**
 * 日曆日期行
 */

.calendar-row {
    display: flex;
    justify-content: space-around;
}

/**
 * 日曆單個日期項
 */

.calendar-cell {
    display: flex;
    flex-direction: column;
    justify-content: center;
    align-items: center;
    margin-top: 10rpx;
    margin-bottom: 10rpx;    
    text-align: center;    
}

.cell-rounded {
    border-radius: 50%;
}

.calendar-lunar-day {    
    font-size: 20rpx;
}

  lunar.js

/* 
 * 農曆資料表
 * 
 * 農曆分大小月,大月30天,小月29天,但一年中哪個月為大月,哪個月為小月,是無規律的。
 * 農曆每十年有4個閏年,但哪一年為閏年也是不確定的。
 * 而閏月中,哪個閏月為大月,哪個為小月也是不確定的。
 * 
 * 下面共20行,每行10個數據。每個資料代表一年,從陽曆1900.1.31日起,為第一個資料年的開始,即陽曆1900.1.31=農曆0.1.1。
 * 200個數據可推200年的農曆,因此目前最大隻能推算到2100年
 * 
 * 對於每一個數據項,5個十六進位制數 = 20個二進位制位
 * 前4位,即0在這一年是閏年時才有意義,它代表這年閏月的大小月,為1則閏大月,為0則閏小月。
 * 中間12位,即4bd,每位代表一個月,為1則為大月,為0則為小月。
 * 最後4位,即8,代表這一年的閏月月份,為0則不閏。首4位要與末4位搭配使用。
 */
const lunarInfo = new Array(
    0x04bd8, 0x04ae0, 0x0a570, 0x054d5, 0x0d260, 0x0d950, 0x16554, 0x056a0, 0x09ad0, 0x055d2, // 1900年~1909年
    0x04ae0, 0x0a5b6, 0x0a4d0, 0x0d250, 0x1d255, 0x0b540, 0x0d6a0, 0x0ada2, 0x095b0, 0x14977, // 1910年~1919年
    0x04970, 0x0a4b0, 0x0b4b5, 0x06a50, 0x06d40, 0x1ab54, 0x02b60, 0x09570, 0x052f2, 0x04970, // 1920年~1929年
    0x06566, 0x0d4a0, 0x0ea50, 0x06e95, 0x05ad0, 0x02b60, 0x186e3, 0x092e0, 0x1c8d7, 0x0c950, // 1930年~1939年
    0x0d4a0, 0x1d8a6, 0x0b550, 0x056a0, 0x1a5b4, 0x025d0, 0x092d0, 0x0d2b2, 0x0a950, 0x0b557, // 1940年~1949年
    0x06ca0, 0x0b550, 0x15355, 0x04da0, 0x0a5d0, 0x14573, 0x052d0, 0x0a9a8, 0x0e950, 0x06aa0, // 1950年~1959年
    0x0aea6, 0x0ab50, 0x04b60, 0x0aae4, 0x0a570, 0x05260, 0x0f263, 0x0d950, 0x05b57, 0x056a0, // 1960年~1969年
    0x096d0, 0x04dd5, 0x04ad0, 0x0a4d0, 0x0d4d4, 0x0d250, 0x0d558, 0x0b540, 0x0b5a0, 0x195a6, // 1970年~1979年
    0x095b0, 0x049b0, 0x0a974, 0x0a4b0, 0x0b27a, 0x06a50, 0x06d40, 0x0af46, 0x0ab60, 0x09570, // 1980年~1989年
    0x04af5, 0x04970, 0x064b0, 0x074a3, 0x0ea50, 0x06b58, 0x055c0, 0x0ab60, 0x096d5, 0x092e0, // 1990年~1999年
    0x0c960, 0x0d954, 0x0d4a0, 0x0da50, 0x07552, 0x056a0, 0x0abb7, 0x025d0, 0x092d0, 0x0cab5, // 2000年~2009年
    0x0a950, 0x0b4a0, 0x0baa4, 0x0ad50, 0x055d9, 0x04ba0, 0x0a5b0, 0x15176, 0x052b0, 0x0a930, // 2010年~2019年
    0x07954, 0x06aa0, 0x0ad50, 0x05b52, 0x04b60, 0x0a6e6, 0x0a4e0, 0x0d260, 0x0ea65, 0x0d530, // 2020年~2029年
    0x05aa0, 0x076a3, 0x096d0, 0x04bd7, 0x04ad0, 0x0a4d0, 0x1d0b6, 0x0d250, 0x0d520, 0x0dd45, // 2030年~2039年
    0x0b5a0, 0x056d0, 0x055b2, 0x049b0, 0x0a577, 0x0a4b0, 0x0aa50, 0x1b255, 0x06d20, 0x0ada0, // 2040年~2049年
    0x14b63, 0x09370, 0x049f8, 0x04970, 0x064b0, 0x168a6, 0x0ea50, 0x06b20, 0x1a6c4, 0x0aae0, // 2050年~2059年
    0x0a2e0, 0x0d2e3, 0x0c960, 0x0d557, 0x0d4a0, 0x0da50, 0x05d55, 0x056a0, 0x0a6d0, 0x055d4, // 2060年~2069年
    0x052d0, 0x0a9b8, 0x0a950, 0x0b4a0, 0x0b6a6, 0x0ad50, 0x055a0, 0x0aba4, 0x0a5b0, 0x052b0, // 2070年~2079年
    0x0b273, 0x06930, 0x07337, 0x06aa0, 0x0ad50, 0x14b55, 0x04b60, 0x0a570, 0x054e4, 0x0d160, // 2080年~2089年
    0x0e968, 0x0d520, 0x0daa0, 0x16aa6, 0x056d0, 0x04ae0, 0x0a9d4, 0x0a2d0, 0x0d150, 0x0f252, // 2090年~2099年
    0x0d520                                                                                   // 2100年
);

const minYear = 1900; // 能計算的最小年份
const maxYear = 2100; // 能計算的最大年份

// 陽曆每月天數,遇到閏年2月需加1天
const solarMonth = new Array(31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31);

// 農曆月份別稱
const monthName = new Array('正月', '二月', '三月', '四月', '五月', '六月', '七月', '八月', '九月', '十月', '冬月', '臘月');

// 二十四節氣
const solarTerm = new Array(
    '小寒', '大寒', '立春', '雨水', '驚蟄', '春分',
    '清明', '穀雨', '立夏', '小滿', '芒種', '夏至',
    '小暑', '大暑', '立秋', '處暑', '白露', '秋分',
    '寒露', '霜降', '立冬', '小雪', '大雪', '冬至'
);

// 二十節氣相關係數
const termInfo = new Array(
    0, 21208, 42467, 63836, 85337, 107014,
    128867, 150921, 173149, 195551, 218072, 240693,
    263343, 285989, 308563, 331033, 353350, 375494,
    397447, 419210, 440795, 462224, 483532, 504758);

/**
 * 檢查年份是否輸入正確
 * @param year int 年份
 */
function _checkYear(year) {
    if (year < minYear) {
        throw new RangeError('年份不能小於' + minYear + '年');
    } else if (year > maxYear) {
        throw new RangeError('年份不能大於' + maxYear + '年');
    }
    return true;
}

/**
 * 檢查月份是否輸入正確
 * @param month int 月份
 */
function _checkMonth(month) {
    if (month < 1) {
        throw new RangeError('月份不能小於1');
    } else if (month > 12) {
        throw new RangeError('月份不能大於12');
    }
    return true;
}

/**
 * 檢查日期是否輸入正確
 * @param day int 日期
 */
function _checkDay(day) {
    if (day < 1) {
        throw new RangeError('日期不能小於1');
    } else if (day > 31) {
        throw new RangeError('日期不能大於31');
    }
    return true;
}

/**
 * 返回農曆year年中哪個月是閏月,沒有閏月返回0
 * @param year int 年份
 */
function getLunarLeapMonth(year) {
    if (_checkYear(year)) {
        return lunarInfo[year - minYear] & 0xf;  // 最後4位,代表這一年的閏月月份,為0則今年沒有閏月
    }
}

/**
 * 返回農曆year年閏月的天數(如果沒有閏月則返回0)
 * @param year int 年份
 */
function getLeapMonthDaysCount(year) {
    if (getLunarLeapMonth(year)) {
        return lunarInfo[year - minYear] & 0x10000 ? 30 : 29; // 前4位,即0在這一年是閏年時才有意義,它代表這年閏月的大小月
    }
    return 0;
}

/**
 * 返回農曆year年的總天數
 * @param year int 年份
 */
function getLunarYearDaysCount(year) {
    if (_checkYear(year)) {
        let sum = 348;  // 29天 * 12個月 = 348日    
        for (let i = 0x8000; i > 0x8; i >>= 1) {
            sum += (lunarInfo[year - minYear] & i ? 1 : 0);
        }
        return sum + getLeapMonthDaysCount(year);
    }
}

/**
 * 返回農曆year年month月的天數
 * @param year int 年份
 * @param month int 月份 1~12
 */
function getLunarYearMonthDaysCount(year, month) {
    if (_checkYear(year) && _checkMonth(month)) {
        return lunarInfo[year - minYear] & (0x10000 >> month) ? 30 : 29;
    }
}

/**
 * 農曆日期的中文字串
 * @param day int 日期
 */
function getLunarDayString(day) {
    if (_checkDay(day)) {
        const nStr1 = new Array('日', '一', '二', '三', '四', '五', '六', '七', '八', '九', '十');
        const nStr2 = new Array('初', '十', '廿', '卅');
        let str = '';
        switch (day) {
            case 10:
                str = '初十';
                break;
            case 20:
                str = '二十';
                break;
            case 30:
                str = '三十';
                break;
            default:
                str = nStr2[Math.floor(day / 10)];
                str += nStr1[day % 10];
                break;
        }
        return str;
    }
}

/**
 * 返回某年的第n個節氣為幾日(從0小寒起算)
 * @param year int 年份
 * @param n 節氣編號 0~23
 */
function getLunarTermDay(year, n) {
    if (_checkYear(year) && n <= 23 && n >= 0) {
        const sTermInfo = new Array(0, 21208, 42467, 63836, 85337, 107014, 128867, 150921, 173149, 195551, 218072, 240693, 263343, 285989, 308563, 331033, 353350, 375494, 397447, 419210, 440795, 462224, 483532, 504758);
        const offDate = new Date((31556925974.7 * (year - minYear) + sTermInfo[n] * 60000) + Date.UTC(minYear, 0, 6, 2, 5));
        return offDate.getUTCDate();
    }
}

/**
 * 陽曆日期轉農曆日期
 * @param year int 年份
 * @param month int 月份 1~12
 * @param day int 日期 1~31
 */
function solarToLunar(year, month, day) {
    if (_checkYear(year) && _checkMonth(month) && _checkDay(day)) {
        const baseDate = new Date(minYear, 0, 31);      // 基礎日期1900年1月31日
        const objDate = new Date(year, month - 1, day); // 目標日期
        let offset = (objDate - baseDate) / 86400000;   // 偏移天數 60 * 60 * 24 * 1000 = 86400000,1天的毫秒數
        let monCycle = 14;
        let temp = 0;
        let i = 0;

        for (i = minYear; i < maxYear && offset > 0; i++) {
            temp = getLunarYearDaysCount(i);             // 農曆year年的總天數
            if (offset - temp < 0) {
                break;
            } else {
                offset -= temp;
            }
            monCycle += 12;
        }


        const lunarYear = i;                             // 農曆年份        
        const leap = getLunarLeapMonth(lunarYear);       // 當年閏月是哪個月
        const isLeapYear = leap > 0 ? true : false;      // 當年是否有閏月        
        let isLeapMonth = false;                         // 當前農曆月份是否是閏月
        for (i = 1; i <= 12 && offset > 0; i++) {
            if (leap > 0 && i == (leap + 1) && !isLeapMonth) {
                --i;
                isLeapMonth = true;
                temp = getLeapMonthDaysCount(year);
            } else {
                temp = getLunarYearMonthDaysCount(year, i);
            }

            if (isLeapMonth && i == (leap + 1)) {
                isLeapMonth = false;
            }

            offset -= temp;
            if (!isLeapMonth) {
                monCycle++;
            }
        }

        if (offset == 0 && leap > 0 && i == leap + 1) {
            if (isLeapMonth) {
                isLeapMonth = false;
            } else {
                isLeapMonth = true;
                --i;
                --monCycle;
            }
        }

        if (offset < 0) {
            offset += temp;
            --i;
            --monCycle;
        }
        const lunarMonth = i;        // 農曆月份
        const lunarDay = offset + 1; // 農曆日期

        let monthStr = '';
        if (isLeapYear) {
            if (lunarMonth < leap) {
                monthStr = monthName[lunarMonth - 1];
            } else if (lunarMonth == leap) {
                monthStr = '閏' + monthName[lunarMonth - 1];
            } else {
                monthStr = monthName[lunarMonth - 2];
            }
        } else {
            monthStr = monthName[lunarMonth - 1];
        }

        return {
            year: lunarYear,                    // 農曆年份
            month: lunarMonth,                  // 農曆月份
            day: lunarDay,                      // 農曆日期
            isLeap: isLeapMonth,                // 是否閏月
            monthStr: monthStr,                 // 月份字串
            dayStr: getLunarDayString(lunarDay) // 日期字串
        };
    }
}

/**
 * 陽曆某個月份天數
 * @param year int 年份
 * @param month int 月份 1~12
 */
function getSolarMonthDaysCount(year, month) {
    if (_checkYear(year) && _checkMonth(month)) {
        if (month == 2) {
            return (((year % 4 == 0) && (year % 100 != 0) || (year % 400 == 0)) ? 29 : 28);
        } else {
            return solarMonth[month - 1];
        }
    }
}

/**
 * 獲取指定日期是陽曆年中的第幾天
 * @param year int 年份
 * @param month int 月份 1-12
 * @param day int 日期
 */
function getSolarDayNumber(year, month, day) {
    if (_checkYear(year) && _checkMonth(month) && _checkDay(day)) {
        const date = new Date(year, month - 1, day);
        const d = date.getDate(); // 本月第幾天
        const m = month - 1;
        let sum = d;
        for (let i = 0; i < m; i++) {
            sum += solarMonth[i];
        }

        if (m > 1 && (year % 4 == 0 && year % 100 != 0) || year % 400 == 0) {
            sum += 1;
        }

        return sum;
    }
}

/**
 * 計算指定日期是否屬於24節氣
 * @param year int 年份
 * @param month int 月份 1~12
 * @param day int 日期 1~31
 */
function getLunar24Days(year, month, day) {
    if (_checkYear(year) && _checkMonth(month) && _checkDay(day)) {
        const baseDate = new Date(1900, 0, 6, 2, 5, 0);
        let str = false;
        for (let i = 1; i <= 24; i++) {
            const num = 525948.76 * (year - 1900) + termInfo[i];
            const timestamp = baseDate.getTime() + num * 60 * 1000;
            const newDate = new Date(timestamp);
            if (getSolarDayNumber(newDate.getFullYear(), newDate.getMonth() + 1, newDate.getDate()) ==
                getSolarDayNumber(year, month, day)) {
                str = solarTerm[i];
                break;
            }
        }
        return str;
    }
}

module.exports = {
    getLunarLeapMonth,          // 返回農曆year年中哪個月是閏月,沒有閏月返回0
    getLeapMonthDaysCount,      // 返回農曆year年閏月的天數(如果沒有閏月則返回0)
    getLunarYearDaysCount,      // 返回農曆year年的總天數
    getLunarYearMonthDaysCount, // 返回農曆year年month月的天數
    getLunarDayString,          // 農曆日期的中文字串        
    getLunarTermDay,            // 返回某年的第n個節氣為第幾日    
    getSolarMonthDaysCount,     // 獲取陽曆某個月份有多少天
    getSolarDayNumber,          // 獲取指定日期是陽曆年中的第幾天
    getLunar24Days,             // 計算指定日期是否屬於24節氣    
    solarToLunar,               // 陽曆日期轉農曆日期
}

 在 其他頁面中使用

<!-- 日曆元件 -->
// 包裹了一層模態框 元件可以自己實現 <modalView show="{{showModal}}" clickMaskClose="{{clickMaskClose}}"> <view class="calendar"> <view class="calendar-cancel" bindtap="cancelSelect">取消</view> <calendar cell-size="45" weeks-type="cn" active-type="square" binddayClick="handleSelectDate" moreDays="true" show-more-days="true" startDate="{{startDate}}" endDate="{{endDate}}" /> </view> </modalView>

  

// 包裹了一層模態框 
js
Component({
  /**
   * 元件的屬性列表
   */
  properties: {
    //是否顯示modal彈窗
    show: {
      type: Boolean,
      value: false,
      observer: '_setShow'
    },
    //控制底部是一個按鈕還是兩個按鈕,預設兩個
    single: {
      type: Boolean,
      value: false
    },
    // 控制clickMask是否關閉
    clickMaskClose: {
      type: Boolean,
      value: true,
      observer: '_setClickMaskClose'
    },
    top: {
      type: Number,
      value: 0
    },
    bottom: {
      type: Number,
      value: 0
    }
  },

  /**
   * 元件的初始資料
   */
  data: {
    clickMaskClose: true,
    show: false,
    top: 0,
    bottom: 0
  },

  /**
   * 元件的方法列表
   */
  methods: {
    _setClickMaskClose: function(newData, oldData) {
      this.setData({
        clickMaskClose: newData
      })
    },
    _setShow: function (newData, oldData) {
      this.setData({
        show: newData
      })
    },
    // 點選modal的回撥函式
    clickMask: function() {
      // 點選modal背景關閉遮罩層,如果不需要註釋掉即可
      if (this.data.clickMaskClose) {
        this.setData({ show: false })
      }
    },
    // 點選取消按鈕的回撥函式
    cancel: function() {
      this.setData({ show: false })
      this.triggerEvent('cancel')  //triggerEvent觸發事件
    },
    // 點選確定按鈕的回撥函式
    confirm: function() {
      this.setData({ show: false })
      this.triggerEvent('confirm')
    }
  }
})

  wxml

<!--components/modal/modal.wxml-->
<view class='modal-mask' wx:if='{{show}}' bindtap='clickMask'>
  <view class='modal-content' style="bottom: {{bottom}}px;">
    <scroll-view scroll-y class='main-content'>
      <slot></slot>
    </scroll-view>
  </view>
</view>

  wxss

/* components/modal/modal.wxss */
/* components/modal/modal.wxss */
/*遮罩層*/
.modal-mask{
  display: flex;
  position: fixed;
  left: 0;
  right: 0;
  top: 0;
  bottom: 0;
  background-color: rgba(0,0,0,0.5);
  z-index: 9999999;
}
/*遮罩內容*/
.modal-content{
  display: flex;
  position: absolute;
  flex-direction: column;
  width: 100%;
  background-color: #fff;
}
/*中間內容*/
.main-content{
  flex: 1;
  height: 100%;
  overflow-y: hidden; 
  max-height: 80vh; /* 內容高度最高80vh 以免內容太多溢位*/
}