fix: 按UTC日期优化独立站日历筛选
2026-08-15:日历默认视图按当前 UTC 日期过滤与排序,未来和当天节点优先,已过节点降权;显式月份仍保留完整历史。日历页隐藏重复目标区域筛选,平台渲染模块独立拆分,更新静态资源版本号与测试契约,避免 Gitea/IIS/Nginx 部署命中旧缓存。
This commit is contained in:
@@ -543,6 +543,28 @@ function formatMonthDay(value) {
|
||||
};
|
||||
}
|
||||
|
||||
export function calendarTodayIso(now = new Date()) {
|
||||
return now.toISOString().slice(0, 10);
|
||||
}
|
||||
|
||||
export function sortCalendarItems(items, now = new Date()) {
|
||||
const today = calendarTodayIso(now);
|
||||
return [...items].sort((a, b) => {
|
||||
const dateA = String(a.start_date || "").slice(0, 10);
|
||||
const dateB = String(b.start_date || "").slice(0, 10);
|
||||
const pastOrder = Number(dateA < today) - Number(dateB < today);
|
||||
if (pastOrder) return pastOrder;
|
||||
const dateOrder = dateA < today && dateB < today ? dateB.localeCompare(dateA) : dateA.localeCompare(dateB);
|
||||
return dateOrder || Number(b.priority_score || 0) - Number(a.priority_score || 0);
|
||||
});
|
||||
}
|
||||
|
||||
export function calendarDefaultItems(items, year, now = new Date()) {
|
||||
const today = calendarTodayIso(now);
|
||||
if (Number(year) !== Number(today.slice(0, 4))) return [...items];
|
||||
return items.filter((item) => String(item.start_date || "").slice(0, 10) >= today);
|
||||
}
|
||||
|
||||
function compactList(values, limit = 5) {
|
||||
const unique = [...new Set((values || []).filter(Boolean))];
|
||||
if (unique.length <= limit) return unique.join("、") || "暂无专项信息";
|
||||
@@ -902,23 +924,25 @@ function renderCalendar(data, container) {
|
||||
decisionToolbar.appendChild(create("p", "calendar-platform-filter-hint", TEXT.calendar.platformFilterHint));
|
||||
container.appendChild(decisionToolbar);
|
||||
|
||||
const today = calendarTodayIso();
|
||||
const selectedMarketItems = all.filter((item) => {
|
||||
if (state.calendarMarketGroup && !(item.affected_regions || []).includes(state.calendarMarketGroup)) return false;
|
||||
if (state.calendarCountry && !(item.affected_countries || []).includes(state.calendarCountry)) return false;
|
||||
if (state.calendarProduct && !calendarProductCategories(item).includes(state.calendarProduct)) return false;
|
||||
if (state.calendarPlatform && !calendarPlatformFamilies(item).includes(state.calendarPlatform)) return false;
|
||||
if (state.calendarFocus === "opportunity" && !isCalendarOpportunity(item, data.as_of_date)) return false;
|
||||
if (state.calendarFocus === "opportunity" && !isCalendarOpportunity(item, today)) return false;
|
||||
return true;
|
||||
});
|
||||
const yearItems = selectedMarketItems.filter((item) => Number(String(item.start_date || "").slice(0, 4)) === state.calendarYear);
|
||||
const displayYearItems = state.calendarMonth ? yearItems : calendarDefaultItems(yearItems, state.calendarYear);
|
||||
const monthCounts = new Map();
|
||||
yearItems.forEach((item) => {
|
||||
displayYearItems.forEach((item) => {
|
||||
const month = Number(String(item.start_date || "").slice(5, 7));
|
||||
monthCounts.set(month, (monthCounts.get(month) || 0) + 1);
|
||||
});
|
||||
const monthRail = create("div", "calendar-month-rail");
|
||||
const allButton = create("button", `calendar-month-btn${state.calendarMonth === 0 ? " active" : ""}`);
|
||||
allButton.append(create("strong", "", "全年"), create("span", "calendar-month-count", `${yearItems.length} 条`));
|
||||
allButton.append(create("strong", "", "全年"), create("span", "calendar-month-count", `${displayYearItems.length} 条`));
|
||||
allButton.type = "button";
|
||||
allButton.addEventListener("click", () => {
|
||||
state.calendarMonth = 0;
|
||||
@@ -942,12 +966,11 @@ function renderCalendar(data, container) {
|
||||
"back-to-school", "singles-day", "black-friday", "cyber-monday", "winter-holiday",
|
||||
"christmas", "lunar-new-year", "eid-fitr", "super-bowl", "la28",
|
||||
];
|
||||
const highValueItems = yearItems
|
||||
.filter((item) => item.business_relevance === "high" && isCalendarOpportunity(item, data.as_of_date))
|
||||
.sort((a, b) => String(a.start_date).localeCompare(String(b.start_date)));
|
||||
const selected = yearItems
|
||||
const highValueItems = displayYearItems
|
||||
.filter((item) => item.business_relevance === "high" && isCalendarOpportunity(item, today));
|
||||
const selected = sortCalendarItems(displayYearItems
|
||||
.filter((item) => !state.calendarMonth || Number(String(item.start_date || "").slice(5, 7)) === state.calendarMonth)
|
||||
.sort((a, b) => String(a.start_date).localeCompare(String(b.start_date)) || Number(b.priority_score || 0) - Number(a.priority_score || 0));
|
||||
, new Date());
|
||||
if (state.calendarMonth) container.appendChild(renderCalendarMonthGrid(state.calendarYear, state.calendarMonth, selected));
|
||||
const highlightPool = state.calendarMonth
|
||||
? highValueItems.filter((item) => Number(String(item.start_date).slice(5, 7)) === state.calendarMonth)
|
||||
@@ -996,91 +1019,4 @@ function renderCalendar(data, container) {
|
||||
});
|
||||
}
|
||||
|
||||
function makePlatformCard(item) {
|
||||
const card = create("article", "platform-card");
|
||||
const header = create("div", "platform-card-header");
|
||||
const logoBox = create("div", "platform-logo-box");
|
||||
const logo = create("img", "platform-logo");
|
||||
logo.src = `./assets/platform-logos/${item.logo}.svg`;
|
||||
logo.alt = `${item.name} 标志`;
|
||||
logo.width = 112;
|
||||
logo.height = 48;
|
||||
logoBox.appendChild(logo);
|
||||
const title = create("div", "platform-title");
|
||||
title.append(create("span", "", `${item.region} · ${item.type}`), create("h3", "", item.name));
|
||||
header.append(logoBox, title, makeBadge(`${item.access_level} ${item.access_label}`, `platform-access is-${String(item.access_level).toLowerCase()}`));
|
||||
const markets = create("div", "platform-markets");
|
||||
appendTags(markets, item.markets || []);
|
||||
const fit = create("div", "platform-fit");
|
||||
fit.append(create("strong", "", "适合谁"), create("p", "", compactList(item.fit_sellers || [])));
|
||||
const category = create("div", "platform-category");
|
||||
category.append(create("strong", "", "家纺经营重点"), create("p", "", item.textile_category || "打开平台类目核对。"));
|
||||
const access = create("div", "platform-access-note");
|
||||
access.append(create("strong", "", "入驻判断"), create("p", "", item.cross_border || "以平台当前规则为准。"));
|
||||
const actions = create("div", "platform-actions");
|
||||
if (item.homepage_url && item.homepage_url !== item.seller_url) actions.appendChild(makePlatformActionGroup("打开平台官网", item.homepage_url, item, "homepage"));
|
||||
actions.appendChild(makePlatformActionGroup("去官方入驻", item.seller_url, item, "seller", "platform-action primary"));
|
||||
if (item.language_url) actions.appendChild(makeExternalLink("官网学习中心(可选中文)", item.language_url, "platform-action language"));
|
||||
const proof = create("p", "platform-proof", `${item.confidence} · ${item.link_status || "待人工复核"} · 核验 ${String(item.checked_at || "").slice(0, 10)}`);
|
||||
card.append(header, markets, category, access, fit, actions, proof);
|
||||
return card;
|
||||
}
|
||||
|
||||
function renderPlatforms(data, container) {
|
||||
const all = (data.platforms || []).filter(matchesFilters);
|
||||
const toolbar = create("section", "platform-filter-toolbar");
|
||||
const makeField = (label, control, extraClass = "") => {
|
||||
const field = create("label", `platform-filter-field ${extraClass}`.trim());
|
||||
field.append(create("span", "", label), control);
|
||||
return field;
|
||||
};
|
||||
const makeSelect = (placeholder, values, current) => {
|
||||
const select = create("select", "platform-filter-control");
|
||||
select.appendChild(Object.assign(create("option", "", placeholder), { value: "" }));
|
||||
values.forEach((value) => select.appendChild(Object.assign(create("option", "", value), { value })));
|
||||
select.value = current;
|
||||
return select;
|
||||
};
|
||||
const markets = [...new Set(all.flatMap((item) => item.markets || []))].sort((a, b) => a.localeCompare(b, "zh-CN"));
|
||||
const types = [...new Set(all.map((item) => item.type).filter(Boolean))].sort((a, b) => a.localeCompare(b, "zh-CN"));
|
||||
const marketSelect = makeSelect("全部市场", markets, state.platformMarket);
|
||||
const typeSelect = makeSelect("全部类型", types, state.platformType);
|
||||
const accessSelect = makeSelect("全部开放等级", ["A", "B", "C"].filter((level) => all.some((item) => item.access_level === level)), state.platformAccess);
|
||||
const search = create("input", "platform-filter-control");
|
||||
search.type = "search";
|
||||
search.name = "platform-channel-search";
|
||||
search.autocomplete = "off";
|
||||
search.placeholder = "搜索平台名称、国家或家纺类目…";
|
||||
search.value = state.platformSearch;
|
||||
toolbar.append(
|
||||
makeField("目标市场", marketSelect),
|
||||
makeField("渠道类型", typeSelect),
|
||||
makeField("开放等级", accessSelect),
|
||||
makeField("平台检索", search, "platform-filter-search"),
|
||||
);
|
||||
const header = create("section", "scene-summary platform-summary");
|
||||
const grid = create("div", "platform-grid");
|
||||
const apply = () => {
|
||||
const keyword = normalized(state.platformSearch);
|
||||
const selected = all.filter((item) => {
|
||||
if (state.platformMarket && !(item.markets || []).includes(state.platformMarket)) return false;
|
||||
if (state.platformType && item.type !== state.platformType) return false;
|
||||
if (state.platformAccess && item.access_level !== state.platformAccess) return false;
|
||||
if (!keyword) return true;
|
||||
return normalized([item.name, item.region, item.type, item.textile_category, item.cross_border, ...(item.markets || []), ...(item.fit_sellers || [])].join(" ")).includes(keyword);
|
||||
}).sort((a, b) => (Number(b.home_textile_sales_score || 0) + Number(b.emergence_score || 0)) - (Number(a.home_textile_sales_score || 0) + Number(a.emergence_score || 0)) || String(a.access_level).localeCompare(String(b.access_level)) || a.name.localeCompare(b.name, "zh-CN"));
|
||||
header.innerHTML = "";
|
||||
header.append(create("strong", "", `${selected.length}/${all.length} 个家纺销售渠道`), create("span", "", "准入条件以平台卖家中心为准"));
|
||||
grid.innerHTML = "";
|
||||
selected.forEach((item) => grid.appendChild(makePlatformCard(item)));
|
||||
if (!selected.length) grid.appendChild(create("div", "intelligence-empty", "当前筛选条件下没有已核验平台,请调整市场或开放等级。"));
|
||||
};
|
||||
marketSelect.addEventListener("change", () => { state.platformMarket = marketSelect.value; apply(); });
|
||||
typeSelect.addEventListener("change", () => { state.platformType = typeSelect.value; apply(); });
|
||||
accessSelect.addEventListener("change", () => { state.platformAccess = accessSelect.value; apply(); });
|
||||
search.addEventListener("input", () => { state.platformSearch = search.value; apply(); });
|
||||
container.append(toolbar, header, grid);
|
||||
apply();
|
||||
}
|
||||
|
||||
export { TEXT, create, openLightbox, sectionItems, trendItems, normalized, matchesFilters, formatDate, formatVerified, sectionOf, makeVisual, makeBadge, makeExternalLink, sourceLinkLabel, currentTranslationProvider, preferredTranslation, detectViewerCountry, cultureReferenceUrl, appendTranslationLinks, appendPrefixedTranslationLink, makePlatformActionGroup, itemShareUrl, makeShareButton, makeCard, appendTags, formatMonthDay, compactList, calendarProductCategories, escapeIcs, nextDateValue, makeCalendarDownloadButton, calendarScopeText, makeCalendarFact, makeLocalHolidayNames, makeCalendarDateVariants, countryFlag, isCalendarOpportunity, makeCalendarCard, makeCalendarSelect, renderCalendarMonthGrid, renderCalendar, makePlatformCard, renderPlatforms };
|
||||
export { TEXT, create, openLightbox, sectionItems, trendItems, normalized, matchesFilters, formatDate, formatVerified, sectionOf, makeVisual, makeBadge, makeExternalLink, sourceLinkLabel, currentTranslationProvider, preferredTranslation, detectViewerCountry, cultureReferenceUrl, appendTranslationLinks, appendPrefixedTranslationLink, makePlatformActionGroup, itemShareUrl, makeShareButton, makeCard, appendTags, formatMonthDay, compactList, calendarProductCategories, escapeIcs, nextDateValue, makeCalendarDownloadButton, calendarScopeText, makeCalendarFact, makeLocalHolidayNames, makeCalendarDateVariants, countryFlag, isCalendarOpportunity, makeCalendarCard, makeCalendarSelect, renderCalendarMonthGrid, renderCalendar };
|
||||
|
||||
Reference in New Issue
Block a user