feat(home): 修复swiper动画、宜忌布局、月历显示、节日速查
1. 修复swiper箭头点击动画丢失问题 - 改用固定7天窗口,滑动到边缘时整体滚动数据 - 保持dayIndex连续,确保swiper平滑动画 2. 修复宜忌两行显示压在一起 - 设置item固定高度32rpx和行距12rpx - 超出2行显示...省略号 3. 修复月历数字竖向排列问题 - 修正wxml嵌套结构,让42个格子正确排成6行7列 - 压缩格子高度,放大数字字体 4. 节日速查功能优化 - 默认显示3条,右侧添加更多
This commit is contained in:
+188
-28
@@ -1,14 +1,24 @@
|
||||
const {
|
||||
getDayInfo, getAlmanacInfo, getMonthCalendar,
|
||||
getLegalHoliday, isWorkday, getUpcomingFestivals, getNextBuddhistFestival
|
||||
getLegalHoliday, isWorkday, getUpcomingFestivals, getNextBuddhistFestival, getYearFestivals
|
||||
} = require('../../utils/core/index.js');
|
||||
const { SOLAR_TERM_TIMES } = require('../../utils/core/data.js');
|
||||
const { request } = require('../../utils/request.js');
|
||||
|
||||
function pad(n) { return String(n).padStart(2, '0'); }
|
||||
function fmt(y, m, d) { return `${y}-${pad(m)}-${pad(d)}`; }
|
||||
|
||||
/** 历史上的今天年份显示:负数为公元前,0 为年份不详 */
|
||||
function fmtYear(year) {
|
||||
if (!year) return '';
|
||||
return year < 0 ? `前${-year}年` : `${year}年`;
|
||||
}
|
||||
|
||||
const OTD_CACHE_KEY = 'otd-cache';
|
||||
const OTD_CACHE_TTL = 48 * 3600 * 1000; // 48 小时(数据按月日维度存储,变化频率低)
|
||||
|
||||
const MONTH_SPAN = 2; // 当前月前后各2个月,共5个月
|
||||
const DAY_SPAN = 2; // 当前日前后各2天,共5天
|
||||
const DAY_SPAN = 3; // 当前日前后各3天,共7天(固定窗口,避免动态增删导致动画丢失)
|
||||
|
||||
Page({
|
||||
data: {
|
||||
@@ -26,6 +36,12 @@ Page({
|
||||
weekHeaders: ['日', '一', '二', '三', '四', '五', '六'],
|
||||
// 当前选中日期(用于月历联动)
|
||||
dayInfo: {},
|
||||
// 历史上的今天加载中
|
||||
otdLoading: false,
|
||||
// 标记是否正在程序触发 swiper 切换(避免 onDaySwiperChange 重复处理)
|
||||
swiperAnimating: false,
|
||||
// 标记是否需要在动画完成后滚动窗口
|
||||
pendingShift: 0,
|
||||
// 全屏弹窗
|
||||
zoomVisible: false,
|
||||
zoomTitle: '',
|
||||
@@ -86,11 +102,13 @@ Page({
|
||||
key: fmt(y, m, d),
|
||||
dayInfo, almanac, legalHoliday, workday, termTime,
|
||||
isFirstOrFifteen, nextBuddhist, upcoming,
|
||||
showBuddhist: this.data.showBuddhist
|
||||
showBuddhist: this.data.showBuddhist,
|
||||
otd: null, // 历史上的今天数据(异步加载)
|
||||
otdTab: 'event' // event:大事记 birth:出生
|
||||
};
|
||||
},
|
||||
|
||||
/** 构建5天的数据,居中显示 */
|
||||
/** 构建7天的数据,居中显示 */
|
||||
buildDayList(centerDate) {
|
||||
const dayList = [];
|
||||
for (let i = -DAY_SPAN; i <= DAY_SPAN; i++) {
|
||||
@@ -105,6 +123,7 @@ Page({
|
||||
currentYear: center.dayInfo.solarYear,
|
||||
currentMonth: center.dayInfo.solarMonth
|
||||
});
|
||||
this.ensureOtd(DAY_SPAN);
|
||||
},
|
||||
|
||||
/** 设置变化后刷新 dayList */
|
||||
@@ -121,7 +140,7 @@ Page({
|
||||
/** 日视图 swiper 切换 */
|
||||
onDaySwiperChange(e) {
|
||||
const index = e.detail.current;
|
||||
const { dayList } = this.data;
|
||||
const { dayList, swiperAnimating } = this.data;
|
||||
const cur = dayList[index];
|
||||
this.setData({
|
||||
dayIndex: index,
|
||||
@@ -129,44 +148,164 @@ Page({
|
||||
currentYear: cur.dayInfo.solarYear,
|
||||
currentMonth: cur.dayInfo.solarMonth
|
||||
});
|
||||
this.ensureOtd(index);
|
||||
|
||||
// 边缘时向对应方向补一天,保持可无限滑动
|
||||
// 程序触发切换时不处理边缘滚动(避免打断动画)
|
||||
if (swiperAnimating) {
|
||||
this.setData({ swiperAnimating: false });
|
||||
return;
|
||||
}
|
||||
|
||||
// 用户手动滑动到边缘时,标记待滚动方向,等动画完成后再滚动
|
||||
if (index === 0) {
|
||||
this.prependDay();
|
||||
this.setData({ pendingShift: -1 });
|
||||
} else if (index === dayList.length - 1) {
|
||||
this.appendDay();
|
||||
this.setData({ pendingShift: 1 });
|
||||
}
|
||||
},
|
||||
|
||||
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 });
|
||||
/** 日视图 swiper 动画完成 */
|
||||
onDaySwiperFinish() {
|
||||
const { pendingShift } = this.data;
|
||||
if (pendingShift !== 0) {
|
||||
this.setData({ pendingShift: 0 });
|
||||
this.shiftDayWindow(pendingShift);
|
||||
}
|
||||
},
|
||||
|
||||
appendDay() {
|
||||
/** 滚动日数据窗口:direction -1=向前,1=向后 */
|
||||
shiftDayWindow(direction) {
|
||||
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 });
|
||||
const edge = direction < 0 ? dayList[0] : dayList[dayList.length - 1];
|
||||
const offset = direction < 0 ? -1 : 1;
|
||||
const newDate = new Date(edge.dayInfo.solarYear, edge.dayInfo.solarMonth - 1, edge.dayInfo.solarDay + offset);
|
||||
const newDay = this.buildDayData(newDate);
|
||||
|
||||
let newList, newIndex;
|
||||
if (direction < 0) {
|
||||
// 向前滚动:新数据加到前面,当前索引+1(因为前面多了一天)
|
||||
newList = [newDay, ...dayList.slice(0, -1)];
|
||||
newIndex = this.data.dayIndex + 1;
|
||||
} else {
|
||||
// 向后滚动:新数据加到后面,当前索引-1(因为前面少了一天)
|
||||
newList = [...dayList.slice(1), newDay];
|
||||
newIndex = this.data.dayIndex - 1;
|
||||
}
|
||||
|
||||
// 滚动窗口后,current 保持连续,swiper 会平滑滑动到新位置
|
||||
this.setData({
|
||||
dayList: newList,
|
||||
dayIndex: newIndex,
|
||||
dayInfo: newList[newIndex].dayInfo,
|
||||
currentYear: newList[newIndex].dayInfo.solarYear,
|
||||
currentMonth: newList[newIndex].dayInfo.solarMonth
|
||||
});
|
||||
this.ensureOtd(newIndex);
|
||||
},
|
||||
|
||||
prevDay() {
|
||||
if (this.data.dayIndex > 0) {
|
||||
this.setData({ dayIndex: this.data.dayIndex - 1 });
|
||||
this.updateCurrentDayInfo();
|
||||
const { dayIndex, dayList } = this.data;
|
||||
if (dayIndex > 0) {
|
||||
// 不在边缘,正常切换
|
||||
const index = dayIndex - 1;
|
||||
this.setData({ dayIndex: index, swiperAnimating: true });
|
||||
this.ensureOtd(index);
|
||||
} else {
|
||||
// 在边缘,滚动窗口(窗口滚动后 dayIndex 会调整为 1,swiper 从 0 滑到 1,有动画)
|
||||
this.shiftDayWindow(-1);
|
||||
}
|
||||
},
|
||||
|
||||
nextDay() {
|
||||
if (this.data.dayIndex < this.data.dayList.length - 1) {
|
||||
this.setData({ dayIndex: this.data.dayIndex + 1 });
|
||||
this.updateCurrentDayInfo();
|
||||
const { dayIndex, dayList } = this.data;
|
||||
if (dayIndex < dayList.length - 1) {
|
||||
// 不在边缘,正常切换
|
||||
const index = dayIndex + 1;
|
||||
this.setData({ dayIndex: index, swiperAnimating: true });
|
||||
this.ensureOtd(index);
|
||||
} else {
|
||||
// 在边缘,滚动窗口(窗口滚动后 dayIndex 会调整为 5,swiper 从 6 滑到 5,有动画)
|
||||
this.shiftDayWindow(1);
|
||||
}
|
||||
},
|
||||
|
||||
/** ===== 历史上的今天(数据来源:中文维基百科) ===== */
|
||||
|
||||
/** 确保指定 index 的日期已加载 OTD 数据(优先读缓存) */
|
||||
ensureOtd(index) {
|
||||
const item = this.data.dayList[index];
|
||||
if (!item || item.otd || item.otdLoading) return;
|
||||
|
||||
const m = item.dayInfo.solarMonth;
|
||||
const d = item.dayInfo.solarDay;
|
||||
const cacheKey = `${m}-${d}`;
|
||||
|
||||
// 读本地缓存(按月日维度,48 小时有效)
|
||||
try {
|
||||
const cache = wx.getStorageSync(OTD_CACHE_KEY) || {};
|
||||
const hit = cache[cacheKey];
|
||||
if (hit && hit.data && Date.now() - hit.ts < OTD_CACHE_TTL) {
|
||||
this.applyOtd(index, item.key, hit.data);
|
||||
return;
|
||||
}
|
||||
} catch (e) { /* 缓存不可用时忽略 */ }
|
||||
|
||||
this.setData({ [`dayList[${index}].otdLoading`]: true });
|
||||
request({ url: `/api/wiki/on-this-day?month=${m}&day=${d}` })
|
||||
.then(res => {
|
||||
if (res && res.code === 0 && res.data) {
|
||||
this.applyOtd(index, item.key, res.data);
|
||||
this.saveOtdCache(cacheKey, res.data);
|
||||
}
|
||||
})
|
||||
.catch(() => { /* 网络失败时不显示卡片 */ })
|
||||
.then(() => {
|
||||
// 窗口可能已滚动,先确认 index 处仍是同一天
|
||||
const cur = this.data.dayList[index];
|
||||
if (cur && cur.key === item.key) {
|
||||
this.setData({ [`dayList[${index}].otdLoading`]: false });
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
/** 将 OTD 数据写入指定日期(异步返回时窗口可能已滚动,需校验 key) */
|
||||
applyOtd(index, key, data) {
|
||||
const cur = this.data.dayList[index];
|
||||
if (!cur || cur.key !== key) return;
|
||||
|
||||
const withYear = list => (list || []).map(it => ({ ...it, yearText: fmtYear(it.year) }));
|
||||
const events = withYear(data.events);
|
||||
const births = withYear(data.births);
|
||||
const deaths = withYear(data.deaths);
|
||||
const festivals = withYear(data.festivals);
|
||||
if (!events.length && !births.length) return; // 无数据不显示卡片
|
||||
|
||||
this.setData({
|
||||
[`dayList[${index}].otd`]: {
|
||||
events, births, deaths, festivals,
|
||||
eventsPreview: events.slice(0, 4),
|
||||
birthsPreview: births.slice(0, 4)
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
/** 写入本地缓存(最多保留 80 天,超出淘汰最早的) */
|
||||
saveOtdCache(cacheKey, data) {
|
||||
try {
|
||||
const cache = wx.getStorageSync(OTD_CACHE_KEY) || {};
|
||||
cache[cacheKey] = { ts: Date.now(), data };
|
||||
const keys = Object.keys(cache);
|
||||
if (keys.length > 80) delete cache[keys[0]];
|
||||
wx.setStorageSync(OTD_CACHE_KEY, cache);
|
||||
} catch (e) { /* 存储失败时忽略 */ }
|
||||
},
|
||||
|
||||
/** 卡片内切换 大事记/出生 */
|
||||
switchOtdTab(e) {
|
||||
const { date, tab } = e.currentTarget.dataset;
|
||||
const index = this.data.dayList.findIndex(item => item.key === date);
|
||||
if (index >= 0) {
|
||||
this.setData({ [`dayList[${index}].otdTab`]: tab });
|
||||
}
|
||||
},
|
||||
|
||||
@@ -291,6 +430,12 @@ Page({
|
||||
} else if (type === 'fest') {
|
||||
zoomTitle = '近期节日';
|
||||
zoomData = { upcoming: dayItem.upcoming };
|
||||
} else if (type === 'fest-year') {
|
||||
zoomTitle = `${dayInfo.solarYear}年节日`;
|
||||
zoomData = { yearFestivals: getYearFestivals(dayInfo.solarYear) };
|
||||
} else if (type === 'otd') {
|
||||
zoomTitle = `${dayInfo.solarMonth}月${dayInfo.solarDay}日 历史上的今天`;
|
||||
zoomData = dayItem.otd;
|
||||
}
|
||||
this.setData({ zoomVisible: true, zoomTitle, zoomType: type, zoomData });
|
||||
},
|
||||
@@ -303,5 +448,20 @@ Page({
|
||||
wx.navigateTo({ url: e.currentTarget.dataset.url });
|
||||
},
|
||||
|
||||
noop() {}
|
||||
noop() {},
|
||||
|
||||
// 分享给好友
|
||||
onShareAppMessage() {
|
||||
return {
|
||||
title: '老黄历 - 传统农历黄历查询',
|
||||
path: '/pages/home/home'
|
||||
};
|
||||
},
|
||||
|
||||
// 分享到朋友圈
|
||||
onShareTimeline() {
|
||||
return {
|
||||
title: '老黄历 - 传统农历黄历查询'
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
+100
-33
@@ -18,6 +18,7 @@
|
||||
class="day-swiper"
|
||||
current="{{dayIndex}}"
|
||||
bindchange="onDaySwiperChange"
|
||||
bindanimationfinish="onDaySwiperFinish"
|
||||
duration="300"
|
||||
circular="{{false}}"
|
||||
>
|
||||
@@ -92,46 +93,75 @@
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 黄历详情 -->
|
||||
<!-- 黄历详情(紧凑布局,每行2个) -->
|
||||
<view class="card" bindtap="openZoom" data-type="detail" data-date="{{dayItem.key}}">
|
||||
<view class="detail-grid">
|
||||
<view class="detail-item">
|
||||
<view class="detail-grid-compact">
|
||||
<view class="detail-item-compact">
|
||||
<text class="detail-label">值神</text>
|
||||
<text class="detail-value">{{dayItem.almanac.duty}}</text>
|
||||
</view>
|
||||
<view class="detail-item">
|
||||
<view class="detail-item-compact">
|
||||
<text class="detail-label">黄道</text>
|
||||
<text class="detail-value">{{dayItem.almanac.twelveStar.name}}({{dayItem.almanac.twelveStar.ecliptic}})</text>
|
||||
</view>
|
||||
<view class="detail-item">
|
||||
<view class="detail-item-compact">
|
||||
<text class="detail-label">二十八宿</text>
|
||||
<text class="detail-value">{{dayItem.almanac.twentyEightStar.name}}</text>
|
||||
</view>
|
||||
<view class="detail-item">
|
||||
<view class="detail-item-compact">
|
||||
<text class="detail-label">六曜</text>
|
||||
<text class="detail-value">{{dayItem.almanac.sixStar}}</text>
|
||||
</view>
|
||||
<view class="detail-item">
|
||||
<view class="detail-item-compact">
|
||||
<text class="detail-label">冲煞</text>
|
||||
<text class="detail-value">冲{{dayItem.almanac.clash}} 煞{{dayItem.almanac.evilDirection}}</text>
|
||||
</view>
|
||||
<view class="detail-item">
|
||||
<view class="detail-item-compact">
|
||||
<text class="detail-label">彭祖百忌</text>
|
||||
<text class="detail-value">{{dayItem.almanac.pengZu}}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 节日速查 -->
|
||||
<view class="card" bindtap="openZoom" data-type="fest" data-date="{{dayItem.key}}">
|
||||
<view class="section-title">节日速查</view>
|
||||
<view class="fest-quick">
|
||||
<view class="fest-quick-item" wx:for="{{dayItem.upcoming}}" wx:key="name" catchtap="goFestival" data-date="{{item.solarDate}}">
|
||||
<!-- 节日速查(默认显示3条,点击查看更多) -->
|
||||
<view class="card">
|
||||
<view class="fest-head">
|
||||
<text class="section-title">节日速查</text>
|
||||
<text class="fest-more" bindtap="openZoom" data-type="fest-year" data-date="{{dayItem.key}}">更多 >></text>
|
||||
</view>
|
||||
<view class="fest-quick" wx:if="{{dayItem.upcoming.length > 0}}">
|
||||
<view class="fest-quick-item" wx:for="{{dayItem.upcoming.slice(0, 3)}}" wx:key="name" catchtap="goFestival" data-date="{{item.solarDate}}">
|
||||
<text class="fest-name">{{item.name}}</text>
|
||||
<text class="fest-date">{{item.solarMonth}}月{{item.solarDay}}日</text>
|
||||
<text class="fest-left {{item.daysLeft === 0 ? 'is-today' : ''}}">{{item.daysLeft === 0 ? '今天' : item.daysLeft + '天后'}}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="fest-empty" wx:else>暂无近期节日</view>
|
||||
</view>
|
||||
|
||||
<!-- 历史上的今天(数据来源:中文维基百科) -->
|
||||
<view class="card otd-card" wx:if="{{dayItem.otd}}">
|
||||
<view class="otd-head">
|
||||
<text class="section-title">历史上的今天</text>
|
||||
<view class="otd-tabs">
|
||||
<text class="otd-tab {{dayItem.otdTab === 'event' ? 'active' : ''}}" catchtap="switchOtdTab" data-date="{{dayItem.key}}" data-tab="event">大事记</text>
|
||||
<text class="otd-tab {{dayItem.otdTab === 'birth' ? 'active' : ''}}" catchtap="switchOtdTab" data-date="{{dayItem.key}}" data-tab="birth">出生</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="otd-list">
|
||||
<view class="otd-item" wx:for="{{dayItem.otdTab === 'event' ? dayItem.otd.eventsPreview : dayItem.otd.birthsPreview}}" wx:key="id" wx:for-item="ev">
|
||||
<text class="otd-year" wx:if="{{ev.yearText}}">{{ev.yearText}}</text>
|
||||
<text class="otd-text {{ev.yearText ? '' : 'no-year'}}">{{ev.content}}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="otd-foot">
|
||||
<text class="otd-source">来源:中文维基百科</text>
|
||||
<text class="otd-more" bindtap="openZoom" data-type="otd" data-date="{{dayItem.key}}">查看完整 ›</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="card placeholder-card" wx:elif="{{dayItem.otdLoading}}">
|
||||
<view class="section-title">历史上的今天</view>
|
||||
<view class="placeholder-content">加载中…</view>
|
||||
</view>
|
||||
|
||||
<!-- 回今天 -->
|
||||
@@ -162,26 +192,22 @@
|
||||
<swiper-item wx:for="{{monthList}}" wx:key="key">
|
||||
<scroll-view scroll-y class="month-scroll">
|
||||
<view class="grid grid-cols-7 month-grid">
|
||||
<view
|
||||
class="day-cell {{d.isToday ? 'today' : ''}} {{d.solarMonth !== item.month ? 'other-month' : ''}}"
|
||||
wx:for="{{item.weeks}}"
|
||||
wx:for-item="week"
|
||||
wx:key="week"
|
||||
>
|
||||
<block wx:for="{{week}}" wx:for-item="d" wx:key="solarDate">
|
||||
<view
|
||||
class="day-cell-inner {{d.isToday ? 'today' : ''}} {{d.solarMonth !== item.month ? 'other-month' : ''}}"
|
||||
bindtap="selectDay"
|
||||
data-date="{{d.solarDate}}"
|
||||
>
|
||||
<text class="day-number">{{d.solarDay}}</text>
|
||||
<text class="day-lunar {{d.lunarFestival || d.solarFestival ? 'festival' : ''}}">
|
||||
{{d.lunarFestival || d.solarFestival || d.lunarDayName}}
|
||||
</text>
|
||||
<view class="day-dot" wx:if="{{d.isTermDay}}"></view>
|
||||
</view>
|
||||
</block>
|
||||
</view>
|
||||
<block wx:for="{{item.weeks}}" wx:for-item="week" wx:key="index">
|
||||
<view
|
||||
class="day-cell-inner {{d.isToday ? 'today' : ''}} {{d.solarMonth !== item.month ? 'other-month' : ''}}"
|
||||
wx:for="{{week}}"
|
||||
wx:for-item="d"
|
||||
wx:key="solarDate"
|
||||
bindtap="selectDay"
|
||||
data-date="{{d.solarDate}}"
|
||||
>
|
||||
<text class="day-number">{{d.solarDay}}</text>
|
||||
<text class="day-lunar {{d.lunarFestival || d.solarFestival ? 'festival' : ''}}">
|
||||
{{d.lunarFestival || d.solarFestival || d.lunarDayName}}
|
||||
</text>
|
||||
<view class="day-dot" wx:if="{{d.isTermDay}}"></view>
|
||||
</view>
|
||||
</block>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</swiper-item>
|
||||
@@ -264,5 +290,46 @@
|
||||
<text class="zoom-fest-left {{item.daysLeft === 0 ? 'is-today' : ''}}">{{item.daysLeft === 0 ? '今天' : '还有' + item.daysLeft + '天'}}</text>
|
||||
</view>
|
||||
</scroll-view>
|
||||
|
||||
<!-- 今年节日(字体稍小,避免溢出) -->
|
||||
<scroll-view scroll-y class="zoom-body" wx:if="{{zoomType === 'fest-year' && zoomData}}">
|
||||
<view class="zoom-fest-year-row" wx:for="{{zoomData.yearFestivals}}" wx:key="name" bindtap="goFestival" data-date="{{item.solarDate}}">
|
||||
<text class="zoom-fest-year-name">{{item.name}}</text>
|
||||
<text class="zoom-fest-year-date">{{item.solarMonth}}月{{item.solarDay}}日</text>
|
||||
<text class="zoom-fest-year-type {{item.type === 'lunar' ? 'lunar' : 'solar'}}">{{item.type === 'lunar' ? '农历' : '公历'}}</text>
|
||||
</view>
|
||||
</scroll-view>
|
||||
|
||||
<!-- 历史上的今天放大 -->
|
||||
<scroll-view scroll-y class="zoom-body" wx:if="{{zoomType === 'otd' && zoomData}}">
|
||||
<block wx:if="{{zoomData.events.length}}">
|
||||
<view class="zoom-otd-title">大事记</view>
|
||||
<view class="zoom-otd-item" wx:for="{{zoomData.events}}" wx:key="id">
|
||||
<text class="otd-year" wx:if="{{item.yearText}}">{{item.yearText}}</text>
|
||||
<text class="otd-text {{item.yearText ? '' : 'no-year'}}">{{item.content}}</text>
|
||||
</view>
|
||||
</block>
|
||||
<block wx:if="{{zoomData.births.length}}">
|
||||
<view class="zoom-otd-title">出生</view>
|
||||
<view class="zoom-otd-item" wx:for="{{zoomData.births}}" wx:key="id">
|
||||
<text class="otd-year" wx:if="{{item.yearText}}">{{item.yearText}}</text>
|
||||
<text class="otd-text {{item.yearText ? '' : 'no-year'}}">{{item.content}}</text>
|
||||
</view>
|
||||
</block>
|
||||
<block wx:if="{{zoomData.deaths.length}}">
|
||||
<view class="zoom-otd-title">逝世</view>
|
||||
<view class="zoom-otd-item" wx:for="{{zoomData.deaths}}" wx:key="id">
|
||||
<text class="otd-year" wx:if="{{item.yearText}}">{{item.yearText}}</text>
|
||||
<text class="otd-text {{item.yearText ? '' : 'no-year'}}">{{item.content}}</text>
|
||||
</view>
|
||||
</block>
|
||||
<block wx:if="{{zoomData.festivals.length}}">
|
||||
<view class="zoom-otd-title">节假日与习俗</view>
|
||||
<view class="zoom-otd-item" wx:for="{{zoomData.festivals}}" wx:key="id">
|
||||
<text class="otd-text no-year">{{item.content}}</text>
|
||||
</view>
|
||||
</block>
|
||||
<view class="zoom-otd-source">数据来源:中文维基百科(CC BY-SA 4.0)</view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
+251
-23
@@ -191,11 +191,12 @@
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 20rpx;
|
||||
padding: 0 16rpx;
|
||||
padding: 0 8rpx; /* 减小padding让数字有更多空间 */
|
||||
min-width: 0; /* 允许内容收缩 */
|
||||
}
|
||||
|
||||
.almanac-day-num {
|
||||
font-size: 200rpx;
|
||||
font-size: 240rpx; /* 宜忌固定高度后,日历数字可以放大 */
|
||||
font-weight: 700;
|
||||
line-height: 1;
|
||||
text-align: center;
|
||||
@@ -299,14 +300,35 @@
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 8rpx;
|
||||
gap: 12rpx 8rpx; /* 行间距12rpx,列间距8rpx */
|
||||
align-content: flex-start;
|
||||
max-height: 88rpx; /* 固定2行高度:item 32rpx*2 + 行距12rpx + padding 12rpx */
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.frame-yiji-items::after {
|
||||
content: '...';
|
||||
position: absolute;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: #FFFDF8;
|
||||
padding-left: 8rpx;
|
||||
font-size: 24rpx;
|
||||
color: #9E8E7E;
|
||||
line-height: 32rpx;
|
||||
}
|
||||
|
||||
.frame-yiji-item {
|
||||
font-size: 24rpx;
|
||||
padding: 4rpx 12rpx;
|
||||
border-radius: 8rpx;
|
||||
height: 32rpx;
|
||||
line-height: 24rpx;
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.frame-yiji-item.yi { background: #FEF2F2; color: #C41E3A; }
|
||||
@@ -330,37 +352,114 @@
|
||||
color: #C41E3A;
|
||||
}
|
||||
|
||||
/* 黄历详情(label 大、value 小、加间隔) */
|
||||
.detail-grid {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 20rpx;
|
||||
/* 黄历详情(紧凑布局,每行2个) */
|
||||
.detail-grid-compact {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
gap: 12rpx;
|
||||
}
|
||||
|
||||
.detail-item {
|
||||
.detail-item-compact {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 24rpx;
|
||||
padding: 16rpx;
|
||||
gap: 12rpx;
|
||||
padding: 12rpx;
|
||||
background: #FFFBF5;
|
||||
border-radius: 12rpx;
|
||||
border-radius: 8rpx;
|
||||
}
|
||||
|
||||
.detail-label {
|
||||
font-size: 28rpx;
|
||||
.detail-item-compact .detail-label {
|
||||
font-size: 24rpx;
|
||||
color: #9E8E7E;
|
||||
min-width: 140rpx;
|
||||
min-width: 70rpx;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.detail-value {
|
||||
font-size: 28rpx;
|
||||
.detail-item-compact .detail-value {
|
||||
font-size: 24rpx;
|
||||
font-weight: 500;
|
||||
color: #1A1A1A;
|
||||
flex: 1;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
/* 节日速查 */
|
||||
/* 预留空间占位卡片 */
|
||||
.placeholder-card {
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
.placeholder-content {
|
||||
text-align: center;
|
||||
padding: 40rpx;
|
||||
color: #C9B8A6;
|
||||
font-size: 26rpx;
|
||||
}
|
||||
|
||||
/* 节日速查头部 */
|
||||
.fest-head {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 16rpx;
|
||||
}
|
||||
|
||||
.fest-head .section-title {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.fest-more {
|
||||
font-size: 26rpx;
|
||||
color: #C41E3A;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.fest-empty {
|
||||
text-align: center;
|
||||
padding: 40rpx;
|
||||
color: #C9B8A6;
|
||||
font-size: 26rpx;
|
||||
}
|
||||
|
||||
/* 今年节日弹窗(字体稍小) */
|
||||
.zoom-fest-year-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 20rpx 0;
|
||||
border-bottom: 1rpx solid #F0E6DA;
|
||||
gap: 20rpx;
|
||||
}
|
||||
|
||||
.zoom-fest-year-name {
|
||||
font-size: 32rpx;
|
||||
font-weight: 600;
|
||||
min-width: 140rpx;
|
||||
}
|
||||
|
||||
.zoom-fest-year-date {
|
||||
font-size: 28rpx;
|
||||
color: #9E8E7E;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.zoom-fest-year-type {
|
||||
font-size: 22rpx;
|
||||
padding: 4rpx 16rpx;
|
||||
border-radius: 999rpx;
|
||||
}
|
||||
|
||||
.zoom-fest-year-type.lunar {
|
||||
background: #FEF2F2;
|
||||
color: #C41E3A;
|
||||
}
|
||||
|
||||
.zoom-fest-year-type.solar {
|
||||
background: #F0F9FF;
|
||||
color: #0369A1;
|
||||
}
|
||||
|
||||
/* 节日速查列表 */
|
||||
.fest-quick {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
@@ -468,10 +567,10 @@
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 16rpx 4rpx;
|
||||
padding: 8rpx 4rpx;
|
||||
background: #FFFFFF;
|
||||
border-radius: 12rpx;
|
||||
min-height: 110rpx;
|
||||
min-height: 80rpx; /* 压缩格子高度,避免数字看起来竖向排列 */
|
||||
}
|
||||
|
||||
.day-cell-inner.today {
|
||||
@@ -488,14 +587,16 @@
|
||||
}
|
||||
|
||||
.day-number {
|
||||
font-size: 30rpx;
|
||||
font-weight: 500;
|
||||
font-size: 36rpx; /* 月历数字放大 */
|
||||
font-weight: 600;
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
.day-lunar {
|
||||
font-size: 20rpx;
|
||||
color: #9E8E7E;
|
||||
margin-top: 4rpx;
|
||||
margin-top: 2rpx;
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
.day-lunar.festival {
|
||||
@@ -663,3 +764,130 @@
|
||||
padding: 4rpx 24rpx;
|
||||
border-radius: 999rpx;
|
||||
}
|
||||
|
||||
/* ===== 历史上的今天 ===== */
|
||||
.otd-head {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 12rpx;
|
||||
}
|
||||
|
||||
.otd-head .section-title {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.otd-tabs {
|
||||
display: flex;
|
||||
gap: 8rpx;
|
||||
}
|
||||
|
||||
.otd-tab {
|
||||
font-size: 24rpx;
|
||||
padding: 4rpx 20rpx;
|
||||
border-radius: 999rpx;
|
||||
color: #9E8E7E;
|
||||
background: #F5EFE8;
|
||||
}
|
||||
|
||||
.otd-tab.active {
|
||||
color: #FFFFFF;
|
||||
background: #C41E3A;
|
||||
}
|
||||
|
||||
.otd-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12rpx;
|
||||
}
|
||||
|
||||
.otd-item {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: 12rpx;
|
||||
}
|
||||
|
||||
.otd-year {
|
||||
flex-shrink: 0;
|
||||
min-width: 104rpx;
|
||||
font-size: 24rpx;
|
||||
font-weight: 600;
|
||||
color: #C41E3A;
|
||||
}
|
||||
|
||||
.otd-text {
|
||||
flex: 1;
|
||||
font-size: 26rpx;
|
||||
color: #5A4E42;
|
||||
line-height: 1.5;
|
||||
display: -webkit-box;
|
||||
-webkit-box-orient: vertical;
|
||||
-webkit-line-clamp: 2;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.otd-text.no-year {
|
||||
margin-left: 116rpx;
|
||||
}
|
||||
|
||||
.otd-foot {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-top: 16rpx;
|
||||
padding-top: 16rpx;
|
||||
border-top: 1rpx solid #F0E8DE;
|
||||
}
|
||||
|
||||
.otd-source {
|
||||
font-size: 22rpx;
|
||||
color: #C9B8A6;
|
||||
}
|
||||
|
||||
.otd-more {
|
||||
font-size: 26rpx;
|
||||
color: #C41E3A;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
/* ===== 历史上的今天放大弹窗 ===== */
|
||||
.zoom-otd-title {
|
||||
font-size: 34rpx;
|
||||
font-weight: 600;
|
||||
color: #C41E3A;
|
||||
margin: 24rpx 0 16rpx;
|
||||
}
|
||||
|
||||
.zoom-otd-title:first-child {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.zoom-otd-item {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: 16rpx;
|
||||
padding: 12rpx 0;
|
||||
border-bottom: 1rpx solid #F0E8DE;
|
||||
}
|
||||
|
||||
.zoom-otd-item .otd-year {
|
||||
font-size: 30rpx;
|
||||
min-width: 120rpx;
|
||||
}
|
||||
|
||||
.zoom-otd-item .otd-text {
|
||||
font-size: 32rpx;
|
||||
-webkit-line-clamp: unset;
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
.zoom-otd-item .otd-text.no-year {
|
||||
margin-left: 136rpx;
|
||||
}
|
||||
|
||||
.zoom-otd-source {
|
||||
margin-top: 24rpx;
|
||||
text-align: center;
|
||||
font-size: 24rpx;
|
||||
color: #C9B8A6;
|
||||
}
|
||||
|
||||
+53
-1
@@ -1,4 +1,5 @@
|
||||
const { SOLAR_TERMS, LUNAR_FESTIVALS, SOLAR_FESTIVALS, HEAVEN_STEMS, EARTH_BRANCHES, ZODIACS } = require('../../utils/core/data.js');
|
||||
const { request } = require('../../utils/request.js');
|
||||
|
||||
const CATEGORIES = [
|
||||
{ key: 'all', name: '全部' },
|
||||
@@ -6,9 +7,16 @@ const CATEGORIES = [
|
||||
{ key: 'lunarFest', name: '农历节日' },
|
||||
{ key: 'solarFest', name: '公历节日' },
|
||||
{ key: 'ganzhi', name: '干支生肖' },
|
||||
{ key: 'termExplain', name: '黄历术语' }
|
||||
{ key: 'termExplain', name: '黄历术语' },
|
||||
{ key: 'other', name: '其他' }
|
||||
];
|
||||
|
||||
// 服务端分类 key 到展示名的映射
|
||||
const CAT_NAMES = {
|
||||
term: '节气', lunarFest: '农历节日', solarFest: '公历节日',
|
||||
ganzhi: '干支生肖', termExplain: '术语', other: '其他'
|
||||
};
|
||||
|
||||
const TERM_BRIEFS = {
|
||||
'立春': '春季开始,万物复苏', '雨水': '降雨增多,气温回升', '惊蛰': '春雷惊醒蛰伏动物',
|
||||
'春分': '昼夜平分,春季过半', '清明': '天气晴朗,草木繁茂', '谷雨': '雨生百谷,播种时节',
|
||||
@@ -112,6 +120,37 @@ Page({
|
||||
onLoad() {
|
||||
const entries = buildEntries();
|
||||
this.setData({ entries, filteredEntries: entries });
|
||||
this.loadServerEntries();
|
||||
},
|
||||
|
||||
/** 从服务端加载百科条目(管理后台可维护),与本地数据合并:同标题以服务端为准,新增条目追加 */
|
||||
loadServerEntries() {
|
||||
request({ url: '/api/wiki/entries' })
|
||||
.then(res => {
|
||||
if (!res || res.code !== 0 || !res.data || !res.data.list) return;
|
||||
const merged = [...this.data.entries];
|
||||
const titleIndex = {};
|
||||
merged.forEach((e, i) => { titleIndex[e.title] = i; });
|
||||
|
||||
res.data.list.forEach(s => {
|
||||
const entry = {
|
||||
cat: s.category,
|
||||
catName: CAT_NAMES[s.category] || s.category,
|
||||
title: s.title,
|
||||
brief: s.brief,
|
||||
content: s.content,
|
||||
open: false
|
||||
};
|
||||
if (titleIndex[s.title] !== undefined) {
|
||||
merged[titleIndex[s.title]] = entry; // 服务端内容覆盖本地
|
||||
} else {
|
||||
merged.push(entry);
|
||||
}
|
||||
});
|
||||
|
||||
this.setData({ entries: merged }, () => this.filter());
|
||||
})
|
||||
.catch(() => { /* 网络失败时使用本地内置数据 */ });
|
||||
},
|
||||
|
||||
switchCat(e) {
|
||||
@@ -134,5 +173,18 @@ Page({
|
||||
const index = e.currentTarget.dataset.index;
|
||||
const key = `filteredEntries[${index}].open`;
|
||||
this.setData({ [key]: !this.data.filteredEntries[index].open });
|
||||
},
|
||||
|
||||
onShareAppMessage() {
|
||||
return {
|
||||
title: '黄历百科 - 传统文化知识',
|
||||
path: '/pages/wiki/wiki'
|
||||
};
|
||||
},
|
||||
|
||||
onShareTimeline() {
|
||||
return {
|
||||
title: '黄历百科 - 传统文化知识'
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user