农历日期组件 【恋人小清单开发总结】

发布于 5 年前作者 jiezhou823 次浏览最后编辑 5 年前来自 share

因为我过农历生日,所以开发了这个功能哈哈哈哈

自己说了算的感觉真好

言归正传

官方的日期组件不支持农历,所以需要借助官方的多列选择器做扩展

因为农历有闰年的缘故,在选择了年份后需要更新月份,选择了月份以后需要更新日期

另外一个需要注意的是闰年的情况下,多列选择器月份选中的索引需要注意一下,如果在闰月之前的月份索引需要减一,闰月之后的月份不需要减一

比如农历2020的四月,对应的月份索引应该等于3,闰4月对应的索引等于4.看下图片就明白了

看下效果图:

可以扫码体验

1、wxml代码

这里用到官方的多列选择器

<picker style="text-align: right;" data-type="lunar" name="lunarPicker"
        bindchange="multiPickerChangeHandler"
        bindcolumnchange="selectMultiPickerColumnHandler"
        mode="multiSelector"
        value="{{lunarDateRangeIndex}}"
        range-key="text"
        range="{{lunarDateRange}}">
    <input readonly disabled placeholder="选择日期" placeholder-class="edit-placeholder" value="{{lunarDate}}"/>
</picker>

2、js

