zhaoxiaohai 2 éve
szülő
commit
cb5c75d62f

+ 122 - 0
components/wx-calendar/config.js

@@ -0,0 +1,122 @@
+import { Lunar } from './lunar'
+
+const LunarMinDate = (new Date(1901, 0, 1)).getTime()
+const LunarMaxDate = (new Date(2100, 0, 1)).getTime()
+const Weeks = ['日', '一', '二', '三', '四', '五', '六']
+const Months = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
+const EnOrders = ['', 'st', 'nd', 'rd', 'th']
+const initCurrInfo = { i: 0, x: 0, y: 0, t: false, d: 0, a: false, v: false, s: false }
+const ConsoleStyle = {
+    info: {
+        label: 'color: #409EFF; font-weight:bold',
+        content: 'color: #8cc5ff',
+        title: '提示'
+    },
+    warn: {
+        label: 'color: #f37b1d; font-weight:bold',
+        content: 'color: #fcdabd',
+        title: '警告'
+    }
+}
+
+const Today = () => {
+    const _today = new Date;
+    return DayDetail(_today.getFullYear(), _today.getMonth() + 1, _today.getDate())
+}
+
+const isOverDate = date => {
+    const queryTime = date.getTime()
+    return queryTime < LunarMinDate || queryTime >= LunarMaxDate
+}
+
+const LunarDetail = (gregorianYear, gregorianMonth, gregorianDay) => {
+    Lunar.resetInitGregorian(gregorianYear, gregorianMonth, gregorianDay)
+    Lunar.setGregorian(gregorianYear, gregorianMonth, gregorianDay)
+    return Lunar.getLunarDate()
+}
+
+const DayDetail = (year, month, day, type = '') => {
+    const date = new Date(year, month - 1, day)
+    const _year = date.getFullYear()
+    const _month = date.getMonth() + 1
+    const _day = date.getDate()
+    const week = date.getDay()
+    const week_name = `周${ Weeks[week] }`
+    const { lunar_order, lunar_year, lunar_month, lunar_day, lunar_date, lunar_type } = (isOverDate(date) ? emptyLunar() : LunarDetail(_year, _month, _day))
+    return { year: _year, month: _month, day: _day, week, week_name, lunar_order, lunar_year, lunar_month, lunar_day, lunar_type, lunar_date, type }
+}
+
+const emptyLunar = () => {
+    return { lunar_order: '', lunar_year: '', lunar_month: '', lunar_day: '', lunar_date: '', lunar_type: '' }
+}
+
+const MonthDaysCount = (year, month) => {
+    return (new Date(year, month, 0)).getDate()
+}
+
+const DayShort = (year, month, day) => {
+    const date = new Date(year, month - 1, day)
+    const _year = date.getFullYear()
+    const _month = date.getMonth() + 1
+    const _day = date.getDate()
+    const { lunar_month, lm, ld } = LunarDetail(_year, _month, _day)
+    return { year: _year, month: _month, day: _day, lunar_month, lm, ld }
+}
+
+const MonthOnly = (year, month) => {
+    const _count = MonthDaysCount(year, month)
+    return Array.apply(null, { length: _count }).map((_, _i) => DayShort(year, month, _i + 1))
+}
+
+const MonthDaysDetail = (year, month) => {
+    const _count = MonthDaysCount(year, month)
+    return Array.apply(null, { length: _count }).map((_, _i) => DayDetail(year, month, _i + 1))
+}
+
+const MonthDaysDetailFull = (year, month) => {
+    const _days = MonthDaysDetail(year, month)
+    const _beforeDays = Array.apply(null, { length: _days[0].week }).map((_, _i) => DayDetail(year, month, -_i, 'prev')).reverse()
+    const _lastDay = _days[_days.length - 1]
+    const _afterDays = Array.apply(null, { length: 6 - _lastDay.week }).map((_, _i) => DayDetail(year, month, _lastDay.day + _i + 1, 'next'))
+    return {
+        count: _days.length,
+        days: _beforeDays.concat(_days).concat(_afterDays)
+    }
+}
+
+const CorrectDate = (gregorianYear, gregorianMonth, gregorianDay) => {
+    const _date = new Date(gregorianYear, gregorianMonth - 1, gregorianDay)
+    return { year: _date.getFullYear(), month: _date.getMonth() + 1, day: _date.getDate() }
+}
+
+const WeekFirstDay = (gregorianYear, gregorianMonth, gregorianDay) => {
+    const _date = new Date(gregorianYear, gregorianMonth - 1, gregorianDay)
+    const week = _date.getDay()
+    const wf = new Date(_date.getFullYear(), _date.getMonth(), _date.getDate() - week)
+    return { year: wf.getFullYear(), month: wf.getMonth() + 1, day: wf.getDate() }
+}
+
+const YearWeekOrder = (gregorianYear, gregorianMonth, gregorianDay) => {
+    const currDate = new Date(gregorianYear, gregorianMonth - 1, gregorianDay)
+    const firstDate = new Date(gregorianYear, 0, 1)
+    const diff = Math.round((currDate.valueOf() - firstDate.valueOf()) / 86400000)
+    return Math.ceil((diff + ((firstDate.getDay() + 1) - 1)) / 7)
+}
+
+module.exports = {
+    Weeks,
+    Today,
+    DayDetail,
+    MonthDaysCount,
+    MonthDaysDetail,
+    MonthDaysDetailFull,
+    initCurrInfo,
+    CorrectDate,
+    LunarDetail,
+    WeekFirstDay,
+    YearWeekOrder,
+    Months,
+    MonthOnly,
+    EnOrders,
+    ConsoleStyle
+}

+ 1224 - 0
components/wx-calendar/index.js

