sard-uniapp 1.17.1 → 1.18.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/components/button/button.vue +1 -1
  3. package/components/calendar-month/calendar-month.vue +8 -8
  4. package/components/config/index.d.ts +1 -0
  5. package/components/config/index.js +1 -0
  6. package/components/datetime-picker/common.d.ts +7 -3
  7. package/components/datetime-picker/common.js +181 -9
  8. package/components/datetime-picker/datetime-picker.d.ts +1 -0
  9. package/components/datetime-picker/datetime-picker.vue +116 -20
  10. package/components/datetime-picker-input/common.d.ts +1 -0
  11. package/components/datetime-picker-input/datetime-picker-input.d.ts +1 -0
  12. package/components/datetime-picker-input/datetime-picker-input.vue +2 -0
  13. package/components/datetime-picker-popout/common.d.ts +1 -0
  14. package/components/datetime-picker-popout/datetime-picker-popout.d.ts +1 -0
  15. package/components/datetime-picker-popout/datetime-picker-popout.vue +2 -0
  16. package/components/datetime-range-picker/datetime-range-picker.vue +1 -0
  17. package/components/datetime-range-picker-input/datetime-range-picker-input.vue +1 -0
  18. package/components/datetime-range-picker-popout/datetime-range-picker-popout.vue +1 -0
  19. package/components/dropdown/common.d.ts +2 -1
  20. package/components/dropdown-item/dropdown-item.d.ts +5 -7
  21. package/components/dropdown-item/dropdown-item.vue +3 -1
  22. package/components/empty/empty.vue +1 -1
  23. package/components/fab/fab.vue +1 -1
  24. package/components/grid-item/grid-item.vue +1 -1
  25. package/components/icon/icon.vue +9 -4
  26. package/components/list-item/list-item.vue +1 -1
  27. package/components/menu-item/menu-item.vue +1 -1
  28. package/components/navbar-item/navbar-item.vue +1 -1
  29. package/components/rate/rate.vue +2 -2
  30. package/components/result/result.vue +1 -1
  31. package/components/share-sheet/share-sheet.vue +1 -1
  32. package/components/step/step.vue +1 -1
  33. package/components/tabbar-item/tabbar-item.vue +1 -5
  34. package/components/timeline-item/timeline-item.vue +1 -5
  35. package/package.json +1 -1
  36. package/utils/date.d.ts +33 -3
  37. package/utils/date.js +209 -11