const lunarDate = require('../../utils/lunarDate.js');
const {calendar} = require('../../utils/calendar.js');
Component({
    /**
 * 组件的属性列表
 */
properties: {
    lunarYear: Number,//农历年
    lunarMonth: Number,//农历月
    lunarDay: Number,//农历日
    isLeapMonth: Boolean,//闰月标记
    solarDate: String,//公历日期
    solarDateRange: Object//公历年份起始
},

/**
 * 组件的初始数据
 */
data: {
    lunarDateRange: [],//农历日期范围
    lunarDateRangeIndex: []//选中农历索引
},

    ready: function () {
        this._init();
    },

    /**
     * 组件的方法列表
     */
    methods: {
        _init: function () {
            const {
                lunarYear,
                lunarMonth,
                lunarDay,
                isLeapMonth,
                solarDate,
                solarDateRange
            } = this.properties;

            this._initLunarDateRange(solarDate, solarDateRange, lunarYear, lunarMonth, lunarDay, isLeapMonth);
        },

        /**
         * 初始化农历日期数据
         * @param solarDate
         * @param solarDateRange
         * @param lunarYear
         * @param lunarMonth
         * @param lunarDay
         * @param isLeapMonth
         * @private
         */
        _initLunarDateRange: function (solarDate, solarDateRange, lunarYear, lunarMonth, lunarDay, isLeapMonth) {
            const lunarDateRange = lunarDate.initLunarDateRangeHandler(solarDateRange);
            const lunarDateRangeIndex = this._setDefaultLunarDateRangeIndex(lunarDateRange, solarDate, solarDateRange, lunarYear, lunarMonth, lunarDay, isLeapMonth);
            this._updateLunarDateRange(lunarDateRange, lunarDateRangeIndex);
            if (lunarYear && lunarMonth && lunarDay) {
                console.log("农历日期不为空,准备显示农历日期");
                this._showLunarDateHandler(lunarDateRange, lunarDateRangeIndex, false);
            } else if (solarDate) {
                console.log("公历日期不为空,准备显示转化后的农历日期");
                this._showLunarDateHandler(lunarDateRange, lunarDateRangeIndex, true);
            }
            console.log("农历日期:", lunarDateRange);
        },

        /**
         * 填充默认的农历日期选中索引
         * 默认为当前日期
         * @param lunarDateRange
         * @param solarDate
         * @param solarDateRange
         * @param lunarYear
         * @param lunarMonth
         * @param lunarDay
         * @param isLeapMonth
         * @returns {*[]}
         * @private
         */
        _setDefaultLunarDateRangeIndex: function (lunarDateRange, solarDate, solarDateRange, lunarYear, lunarMonth, lunarDay, isLeapMonth) {
            let lunarDateRangeIndex;
            if (lunarYear && lunarMonth && lunarDay) {
                console.log("外部传入农历日期:", lunarYear, lunarMonth, lunarDay, isLeapMonth);
                lunarDateRangeIndex = [lunarYear - solarDateRange.start, this._getLunarMonthIndex(lunarYear, lunarMonth, isLeapMonth), lunarDay - 1];
            } else {
                console.log("外部传入公历日期:", solarDate);
                let date = solarDate != null ? new Date(solarDate.replace(/-/g,"/")) : new Date();
                // let date = new Date(solarDate.replace(/-/g,"/"));
                const year = date.getFullYear();
                const month = date.getMonth() + 1;
                const day = date.getDate();
                const targetLunarDate = calendar.solar2lunar(year, month, day);
                console.log("转化后的农历日期:", JSON.stringify(targetLunarDate));
                const {lYear, lMonth, lDay, isLeap} = targetLunarDate;

                lunarYear = lYear;
                lunarMonth = lMonth;
                isLeapMonth = isLeap;
                lunarDateRangeIndex = [lYear - solarDateRange.start, this._getLunarMonthIndex(lunarYear, lunarMonth, isLeapMonth), lDay - 1];
            }
            lunarDateRange[1] = lunarDate.resetLunarMonthRangeHandler(lunarYear);
            lunarDateRange[2] = lunarDate.resetLunarDayRangeHandler(lunarYear, lunarMonth, isLeapMonth);

            console.log("默认的农历日期选中索引:", lunarDateRangeIndex);
            return lunarDateRangeIndex;
        },

        /**
         * 获取农历月份索引
         * @param lunarYear
         * @param lunarMonth
         * @param isLeapMonth
         * @returns {number}
         * @private
         */
        _getLunarMonthIndex: function (lunarYear, lunarMonth, isLeapMonth) {
            const leapMonth = calendar.leapMonth(lunarYear);
            //1、闰月索引不需要减一
            //2、闰年、非闰月,并且当前月份在闰月之后,索引不需要减一
            return (isLeapMonth || (!isLeapMonth && leapMonth > 0 && lunarMonth > leapMonth)) ? lunarMonth : lunarMonth - 1;
        },

        /**
         * 更新农历日期选择范围、更新农历日期选中索引
         * @param lunarDateRange
         * @param lunarDateRangeIndex
         * @private
         */
        _updateLunarDateRange: function (lunarDateRange, lunarDateRangeIndex) {
            this.setData({
                lunarDateRange,
                lunarDateRangeIndex
            }, function () {
                console.log("农历日期选择范围/农历日期选中索引更新完毕:", lunarDateRange, lunarDateRangeIndex)
            });
        },

        /**
         * 多列选择事件
         * @param e
         */
        selectMultiPickerColumnHandler: function (e) {
            const {column, value} = e.detail;
            let {lunarDateRange, lunarDateRangeIndex} = this.data;
            let selectYear;
            switch (column) {
                case 0:
                    //选择年份,准备更新月份数据
                    selectYear = lunarDateRange[column][value].value;
                    console.log("选择年份:", selectYear);
                    const memoryLunarMonthRange = lunarDate.resetLunarMonthRangeHandler(selectYear);
                    lunarDateRange[1] = memoryLunarMonthRange;
                    //更新选中值
                    lunarDateRangeIndex = [value, 0, 0];
                    this._updateLunarDateRange(lunarDateRange, lunarDateRangeIndex);
                    break;
                case 1:
                    //选择月份,准备更新天数数据
                    const {value: selectMonth, isLeapMonth} = lunarDateRange[column][value];
                    selectYear = lunarDateRange[0][lunarDateRangeIndex[0]].value;
                    console.log("选择年份:", selectYear, "选择月份:", selectMonth, "是否闰月:", isLeapMonth);
                    const memoryLunarDayRange = lunarDate.resetLunarDayRangeHandler(selectYear, selectMonth, isLeapMonth);
                    lunarDateRange[2] = memoryLunarDayRange;
                    //更新选中值
                    lunarDateRangeIndex = [lunarDateRangeIndex[0], value, 0];
                    this._updateLunarDateRange(lunarDateRange, lunarDateRangeIndex);
                    break;
                default:
                    console.log("选择日:", lunarDateRange[column][value]);
                    break;
            }

        },

        /**
         * 选择农历日期
         * @param e
         */
        multiPickerChangeHandler: function (e) {
            console.log('选择农历日期:', e);
            const {value: selectValue} = e.detail;
            const {lunarDateRange} = this.data;
            this._showLunarDateHandler(lunarDateRange, selectValue, true);
        },

        /**
         * 显示选中的农历日期
         * @param lunarDateRange
         * @param lunarDateRangeIndex
         * @param doTriggerEvent 是否触发通知事件,告知父页面当前选中的农历日期
         * @private
         */
        _showLunarDateHandler: function (lunarDateRange, lunarDateRangeIndex, doTriggerEvent) {
            const [lunarYearIndex, lunarMonthIndex, lunarDayIndex] = lunarDateRangeIndex;
            const isLeapMonth = lunarDateRange[1][lunarMonthIndex].isLeapMonth;
            this.setData({
                lunarDate: lunarDateRange[0][lunarYearIndex].text + lunarDateRange[1][lunarMonthIndex].text + lunarDateRange[2][lunarDayIndex].text,
                isLeapMonth,
                lunarDateRangeIndex
            }, function () {
                if (doTriggerEvent) {
                    console.log("触发lunarDateConfirm事件");
                    this.triggerEvent("lunarDateConfirm", {
                        lunarYear: lunarDateRange[0][lunarYearIndex].value,
                        lunarMonth: lunarDateRange[1][lunarMonthIndex].value,
                        lunarDay: lunarDateRange[2][lunarDayIndex].value,
                        isLeapMonth
                    });
                }
            });
        }
    }
});