@@ -0,0 +1,1224 @@
+import {
+    Weeks,
+    Today,
+    CorrectDate,
+    MonthOnly,
+    MonthDaysDetailFull,
+    initCurrInfo,
+    LunarDetail,
+    WeekFirstDay,
+    YearWeekOrder,
+    Months,
+    EnOrders,
+    DayDetail,
+    ConsoleStyle
+} from './config'
+
+const CalendarPositions = ['relative', 'absolute', 'fixed']
+const CalendarHeight = 820 //日历高度,单位rpx
+const Version = '1.0.1'
+
+Component({
+    behaviors: ['wx://component-export'],
+    options: {
+        pureDataPattern: /^_/,
+        // multipleSlots: true
+    },
+    properties: {
+        /**
+         * 是否选择日期时震动
+         */
+        _vibrate: {
+            type: Boolean,
+            value: true
+        },
+        /**
+         * 初始化视图 month, week
+         */
+        view: {
+            type: String,
+            value: 'month'
+        },
+        /**
+         * 定位
+         */
+        _position: {
+            type: String,
+            value: 'relative'
+        },
+        /**
+         * 绝对定位有用
+         */
+        _top: {
+            type: String,
+            optionalTypes: [Number],
+            value: '0rpx'
+        },
+        /**
+         * { year: 2021, month: 4, day: 1, type: 'holiday', mark: '愚人节', color: '#2a97ff', bgColor: '#cce6ff' }
+         * type: 角标corner,节假日holiday,日程schedule
+         */
+        _markers: {
+            type: Array,
+            value: [
+                // { year: 2021, month: 4, day: 1, type: 'holiday', mark: '愚人节', color: '#2a97ff', bgColor: '#cce6ff' },
+                // { year: 2021, month: 4, day: 4, type: 'holiday', mark: '清明', color: '#2a97ff', bgColor: '#cce6ff' },
+                // { year: 2021, month: 4, day: 3, type: 'corner', mark: '休', color: '#61b057' },
+                // { year: 2021, month: 4, day: 4, type: 'corner', mark: '休', color: '#61b057' },
+                // { year: 2021, month: 4, day: 5, type: 'corner', mark: '休', color: '#61b057' },
+                // { year: 2021, month: 4, day: 6, type: 'schedule', mark: '测试一下', color: '#2a97ff', bgColor: '#cce6ff' },
+                // { year: 2021, month: 4, day: 6, type: 'schedule', mark: '测试一下哈哈哈', color: '#2a97ff', bgColor: '#cce6ff' },
+                // { year: 2021, month: 4, day: 6, type: 'schedule', mark: '测试一下哈哈哈', color: '#2a97ff', bgColor: '#cce6ff' }
+            ]
+        },
+        _markerKey: {
+            type: String,
+            value: 'id'
+        },
+        darkmode: {
+            type: Boolean,
+            value: false
+        },
+        _date: {
+            type: Number,
+            optionalTypes: [String],
+            value: (new Date).getTime()
+        },
+        checkedShow: {
+            type: Boolean,
+            value: true
+        }
+    },
+    data: {
+        Weeks,
+        style: 0,
+        currTab: 2,
+        months: [],
+        titleInfo: '',
+        minHeight: 0,
+        maxHeight: 0,
+        panelHeight: 0,
+        calendarHeight: 0,
+        currView: 1,
+        tdOpShow: false,
+        monthchange: false,
+        barAni: true,
+        weektabchange: -1,
+        viewchange: '',
+        solidDay: true,
+        loading: true,
+        yearMs: [],
+        currYmTab: 2,
+        yearPanelShow: false,
+        needInitTrans: false,
+        _currWeekIdx: 0,
+        _markerdays: {},
+        _selDay: null,
+        _selWeek: 0,
+        _rects: [],
+        _today: {}
+    },
+    attached() {
+        this._rectsLoading = true
+        this._year_panel_show = false
+        this._curr_view = this.data.view == 'week' ? 2 : 1
+        const current = this.data.currTab
+        this.initialize(current, _date => {
+            this.getRects().then(() => {
+                if (this._curr_view == 2) {
+                    this.setWeeks(_date, current).then(() => {
+                        this.getDayCurr(current, _date.month)
+                        this.bindLoad(true)
+                    })
+                } else {
+                    this.setMonths(_date, current).then(() => {
+                        this.getDayCurr(current, _date.month)
+                        this.bindLoad()
+                    })
+                }
+            })
+        })
+    },
+    methods: {
+        initialize(current, callback) {
+            const _today = Today()
+            const system = wx.getSystemInfoSync()
+            const maxRate = this.judgeScreen(system) ? 0.8 : 0.9
+            const _clientWidth = system.windowWidth
+            const _otherHeight = Math.floor(200 * _clientWidth / 750)
+            const calendarHeight = Math.floor(CalendarHeight * _clientWidth / 750)
+            const panelHeight = calendarHeight - _otherHeight
+            const maxHeight = Math.floor(system.windowHeight * maxRate)
+            const minHeight = panelHeight / 5 + _otherHeight
+            let _date = this.getCorrectDate(this.data._date)
+            _date = _date ? DayDetail(_date.year, _date.month, _date.day) : _today
+            this.setData({
+                _today,
+                style: this.initStyle(),
+                maxHeight,
+                minHeight,
+                calendarHeight,
+                panelHeight,
+                currView: this._curr_view,
+                _selDay: _date,
+                _selWeek: _date.week,
+                titleInfo: this.setTitleInfo(_date, _today),
+                yearMs: this.setYearMs(_today.year, current, _today),
+                _markerdays: this.initMarkDays()
+            }, () => {
+                typeof callback === 'function' && callback.call(this, _date)
+            })
+        },
+        judgeScreen(system) {
+            const rate = system.windowHeight / system.windowWidth
+            const limit = system.windowHeight == system.screenHeight ? 1.8 : 1.65
+            return rate > limit
+        },
+        initStyle() {
+            const position = CalendarPositions.includes(this.data._position) ? this.data._position : 'relative'
+            const top = typeof this.data._top === 'number' ? this.data._top + 'px' : this.data._top
+            let style = { position }
+            if (position != 'relative') style.top = top
+            return Object.keys(style).map(attr => `${ attr }:${ style[attr] };`).join('')
+        },
+        trigger(event, detail = {}, view = '', eventOptions = {}) {
+            let _o = view ? { view } : {}
+            detail = Object.assign({}, detail, _o)
+            this.triggerEvent(event, detail, eventOptions)
+        },
+        bindLoad(needInitTrans = false) {
+            let setData = { loading: false }
+            if (needInitTrans) setData.needInitTrans = true
+            const view = this.data.currView == 2 ? 'week' : 'month'
+            this.setData(setData, () => {
+                this.console('欢迎到%chttps:\/\/github.com\/lspriv\/wx-calendar\/issues%c提出建议或Bug或✭', 'info', 'font-weight:bold;margin: 0 2px;', 'color: #8cc5ff')
+                this.trigger('load', { date: this.data._selDay }, view)
+                this.triggerChange(view)
+            })
+        },
+        triggerChange(type) {
+            const detail = this.getTriggerDetail()
+            detail.markerCommit = markers => this.handleDynamicMarkers(markers)
+            this.trigger('rangechange', detail, type)
+        },
+        triggerSel(d) {
+            this.trigger('datechange', { date: d })
+        },
+        triggerView(v) {
+            this.trigger('viewchange', { view: v == 2 ? 'week' : 'month' })
+        },
+        getTriggerDetail() {
+            const currTab = this.data.currTab
+            const _first = this.data.months[(currTab + 3) % 5]
+            const _last = this.data.months[(currTab + 2) % 5]
+            const _curr = this.data.months[currTab]
+            const _detail = this._curr_view == 2 ? this.getTriggerWeekDetail(_curr, _first, _last) : this.getTriggerMonthDetail(_curr, _first, _last)
+            return Object.assign({}, _detail, { curr: this.data._selDay })
+        },
+        getTriggerWeekDetail(_curr, _first, _last) {
+            const _l = CorrectDate(_last.wf.year, _last.wf.month, _last.wf.day + 6)
+            const _c = CorrectDate(_curr.wf.year, _curr.wf.month, _curr.wf.day + 6)
+            const range = [
+                { year: _first.wf.year, month: _first.wf.month, day: _first.wf.day },
+                { year: _l.year, month: _l.month, day: _l.day }
+            ]
+            const visual = [
+                { year: _curr.wf.year, month: _curr.wf.month, day: _curr.wf.day },
+                { year: _c.year, month: _c.month, day: _c.day }
+            ]
+            return { range, visual }
+        },
+        getTriggerMonthDetail(_curr, _first, _last) {
+            const _f = _first.idays[0]
+            const _l = _last.idays[_last.idays.length - 1]
+            const range = [
+                { year: _f.year, month: _f.month, day: _f.day },
+                { year: _l.year, month: _l.month, day: _l.day }
+            ]
+            const _cf = _curr.idays[0]
+            const _cl = _curr.idays[_curr.idays.length - 1]
+            const visual = [
+                { year: _cf.year, month: _cf.month, day: _cf.day },
+                { year: _cl.year, month: _cl.month, day: _cl.day }
+            ]
+            return { range, visual }
+        },
+        initMarkDays(_markdays) {
+            _markdays = _markdays ? _markdays : this.data._markers
+            const _marks = (_ => {
+                for (let i = 0; i < _markdays.length; i++) {
+                    _ = this.setMarkerItem(_markdays[i], _)
+                }
+                return _
+            })(new Object)
+            return _marks
+        },
+        initMarker(year, month, day) {
+            return { year, month, day, corner: [], holiday: [], schedule: [] }
+        },
+        getMarker(key) {
+            const marker = this.data._markerdays[key]
+            return marker ? marker : null
+        },
+        setMarkerItem(marker, markers) {
+            let { year, month, day, type, mark, color, bgColor } = marker
+            let _key = `d_${ year }_${ month }_${ day }`
+            if (!markers.hasOwnProperty(_key)) markers[_key] = this.initMarker(year, month, day)
+            if (type == 'corner') mark = mark.substring(0, 2)
+            let key = marker[this.data._markerKey] || markers[_key].length
+            markers[_key][type].push({ mark, color, bgColor, key })
+            return markers
+        },
+        addMarker(marker) {
+            let _markers = this.data._markerdays
+            let { year, month, day } = marker
+            _markers = this.setMarkerItem(marker, _markers)
+            let _key = `d_${ year }_${ month }_${ day }`
+            let keys = this.findDateInCurrDateSwiper(year, month, day)
+            if (keys.length > 0) {
+                this.setData((_ => {
+                    for (let i = 0; i < keys.length; i++) {
+                        _[keys[i]] = _markers[_key]
+                    }
+                    return _
+                })(new Object))
+            }
+        },
+        editMarker(marker) {
+            let _markers = this.data._markerdays
+            let { year, month, day, type, mark, color, bgColor } = marker
+            let _mkey = `d_${ year }_${ month }_${ day }`
+            if (!_markers.hasOwnProperty(_mkey)) return
+            let _markerInfo = _markers[_mkey]
+            let key = this.data._markerKey
+            let setData = new Object
+            _markerInfo[type] = _markerInfo[type].map(_ => {
+                if (_.key == marker[key]) {
+                    _ = { mark, color, bgColor, key }
+                }
+                return _
+            })
+            setData[`_markerdays.${ _mkey }.${ type }`] = _markerInfo[type]
+            let keys = this.findDateInCurrDateSwiper(year, month, day)
+            if (keys.length > 0) {
+                for (let i = 0; i < keys.length; i++) {
+                    setData[keys[i]] = _markerInfo
+                }
+            }
+            this.setData(setData)
+        },
+        delMarker({ year, month, day }, type = '', key = '') {
+            let setData = new Object
+            let _markers = this.data._markerdays
+            let _mkey = `d_${ year }_${ month }_${ day }`
+            if (!_markers.hasOwnProperty(_mkey)) return
+            let _markerInfo = _markers[_mkey]
+            if (type != '' && key != '') {
+                _markerInfo[type] = _markerInfo[type].filter(_ => _.key != key)
+                setData[`_markerdays.${ _mkey }.${ type }`] = _markerInfo[type]
+            } else {
+                if (type == '') {
+                    _markerInfo = null
+                    setData[`_markerdays.${ _mkey }`] = null
+                } else {
+                    _markerInfo[type] = []
+                    setData[`_markerdays.${ _mkey }.${ type }`] = []
+                }
+            }
+            let keys = this.findDateInCurrDateSwiper(year, month, day)
+            if (keys.length > 0) {
+                for (let i = 0; i < keys.length; i++) {
+                    setData[keys[i]] = _markerInfo
+                }
+            }
+            this.setData(setData)
+        },
+        findDateInCurrDateSwiper(year, month, day) {
+            let keys = []
+            for (let i = 0; i < this.data.months.length; i++) {
+                let _month = this.data.months[i]
+                if (
+                    Math.abs(_month.year - year) < 2 &&
+                    Math.abs(_month.month - month) < 2
+                ) {
+
+                    let _idx = _month.idays.findIndex(_ => _.year == year && _.month == month && _.day == day)
+                    if (_idx >= 0) {
+                        let _wdx = Math.floor(_idx / 7)
+                        let _widx = _idx % 7
+                        keys.push(`months[${ i }].idays[${ _idx }].marker`)
+                        keys.push(`months[${ i }].days[${ _wdx }].days[${ _widx }].marker`)
+                    }
+                }
+            }
+            return keys
+        },
+        handleDynamicMarkers(markers) {
+            if (Array.isArray(markers) && markers.length > 0) {
+                this.clearMarkers()
+                const _ms = markers.filter(_ => _.year && _.month && _.day)
+                const _markers = this.initMarkDays(_ms)
+                this.setData({ _markerdays: _markers })
+                this.setDynamicMarkers(_markers)
+            }
+        },
+        setDynamicMarkers(markers) {
+            const _markers = Object.keys(markers)
+            const months = this.data.months
+            for (let i = 0; i < _markers.length; i++) {
+                let _marker = markers[_markers[i]]
+                for (let j = 0; j < months.length; j++) {
+                    let _month = months[j]
+                    if (
+                        Math.abs(_month.year - _marker.year) < 2 &&
+                        Math.abs(_month.month - _marker.month) < 2
+                    ) {
+                        let _idx = _month.idays.findIndex(_ => _.year == _marker.year && _.month == _marker.month && _.day == _marker.day)
+                        if (_idx >= 0) {
+                            let _wdx = Math.floor(_idx / 7)
+                            let _widx = _idx % 7
+                            months[j].idays[_idx].marker = _marker
+                            months[j].days[_wdx].days[_widx].marker = _marker
+                        }
+                    }
+                }
+            }
+            this.setData({ months })
+        },
+        clearMarkers() {
+            const markers = Object.values(this.data._markerdays)
+            const months = this.data.months
+            let setData = new Object
+            for (let i = 0; i < markers.length; i++) {
+                let _marker = markers[i]
+                for (let j = 0; j < months.length; j++) {
+                    let _month = months[j]
+                    if (
+                        Math.abs(_month.year - _marker.year) < 2 &&
+                        Math.abs(_month.month - _marker.month) < 2
+                    ) {
+                        let _idx = _month.idays.findIndex(_ => _.year == _marker.year && _.month == _marker.month && _.day == _marker.day)
+                        if (_idx >= 0) {
+                            let _wdx = Math.floor(_idx / 7)
+                            let _widx = _idx % 7
+                            setData[`months[${ j }].idays[${ _idx }].marker`] = null
+                            setData[`months[${ j }].days[${ _wdx }].days[${ _widx }].marker`] = null
+                        }
+                    }
+                }
+            }
+            this.setData(setData)
+        },
+        initCalendarMonth(d, trans = '', wf = null) {
+            const { year, month, lunar_order, lunar_year } = d
+            const _ds = this.getMonthDays(d)
+            const _wds = this.getMonthWeekDays(_ds.days, year, month)
+            return {
+                key: `m_${ year }_${ month }`,
+                year,
+                month,
+                lunar_order,
+                lunar_year,
+                idays: _ds.days,
+                bar: JSON.parse(JSON.stringify(initCurrInfo)),
+                days: _wds,
+                count: _ds.count,
+                trans: typeof trans === 'function' ? trans.call(this, _ds, _wds) : trans,
+                wf
+            }
+        },
+        setMonths(d, current) {
+            return new Promise((resolve, reject) => {
+                const _trans = this.getMonthsTrans(d, current)
+                const months = this.getSwiperMonths(d, current, _trans)
+                this.setData({ months }, () => {
+                    resolve()
+                })
+            })
+        },
+        getSwiperMonths(d, current, trans) {
+            return Array.apply(null, { length: 5 }).map((_, i) => {
+                if (i === current) return this.initCalendarMonth(d, trans[i])
+                return this.initCalendarMonth(DayDetail(d.year, d.month + i - current, 1), trans[i])
+            })
+        },
+        getMonthDays(d) {
+            const today = this.data._today
+            const { count, days } = MonthDaysDetailFull(d.year, d.month)
+            return {
+                count,
+                days: days.map(item => this.replenishDateInfo(item, today))
+            }
+        },
+        getMonthWeekDays(idays, year, month) {
+            return Array.apply(null, { length: idays.length / 7 }).map((w, idx) => {
+                return {
+                    key: `w_${ year }_${ month }_${ idx + 1 }`,
+                    days: Array.apply(null, { length: 7 }).map((_, _i) => idays[idx * 7 + _i])
+                }
+            })
+        },
+        replenishDateInfo(d, t) {
+            d.isToday = (t.year == d.year && t.month == d.month && t.day == d.day)
+            d.key = `d_${ d.year }_${ d.month }_${ d.day }`
+            d.marker = this.getMarker(d.key)
+            return d
+        },
+        setWeeks(d, current) {
+            return new Promise((resolve, reject) => {
+                const { year, month, day } = d
+                const months = Array.apply(null, { length: 5 }).map((_, i) => {
+                    if (i == current) return this.getMonthByWeekDayInIdx(d)
+                    let _d = DayDetail(year, month, day + (i - 2) * 7)
+                    return this.getMonthByWeekDayInIdx(_d)
+                })
+                this.setData({ months }, () => {
+                    resolve()
+                })
+            })
+        },
+        getRects() {
+            this._rectsLoading = true
+            return new Promise(resolve => {
+                Promise.all([this.getPos('#calendar'), this.getPos('.wd-calendar-week-item')])
+                    .then(([calendar, rects]) => {
+                        const _initX = calendar[0].left
+                        const _rects = rects.map(item => {
+                            item.center = item.left + item.width / 2 - _initX
+                            return item
+                        })
+                        this.setData({
+                            _rects
+                        }, () => {
+                            this._rectsLoading = false
+                            resolve()
+                        })
+                    })
+            })
+        },
+        getPos(selector) {
+            return new Promise((resolve, reject) => {
+                const query = this.createSelectorQuery()
+                query.selectAll(selector).boundingClientRect(rects => {
+                    if (rects.length > 0) resolve(rects)
+                    else reject(rects)
+                }).exec()
+            })
+        },
+        reloadPos() {
+            return new Promise((resolve, reject) => {
+                this.getRects().then(() => {
+                    this.getDayCurr(this.data.currTab, this.data._selDay.month)
+                    resolve()
+                })
+            })
+        },
+        initSelBar(i, d, l, w, v) {
+            const r = this.data._rects[i % 7]
+            return {
+                i,
+                x: r.center,
+                y: `calc(100% / ${ l } * ${ w + 0.5 })`,
+                t: d.isToday,
+                d: d.day,
+                a: true,
+                s: true,
+                v
+            }
+        },
+        getDayCurr(idx, month, callback = null, vibrate = false) {
+            const _month = this.data.months[idx]
+            const day = this.data._selDay.day <= _month.count ? this.data._selDay.day : _month.count
+            const seekIdx = _month.idays.findIndex(_d => (_d.month == month && _d.day == day))
+            if (seekIdx >= 0) {
+                const seekDay = _month.idays[seekIdx]
+                const wdx = this.getDayInCurrMonthWeek(seekDay)
+                let setData = {
+                    _currWeekIdx: wdx,
+                    [`months[${ idx }].bar`]: this.initSelBar(seekIdx, seekDay, _month.days.length, wdx, vibrate)
+                }
+                typeof callback === 'function' && (setData = callback(setData, seekDay, seekIdx))
+                this.setData(setData, () => {
+                    this.setTitleInfo(seekDay)
+                    this.triggerSel(seekDay)
+                })
+            }
+        },
+        swiperTo(type, d, current) {
+            current = current ? current : this.data.currTab
+            const _current = type == 'prev' ? (current + 4) % 5 : (current + 1) % 5
+            this.setData({
+                _selDay: d,
+                _selWeek: d.week,
+                currTab: _current
+            })
+        },
+        selDate(e) {
+            if (this._rectsLoading) return
+            const currTab = this.data.currTab
+            const { wdx, ddx } = e.currentTarget.dataset
+            const idx = wdx * 7 + ddx
+            const { idays, month } = this.data.months[currTab]
+            const seek = idays[idx]
+            if (this.data.currView != 2) {
+                if (seek.type == 'prev' || seek.type == 'next') {
+                    this.swiperTo(seek.type, seek, currTab)
+                } else {
+                    this.setDate(idx, currTab, (setData, _s, _w) => {
+                        if (_w === this.data._currWeekIdx) return setData
+                        setData = Object.assign({}, setData, this.refreshAllTrans(_s))
+                        return setData
+                    })
+                }
+            } else {
+                if (seek.month != month) {
+                    let newMonth = this.getMonthByWeekDayInIdx(seek)
+                    this.setData({
+                        [`months[${ currTab }]`]: this.handelWeekMonthChange(this.data._selDay, newMonth),
+                        monthchange: true,
+                        barAni: false
+                    }, () => {
+                        this.handleWeekMonthChangeSel(seek, currTab, newMonth)
+                    })
+                } else {
+                    this.setDate(idx, currTab)
+                }
+            }
+        },
+        handelWeekMonthChange(d, newMonth) {
+            const seekIdx = newMonth.idays.findIndex(_d => (_d.month == d.month && _d.day == d.day))
+            if (seekIdx >= 0) {
+                const seekDay = newMonth.idays[seekIdx]
+                newMonth.bar = this.initSelBar(seekIdx, seekDay, newMonth.days.length, Math.floor(seekIdx / 7), false)
+            }
+            return newMonth
+        },
+        handleWeekMonthChangeSel(seek, currTab, month) {
+            const seekIdx = month.idays.findIndex(_d => (_d.month == seek.month && _d.day == seek.day))
+            if (seekIdx >= 0) {
+                const seekDay = month.idays[seekIdx]
+                const wdx = Math.floor(seekIdx / 7)
+                let setData = {
+                    _currWeekIdx: wdx,
+                    _selDay: seekDay,
+                    _selWeek: seekDay.week,
+                    barAni: true,
+                    [`months[${ currTab }].bar`]: this.initSelBar(seekIdx, seekDay, month.days.length, wdx, true)
+                }
+                this.setData(setData, () => {
+                    this.setTitleInfo(seekDay)
+                    this.triggerSel(seekDay)
+                })
+            }
+        },
+        setDate(idx, currTab, callback) {
+            const month = this.data.months[currTab]
+            const seek = month.idays[idx]
+            const wdx = this.getDayInCurrMonthWeek(seek)
+            let setData = {
+                _selDay: seek,
+                _selWeek: seek.week,
+                _currWeekIdx: wdx,
+                [`months[${ currTab }].bar`]: this.initSelBar(idx, seek, month.days.length, wdx, true)
+            }
+            if (typeof callback === 'function') setData = callback(setData, seek, wdx)
+            this.setData(setData, () => {
+                this.setTitleInfo(seek)
+                this.triggerSel(seek)
+            })
+        },
+        refreshAllTrans(seek) {
+            const _trans = this.getMonthsTrans(seek, this.data.currTab)
+            return (_ => {
+                for (let i = 0; i < 5; i++) {
+                    _[`months[${ i }].trans`] = _trans[i]
+                }
+                return _
+            })(new Object)
+        },
+        handleSelBarAniEnd(e) {
+            const currTab = this.data.currTab
+            const key = `months[${ currTab }].bar.a`
+            this.setData({
+                [key]: false,
+                tdOpShow: !this.data.months[currTab].bar.t
+            })
+            if (this.data.months[currTab].bar.v && this.data._vibrate) {
+                wx.vibrateShort({
+                    type: 'light'
+                })
+            }
+        },
+        handleSwiperAniEnd(e) {
+            const { current, source } = e.detail
+            if (source == 'touch') {
+                if (current != this.data.currTab) {
+                    this.setCurrTab(current)
+                }
+            } else {
+                this.refrenMonths(current, true)
+            }
+        },
+        setCurrTab(currTab) {
+            this.setData({ currTab }, () => {
+                this.refrenMonths(currTab)
+            })
+        },
+        refrenMonths(current, v = false) {
+            if (this.data.currView == 2) {
+                this.refreshMonthWeekStatus(current, v)
+            } else {
+                this.refreshMonthStatus(current, v)
+            }
+        },
+        refreshMonthStatus(current, v = false) {
+            const currMonth = this.data.months[current]
+            this.getDayCurr(current, currMonth.month, (setData, seekday) => {
+                setData._selDay = seekday
+                return setData
+            }, v)
+            const _trans = this.getMonthsTrans(this.data._selDay, current)
+            this.refreshAllMonth(currMonth, current, _trans, setData => {
+                setData[`months[${ current }].trans`] = _trans[current]
+                return setData
+            }, () => {
+                this.triggerChange('month')
+            })
+        },
+        refreshAllMonth(currMonth, current, _trans, _scallback, _acallback) {
+            const { year, month } = currMonth
+            const idx_1 = (current + 3) % 5
+            const idx_2 = (current + 4) % 5
+            const idx_3 = (current + 1) % 5
+            const idx_4 = (current + 2) % 5
+            const d1 = DayDetail(year, month - 2, 1)
+            const d2 = DayDetail(year, month - 1, 1)
+            const d3 = DayDetail(year, month + 1, 1)
+            const d4 = DayDetail(year, month + 2, 1)
+            let setData = {
+                [`months[${ idx_1 }]`]: this.refreshMonth(d1, idx_1, _trans),
+                [`months[${ idx_2 }]`]: this.refreshMonth(d2, idx_2, _trans),
+                [`months[${ idx_3 }]`]: this.refreshMonth(d3, idx_3, _trans),
+                [`months[${ idx_4 }]`]: this.refreshMonth(d4, idx_4, _trans)
+            }
+            if (typeof _scallback === 'function') setData = _scallback(setData)
+            this.setData(setData, () => {
+                typeof _acallback === 'function' && _acallback()
+            })
+        },
+        refreshMonth(d, idx, _trans) {
+            return this.initCalendarMonth(d, _trans[idx])
+        },
+        refreshMonthWeekStatus(current, v = false) {
+            this.getDayCurrForWeek(current, () => {
+                this.setMonthsForWeek(current, setData => {
+                    setData.weektabchange = current
+                    return setData
+                }, () => {
+                    this.triggerChange('week')
+                })
+            }, v)
+        },
+        getDayCurrForWeek(current, callback, v = false) {
+            const month = this.data.months[current]
+            const wf = month.wf
+            const newSelDay = CorrectDate(wf.year, wf.month, wf.day + this.data._selDay.week)
+            const seekIdx = month.idays.findIndex(_d => (_d.month == newSelDay.month && _d.day == newSelDay.day))
+            if (seekIdx >= 0) {
+                const seekDay = month.idays[seekIdx]
+                const wdx = this.getDayInCurrMonthWeek(seekDay)
+                this.setData({
+                    _selDay: seekDay,
+                    _selWeek: seekDay.week,
+                    _currWeekIdx: wdx,
+                    [`months[${ current }].bar`]: this.initSelBar(seekIdx, seekDay, month.days.length, wdx, v)
+                }, () => {
+                    this.setTitleInfo(seekDay)
+                    this.triggerSel(seekDay)
+                    typeof callback === 'function' && callback()
+                })
+            }
+        },
+        setTitleInfo(d, today = null) {
+            today = today ? today : this.data._today
+            let titleInfo = this.data.titleInfo
+            if (d.year == today.year && d.month == today.month && d.day == today.day) {
+                titleInfo = this._curr_view == 2 ? `第${ YearWeekOrder(today.year, today.month, today.day) }周  ${ today.week_name }` : today.week_name
+            } else {
+                const count = this.getDateDiff(new Date(today.year, today.month - 1, today.day), new Date(d.year, d.month - 1, d.day))
+                titleInfo = `${ this._curr_view == 2 ? '第' + YearWeekOrder(d.year, d.month, d.day) + '周  ' : ''}${ Math.abs(count) }天${ count < 0 ? '前' : '后' }`
+            }
+            this.setData({ titleInfo })
+        },
+        getDateDiff(startDate, endDate) {
+            return Math.floor((endDate.getTime() - startDate.getTime()) / (1000 * 60 * 60 * 24))
+        },
+        toggleView({ state }) {
+            this._curr_view = state
+        },
+        handleCalendarTransEnd() {
+            const noChanged = ((this.data.currView == 1 || this.data.currView == 3) && (this._curr_view == 1 || this._curr_view == 3) || this._curr_view == this.data.currView)
+            if (!noChanged) {
+                this.triggerView(this._curr_view)
+                this.setData({
+                    currView: this._curr_view
+                }, () => {
+                    const { _selDay, currTab } = this.data
+                    if (this._curr_view == 2) {
+                        this.setMonthsForWeek(currTab, setData => {
+                            setData.weektabchange = currTab
+                            if (this._curr_view == 1) setData.yearPanelShow = this._year_panel_show
+                            return setData
+                        }, () => {
+                            this.triggerChange('week')
+                        })
+                    } else {
+                        const _trans = this.getMonthsTrans(_selDay, this.data.months[currTab].trans, true)
+                        this.refreshAllMonth(_selDay, currTab, _trans, setData => {
+                            if (this._curr_view == 1) setData.yearPanelShow = this._year_panel_show
+                            return setData
+                        }, () => {
+                            this.triggerChange('month')
+                        })
+                    }
+                    this.setTitleInfo(_selDay)
+                })
+            } else if (this._curr_view != this.data.currView) {
+                let setData = { currView: this._curr_view }
+                if (this._curr_view == 1) setData.yearPanelShow = this._year_panel_show
+                this.setData(setData)
+            }
+        },
+        handleOpBarTransEnd() {
+            this.setData({
+                solidDay: this._curr_view != 2
+            })
+        },
+        toToday() {
+            if (this.data.tdOpShow) {
+                if (this.data.currView == 2) {
+                    this.handleWeekToDate(this.data._today)
+                } else {
+                    this.handleMonthToToday(this.data._today)
+                }
+            }
+        },
+        handleDayInOtherMonth(day, tab, callback) {
+            let month = this.data.months[tab]
+            if (day.month != month.month) {
+                let newMonth = this.getMonthByWeekDayInIdx(day)
+                this.setData({
+                    [`months[${ tab }]`]: this.handelWeekMonthChange(this.data._selDay, newMonth),
+                    monthchange: true,
+                    barAni: false
+                }, () => {
+                    typeof callback === 'function' && callback()
+                })
+            } else {
+                typeof callback === 'function' && callback()
+            }
+        },
+        handleWeekToDate(date) {
+            const todayDate = (new Date(date.year, date.month - 1, date.day)).getTime()
+            const isInSwiper = this.data.months.findIndex(_m => {
+                let startDate = (new Date(_m.wf.year, _m.wf.month - 1, _m.wf.day)).getTime()
+                let endDate = (new Date(_m.wf.year, _m.wf.month - 1, _m.wf.day + 7)).getTime()
+                return startDate <= todayDate && endDate > todayDate
+            })
+            const currTab = this.data.currTab
+            if (isInSwiper >= 0) {
+                if (isInSwiper == currTab) {
+                    this.handleDayInOtherMonth(date, isInSwiper, () => {
+                        const idx = this.getDayInCurrMonth(date)
+                        if (idx >= 0) this.setDate(idx, currTab, (setData, _s, _w) => {
+                            if (_w === this.data._currWeekIdx) return setData
+                            setData = Object.assign({}, setData, this.refreshAllTrans(_s))
+                            setData.barAni = true
+                            return setData
+                        })
+                    })
+                } else {
+                    this.setData({
+                        currTab: isInSwiper
+                    }, () => {
+                        this.handleDayInOtherMonth(date, isInSwiper, () => {
+                            const _idx = this.data.months[isInSwiper].idays.findIndex(_d => (_d.month == date.month && _d.day == date.day))
+                            this.setDate(_idx, isInSwiper, (setData, _s, _w) => {
+                                setData = Object.assign({}, setData, this.refreshAllTrans(_s))
+                                setData.barAni = true
+                                return setData
+                            })
+                        })
+                    })
+                }
+            } else {
+                this.setData({
+                    _selDay: date,
+                    _selWeek: date.week
+                }, () => {
+                    this.setMonthsForWeek(currTab, setData => {
+                        let month = this.getMonthByWeekDayInIdx(date)
+                        month.wf = setData[`months[${ currTab }].wf`]
+                        delete(setData[`months[${ currTab }].wf`])
+                        setData[`months[${ currTab }]`] = month
+                        setData.monthchange = true
+                        setData.weektabchange = this.data.currTab
+                        return setData
+                    }, () => {
+                        this.getDayCurr(currTab, date.month, null, true)
+                        this.triggerChange('week')
+                    })
+                })
+            }
+        },
+        handleMonthToToday(date) {
+            const isInSwiper = this.data.months.findIndex(_m => (_m.year == date.year && _m.month == date.month))
+            const currTab = this.data.currTab
+            if (isInSwiper >= 0) {
+                if (currTab == isInSwiper) {
+                    const idx = this.getDayInCurrMonth(date)
+                    if (idx >= 0) this.setDate(idx, currTab, (setData, _s, _w) => {
+                        if (_w === this.data._currWeekIdx) return setData
+                        setData = Object.assign({}, setData, this.refreshAllTrans(_s))
+                        return setData
+                    })
+                } else {
+                    this.setData({
+                        currTab: isInSwiper
+                    }, () => {
+                        const _idx = this.data.months[isInSwiper].idays.findIndex(_d => (_d.month == date.month && _d.day == date.day))
+                        this.setDate(_idx, isInSwiper, (setData, _s, _w) => {
+                            setData = Object.assign({}, setData, this.refreshAllTrans(_s))
+                            return setData
+                        })
+                    })
+
+                }
+            } else {
+                const d = CorrectDate(date.year, date.month, 1)
+                const _trans = this.getMonthsTrans(date, currTab)
+                this.setData({
+                    [`months[${ currTab }]`]: this.refreshMonth(d, currTab, _trans),
+                    _selDay: date,
+                    _selWeek: date.week
+                }, () => {
+                    this.getDayCurr(currTab, date.month, null, true)
+                })
+                this.refreshAllMonth(date, currTab, _trans, null, () => {
+                    this.triggerChange('month')
+                })
+            }
+        },
+        getDayInCurrMonth(d) {
+            const _ = this.data.months[this.data.currTab].idays
+            return _.findIndex(_d => (d.year == _d.year && d.month == _d.month && d.day == _d.day))
+        },
+        getDayInCurrMonthWeek(d) {
+            const _ = this.data.months[this.data.currTab].idays
+            return this.getDayWeekIdxInMonth(d, _)
+        },
+        getDayWeekIdxInMonth(d, _) {
+            const idx = _.findIndex(_d => (d.month == _d.month && d.day == _d.day))
+            return Math.floor(idx / 7)
+        },
+        setMonthsForWeek(current, callback, userFunc) {
+            const currDay = this.data._selDay
+            this.setWeekMonths(currDay, current, callback, userFunc)
+        },
+        setWeekMonths(d, current, callback, userFunc) {
+            const { year, month, day } = d
+            current = current ? current : this.data.currTab
+            const idx_1 = (current + 3) % 5
+            const idx_2 = (current + 4) % 5
+            const idx_3 = (current + 1) % 5
+            const idx_4 = (current + 2) % 5
+            const d1 = DayDetail(year, month, day - 14)
+            const d2 = DayDetail(year, month, day - 7)
+            const d3 = DayDetail(year, month, day + 7)
+            const d4 = DayDetail(year, month, day + 14)
+            let setData = {
+                [`months[${ idx_1 }]`]: this.getMonthByWeekDayInIdx(d1),
+                [`months[${ idx_2 }]`]: this.getMonthByWeekDayInIdx(d2),
+                [`months[${ current }].wf`]: WeekFirstDay(d.year, d.month, d.day),
+                [`months[${ idx_3 }]`]: this.getMonthByWeekDayInIdx(d3),
+                [`months[${ idx_4 }]`]: this.getMonthByWeekDayInIdx(d4),
+            }
+            if (typeof callback === 'function') setData = callback(setData)
+            this.setData(setData, () => {
+                typeof userFunc === 'function' && userFunc()
+            })
+        },
+        getMonthByWeekDayInIdx(d) {
+            return this.initCalendarMonth(
+                d,
+                (ids, ds) => this.calcTrans(this.getDayWeekIdxInMonth(d, ids.days), ds.length),
+                WeekFirstDay(d.year, d.month, d.day)
+            )
+        },
+        calcTrans(wdx, wlength) {
+            const defaultDayHeight = this.data.panelHeight / 5
+            const currentDayHeight = wlength == 5 ? defaultDayHeight : this.data.panelHeight / wlength
+            const dayHeightDifference = wlength == 5 ? 0 : (defaultDayHeight - currentDayHeight) / 2
+            return Math.floor((wdx * currentDayHeight - dayHeightDifference) * 100) / 100
+        },
+        getMonthsTrans(d, current, isCurrTrans = false) {
+            const { year, month, day } = d
+            const idx_1 = (current + 3) % 5
+            const idx_2 = (current + 4) % 5
+            const idx_3 = (current + 1) % 5
+            const idx_4 = (current + 2) % 5
+            const d1 = CorrectDate(year, month, day - 14)
+            const d2 = CorrectDate(year, month, day - 7)
+            const d3 = CorrectDate(year, month, day + 7)
+            const d4 = CorrectDate(year, month, day + 14)
+            const arr = Array.apply(null, { length: 5 }).map(_ => 0)
+            arr[idx_1] = this.getTransByDayInMonth(d1)
+            arr[idx_2] = this.getTransByDayInMonth(d2)
+            arr[current] = isCurrTrans ? current : this.getTransByDayInMonth(d)
+            arr[idx_3] = this.getTransByDayInMonth(d3)
+            arr[idx_4] = this.getTransByDayInMonth(d4)
+            return arr
+        },
+        getTransByDayInMonth(d) {
+            const idays = this.getMonthDays(d).days
+            return this.calcTrans(this.getDayWeekIdxInMonth(d, idays), idays.length / 7)
+        },
+        setYearMs(year, current, today) {
+            today = today ? today : this.data._today
+            let years = Array.apply(null, { length: 5 })
+            years[(current + 3) % 5] = year - 2
+            years[(current + 4) % 5] = year - 1
+            years[current] = year
+            years[(current + 1) % 5] = year + 1
+            years[(current + 2) % 5] = year + 2
+            return Array.apply(null, { length: 5 }).map((_, i) => this.getYearMonth(years[i], today))
+        },
+        refreshYearMs(year, current, callback) {
+            const today = this.data._today
+            const idx_1 = (current + 3) % 5
+            const idx_2 = (current + 4) % 5
+            const idx_3 = (current + 1) % 5
+            const idx_4 = (current + 2) % 5
+            const y1 = year - 2
+            const y2 = year - 1
+            const y3 = year + 1
+            const y4 = year + 2
+            this.setData({
+                [`yearMs[${ idx_1 }]`]: this.getYearMonth(y1, today),
+                [`yearMs[${ idx_2 }]`]: this.getYearMonth(y2, today),
+                [`yearMs[${ idx_3 }]`]: this.getYearMonth(y3, today),
+                [`yearMs[${ idx_4 }]`]: this.getYearMonth(y4, today)
+            }, () => {
+                typeof callback === 'function' && callback()
+            })
+        },
+        getYearMonth(year, today) {
+            today = today ? today : this.data._today
+            let { lunar_year } = LunarDetail(year, 6, 1)
+            return {
+                year,
+                lunar_year,
+                key: `yp_${ year }`,
+                months: Months.map(m => {
+                    return {
+                        month: m,
+                        curr: today.year == year && today.month == m,
+                        lunar: this.getMonthLunarMonthFirst(year, m)
+                    }
+                })
+            }
+        },
+        getMonthLunarMonthFirst(year, month) {
+            const monthDays = MonthOnly(year, month)
+            return monthDays.filter(d => d.ld == 1).map(d => {
+                let { day, lunar_month } = d
+                return { day, lunar: lunar_month, order: day > 3 ? EnOrders[4] : EnOrders[day] }
+            })
+        },
+        handleYmSwiperAniEnd(e) {
+            const { current, source } = e.detail
+            const year = this.data.yearMs[current].year
+            if (source == 'touch') {
+                if (current != this.data.currYmTab) {
+                    this.setData({
+                        currYmTab: current
+                    }, () => {
+                        this.refreshYearMs(year, current)
+                    })
+                }
+            } else {
+                this.refreshYearMs(year, current)
+            }
+        },
+        justYearPanelShow() {
+            this._year_panel_show = true
+            this.setData({
+                yearPanelShow: true
+            })
+        },
+        handleYearPanelShow() {
+            this._year_panel_show = true
+        },
+        handleYearPanelDayClick(e) {
+            const { year, month } = e.currentTarget.dataset
+            const currTab = this.data.currTab
+            const currMonth = this.data.months[currTab]
+            if (currMonth.year == year && currMonth.month == month) {
+                this._year_panel_show = false
+                this.setData({
+                    yearPanelShow: false
+                })
+            } else {
+                const d = this.getMonthDay(year, month, this.data._selDay.day)
+                const _trans = this.getMonthsTrans(d, currTab)
+                this._year_panel_show = false
+                this.setData({
+                    [`months[${ currTab }]`]: this.refreshMonth(d, currTab, _trans),
+                    _selDay: d,
+                    _selWeek: d.week,
+                    yearPanelShow: false
+                }, () => {
+                    this.getDayCurr(currTab, d.month)
+                })
+                this.refreshAllMonth(d, currTab, _trans, null, () => {
+                    this.triggerChange('month')
+                })
+            }
+        },
+        getMonthDay(year, month, day) {
+            const dayCount = MonthOnly(year, month).length
+            const today = this.data._today
+            day = day > dayCount ? dayCount : day
+            let date = this.replenishDateInfo(DayDetail(year, month, day), today)
+            return date
+        },
+        getCorrectDate() {
+            if (arguments.length == 1) {
+                let date
+                if (typeof arguments[0] === 'string') {
+                    const strDates = arguments[0].split('-').map(_ => parseInt(_))
+                    if (strDates.length < 3) {
+                        this.console('日期格式错误', 'warn')
+                        return null
+                    }
+                    strDates[1] = strDates[1] - 1
+                    date = new Date(...strDates)
+                } else {
+                    date = new Date(arguments[0])
+                }
+                if (isNaN(date.getTime())) {
+                    this.console('日期格式错误', 'warn')
+                    return null
+                }
+                const year = date.getFullYear()
+                const month = date.getMonth() + 1
+                const day = date.getDate()
+                return { year, month, day }
+            } else if (arguments.length == 3) {
+                const [_y, _m, _d] = arguments
+                const [y, m, d] = [_y, _m, _d].map(_ => parseInt(_))
+                const { year, month, day } = CorrectDate(y, m, d)
+                return { year, month, day }
+            } else {
+                this.console('日期格式错误', 'warn')
+                return null
+            }
+        },
+        calendarToDate() {
+            const date = this.getCorrectDate(...arguments)
+            if (date) {
+                const today = this.data._today
+                let d = this.replenishDateInfo(DayDetail(date.year, date.month, date.day), today)
+                if (this.data.currView == 2) {
+                    this.handleWeekToDate(d)
+                } else {
+                    this.handleMonthToToday(d)
+                }
+            }
+        },
+        calendarToMonth(year, month) {
+            const d = DayDetail(year, month, 1)
+            if (this.data.currView == 2) {
+                this.handleWeekToDate(d)
+            } else {
+                this.handleMonthToToday(d)
+            }
+        },
+        calendarPrev() {
+            const _prev = (this.data.currTab + 4) % 5
+            this.setCurrTab(_prev)
+        },
+        calendarNext() {
+            const _next = (this.data.currTab + 1) % 5
+            this.setCurrTab(_next)
+        },
+        switchCalendarView(view) {
+            view = view ? view : 'month'
+            view = (view != 'month' && view != 'week') ? 'month' : view
+            this.setData({
+                viewchange: view
+            })
+        },
+        getCalendarDateInfo() {
+            const { year, month, day } = this.getCorrectDate(...arguments)
+            return this.replenishDateInfo(DayDetail(year, month, day), this.data._today)
+        },
+        console(tips, type = 'info', ...args) {
+            const { label, content, title } = ConsoleStyle[type]
+            console.log(`%cWxCalendar${ title } %c${ tips }`, label, content, ...args)
+        }
+    },
+    export () {
+        if (this.data.loading) {
+            this.console('请在bindload回调后执行selectComponent', 'warn')
+            return null
+        }
+        const { minHeight, maxHeight, calendarHeight } = this.data
+        const calendarInstance = this
+        return {
+            name: 'wx-calendar',
+            version: Version,
+            minHeight,
+            maxHeight,
+            height: calendarHeight,
+            getDateInfo() {
+                return calendarInstance.getCalendarDateInfo(...arguments)
+            },
+            setMarkers() {
+                calendarInstance.handleDynamicMarkers(arguments[0])
+            },
+            addMarker() {
+                calendarInstance.addMarker(arguments[0])
+            },
+            editMarker() {
+                calendarInstance.editMarker(arguments[0])
+            },
+            delMarker() {
+                const [date, type, key] = arguments
+                calendarInstance.delMarker(date, type, key)
+            },
+            reloadMarkers() {
+                calendarInstance.handleDynamicMarkers(calendarInstance.data._markers)
+            },
+            toDate() {
+                calendarInstance.calendarToDate(...arguments)
+            },
+            toMonth() {
+                const [year, month] = arguments
+                calendarInstance.calendarToMonth(year, month)
+            },
+            toggleView() {
+                calendarInstance.switchCalendarView(arguments[0])
+            },
+            prev() {
+                calendarInstance.calendarPrev()
+            },
+            next() {
+                calendarInstance.calendarNext()
+            },
+            reloadPos() {
+                return calendarInstance.reloadPos()
+            }
+        }
+    }
+})

