最近在用ant-design-vue开发过程中,想使用农历日历,不过现有的Calendar暂时不支持农历日历,于是基于ant-design-vue的组件Calendar,再此基础上进行扩展开发了一个简易的农历日历组件。

核心思想就是有一个数组记录1900~2049之间每个农历年的信息(这些信息包括每个月的天数、闰月月份、闰月天数),取到公历日期后,计算此日期与公历1900年1月30日0时的差距天数(该天是农历1900年正月初一的前一天),根据差距天数计算出当前日期的农历日期。

目前仅支持1900~2049年的农历日期,另外当前也比较简易,算法可以继续优化,功能也可以进行扩展,有需要可以再次基础进行扩展。

下面直接贴出代码,代码就两个文件,一个.vue文件一个.js文件,代码中有注释,逻辑比较清晰。

js文件:

/*** 每一年的农历信息共计存储在5位十六制数中,即20位二进制数中* 1111 1111 1111 1111 1111* 高3位暂时空置,第4位当该年是闰年时标志该年闰月是大月(30天)还是小月(29天),是大月则标志位为1* 低4位用来标志该年闰月是哪一月份* 中间12位由高到低依次用来标志该年1~12月份分别是大月(30天)还是小月(29天),是大月则标志位为1*/
const LUNAR_INFO = [0x04bd8, 0x04ae0, 0x0a570,0x054d5, 0x0d260, 0x0d950, 0x16554, 0x056a0, 0x09ad0, 0x055d2,0x04ae0, 0x0a5b6, 0x0a4d0, 0x0d250, 0x1d255, 0x0b540, 0x0d6a0,0x0ada2, 0x095b0, 0x14977, 0x04970, 0x0a4b0, 0x0b4b5, 0x06a50,0x06d40, 0x1ab54, 0x02b60, 0x09570, 0x052f2, 0x04970, 0x06566,0x0d4a0, 0x0ea50, 0x06e95, 0x05ad0, 0x02b60, 0x186e3, 0x092e0,0x1c8d7, 0x0c950, 0x0d4a0, 0x1d8a6, 0x0b550, 0x056a0, 0x1a5b4,0x025d0, 0x092d0, 0x0d2b2, 0x0a950, 0x0b557, 0x06ca0, 0x0b550,0x15355, 0x04da0, 0x0a5d0, 0x14573, 0x052d0, 0x0a9a8, 0x0e950,0x06aa0, 0x0aea6, 0x0ab50, 0x04b60, 0x0aae4, 0x0a570, 0x05260,0x0f263, 0x0d950, 0x05b57, 0x056a0, 0x096d0, 0x04dd5, 0x04ad0,0x0a4d0, 0x0d4d4, 0x0d250, 0x0d558, 0x0b540, 0x0b5a0, 0x195a6,0x095b0, 0x049b0, 0x0a974, 0x0a4b0, 0x0b27a, 0x06a50, 0x06d40,0x0af46, 0x0ab60, 0x09570, 0x04af5, 0x04970, 0x064b0, 0x074a3,0x0ea50, 0x06b58, 0x055c0, 0x0ab60, 0x096d5, 0x092e0, 0x0c960,0x0d954, 0x0d4a0, 0x0da50, 0x07552, 0x056a0, 0x0abb7, 0x025d0,0x092d0, 0x0cab5, 0x0a950, 0x0b4a0, 0x0baa4, 0x0ad50, 0x055d9,0x04ba0, 0x0a5b0, 0x15176, 0x052b0, 0x0a930, 0x07954, 0x06aa0,0x0ad50, 0x05b52, 0x04b60, 0x0a6e6, 0x0a4e0, 0x0d260, 0x0ea65,0x0d530, 0x05aa0, 0x076a3, 0x096d0, 0x04bd7, 0x04ad0, 0x0a4d0,0x1d0b6, 0x0d250, 0x0d520, 0x0dd45, 0x0b5a0, 0x056d0, 0x055b2,0x049b0, 0x0a577, 0x0a4b0, 0x0aa50, 0x1b255, 0x06d20, 0x0ada0];const LUNAR_MONTH_NUMBER = ["正", "二", "三", "四", "五", "六", "七", "八", "九", "十", "冬", "腊"];
const CHINESE_NUMBER = ["一", "二", "三", "四", "五", "六", "七", "八", "九", "十"];
const CHINESE_ZODIAC = ["鼠", "牛", "虎", "兔", "龙", "蛇", "马", "羊", "猴", "鸡", "狗", "猪"];
const HEAVENLY_STEMS = ["甲", "乙", "丙", "丁", "戊", "己", "庚", "辛", "壬", "癸"];
const EARTHLY_BRANCHES = ["子", "丑", "寅", "卯", "辰", "巳", "午", "未", "申", "酉", "戌", "亥"];
const CHINESE_TEN = ["初", "十", "廿", "卅"];
/*** 公历1900年1月30日0时的时间戳,该天是农历1900年正月初一的前一天*/
const START_DATE_MS = -2206512343000;/*** 最小和最大允许的公历日期范围* 农历1900年正与初一和农历1949年腊月廿九*/
const MIN_LUNAR_DATE_MS = -2206425943000;
const MAX_LUNAR_DATE_MS = 2526480000000;export default class Lunar {constructor(moment) {let time = moment.valueOf();if (time < MIN_LUNAR_DATE_MS || time >= MAX_LUNAR_DATE_MS) {throw new Error("date out of range,the range is 1900-01-31 ~ 2050-01-22");}//求出和1900年1月30日相差的天数let offset = parseInt((time - START_DATE_MS) / 86400000);//依次获取每农历年的天数,用offset减去每年天数//当offset小于当年总天数时,则当年即为该日期所属年份,剩余offset即为该年的第几天let iYear, daysOfYear;for (iYear = 1900; iYear < 2050; iYear++) {daysOfYear = Lunar.getYearDays(iYear);if (offset > daysOfYear) {offset -= daysOfYear;} else {break;}}// 农历年份this.year = iYear;this.leapMonth = Lunar.getLeapMonth(iYear); // 闰哪个月,1-12//用当年的天数offset,逐个减去每月(农历)的天数//当offset小于当月总天数时,则当月即为该日期所属月份,求出当天是本月的第几天//当该年为闰年是,遍历到闰月月份后,需减去闰月总天数,并标记该月是闰月let iMonth, daysOfMonth;for (iMonth = 1; iMonth < 13; iMonth++) {daysOfMonth = Lunar.getMonthDays(this.year, iMonth);if (offset > daysOfMonth) {offset -= daysOfMonth;} else {break;}//如果当前月份与闰月月份一致时开始处理闰月数据if (iMonth === this.leapMonth) {daysOfMonth = Lunar.getLeapMonthDays(this.year);if (offset > daysOfMonth) {offset -= daysOfMonth;} else {this.isLeapMonth = true;break;}}}this.month = iMonth;this.day = offset;}/*** 获取农历年份* @author bawy* @date 2019/11/4 22:56* @return 农历年份*/getLunarYear() {return this.year;}/*** 获取农历月份* @author bawy* @date 2019/11/4 22:56* @return 农历月份*/getLunarMonth() {return this.month;}/*** 获取农历天数* @author bawy* @date 2019/11/4 22:56* @return 农历天数*/getLunarDay() {return this.day;}/*** 获取中文农历日期* @author bawy* @date 2019/11/7 0:51* @return 中文农历日期*/getChineseLunarDay() {if (this.day === 1) {return (this.isLeapMonth === true ? '闰' :'') + LUNAR_MONTH_NUMBER[this.month - 1] + '月';}return Lunar.getChinaDayString(this.day);}/*** 是否闰年* @author bawy* @date 2019/11/4 22:55* @return 是闰年返回true*/isLeap() {return this.leapMonth > 0;}/*** 当前日期所属月份是否为闰月* @author bawy* @date 2019/11/4 22:55* @return 是则返回true*/isLeapMonth() {return this.isLeapMonth === true;}/*** 获取当前日期所属年份生肖* @author bawy* @date 2019/11/4 22:55* @return 生肖*/getChinesZodiac() {return Lunar.getChinesZodiac(this.year);}/*** 获取当前农历日期的天干地支* @author bawy* @date 2019/11/4 22:56* @return 当前农历日期的天干地支*/getChineseEra() {return Lunar.getChineseEra(this.year);}/*** 获取农历 y 年的总天数* 基础天数为12*29=348天* 遍历该年份每个月是大月还是小月,大月则在基础天数上增加一天* 获取该年闰月天数,加上闰月天数得到该年总天数* @author bawy* @date 2019/11/4 22:56* @param y 年份* @return 总天数*/static getYearDays(y) {let i, sum = 348;for (i = 0x8000; i > 0x8; i >>= 1) {if ((LUNAR_INFO[y - 1900] & i) !== 0) {sum += 1;}}return (sum + Lunar.getLeapMonthDays(y));}/*** 获取农历 y 年闰月的天数(29或30天),没有闰月返回0* 判断该年份是否存在闰月* 如果存在闰月,提取闰月标志位,如果为1(大月)则返回30,反之返回29* @author bawy* @date 2019/11/4 22:56* @param y 年份* @return 闰月的天数*/static getLeapMonthDays(y) {if (Lunar.getLeapMonth(y) === 0) {return 0;} else {if ((LUNAR_INFO[y - 1900] & 0x10000) === 0) {return 29;} else {return 30;}}}/*** 获取农历 y 年闰哪个月1-12,没闰月返回0* 取出指定年份信息的后四位,对应的值即为闰月的月份* @author bawy* @date 2019/11/4 22:57* @param y 年份* @return 闰月月份*/static getLeapMonth(y) {return LUNAR_INFO[y - 1900] & 0xf;}/*** 获取指定农历 y 年 m 月的总天数(29或30天)* 提取指定年份信息中指定月份的标志位,标志位为1则返回30否则返回29* @author bawy* @date 2019/11/4 22:57* @param y 年份* @param m 月份* @return 该年该月总天数*/static getMonthDays(y, m) {if ((LUNAR_INFO[y - 1900] & (0x10000 >> m)) === 0) {return 29;} else {return 30;}}/*** 获取指定农历年份对应的生肖* @author bawy* @date 2019/11/4 22:57* @param y 农历年份* @return 生肖*/static getChinesZodiac(y) {return CHINESE_ZODIAC[(y - 4) % 12];}/*** 获取指定农历年份的天干地支* 1864年是甲子年,作为0坐标,根据偏移量计算天干地支* @author bawy* @date 2019/11/4 22:57* @param y 农历年份* @return 天干地支*/static getChineseEra(y) {let num = y - 1864;return (HEAVENLY_STEMS[num % 10] + EARTHLY_BRANCHES[num % 12]);}/*** 指定日期转为中文* @author bawy* @date 2019/11/4 22:57* @param day 日期天数* @return 中文日期*/static getChinaDayString(day) {if (day > 30) {throw new Error("the lunar month most has 30 days");}if (day === 10) {return "初十";} else {let n = day % 10 === 0 ? 9 : day % 10 - 1;return CHINESE_TEN[parseInt(day / 10)] + CHINESE_NUMBER[n];}}
}

vue文件:

<template><div :style="{width: width + 'px', border: '1px solid #d9d9d9', borderRadius: '4px'}"><a-calendar :locale="locale" :fullscreen="false" @panelChange="onPanelChange" @change="onDateChange"><div slot="dateFullCellRender" slot-scope="text" :class="getClass(text)"><li class="calendar-date">{{getDate(text)}}</li><li>{{getLunarDate(text)}}</li></div></a-calendar></div>
</template><script>import moment from "moment";import Lunar from "./lunar";import zhCN from 'ant-design-vue/lib/locale-provider/zh_CN';export default {name: "LunarCalendar",data() {return {locale: zhCN,today: null,currentMonth: 0,selectDate: null}},props: {width: {type: Number,required: false,default() {return 422;}}},methods: {onPanelChange(value, mode) {this.currentMonth = parseInt(value.format("M"));},onDateChange(value) {this.selectDate = value;if (parseInt(value.format("M")) !== this.currentMonth) {this.currentMonth = parseInt(value.format("M"));}},getClass(value) {let classStr = "lunar-date-cell";if (value.format("YYYY-MM-DD") === this.today.format("YYYY-MM-DD")) {classStr += " lunar-date-cell-today"}let month = parseInt(value.format("M"));if (month === this.currentMonth - 1 || (month === 12 && this.currentMonth === 1)) {classStr += " lunar-date-cell-last-month"}if (month === this.currentMonth + 1 || (month === 1 && this.currentMonth === 12)) {classStr += " lunar-date-cell-next-month"}if (this.selectDate && value.format("YYYY-MM-DD") === this.selectDate.format("YYYY-MM-DD")) {classStr += " lunar-date-cell-selected-day"}return classStr;},getDate(value) {return value.format("D");},getLunarDate(value) {let lunar = new Lunar(value);return lunar.getChineseLunarDay();}},created() {this.today = new moment();this.currentMonth = parseInt(this.today.format("M"));}}
</script><style scoped lang="less">.lunar-date-cell {text-align: center;}.lunar-date-cell:hover {background: #e6f7ff;cursor: pointer;}.lunar-date-cell-today {box-shadow: 0 0 0 1px #1890ff inset;}.lunar-date-cell-selected-day {color: #fff;background: #1890ff;}//上一个月的日期单元格.lunar-date-cell-last-month {color: rgba(0, 0, 0, 0.25);}//下一个月的日期单元格.lunar-date-cell-next-month {color: rgba(0, 0, 0, 0.25);}//公历日期样式.calendar-date {font-weight: bold;}</style>

基于ant-design-vue的简易农历日历相关推荐

  1. Vue 2.x折腾记 - (16) 基于Ant Design Vue 封装一个配置式的表单搜索组件

    前言 这次的后台管理系统项目选型用了Vue来作为主技术栈: 因为前段时间用过React来写过项目(用了antd),感觉棒棒的. 所以这次就排除了Element UI,而采用了Ant Design Vu ...

  2. 基于Ant Design vue框架登录demo

    我们直接进入正题吧~~~ 先来看下效果图 那么前端代码呢~~~ 不着急,这就双手奉上哈~~ <a-col :span="12"><div class=" ...

  3. Vue 2.x折腾记 - (17) 基于Ant Design Vue 封装一个配置式的表单组件

    前言 写了个类似上篇搜索的封装,但是要考虑的东西更多. 具体业务比展示的代码要复杂,篇幅太长就不引入了. 效果图 2019-04-25 添加了下拉多选的渲染,并搜索默认过滤文本而非值 简化了渲染的子组 ...

  4. 基于Ant Design vue框架之三 删除功能细分

    我们还是老规矩,先上效果图吧~~ 需要看整个页面的小盆友可以点下面这个路径哈~~ 页面路径:总页面展示 继续上干货吧~翠花,上代码~~ <a-button type="danger&q ...

  5. 基于 Vue3.0 和 Ant Design Vue ,高颜值管理后台UI框架vue-vben-admin运行

    简介 Vue Vben Admin 是一个免费开源的中后台模版.使用了最新的vue3,vite2,TypeScript等主流技术开发,开箱即用的中后台前端解决方案,也可用于学习参考. Github地址 ...

  6. 基于Ant Design of Vue实现时长组件 duration

    最近遇到一个需求,需要一个输入时长的组件,在经过一番寻找后没有合适的,最终自己动手写一个(实现了v-model双向绑定),记录一下,也给小伙伴们提供一个方便. 本示例基于ant design of v ...

  7. 【Ant Design Vue】之Grid栅格和Space间距

    文章目录 Grid 栅格 Space 间距 Grid 栅格 Ant Design Vue 将整个设计建议区域按照 24 等分的原则进行划分,划分之后的信息区块我们称之为『盒子』.建议横向排列的盒子数量 ...

  8. ant design vue table 高度自适应_很受欢迎的vue前端UI框架

    最近在逛各大网站,论坛,SegmentFault等编程问答社区,发现Vue.js异常火爆,重复性的提问和内容也很多,小编自己也趁着这个大前端的热潮,着手学习了一段时间的Vue.js,目前用它正在做自己 ...

  9. Ant Design Vue 相关介绍

    Ant Design Vue有三个版本: v1(基于vue2.x):https://www.antdv.com/docs/vue/introduce-cn/ v2(基于vue3.x):https:// ...

  10. ant design vue table 高度自适应_Vue组件库大评测 Element, iView, HeyUI, Ant Design Vue

    今天偶然的机会想了解下其他Vue相关的组件库,网上刚好有文章,顺便自己做一下笔记,算是资源整理吧 .好了,话不多说,直接开始: 组件库的选择对于前端开发有者至关重要的影响,而组件的丰富性以及健壮性是我 ...

最新文章

  1. 需求用例分析之三:补充规约
  2. boost::mpl模块实现pair_view相关的测试程序
  3. wireshark https_测开日常积累-wireshark应用
  4. PDE12 wave equation: charactistics
  5. 190413每日一句
  6. GSM与GPRS的区别
  7. zz很有用的生活小窍门
  8. Unity3D内置Shader私房课(三)Decal贴花
  9. 将/etc/passwd文件的内容写入/tmp/passwd文件中
  10. Java基础篇——面向对象编程
  11. 冷暖自知!看完这篇区块链媒体舆情报告,想说点啥?
  12. 关于计算机优点缺点的英语作文,关于网络优缺点的英语作文(精选3篇)
  13. 计算机专业中国十大名校最新排名,中国最顶尖的十所大学 中国十大名校排名...
  14. 云演CTF刷题 lfi
  15. 股票几个指数周收益率和月收益率的计算
  16. IDEA 对单个的java class文件打成jar包
  17. python实现画图工具
  18. 设置MySQL复制时,replicate-ignore-db模式下如何正常工作
  19. Math.floor() 返回小于或等于一个给定数字的最大整数。
  20. Python 量化分析ETF指数基金投资

热门文章

  1. 图形在计算机中用什么格式表示什么,使用什么软件打开dat文件,图形文字说明如何在计算机中查看DAT类型文件...
  2. 合并多个HEX文件的方法
  3. 一个朋友写的新浪灌水机一部
  4. 迅雷5(Thunder) v5.9.5.990下载
  5. C++如何定义一个长度超过一百万的整型数组
  6. 3dmax 管子动画_3Dmax怎么制作发光管?
  7. libvlc_media_player_stop死锁问题解决
  8. GDUT2020新生赛——解题报告
  9. html 下拉框a标签跳转,html下拉框跳转问题
  10. jpeg2000算法详解(1)