package/CHANGELOG.md CHANGED
@@ -1,3 +1,14 @@
1
+ # [1.18.0](https://github.com/sutras/sard-uniapp/compare/v1.17.1...v1.18.0) (2025-06-06)
2
+
3
+
4
+ ### Features
5
+
6
+ * datetime-picker 组件新增农历类型 ([7b0f305](https://github.com/sutras/sard-uniapp/commit/7b0f305f57ff43f322ef4a398f95415a5a9e01e0))
7
+ * dropdown-item 组件新增显隐动画相关事件 ([83e8c52](https://github.com/sutras/sard-uniapp/commit/83e8c52e996bdc4901ea446b30d9273d0e5e956d))
8
+ * icon支持冒号分隔符名称属性 ([7ffcd90](https://github.com/sutras/sard-uniapp/commit/7ffcd9056e006cc353aadf5cb2831edde9515719))
9
+
10
+
11
+
1
12
  ## [1.17.1](https://github.com/sutras/sard-uniapp/compare/v1.17.0...v1.17.1) (2025-05-30)
2
13
 
3
14
 
@@ -42,7 +42,7 @@
42
42
  <sar-icon
43
43
  v-else-if="icon"
44
44
  :name="icon"
45
- :family="iconFamily || 'sari'"
45
+ :family="iconFamily"
46
46
  :size="iconSize"
47
47
  />
48
48
  </view>
@@ -34,9 +34,9 @@
34
34
  import { defineComponent as _defineComponent } from "vue";
35
35
  import { computed } from "vue";
36
36
  import {
37
- getDaysInMonth,
38
- getOffsetDaysFromFirstDay,
39
- getDayOnFirstOfMonth,
37
+ getMonthDays,
38
+ getOffsetDaysFromMonthStart,
39
+ getWeekOfMonthStart,
40
40
  getPadStartDays,
41
41
  getPadEndDays,
42
42
  toDateNumber,
@@ -72,11 +72,11 @@ export default _defineComponent({
72
72
  const props = __props;
73
73
  const emit = __emit;
74
74
  const days = computed(() => {
75
- return getDaysInMonth(props.year, props.month);
75
+ return getMonthDays(props.year, props.month + 1);
76
76
  });
77
77
  const offsetDays = computed(() => {
78
- return getOffsetDaysFromFirstDay(
79
- getDayOnFirstOfMonth(props.year, props.month),
78
+ return getOffsetDaysFromMonthStart(
79
+ getWeekOfMonthStart(props.year, props.month + 1),
80
80
  props.weekStartsOn
81
81
  );
82
82
  });
@@ -87,12 +87,12 @@ export default _defineComponent({
87
87
  }
88
88
  const padStartDays = getPadStartDays(
89
89
  props.year,
90
- props.month,
90
+ props.month + 1,
91
91
  offsetDays.value
92
92
  );
93
93
  const padEndDays = getPadEndDays(
94
94
  props.year,
95
- props.month,
95
+ props.month + 1,
96
96
  42 - offsetDays.value - days.value
97
97
  );
98
98
  return [...padStartDays, ...currentDays, ...padEndDays];
@@ -115,6 +115,7 @@ export declare const defaultConfig: {
115
115
  };
116
116
  datetimePicker: {
117
117
  type: string;
118
+ calendar: "solar";
118
119
  };
119
120
  datetimePickerPopout: {
120
121
  validateEvent: boolean;
@@ -77,6 +77,7 @@ export const defaultConfig = {
77
77
  },
78
78
  datetimePicker: {
79
79
  type: 'yMd',
80
+ calendar: 'solar',
80
81
  },
81
82
  datetimePickerPopout: {
82
83
  validateEvent: true,
@@ -4,6 +4,7 @@ export interface DatetimePickerProps {
4
4
  rootStyle?: StyleValue;
5
5
  rootClass?: string;
6
6
  type?: string;
7
+ calendar?: 'solar' | 'lunar';
7
8
  min?: Date | string;
8
9
  max?: Date | string;
9
10
  modelValue?: Date | string;
@@ -13,6 +14,7 @@ export interface DatetimePickerProps {
13
14
  }
14
15
  export declare const defaultDatetimePickerProps: {
15
16
  type: string;
17
+ calendar: "solar";
16
18
  };
17
19
  export interface DatetimePickerSlots extends PickerSlots {
18
20
  }
@@ -42,8 +44,10 @@ interface Strategies {
42
44
  }
43
45
  export declare const strategies: Strategies;
44
46
  export declare const letterArray: DatetimeLetter[];
45
- export declare function getBoundaryValue(isMax: boolean, endDate: Date, currentDate: Date): number[];
46
- export declare function correctDate(date: DateEvery, minDate: Date, maxDate: Date): void;
47
- export declare const getColumnData: (count: number, start: number, length: number, letter: DatetimeLetter, currentDate: Date, translate: (...args: any[]) => any, filter?: (letter: DatetimeLetter, value: number, date: Date, index: number) => boolean, formatter?: (letter: DatetimeLetter, option: DatetimeColumnOption, date: Date, index: number) => string | void | undefined) => DatetimeColumnOption[];
47
+ export declare function getBoundaryValue(calendar: 'solar' | 'lunar', isMax: boolean, endDate: Date, currentDate: Date): number[];
48
+ export declare function correctSolarDate(date: DateEvery, minDate: Date, maxDate: Date): void;
49
+ export declare function correctLunarDate(date: DateEvery, minDate: Date, maxDate: Date): void;
50
+ export declare function correctDate(calendar: 'solar' | 'lunar', date: DateEvery, minDate: Date, maxDate: Date): void;
51
+ export declare const getColumnData: (calendar: "solar" | "lunar", min: number, max: number, length: number, letter: DatetimeLetter, currentDate: Date, translate: (...args: any[]) => any, filter?: (letter: DatetimeLetter, value: number, date: Date, index: number) => boolean, formatter?: (letter: DatetimeLetter, option: DatetimeColumnOption, date: Date, index: number) => string | void | undefined) => DatetimeColumnOption[];
48
52
  export declare function getInitialValue(minDate: Date, maxDate: Date): Date;
49
53
  export {};
@@ -1,4 +1,4 @@
1
- import { getDaysInMonth, minmax } from '../../utils';
1
+ import { getLunarDayName, getLunarLeapMonth, getLunarLeapMonthDays, getLunarMonthDays, getLunarMonthName, getMonthDays, minmax, solarToLunar, } from '../../utils';
2
2
  import { defaultConfig } from '../config';
3
3
  export const defaultDatetimePickerProps = defaultConfig.datetimePicker;
4
4
  export const getMinDate = () => {
@@ -16,7 +16,7 @@ export const strategies = {
16
16
  s: [5, 2, 0, 59, (d) => d.getSeconds(), (d, val) => d.setSeconds(val)],
17
17
  };
18
18
  export const letterArray = ['y', 'M', 'd', 'h', 'm', 's'];
19
- export function getBoundaryValue(isMax, endDate, currentDate) {
19
+ function getSolarBoundaryValue(isMax, endDate, currentDate) {
20
20
  const currEvery = [endDate.getFullYear()];
21
21
  const minOrMaxIndex = isMax ? 3 : 2;
22
22
  let aside = true;
@@ -25,7 +25,7 @@ export function getBoundaryValue(isMax, endDate, currentDate) {
25
25
  const strategy = strategies[letter];
26
26
  let minOrMax = strategy[minOrMaxIndex];
27
27
  if (isMax && letter === 'd') {
28
- minOrMax = getDaysInMonth(currentDate.getFullYear(), currentDate.getMonth());
28
+ minOrMax = getMonthDays(currentDate.getFullYear(), currentDate.getMonth() + 1);
29
29
  }
30
30
  aside = aside && currEvery[index] === prevGetter(currentDate);
31
31
  currEvery[index + 1] = aside ? strategy[4](endDate) : minOrMax;
@@ -33,7 +33,56 @@ export function getBoundaryValue(isMax, endDate, currentDate) {
33
33
  });
34
34
  return currEvery;
35
35
  }
36
- export function correctDate(date, minDate, maxDate) {
36
+ function getLunarBoundaryValue(isMax, endDate, currentDate) {
37
+ const lunarDate = solarToLunar(currentDate.getFullYear(), currentDate.getMonth() + 1, currentDate.getDate());
38
+ const lunarEndDate = solarToLunar(endDate.getFullYear(), endDate.getMonth() + 1, endDate.getDate());
39
+ const currEvery = [lunarEndDate.year];
40
+ const minOrMaxIndex = isMax ? 3 : 2;
41
+ let aside = true;
42
+ let prevGetter = strategies.y[4];
43
+ letterArray.slice(1).forEach((letter, index) => {
44
+ const prevValue = currEvery[index];
45
+ const strategy = strategies[letter];
46
+ const currGetter = strategy[4];
47
+ let minOrMax = strategy[minOrMaxIndex];
48
+ if (isMax && letter === 'd') {
49
+ minOrMax =
50
+ lunarDate.month < 0
51
+ ? getLunarLeapMonthDays(lunarDate.year)
52
+ : getLunarMonthDays(lunarDate.year, lunarDate.month);
53
+ }
54
+ let asideValue = 0;
55
+ if (aside) {
56
+ if (letter === 'M') {
57
+ aside = lunarDate.year === prevValue;
58
+ if (aside) {
59
+ asideValue = lunarEndDate.month;
60
+ }
61
+ }
62
+ else if (letter === 'd') {
63
+ aside = lunarDate.month === prevValue;
64
+ if (aside) {
65
+ asideValue = lunarEndDate.day;
66
+ }
67
+ }
68
+ else {
69
+ aside = prevGetter(currentDate) === prevValue;
70
+ if (aside) {
71
+ asideValue = currGetter(currentDate);
72
+ }
73
+ }
74
+ }
75
+ currEvery[index + 1] = aside ? asideValue : minOrMax;
76
+ prevGetter = currGetter;
77
+ });
78
+ return currEvery;
79
+ }
80
+ export function getBoundaryValue(calendar, isMax, endDate, currentDate) {
81
+ return calendar === 'solar'
82
+ ? getSolarBoundaryValue(isMax, endDate, currentDate)
83
+ : getLunarBoundaryValue(isMax, endDate, currentDate);
84
+ }
85
+ export function correctSolarDate(date, minDate, maxDate) {
37
86
  let minAside = true;
38
87
  let maxAside = true;
39
88
  let prevGetter = strategies.y[4];
@@ -42,7 +91,7 @@ export function correctDate(date, minDate, maxDate) {
42
91
  let minValue = strategy[2];
43
92
  let maxValue = strategy[3];
44
93
  if (letter === 'd') {
45
- maxValue = getDaysInMonth(date[0], date[1] - 1);
94
+ maxValue = getMonthDays(date[0], date[1]);
46
95
  }
47
96
  const currGetter = strategy[4];
48
97
  if ((minAside = minAside && prevGetter(minDate) === date[index])) {
@@ -55,18 +104,141 @@ export function correctDate(date, minDate, maxDate) {
55
104
  prevGetter = currGetter;
56
105
  });
57
106
  }
58
- export const getColumnData = (count, start, length, letter, currentDate, translate, filter, formatter) => {
59
- let column = Array(count)
107
+ export function correctLunarDate(date, minDate, maxDate) {
108
+ const lunarMinDate = solarToLunar(minDate.getFullYear(), minDate.getMonth() + 1, minDate.getDate());
109
+ const lunarMaxDate = solarToLunar(maxDate.getFullYear(), maxDate.getMonth() + 1, maxDate.getDate());
110
+ const range = [
111
+ {
112
+ value: 0,
113
+ edge: true,
114
+ date: minDate,
115
+ lunarDate: lunarMinDate,
116
+ },
117
+ {
118
+ value: 0,
119
+ edge: true,
120
+ date: maxDate,
121
+ lunarDate: lunarMaxDate,
122
+ },
123
+ ];
124
+ let prevGetter = strategies.y[4];
125
+ letterArray.slice(1).forEach((letter, index) => {
126
+ const prevValue = date[index];
127
+ const currIndex = index + 1;
128
+ const currValue = date[currIndex];
129
+ const strategy = strategies[letter];
130
+ range[0].value = strategy[2];
131
+ range[1].value = strategy[3];
132
+ if (letter === 'd') {
133
+ range[1].value =
134
+ date[1] < 0
135
+ ? getLunarLeapMonthDays(date[0])
136
+ : getLunarMonthDays(date[0], date[1]);
137
+ }
138
+ let currGetter = strategy[4];
139
+ range.forEach((point) => {
140
+ if (point.edge) {
141
+ if (letter === 'M') {
142
+ point.edge = point.lunarDate.year === prevValue;
143
+ if (point.edge) {
144
+ point.value = point.lunarDate.month;
145
+ }
146
+ }
147
+ else if (letter === 'd') {
148
+ point.edge = point.lunarDate.month === prevValue;
149
+ if (point.edge) {
150
+ point.value = point.lunarDate.day;
151
+ }
152
+ }
153
+ else {
154
+ point.edge = prevGetter(point.date) === prevValue;
155
+ if (point.edge) {
156
+ point.value = currGetter(point.date);
157
+ }
158
+ }
159
+ }
160
+ });
161
+ if (letter === 'M') {
162
+ let min = range[0].value;
163
+ let max = range[1].value;
164
+ let value = currValue;
165
+ if (min < 0) {
166
+ min = Math.abs(min) + 0.5;
167
+ }
168
+ else if (max < 0) {
169
+ max = Math.abs(max) + 0.5;
170
+ }
171
+ if (value < 0) {
172
+ value = Math.abs(value) + 0.5;
173
+ }
174
+ value = minmax(value, min, max);
175
+ date[currIndex] = value % 1 === 0.5 ? ~~value * -1 : value;
176
+ }
177
+ else {
178
+ date[currIndex] = minmax(currValue, range[0].value, range[1].value);
179
+ }
180
+ if (letter === 'd') {
181
+ currGetter = (date) => {
182
+ return date === minDate ? lunarMinDate.day : lunarMaxDate.day;
183
+ };
184
+ }
185
+ prevGetter = currGetter;
186
+ });
187
+ }
188
+ export function correctDate(calendar, date, minDate, maxDate) {
189
+ if (calendar === 'solar') {
190
+ correctSolarDate(date, minDate, maxDate);
191
+ }
192
+ else {
193
+ correctLunarDate(date, minDate, maxDate);
194
+ }
195
+ }
196
+ export const getColumnData = (calendar, min, max, length, letter, currentDate, translate, filter, formatter) => {
197
+ const lunarDate = solarToLunar(currentDate.getFullYear(), currentDate.getMonth() + 1, currentDate.getDate());
198
+ let column = Array(Math.abs(max) - Math.abs(min) + 1)
60
199
  .fill(0)
61
200
  .map((_, i) => ({
62
- value: i + start,
201
+ value: i + Math.abs(min),
63
202
  }));
203
+ if (calendar === 'lunar' && letter === 'M') {
204
+ if (min < 0) {
205
+ column[0].value = min;
206
+ }
207
+ else if (max < 0) {
208
+ column.push({
209
+ value: max,
210
+ });
211
+ }
212
+ else {
213
+ const leapMonth = getLunarLeapMonth(lunarDate.year);
214
+ if (leapMonth > 0 && leapMonth > min && leapMonth < max) {
215
+ column.splice(column.findIndex((item) => item.value === leapMonth) + 1, 0, {
216
+ value: -leapMonth,
217
+ });
218
+ }
219
+ }
220
+ }
64
221
  if (filter) {
65
222
  column = column.filter((option, i) => filter(letter, option.value, currentDate, i));
66
223
  }
67
224
  column.forEach((option) => {
68
225
  option.zerofill = String(option.value).padStart(length, '0');
69
- option.label = option.zerofill + translate(letter);
226
+ if (calendar === 'solar') {
227
+ option.label = option.zerofill + translate(letter);
228
+ }
229
+ else {
230
+ switch (letter) {
231
+ case 'M':
232
+ option.label = getLunarMonthName(Math.abs(option.value), option.value < 0);
233
+ break;
234
+ case 'd':
235
+ option.label = getLunarDayName(option.value);
236
+ break;
237
+ default:
238
+ option.label = option.zerofill + translate(letter);
239
+ break;
240
+ }
241
+ }
70
242
  });
71
243
  if (formatter) {
72
244
  column.forEach((option, i) => {
@@ -8,6 +8,7 @@ declare const __VLS_component: import("vue").DefineComponent<DatetimePickerProps
8
8
  onChange?: ((date: string | Date) => any) | undefined;
9
9
  }>, {
10
10
  type: string;
11
+ calendar: "solar" | "lunar";
11
12
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
12
13
  declare const _default: __VLS_WithTemplateSlots<typeof __VLS_component, ReturnType<typeof __VLS_template>>;
13
14
  export default _default;
@@ -11,7 +11,14 @@
11
11
  <script>
12
12
  import { mergeDefaults as _mergeDefaults, defineComponent as _defineComponent } from "vue";
13
13
  import { computed, ref, watch } from "vue";
14
- import { classNames, formatDate, stringifyStyle, toDate } from "../../utils";
14
+ import {
15
+ classNames,
16
+ formatDate,
17
+ lunarToSolar,
18
+ solarToLunar,
19
+ stringifyStyle,
20
+ toDate
21
+ } from "../../utils";
15
22
  import SarPicker from "../picker/picker.vue";
16
23
  import {
17
24
  correctDate,
@@ -39,6 +46,7 @@ export default _defineComponent({
39
46
  rootStyle: { type: [Boolean, null, String, Object, Array], required: false, skipCheck: true },
40
47
  rootClass: { type: String, required: false },
41
48
  type: { type: String, required: false },
49
+ calendar: { type: String, required: false },
42
50
  min: { type: [Date, String], required: false },
43
51
  max: { type: [Date, String], required: false },
44
52
  modelValue: { type: [Date, String], required: false },
@@ -53,14 +61,20 @@ export default _defineComponent({
53
61
  const emit = __emit;
54
62
  const { t } = useTranslate("datetimePicker");
55
63
  const createColumnData = (types, currentDate) => {
56
- minValues = getBoundaryValue(false, minDate.value, currentDate);
57
- maxValues = getBoundaryValue(true, maxDate.value, currentDate);
64
+ minValues = getBoundaryValue(
65
+ props.calendar,
66
+ false,
67
+ minDate.value,
68
+ currentDate
69
+ );
70
+ maxValues = getBoundaryValue(props.calendar, true, maxDate.value, currentDate);
58
71
  const getColumnDataByType = (letter) => {
59
72
  const strategy = strategies[letter];
60
73
  const index = strategy[0];
61
74
  return getColumnData(
62
- maxValues[index] - minValues[index] + 1,
75
+ props.calendar,
63
76
  minValues[index],
77
+ maxValues[index],
64
78
  strategy[1],
65
79
  letter,
66
80
  currentDate,
@@ -74,14 +88,24 @@ export default _defineComponent({
74
88
  );
75
89
  };
76
90
  const getChangedLetter = (currentDate) => {
77
- const min = getBoundaryValue(false, minDate.value, currentDate);
78
- const max = getBoundaryValue(true, maxDate.value, currentDate);
91
+ const min = getBoundaryValue(
92
+ props.calendar,
93
+ false,
94
+ minDate.value,
95
+ currentDate
96
+ );
97
+ const max = getBoundaryValue(props.calendar, true, maxDate.value, currentDate);
79
98
  return letterArray.filter(
80
99
  (_, i) => min[i] !== minValues[i] || max[i] !== maxValues[i]
81
100
  );
82
101
  };
102
+ let lunarYearChanged = false;
83
103
  const updateColumns = (currentDate) => {
84
104
  const changedLetter = getChangedLetter(currentDate);
105
+ if (props.calendar === "lunar" && !changedLetter.includes("M") && innerType.value.includes("M") && lunarYearChanged) {
106
+ changedLetter.push("M");
107
+ lunarYearChanged = false;
108
+ }
85
109
  if (changedLetter.length) {
86
110
  const changedColumns = createColumnData(changedLetter, currentDate);
87
111
  const nextColumns = innerType.value.map((letter) => {
@@ -96,18 +120,66 @@ export default _defineComponent({
96
120
  }
97
121
  };
98
122
  const getDateByPickerValue = (value) => {
99
- const currEvery = letterArray.map((letter) => {
100
- const stratery = strategies[letter];
101
- for (let i = 0, l = innerType.value.length; i < l; i++) {
102
- if (innerType.value[i] === letter) {
103
- return value[i];
123
+ let currEvery;
124
+ if (props.calendar === "solar") {
125
+ currEvery = letterArray.map((letter) => {
126
+ const stratery = strategies[letter];
127
+ for (let i = 0, l = innerType.value.length; i < l; i++) {
128
+ if (innerType.value[i] === letter) {
129
+ return value[i];
130
+ }
104
131
  }
132
+ return stratery[4](innerValue.value);
133
+ });
134
+ } else {
135
+ const lunarDate = solarToLunar(
136
+ innerValue.value.getFullYear(),
137
+ innerValue.value.getMonth() + 1,
138
+ innerValue.value.getDate()
139
+ );
140
+ const yearIndex = innerType.value.indexOf("y");
141
+ if (yearIndex !== -1) {
142
+ lunarYearChanged = lunarDate.year !== value[yearIndex];
105
143
  }
106
- return stratery[4](innerValue.value);
107
- });
108
- correctDate(currEvery, minDate.value, maxDate.value);
109
- currEvery[1]--;
110
- const date = new Date(...currEvery);
144
+ currEvery = letterArray.map((letter) => {
145
+ for (let i = 0, l = innerType.value.length; i < l; i++) {
146
+ if (innerType.value[i] === letter) {
147
+ return value[i];
148
+ }
149
+ }
150
+ switch (letter) {
151
+ case "y":
152
+ return lunarDate.year;
153
+ case "M":
154
+ return lunarDate.month;
155
+ case "d":
156
+ return lunarDate.day;
157
+ default:
158
+ return strategies[letter][4](innerValue.value);
159
+ }
160
+ });
161
+ }
162
+ correctDate(
163
+ props.calendar,
164
+ currEvery,
165
+ minDate.value,
166
+ maxDate.value
167
+ );
168
+ let date;
169
+ if (props.calendar === "solar") {
170
+ currEvery[1]--;
171
+ date = new Date(...currEvery);
172
+ } else {
173
+ const solarDate = lunarToSolar(currEvery[0], currEvery[1], currEvery[2]);
174
+ date = new Date(
175
+ solarDate.year,
176
+ solarDate.month - 1,
177
+ solarDate.day,
178
+ currEvery[3],
179
+ currEvery[4],
180
+ currEvery[5]
181
+ );
182
+ }
111
183
  return date;
112
184
  };
113
185
  const normalizeValue = (value) => {
@@ -153,9 +225,29 @@ export default _defineComponent({
153
225
  }
154
226
  });
155
227
  const pickerValue = computed(() => {
156
- return innerType.value.map((letter) => {
157
- return strategies[letter][4](innerValue.value);
158
- });
228
+ if (props.calendar === "solar") {
229
+ return innerType.value.map((letter) => {
230
+ return strategies[letter][4](innerValue.value);
231
+ });
232
+ } else {
233
+ const lunarDate = solarToLunar(
234
+ innerValue.value.getFullYear(),
235
+ innerValue.value.getMonth() + 1,
236
+ innerValue.value.getDate()
237
+ );
238
+ return innerType.value.map((letter) => {
239
+ switch (letter) {
240
+ case "y":
241
+ return lunarDate.year;
242
+ case "M":
243
+ return lunarDate.month;
244
+ case "d":
245
+ return lunarDate.day;
246
+ default:
247
+ return strategies[letter][4](innerValue.value);
248
+ }
249
+ });
250
+ }
159
251
  });
160
252
  const columnsMap = {};
161
253
  let minValues = [];
@@ -168,7 +260,11 @@ export default _defineComponent({
168
260
  updateColumns(nextValue);
169
261
  emitChange();
170
262
  };
171
- const __returned__ = { props, emit, t, createColumnData, getChangedLetter, updateColumns, getDateByPickerValue, normalizeValue, innerType, minDate, maxDate, innerValue, get currentEmitValue() {
263
+ const __returned__ = { props, emit, t, createColumnData, getChangedLetter, get lunarYearChanged() {
264
+ return lunarYearChanged;
265
+ }, set lunarYearChanged(v) {
266
+ lunarYearChanged = v;
267
+ }, updateColumns, getDateByPickerValue, normalizeValue, innerType, minDate, maxDate, innerValue, get currentEmitValue() {
172
268
  return currentEmitValue;
173
269
  }, set currentEmitValue(v) {
174
270
  currentEmitValue = v;
@@ -6,6 +6,7 @@ export interface DatetimePickerInputProps extends DatetimePickerPopoutProps, Omi
6
6
  export declare const defaultDatetimePickerInputProps: () => {
7
7
  validateEvent: boolean;
8
8
  type: string;
9
+ calendar: "solar";
9
10
  };
10
11
  export interface DatetimePickerInputSlots extends DatetimePickerPopoutSlots {
11
12
  }
@@ -11,6 +11,7 @@ declare const __VLS_component: import("vue").DefineComponent<DatetimePickerInput
11
11
  }>, {
12
12
  type: string;
13
13
  validateEvent: boolean;
14
+ calendar: "solar" | "lunar";
14
15
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
15
16
  declare const _default: __VLS_WithTemplateSlots<typeof __VLS_component, ReturnType<typeof __VLS_template>>;
16
17
  export default _default;
@@ -24,6 +24,7 @@
24
24
  :formatter="formatter"
25
25
  :value-format="valueFormat"
26
26
  :validate-event="validateEvent"
27
+ :calendar="calendar"
27
28
  @change="onChange"
28
29
  />
29
30
  </sar-popout-input>
@@ -62,6 +63,7 @@ export default _defineComponent({
62
63
  rootStyle: { type: [Boolean, null, String, Object, Array], required: false, skipCheck: true },
63
64
  rootClass: { type: String, required: false },
64
65
  type: { type: String, required: false },
66
+ calendar: { type: String, required: false },
65
67
  min: { type: [Date, String], required: false },
66
68
  max: { type: [Date, String], required: false },
67
69
  modelValue: { type: [Date, String], required: false },
@@ -10,6 +10,7 @@ export interface DatetimePickerPopoutProps extends DatetimePickerProps {
10
10
  export declare const defaultDatetimePickerPopoutProps: () => {
11
11
  validateEvent: boolean;
12
12
  type: string;
13
+ calendar: "solar";
13
14
  };
14
15
  export interface DatetimePickerPopoutSlots extends DatetimePickerSlots {
15
16
  }
@@ -11,6 +11,7 @@ declare const __VLS_component: import("vue").DefineComponent<DatetimePickerPopou
11
11
  }>, {
12
12
  type: string;
13
13
  validateEvent: boolean;
14
+ calendar: "solar" | "lunar";
14
15
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
15
16
  declare const _default: __VLS_WithTemplateSlots<typeof __VLS_component, ReturnType<typeof __VLS_template>>;
16
17
  export default _default;
@@ -18,6 +18,7 @@
18
18
  :filter="filter"
19
19
  :formatter="formatter"
20
20
  :value-format="valueFormat"
21
+ :calendar="calendar"
21
22
  @change="onChange"
22
23
  />
23
24
  </template>
@@ -60,6 +61,7 @@ export default _defineComponent({
60
61
  rootStyle: { type: [Boolean, null, String, Object, Array], required: false, skipCheck: true },
61
62
  rootClass: { type: String, required: false },
62
63
  type: { type: String, required: false },
64
+ calendar: { type: String, required: false },
63
65
  min: { type: [Date, String], required: false },
64
66
  max: { type: [Date, String], required: false },
65
67
  modelValue: { type: [Date, String], required: false },
@@ -69,6 +69,7 @@ export default _defineComponent({
69
69
  rootStyle: { type: [Boolean, null, String, Object, Array], required: false, skipCheck: true },
70
70
  rootClass: { type: String, required: false },
71
71
  type: { type: String, required: false },
72
+ calendar: { type: String, required: false },
72
73
  min: { type: [Date, String], required: false },
73
74
  max: { type: [Date, String], required: false },
74
75
  filter: { type: Function, required: false },
@@ -66,6 +66,7 @@ export default _defineComponent({
66
66
  rootStyle: { type: [Boolean, null, String, Object, Array], required: false, skipCheck: true },
67
67
  rootClass: { type: String, required: false },
68
68
  type: { type: String, required: false },
69
+ calendar: { type: String, required: false },
69
70
  min: { type: [Date, String], required: false },
70
71
  max: { type: [Date, String], required: false },
71
72
  filter: { type: Function, required: false },
@@ -63,6 +63,7 @@ export default _defineComponent({
63
63
  rootStyle: { type: [Boolean, null, String, Object, Array], required: false, skipCheck: true },
64
64
  rootClass: { type: String, required: false },
65
65
  type: { type: String, required: false },
66
+ calendar: { type: String, required: false },
66
67
  min: { type: [Date, String], required: false },
67
68
  max: { type: [Date, String], required: false },
68
69
  filter: { type: Function, required: false },
@@ -1,4 +1,5 @@
1
1
  import { type StyleValue, type Ref } from 'vue';
2
+ import { type TransitionHookEmits } from '../popup/common';
2
3
  export interface DropdownProps {
3
4
  rootStyle?: StyleValue;
4
5
  rootClass?: string;
@@ -40,7 +41,7 @@ export declare const defaultDropdownItemProps: {
40
41
  export interface DropdownItemSlots {
41
42
  default?(props: Record<string, never>): any;
42
43
  }
43
- export interface DropdownItemEmits {
44
+ export interface DropdownItemEmits extends TransitionHookEmits {
44
45
  (e: 'update:model-value', value: any): void;
45
46
  (e: 'change', value: any): void;
46
47
  (e: 'update:visible', visible: boolean): void;
@@ -1,13 +1,11 @@
1
1
  import { type DropdownItemProps, type DropdownItemSlots, type DropdownOption } from '../dropdown/common';
2
2
  declare function __VLS_template(): Readonly<DropdownItemSlots> & DropdownItemSlots;
3
- declare const __VLS_component: import("vue").DefineComponent<DropdownItemProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {} & {
4
- "update:model-value": (value: any) => any;
5
- change: (value: any) => any;
6
- "update:visible": (visible: boolean) => any;
3
+ declare const __VLS_component: import("vue").DefineComponent<DropdownItemProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
4
+ [x: string]: any;
5
+ } & {
6
+ [x: string]: any;
7
7
  }, string, import("vue").PublicProps, Readonly<DropdownItemProps> & Readonly<{
8
- "onUpdate:model-value"?: ((value: any) => any) | undefined;
9
- onChange?: ((value: any) => any) | undefined;
10
- "onUpdate:visible"?: ((visible: boolean) => any) | undefined;
8
+ [x: `on${Capitalize<any>}`]: ((...args: any) => any) | undefined;
11
9
  }>, {
12
10
  options: DropdownOption[];
13
11
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
@@ -128,7 +128,7 @@ export default _defineComponent({
128
128
  separator: { type: String, required: false },
129
129
  placeholder: { type: String, required: false }
130
130
  }, defaultDropdownItemProps),
131
- emits: ["update:model-value", "change", "update:visible"],
131
+ emits: ["update:model-value", "change", "update:visible", "before-enter", "enter", "after-enter", "enter-cancelled", "before-leave", "leave", "after-leave", "leave-cancelled", "visible-hook"],
132
132
  setup(__props, { expose: __expose, emit: __emit }) {
133
133
  __expose();
134
134
  const props = __props;
@@ -254,6 +254,8 @@ export default _defineComponent({
254
254
  if (name === "after-leave") {
255
255
  onAfterLeave();
256
256
  }
257
+ emit("visible-hook", name);
258
+ emit(name);
257
259
  }
258
260
  })
259
261
  );
@@ -2,7 +2,7 @@
2
2
  <view :class="emptyClass" :style="emptyStyle">
3
3
  <view :class="bem.e('icon')" :style="iconStyle">
4
4
  <slot name="icon">
5
- <sar-icon :family="iconFamily || 'sari'" :name="icon" />
5
+ <sar-icon :family="iconFamily" :name="icon" />
6
6
  </slot>
7
7
  </view>
8
8
  <slot name="description">
@@ -27,7 +27,7 @@
27
27
  stringifyStyle({ background: item.background, color: item.color })
28
28
  "
29
29
  >
30
- <sar-icon :family="item.iconFamily || 'sari'" :name="item.icon" />
30
+ <sar-icon :family="item.iconFamily" :name="item.icon" />
31
31
  </view>
32
32
  </view>
33
33
  </view>
@@ -14,7 +14,7 @@
14
14
  :name="icon"
15
15
  :color="iconColor"
16
16
  :size="iconSize"
17
- :family="iconFamily || 'sari'"
17
+ :family="iconFamily"
18
18
  />
19
19
  </slot>
20
20
  </view>
@@ -33,12 +33,17 @@ export default _defineComponent({
33
33
  return isFileUrl(props.name);
34
34
  });
35
35
  const iconClass = computed(() => {
36
+ if (isImg.value) {
37
+ return classNames(bem.b(), props.rootClass);
38
+ }
39
+ if (props.name.includes(":")) {
40
+ const [family, name] = props.name.split(":");
41
+ return classNames(bem.b(), family, `${family}-${name}`, props.rootClass);
42
+ }
36
43
  return classNames(
37
44
  bem.b(),
38
- {
39
- [props.family]: !isImg.value,
40
- [`${props.family}-${props.name}`]: !isImg.value
41
- },
45
+ props.family,
46
+ `${props.family}-${props.name}`,
42
47
  props.rootClass
43
48
  );
44
49
  });
@@ -11,7 +11,7 @@
11
11
  :name="icon"
12
12
  :color="iconColor"
13
13
  :size="iconSize"
14
- :family="iconFamily || 'sari'"
14
+ :family="iconFamily"
15
15
  />
16
16
  </slot>
17
17
  </view>
@@ -4,7 +4,7 @@
4
4
  v-if="icon || (withIcon && direction === 'vertical')"
5
5
  :class="bem.e('icon')"
6
6
  >
7
- <sar-icon v-if="icon" :name="icon" :family="iconFamily || 'sari'" />
7
+ <sar-icon v-if="icon" :name="icon" :family="iconFamily" />
8
8
  </view>
9
9
  <view v-if="text" :class="bem.e('text')">
10
10
  {{ text }}
@@ -9,7 +9,7 @@
9
9
  v-if="icon"
10
10
  :class="bem.e('item-icon')"
11
11
  :name="icon"
12
- :family="iconFamily || 'sari'"
12
+ :family="iconFamily"
13
13
  :size="iconSize"
14
14
  />
15
15
 
@@ -31,7 +31,7 @@
31
31
  <template v-if="voidText">
32
32
  {{ voidText }}
33
33
  </template>
34
- <sar-icon v-else :name="voidIcon" :family="iconFamily || 'sari'" />
34
+ <sar-icon v-else :name="voidIcon" :family="iconFamily" />
35
35
  </view>
36
36
  <view
37
37
  :class="bem.e('star')"
@@ -45,7 +45,7 @@
45
45
  <template v-if="text">
46
46
  {{ text }}
47
47
  </template>
48
- <sar-icon v-else :name="icon" :family="iconFamily || 'sari'" />
48
+ <sar-icon v-else :name="icon" :family="iconFamily" />
49
49
  </view>
50
50
  </view>
51
51
  </view>
@@ -4,7 +4,7 @@
4
4
  <view :class="iconClass">
5
5
  <sar-icon
6
6
  :name="icon || mapStatusIcon[status]"
7
- :family="iconFamily || 'sari'"
7
+ :family="iconFamily"
8
8
  :color="iconColor"
9
9
  />
10
10
  </view>
@@ -47,7 +47,7 @@
47
47
  <sar-icon
48
48
  v-else
49
49
  :name="item.icon"
50
- :family="item.iconFamily || 'sari'"
50
+ :family="item.iconFamily"
51
51
  />
52
52
  </view>
53
53
  <view :class="bem.e('item-name')">{{ item.name }}</view>
@@ -5,7 +5,7 @@
5
5
  <view :class="bem.e('icon')">
6
6
  <slot name="icon" :status="currentStatus">
7
7
  <sar-icon
8
- :family="context.iconFamily || 'sari'"
8
+ :family="context.iconFamily"
9
9
  :name="statusIcon"
10
10
  :size="context.iconSize"
11
11
  />
@@ -3,11 +3,7 @@
3
3
  <slot>
4
4
  <view :class="bem.e('icon')">
5
5
  <slot name="icon" :active="isCurrent">
6
- <sar-icon
7
- :name="icon"
8
- :family="iconFamily || 'sari'"
9
- :size="iconSize"
10
- />
6
+ <sar-icon :name="icon" :family="iconFamily" :size="iconSize" />
11
7
  </slot>
12
8
  <sar-badge v-if="badge || dot" :value="badge" :dot="dot" fixed />
13
9
  </view>
@@ -5,11 +5,7 @@
5
5
  <view :class="bem.e('icon-wrapper')">
6
6
  <view v-if="icon || $slots.icon" :class="bem.e('icon')">
7
7
  <slot name="icon">
8
- <sar-icon
9
- :name="icon"
10
- :family="iconFamily || 'sari'"
11
- :color="iconColor"
12
- />
8
+ <sar-icon :name="icon" :family="iconFamily" :color="iconColor" />
13
9
  </slot>
14
10
  </view>
15
11
  <view v-else :class="bem.e('dot')"></view>
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "id": "sard-uniapp",
3
3
  "name": "sard-uniapp",
4
4
  "displayName": "sard-uniapp",
5
- "version": "1.17.1",
5
+ "version": "1.18.0",
6
6
  "description": "sard-uniapp 是一套基于 Uniapp + Vue3 框架开发的兼容多端的 UI 组件库",
7
7
  "main": "index.js",
8
8
  "scripts": {
package/utils/date.d.ts CHANGED
@@ -1,16 +1,46 @@
1
1
  export declare function isLeapYear(year: number): boolean;
2
- export declare function getDaysInMonth(year: number, month: number): 29 | 28 | 30 | 31;
3
- export declare function getDayOnFirstOfMonth(year: number, month: number): number;
2
+ export declare function getMonthDays(year: number, month: number): number;
3
+ export declare function getDaysInYear(date: Date): number;
4
+ export declare function getWeekOfMonthStart(year: number, month: number): number;
4
5
  export declare function getDaysInDate(date: Date): number;
5
6
  export declare function toDateNumber(date: Date): number;
6
7
  export declare function toDateString(date: Date): string;
7
8
  export declare function toMonthNumber(date: Date): number;
8
9
  export declare function getPadStartDays(year: number, month: number, amount: number): Date[];
9
10
  export declare function getPadEndDays(year: number, month: number, amount: number): Date[];
10
- export declare function getOffsetDaysFromFirstDay(weekOnFirstDay: number, weekStartsOn: number): number;
11
+ export declare function getOffsetDaysFromMonthStart(weekOnMonthStart: number, weekStartsOn: number): number;
11
12
  export declare function formatDate(date: Date, format?: string): string;
12
13
  export declare function parseDate(value: string, format?: string): Date;
13
14
  export declare function toDate(date: Date | string, valueFormat?: string): Date;
14
15
  export declare function minmaxDate(date: Date, minDate: Date, maxDate: Date): Date;
15
16
  export declare function getPrevMonthDate(date: Date): Date;
16
17
  export declare function getNextMonthDate(date: Date): Date;
18
+ /****************************************************************
19
+ * 农历
20
+ ****************************************************************/
21
+ export declare const lunarInfo: number[];
22
+ export declare const springFestivals: number[][];
23
+ export declare const baseLunarYear = 1900;
24
+ export declare function getLunarLeapMonth(year: number): number;
25
+ export declare function getLunarLeapMonthDays(year: number): 0 | 30 | 29;
26
+ export declare function getLunarYearDays(year: number): number;
27
+ export declare function getLunarMonthDays(year: number, month: number): 30 | 29;
28
+ export declare function solarToLunar(year: number, _month: number, date: number): {
29
+ year: number;
30
+ month: number;
31
+ day: number;
32
+ };
33
+ export declare function lunarToSolar(lunarYear: number, lunarMonth: number, lunarDay: number): {
34
+ year: number;
35
+ month: number;
36
+ day: number;
37
+ };
38
+ export declare const lunarYearNames: string[];
39
+ export declare function getLunarYearName(year: number): string;
40
+ export declare const lunarMonthNames: string[];
41
+ export declare function getLunarMonthName(month: number, isLeapMonth?: boolean): string;
42
+ export declare const lunarDayNames: string[];
43
+ export declare function getLunarDayName(day: number): string;
44
+ export declare const heavenlyStems: string[];
45
+ export declare const earthlyBranches: string[];
46
+ export declare function getLunarHourName(hour: number): string;
package/utils/date.js CHANGED
@@ -4,19 +4,30 @@ import { minmax } from './utils';
4
4
  export function isLeapYear(year) {
5
5
  return (year % 4 === 0 && year % 100 !== 0) || year % 400 === 0;
6
6
  }
7
- // 获取一个月中的天数
8
- export function getDaysInMonth(year, month) {
9
- month++;
7
+ const monthDays = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
8
+ // 获取某月的天数
9
+ export function getMonthDays(year, month) {
10
10
  if (month === 2) {
11
11
  return isLeapYear(year) ? 29 : 28;
12
12
  }
13
13
  else {
14
- return [4, 6, 9, 11].includes(month) ? 30 : 31;
14
+ return monthDays[month - 1];
15
15
  }
16
16
  }
17
- // 获取一个月中一号对应的星期
18
- export function getDayOnFirstOfMonth(year, month) {
19
- return new Date(year, month, 1).getDay();
17
+ // 获取当前日期是一年中的第几天
18
+ export function getDaysInYear(date) {
19
+ const year = date.getFullYear();
20
+ const month = date.getMonth();
21
+ const d = date.getDate();
22
+ let days = 0;
23
+ for (let m = 0; m < month; m++) {
24
+ days += getMonthDays(year, m + 1);
25
+ }
26
+ return days + d;
27
+ }
28
+ // 获取某月一号对应是星期几
29
+ export function getWeekOfMonthStart(year, month) {
30
+ return new Date(year, month - 1, 1).getDay();
20
31
  }
21
32
  // 获取 Date 中的总天数
22
33
  export function getDaysInDate(date) {
@@ -38,7 +49,7 @@ export function toMonthNumber(date) {
38
49
  export function getPadStartDays(year, month, amount) {
39
50
  const dates = [];
40
51
  for (let i = amount - 1; i >= 0; i--) {
41
- dates.push(new Date(year, month, -i));
52
+ dates.push(new Date(year, month - 1, -i));
42
53
  }
43
54
  return dates;
44
55
  }
@@ -46,13 +57,13 @@ export function getPadStartDays(year, month, amount) {
46
57
  export function getPadEndDays(year, month, amount) {
47
58
  const dates = [];
48
59
  for (let i = 1; i <= amount; i++) {
49
- dates.push(new Date(year, month + 1, i));
60
+ dates.push(new Date(year, month + 1 - 1, i));
50
61
  }
51
62
  return dates;
52
63
  }
53
64
  // 获取一号偏移的天数
54
- export function getOffsetDaysFromFirstDay(weekOnFirstDay, weekStartsOn) {
55
- return (weekOnFirstDay - weekStartsOn + 7) % 7;
65
+ export function getOffsetDaysFromMonthStart(weekOnMonthStart, weekStartsOn) {
66
+ return (weekOnMonthStart - weekStartsOn + 7) % 7;
56
67
  }
57
68
  const dateTokensReg = /(YYYY|YY|MM|M|DD|D|HH|H|hh|h|mm|m|ss|s|SSS)/g;
58
69
  const dateGetters = {
@@ -147,3 +158,190 @@ export function getPrevMonthDate(date) {
147
158
  export function getNextMonthDate(date) {
148
159
  return new Date(date.getFullYear(), date.getMonth() + 1, 1);
149
160
  }
161
+ /****************************************************************
162
+ * 农历
163
+ ****************************************************************/
164
+ // prettier-ignore
165
+ export const lunarInfo = [
166
+ 0x04bd8, 0x04ae0, 0x0a570, 0x054d5, 0x0d260, 0x0d950, 0x16554, 0x056a0, 0x09ad0, 0x055d2, // 1900-1909
167
+ 0x04ae0, 0x0a5b6, 0x0a4d0, 0x0d250, 0x1d255, 0x0b540, 0x0d6a0, 0x0ada2, 0x095b0, 0x14977, // 1910-1919
168
+ 0x04970, 0x0a4b0, 0x0b4b5, 0x06a50, 0x06d40, 0x1ab54, 0x02b60, 0x09570, 0x052f2, 0x04970, // 1920-1929
169
+ 0x06566, 0x0d4a0, 0x0ea50, 0x16a95, 0x05ad0, 0x02b60, 0x186e3, 0x092e0, 0x1c8d7, 0x0c950, // 1930-1939
170
+ 0x0d4a0, 0x1d8a6, 0x0b550, 0x056a0, 0x1a5b4, 0x025d0, 0x092d0, 0x0d2b2, 0x0a950, 0x0b557, // 1940-1949
171
+ 0x06ca0, 0x0b550, 0x15355, 0x04da0, 0x0a5b0, 0x14573, 0x052b0, 0x0a9a8, 0x0e950, 0x06aa0, // 1950-1959
172
+ 0x0aea6, 0x0ab50, 0x04b60, 0x0aae4, 0x0a570, 0x05260, 0x0f263, 0x0d950, 0x05b57, 0x056a0, // 1960-1969
173
+ 0x096d0, 0x04dd5, 0x04ad0, 0x0a4d0, 0x0d4d4, 0x0d250, 0x0d558, 0x0b540, 0x0b6a0, 0x195a6, // 1970-1979
174
+ 0x095b0, 0x049b0, 0x0a974, 0x0a4b0, 0x0b27a, 0x06a50, 0x06d40, 0x0af46, 0x0ab60, 0x09570, // 1980-1989
175
+ 0x04af5, 0x04970, 0x064b0, 0x074a3, 0x0ea50, 0x06b58, 0x05ac0, 0x0ab60, 0x096d5, 0x092e0, // 1990-1999
176
+ 0x0c960, 0x0d954, 0x0d4a0, 0x0da50, 0x07552, 0x056a0, 0x0abb7, 0x025d0, 0x092d0, 0x0cab5, // 2000-2009
177
+ 0x0a950, 0x0b4a0, 0x0baa4, 0x0ad50, 0x055d9, 0x04ba0, 0x0a5b0, 0x15176, 0x052b0, 0x0a930, // 2010-2019
178
+ 0x07954, 0x06aa0, 0x0ad50, 0x05b52, 0x04b60, 0x0a6e6, 0x0a4e0, 0x0d260, 0x0ea65, 0x0d530, // 2020-2029
179
+ 0x05aa0, 0x076a3, 0x096d0, 0x04afb, 0x04ad0, 0x0a4d0, 0x1d0b6, 0x0d250, 0x0d520, 0x0dd45, // 2030-2039
180
+ 0x0b5a0, 0x056d0, 0x055b2, 0x049b0, 0x0a577, 0x0a4b0, 0x0aa50, 0x1b255, 0x06d20, 0x0ada0, // 2040-2049
181
+ 0x14b63, 0x09370, 0x049f8, 0x04970, 0x064b0, 0x168a6, 0x0ea50, 0x06b20, 0x1a6c4, 0x0aae0, // 2050-2059
182
+ 0x092e0, 0x0d2e3, 0x0c960, 0x0d557, 0x0d4a0, 0x0da50, 0x05d55, 0x056a0, 0x0a6d0, 0x055d4, // 2060-2069
183
+ 0x052d0, 0x0a9b8, 0x0a950, 0x0b4a0, 0x0b6a6, 0x0ad50, 0x055a0, 0x0aba4, 0x0a5b0, 0x052b0, // 2070-2079
184
+ 0x0b273, 0x06930, 0x07337, 0x06aa0, 0x0ad50, 0x14b55, 0x04b60, 0x0a570, 0x054e4, 0x0d160, // 2080-2089
185
+ 0x0e968, 0x0d520, 0x0daa0, 0x16aa6, 0x056d0, 0x04ae0, 0x0a9d4, 0x0a2d0, 0x0d150, 0x0f252, // 2090-2099
186
+ 0x0d520, // 2100
187
+ ];
188
+ // 1900-2100春节对应的公历日期
189
+ // prettier-ignore
190
+ export const springFestivals = [
191
+ [1, 31], [2, 19], [2, 8], [1, 29], [2, 16], [2, 4], [1, 25], [2, 13], [2, 2], [1, 22], // 1900-1909
192
+ [2, 10], [1, 30], [2, 18], [2, 6], [1, 26], [2, 14], [2, 3], [1, 23], [2, 11], [2, 1], // 1910-1919
193
+ [2, 20], [2, 8], [1, 28], [2, 16], [2, 5], [1, 24], [2, 13], [2, 2], [1, 23], [2, 10], // 1920-1929
194
+ [1, 30], [2, 17], [2, 6], [1, 26], [2, 14], [2, 4], [1, 24], [2, 11], [1, 31], [2, 19], // 1930-1939
195
+ [2, 8], [1, 27], [2, 15], [2, 5], [1, 25], [2, 13], [2, 2], [1, 22], [2, 10], [1, 29], // 1940-1949
196
+ [2, 17], [2, 6], [1, 27], [2, 14], [2, 3], [1, 24], [2, 12], [1, 31], [2, 18], [2, 8], // 1950-1959
197
+ [1, 28], [2, 15], [2, 5], [1, 25], [2, 13], [2, 2], [1, 21], [2, 9], [1, 30], [2, 17], // 1960-1969
198
+ [2, 6], [1, 27], [2, 15], [2, 3], [1, 23], [2, 11], [1, 31], [2, 18], [2, 7], [1, 28], // 1970-1979
199
+ [2, 16], [2, 5], [1, 25], [2, 13], [2, 2], [2, 20], [2, 9], [1, 29], [2, 17], [2, 6], // 1980-1989
200
+ [1, 27], [2, 15], [2, 4], [1, 23], [2, 10], [1, 31], [2, 19], [2, 7], [1, 28], [2, 16], // 1990-1999
201
+ [2, 5], [1, 24], [2, 12], [2, 1], [1, 22], [2, 9], [1, 29], [2, 18], [2, 7], [1, 26], // 2000-2009
202
+ [2, 14], [2, 3], [1, 23], [2, 10], [1, 31], [2, 19], [2, 8], [1, 28], [2, 16], [2, 5], // 2010-2019
203
+ [1, 25], [2, 12], [2, 1], [1, 22], [2, 10], [1, 29], [2, 17], [2, 6], [1, 26], [2, 13], // 2020-2029
204
+ [2, 3], [1, 23], [2, 11], [1, 31], [2, 19], [2, 8], [1, 28], [2, 15], [2, 4], [1, 24], // 2030-2039
205
+ [2, 12], [2, 1], [1, 22], [2, 10], [1, 30], [2, 17], [2, 6], [1, 26], [2, 14], [2, 2], // 2040-2049
206
+ [1, 23], [2, 11], [2, 1], [2, 19], [2, 8], [1, 28], [2, 15], [2, 4], [1, 24], [2, 12], // 2050-2059
207
+ [2, 2], [1, 21], [2, 9], [1, 29], [2, 17], [2, 5], [1, 26], [2, 14], [2, 3], [1, 23], // 2060-2069
208
+ [2, 11], [1, 31], [2, 19], [2, 7], [1, 27], [2, 15], [2, 5], [1, 24], [2, 12], [2, 2], // 2070-2079
209
+ [1, 22], [2, 9], [1, 29], [2, 17], [2, 6], [1, 26], [2, 14], [2, 3], [1, 24], [2, 10], // 2080-2089
210
+ [1, 30], [2, 18], [2, 7], [1, 27], [2, 15], [2, 5], [1, 25], [2, 12], [2, 1], [1, 21], // 2090-2099
211
+ [2, 9], // 2100
212
+ ];
213
+ export const baseLunarYear = 1900;
214
+ // 获取农历某年闰月的月份(0表示无闰月)
215
+ export function getLunarLeapMonth(year) {
216
+ return lunarInfo[year - baseLunarYear] & 0xf;
217
+ }
218
+ // 获取农历某年闰月的天数
219
+ export function getLunarLeapMonthDays(year) {
220
+ if (getLunarLeapMonth(year)) {
221
+ return lunarInfo[year - baseLunarYear] & 0x10000 ? 30 : 29;
222
+ }
223
+ return 0;
224
+ }
225
+ // 获取农历某年的总天数
226
+ export function getLunarYearDays(year) {
227
+ let sum = 348; // 29天*12个月
228
+ const info = lunarInfo[year - baseLunarYear];
229
+ // 加上大月的天数
230
+ for (let i = 0x8000; i > 0x8; i >>= 1) {
231
+ sum += info & i ? 1 : 0;
232
+ }
233
+ // 加上闰月的天数
234
+ return sum + getLunarLeapMonthDays(year);
235
+ }
236
+ // 获取农历某年某月的天数
237
+ export function getLunarMonthDays(year, month) {
238
+ return lunarInfo[year - baseLunarYear] & (0x10000 >> month) ? 30 : 29;
239
+ }
240
+ // 公历转农历
241
+ export function solarToLunar(year, _month, date) {
242
+ // 计算输入日期与基准日期的天数差
243
+ const offsetDays = Math.floor((Date.UTC(year, _month - 1, date) - Date.UTC(baseLunarYear, 0, 31)) /
244
+ (24 * 60 * 60 * 1000));
245
+ // 农历年、月、日初始化
246
+ let lunarYear = baseLunarYear;
247
+ let lunarMonth = 1;
248
+ let lunarDay = 1;
249
+ let isLeapMonth = false;
250
+ let daysRemaining = offsetDays;
251
+ // 计算农历年
252
+ while (true) {
253
+ const yearDays = getLunarYearDays(lunarYear);
254
+ if (daysRemaining < yearDays) {
255
+ break;
256
+ }
257
+ daysRemaining -= yearDays;
258
+ lunarYear++;
259
+ }
260
+ // 计算农历月和日
261
+ const leapMonth = getLunarLeapMonth(lunarYear);
262
+ let monthDays = 0;
263
+ let month = 1;
264
+ for (; month <= 12; month++) {
265
+ // 处理闰月
266
+ if (leapMonth > 0 && month === leapMonth + 1) {
267
+ monthDays = getLunarLeapMonthDays(lunarYear);
268
+ if (daysRemaining < monthDays) {
269
+ isLeapMonth = true;
270
+ month--;
271
+ break;
272
+ }
273
+ daysRemaining -= monthDays;
274
+ }
275
+ monthDays = getLunarMonthDays(lunarYear, month);
276
+ if (daysRemaining < monthDays) {
277
+ break;
278
+ }
279
+ daysRemaining -= monthDays;
280
+ }
281
+ lunarMonth = month;
282
+ lunarDay = daysRemaining + 1;
283
+ return {
284
+ year: lunarYear,
285
+ month: lunarMonth * (isLeapMonth ? -1 : 1),
286
+ day: lunarDay,
287
+ };
288
+ }
289
+ // 农历转公历(闰月需传递负数)
290
+ export function lunarToSolar(lunarYear, lunarMonth, lunarDay) {
291
+ const springFestival = springFestivals[lunarYear - baseLunarYear];
292
+ const solarDate = new Date(lunarYear, springFestival[0] - 1, springFestival[1]);
293
+ const info = lunarInfo[lunarYear - baseLunarYear];
294
+ const leapMonth = info & 0xf;
295
+ let totalDays = 0;
296
+ const absMonth = Math.abs(lunarMonth);
297
+ for (let m = 1; m < absMonth; m++) {
298
+ if (m === leapMonth) {
299
+ totalDays += getLunarLeapMonthDays(lunarYear);
300
+ }
301
+ totalDays += getLunarMonthDays(lunarYear, m);
302
+ }
303
+ if (lunarMonth < 0) {
304
+ totalDays += getLunarMonthDays(lunarYear, absMonth);
305
+ }
306
+ totalDays += lunarDay - 1;
307
+ solarDate.setDate(solarDate.getDate() + totalDays);
308
+ return {
309
+ year: solarDate.getFullYear(),
310
+ month: solarDate.getMonth() + 1,
311
+ day: solarDate.getDate(),
312
+ };
313
+ }
314
+ // 农历年份的名称
315
+ // prettier-ignore
316
+ export const lunarYearNames = ['零', '一', '二', '三', '四', '五', '六', '七', '八', '九', '十'];
317
+ export function getLunarYearName(year) {
318
+ return (String(year)
319
+ .split('')
320
+ .map((item) => lunarYearNames[+item])
321
+ .join('') + '年');
322
+ }
323
+ // 农历月份的名称
324
+ // prettier-ignore
325
+ export const lunarMonthNames = ['正', '二', '三', '四', '五', '六', '七', '八', '九', '十', '冬', '腊'];
326
+ // 获取农历月份名称
327
+ export function getLunarMonthName(month, isLeapMonth) {
328
+ return (isLeapMonth ? '闰' : '') + lunarMonthNames[month - 1] + '月';
329
+ }
330
+ // 农历日期的名称
331
+ // prettier-ignore
332
+ export const lunarDayNames = ['初一', '初二', '初三', '初四', '初五', '初六', '初七', '初八', '初九', '初十', '十一', '十二', '十三', '十四', '十五', '十六', '十七', '十八', '十九', '二十', '廿一', '廿二', '廿三', '廿四', '廿五', '廿六', '廿七', '廿八', '廿九', '三十'];
333
+ // 获取农历日期名称
334
+ export function getLunarDayName(day) {
335
+ return lunarDayNames[day - 1];
336
+ }
337
+ // 十天干
338
+ // prettier-ignore
339
+ export const heavenlyStems = ['甲', '乙', '丙', '丁', '戊', '己', '庚', '辛', '壬', '癸'];
340
+ // 十二地支
341
+ // prettier-ignore
342
+ export const earthlyBranches = ['子', '丑', '寅', '卯', '辰', '巳', '午', '未', '申', '酉', '戌', '亥'];
343
+ // 获取农历时辰名称
344
+ export function getLunarHourName(hour) {
345
+ const index = Math.floor(((hour === 23 ? 0 : hour) + 1) / 2) % 12;
346
+ return earthlyBranches[index] + (hour % 2 === 1 ? '初' : '正');
347
+ }