+ 4 - 0
components/wx-calendar/index.json

@@ -0,0 +1,4 @@
+{
+    "component": true,
+    "styleIsolation": "isolated"
+}

+ 174 - 0
components/wx-calendar/index.wxml

@@ -0,0 +1,174 @@
+<wxs module="calendar_wxs" src="./index.wxs"></wxs>
+<view class="wd-calendar-container {{ darkmode ? 'darkmode' : '' }} {{ checkedShow ? '' : 'sel-hidden' }}" style="{{ style }}">
+    <view 
+        id="calendar" 
+        class="wd-calendar {{ !loading ? 'load' : '' }}" 
+        style="height: {{ view == 'week' ? minHeight : calendarHeight }}px;"
+        catchtransitionend="handleCalendarTransEnd"
+        mark:panel="{{ panelHeight }}"
+        mark:calendar="{{ calendarHeight }}"
+        mark:max="{{ maxHeight }}"
+        mark:min="{{ minHeight }}"
+        mark:view="{{ currView }}"
+        data-panel="{{ panelHeight }}"
+        data-calendar="{{ calendarHeight }}"
+        data-min="{{ minHeight }}">
+        <view class="wd-calendar-bar">
+            <view class="wd-calendar-title" data-info="{{ titleInfo }}" bindtap="{{ calendar_wxs.handleYearPanelShow }}">
+                <text >{{ months[currTab].year }}</text><text class="wd-calendar-title-cn">年</text>
+                <text >{{ months[currTab].month }}</text>
+                <text class="wd-calendar-title-cn">月</text>
+            </view>
+            <view class="wd-calendar-options">
+                <view class="wd-calendar-option">
+                    <view class="wd-calendar-option-it ">
+                        <view class="wd-calendar-option-views today {{ tdOpShow ? 'show' : '' }}" catchtap="toToday">
+                            <view class="wd-calendar-option-view">今</view>
+                        </view>
+                    </view>
+                </view>
+                <view class="wd-calendar-option">
+                    <view class="wd-calendar-option-it">
+                        <view class="wd-calendar-option-views" catchtap="{{ calendar_wxs.toggleView }}">
+                            <view catchtransitionend="handleOpBarTransEnd" class="wd-calendar-option-view {{ (currView == 1 || currView == 3)? 'curr' : '' }}">月</view>
+                            <view catchtransitionend="handleOpBarTransEnd" class="wd-calendar-option-view {{ currView == 2 ? 'curr' : '' }}">周</view>
+                            <view class="wd-calendar-option-view-bar" style="transform: translateX({{ currView == 2 ? '70rpx' : 0 }}) translateZ(0px);"></view>
+                        </view>
+                    </view>
+                </view>
+            </view>
+        </view>
+        <view class="wd-calendar-week-bar">
+            <view class="wd-calendar-week-item" wx:for="{{ Weeks }}">{{ item }}</view>
+        </view>
+        <view 
+            class="wd-calendar-month-body {{ solidDay ? '' : 'solid' }}" 
+            change:weektabchange="{{ calendar_wxs.handleWeekSwiperChange }}" weektabchange="{{ weektabchange }}"
+            change:monthchange="{{ calendar_wxs.handleMonthChange }}" monthchange="{{ monthchange }}"
+            change:needInitTrans="{{ calendar_wxs.handleInitTrans }}" needInitTrans="{{ needInitTrans }}"
+            change:viewchange="{{ calendar_wxs.handleViewChange }}" viewchange="{{ viewchange }}"
+            bindtouchstart="{{ calendar_wxs.touchStart }}"
+            catchtouchmove="{{ calendar_wxs.touchMove }}" 
+            catchtouchend="{{ calendar_wxs.touchEnd }}">
+            <swiper circular duration="{{ 300 }}" current="{{ currTab }}" class="wd-calendar-swiper" bindanimationfinish="handleSwiperAniEnd">
+                <swiper-item
+                    wx:for="{{ months }}" 
+                    wx:for-item="month" 
+                    wx:for-index="mdx"
+                    skip-hidden-item-layout
+                    class="wd-calendar-swiper-item" >
+                    <view 
+                        class="wd-calendar-month-panel {{ currTab === mdx ? 'curr' : 'other' }}" 
+                        style="height: {{ panelHeight }}px;min-height: {{ panelHeight }}px;"
+                        data-trans="{{ month.trans }}">
+                        <view 
+                            wx:if="{{ month.bar.s && currTab === mdx && checkedShow }}"
+                            class="wd-calendar-day-sel {{ barAni ? '' : 'no-ani' }} {{ month.bar.t ? 'today' : '' }} " 
+                            style="top: {{ month.bar.y }};left: {{ month.bar.x }}px;" >
+                            <view class="wd-calendar-day-sel-bar {{ month.bar.a ? 'animation' : '' }}" bindanimationend="handleSelBarAniEnd"></view>
+                        </view>
+                        <view 
+                            class="wd-calendar-week-row l-{{ month.days.length }}" 
+                            wx:for="{{ month.days }}" 
+                            wx:for-item="w" 
+                            wx:for-index="wdx" >
+                            <view 
+                                wx:for="{{ w.days }}" 
+                                wx:for-item="d" 
+                                wx:for-index="ddx"
+                                class="wd-calendar-day {{ d.type }} {{ d.isToday ? 'today' : ''  }} {{ ((wdx * 7 + ddx) == month.bar.i && currTab === mdx) ? 'curr' : '' }}" 
+                                catchtap="selDate"
+                                data-wdx="{{ wdx }}"
+                                data-ddx="{{ ddx }}">
+                                <view class="wd-calendar-gregorian {{ (currView != 3 && d.marker && d.marker.schedule.length > 0) ? 'dot' : '' }}">
+                                    <text>{{ d.day }}</text>
+                                    <view 
+                                        class="wd-calendar-gregorian-corner" 
+                                        wx:if="{{ d.marker && d.marker.corner.length > 0 }}"
+                                        style="{{ d.marker.corner[0].color ? 'color:' + d.marker.corner[0].color + ';' : '' }}"
+                                        >{{ d.marker.corner[0].mark }}</view>
+                                </view>
+                                <view class="wd-calendar-solar {{ d.lunar_type }}">
+                                    <text 
+                                        wx:if="{{ d.marker && d.marker.holiday.length > 0 }}"
+                                        style="color: {{ d.marker.corner[0].color ? d.marker.corner[0].color : '#2a97ff' }};">{{ d.marker.holiday[0].mark }}</text>
+                                    <text wx:else>{{ d.lunar_day }}</text>
+                                    <view class="wd-calendar-schedules" wx:if="{{ d.marker && d.marker.schedule.length > 0 }}">
+                                        <view 
+                                            class="wd-calendar-schedule"
+                                            style="{{ d.marker.schedule[0].color ? 'color:' + d.marker.schedule[0].color + ';' : '' }}{{ d.marker.schedule[0].bgColor ? 'background-color:' + d.marker.schedule[0].bgColor + ';' : '' }}">{{ d.marker.schedule[0].mark }}</view>
+                                        <view class="wd-calendar-schedule-more" wx:if="{{ d.marker.schedule.length - 1 > 0 }}">+{{ d.marker.schedule.length - 1 }}</view>
+                                    </view>
+                                </view>
+                            </view>
+                        </view>
+                    </view>
+                </swiper-item>
+            </swiper>
+        </view>
+        <view 
+            class="wd-calendar-bt-bar"
+            bindtouchstart="{{ calendar_wxs.touchStart }}"
+            catchtouchmove="{{ calendar_wxs.touchMove }}" 
+            catchtouchend="{{ calendar_wxs.touchEnd }}">
+            <view class="wd-calendar-bt-control">
+                <view class="wd-calendar-bt-control-item" >
+                    <view class="wd-calendar-bt-control-bar" id="control_1"></view>
+                </view>
+                <view class="wd-calendar-bt-control-item" >
+                    <view class="wd-calendar-bt-control-bar" id="control_2"></view>
+                </view>
+            </view>
+        </view>
+
+        <view class="wd-calendar-years-panel {{ yearPanelShow ? 'show' : '' }}">
+            <view class="wd-calendar-bar">
+                <view class="wd-calendar-title show ym" data-info="{{ yearMs[currYmTab].lunar_year }}">
+                    <text >{{ yearMs[currYmTab].year }}</text><text class="wd-calendar-title-cn margin-left">年</text>
+                </view>
+            </view>
+            <view class="wd-calendar-years-panel-body">
+                <swiper 
+                    circular
+                    duration="{{ 300 }}"
+                    current="{{ currYmTab }}" 
+                    class="wd-calendar-ym-swiper" 
+                    bindanimationfinish="handleYmSwiperAniEnd">
+                    <swiper-item
+                        wx:for="{{ yearMs }}" 
+                        wx:for-item="year" 
+                        wx:for-index="ydx"
+                        skip-hidden-item-layout
+                        class="wd-calendar-ym-swiper-item" >
+                        <view class="wd-calendar-ym-months">
+                            <view class="wd-calendar-ym-row" wx:for="{{ 3 }}" wx:for-item="mr" wx:for-index="mrdx" >
+                                <view 
+                                    wx:for="{{ 4 }}" 
+                                    wx:for-item="m" 
+                                    wx:for-index="mdx"
+                                    class="wd-calendar-ym-month {{ year.months[mrdx * 4 + mdx].curr ? 'curr' : '' }}"
+                                    bindtap="handleYearPanelDayClick"
+                                    data-year="{{ year.year }}"
+                                    data-month="{{ year.months[mrdx * 4 + mdx].month }}">
+                                    <view class="wd-calendar-ym-month-contaner">
+                                        <view class="wd-calendar-ym-m-r">
+                                            <view class="wd-calendar-ym-m">{{ year.months[mrdx * 4 + mdx].month }}</view>
+                                        </view> 
+                                        <view wx:for="{{ year.months[mrdx * 4 + mdx].lunar }}" wx:for-item="l" wx:for-index="idx" class="wd-calendar-ym-l" >
+                                            <view class="wd-calendar-ym-l-i"><text data-order="{{ l.order }}">{{ l.day }}</text></view>
+                                            <view class="wd-calendar-ym-l-i"><text >{{ l.lunar }}</text></view>
+                                        </view>
+                                    </view>
+                                </view>
+                            </view>
+                        </view>
+                    </swiper-item>
+                </swiper>
+            </view>
+        </view>
+    </view>
+    <view class="wd-calendar-content">
+        <slot></slot>
+    </view>
+</view>
+

