feat(home): 日视图swiper轮播+老黄历方框合并+引入lunar-javascript真实宜忌
Publish Mini Program Dev Version / publish (push) Has been cancelled

1. 日视图改为swiper轮播,左右滑动切换日期,箭头固定在最左最右
2. 大日期方框扩大到包含农历和干支
3. 生肖(马年)移到导航栏右侧
4. 星期移到工作日提示左侧
5. 引入lunar-javascript(6tail库),用真实宜忌(最多20+项)替换手写4-5项
6. 值神/黄道详情样式调整:label大、value小、加间隔
7. 去掉点击放大提示
This commit is contained in:
gouki
2026-08-08 14:04:59 +00:00
parent ebbe994af7
commit 692f291338
5 changed files with 359 additions and 225 deletions
+113 -58
View File
@@ -8,33 +8,32 @@ function pad(n) { return String(n).padStart(2, '0'); }
function fmt(y, m, d) { return `${y}-${pad(m)}-${pad(d)}`; }
const MONTH_SPAN = 2; // 当前月前后各2个月,共5个月
const DAY_SPAN = 2; // 当前日前后各2天,共5天
Page({
data: {
statusBarHeight: 20,
navBarHeight: 44,
viewMode: 'day',
dayInfo: {},
almanac: {},
legalHoliday: null,
workday: true,
termTime: '',
isFirstOrFifteen: false,
nextBuddhist: null,
showBuddhist: false,
highlightLunar: true,
upcoming: [],
// 日视图 swiper
dayList: [], // [{key, dayInfo, almanac, legalHoliday, workday, termTime, isFirstOrFifteen, nextBuddhist, showBuddhist, upcoming}]
dayIndex: 2, // 当前居中
// 月历 swiper
monthList: [], // [{key, year, month, weeks}]
monthIndex: 2, // 当前居中
monthList: [],
monthIndex: 2,
currentYear: 0,
currentMonth: 0,
weekHeaders: ['日', '一', '二', '三', '四', '五', '六'],
// 当前选中日期(用于月历联动)
dayInfo: {},
// 全屏弹窗
zoomVisible: false,
zoomTitle: '',
zoomType: '', // yiji | detail | fest
zoomData: null
zoomType: '',
zoomData: null,
// 设置
showBuddhist: false,
highlightLunar: true
},
onLoad() {
@@ -45,14 +44,12 @@ Page({
if (menu && menu.top) navBarHeight = (menu.top - statusBarHeight) * 2 + menu.height;
this.setData({ statusBarHeight, navBarHeight });
this.loadSettings();
this.loadDay(new Date());
this.loadUpcoming();
this.buildDayList(new Date());
},
onShow() {
this.loadSettings();
// 设置可能变化,刷新
if (this.data.dayInfo.solarYear) this.refreshDerived();
if (this.data.dayList.length > 0) this.refreshDayListSettings();
},
loadSettings() {
@@ -65,55 +62,116 @@ Page({
onPullDownRefresh() {
if (this.data.viewMode === 'day') {
this.loadDay(new Date());
this.loadUpcoming();
this.buildDayList(new Date());
} else {
this.buildMonthList(this.data.currentYear, this.data.currentMonth);
}
wx.stopPullDownRefresh();
},
/** 加载某日的老黄历 */
loadDay(date) {
/** 构建某日的完整数据 */
buildDayData(date) {
const y = date.getFullYear();
const m = date.getMonth() + 1;
const d = date.getDate();
const dayInfo = getDayInfo(y, m, d);
const almanac = getAlmanacInfo(y, m, d);
this.setData({ dayInfo, almanac, currentYear: y, currentMonth: m });
this.refreshDerived();
this.loadUpcoming();
},
/** 计算派生数据(节假日/工作日/佛历/初一十五) */
refreshDerived() {
const { dayInfo, showBuddhist, highlightLunar } = this.data;
const { solarYear: y, solarMonth: m, solarDay: d } = dayInfo;
const legalHoliday = getLegalHoliday(y, m, d);
const workday = isWorkday(y, m, d);
const termTime = dayInfo.solarTerm ? (SOLAR_TERM_TIMES[dayInfo.solarTerm] || '') : '';
const isFirstOrFifteen = highlightLunar && (dayInfo.lunarDay === 1 || dayInfo.lunarDay === 15);
const nextBuddhist = showBuddhist ? getNextBuddhistFestival(y, m, d) : null;
this.setData({ legalHoliday, workday, termTime, isFirstOrFifteen, nextBuddhist });
const isFirstOrFifteen = this.data.highlightLunar && (dayInfo.lunarDay === 1 || dayInfo.lunarDay === 15);
const nextBuddhist = this.data.showBuddhist ? getNextBuddhistFestival(y, m, d) : null;
const upcoming = getUpcomingFestivals(5, y, m, d);
return {
key: fmt(y, m, d),
dayInfo, almanac, legalHoliday, workday, termTime,
isFirstOrFifteen, nextBuddhist, upcoming,
showBuddhist: this.data.showBuddhist
};
},
loadUpcoming() {
const { dayInfo } = this.data;
const { solarYear: y, solarMonth: m, solarDay: d } = dayInfo;
this.setData({ upcoming: getUpcomingFestivals(5, y, m, d) });
/** 构建5天的数据,居中显示 */
buildDayList(centerDate) {
const dayList = [];
for (let i = -DAY_SPAN; i <= DAY_SPAN; i++) {
const date = new Date(centerDate.getFullYear(), centerDate.getMonth(), centerDate.getDate() + i);
dayList.push(this.buildDayData(date));
}
const center = dayList[DAY_SPAN];
this.setData({
dayList,
dayIndex: DAY_SPAN,
dayInfo: center.dayInfo,
currentYear: center.dayInfo.solarYear,
currentMonth: center.dayInfo.solarMonth
});
},
prevDay() { this.shiftDay(-1); },
nextDay() { this.shiftDay(1); },
shiftDay(delta) {
const { dayInfo } = this.data;
const date = new Date(dayInfo.solarYear, dayInfo.solarMonth - 1, dayInfo.solarDay + delta);
this.loadDay(date);
/** 设置变化后刷新 dayList */
refreshDayListSettings() {
const dayList = this.data.dayList.map(item => ({
...item,
showBuddhist: this.data.showBuddhist,
isFirstOrFifteen: this.data.highlightLunar && (item.dayInfo.lunarDay === 1 || item.dayInfo.lunarDay === 15),
nextBuddhist: this.data.showBuddhist ? getNextBuddhistFestival(item.dayInfo.solarYear, item.dayInfo.solarMonth, item.dayInfo.solarDay) : null
}));
this.setData({ dayList });
},
/** 日视图 swiper 切换 */
onDaySwiperChange(e) {
const index = e.detail.current;
const { dayList } = this.data;
const cur = dayList[index];
this.setData({
dayIndex: index,
dayInfo: cur.dayInfo,
currentYear: cur.dayInfo.solarYear,
currentMonth: cur.dayInfo.solarMonth
});
// 边缘时向对应方向补一天,保持可无限滑动
if (index === 0) {
this.prependDay();
} else if (index === dayList.length - 1) {
this.appendDay();
}
},
prependDay() {
const { dayList } = this.data;
const first = dayList[0];
const date = new Date(first.dayInfo.solarYear, first.dayInfo.solarMonth - 1, first.dayInfo.solarDay - 1);
dayList.unshift(this.buildDayData(date));
if (dayList.length > 7) dayList.pop();
this.setData({ dayList, dayIndex: this.data.dayIndex + 1 });
},
appendDay() {
const { dayList } = this.data;
const last = dayList[dayList.length - 1];
const date = new Date(last.dayInfo.solarYear, last.dayInfo.solarMonth - 1, last.dayInfo.solarDay + 1);
dayList.push(this.buildDayData(date));
if (dayList.length > 7) dayList.shift();
this.setData({ dayList, dayIndex: this.data.dayIndex - 1 });
},
prevDay() {
if (this.data.dayIndex > 0) {
this.setData({ dayIndex: this.data.dayIndex - 1 });
this.onDaySwiperChange({ detail: { current: this.data.dayIndex - 1 } });
}
},
nextDay() {
if (this.data.dayIndex < this.data.dayList.length - 1) {
this.setData({ dayIndex: this.data.dayIndex + 1 });
this.onDaySwiperChange({ detail: { current: this.data.dayIndex + 1 } });
}
},
backToday() {
this.loadDay(new Date());
this.loadUpcoming();
this.buildDayList(new Date());
},
/** 切换 日/月 视图 */
@@ -139,14 +197,13 @@ Page({
this.setData({ monthList, monthIndex: MONTH_SPAN, currentYear: year, currentMonth: month });
},
/** swiper 切换 */
/** 月历 swiper 切换 */
onMonthSwiperChange(e) {
const index = e.detail.current;
const { monthList } = this.data;
const cur = monthList[index];
this.setData({ monthIndex: index, currentYear: cur.year, currentMonth: cur.month });
// 边缘时向对应方向补一个月,保持可无限滚动
if (index === 0) {
this.prependMonth();
} else if (index === monthList.length - 1) {
@@ -180,28 +237,26 @@ Page({
selectDay(e) {
const date = e.currentTarget.dataset.date;
const [y, m, d] = date.split('-').map(Number);
this.loadDay(new Date(y, m - 1, d));
this.buildDayList(new Date(y, m - 1, d));
this.setData({ viewMode: 'day' });
},
goDetail() {
const { dayInfo } = this.data;
wx.navigateTo({ url: `/pages/day-detail/day-detail?date=${fmt(dayInfo.solarYear, dayInfo.solarMonth, dayInfo.solarDay)}` });
},
/** 跳转到某节日的老黄历 */
goFestival(e) {
const date = e.currentTarget.dataset.date;
const [y, m, d] = date.split('-').map(Number);
this.loadDay(new Date(y, m - 1, d));
this.buildDayList(new Date(y, m - 1, d));
this.setData({ viewMode: 'day' });
wx.pageScrollTo && wx.pageScrollTo({ scrollTop: 0, duration: 0 });
},
/** ===== 全屏放大弹窗 ===== */
openZoom(e) {
const type = e.currentTarget.dataset.type;
const { dayInfo, almanac, legalHoliday, termTime } = this.data;
const date = e.currentTarget.dataset.date;
const dayItem = this.data.dayList.find(item => item.key === date);
if (!dayItem) return;
const { dayInfo, almanac, legalHoliday, termTime } = dayItem;
let zoomTitle = '';
let zoomData = null;
if (type === 'yiji') {
@@ -223,7 +278,7 @@ Page({
};
} else if (type === 'fest') {
zoomTitle = '近期节日';
zoomData = { upcoming: this.data.upcoming };
zoomData = { upcoming: dayItem.upcoming };
}
this.setData({ zoomVisible: true, zoomTitle, zoomType: type, zoomData });
},