3、lunarDate.js

const {calendar} = require('/calendar.js');
/**
 * 填充农历日期
 * [@param](/user/param) solarDateRange
 * [@returns](/user/returns) {Array}
 */
const initLunarDateRangeHandler = function (solarDateRange) {
    console.log("公历日期范围:", solarDateRange);
    const lunarDateRange = [];
    const lunarYearRange = [], lunarMonthRange = [], lunarDayRange = [];
    for (let i = solarDateRange.start; i <= solarDateRange.end; i++) {
        lunarYearRange.push({text: calendar.toGanZhiYear(i) + '(' + i + ')', value: i});
    }
    for (let i = 1; i <= 12; i++) {
        lunarMonthRange.push({text: calendar.toChinaMonth(i), value: i, isLeapMonth: false});
    }
    for (let i = 1; i <= 29; i++) {
        lunarDayRange.push({text: calendar.toChinaDay(i), value: i});
    }
    lunarDateRange.push(lunarYearRange);
    lunarDateRange.push(lunarMonthRange);
    lunarDateRange.push(lunarDayRange);
    console.log("农历日期:", lunarDateRange);
    return lunarDateRange;
};

/**
 * 重置农历月份
 * [@param](/user/param) selectYear
 * [@returns](/user/returns) {Array}
 */
const resetLunarMonthRangeHandler = function (selectYear) {
    const leapMonth = calendar.leapMonth(selectYear);
    const lunarMonthRange = [];
    console.log("选择年份:", selectYear, "闰月:", leapMonth);
    for (let i = 1; i <= 12; i++) {
        lunarMonthRange.push({text: calendar.toChinaMonth(i), value: i, isLeapMonth: false});
    }
    if (leapMonth > 0) {
        console.log("有闰月,准备更新月份数据");
        lunarMonthRange.splice(leapMonth, 0, {
            text: "闰" + calendar.toChinaMonth(leapMonth),
            value: leapMonth,
            isLeapMonth: true
        });
    }
    return lunarMonthRange;
};

/**
 * 重置农历日
 * [@param](/user/param) selectYear
 * [@param](/user/param) selectMonth
 * [@param](/user/param) isLeapMonth
 * [@returns](/user/returns) {Array}
 */
const resetLunarDayRangeHandler = function (selectYear, selectMonth, isLeapMonth) {
    const lunarDayRange = [];
    console.log("选择年份:", selectYear, "选择月份:", selectMonth, "是否闰月:", isLeapMonth);
    const days = isLeapMonth ? calendar.leapDays(selectYear) : calendar.monthDays(selectYear, selectMonth);
    for (let i = 1; i <= days; i++) {
        lunarDayRange.push({text: calendar.toChinaDay(i), value: i});
    }
    return lunarDayRange;
};

module.exports = {initLunarDateRangeHandler, resetLunarMonthRangeHandler, resetLunarDayRangeHandler};

4、calendar.js(来自网上)

/**
 * [@1900-2100](/user/1900-2100)区间内的公历、农历互转
 * @charset UTF-8
 * @Author Jea杨(JJonline@JJonline.Cn)
 * @Time  2014-7-21
 * @Time  2016-8-13 Fixed 2033hex、Attribution Annals
 * @Time  2016-9-25 Fixed lunar LeapMonth Param Bug
 * @Version 1.0.2
 * @公历转农历:calendar.solar2lunar(1987,11,01); //[you can ignore params of prefix 0]
 * @农历转公历:calendar.lunar2solar(1987,09,10); //[you can ignore params of prefix 0]
 */