+ 307 - 0
components/wx-calendar/index.wxs

@@ -0,0 +1,307 @@
+var touchStart = function(e, ins) {
+    var toucheY = e.changedTouches[0].pageY
+    var dataset = e.mark
+    var state = ins.getState()
+    state.calendarPageY = toucheY
+    state.calendarHeight = dataset.calendar
+    state.panelHeight = dataset.panel
+    state.calendarMax = dataset.max
+    state.calendarMin = dataset.min
+    state.calendarMoving = false
+    if (!state.calendarCurrHeight) state.calendarCurrHeight = dataset.calendar
+    if (dataset.view == 1) state.calendarCurrHeight = dataset.calendar
+    if (dataset.view == 2) state.calendarCurrHeight = dataset.min
+    if (dataset.view == 3) state.calendarCurrHeight = dataset.max
+}
+
+var touchMove = function(e, ins) {
+    var state = ins.getState()
+    if (state.calendarMoving) return false
+    state.calendarMoving = true
+    var diff = Math.floor((e.changedTouches[0].pageY - state.calendarPageY) * 10) / 10
+    if ((state.calendarCurrHeight + diff) >= state.calendarMin && (state.calendarCurrHeight + diff) <= state.calendarMax) {
+        //calendar移动距离
+        ins.selectComponent('#calendar').setStyle({
+            transition: 'unset',
+            height: (state.calendarCurrHeight + diff) + 'px'
+        })
+        var currPanel = ins.selectComponent('.wd-calendar-month-panel.curr')
+        if ((state.calendarCurrHeight + diff) >= state.calendarHeight) {
+            currPanel.setStyle({
+                transition: 'unset',
+                height: 'calc(' + (state.calendarCurrHeight + diff) + 'px - 200rpx)'
+            })
+        }
+        if ((state.calendarCurrHeight + diff) < state.calendarHeight) {
+            var totalTrans = currPanel.getDataset().trans
+            var totalTransDis = state.calendarHeight - state.calendarMin
+            var panleTrans = Math.floor(10 * totalTrans * Math.abs(state.calendarCurrHeight + diff - state.calendarHeight) / totalTransDis) / 10
+            currPanel.setStyle({
+                transition: 'unset',
+                transform: 'translateY(' + (-panleTrans) + 'px)'
+            })
+        }
+        if (((state.calendarCurrHeight + diff) < (state.calendarMax - 40))) {
+            var schedules = ins.selectAllComponents('.wd-calendar-schedules')
+            for (var i = 0; i < schedules.length; i++) {
+                schedules[i].removeClass('show')
+            }
+        }
+        //底部控制条样式
+        var director = diff > 0 ? 1 : -1
+        var deg = 0
+        if (Math.abs(diff) < 120) {
+            deg = director * Math.floor(Math.abs(diff) / 120 * 20)
+        } else {
+            deg = director * 20
+        }
+        ins.selectComponent('#control_1').setStyle({
+            transition: 'unset',
+            transform: 'rotate(' + deg + 'deg)'
+        })
+        ins.selectComponent('#control_2').setStyle({
+            transition: 'unset',
+            transform: 'rotate(' + (-deg) + 'deg)'
+        })
+    }
+    state.calendarMoving = false
+}
+
+var touchEnd = function(e, ins) {
+    var state = ins.getState()
+    var _pageY = e.changedTouches[0].pageY
+    var diff = Math.floor((_pageY - state.calendarPageY))
+    var position = state.calendarCurrHeight + diff
+    var viewSate = e.mark.view
+    var calendarCriticalMin = state.calendarHeight - 120
+    var calendarCriticalMax = state.calendarHeight + Math.floor((state.calendarMax - state.calendarHeight) * 0.3)
+    if (position > calendarCriticalMax) {
+        viewSate = 3
+        ins.selectComponent('#calendar').setStyle({
+            transition: 'all .3s ease 0s',
+            height: state.calendarMax + 'px'
+        })
+        ins.selectComponent('.wd-calendar-month-panel.curr').setStyle({
+            transition: 'all .3s ease 0s',
+            height: 'calc(' + state.calendarMax + 'px - 200rpx)',
+            transform: 'translateY(0)'
+        })
+        var others = ins.selectAllComponents('.wd-calendar-month-panel.other')
+        for (var i = 0; i < others.length; i++) {
+            others[i].setStyle({
+                transition: 'unset',
+                height: 'calc(' + state.calendarMax + 'px - 200rpx)'
+            })
+        }
+        var schedules = ins.selectAllComponents('.wd-calendar-schedules')
+        for (var i = 0; i < schedules.length; i++) {
+            schedules[i].addClass('show')
+        }
+        state.schedule_show = true
+    } else if (position <= calendarCriticalMax && position > calendarCriticalMin) {
+        viewSate = 1
+        ins.selectComponent('#calendar').setStyle({
+            transition: 'all .3s cubic-bezier(0.5, 0, 0.27, 1.5) 0s',
+            height: state.calendarHeight + 'px'
+        })
+        ins.selectComponent('.wd-calendar-month-panel.curr').setStyle({
+            transition: 'all .3s cubic-bezier(0.5, 0, 0.27, 1.5) 0s',
+            height: state.panelHeight + 'px',
+            transform: 'translateY(0)'
+        })
+        var others = ins.selectAllComponents('.wd-calendar-month-panel.other')
+        for (var i = 0; i < others.length; i++) {
+            others[i].setStyle({
+                transition: 'unset',
+                height: state.panelHeight + 'px',
+                transform: 'translateY(0)'
+            })
+        }
+    } else {
+        viewSate = 2
+        ins.selectComponent('#calendar').setStyle({
+            transition: 'all .3s cubic-bezier(0.5, 0, 0.27, 1.5) 0s',
+            height: state.calendarMin + 'px'
+        })
+        var curr = ins.selectComponent('.wd-calendar-month-panel.curr')
+        curr.setStyle({
+            transition: 'all .3s cubic-bezier(0.5, 0, 0.27, 1.5) 0s',
+            height: state.panelHeight + 'px',
+            transform: 'translateY(' + (-curr.getDataset().trans) + 'px)'
+        })
+        var others = ins.selectAllComponents('.wd-calendar-month-panel.other')
+        for (var i = 0; i < others.length; i++) {
+            var trans = others[i].getDataset().trans
+            others[i].setStyle({
+                transition: 'unset',
+                height: state.panelHeight + 'px',
+                transform: 'translateY(' + (-trans) + 'px)'
+            })
+        }
+    }
+    ins.selectComponent('#control_1').setStyle({
+        transition: 'all .3s cubic-bezier(0.6, 0, 0.27, 1.5) .2s',
+        transform: 'rotate(0deg)'
+    })
+    ins.selectComponent('#control_2').setStyle({
+        transition: 'all .3s cubic-bezier(0.6, 0, 0.27, 1.5) .2s',
+        transform: 'rotate(0deg)'
+    })
+    ins.callMethod('toggleView', { state: viewSate })
+}
+
+var handleViewToggle = function(viewState, dataset, ins) {
+    if (viewState == 2) {
+        ins.selectComponent('#calendar').setStyle({
+            transition: 'all .28s ease 0s',
+            height: dataset.min + 'px'
+        })
+        var curr = ins.selectComponent('.wd-calendar-month-panel.curr')
+        curr.setStyle({
+            transition: 'all .28s ease 0s',
+            height: dataset.panel + 'px',
+            transform: 'translateY(' + (-curr.getDataset().trans) + 'px)'
+        })
+        var others = ins.selectAllComponents('.wd-calendar-month-panel.other')
+        for (var i = 0; i < others.length; i++) {
+            var trans = others[i].getDataset().trans
+            others[i].setStyle({
+                transition: 'unset',
+                height: dataset.panel + 'px',
+                transform: 'translateY(' + (-trans) + 'px)'
+            })
+        }
+    } else {
+        ins.selectComponent('#calendar').setStyle({
+            transition: 'all .28s ease 0s',
+            height: dataset.calendar + 'px'
+        })
+        ins.selectComponent('.wd-calendar-month-panel.curr').setStyle({
+            transition: 'all .28s ease 0s',
+            height: dataset.panel + 'px',
+            transform: 'translateY(0)'
+        })
+        var others = ins.selectAllComponents('.wd-calendar-month-panel.other')
+        for (var i = 0; i < others.length; i++) {
+            others[i].setStyle({
+                transition: 'unset',
+                height: dataset.panel + 'px',
+                transform: 'translateY(0)'
+            })
+        }
+    }
+    var schedules = ins.selectAllComponents('.wd-calendar-schedules')
+    for (var i = 0; i < schedules.length; i++) {
+        schedules[i].removeClass('show')
+    }
+}
+
+var toggleView = function(e, ins) {
+    var dataset = e.mark
+    var viewSate = dataset.view
+    if (dataset.view == 2) {
+        viewSate = 1
+        handleViewToggle(1, dataset, ins)
+    } else {
+        viewSate = 2
+        handleViewToggle(2, dataset, ins)
+    }
+    ins.callMethod('toggleView', { state: viewSate })
+}
+
+var handleViewChange = function(view, _, ins) {
+    if (view) {
+        var dataset = ins.selectComponent('#calendar').getDataset()
+        var viewSate = view == 'week' ? 2 : 1
+        handleViewToggle(viewSate, dataset, ins)
+        ins.callMethod('toggleView', { state: viewSate })
+    }
+}
+
+var handleMonthChange = function(monthchange, _, ins) {
+    if (monthchange) {
+        var dataset = ins.selectComponent('#calendar').getDataset()
+        var curr = ins.selectComponent('.wd-calendar-month-panel.curr')
+        curr.setStyle({
+            transition: 'unset',
+            height: dataset.panel + 'px',
+            transform: 'translateY(' + (-curr.getDataset().trans) + 'px)'
+        })
+    }
+}
+
+var handleInitTrans = function(needInitTrans, _, ins) {
+    if (needInitTrans) {
+        var dataset = ins.selectComponent('#calendar').getDataset()
+        var curr = ins.selectComponent('.wd-calendar-month-panel.curr')
+        curr.setStyle({
+            transition: 'unset',
+            height: dataset.panel + 'px',
+            transform: 'translateY(' + (-curr.getDataset().trans) + 'px)'
+        })
+        var others = ins.selectAllComponents('.wd-calendar-month-panel.other')
+        for (var i = 0; i < others.length; i++) {
+            var trans = others[i].getDataset().trans
+            others[i].setStyle({
+                transition: 'unset',
+                height: dataset.panel + 'px',
+                transform: 'translateY(' + (-trans) + 'px)'
+            })
+        }
+    }
+}
+
+var handleWeekSwiperChange = function(weektabchange, _, ins) {
+    if (weektabchange >= 0) {
+        var dataset = ins.selectComponent('#calendar').getDataset()
+        var others = ins.selectAllComponents('.wd-calendar-month-panel.other')
+        for (var i = 0; i < others.length; i++) {
+            var trans = others[i].getDataset().trans
+            others[i].setStyle({
+                transition: 'unset',
+                height: dataset.panel + 'px',
+                transform: 'translateY(' + (-trans) + 'px)'
+            })
+        }
+    }
+}
+
+var handleYearPanelShow = function(e, ins) {
+    var dataset = e.mark
+    var viewSate = dataset.view
+    if (viewSate == 1) {
+        ins.callMethod('justYearPanelShow')
+    } else {
+        ins.selectComponent('#calendar').setStyle({
+            transition: 'all .28s ease 0s',
+            height: dataset.calendar + 'px'
+        })
+        ins.selectComponent('.wd-calendar-month-panel.curr').setStyle({
+            transition: 'all .28s ease 0s',
+            height: dataset.panel + 'px',
+            transform: 'translateY(0)'
+        })
+        var others = ins.selectAllComponents('.wd-calendar-month-panel.other')
+        for (var i = 0; i < others.length; i++) {
+            others[i].setStyle({
+                transition: 'unset',
+                height: dataset.panel + 'px',
+                transform: 'translateY(0)'
+            })
+        }
+        ins.callMethod('handleYearPanelShow')
+        ins.callMethod('toggleView', { state: 1 })
+    }
+}
+
+module.exports = {
+    touchStart: touchStart,
+    touchMove: touchMove,
+    touchEnd: touchEnd,
+    toggleView: toggleView,
+    handleMonthChange: handleMonthChange,
+    handleWeekSwiperChange: handleWeekSwiperChange,
+    handleYearPanelShow: handleYearPanelShow,
+    handleInitTrans: handleInitTrans,
+    handleViewChange: handleViewChange
+}

