diff --git a/public/themes/dist_static/static/css/about-history.css b/public/themes/dist_static/static/css/about-history.css new file mode 100644 index 0000000..0c426ed --- /dev/null +++ b/public/themes/dist_static/static/css/about-history.css @@ -0,0 +1,248 @@ +.main-about-history { + --ah-sticky-top: calc(var(--header-h) + 5rem); + background: var(--color-white); + min-height: 10vh; +} + +.ah-page { + padding-top: var(--ah-sticky-top); + padding-bottom: 7.5rem; + padding-left: 18.75rem; + padding-right: 18.75rem; +} + +.ah-layout { + display: grid; + grid-template-columns: max-content 2px minmax(0, 1fr); + gap: 5rem; /* 80px:年份 / 轨道 / 内容 */ + align-items: start; +} + +.ah-nav__years { + position: sticky; + width: 12.5rem; + top: var(--ah-sticky-top); + display: flex; + flex-direction: column; + align-items: flex-end; + gap: 1rem; +} + +.ah-nav__item { + position: relative; + display: block; + color: var(--color-text-link); + font-family: "D-DIN-PRO"; + font-size: var(--font-24); + font-weight: 400; + line-height: var(--font-24); + text-decoration: none; + transition: color 0.25s ease, font-size 0.25s ease, font-weight 0.25s ease; +} + +.ah-nav__item:hover { + color: var(--color-text-muted); +} + +.ah-nav__item.is-active { + color: var(--color-text); + font-size: var(--font-64); + font-weight: 500; + line-height: 1; +} + +.ah-nav__rail { + position: sticky; + top: var(--ah-sticky-top); + align-self: start; + width: 2px; + background: rgba(46, 53, 58, 0.1); + border-radius: 1px; +} + +/* 轨道原点:始终固定在顶部 */ +.ah-nav__rail::before { + content: ""; + position: absolute; + left: 50%; + transform: translateX(-50%); + top: 0; + z-index: 2; + width: .75rem; + height: .75rem; + border-radius: 50%; + background: var(--color-primary); +} + +/* 红线:从原点向下变长,不位移 */ +.ah-nav__indicator { + position: absolute; + left: 50%; + top: 0; + width: 2px; + height: 0; + margin-left: -1px; + background: var(--color-primary); + border-radius: 1px; + transform: none; + transition: height 0.3s ease; +} + +.ah-content { + min-width: 0; +} + +.ah-block + .ah-block { + margin-top: 4.5rem; + padding-top: 1rem; +} + +.ah-block { + scroll-margin-top: calc(var(--ah-sticky-top) + 1rem); +} + +.ah-richtext { + color: #666; + font-family: "MiSans VF", "PingFang SC", sans-serif; + font-size: var(--font-18); + font-weight: 400; + line-height: var(--font-32); +} +.ah-richtext p{ + width: 100%!important; +} +.ah-richtext > *:first-child { + margin-top: 0; +} + +.ah-richtext > *:last-child { + margin-bottom: 0; +} + +.ah-richtext h1, +.ah-richtext h2, +.ah-richtext h3, +.ah-richtext h4, +.ah-richtext strong { + color: var(--color-text); + font-weight: 600; +} + +.ah-richtext h1, +.ah-richtext h2, +.ah-richtext h3, +.ah-richtext h4 { + margin: 2rem 0 1rem; + font-size: var(--font-20); + line-height: var(--font-32); +} + +.ah-richtext h1:first-child, +.ah-richtext h2:first-child, +.ah-richtext h3:first-child, +.ah-richtext h4:first-child { + margin-top: 0; +} + +.ah-richtext p { + margin: 0 0 0.75rem; +} + +.ah-richtext ul, +.ah-richtext ol { + margin: 0 0 1.5rem; + padding-left: 1.25rem; +} + +.ah-richtext li + li { + margin-top: 0.75rem; +} + +.ah-richtext li::marker { + color: #999; +} + +.ah-richtext a { + color: var(--color-primary); + text-decoration: underline; +} + +.ah-richtext img { + max-width: 100%!important; + height: auto!important; + vertical-align: middle!important; +} + +@media (max-width: 992px) { + .main-about-history { + --ah-years-h: 3.75rem; /* 移动端年份导航固定高度 */ + } + + .ah-page { + padding-top: calc(var(--header-h, 5.5rem) + 2rem); + padding-bottom: 4rem; + padding-left: var(--p-x); + padding-right: var(--p-x); + } + + .ah-layout { + grid-template-columns: 1fr; + gap: 2rem; + } + + .ah-nav__years { + position: sticky; + top: var(--header-h, 5.5rem); + z-index: 5; + box-sizing: border-box; + width: 100%; + height: var(--ah-years-h); + min-height: var(--ah-years-h); + max-height: var(--ah-years-h); + flex-direction: row; + align-items: center; + gap: 1.25rem; + overflow-x: auto; + overflow-y: hidden; + -webkit-overflow-scrolling: touch; + scrollbar-width: none; + padding: 0 var(--p-x); + background: rgba(255, 255, 255, 0.96); + backdrop-filter: blur(8px); + } + + .ah-nav__years::-webkit-scrollbar { + display: none; + } + + .ah-nav__item { + flex: 0 0 auto; + font-size: var(--font-18); + line-height: 1; + transform: scale(1); + transform-origin: center center; + transition: color 0.25s ease, transform 0.25s ease; + } + + .ah-nav__item.is-active { + color: var(--color-text); + font-size: var(--font-18); + font-weight: 500; + line-height: 1; + transform: scale(1.45); + transform-origin: center; + } + + .ah-nav__rail { + display: none; + } + + /* 移动端:头部 + 固定年份导航,单独偏移 */ + .ah-block { + scroll-margin-top: calc(var(--header-h, 5.5rem) + var(--ah-years-h) + 0.75rem); + } + + .ah-block + .ah-block { + margin-top: 3rem; + } +} diff --git a/public/themes/dist_static/static/css/about.css b/public/themes/dist_static/static/css/about.css index bee8e71..c322820 100644 --- a/public/themes/dist_static/static/css/about.css +++ b/public/themes/dist_static/static/css/about.css @@ -205,18 +205,40 @@ opacity: 1; } .ab-history__slide-title { - margin-bottom: 1.5rem; + margin: 0 0 0.75rem; color: var(--color-text); font-family: "MiSans VF", "PingFang SC", sans-serif; font-size: var(--font-18); font-weight: 500; line-height: var(--font-28); - min-height:calc(var(--font-28)*4); + min-height: calc(var(--font-28) * 4); display: -webkit-box; -webkit-line-clamp: 4; -webkit-box-orient: vertical; overflow: hidden; } +.ab-history__more { + display: inline-flex; + align-items: center; + gap: 0.5rem; + margin-bottom: 1.5rem; + color: var(--color-text-muted); + font-family: "MiSans VF", "PingFang SC", sans-serif; + font-size: var(--font-16); + font-weight: 400; + line-height: var(--font-24); + text-decoration: none; + transition: color var(--duration-fast, 0.2s) var(--easing, ease); +} +.ab-history__more svg { + display: block; + width: 1rem; + height: auto; + flex-shrink: 0; +} +.ab-history__more:hover { + color: var(--color-primary); +} /* .ab-history__slide-desc { margin-bottom: 2rem; min-height: 3rem; diff --git a/public/themes/dist_static/static/js/about-history.js b/public/themes/dist_static/static/js/about-history.js new file mode 100644 index 0000000..85b0b0f --- /dev/null +++ b/public/themes/dist_static/static/js/about-history.js @@ -0,0 +1,151 @@ +import "./common.js"; +//#region js/about-history.js +function setActiveNav() { + const pageNav = document.querySelector("[data-nav]")?.getAttribute("data-nav"); + if (!pageNav) return; + document.querySelectorAll(".site-header__nav a").forEach((link) => { + const active = link.textContent.trim().startsWith(pageNav); + link.classList.toggle("is-active", active); + }); +} + +function getHeaderOffset() { + const header = document.querySelector(".site-header"); + const h = header ? header.offsetHeight : 88; + const isMobile = window.matchMedia("(max-width: 992px)").matches; + if (isMobile) { + const years = document.querySelector(".ah-nav__years"); + const yearsH = years ? years.offsetHeight : 60; + return h + yearsH + 12; + } + return h + 24; +} + +function initHistorySpy() { + const root = document.querySelector("[data-ah-spy]"); + if (!root) return; + const navItems = Array.from(root.querySelectorAll(".ah-nav__item[data-year]")); + const blocks = Array.from(root.querySelectorAll(".ah-block[data-year]")); + const yearsWrap = root.querySelector(".ah-nav__years"); + const rail = root.querySelector(".ah-nav__rail"); + const indicator = root.querySelector(".ah-nav__indicator"); + if (!navItems.length || !blocks.length) return; + + let activeYear = navItems[0].dataset.year; + let clicking = false; + + const syncRailHeight = () => { + if (!yearsWrap || !rail) return; + rail.style.height = `${yearsWrap.offsetHeight}px`; + }; + + const updateLine = (item) => { + if (!indicator || !item || !yearsWrap) return; + syncRailHeight(); + const itemRect = item.getBoundingClientRect(); + const yearsRect = yearsWrap.getBoundingClientRect(); + // 原点固定在顶部,线条延伸到当前高亮年份底部 + const height = Math.max(itemRect.bottom - yearsRect.top, 8); + indicator.style.height = `${height}px`; + indicator.style.transform = ""; + }; + + const updateLineSoon = (item) => { + updateLine(item); + requestAnimationFrame(() => updateLine(item)); + window.setTimeout(() => updateLine(item), 320); + }; + + const setActive = (year) => { + if (!year || year === activeYear) { + const current = navItems.find((el) => el.dataset.year === activeYear); + updateLineSoon(current); + return; + } + activeYear = year; + navItems.forEach((el) => { + el.classList.toggle("is-active", el.dataset.year === year); + }); + const current = navItems.find((el) => el.dataset.year === year); + updateLineSoon(current); + if (current && current.closest(".ah-nav__years") && window.matchMedia("(max-width: 992px)").matches) { + current.scrollIntoView({ + inline: "center", + block: "nearest", + behavior: "smooth" + }); + } + }; + + const scrollToYear = (year) => { + const target = blocks.find((el) => el.dataset.year === year); + if (!target) return; + clicking = true; + setActive(year); + const top = window.scrollY + target.getBoundingClientRect().top - getHeaderOffset(); + const done = () => { + clicking = false; + }; + if (window.lenis) { + window.lenis.scrollTo(top, { + duration: 1, + onComplete: done + }); + } else { + window.scrollTo({ + top, + behavior: "smooth" + }); + window.setTimeout(done, 800); + } + }; + + navItems.forEach((item) => { + item.addEventListener("click", (event) => { + event.preventDefault(); + scrollToYear(item.dataset.year); + }); + }); + + const io = new IntersectionObserver((entries) => { + if (clicking) return; + const visible = entries.filter((e) => e.isIntersecting).sort((a, b) => b.intersectionRatio - a.intersectionRatio); + if (!visible.length) return; + const year = visible[0].target.getAttribute("data-year"); + if (year) setActive(year); + }, { + root: null, + rootMargin: `-${getHeaderOffset()}px 0px -45% 0px`, + threshold: [0.05, 0.2, 0.4, 0.6] + }); + + blocks.forEach((block) => io.observe(block)); + + const onScrollFallback = () => { + if (clicking) return; + const offset = getHeaderOffset() + 8; + let current = blocks[0]?.dataset.year; + for (const block of blocks) { + if (block.getBoundingClientRect().top <= offset) current = block.dataset.year; + } + if (current) setActive(current); + }; + + if (window.lenis) window.lenis.on("scroll", onScrollFallback); + else window.addEventListener("scroll", onScrollFallback, { passive: true }); + + setActive(activeYear); + window.addEventListener("resize", () => { + const current = navItems.find((el) => el.dataset.year === activeYear); + updateLineSoon(current); + }); + + const hashYear = (location.hash || "").replace(/^#year-/, ""); + if (hashYear && blocks.some((el) => el.dataset.year === hashYear)) { + requestAnimationFrame(() => scrollToYear(hashYear)); + } +} + +setActiveNav(); +initHistorySpy(); +//#endregion diff --git a/public/themes/website/1/en/demo/about-history.html b/public/themes/website/1/en/demo/about-history.html new file mode 100644 index 0000000..ab9ccb2 --- /dev/null +++ b/public/themes/website/1/en/demo/about-history.html @@ -0,0 +1,119 @@ + + + + + + + {$current_cate.seo_title} + + + + + + + + + + + + + + + + + + + +{include file="components/header"} + +
+ {php} + $ui_about_history = uiId('about_history'); + $seller_id = (int)($seller_id ?? 1); + $history_items = getContentByCategory($ui_about_history, $seller_id, 9999, 'sort desc', []); + if (!is_array($history_items)) { + $history_items = []; + } + usort($history_items, function ($a, $b) { + return (int)($b['sub_title'] ?? 0) - (int)($a['sub_title'] ?? 0); + }); + foreach ($history_items as &$history_item) { + if (!empty($history_item['content'])) { + $history_item['content'] = htmlspecialchars_decode($history_item['content']); + } + } + unset($history_item); + {/php} +
+
+ + +
+ {volist name="history_items" id="item"} + {php}$year = trim((string)($item['sub_title'] ?? ''));{/php} + {if $year !== ''} +
+
+ {$item.content|raw} +
+
+ {/if} + {/volist} +
+
+
+
+ + {include file="components/mask"} + +{include file="components/footer"} + + + + + + + + + + + diff --git a/public/themes/website/1/en/demo/about.html b/public/themes/website/1/en/demo/about.html index 4112a6f..a5bfcc4 100644 --- a/public/themes/website/1/en/demo/about.html +++ b/public/themes/website/1/en/demo/about.html @@ -135,7 +135,12 @@

{$case_top.title}

- + + Learn more + +
diff --git a/public/themes/website/1/zh/demo/about-history.html b/public/themes/website/1/zh/demo/about-history.html new file mode 100644 index 0000000..01cdc8d --- /dev/null +++ b/public/themes/website/1/zh/demo/about-history.html @@ -0,0 +1,119 @@ + + + + + + + {$current_cate.seo_title} + + + + + + + + + + + + + + + + + + + +{include file="components/header"} + +
+ {php} + $ui_about_history = uiId('about_history'); + $seller_id = (int)($seller_id ?? 1); + $history_items = getContentByCategory($ui_about_history, $seller_id, 9999, 'sort desc', []); + if (!is_array($history_items)) { + $history_items = []; + } + usort($history_items, function ($a, $b) { + return (int)($b['sub_title'] ?? 0) - (int)($a['sub_title'] ?? 0); + }); + foreach ($history_items as &$history_item) { + if (!empty($history_item['content'])) { + $history_item['content'] = htmlspecialchars_decode($history_item['content']); + } + } + unset($history_item); + {/php} +
+
+ + +
+ {volist name="history_items" id="item"} + {php}$year = trim((string)($item['sub_title'] ?? ''));{/php} + {if $year !== ''} +
+
+ {$item.content|raw} +
+
+ {/if} + {/volist} +
+
+
+
+ + {include file="components/mask"} + +{include file="components/footer"} + + + + + + + + + + + diff --git a/public/themes/website/1/zh/demo/about.html b/public/themes/website/1/zh/demo/about.html index 81e716c..3fca20c 100644 --- a/public/themes/website/1/zh/demo/about.html +++ b/public/themes/website/1/zh/demo/about.html @@ -135,7 +135,12 @@

{$case_top.title}

- + + 了解更多 + +