const calendar = {
    /**
     * 农历1900-2100的润大小信息表
     * @Array Of Property
     * @return Hex
     */
    lunarInfo: [0x04bd8, 0x04ae0, 0x0a570, 0x054d5, 0x0d260, 0x0d950, 0x16554, 0x056a0, 0x09ad0, 0x055d2,//1900-1909
        0x04ae0, 0x0a5b6, 0x0a4d0, 0x0d250, 0x1d255, 0x0b540, 0x0d6a0, 0x0ada2, 0x095b0, 0x14977,//1910-1919
        0x04970, 0x0a4b0, 0x0b4b5, 0x06a50, 0x06d40, 0x1ab54, 0x02b60, 0x09570, 0x052f2, 0x04970,//1920-1929
        0x06566, 0x0d4a0, 0x0ea50, 0x06e95, 0x05ad0, 0x02b60, 0x186e3, 0x092e0, 0x1c8d7, 0x0c950,//1930-1939
        0x0d4a0, 0x1d8a6, 0x0b550, 0x056a0, 0x1a5b4, 0x025d0, 0x092d0, 0x0d2b2, 0x0a950, 0x0b557,//1940-1949
        0x06ca0, 0x0b550, 0x15355, 0x04da0, 0x0a5b0, 0x14573, 0x052b0, 0x0a9a8, 0x0e950, 0x06aa0,//1950-1959
        0x0aea6, 0x0ab50, 0x04b60, 0x0aae4, 0x0a570, 0x05260, 0x0f263, 0x0d950, 0x05b57, 0x056a0,//1960-1969
        0x096d0, 0x04dd5, 0x04ad0, 0x0a4d0, 0x0d4d4, 0x0d250, 0x0d558, 0x0b540, 0x0b6a0, 0x195a6,//1970-1979
        0x095b0, 0x049b0, 0x0a974, 0x0a4b0, 0x0b27a, 0x06a50, 0x06d40, 0x0af46, 0x0ab60, 0x09570,//1980-1989
        0x04af5, 0x04970, 0x064b0, 0x074a3, 0x0ea50, 0x06b58, 0x055c0, 0x0ab60, 0x096d5, 0x092e0,//1990-1999
        0x0c960, 0x0d954, 0x0d4a0, 0x0da50, 0x07552, 0x056a0, 0x0abb7, 0x025d0, 0x092d0, 0x0cab5,//2000-2009
        0x0a950, 0x0b4a0, 0x0baa4, 0x0ad50, 0x055d9, 0x04ba0, 0x0a5b0, 0x15176, 0x052b0, 0x0a930,//2010-2019
        0x07954, 0x06aa0, 0x0ad50, 0x05b52, 0x04b60, 0x0a6e6, 0x0a4e0, 0x0d260, 0x0ea65, 0x0d530,//2020-2029
        0x05aa0, 0x076a3, 0x096d0, 0x04afb, 0x04ad0, 0x0a4d0, 0x1d0b6, 0x0d250, 0x0d520, 0x0dd45,//2030-2039
        0x0b5a0, 0x056d0, 0x055b2, 0x049b0, 0x0a577, 0x0a4b0, 0x0aa50, 0x1b255, 0x06d20, 0x0ada0,//2040-2049
        /**Add By JJonline@JJonline.Cn**/
        0x14b63, 0x09370, 0x049f8, 0x04970, 0x064b0, 0x168a6, 0x0ea50, 0x06b20, 0x1a6c4, 0x0aae0,//2050-2059
        0x0a2e0, 0x0d2e3, 0x0c960, 0x0d557, 0x0d4a0, 0x0da50, 0x05d55, 0x056a0, 0x0a6d0, 0x055d4,//2060-2069
        0x052d0, 0x0a9b8, 0x0a950, 0x0b4a0, 0x0b6a6, 0x0ad50, 0x055a0, 0x0aba4, 0x0a5b0, 0x052b0,//2070-2079
        0x0b273, 0x06930, 0x07337, 0x06aa0, 0x0ad50, 0x14b55, 0x04b60, 0x0a570, 0x054e4, 0x0d160,//2080-2089
        0x0e968, 0x0d520, 0x0daa0, 0x16aa6, 0x056d0, 0x04ae0, 0x0a9d4, 0x0a2d0, 0x0d150, 0x0f252,//2090-2099
        0x0d520],//2100
    /**
     * 公历每个月份的天数普通表
     * @Array Of Property
     * @return Number
     */
    solarMonth: [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31],
    /**
     * 天干地支之天干速查表
     * @Array Of Property trans["甲","乙","丙","丁","戊","己","庚","辛","壬","癸"]
     * @return Cn string
     */
    Gan: ["\u7532", "\u4e59", "\u4e19", "\u4e01", "\u620a", "\u5df1", "\u5e9a", "\u8f9b", "\u58ec", "\u7678"],
    /**
     * 天干地支之地支速查表
     * @Array Of Property
     * @trans["子","丑","寅","卯","辰","巳","午","未","申","酉","戌","亥"]
     * @return Cn string
     */
    Zhi: ["\u5b50", "\u4e11", "\u5bc5", "\u536f", "\u8fb0", "\u5df3", "\u5348", "\u672a", "\u7533", "\u9149", "\u620c", "\u4ea5"],
    /**
     * 天干地支之地支速查表<=>生肖
     * @Array Of Property
     * @trans["鼠","牛","虎","兔","龙","蛇","马","羊","猴","鸡","狗","猪"]
     * @return Cn string
     */
    Animals: ["\u9f20", "\u725b", "\u864e", "\u5154", "\u9f99", "\u86c7", "\u9a6c", "\u7f8a", "\u7334", "\u9e21", "\u72d7", "\u732a"],
    /**
     * 24节气速查表
     * @Array Of Property
     * @trans["小寒","大寒","立春","雨水","惊蛰","春分","清明","谷雨","立夏","小满","芒种","夏至","小暑","大暑","立秋","处暑","白露","秋分","寒露","霜降","立冬","小雪","大雪","冬至"]
     * @return Cn string
     */
    solarTerm: ["\u5c0f\u5bd2", "\u5927\u5bd2", "\u7acb\u6625", "\u96e8\u6c34", "\u60ca\u86f0", "\u6625\u5206", "\u6e05\u660e", "\u8c37\u96e8", "\u7acb\u590f", "\u5c0f\u6ee1", "\u8292\u79cd", "\u590f\u81f3", "\u5c0f\u6691", "\u5927\u6691", "\u7acb\u79cb", "\u5904\u6691", "\u767d\u9732", "\u79cb\u5206", "\u5bd2\u9732", "\u971c\u964d", "\u7acb\u51ac", "\u5c0f\u96ea", "\u5927\u96ea", "\u51ac\u81f3"],
    /**
     * 1900-2100各年的24节气日期速查表
     * @Array Of Property
     * @return 0x string For splice
     */
    sTermInfo: ['9778397bd097c36b0b6fc9274c91aa', '97b6b97bd19801ec9210c965cc920e', '97bcf97c3598082c95f8c965cc920f',
        '97bd0b06bdb0722c965ce1cfcc920f', 'b027097bd097c36b0b6fc9274c91aa', '97b6b97bd19801ec9210c965cc920e',
        '97bcf97c359801ec95f8c965cc920f', '97bd0b06bdb0722c965ce1cfcc920f', 'b027097bd097c36b0b6fc9274c91aa',
        '97b6b97bd19801ec9210c965cc920e', '97bcf97c359801ec95f8c965cc920f', '97bd0b06bdb0722c965ce1cfcc920f',
        'b027097bd097c36b0b6fc9274c91aa', '9778397bd19801ec9210c965cc920e', '97b6b97bd19801ec95f8c965cc920f',
        '97bd09801d98082c95f8e1cfcc920f', '97bd097bd097c36b0b6fc9210c8dc2', '9778397bd197c36c9210c9274c91aa',
        '97b6b97bd19801ec95f8c965cc920e', '97bd09801d98082c95f8e1cfcc920f', '97bd097bd097c36b0b6fc9210c8dc2',
        '9778397bd097c36c9210c9274c91aa', '97b6b97bd19801ec95f8c965cc920e', '97bcf97c3598082c95f8e1cfcc920f',
        '97bd097bd097c36b0b6fc9210c8dc2', '9778397bd097c36c9210c9274c91aa', '97b6b97bd19801ec9210c965cc920e',
        '97bcf97c3598082c95f8c965cc920f', '97bd097bd097c35b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa',
        '97b6b97bd19801ec9210c965cc920e', '97bcf97c3598082c95f8c965cc920f', '97bd097bd097c35b0b6fc920fb0722',
        '9778397bd097c36b0b6fc9274c91aa', '97b6b97bd19801ec9210c965cc920e', '97bcf97c359801ec95f8c965cc920f',
        '97bd097bd097c35b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa', '97b6b97bd19801ec9210c965cc920e',
        '97bcf97c359801ec95f8c965cc920f', '97bd097bd097c35b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa',
        '97b6b97bd19801ec9210c965cc920e', '9

...

1 回复
mxia
mxia1 楼8 个月前

运行时出现错误如下,请求解决:

VM292:1 MiniProgramError

Cannot read property ‘start’ of null

TypeError: Cannot read property ‘start’ of null

    at Object.initLunarDateRangeHandler (http://127.0.0.1:45915/appservice/utils/lunarDate.js:19:33)