+ 776 - 0
components/wx-calendar/index.wxss

@@ -0,0 +1,776 @@
+view,
+scroll-view,
+swiper,
+button {
+    box-sizing: border-box;
+}
+
+text {
+    color: inherit;
+    font-size: inherit;
+    font-weight: inherit;
+}
+
+view,
+text {
+    font-family: Helvetica, Tahoma, Arial, "PingFang SC", "Hiragino Sans GB", "Heiti SC", "Microsoft YaHei", "WenQuanYi Micro Hei", sans-serif;
+}
+
+.wd-btn {
+    position: relative;
+    border: 0rpx;
+    display: inline-flex;
+    align-items: center;
+    justify-content: center;
+    box-sizing: border-box;
+    padding: 0 30rpx;
+    font-size: 28rpx;
+    height: 64rpx;
+    line-height: 1;
+    text-align: center;
+    text-decoration: none;
+    overflow: visible;
+    margin-left: initial;
+    transform: translate(0rpx, 0rpx);
+    margin-right: initial;
+}
+
+.wd-btn::after {
+    display: none;
+}
+
+.wd-btn-hover {
+    transform: translate(1rpx, 1rpx);
+}
+
+.wd-calendar-container {
+    position: relative;
+    width: 100vw;
+    --wd-calendar-main-bg: #fff;
+    --wd-calendar-second-bg: #f8f8f8;
+    --wd-calendar-main-font-color: #313313;
+    --wd-calendar-second-font-color: #ababab;
+    --wd-calendar-info-font-color: #7a7a7a;
+    --wd-calendar-sel-bar: #f5f5f5;
+    --wd-calendar-view-bar: #fff9f3;
+    --wd-calendar-ym: #ecf5ff;
+    --wd-calendar-control-bar: #dfdfdf;
+}
+
+.wd-calendar {
+    position: relative;
+    width: 100%;
+    z-index: 1024;
+    background-color: var(--wd-calendar-main-bg);
+    display: flex;
+    flex-direction: column;
+    overflow: hidden;
+}
+
+.wd-calendar-bar {
+    display: flex;
+    flex-direction: row;
+    justify-content: space-between;
+    height: 100rpx;
+    padding-top: 20rpx;
+    align-items: center;
+    position: absolute;
+    width: 100%;
+    top: 0;
+    left: 0;
+    z-index: 1024;
+    background-color: var(--wd-calendar-main-bg);
+    overflow: hidden;
+}
+
+.wd-calendar-title {
+    padding: 0 0 0 42rpx;
+    color: var(--wd-calendar-main-font-color);
+    font-size: 42rpx;
+    position: relative;
+    line-height: 42rpx;
+    letter-spacing: 1px;
+    font-weight: 500;
+    transition: all .28s ease 0s;
+    opacity: 0;
+}
+
+.wd-calendar-title text {
+    color: var(--wd-calendar-main-font-color);
+    font-size: 42rpx;
+    vertical-align: middle;
+}
+
+.wd-calendar-title text.wd-calendar-title-cn {
+    font-size: 38rpx;
+    margin: 0 2rpx;
+}
+
+.wd-calendar.load .wd-calendar-title {
+    opacity: 1;
+}
+
+.wd-calendar-title::after {
+    content: attr(data-info);
+    position: relative;
+    font-size: 20rpx;
+    color: var(--wd-calendar-info-font-color);
+    margin-left: 8rpx;
+    font-weight: normal;
+    letter-spacing: 0px;
+}
+
+.wd-calendar-title::before {
+    content: "";
+    width: 100%;
+    height: 100%;
+    position: absolute;
+    top: 0;
+    left: 0;
+    z-index: 1000000;
+}
+
+.wd-calendar-title-swiper {
+    width: 44rpx;
+    height: 44rpx;
+    line-height: 44rpx;
+    display: inline-block;
+    vertical-align: middle;
+}
+
+.wd-calendar-title-swiper-main {
+    width: 100%;
+    height: 100%;
+}
+
+.wd-calendar-title-swiper-item {}
+
+.wd-calendar-title-month {
+    width: 100%;
+    height: 100%;
+    line-height: 44rpx;
+    font-size: 42rpx;
+    font-weight: 500;
+    color: var(--wd-calendar-main-font-color);
+    text-align: center;
+    letter-spacing: -2px;
+    padding-right: 2px;
+    will-change: opacity;
+    transition: opacity .25s ease 0s;
+    opacity: .3;
+}
+
+.wd-calendar-title-month.curr {
+    opacity: 1;
+}
+
+.wd-calendar-week-bar {
+    height: 50rpx;
+    padding: 10rpx 10rpx 0;
+    position: absolute;
+    width: 100%;
+    top: 100rpx;
+    left: 0;
+    z-index: 1024;
+    background-color: var(--wd-calendar-main-bg);
+    transition: all .28s ease 0s;
+    opacity: 0;
+}
+
+.wd-calendar.load .wd-calendar-week-bar {
+    opacity: 1;
+}
+
+.wd-calendar-week-bar,
+.wd-calendar-week-row {
+    display: flex;
+    flex-direction: row;
+    align-items: center;
+    justify-content: space-between;
+}
+
+.wd-calendar-week-item {
+    text-align: center;
+    font-size: 20rpx;
+    color: var(--wd-calendar-second-font-color);
+    font-weight: bold;
+}
+
+.wd-calendar-week-item,
+.wd-calendar-day {
+    width: calc(100% / 7);
+}
+
+.wd-calendar-month-body {
+    width: 100%;
+    height: calc(100% - 200rpx);
+    position: absolute;
+    top: 150rpx;
+    left: 0;
+    z-index: 10;
+    background-color: var(--wd-calendar-main-bg);
+    overflow: hidden;
+}
+
+.wd-calendar-swiper {
+    width: 100%;
+    height: 100%;
+}
+
+.wd-calendar-swiper-item {}
+
+.wd-calendar-month-panel {
+    width: 100%;
+    /* height: 620rpx;
+    min-height: 620rpx; */
+    display: flex;
+    flex-direction: column;
+    position: absolute;
+    top: 0;
+    left: 0;
+}
+
+.wd-calendar-week-row {
+    width: 100%;
+    padding: 0 10rpx;
+}
+
+.wd-calendar-week-row.l-4 {
+    height: 25%;
+}
+
+.wd-calendar-week-row.l-5 {
+    height: 20%;
+}
+
+.wd-calendar-week-row.l-6 {
+    height: calc(100% / 6);
+}
+
+.wd-calendar-day {
+    height: 100%;
+    display: flex;
+    flex-direction: column;
+    justify-content: center;
+    align-items: center;
+    transition: opacity .28s ease 0s;
+    overflow: hidden;
+}
+
+.wd-calendar-gregorian {
+    font-size: 34rpx;
+    font-weight: bold;
+    line-height: 34rpx;
+    color: var(--wd-calendar-main-font-color);
+    transition: color .3s ease 0s;
+    position: relative;
+}
+
+.wd-calendar-gregorian.dot::before {
+    content: "・";
+    position: absolute;
+    top: -22rpx;
+    left: 50%;
+    transform: translateX(-50%)translateZ(0px);
+    /* color: #ababab; */
+    color: #f37b1d;
+    animation: dot .3s ease 0s;
+    animation-fill-mode: both;
+    opacity: 0;
+    will-change: opacity;
+}
+
+@keyframes dot {
+    from {
+        opacity: 0;
+    }
+    to {
+        opacity: 1;
+    }
+}
+
+.wd-calendar-solar {
+    font-size: 20rpx;
+    color: var(--wd-calendar-second-font-color);
+    font-weight: bold;
+    line-height: 20rpx;
+    margin-top: 8rpx;
+    transition: color .3s ease 0s;
+    letter-spacing: 2rpx;
+    padding-left: 2rpx;
+    position: relative;
+    width: 100%;
+    text-align: center;
+}
+
+.wd-calendar-solar.solar {
+    color: #2a97ff;
+}
+
+.wd-calendar-day.prev,
+.wd-calendar-day.next {
+    opacity: .3;
+    will-change: opacity;
+}
+
+.wd-calendar-month-body.solid .wd-calendar-day.prev,
+.wd-calendar-month-body.solid .wd-calendar-day.next {
+    opacity: 1;
+}
+
+.wd-calendar-day.today .wd-calendar-gregorian {
+    color: #2a97ff;
+}
+
+.wd-calendar-day.today.curr .wd-calendar-gregorian,
+.wd-calendar-day.today.curr .wd-calendar-solar,
+.wd-calendar-day.today.curr .wd-calendar-gregorian .wd-calendar-gregorian-corner {
+    color: #fff !important;
+}
+
+.wd-calendar-container.sel-hidden .wd-calendar-day.today.curr .wd-calendar-gregorian,
+.wd-calendar-container.sel-hidden .wd-calendar-day.today.curr .wd-calendar-solar {
+    color: #2a97ff !important;
+}
+
+.wd-calendar-day.today.curr .wd-calendar-gregorian.dot::before {
+    color: #eee !important;
+}
+
+.wd-calendar-container.sel-hidden .wd-calendar-day.today.curr .wd-calendar-gregorian.dot::before {
+    color: #f37b1d !important;
+}
+
+.wd-calendar-gregorian-corner {
+    position: absolute;
+    top: -8rpx;
+    right: -26rpx;
+    font-size: 16rpx;
+    font-weight: bold;
+    line-height: 16rpx;
+    padding: 4rpx;
+    display: flex;
+    justify-content: center;
+    align-items: center;
+    border-radius: 4rpx;
+}
+
+.wd-calendar-schedules {
+    position: absolute;
+    width: 100%;
+    padding: 0 8rpx;
+    top: 30rpx;
+    left: 0;
+    display: flex;
+    flex-direction: column;
+    overflow: hidden;
+    opacity: 0;
+    transition: all .28s ease 0s;
+    will-change: opacity;
+}
+
+.wd-calendar-schedule {
+    font-size: 18rpx;
+    font-weight: bold;
+    color: #2a97ff;
+    background-color: #f8f8f8;
+    padding: 4rpx;
+    border-radius: 6rpx;
+    text-align: center;
+    overflow: hidden;
+    text-overflow: ellipsis;
+    display: -webkit-box;
+    -webkit-box-orient: vertical;
+    word-break: break-all;
+    -webkit-line-clamp: 1;
+    line-height: 26rpx;
+}
+
+.wd-calendar-schedules.show {
+    opacity: 1;
+}
+
+.wd-calendar-schedule-more {
+    color: #2a97ff;
+    font-size: 18rpx;
+    font-weight: bold;
+    margin-top: 6rpx;
+}
+
+.wd-calendar-bt-bar {
+    position: absolute;
+    width: 100%;
+    height: 50rpx;
+    bottom: 0;
+    left: 0;
+    background-color: var(--wd-calendar-main-bg);
+    z-index: 1024;
+    display: flex;
+    align-items: flex-end;
+    justify-content: center;
+    transition: all .28s ease 0s;
+    opacity: 0;
+}
+
+.wd-calendar.load .wd-calendar-bt-bar {
+    opacity: 1;
+}
+
+.wd-calendar-bt-control {
+    display: flex;
+    justify-content: center;
+}
+
+.wd-calendar-bt-control-item {
+    width: 30rpx;
+    height: 40rpx;
+    overflow: hidden;
+    position: relative;
+}
+
+.wd-calendar-bt-control-bar {
+    width: 60rpx;
+    height: 6rpx;
+    background-color: var(--wd-calendar-control-bar);
+    position: absolute;
+    top: calc(50% - 8rpx);
+}
+
+.wd-calendar-bt-control-item:first-child .wd-calendar-bt-control-bar {
+    transform-origin: 0 50%;
+    border-radius: 4rpx 0 0 4rpx;
+    left: 0;
+}
+
+.wd-calendar-bt-control-item:last-child .wd-calendar-bt-control-bar {
+    transform-origin: 100% 50%;
+    border-radius: 0 4rpx 4rpx 0;
+    right: 0;
+}
+
+.wd-calendar-day-sel {
+    position: absolute;
+    top: 0;
+    left: 0;
+    width: 100rpx;
+    height: 100rpx;
+    border-radius: 50%;
+    transform: translate(-50%, -50%);
+    transition: all .28s ease 0s;
+    z-index: -1;
+    will-change: top, left;
+    overflow: hidden;
+}
+
+.wd-calendar-day-sel.no-ani {
+    transition: unset;
+}
+
+.wd-calendar-day-sel.today {}
+
+.wd-calendar-day-sel .wd-calendar-day-sel-bar {
+    position: absolute;
+    width: 100%;
+    height: 100%;
+    top: 0;
+    left: 0;
+    border-radius: inherit;
+    background-color: var(--wd-calendar-sel-bar);
+    will-change: transform;
+}
+
+.wd-calendar-day-sel.today .wd-calendar-day-sel-bar {
+    /* background-color: #2a97ff !important; */
+    background: linear-gradient(145deg, #2a97ff, #4da4e6);
+}
+
+.wd-calendar-day-sel-bar.animation {
+    animation: selBar .28s ease 0s;
+}
+
+@keyframes selBar {
+    0%,
+    100% {
+        transform: scale(1);
+    }
+    50% {
+        transform: scale(.5);
+    }
+}
+
+.wd-calendar-options {
+    display: flex;
+    flex-direction: row;
+    align-items: right;
+    padding: 0 50rpx 6rpx 0;
+    transition: all .28s ease 0s;
+    opacity: 0;
+    width: 270rpx;
+    max-width: 270rpx;
+    overflow: hidden;
+}
+
+.wd-calendar.load .wd-calendar-options {
+    opacity: 1;
+}
+
+.wd-calendar-option {
+    margin: 0 15rpx;
+}
+
+.wd-calendar-option-it {
+    transition: all 0.28s ease 0s;
+}
+
+.wd-calendar-option-views {
+    width: 140rpx;
+    height: 60rpx;
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+    border-radius: 30rpx;
+    background-color: var(--wd-calendar-view-bar);
+    overflow: hidden;
+    position: relative;
+}
+
+.wd-calendar-option-views.today {
+    width: 60rpx;
+    justify-content: center;
+    transition: all .28s ease 0s;
+    opacity: 0;
+    max-width: 60rpx;
+    overflow: hidden;
+    will-change: transform;
+}
+
+.wd-calendar-option-views.today.show {
+    opacity: 1;
+}
+
+.wd-calendar-option-view {
+    width: 70rpx;
+    height: 60rpx;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    color: #e54d42;
+    font-size: 22rpx;
+    font-weight: bold;
+    transition: color .1s linear .1s;
+    position: relative;
+    line-height: 22rpx;
+    z-index: 1024;
+    will-change: transform;
+    transform: translateZ(0px);
+}
+
+.wd-calendar-option-view.curr {
+    color: #fff;
+}
+
+.wd-calendar-option-view-bar {
+    position: absolute;
+    width: 44rpx;
+    height: 44rpx;
+    top: 8rpx;
+    left: calc(70rpx / 2 + 70rpx * 0 - 22rpx);
+    border-radius: 50%;
+    transform: translateX(0) translateZ(0px);
+    background-image: linear-gradient(145deg, #f37b1d, #e54d42);
+    transition: all .3s ease 0s;
+    z-index: 10;
+    will-change: transform;
+}
+
+.wd-calendar-option-views.today .wd-calendar-option-view {
+    width: 44rpx;
+    height: 44rpx;
+    border-radius: 50%;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    color: #fff;
+    font-size: 22rpx;
+    font-weight: bold;
+    position: relative;
+    line-height: 22rpx;
+    z-index: 1024;
+    background-image: linear-gradient(145deg, #f37b1d, #e54d42);
+    transition: all .28s ease 0s;
+    transform: scale(.3) translateZ(0px);
+}
+
+.wd-calendar-option-views.today.show .wd-calendar-option-view {
+    transform: scale(1) translateZ(0px);
+}
+
+
+/* 年面板 */
+
+.wd-calendar-years-panel {
+    position: absolute;
+    width: 100%;
+    height: 820rpx;
+    top: 0;
+    left: 0;
+    z-index: 102400;
+    background-color: var(--wd-calendar-main-bg);
+    transition: all .3s ease 0s;
+    transform: translateY(-100%) translateZ(0px);
+    box-shadow: 0 0 0rpx rgba(0, 0, 0, 0);
+    overflow: hidden;
+}
+
+.wd-calendar-years-panel.show {
+    transform: translateY(0);
+    box-shadow: 0 0 30rpx rgba(0, 0, 0, 0.15);
+}
+
+.wd-calendar-years-panel-body {
+    position: absolute;
+    top: 100rpx;
+    left: 0;
+    width: 100%;
+    height: calc(100% - 100rpx);
+    background-color: var(--wd-calendar-main-bg);
+}
+
+.wd-calendar-ym-swiper {
+    width: 100%;
+    height: 100%;
+    background-color: var(--wd-calendar-main-bg);
+}
+
+.wd-calendar-ym-swiper-item {
+    background-color: var(--wd-calendar-main-bg);
+}
+
+.wd-calendar-ym-months {
+    width: 100%;
+    height: 100%;
+    display: flex;
+    flex-direction: column;
+    padding: 20rpx 40rpx 50rpx;
+    background-color: var(--wd-calendar-main-bg);
+}
+
+.wd-calendar-ym-row {
+    flex-grow: 1;
+    width: 100%;
+    height: calc(100% / 3 - 12rpx);
+    display: flex;
+    flex-direction: row;
+    justify-content: space-between;
+    background-color: var(--wd-calendar-main-bg);
+}
+
+.wd-calendar-ym-row+.wd-calendar-ym-row {
+    margin-top: 12rpx;
+}
+
+.wd-calendar-ym-month {
+    width: calc(25% - 10rpx);
+    height: 100%;
+    background-color: var(--wd-calendar-second-bg);
+}
+
+.wd-calendar-ym-month.curr {
+    background-color: var(--wd-calendar-ym);
+}
+
+.wd-calendar-ym-month-contaner {
+    width: 100%;
+    height: 100%;
+    padding: 20rpx;
+}
+
+.wd-calendar-title.ym text.wd-calendar-title-cn.margin-left {
+    margin-left: 0;
+}
+
+.wd-calendar-title.ym::after {
+    color: #e54d42;
+    font-weight: bold;
+}
+
+.wd-calendar-ym-m-r {}
+
+.wd-calendar-ym-m {
+    color: var(--wd-calendar-main-font-color);
+    font-weight: bold;
+    font-size: 36rpx;
+    line-height: 36rpx;
+    position: relative;
+    display: inline-block;
+}
+
+.wd-calendar-ym-month.curr .wd-calendar-ym-m {
+    color: #2a97ff;
+}
+
+.wd-calendar-ym-m::after {
+    content: "月";
+    position: relative;
+    font-size: 20rpx;
+    font-weight: normal;
+    margin-left: 4rpx;
+    color: inherit;
+}
+
+.wd-calendar-ym-l {
+    font-size: 20rpx;
+    line-height: 20rpx;
+    position: relative;
+    display: flex;
+}
+
+.wd-calendar-ym-l .wd-calendar-ym-l-i text {
+    font-size: 20rpx;
+    color: #f37b1d;
+    position: relative;
+}
+
+.wd-calendar-ym-l .wd-calendar-ym-l-i:first-child {
+    margin-right: 6rpx;
+    min-width: 30rpx;
+}
+
+.wd-calendar-ym-l .wd-calendar-ym-l-i:first-child text::after {
+    content: attr(data-order);
+    position: relative;
+    color: inherit;
+    font-size: 60%;
+    opacity: .8;
+}
+
+.wd-calendar-ym-l .wd-calendar-ym-l-i:last-child text::before {
+    content: "农";
+    position: relative;
+    color: inherit;
+    font-size: 60%;
+    margin-right: 2rpx;
+    opacity: .8;
+}
+
+
+/* 年面板 End */
+
+.wd-calendar-content {
+    position: relative;
+    background-color: var(--wd-calendar-main-bg);
+}
+
+@media (prefers-color-scheme: dark) {
+    .wd-calendar-container.darkmode {
+        --wd-calendar-main-bg: #111;
+        --wd-calendar-second-bg: #4a4a4a;
+        --wd-calendar-main-font-color: #eee;
+        --wd-calendar-second-font-color: #bebebe;
+        --wd-calendar-info-font-color: #999999;
+        --wd-calendar-sel-bar: #262626;
+        --wd-calendar-view-bar: #332d2d;
+        --wd-calendar-ym: #515963;
+        --wd-calendar-control-bar: #262626;
+    }
+}

+ 383 - 0
components/wx-calendar/lunar.js

@@ -0,0 +1,383 @@
+/* 
+ * 参考了eleworld.com上的算法,并修正了5处节气错误
+ * 中国农历算法 - 实用于公历 1901 年至 2100 年之间的 200 年 
+ */
+const ChineseCalendars = [
+    0x00, 0x04, 0xad, 0x08, 0x5a, 0x01, 0xd5, 0x54, 0xb4, 0x09, 0x64, 0x05, 0x59, 0x45,
+    0x95, 0x0a, 0xa6, 0x04, 0x55, 0x24, 0xad, 0x08, 0x5a, 0x62, 0xda, 0x04, 0xb4, 0x05,
+    0xb4, 0x55, 0x52, 0x0d, 0x94, 0x0a, 0x4a, 0x2a, 0x56, 0x02, 0x6d, 0x71, 0x6d, 0x01,
+    0xda, 0x02, 0xd2, 0x52, 0xa9, 0x05, 0x49, 0x0d, 0x2a, 0x45, 0x2b, 0x09, 0x56, 0x01,
+    0xb5, 0x20, 0x6d, 0x01, 0x59, 0x69, 0xd4, 0x0a, 0xa8, 0x05, 0xa9, 0x56, 0xa5, 0x04,
+    0x2b, 0x09, 0x9e, 0x38, 0xb6, 0x08, 0xec, 0x74, 0x6c, 0x05, 0xd4, 0x0a, 0xe4, 0x6a,
+    0x52, 0x05, 0x95, 0x0a, 0x5a, 0x42, 0x5b, 0x04, 0xb6, 0x04, 0xb4, 0x22, 0x6a, 0x05,
+    0x52, 0x75, 0xc9, 0x0a, 0x52, 0x05, 0x35, 0x55, 0x4d, 0x0a, 0x5a, 0x02, 0x5d, 0x31,
+    0xb5, 0x02, 0x6a, 0x8a, 0x68, 0x05, 0xa9, 0x0a, 0x8a, 0x6a, 0x2a, 0x05, 0x2d, 0x09,
+    0xaa, 0x48, 0x5a, 0x01, 0xb5, 0x09, 0xb0, 0x39, 0x64, 0x05, 0x25, 0x75, 0x95, 0x0a,
+    0x96, 0x04, 0x4d, 0x54, 0xad, 0x04, 0xda, 0x04, 0xd4, 0x44, 0xb4, 0x05, 0x54, 0x85,
+    0x52, 0x0d, 0x92, 0x0a, 0x56, 0x6a, 0x56, 0x02, 0x6d, 0x02, 0x6a, 0x41, 0xda, 0x02,
+    0xb2, 0xa1, 0xa9, 0x05, 0x49, 0x0d, 0x0a, 0x6d, 0x2a, 0x09, 0x56, 0x01, 0xad, 0x50,
+    0x6d, 0x01, 0xd9, 0x02, 0xd1, 0x3a, 0xa8, 0x05, 0x29, 0x85, 0xa5, 0x0c, 0x2a, 0x09,
+    0x96, 0x54, 0xb6, 0x08, 0x6c, 0x09, 0x64, 0x45, 0xd4, 0x0a, 0xa4, 0x05, 0x51, 0x25,
+    0x95, 0x0a, 0x2a, 0x72, 0x5b, 0x04, 0xb6, 0x04, 0xac, 0x52, 0x6a, 0x05, 0xd2, 0x0a,
+    0xa2, 0x4a, 0x4a, 0x05, 0x55, 0x94, 0x2d, 0x0a, 0x5a, 0x02, 0x75, 0x61, 0xb5, 0x02,
+    0x6a, 0x03, 0x61, 0x45, 0xa9, 0x0a, 0x4a, 0x05, 0x25, 0x25, 0x2d, 0x09, 0x9a, 0x68,
+    0xda, 0x08, 0xb4, 0x09, 0xa8, 0x59, 0x54, 0x03, 0xa5, 0x0a, 0x91, 0x3a, 0x96, 0x04,
+    0xad, 0xb0, 0xad, 0x04, 0xda, 0x04, 0xf4, 0x62, 0xb4, 0x05, 0x54, 0x0b, 0x44, 0x5d,
+    0x52, 0x0a, 0x95, 0x04, 0x55, 0x22, 0x6d, 0x02, 0x5a, 0x71, 0xda, 0x02, 0xaa, 0x05,
+    0xb2, 0x55, 0x49, 0x0b, 0x4a, 0x0a, 0x2d, 0x39, 0x36, 0x01, 0x6d, 0x80, 0x6d, 0x01,
+    0xd9, 0x02, 0xe9, 0x6a, 0xa8, 0x05, 0x29, 0x0b, 0x9a, 0x4c, 0xaa, 0x08, 0xb6, 0x08,
+    0xb4, 0x38, 0x6c, 0x09, 0x54, 0x75, 0xd4, 0x0a, 0xa4, 0x05, 0x45, 0x55, 0x95, 0x0a,
+    0x9a, 0x04, 0x55, 0x44, 0xb5, 0x04, 0x6a, 0x82, 0x6a, 0x05, 0xd2, 0x0a, 0x92, 0x6a,
+    0x4a, 0x05, 0x55, 0x0a, 0x2a, 0x4a, 0x5a, 0x02, 0xb5, 0x02, 0xb2, 0x31, 0x69, 0x03,
+    0x31, 0x73, 0xa9, 0x0a, 0x4a, 0x05, 0x2d, 0x55, 0x2d, 0x09, 0x5a, 0x01, 0xd5, 0x48,
+    0xb4, 0x09, 0x68, 0x89, 0x54, 0x0b, 0xa4, 0x0a, 0xa5, 0x6a, 0x95, 0x04, 0xad, 0x08,
+    0x6a, 0x44, 0xda, 0x04, 0x74, 0x05, 0xb0, 0x25, 0x54, 0x03
+]
+const BigLeapMonthYears = [
+    //大闰月的闰年年份
+    6, 14, 19, 25, 33, 36, 38, 41, 44, 52, 55, 79, 117, 136, 147, 150, 155, 158, 185, 193
+]
+const DaysInGregorianMonths = [
+    //公历每个月的天数
+    31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
+]
+const SectionalTermMap = [
+    [7, 6, 6, 6, 6, 6, 6, 6, 6, 5, 6, 6, 6, 5, 5, 6, 6, 5, 5, 5, 5, 5, 5, 5, 5, 4, 5, 5],
+    [5, 4, 5, 5, 5, 4, 4, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4, 3, 4, 4, 4, 3, 3, 4, 4, 3, 3, 3],
+    [6, 6, 6, 7, 6, 6, 6, 6, 5, 6, 6, 6, 5, 5, 6, 6, 5, 5, 5, 6, 5, 5, 5, 5, 4, 5, 5, 5, 5],
+    [5, 5, 6, 6, 5, 5, 5, 6, 5, 5, 5, 5, 4, 5, 5, 5, 4, 4, 5, 5, 4, 4, 4, 5, 4, 4, 4, 4, 5],
+    [6, 6, 6, 7, 6, 6, 6, 6, 5, 6, 6, 6, 5, 5, 6, 6, 5, 5, 5, 6, 5, 5, 5, 5, 4, 5, 5, 5, 5],
+    [6, 6, 7, 7, 6, 6, 6, 7, 6, 6, 6, 6, 5, 6, 6, 6, 5, 5, 6, 6, 5, 5, 5, 6, 5, 5, 5, 5, 4, 5, 5, 5, 5],
+    [7, 8, 8, 8, 7, 7, 8, 8, 7, 7, 7, 8, 7, 7, 7, 7, 6, 7, 7, 7, 6, 6, 7, 7, 6, 6, 6, 7, 7],
+    [8, 8, 8, 9, 8, 8, 8, 8, 7, 8, 8, 8, 7, 7, 8, 8, 7, 7, 7, 8, 7, 7, 7, 7, 6, 7, 7, 7, 6, 6, 7, 7, 7],
+    [8, 8, 8, 9, 8, 8, 8, 8, 7, 8, 8, 8, 7, 7, 8, 8, 7, 7, 7, 8, 7, 7, 7, 7, 6, 7, 7, 7, 7],
+    [9, 9, 9, 9, 8, 9, 9, 9, 8, 8, 9, 9, 8, 8, 8, 9, 8, 8, 8, 8, 7, 8, 8, 8, 7, 7, 8, 8, 8],
+    [8, 8, 8, 8, 7, 8, 8, 8, 7, 7, 8, 8, 7, 7, 7, 8, 7, 7, 7, 7, 6, 7, 7, 7, 6, 6, 7, 7, 7],
+    [7, 8, 8, 8, 7, 7, 8, 8, 7, 7, 7, 8, 7, 7, 7, 7, 6, 7, 7, 7, 6, 6, 7, 7, 6, 6, 6, 7, 7]
+]
+const SectionalTermYear = [
+    [13, 49, 85, 117, 149, 185, 201, 250, 250],
+    [13, 45, 81, 117, 149, 185, 201, 250, 250],
+    [13, 48, 84, 112, 148, 184, 200, 201, 250],
+    [13, 45, 76, 108, 140, 172, 200, 201, 250],
+    [13, 44, 72, 104, 132, 168, 200, 201, 250],
+    [5, 33, 68, 96, 124, 152, 188, 200, 201],
+    [29, 57, 85, 120, 148, 176, 200, 201, 250],
+    [13, 48, 76, 104, 132, 168, 196, 200, 201],
+    [25, 60, 88, 120, 148, 184, 200, 201, 250],
+    [16, 44, 76, 108, 144, 172, 200, 201, 250],
+    [28, 60, 92, 124, 160, 192, 200, 201, 250],
+    [17, 53, 85, 124, 156, 188, 200, 201, 250]
+]
+const PrincipleTermMap = [
+    [21, 21, 21, 21, 21, 20, 21, 21, 21, 20, 20, 21, 21, 20, 20, 20, 20, 20, 20, 20, 20, 19, 20, 20, 20, 19, 19, 20],
+    [20, 19, 19, 20, 20, 19, 19, 19, 19, 19, 19, 19, 19, 18, 19, 19, 19, 18, 18, 19, 19, 18, 18, 18, 18, 18, 18, 18],
+    [21, 21, 21, 22, 21, 21, 21, 21, 20, 21, 21, 21, 20, 20, 21, 21, 20, 20, 20, 21, 20, 20, 20, 20, 19, 20, 20, 20, 20],
+    [20, 21, 21, 21, 20, 20, 21, 21, 20, 20, 20, 21, 20, 20, 20, 20, 19, 20, 20, 20, 19, 19, 20, 20, 19, 19, 19, 20, 20],
+    [21, 22, 22, 22, 21, 21, 22, 22, 21, 21, 21, 22, 21, 21, 21, 21, 20, 21, 21, 21, 20, 20, 21, 21, 20, 20, 20, 21, 21],
+    [22, 22, 22, 22, 21, 22, 22, 22, 21, 21, 22, 22, 21, 21, 21, 22, 21, 21, 21, 21, 20, 21, 21, 21, 20, 20, 21, 21, 21],
+    [23, 23, 24, 24, 23, 23, 23, 24, 23, 23, 23, 23, 22, 23, 23, 23, 22, 22, 23, 23, 22, 22, 22, 23, 22, 22, 22, 22, 23],
+    [23, 24, 24, 24, 23, 23, 24, 24, 23, 23, 23, 24, 23, 23, 23, 23, 22, 23, 23, 23, 22, 22, 23, 23, 22, 22, 22, 23, 23],
+    [23, 24, 24, 24, 23, 23, 24, 24, 23, 23, 23, 24, 23, 23, 23, 23, 22, 23, 23, 23, 22, 22, 23, 23, 22, 22, 22, 23, 23],
+    [24, 24, 24, 24, 23, 24, 24, 24, 23, 23, 24, 24, 23, 23, 23, 24, 23, 23, 23, 23, 22, 23, 23, 23, 22, 22, 23, 23, 23],
+    [23, 23, 23, 23, 22, 23, 23, 23, 22, 22, 23, 23, 22, 22, 22, 23, 22, 22, 22, 22, 21, 22, 22, 22, 21, 21, 22, 22, 22],
+    [22, 22, 23, 23, 22, 22, 22, 23, 22, 22, 22, 22, 21, 22, 22, 22, 21, 21, 22, 22, 21, 21, 21, 22, 21, 21, 21, 21, 22]
+]
+const PrincipleTermYear = [
+    [13, 45, 81, 113, 149, 185, 201],
+    [21, 57, 93, 125, 161, 193, 201],
+    [21, 56, 88, 120, 152, 188, 200, 201],
+    [21, 49, 81, 116, 144, 176, 200, 201],
+    [17, 49, 77, 112, 140, 168, 200, 201],
+    [28, 60, 88, 116, 148, 180, 200, 201],
+    [25, 53, 84, 112, 144, 172, 200, 201],
+    [29, 57, 89, 120, 148, 180, 200, 201],
+    [17, 45, 73, 108, 140, 168, 200, 201],
+    [28, 60, 92, 124, 160, 192, 200, 201],
+    [16, 44, 80, 112, 148, 180, 200, 201],
+    [17, 53, 88, 120, 156, 188, 200, 201]
+]
+const StemNames = '甲乙丙丁戊己庚辛壬癸'
+const BranchNames = '子丑寅卯辰巳午未申酉戌亥'
+const ZodiacSigns = '鼠牛虎兔龙蛇马羊猴鸡狗猪'
+const LunarMonths = '正二三四五六七八九十冬腊'
+const PrincipleTermNames = ["大寒", "雨水", "春分", "谷雨", "小满", "夏至", "大暑", "处暑", "秋分", "霜降", "小雪", "冬至"]
+const SectionalTermNames = ["小寒", "立春", "惊蛰", "清明", "立夏", "芒种", "小暑", "立秋", "白露", "寒露", "立冬", "大雪"]
+const ChineseNums = '十一二三四五六七八九十'
+
+//星座
+// const Astro = "\u9b54\u7faf\u6c34\u74f6\u53cc\u9c7c\u767d\u7f8a\u91d1\u725b\u53cc\u5b50\u5de8\u87f9\u72ee\u5b50\u5904\u5973\u5929\u79e4\u5929\u874e\u5c04\u624b\u9b54\u7faf"
+// const AstroDays = [20, 19, 21, 21, 21, 22, 23, 23, 23, 23, 22, 22]
+const BaseDate = {
+    // 初始日,公历农历对应日期: 公历 1901 年 1 月 1 日,对应农历 4598 年 11 月 11 日
+    year: 1901,
+    month: 1,
+    day: 1,
+    index: 0,
+    chineseYear: 4598 - 1,
+    chineseMonth: 11,
+    chineseDate: 11
+}
+const SolarTermRevises = [
+    { solar: SectionalTermNames[2], year: 2014, month: 3, wrong: 5, correct: 6 },
+    { solar: PrincipleTermNames[2], year: 2051, month: 3, wrong: 21, correct: 20 },
+    { solar: SectionalTermNames[1], year: 2083, month: 2, wrong: 4, correct: 3 },
+    { solar: PrincipleTermNames[2], year: 2084, month: 3, wrong: 20, correct: 19 },
+    { solar: SectionalTermNames[5], year: 2094, month: 6, wrong: 6, correct: 5 }
+]
+
+const isGregorianLeapYear = gregorianYear => {
+    let isLeap = false
+    if (gregorianYear % 4 == 0) isLeap = true
+    if (gregorianYear % 100 == 0) isLeap = false
+    if (gregorianYear % 400 == 0) isLeap = true
+    return isLeap
+}
+
+const daysInGregorianMonth = (y, m) => {
+    let d = DaysInGregorianMonths[m - 1]
+    if (m == 2 && isGregorianLeapYear(y)) d++ // 公历闰年二月多一天
+        return d
+}
+
+const dayOfYear = (y, m, d) => {
+    let c = 0
+    for (let i = 1; i < m; i++) {
+        c = c + daysInGregorianMonth(y, i)
+    }
+    c = c + d
+    return c
+}
+
+const dayOfWeek = (y, m, d) => {
+    let w = 1 // 公历一年一月一日是星期一,所以起始值为星期日
+    y = (y - 1) % 400 + 1 // 公历星期值分部 400 年循环一次
+    let ly = (y - 1) / 4 // 闰年次数
+    ly = ly - (y - 1) / 100
+    ly = ly + (y - 1) / 400
+    let ry = y - 1 - ly // 常年次数
+    w = w + ry // 常年星期值增一
+    w = w + 2 * ly // 闰年星期值增二
+    w = w + dayOfYear(y, m, d)
+    w = (w - 1) % 7 + 1
+    return w
+}
+
+const daysInChineseMonth = (y, m) => {
+    // 注意:闰月 m < 0
+    let index = y - BaseDate.chineseYear + BaseDate.index
+    let v = 0
+    let l = 0
+    let d = 30
+    if (1 <= m && m <= 8) {
+        v = ChineseCalendars[2 * index]
+        l = m - 1
+        if (((v >> l) & 0x01) == 1) d = 29
+    } else if (9 <= m && m <= 12) {
+        v = ChineseCalendars[2 * index + 1]
+        l = m - 9
+        if (((v >> l) & 0x01) == 1) d = 29
+    } else {
+        v = ChineseCalendars[2 * index + 1]
+        v = (v >> 4) & 0x0F
+        if (v != Math.abs(m)) {
+            d = 0
+        } else {
+            d = 29
+            for (let i = 0; i < BigLeapMonthYears.length; i++) {
+                if (BigLeapMonthYears[i] == index) {
+                    d = 30
+                    break
+                }
+            }
+        }
+    }
+    return d
+}
+
+const nextChineseMonth = (y, m) => {
+    let n = Math.abs(m) + 1
+    if (m > 0) {
+        let index = y - BaseDate.chineseYear + BaseDate.index;
+        let v = ChineseCalendars[2 * index + 1]
+        v = (v >> 4) & 0x0F
+        if (v == m) n = -m
+    }
+    if (n == 13) n = 1
+    return n
+}
+
+const sectionalTerm = (y, m) => {
+    if (y < 1901 || y > 2100) return 0
+    let index = 0
+    let ry = y - BaseDate.year + 1
+    while (ry >= SectionalTermYear[m - 1][index]) { index++ }
+    return SectionalTermMap[m - 1][4 * index + ry % 4]
+}
+
+const principleTerm = (y, m) => {
+    if (y < 1901 || y > 2100) return 0
+    let index = 0
+    let ry = y - BaseDate.year + 1
+    while (ry >= PrincipleTermYear[m - 1][index]) { index++ }
+    return PrincipleTermMap[m - 1][4 * index + ry % 4]
+}
+
+const checkSolarTerm = (gregorianYear, gregorianMonth, gregorianDay) => {
+    let filterYear = SolarTermRevises.filter(item => item.year == gregorianYear)
+    if (filterYear.length > 0) {
+        filterYear = filterYear[0]
+        if (filterYear.month == gregorianMonth) {
+            if (filterYear.wrong == gregorianDay) return { correct: false, type: 'wroung' }
+            if (filterYear.correct == gregorianDay) return { correct: false, type: 'revise', solar: filterYear.solar }
+        }
+    }
+    return { correct: true }
+}
+
+const lunarYear = chineseYear => StemNames[(chineseYear - 1) % 10] + BranchNames[(chineseYear - 1) % 12] + ZodiacSigns[(chineseYear - 1) % 12] + '年'
+const lunarMonth = chineseMonth => chineseMonth > 0 ? `${ LunarMonths[chineseMonth - 1] }月` : `闰${ LunarMonths[-chineseMonth - 1] }月`
+const lunarDay = chineseDay => {
+    if (chineseDay < 1 || chineseDay > 30) return ''
+    if (chineseDay <= 10) return `初${ChineseNums[chineseDay]}`
+    else if (chineseDay < 20) return `十${ ChineseNums[chineseDay % 10] }`
+    else if (chineseDay == 20) return `二十`
+    else if (chineseDay < 30) return `廿${ ChineseNums[chineseDay % 10] }`
+    return '三十'
+}
+
+// //获取星座
+// const getAstro = (gregorianMonth, gregorianDay) => Astro.substr(gregorianMonth * 2 - (gregorianDay < AstroDays[gregorianMonth - 1] ? 2 : 0), 2) + "\u5ea7"
+
+class LunarDate {
+
+    constructor(gregorianYear, gregorianMonth, gregorianDay) {
+        this.initGregorianYear = gregorianYear
+        this.initGregorianMonth = gregorianMonth
+        this.initGregorianDay = gregorianDay
+        this.setGregorian(gregorianYear, gregorianMonth, gregorianDay)
+    }
+
+    resetInitGregorian(gregorianYear, gregorianMonth, gregorianDay) {
+        this.initGregorianYear = gregorianYear
+        this.initGregorianMonth = gregorianMonth
+        this.initGregorianDay = gregorianDay
+    }
+
+    setGregorian(gregorianYear, gregorianMonth = 1, gregorianDay = 1) {
+        this.gregorianYear = gregorianYear
+        this.gregorianMonth = gregorianMonth
+        this.gregorianDate = gregorianDay
+        this.isGregorianLeap = isGregorianLeapYear(gregorianYear)
+        this.dayOfYear = dayOfYear(gregorianYear, gregorianMonth, gregorianDay)
+        this.dayOfWeek = dayOfWeek(gregorianYear, gregorianMonth, gregorianDay)
+        this.reviseInfo = checkSolarTerm(gregorianYear, gregorianMonth, gregorianDay)
+        this.computeChineseFields()
+        this.computeSolarTerms()
+    }
+
+    getReviseInfo() {
+        return this.reviseInfo
+    }
+
+    computeChineseFields() {
+        if (this.gregorianYear < 1901 || this.gregorianYear > 2100) return 1
+        const { year, month, day, chineseYear, chineseMonth, chineseDate } = BaseDate
+        let startYear = year
+        let startMonth = month
+        let startDate = day
+        this.chineseYear = chineseYear
+        this.chineseMonth = chineseMonth
+        this.chineseDate = chineseDate
+        if (this.gregorianYear >= 2000) {
+            // 第二个对应日,用以提高计算效率
+            // 公历 2000 年 1 月 1 日,对应农历 4697 年 11 月 25 日
+            startYear = year + 99
+            startMonth = 1
+            startDate = 1
+            this.chineseYear = chineseYear + 99
+            this.chineseMonth = 11
+            this.chineseDate = 25
+        }
+        let daysDiff = 0
+        for (let i = startYear; i < this.gregorianYear; i++) {
+            daysDiff += 365
+            if (isGregorianLeapYear(i)) daysDiff += 1
+        }
+        for (let i = startMonth; i < this.gregorianMonth; i++) {
+            daysDiff += daysInGregorianMonth(this.gregorianYear, i)
+        }
+        daysDiff += this.gregorianDate - startDate
+        this.chineseDate += daysDiff
+        let lastDate = daysInChineseMonth(this.chineseYear, this.chineseMonth)
+        let nextMonth = nextChineseMonth(this.chineseYear, this.chineseMonth)
+        while (this.chineseDate > lastDate) {
+            if (Math.abs(nextMonth) < Math.abs(this.chineseMonth)) { this.chineseYear++ }
+            this.chineseMonth = nextMonth
+            this.chineseDate -= lastDate
+            lastDate = daysInChineseMonth(this.chineseYear, this.chineseMonth)
+            nextMonth = nextChineseMonth(this.chineseYear, this.chineseMonth)
+        }
+        return 0
+    }
+
+    computeSolarTerms() {
+        if (this.gregorianYear < 1901 || this.gregorianYear > 2100) return 1
+        this.sectionalTerm = sectionalTerm(this.gregorianYear, this.gregorianMonth)
+        this.principleTerm = principleTerm(this.gregorianYear, this.gregorianMonth)
+        return 0
+    }
+
+    getLunarDate() {
+        this.setGregorian(this.initGregorianYear, 1, 1)
+        const _lunarYear = this.chineseYear + 1
+        this.setGregorian(this.initGregorianYear, this.initGregorianMonth, this.initGregorianDay)
+        let lunar_day = ''
+        let lunar_type = ''
+        let lunar_date = lunarDay(this.chineseDate)
+        if (this.reviseInfo.correct) {
+            if (this.gregorianDate == this.sectionalTerm) {
+                lunar_day = SectionalTermNames[this.gregorianMonth - 1]
+                lunar_type = 'solar'
+            } else if (this.gregorianDate == this.principleTerm) {
+                lunar_day = PrincipleTermNames[this.gregorianMonth - 1]
+                lunar_type = 'solar'
+            } else if (this.chineseDate == 1 && this.chineseMonth > 0) {
+                lunar_day = LunarMonths[this.chineseMonth - 1] + '月'
+            } else if (this.chineseDate == 1 && this.chineseMonth < 0) {
+                lunar_day = '闰' + LunarMonths[-this.chineseMonth - 1] + '月'
+            } else {
+                lunar_day = lunar_date
+            }
+        } else {
+            if (this.reviseInfo.type == 'wroung') {
+                if (this.chineseDate == 1 && this.chineseMonth > 0) {
+                    lunar_day = LunarMonths[this.chineseMonth - 1] + '月'
+                } else if (this.chineseDate == 1 && this.chineseMonth < 0) {
+                    lunar_day = '闰' + LunarMonths[-this.chineseMonth - 1] + '月'
+                } else {
+                    lunar_day = lunar_date
+                }
+            } else {
+                lunar_day = this.reviseInfo.solar
+                lunar_type = 'solar'
+            }
+        }
+        return {
+            year: this.gregorianYear,
+            month: this.gregorianMonth,
+            day: this.gregorianDate,
+            lunar_order: _lunarYear,
+            lunar_year: lunarYear(_lunarYear),
+            lunar_month: lunarMonth(this.chineseMonth),
+            lunar_day,
+            lunar_date,
+            lunar_type,
+            lm: this.chineseMonth,
+            ld: this.chineseDate,
+            // astro: getAstro(this.gregorianMonth, this.gregorianDate)
+        }
+    }
+}
+
+module.exports.Lunar = new LunarDate(2000, 1, 1)