瀏覽代碼

更新库

快乐的梦鱼 2 月之前
父節點
當前提交
28dda12717

+ 1 - 1
src/App.vue

@@ -40,7 +40,7 @@ configTheme((theme) => {
 });
 });
 </script>
 </script>
 
 
-<style>
+<style lang="scss">
 	/*每个页面公共css */
 	/*每个页面公共css */
   @import "@/static/css/font.css";
   @import "@/static/css/font.css";
   @import "@/components/index.scss";
   @import "@/components/index.scss";

+ 85 - 34
src/components/basic/Button.vue

@@ -8,8 +8,7 @@
     center
     center
     direction="row"
     direction="row"
     v-bind="viewProps"
     v-bind="viewProps"
-    :pressedColor="type === 'custom' ? themeContext.resolveThemeColor(pressedColor) :
-      (themeContext.resolveThemeColor((plain || type === 'text') ? 'pressed.notice' : 'pressed.' + type))"
+    :pressedColor="finalPressedColor"
     :touchable="touchable && !loading"
     :touchable="touchable && !loading"
     @state="(v) => state = v"
     @state="(v) => state = v"
     @click="emit('click', $event)"
     @click="emit('click', $event)"
@@ -88,10 +87,11 @@ export interface ButtonProp {
    */
    */
   block?: boolean,
   block?: boolean,
   /**
   /**
-   * 通过 plain 属性将按钮设置为朴素按钮,朴素按钮的文字为按钮颜色,背景为白色。
-   * @default false
+   * * plain 将按钮设置为朴素按钮,朴素按钮的文字为按钮颜色,背景为白色。
+   * * light 将按钮设置为浅色按钮,浅色按钮的文字为按钮颜色,背景为主色调浅色。
+   * @default 'default'
    */
    */
-  plain?: boolean,
+  scheme?: 'default'|'plain'|'light',
   /**
   /**
    * 通过 loading 属性设置按钮为加载状态,加载状态下默认会隐藏按钮文字,可以通过 loadingText 设置加载状态下的文字。
    * 通过 loading 属性设置按钮为加载状态,加载状态下默认会隐藏按钮文字,可以通过 loadingText 设置加载状态下的文字。
    * @default false
    * @default false
@@ -262,6 +262,26 @@ const themeStyles = themeContext.useThemeStyles({
     borderColor: DynamicColor('ButtonPlainDangerBorderColor', 'danger'),
     borderColor: DynamicColor('ButtonPlainDangerBorderColor', 'danger'),
     color: DynamicColor('ButtonPlainDangerColor', 'danger'),
     color: DynamicColor('ButtonPlainDangerColor', 'danger'),
   },
   },
+  lightButtonDefault: {
+    backgroundColor: DynamicColor('ButtonLightDefaultBackgroundColor', 'background.button'),
+    color: DynamicColor('ButtonLightDefaultColor', 'text'),
+  },
+  lightButtonPrimary: {
+    backgroundColor: DynamicColor('ButtonLightPrimaryBackgroundColor', 'background.primary'),
+    color: DynamicColor('ButtonLightPrimaryColor', 'text.primary'),
+  },
+  lightButtonSuccess: {
+    backgroundColor: DynamicColor('ButtonLightSuccessBackgroundColor', 'background.success'),
+    color: DynamicColor('ButtonLightSuccessColor', 'text.success'),
+  },
+  lightButtonWarning: {
+    backgroundColor: DynamicColor('ButtonLightWarningBackgroundColor', 'background.warning'),
+    color: DynamicColor('ButtonLightWarningColor', 'text.warning'),
+  },
+  lightButtonDanger: {
+    backgroundColor: DynamicColor('ButtonLightDangerBackgroundColor', 'background.danger'),
+    color: DynamicColor('ButtonLightDangerColor', 'text.danger'),
+  },
   buttonSizeLarger: {
   buttonSizeLarger: {
     paddingVertical: DynamicSize('ButtonPaddingVerticalLarger', 25),
     paddingVertical: DynamicSize('ButtonPaddingVerticalLarger', 25),
     paddingHorizontal: DynamicSize('ButtonPaddingHorizontalLarger', 30),
     paddingHorizontal: DynamicSize('ButtonPaddingHorizontalLarger', 30),
@@ -306,35 +326,54 @@ const themeStyles = themeContext.useThemeStyles({
 
 
 //按钮样式生成
 //按钮样式生成
 const currentStyle = computed(() => {
 const currentStyle = computed(() => {
-  const colorStyle = selectStyleType<ViewStyle, ButtomType>(props.type, 'default', props.plain ? {
-    default: themeStyles.plainButtonDefault.value,
-    primary: themeStyles.plainButtonPrimary.value,
-    success: themeStyles.plainButtonSuccess.value,
-    warning: themeStyles.plainButtonWarning.value,
-    danger: themeStyles.plainButtonDanger.value,
-    custom: {
-      borderStyle: 'solid',
-      borderWidth: themeContext.resolveThemeSize(themeVars.ButtonBorderWidth),
-      borderColor: themeContext.resolveThemeColor(props.color),
-      color: themeContext.resolveThemeColor(props.color),
-    },
-    text: {
-      color: themeContext.resolveThemeColor(props.color),
-    },
-  } : {
-    default: themeStyles.buttonDefault.value,
-    primary: themeStyles.buttonPrimary.value,
-    success: themeStyles.buttonSuccess.value,
-    warning: themeStyles.buttonWarning.value,
-    danger: themeStyles.buttonDanger.value,
-    custom: {
-      backgroundColor: themeContext.resolveThemeColor(props.touchable ? props.color : props.disabledColor),
-      color: themeContext.resolveThemeColor(props.textColor),
-    },
-    text: {
-      color: themeContext.resolveThemeColor(props.textColor),
-    },
-  });
+  const colorStyle = selectStyleType<ViewStyle, ButtomType>(props.type, 'default', 
+    selectStyleType(props.scheme, 'default', {
+      default: {
+        default: themeStyles.buttonDefault.value,
+        primary: themeStyles.buttonPrimary.value,
+        success: themeStyles.buttonSuccess.value,
+        warning: themeStyles.buttonWarning.value,
+        danger: themeStyles.buttonDanger.value,
+        custom: {
+          backgroundColor: themeContext.resolveThemeColor(props.touchable ? props.color : props.disabledColor),
+          color: themeContext.resolveThemeColor(props.textColor),
+        },
+        text: {
+          color: themeContext.resolveThemeColor(props.textColor),
+        },
+      },
+      plain: {
+        default: themeStyles.plainButtonDefault.value,
+        primary: themeStyles.plainButtonPrimary.value,
+        success: themeStyles.plainButtonSuccess.value,
+        warning: themeStyles.plainButtonWarning.value,
+        danger: themeStyles.plainButtonDanger.value,
+        custom: {
+          borderStyle: 'solid',
+          borderWidth: themeContext.resolveThemeSize(themeVars.ButtonBorderWidth),
+          borderColor: themeContext.resolveThemeColor(props.color),
+          color: themeContext.resolveThemeColor(props.color),
+        },
+        text: {
+          color: themeContext.resolveThemeColor(props.color),
+        },
+      },
+      light: {
+        default: themeStyles.lightButtonDefault.value,
+        primary: themeStyles.lightButtonPrimary.value,
+        success: themeStyles.lightButtonSuccess.value,
+        warning: themeStyles.lightButtonWarning.value,
+        danger: themeStyles.lightButtonDanger.value,
+        custom: {
+          backgroundColor: themeContext.resolveThemeColor(props.touchable ? props.color : props.disabledColor),
+          color: themeContext.resolveThemeColor(props.textColor),
+        },
+        text: {
+          color: themeContext.resolveThemeColor(props.color),
+        },
+      },
+    })
+  );
 
 
   const speicalStyle : ViewStyle = {
   const speicalStyle : ViewStyle = {
     opacity: props.touchable ? 1 : themeVars.ButtonDisableOpacity,
     opacity: props.touchable ? 1 : themeVars.ButtonDisableOpacity,
@@ -377,6 +416,18 @@ const textColorFinal = computed(() => (
     themeContext.resolveThemeColor(props.pressedTextColor) :
     themeContext.resolveThemeColor(props.pressedTextColor) :
     themeContext.resolveThemeColor(props.textColor)
     themeContext.resolveThemeColor(props.textColor)
 ) || currentStyle.value.color);
 ) || currentStyle.value.color);
+const finalPressedColor = computed(() => {
+  if (props.type === 'custom')
+    return themeContext.resolveThemeColor(props.pressedColor);
+  return (themeContext.resolveThemeColor((
+    props.scheme === 'plain' 
+    || props.scheme === 'light' 
+    || props.type === 'text'
+  ) ? 
+    'pressed.notice' : 
+    'pressed.' + props.type))
+});
+
 
 
 </script>
 </script>
 
 

+ 3 - 1
src/components/basic/Image.vue

@@ -2,6 +2,7 @@
   <view 
   <view 
     class="nana-image-wrapper"
     class="nana-image-wrapper"
     :style="style"
     :style="style"
+    :class="innerClass"
     @click="handleClick"
     @click="handleClick"
   >
   >
     <image 
     <image 
@@ -22,7 +23,7 @@
     />
     />
     <view v-if="showFailed && isErrorState" class="inner-view error">
     <view v-if="showFailed && isErrorState" class="inner-view error">
       <!-- todo: failed -->
       <!-- todo: failed -->
-      <Text color="second" :text="src ? '暂无' : '失败'" />
+      <Text color="second" :text="src ? '暂无图片' : '加载失败'" />
     </view>
     </view>
     <view v-if="showLoading && isLoadState" class="inner-view loading">
     <view v-if="showLoading && isLoadState" class="inner-view loading">
       <ActivityIndicator
       <ActivityIndicator
@@ -98,6 +99,7 @@ export interface ImageProps {
    * 内部样式
    * 内部样式
    */
    */
   innerStyle?: object;
   innerStyle?: object;
+  innerClass?: string,
 }
 }
 
 
 defineOptions({
 defineOptions({

+ 1 - 1
src/components/dynamic/DynamicForm.vue

@@ -163,7 +163,7 @@ function initDefaultValuesToModel() {
         const oldValue = accessFormModel(currentKey, false, undefined);
         const oldValue = accessFormModel(currentKey, false, undefined);
         if (oldValue !== undefined && oldValue !== null)
         if (oldValue !== undefined && oldValue !== null)
           continue;
           continue;
-        accessFormModel(currentKey, true, item.defaultValue);
+        accessFormModel(currentKey, true, typeof item.defaultValue === 'function' ? item.defaultValue() : item.defaultValue);
       }
       }
       i++;
       i++;
     }
     }

+ 2 - 0
src/components/dynamic/wrappers/PickerIdField.vue

@@ -16,6 +16,8 @@ const props = defineProps<PickerIdFieldProps>();
 const emit = defineEmits(['update:modelValue']);
 const emit = defineEmits(['update:modelValue']);
 const loader = useDataLoader<PickerIdFieldOption[]>(async () => {
 const loader = useDataLoader<PickerIdFieldOption[]>(async () => {
   const res = await props.loadData();
   const res = await props.loadData();
+  if (res.length === 0)
+    return [];
   return ([{
   return ([{
     text: '请选择',
     text: '请选择',
     value: '',
     value: '',

+ 24 - 1
src/components/index.scss

@@ -20,7 +20,30 @@
 }
 }
 
 
 wx-action-sheet-item {
 wx-action-sheet-item {
-  padding: 0!important;
+  padding: 0 !important;
+}
+
+.remove-button-style {
+  margin: 0;
+  padding: 0;
+  border: none;
+  outline: none;
+  border-radius: 0;
+  background: none;
+  line-height: normal;
+  font-weight: normal;
+  font-size: inherit;
+  color: inherit;
+  text-align: inherit;
+  -webkit-tap-highlight-color: transparent;
+
+  &.button-hover {
+    background: none;
+    color: inherit;
+  }
+  &::after {
+    border: none;
+  }
 }
 }
 
 
 :root {
 :root {

+ 1 - 1
src/components/layout/FlexView.vue

@@ -34,7 +34,7 @@ export interface FlexProps {
   /**
   /**
    * 盒子定位
    * 盒子定位
    */ 
    */ 
-  position?: "absolute" | "relative",
+  position?: "absolute" | "relative" | 'fixed' | 'sticky',
   /**
   /**
    * 弹性盒子方向
    * 弹性盒子方向
    */
    */

+ 10 - 1
src/components/nav/Tabs.vue

@@ -48,6 +48,7 @@
               class="tab-item-text"
               class="tab-item-text"
               :style="{
               :style="{
                 color: tab.disabled ? themedDisableTextColor : (currentIndex == index ? themedActiveTextColor : themedTextColor),
                 color: tab.disabled ? themedDisableTextColor : (currentIndex == index ? themedActiveTextColor : themedTextColor),
+                whiteSpace: props.noWrap ? 'nowrap' : 'normal',
                 ...(currentIndex == index ? activeTextStyle : textStyle),
                 ...(currentIndex == index ? activeTextStyle : textStyle),
               }"
               }"
             >
             >
@@ -155,6 +156,11 @@ export interface TabsProps {
    */
    */
   autoScroll?: boolean,
   autoScroll?: boolean,
   /**
   /**
+   * 是否禁止标签文字换行
+   * @default true
+   */
+  noWrap?: boolean,
+  /**
    * 标签宽度,在 autoItemWidth 为 false 时有效。
    * 标签宽度,在 autoItemWidth 为 false 时有效。
    * 如果设置为-1,则根据文字宽度自动调整。
    * 如果设置为-1,则根据文字宽度自动调整。
    * @default 100 (rpx)
    * @default 100 (rpx)
@@ -210,7 +216,7 @@ export interface TabsProps {
   innerStyle?: ViewStyle,
   innerStyle?: ViewStyle,
 }
 }
 
 
-const emit = defineEmits([ 'update:currentIndex' ])
+const emit = defineEmits([ 'update:currentIndex', 'click' ])
 const props = withDefaults(defineProps<TabsProps>(), {
 const props = withDefaults(defineProps<TabsProps>(), {
   height: () => propGetThemeVar('TabsDefaultHeight', 90),
   height: () => propGetThemeVar('TabsDefaultHeight', 90),
   width: () => propGetThemeVar('TabsDefaultWidth', 750),
   width: () => propGetThemeVar('TabsDefaultWidth', 750),
@@ -218,6 +224,7 @@ const props = withDefaults(defineProps<TabsProps>(), {
   indicatorAnim: true,
   indicatorAnim: true,
   showIndicator: true,
   showIndicator: true,
   autoScroll: true,
   autoScroll: true,
+  noWrap: true,
   defaultItemWidth: () => propGetThemeVar('TabsDefaultItemWidth', 120),
   defaultItemWidth: () => propGetThemeVar('TabsDefaultItemWidth', 120),
   defaultIndicatorWidth: () => propGetThemeVar('TabsDefaultIndicatorWidth', 100),
   defaultIndicatorWidth: () => propGetThemeVar('TabsDefaultIndicatorWidth', 100),
   textColor: () => propGetThemeVar('TabsTextColor', 'text.title'),
   textColor: () => propGetThemeVar('TabsTextColor', 'text.title'),
@@ -323,6 +330,7 @@ onMounted(() => {
 })
 })
 
 
 function onTabClick(index: number) {
 function onTabClick(index: number) {
+  emit('click', props.tabs[index]);
   if (index !== props.currentIndex) {
   if (index !== props.currentIndex) {
     emit('update:currentIndex', index);
     emit('update:currentIndex', index);
   }
   }
@@ -337,6 +345,7 @@ function onTabClick(index: number) {
   flex-shrink: 0;
   flex-shrink: 0;
   flex-grow: 0;
   flex-grow: 0;
   height: auto;
   height: auto;
+  overflow: hidden;
 
 
   .tab-item {
   .tab-item {
     position: relative;
     position: relative;

+ 109 - 0
src/components/utils/Base64.ts

@@ -0,0 +1,109 @@
+export default {
+  encode: encode,
+  decode: decode,
+}
+
+var _keyStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
+
+/**
+ * 对字符串进行 Base64 编码
+ * @param input 字符串
+ */
+function encode(input : string) {
+    var output = "";
+    var chr1, chr2, chr3, enc1, enc2, enc3, enc4;
+    var i = 0;
+    input = _utf8_encode(input);
+    while (i < input.length) {
+        chr1 = input.charCodeAt(i++);
+        chr2 = input.charCodeAt(i++);
+        chr3 = input.charCodeAt(i++);
+        enc1 = chr1 >> 2;
+        enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
+        enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
+        enc4 = chr3 & 63;
+        if (isNaN(chr2)) {
+            enc3 = enc4 = 64
+        } else {
+            if (isNaN(chr3)) {
+                enc4 = 64
+            }
+        }
+        output = output + _keyStr.charAt(enc1) + _keyStr.charAt(enc2) + _keyStr.charAt(enc3) + _keyStr.charAt(enc4)
+    }
+    return output
+}
+/**
+ * 对 Base64字符串 进行 Base64 解码
+ * @param input Base64字符串
+ */
+function decode(input : string) {
+    var output = "";
+    var chr1, chr2, chr3;
+    var enc1, enc2, enc3, enc4;
+    var i = 0;
+    input = input.replace(/[^A-Za-z0-9\+\/\=]/g, "");
+    while (i < input.length) {
+        enc1 = _keyStr.indexOf(input.charAt(i++));
+        enc2 = _keyStr.indexOf(input.charAt(i++));
+        enc3 = _keyStr.indexOf(input.charAt(i++));
+        enc4 = _keyStr.indexOf(input.charAt(i++));
+        chr1 = (enc1 << 2) | (enc2 >> 4);
+        chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
+        chr3 = ((enc3 & 3) << 6) | enc4;
+        output = output + String.fromCharCode(chr1);
+        if (enc3 != 64) {
+            output = output + String.fromCharCode(chr2)
+        }
+        if (enc4 != 64) {
+            output = output + String.fromCharCode(chr3)
+        }
+    }
+    output = _utf8_decode(output);
+    return output
+}
+function _utf8_encode(string: string) {
+    string = string.replace(/\r\n/g, "\n");
+    var utftext = "";
+    for (var n = 0; n < string.length; n++) {
+        var c = string.charCodeAt(n);
+        if (c < 128) {
+            utftext += String.fromCharCode(c)
+        } else {
+            if ((c > 127) && (c < 2048)) {
+                utftext += String.fromCharCode((c >> 6) | 192);
+                utftext += String.fromCharCode((c & 63) | 128)
+            } else {
+                utftext += String.fromCharCode((c >> 12) | 224);
+                utftext += String.fromCharCode(((c >> 6) & 63) | 128);
+                utftext += String.fromCharCode((c & 63) | 128)
+            }
+        }
+    }
+    return utftext
+}
+function _utf8_decode(utftext: string) {
+    var string = "";
+    var i = 0;
+    var c1, c2, c3;
+    var c = c1 = c2 = 0;
+    while (i < utftext.length) {
+        c = utftext.charCodeAt(i);
+        if (c < 128) {
+            string += String.fromCharCode(c);
+            i++
+        } else {
+            if ((c > 191) && (c < 224)) {
+                c2 = utftext.charCodeAt(i + 1);
+                string += String.fromCharCode(((c & 31) << 6) | (c2 & 63));
+                i += 2
+            } else {
+                c2 = utftext.charCodeAt(i + 1);
+                c3 = utftext.charCodeAt(i + 2);
+                string += String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63));
+                i += 3
+            }
+        }
+    }
+    return string
+}

+ 547 - 0
src/components/utils/Calendar.ts

@@ -0,0 +1,547 @@
+/**
+* @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]
+*/
+export 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', '97bcf97c359801ec95f8c965cc920f', '97bd097bd07f595b0b6fc920fb0722',
+    '9778397bd097c36b0b6fc9210c8dc2', '9778397bd19801ec9210c9274c920e', '97b6b97bd19801ec95f8c965cc920f',
+    '97bd07f5307f595b0b0bc920fb0722', '7f0e397bd097c36b0b6fc9210c8dc2', '9778397bd097c36c9210c9274c920e',
+    '97b6b97bd19801ec95f8c965cc920f', '97bd07f5307f595b0b0bc920fb0722', '7f0e397bd097c36b0b6fc9210c8dc2',
+    '9778397bd097c36c9210c9274c91aa', '97b6b97bd19801ec9210c965cc920e', '97bd07f1487f595b0b0bc920fb0722',
+    '7f0e397bd097c36b0b6fc9210c8dc2', '9778397bd097c36b0b6fc9274c91aa', '97b6b97bd19801ec9210c965cc920e',
+    '97bcf7f1487f595b0b0bb0b6fb0722', '7f0e397bd097c35b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa',
+    '97b6b97bd19801ec9210c965cc920e', '97bcf7f1487f595b0b0bb0b6fb0722', '7f0e397bd097c35b0b6fc920fb0722',
+    '9778397bd097c36b0b6fc9274c91aa', '97b6b97bd19801ec9210c965cc920e', '97bcf7f1487f531b0b0bb0b6fb0722',
+    '7f0e397bd097c35b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa', '97b6b97bd19801ec9210c965cc920e',
+    '97bcf7f1487f531b0b0bb0b6fb0722', '7f0e397bd07f595b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa',
+    '97b6b97bd19801ec9210c9274c920e', '97bcf7f0e47f531b0b0bb0b6fb0722', '7f0e397bd07f595b0b0bc920fb0722',
+    '9778397bd097c36b0b6fc9210c91aa', '97b6b97bd197c36c9210c9274c920e', '97bcf7f0e47f531b0b0bb0b6fb0722',
+    '7f0e397bd07f595b0b0bc920fb0722', '9778397bd097c36b0b6fc9210c8dc2', '9778397bd097c36c9210c9274c920e',
+    '97b6b7f0e47f531b0723b0b6fb0722', '7f0e37f5307f595b0b0bc920fb0722', '7f0e397bd097c36b0b6fc9210c8dc2',
+    '9778397bd097c36b0b70c9274c91aa', '97b6b7f0e47f531b0723b0b6fb0721', '7f0e37f1487f595b0b0bb0b6fb0722',
+    '7f0e397bd097c35b0b6fc9210c8dc2', '9778397bd097c36b0b6fc9274c91aa', '97b6b7f0e47f531b0723b0b6fb0721',
+    '7f0e27f1487f595b0b0bb0b6fb0722', '7f0e397bd097c35b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa',
+    '97b6b7f0e47f531b0723b0b6fb0721', '7f0e27f1487f531b0b0bb0b6fb0722', '7f0e397bd097c35b0b6fc920fb0722',
+    '9778397bd097c36b0b6fc9274c91aa', '97b6b7f0e47f531b0723b0b6fb0721', '7f0e27f1487f531b0b0bb0b6fb0722',
+    '7f0e397bd097c35b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa', '97b6b7f0e47f531b0723b0b6fb0721',
+    '7f0e27f1487f531b0b0bb0b6fb0722', '7f0e397bd07f595b0b0bc920fb0722', '9778397bd097c36b0b6fc9274c91aa',
+    '97b6b7f0e47f531b0723b0787b0721', '7f0e27f0e47f531b0b0bb0b6fb0722', '7f0e397bd07f595b0b0bc920fb0722',
+    '9778397bd097c36b0b6fc9210c91aa', '97b6b7f0e47f149b0723b0787b0721', '7f0e27f0e47f531b0723b0b6fb0722',
+    '7f0e397bd07f595b0b0bc920fb0722', '9778397bd097c36b0b6fc9210c8dc2', '977837f0e37f149b0723b0787b0721',
+    '7f07e7f0e47f531b0723b0b6fb0722', '7f0e37f5307f595b0b0bc920fb0722', '7f0e397bd097c35b0b6fc9210c8dc2',
+    '977837f0e37f14998082b0787b0721', '7f07e7f0e47f531b0723b0b6fb0721', '7f0e37f1487f595b0b0bb0b6fb0722',
+    '7f0e397bd097c35b0b6fc9210c8dc2', '977837f0e37f14998082b0787b06bd', '7f07e7f0e47f531b0723b0b6fb0721',
+    '7f0e27f1487f531b0b0bb0b6fb0722', '7f0e397bd097c35b0b6fc920fb0722', '977837f0e37f14998082b0787b06bd',
+    '7f07e7f0e47f531b0723b0b6fb0721', '7f0e27f1487f531b0b0bb0b6fb0722', '7f0e397bd097c35b0b6fc920fb0722',
+    '977837f0e37f14998082b0787b06bd', '7f07e7f0e47f531b0723b0b6fb0721', '7f0e27f1487f531b0b0bb0b6fb0722',
+    '7f0e397bd07f595b0b0bc920fb0722', '977837f0e37f14998082b0787b06bd', '7f07e7f0e47f531b0723b0b6fb0721',
+    '7f0e27f1487f531b0b0bb0b6fb0722', '7f0e397bd07f595b0b0bc920fb0722', '977837f0e37f14998082b0787b06bd',
+    '7f07e7f0e47f149b0723b0787b0721', '7f0e27f0e47f531b0b0bb0b6fb0722', '7f0e397bd07f595b0b0bc920fb0722',
+    '977837f0e37f14998082b0723b06bd', '7f07e7f0e37f149b0723b0787b0721', '7f0e27f0e47f531b0723b0b6fb0722',
+    '7f0e397bd07f595b0b0bc920fb0722', '977837f0e37f14898082b0723b02d5', '7ec967f0e37f14998082b0787b0721',
+    '7f07e7f0e47f531b0723b0b6fb0722', '7f0e37f1487f595b0b0bb0b6fb0722', '7f0e37f0e37f14898082b0723b02d5',
+    '7ec967f0e37f14998082b0787b0721', '7f07e7f0e47f531b0723b0b6fb0722', '7f0e37f1487f531b0b0bb0b6fb0722',
+    '7f0e37f0e37f14898082b0723b02d5', '7ec967f0e37f14998082b0787b06bd', '7f07e7f0e47f531b0723b0b6fb0721',
+    '7f0e37f1487f531b0b0bb0b6fb0722', '7f0e37f0e37f14898082b072297c35', '7ec967f0e37f14998082b0787b06bd',
+    '7f07e7f0e47f531b0723b0b6fb0721', '7f0e27f1487f531b0b0bb0b6fb0722', '7f0e37f0e37f14898082b072297c35',
+    '7ec967f0e37f14998082b0787b06bd', '7f07e7f0e47f531b0723b0b6fb0721', '7f0e27f1487f531b0b0bb0b6fb0722',
+    '7f0e37f0e366aa89801eb072297c35', '7ec967f0e37f14998082b0787b06bd', '7f07e7f0e47f149b0723b0787b0721',
+    '7f0e27f1487f531b0b0bb0b6fb0722', '7f0e37f0e366aa89801eb072297c35', '7ec967f0e37f14998082b0723b06bd',
+    '7f07e7f0e47f149b0723b0787b0721', '7f0e27f0e47f531b0723b0b6fb0722', '7f0e37f0e366aa89801eb072297c35',
+    '7ec967f0e37f14998082b0723b06bd', '7f07e7f0e37f14998083b0787b0721', '7f0e27f0e47f531b0723b0b6fb0722',
+    '7f0e37f0e366aa89801eb072297c35', '7ec967f0e37f14898082b0723b02d5', '7f07e7f0e37f14998082b0787b0721',
+    '7f07e7f0e47f531b0723b0b6fb0722', '7f0e36665b66aa89801e9808297c35', '665f67f0e37f14898082b0723b02d5',
+    '7ec967f0e37f14998082b0787b0721', '7f07e7f0e47f531b0723b0b6fb0722', '7f0e36665b66a449801e9808297c35',
+    '665f67f0e37f14898082b0723b02d5', '7ec967f0e37f14998082b0787b06bd', '7f07e7f0e47f531b0723b0b6fb0721',
+    '7f0e36665b66a449801e9808297c35', '665f67f0e37f14898082b072297c35', '7ec967f0e37f14998082b0787b06bd',
+    '7f07e7f0e47f531b0723b0b6fb0721', '7f0e26665b66a449801e9808297c35', '665f67f0e37f1489801eb072297c35',
+    '7ec967f0e37f14998082b0787b06bd', '7f07e7f0e47f531b0723b0b6fb0721', '7f0e27f1487f531b0b0bb0b6fb0722'],
+
+  /**
+    * 数字转中文速查表
+    * @Array Of Property 
+    * @trans ['日','一','二','三','四','五','六','七','八','九','十']
+    * @return Cn string 
+    */
+  nStr1: ["\u65e5", "\u4e00", "\u4e8c", "\u4e09", "\u56db", "\u4e94", "\u516d", "\u4e03", "\u516b", "\u4e5d", "\u5341"],
+
+  /**
+    * 日期转农历称呼速查表
+    * @Array Of Property 
+    * @trans ['初','十','廿','卅']
+    * @return Cn string 
+    */
+  nStr2: ["\u521d", "\u5341", "\u5eff", "\u5345"],
+
+  /**
+    * 月份转农历称呼速查表
+    * @Array Of Property 
+    * @trans ['正','一','二','三','四','五','六','七','八','九','十','冬','腊']
+    * @return Cn string 
+    */
+  nStr3: ["\u6b63", "\u4e8c", "\u4e09", "\u56db", "\u4e94", "\u516d", "\u4e03", "\u516b", "\u4e5d", "\u5341", "\u51ac", "\u814a"],
+
+  /**
+    * 返回农历y年一整年的总天数
+    * @param lunar Year
+    * @return Number
+    * @eg:let count = calendar.lYearDays(1987) ;//count=387
+    */
+  lYearDays: function (y: number) {
+    let i, sum = 348;
+    for (i = 0x8000; i > 0x8; i >>= 1) { sum += (calendar.lunarInfo[y - 1900] & i) ? 1 : 0; }
+    return (sum + calendar.leapDays(y));
+  },
+
+  /**
+    * 返回农历y年闰月是哪个月;若y年没有闰月 则返回0
+    * @param lunar Year
+    * @return Number (0-12)
+    * @eg:let leapMonth = calendar.leapMonth(1987) ;//leapMonth=6
+    */
+  leapMonth: function (y: number) { //闰字编码 \u95f0
+    return (calendar.lunarInfo[y - 1900] & 0xf);
+  },
+
+  /**
+    * 返回农历y年闰月的天数 若该年没有闰月则返回0
+    * @param lunar Year
+    * @return Number (0、29、30)
+    * @eg:let leapMonthDay = calendar.leapDays(1987) ;//leapMonthDay=29
+    */
+  leapDays: function (y: number) {
+    if (calendar.leapMonth(y)) {
+      return ((calendar.lunarInfo[y - 1900] & 0x10000) ? 30 : 29);
+    }
+    return (0);
+  },
+
+  /**
+    * 返回农历y年m月(非闰月)的总天数,计算m为闰月时的天数请使用leapDays方法
+    * @param lunar Year
+    * @return Number (-1、29、30)
+    * @eg:let MonthDay = calendar.monthDays(1987,9) ;//MonthDay=29
+    */
+  monthDays: function (y: number, m: number) {
+    if (m > 12 || m < 1) { return -1 }//月份参数从1至12,参数错误返回-1
+    return ((calendar.lunarInfo[y - 1900] & (0x10000 >> m)) ? 30 : 29);
+  },
+
+  /**
+    * 返回公历(!)y年m月的天数
+    * @param solar Year
+    * @return Number (-1、28、29、30、31)
+    * @eg:let solarMonthDay = calendar.leapDays(1987) ;//solarMonthDay=30
+    */
+  solarDays: function (y: number, m: number) {
+    if (m > 12 || m < 1) { return -1 } //若参数错误 返回-1
+    let ms = m - 1;
+    if (ms == 1) { //2月份的闰平规律测算后确认返回28或29
+      return (((y % 4 == 0) && (y % 100 != 0) || (y % 400 == 0)) ? 29 : 28);
+    } else {
+      return (calendar.solarMonth[ms]);
+    }
+  },
+
+  /**
+   * 农历年份转换为干支纪年
+   * @param  lYear 农历年的年份数
+   * @return Cn string
+   */
+  toGanZhiYear: function (lYear: number) {
+    let ganKey = (lYear - 3) % 10;
+    let zhiKey = (lYear - 3) % 12;
+    if (ganKey == 0) ganKey = 10;//如果余数为0则为最后一个天干
+    if (zhiKey == 0) zhiKey = 12;//如果余数为0则为最后一个地支
+    return calendar.Gan[ganKey - 1] + calendar.Zhi[zhiKey - 1];
+
+  },
+
+  /**
+   * 公历月、日判断所属星座
+   * @param  cMonth [description]
+   * @param  cDay [description]
+   * @return Cn string
+   */
+  toAstro: function (cMonth: number, cDay: number) {
+    let s = "\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";
+    let arr = [20, 19, 21, 21, 21, 22, 23, 23, 23, 23, 22, 22];
+    return s.substr(cMonth * 2 - (cDay < arr[cMonth - 1] ? 2 : 0), 2) + "\u5ea7";//座
+  },
+
+  /**
+    * 传入offset偏移量返回干支
+    * @param offset 相对甲子的偏移量
+    * @return Cn string
+    */
+  toGanZhi: function (offset: number) {
+    return calendar.Gan[offset % 10] + calendar.Zhi[offset % 12];
+  },
+
+  /**
+    * 传入公历(!)y年获得该年第n个节气的公历日期
+    * @param y公历年(1900-2100);n二十四节气中的第几个节气(1~24);从n=1(小寒)算起 
+    * @return day Number
+    * @eg:let _24 = calendar.getTerm(1987,3) ;//_24=4;意即1987年2月4日立春
+    */
+  getTerm: function (y: number, n: number) {
+    if (y < 1900 || y > 2100) { return -1; }
+    if (n < 1 || n > 24) { return -1; }
+    let _table = calendar.sTermInfo[y - 1900];
+    let _info = [
+      parseInt('0x' + _table.substr(0, 5)).toString(),
+      parseInt('0x' + _table.substr(5, 5)).toString(),
+      parseInt('0x' + _table.substr(10, 5)).toString(),
+      parseInt('0x' + _table.substr(15, 5)).toString(),
+      parseInt('0x' + _table.substr(20, 5)).toString(),
+      parseInt('0x' + _table.substr(25, 5)).toString()
+    ];
+    let _calday = [
+      _info[0].substr(0, 1),
+      _info[0].substr(1, 2),
+      _info[0].substr(3, 1),
+      _info[0].substr(4, 2),
+
+      _info[1].substr(0, 1),
+      _info[1].substr(1, 2),
+      _info[1].substr(3, 1),
+      _info[1].substr(4, 2),
+
+      _info[2].substr(0, 1),
+      _info[2].substr(1, 2),
+      _info[2].substr(3, 1),
+      _info[2].substr(4, 2),
+
+      _info[3].substr(0, 1),
+      _info[3].substr(1, 2),
+      _info[3].substr(3, 1),
+      _info[3].substr(4, 2),
+
+      _info[4].substr(0, 1),
+      _info[4].substr(1, 2),
+      _info[4].substr(3, 1),
+      _info[4].substr(4, 2),
+
+      _info[5].substr(0, 1),
+      _info[5].substr(1, 2),
+      _info[5].substr(3, 1),
+      _info[5].substr(4, 2),
+    ];
+    return parseInt(_calday[n - 1]);
+  },
+
+  /**
+    * 传入农历数字月份返回汉语通俗表示法
+    * @param lunar month
+    * @return Cn string
+    * @eg:let cnMonth = calendar.toChinaMonth(12) ;//cnMonth='腊月'
+    */
+  toChinaMonth: function (m: number) { // 月 => \u6708
+    if (m > 12 || m < 1) { return -1 } //若参数错误 返回-1
+    let s = calendar.nStr3[m - 1];
+    s += "\u6708";//加上月字
+    return s;
+  },
+
+  /**
+    * 传入农历日期数字返回汉字表示法
+    * @param lunar day
+    * @return Cn string
+    * @eg:let cnDay = calendar.toChinaDay(21) ;//cnMonth='廿一'
+    */
+  toChinaDay: function (d: number) { //日 => \u65e5
+    let s;
+    switch (d) {
+      case 10:
+        s = '\u521d\u5341'; break;
+      case 20:
+        s = '\u4e8c\u5341'; break;
+        break;
+      case 30:
+        s = '\u4e09\u5341'; break;
+        break;
+      default:
+        s = calendar.nStr2[Math.floor(d / 10)];
+        s += calendar.nStr1[d % 10];
+    }
+    return (s);
+  },
+
+  /**
+    * 年份转生肖[!仅能大致转换] => 精确划分生肖分界线是“立春”
+    * @param y year
+    * @return Cn string
+    * @eg:let animal = calendar.getAnimal(1987) ;//animal='兔'
+    */
+  getAnimal: function (y: number) {
+    return calendar.Animals[(y - 4) % 12]
+  },
+
+  /**
+    * 传入阳历年月日获得详细的公历、农历object信息 <=>JSON
+    * @param y  solar year
+    * @param m  solar month
+    * @param d  solar day
+    * @return JSON object
+    * @eg:console.log(calendar.solar2lunar(1987,11,01));
+    */
+  solar2lunar: function (y: number, m: number, d: number) { //参数区间1900.1.31~2100.12.31
+    let objDate : Date;
+    if (
+      y < 1900 || y > 2100 || //年份限定、上限
+      (y == 1900 && m == 1 && d < 31) //下限
+    )
+      throw new Error('年份超出范围');
+    objDate = new Date(y, m - 1, d)
+
+    let i, leap = 0, temp = 0;
+    //修正ymd参数
+    y = objDate.getFullYear(), m = objDate.getMonth() + 1, d = objDate.getDate();
+    let offset = (Date.UTC(objDate.getFullYear(), objDate.getMonth(), objDate.getDate()) - Date.UTC(1900, 0, 31)) / 86400000;
+    for (i = 1900; i < 2101 && offset > 0; i++) { temp = calendar.lYearDays(i); offset -= temp; }
+    if (offset < 0) { offset += temp; i--; }
+
+    //是否今天
+    let isTodayObj = new Date(), isToday = false;
+    if (isTodayObj.getFullYear() == y && isTodayObj.getMonth() + 1 == m && isTodayObj.getDate() == d) {
+      isToday = true;
+    }
+    //星期几
+    let nWeek = objDate.getDay(), cWeek = calendar.nStr1[nWeek];
+    if (nWeek == 0) { nWeek = 7; }//数字表示周几顺应天朝周一开始的惯例
+    //农历年
+    let year = i;
+
+    leap = calendar.leapMonth(i); //闰哪个月
+    let isLeap = false;
+
+    //效验闰月
+    for (i = 1; i < 13 && offset > 0; i++) {
+      //闰月
+      if (leap > 0 && i == (leap + 1) && isLeap == false) {
+        --i;
+        isLeap = true; temp = calendar.leapDays(year); //计算农历闰月天数
+      }
+      else {
+        temp = calendar.monthDays(year, i);//计算农历普通月天数
+      }
+      //解除闰月
+      if (isLeap == true && i == (leap + 1)) { isLeap = false; }
+      offset -= temp;
+    }
+
+    if (offset == 0 && leap > 0 && i == leap + 1)
+      if (isLeap) {
+        isLeap = false;
+      } else {
+        isLeap = true; --i;
+      }
+    if (offset < 0) { offset += temp; --i; }
+    //农历月
+    let month = i;
+    //农历日
+    let day = offset + 1;
+
+    //天干地支处理
+    let sm = m - 1;
+    let gzY = calendar.toGanZhiYear(year);
+
+    //月柱 1900年1月小寒以前为 丙子月(60进制12)
+    let firstNode = calendar.getTerm(year, (m * 2 - 1));//返回当月「节」为几日开始
+    let secondNode = calendar.getTerm(year, (m * 2));//返回当月「节」为几日开始
+
+    //依据12节气修正干支月
+    let gzM = calendar.toGanZhi((y - 1900) * 12 + m + 11);
+    if (d >= firstNode) {
+      gzM = calendar.toGanZhi((y - 1900) * 12 + m + 12);
+    }
+
+    //传入的日期的节气与否
+    let isTerm = false;
+    let Term = null;
+    if (firstNode == d) {
+      isTerm = true;
+      Term = calendar.solarTerm[m * 2 - 2];
+    }
+    if (secondNode == d) {
+      isTerm = true;
+      Term = calendar.solarTerm[m * 2 - 1];
+    }
+    //日柱 当月一日与 1900/1/1 相差天数
+    let dayCyclical = Date.UTC(y, sm, 1, 0, 0, 0, 0) / 86400000 + 25567 + 10;
+    let gzD = calendar.toGanZhi(dayCyclical + d - 1);
+    //该日期所属的星座
+    let astro = calendar.toAstro(m, d);
+
+    return { 'lYear': year, 'lMonth': month, 'lDay': day, 'Animal': calendar.getAnimal(year), 'IMonthCn': (isLeap ? "\u95f0" : '') + calendar.toChinaMonth(month), 'IDayCn': calendar.toChinaDay(day), 'cYear': y, 'cMonth': m, 'cDay': d, 'gzYear': gzY, 'gzMonth': gzM, 'gzDay': gzD, 'isToday': isToday, 'isLeap': isLeap, 'nWeek': nWeek, 'ncWeek': "\u661f\u671f" + cWeek, 'isTerm': isTerm, 'Term': Term, 'astro': astro };
+  },
+
+  /**
+    * 传入农历年月日以及传入的月份是否闰月获得详细的公历、农历object信息 <=>JSON
+    * @param y  lunar year
+    * @param m  lunar month
+    * @param d  lunar day
+    * @param isLeapMonth  lunar month is leap or not.[如果是农历闰月第四个参数赋值true即可]
+    * @return JSON object
+    * @eg:console.log(calendar.lunar2solar(1987,9,10));
+    */
+  lunar2solar: function (y: number, m: number, d: number, isLeapMonth: boolean = false) {   //参数区间1900.1.31~2100.12.1
+    let leapOffset = 0;
+    let leapMonth = calendar.leapMonth(y);
+    let leapDay = calendar.leapDays(y);
+    if (isLeapMonth && (leapMonth != m)) { return -1; }//传参要求计算该闰月公历 但该年得出的闰月与传参的月份并不同
+    if (y == 2100 && m == 12 && d > 1 || y == 1900 && m == 1 && d < 31) { return -1; }//超出了最大极限值 
+    let day = calendar.monthDays(y, m);
+    let _day = day;
+    //bugFix 2016-9-25 
+    //if month is leap, _day use leapDays method 
+    if (isLeapMonth) {
+      _day = calendar.leapDays(y);
+    }
+    if (y < 1900 || y > 2100 || d > _day) { return -1; }//参数合法性效验
+
+    //计算农历的时间差
+    let offset = 0;
+    for (let i = 1900; i < y; i++) {
+      offset += calendar.lYearDays(i);
+    }
+    let leap = 0, isAdd = false;
+    for (let i = 1; i < m; i++) {
+      leap = calendar.leapMonth(y);
+      if (!isAdd) {//处理闰月
+        if (leap <= i && leap > 0) {
+          offset += calendar.leapDays(y); isAdd = true;
+        }
+      }
+      offset += calendar.monthDays(y, i);
+    }
+    //转换闰月农历 需补充该年闰月的前一个月的时差
+    if (isLeapMonth) { offset += day; }
+    //1900年农历正月一日的公历时间为1900年1月30日0时0分0秒(该时间也是本农历的最开始起始点)
+    let stmap = Date.UTC(1900, 1, 30, 0, 0, 0);
+    let calObj = new Date((offset + d - 31) * 86400000 + stmap);
+    let cY = calObj.getUTCFullYear();
+    let cM = calObj.getUTCMonth() + 1;
+    let cD = calObj.getUTCDate();
+
+    return calendar.solar2lunar(cY, cM, cD);
+  }
+};
+
+/**
+ * 获取今日的节日
+ * @return {string} 今日的节日
+ */
+export function getFestival(calendar: Date){
+  let str = '';
+  const month = calendar.getMonth();
+  const date = calendar.getDate();
+  
+  if ((month == 0) && (date == 1)) str = "元旦";
+  if ((month == 2) && (date == 12)) str = "植树节";
+  if ((month == 3) && (date == 5)) str = "清明节";
+  if ((month == 4) && (date == 1)) str = "劳动节";
+  if ((month == 4) && (date == 4)) str = "青年节";
+  if ((month == 5) && (date == 1)) str = "儿童节";
+  if ((month == 7) && (date == 1)) str = "建军节"
+  if ((month == 9) && (date == 1)) str = "国庆节";
+  if ((month == 11) && (date == 24)) str = "平安夜";
+  if ((month == 11) && (date == 25)) str = "圣诞节";
+
+  return str;
+}
+
+export default calendar

+ 100 - 0
src/components/utils/Timer.ts

@@ -0,0 +1,100 @@
+/**
+ * Copyrigt (C) 2022 imengyu.top
+ */
+
+export class SimpleDelay<T = any> {
+
+  private executor: (data: T) => void;
+  private interval: number;
+  private data: T;
+  private timer = 0;
+
+  public constructor(data: T, executor: (d: T) => void, interval: number) {
+    this.executor = executor;
+    this.interval = interval;
+    this.data = data;
+  }
+
+  public start() {
+    if (this.timer)
+      clearTimeout(this.timer);
+    this.timer = setTimeout(() => 
+      this.executor(this.data), 
+      this.interval
+    ) as unknown as number;
+    return this;
+  }
+  public stop() {
+    if (this.timer) {
+      clearTimeout(this.timer);
+      this.timer = 0;
+    }
+    return this;
+  }
+
+}
+export class SimpleTimer<T = any> {
+
+  private executor: (data: T) => void;
+  private interval: number;
+  private data: T;
+  private timer = 0;
+  private executorTimeLimitWarnCount = 0;
+  private lasExecuteTime = 0;
+
+  public constructor(data: T, executor: (d: T) => void, interval: number) {
+    this.executor = executor;
+    this.interval = interval;
+    this.data = data;
+  }
+
+  public start() {
+    if (this.timer)
+      clearInterval(this.timer);
+    this.timer = setInterval(() => {
+
+      const startTime = new Date();
+      this.executor(this.data);
+
+      //计算执行时间
+      const executeTime = new Date().getTime() - startTime.getTime();
+
+      //如果与上一次触发时间过近,则取消
+      const lastInterval = startTime.getTime() - this.lasExecuteTime
+      if (this.lasExecuteTime > 0 && lastInterval < (this.interval - 200)) {
+        console.warn('The execution time of the timer is too fast, lastInterval: ' + lastInterval, 'executor:', this.executor);
+        return;
+      }
+
+      this.lasExecuteTime = startTime.getTime();
+
+      //如果执行时间过长,则警告
+      if (executeTime > this.interval * 0.5 && this.executorTimeLimitWarnCount < 1) {
+        console.warn('The execution time of the timer is too long, exceeding the timing for ' + this.interval + 'ms. executor:', this.executor, 'count:', this.executorTimeLimitWarnCount);
+        this.executorTimeLimitWarnCount++;
+      } else
+        this.executorTimeLimitWarnCount--;
+
+    }, this.interval) as unknown as number;
+    return this; 
+  }
+  public stop() {
+    if (this.timer) {
+      clearInterval(this.timer);
+      this.timer = 0;
+    }
+    return this; 
+  }
+
+}
+
+export function exactDelay(cb: () => void, ms: number) {
+  const startTime = new Date().getTime();
+
+  return setTimeout(() => {
+    const execTime = new Date().getTime() - startTime;
+    if (execTime > (ms + (ms < 500 ? 100 : 1000)))
+      console.warn('warn: exactDelay slow! execTime/ms:', execTime, '/', ms);
+    cb();
+  }, ms);
+}