From 20e101bb65a296d08bf24c6f99e417f094aa589b Mon Sep 17 00:00:00 2001 From: hzm <934585316@qq.com> Date: Mon, 2 Mar 2026 08:18:54 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E5=89=8D10=E9=87=8D=E4=BB=93=E8=82=A1?= =?UTF-8?q?=E7=A5=A8=E5=88=A4=E6=96=AD=E8=8E=B7=E5=8F=96=E5=88=B0=E7=9A=84?= =?UTF-8?q?=E6=95=B0=E6=8D=AE=E6=98=AF=E5=90=A6=E4=B8=BA=E4=B8=8A=E4=B8=80?= =?UTF-8?q?=E5=AD=A3=E5=BA=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/api/fund.js | 80 +++++++++++++++++++++++++++++++++++--- app/page.jsx | 101 ++++++++++++++++++++++++------------------------ 2 files changed, 126 insertions(+), 55 deletions(-) diff --git a/app/api/fund.js b/app/api/fund.js index 1208bfc..44ad915 100644 --- a/app/api/fund.js +++ b/app/api/fund.js @@ -126,6 +126,55 @@ const parseLatestNetValueFromLsjzContent = (content) => { return null; }; +const extractHoldingsReportDate = (html) => { + if (!html) return null; + + // 优先匹配带有“报告期 / 截止日期”等关键字附近的日期 + const m1 = html.match(/(报告期|截止日期)[^0-9]{0,20}(\d{4}-\d{2}-\d{2})/); + if (m1) return m1[2]; + + // 兜底:取文中出现的第一个 yyyy-MM-dd 格式日期 + const m2 = html.match(/(\d{4}-\d{2}-\d{2})/); + return m2 ? m2[1] : null; +}; + +const isLastQuarterReport = (reportDateStr) => { + if (!reportDateStr) return false; + + const report = dayjs(reportDateStr, 'YYYY-MM-DD'); + if (!report.isValid()) return false; + + const now = nowInTz(); + const m = now.month(); // 0-11 + const q = Math.floor(m / 3); // 当前季度 0-3 => Q1-Q4 + + let lastQ; + let year; + if (q === 0) { + // 当前为 Q1,则上一季度是上一年的 Q4 + lastQ = 3; + year = now.year() - 1; + } else { + lastQ = q - 1; + year = now.year(); + } + + const quarterEnds = [ + { month: 2, day: 31 }, // Q1 -> 03-31 + { month: 5, day: 30 }, // Q2 -> 06-30 + { month: 8, day: 30 }, // Q3 -> 09-30 + { month: 11, day: 31 } // Q4 -> 12-31 + ]; + + const { month: endMonth, day: endDay } = quarterEnds[lastQ]; + const lastQuarterEnd = dayjs( + `${year}-${String(endMonth + 1).padStart(2, '0')}-${endDay}`, + 'YYYY-MM-DD' + ); + + return report.isSame(lastQuarterEnd, 'day'); +}; + export const fetchSmartFundNetValue = async (code, startDate) => { const today = nowInTz().startOf('day'); let current = toTz(startDate).startOf('day'); @@ -199,7 +248,9 @@ export const fetchFundDataFallback = async (c) => { gszzl: null, zzl: Number.isFinite(latest.growth) ? latest.growth : null, noValuation: true, - holdings: [] + holdings: [], + holdingsReportDate: null, + holdingsIsLastQuarter: false }); } else { reject(new Error('未能获取到基金数据')); @@ -258,6 +309,15 @@ export const fetchFundData = async (c) => { loadScript(holdingsUrl).then(async (apidata) => { let holdings = []; const html = apidata?.content || ''; + const holdingsReportDate = extractHoldingsReportDate(html); + const holdingsIsLastQuarter = isLastQuarterReport(holdingsReportDate); + + // 如果不是上一季度末的披露数据,则不展示重仓(并避免继续解析/请求行情) + if (!holdingsIsLastQuarter) { + resolveH({ holdings: [], holdingsReportDate, holdingsIsLastQuarter: false }); + return; + } + const headerRow = (html.match(/[\s\S]*?<\/thead>/i) || [])[0] || ''; const headerCells = (headerRow.match(/([\s\S]*?)<\/th>/gi) || []).map(th => th.replace(/<[^>]*>/g, '').trim()); let idxCode = -1, idxName = -1, idxWeight = -1; @@ -354,10 +414,15 @@ export const fetchFundData = async (c) => { } catch (e) { } } - resolveH(holdings); - }).catch(() => resolveH([])); + resolveH({ holdings, holdingsReportDate, holdingsIsLastQuarter }); + }).catch(() => resolveH({ holdings: [], holdingsReportDate: null, holdingsIsLastQuarter: false })); }); - Promise.all([lsjzPromise, holdingsPromise]).then(([tData, holdings]) => { + Promise.all([lsjzPromise, holdingsPromise]).then(([tData, holdingsResult]) => { + const { + holdings, + holdingsReportDate, + holdingsIsLastQuarter + } = holdingsResult || {}; if (tData) { if (tData.jzrq && (!gzData.jzrq || tData.jzrq >= gzData.jzrq)) { gzData.dwjz = tData.dwjz; @@ -365,7 +430,12 @@ export const fetchFundData = async (c) => { gzData.zzl = tData.zzl; } } - resolve({ ...gzData, holdings }); + resolve({ + ...gzData, + holdings, + holdingsReportDate, + holdingsIsLastQuarter + }); }); }; scriptGz.onerror = () => { diff --git a/app/page.jsx b/app/page.jsx index 5f363ea..6688abd 100644 --- a/app/page.jsx +++ b/app/page.jsx @@ -4162,7 +4162,8 @@ export default function HomePage() { if (shouldHideChange) return null; - const changeLabel = hasTodayData ? '涨跌幅' : (isYesterdayChange ? '昨日涨跌幅' : (isPreviousTradingDay ? '上一交易日涨跌幅' : '涨跌幅')); + // 不再区分“上一交易日涨跌幅”名称,统一使用“昨日涨跌幅” + const changeLabel = hasTodayData ? '涨跌幅' : '昨日涨跌幅'; return ( ); })()} -
toggleCollapse(f.code)} - > -
-
- 前10重仓股票 - -
- 涨跌幅 / 占比 -
-
- - {!collapsedCodes.has(f.code) && ( - 0 && ( + <> +
toggleCollapse(f.code)} > - {Array.isArray(f.holdings) && f.holdings.length ? ( -
- {f.holdings.map((h, idx) => ( -
- {h.name} -
- {isNumber(h.change) && ( - 0 ? 'up' : h.change < 0 ? 'down' : ''}`} style={{ marginRight: 8 }}> - {h.change > 0 ? '+' : ''}{h.change.toFixed(2)}% - - )} - {h.weight} -
-
- ))} +
+
+ 前10重仓股票 +
- ) : ( -
暂无重仓数据
+ 涨跌幅 / 占比 +
+
+ + {!collapsedCodes.has(f.code) && ( + +
+ {f.holdings.map((h, idx) => ( +
+ {h.name} +
+ {isNumber(h.change) && ( + 0 ? 'up' : h.change < 0 ? 'down' : ''}`} style={{ marginRight: 8 }}> + {h.change > 0 ? '+' : ''}{h.change.toFixed(2)}% + + )} + {h.weight} +
+
+ ))} +
+
)} - - )} -
+ + + )}