该页面由AI翻译并经过人工校对,你可能想要
查看原文
创建时间轴页面
在 /source/[lang] 目录下创建 timeline/index.md 文件,并设置内容如下。
data保存站点的更新历史,startPoint是开始点。
/source/en/timeline/index.md 的内容。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| --- title: Timeline date: 2025-09-01 16:53:19 type: "timeline" lang: en startPoint: title: Singularity content: Over the course of 14 billion years since the Big Bang, the first light elements formed, neutral atoms coalesced, the earliest stars ignited, galaxies clustered, and eventually, our solar system emerged—giving rise to life on Earth. As part of this vast evolutionary saga, we too will ultimately vanish, becoming but a fleeting chapter in the grand narrative of existence. When that day comes, what part of us will endure? What legacy of our being will persist beyond our physical demise? Will it be the knowledge we have uncovered, the art we have created, the values we have upheld, or the impact we have imprinted upon the world? Will it be the dreams we dared to dream, the lives we touched, or the changes we set in motion? In the face of inevitable oblivion, it is these contributions—our ideas, our culture, our stewardship of the Earth—that may echo through the corridors of time, long after we are gone. data: - title: Create a site using the Chic theme. date: 2023-10-23 link: 2025/07/hello-world - title: Use Oranges theme and add album. date: 2025-7-27 - title: Add 404 page and optimize search. date: 2025-7-28 - title: Use Arch theme to support language switching; Optimize Arch to support Oranges original features; add prompt to view the original text on translated pages. date: 2025-8-14 link: 2025/08/arch-theme-optimization - title: Optimize the image storage directory to reduce redundant static resources in multilingual sites; add navigation menu icons and modify some styles. date: 2025-8-31 - title: Add timeline page which supports viewing by month or year. date: 2025-9-2 ---
|
/source/zh-CN/timeline/index.md 的内容。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| --- title: 时间轴 date: 2025-09-01 16:53:19 type: "timeline" lang: zh-CN startPoint: title: 奇点 content: 自宇宙大爆炸以来的140亿年里,第一批轻元素形成,中性原子聚合,最早的恒星燃烧,星系聚集成团,最终,我们的太阳系诞生——地球上的生命由此而来。在这浩瀚的进化史诗中,我们终将消逝,成为存在宏大叙事中转瞬即逝的一章。当那一天来临,我们何物能永存?超越肉体消亡的永恒印记究竟是什么?是我们发掘的知识、创造的艺术、坚守的价值观,还是对世界留下的烙印?是敢于追逐的梦想、触动过的人生,抑或引发的变革浪潮?面对不可避免的湮没,正是这些贡献——我们的思想,我们的文化,我们对地球的守护——将在我们逝去很久之后,在时间的长河中回荡。 padZero: false data: - title: 建立站点,使用Chic主题。 date: 2023-10-23 link: 2025/07/hello-world - title: 使用Oranges主题,增加相册功能。 date: 2025-7-27 - title: 增加404页面,优化搜索功能。 date: 2025-7-28 - title: 使用Arch主题以支持切换语言;优化Arch以支持Oranges原有功能;翻译的页面增加查看原文的提示。 date: 2025-8-14 link: 2025/08/arch-theme-optimization - title: 优化图片保存目录以减少多语言站点下冗余的静态资源;添加导航菜单的icon,修改部分样式。 date: 2025-8-31 - title: 增加时间轴页面,支持按月或按年查看。 date: 2025-9-2 ---
|
设置页面布局
post.ejs
更新 /themes/arch/layout 下的文件 post.ejs 并添加如下内容。
1 2 3 4 5 6 7 8 9 10 11 12
| <!-- timeline --> <% if(page.type === 'timeline') { %> <div class="container post-details album-index"> <div class="markdown-body"> <% if (page.data && page.data.length) { %> <% let viewByYear = __('viewByYear') %> <% let viewByMonth = __('viewByMonth') %> <%- partial('_partial/timeline', { data: page.data, lang: page.lang, padZero: page.padZero, viewByYear, viewByMonth,startPoint: page.startPoint, title: page.title }) %> <% } %> </div> </div> <% } %>
|
timeline.ejs
在 /themes/arch/layout/_patial 下创建文件 timeline.ejs 并设置以下内容。
非常感谢 https://github.com/PrintNow/TimeLine 的作者。本文件的大部分内容取自该文件。以下列出了一些差异。
- time重命名为date,并且不是时间戳,而是 yyyy-m-d 格式的字符串。
- 添加 groupBy 功能(按年或月),点击可切换分组条件。
- 添加 startPoint,过多的行会折叠。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182
| <div class='albums-container'> <ul class='time-line'></ul> </div> <script type='text/javascript'> let data = <%- JSON.stringify(data || []) %>; const lang = <%- JSON.stringify(lang || 'en') %>;
const startPoint = <%- JSON.stringify(startPoint || '{}') %>;
const viewByYear = <%- JSON.stringify(viewByYear || 'View by Year') %>; const viewByMonth = <%- JSON.stringify(viewByMonth || 'View by Month') %>;
const padZero = <%- JSON.stringify(padZero || false) %>;
let groupByMonthFlag = true;
const eng_months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
data = data.sort((a, b) => new Date(b.date) - new Date(a.date));
let time_line = document.querySelector('.time-line'); time_line.dataset.lang = lang;
renderTimeline(data);
function renderTimeline(data = []) { if (!(Array.isArray(data) && data.length)) return; let grouped2DArr = groupByMonthFlag ? groupByYearMonth(data) : groupByYear(data); let html = '';
time_line.innerHTML = ''; for (let item of grouped2DArr) { html += ` <li class='tl-header group-condition' title='${groupByMonthFlag ? viewByYear : viewByMonth}'> <p class='group-condition'>${formatGroup(item[0], groupByMonthFlag)}</p> </li> <ul class='tl-body'>`;
for (let obj of item[1]) { html += `<li> <span>${formatRest(obj['date'], groupByMonthFlag)}</span> ${obj['link'] ? `<p class='title'><a href='/${lang}/${obj['link']}'>${obj['title']}</a></p>` : `<p class='title'>${obj['title']}</p>`} </li>` } html += `</ul>`;
} if (startPoint) { html += `<li class='tl-header start'> <p>${startPoint.title}</p> </li>`; html += `<ul class='tl-body start'> <li> <div class="title section"> <input class="content-check" type="checkbox" id="c1" hidden /> <div class="content"> <p class="text">${startPoint.content}</p> <label for="c1" class="btn"></label> </div> </div> </li> </ul>`; } time_line.innerHTML = html; }
time_line.addEventListener('click', toggleGroupCondition);
function toggleGroupCondition(e) { if (!e.target.classList.contains('group-condition')) { return; } groupByMonthFlag = !groupByMonthFlag; if (time_line.classList.contains('group-by-year')) { time_line.classList.remove('group-by-year'); } else { time_line.classList.add('group-by-year'); } renderTimeline(data); }
function formatGroup(dateStr, isGroupByMonth) { let date = new Date(dateStr); let year = date.getFullYear(); if (!isGroupByMonth) { if (lang === 'zh-CN') { return addLeadingZero(year, padZero) + '年'; } return year; } let month = date.getMonth() + 1;
if (lang === 'zh-CN') { year = addLeadingZero(year, padZero) + '年'; month = addLeadingZero(month, padZero) + '月'; return year + month; }
return eng_months[month - 1] + ' ' + year; }
function formatRest(dateStr, isGroupByMonth) { let date = new Date(dateStr); let day = date.getDate(); if (isGroupByMonth) { if (lang === 'zh-CN') { return addLeadingZero(day, padZero) + '日'; } return day + getOrdinalSuffix(day); }
let month = date.getMonth() + 1; if (lang === 'zh-CN') { month = addLeadingZero(month, padZero) + '月'; day = addLeadingZero(day, padZero) + '日'; return month + day; }
month = eng_months[month - 1]; day = day + getOrdinalSuffix(day); return day + ' ' + month; }
function groupByYear(data) { const groupObj = data.reduce((acc, item) => { const year = new Date(item.date).getFullYear(); if (!acc[year]) acc[year] = []; acc[year].push(item); return acc; }, {}); return Object.entries(groupObj).sort((a, b) => b[0] - a[0]); }
function groupByYearMonth(data) { const groupObj = data.reduce((acc, item) => { const date = new Date(item.date); const yearMonth = `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}`; if (!acc[yearMonth]) acc[yearMonth] = []; acc[yearMonth].push(item); return acc; }, {}); return Object.entries(groupObj).sort((a, b) => b[0].localeCompare(a[0])); }
function addLeadingZero(num, padZero) { return padZero && num < 10 ? '0' + num : num; }
function getOrdinalSuffix(day) { if (day >= 11 && day <= 13) { return 'th'; } switch (day % 10) { case 1: return 'st'; case 2: return 'nd'; case 3: return 'rd'; default: return 'th'; } }
</script> <link rel='stylesheet' href='/css/timeline.css' />
|
设置页面样式
timeline.css
在 /themes/arch/source/css 下创建文件 timeline.css 并设置以下内容。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233
| .time-line li { list-style-type: none; }
.time-line .tl-header { cursor: pointer; background-color: #23b7e5; color: white; text-align: center; display: inline-block; padding: 0 14px; border-radius: 8px; } .time-line .tl-header p { font-size: 14px; line-height: 8px; }
.time-line .tl-body { position: relative; border-left: 4px solid #23b7e5; margin-left: 40px; min-height: 88px; padding-top: 1px; padding-bottom: 1px; }
.time-line .tl-body span { position: absolute; left: -50px; top: 16px; font-weight: 400; font-size: 14px; }
.time-line .tl-body li { display: block; position: relative; left: -24px; margin: 8px 0; } .time-line .tl-body li:before { position: absolute; content: ""; top: 21px; left: -15px; width: 6px; height: 6px; background: #fff; border: 2px solid #23b7e5; border-radius: 50%; }
@media (max-width: 770px) { .time-line .tl-body li:before { left: -11px; } }
.time-line.group-by-year .tl-header:not(.start) { padding: 0 28px; }
.time-line.group-by-year[data-lang="zh-CN"] .tl-header:not(.start) { padding: 0 21px; }
.time-line[data-lang="zh-CN"]:not(.group-by-year) .tl-body:not(.start) { margin-left: 48px; }
.time-line.group-by-year .tl-body:not(.start) span { left: -76px; }
.time-line .tl-body li .title { background-color: #23b7e5; color: white; display: inline-block; margin: 8px 0; margin-left: 10px; padding: 8px 16px; border-radius: 6px; font-weight: 400; } .time-line .tl-body li .title:before { content: ""; position: absolute; left: -8px; border: 10px solid rgba(0, 0, 0, 0); border-right-color: #23b7e5; }
.time-line .tl-body li a { text-decoration: none; color: white; border-bottom: none; }
.time-line .tl-body li a:hover { border-bottom: 1px solid #fff; }
.tl-header.start { margin-top: 100px; padding: 0 8px; position: relative; left: -4px; cursor: default; }
.time-line[data-lang="zh-CN"] .tl-header.start { padding: 0 30px; }
.time-line[data-lang="zh-CN"]:not(.group-by-year) .tl-header.start { left: 4px; }
.time-line[data-lang="zh-CN"]:not(.group-by-year) .tl-body.start { margin-left: 94px; }
.tl-header.start::before { content: ""; position: absolute; top: -96px; height: 92px; border-left: 4px dotted #23b7e5; }
.tl-body.start { border: none; margin-left: 86px; margin-top: -50px; }
.tl-body.start li::before { content: none !important; } .tl-body.start .title { margin-left: 50px; }
.tl-body.start .title ::before { top: 24px !important; }
.tl-header.start > .title { font-weight: 500; }
.tl-body.start .section { display: flex !important; }
.tl-body.start .content { max-height: 68px; overflow: hidden; border-radius: 4px; padding: 0; }
.tl-body.start .content .text { box-sizing: border-box; white-space: pre-wrap; width: 100%; float: right; line-height: 1.5; margin: 0; margin-left: -100px; -webkit-mask: linear-gradient(red 30px, transparent 70px); }
.tl-body.start .content::before { content: ""; width: 100px; height: 100%; float: left; }
.tl-body.start .content .btn { float: right; width: 100px; text-align: center; position: relative; left: calc(50% - 50px); transform: translateY(-100%); cursor: pointer; }
.tl-body.start .content .btn::after { content: ""; position: relative; top: -2px; display: block; height: 16px; background-color: #fff; -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 320 512'%3E %3Cpath d='M143 352.3L7 216.3c-9.4-9.4-9.4-24.6 0-33.9l22.6-22.6c9.4-9.4 24.6-9.4 33.9 0l96.4 96.4 96.4-96.4c9.4-9.4 24.6-9.4 33.9 0l22.6 22.6c9.4 9.4 9.4 24.6 0 33.9l-136 136c-9.2 9.4-24.4 9.4-33.8 0z'%3E%3C/path%3E %3C/svg%3E") center/ 24px 24px no-repeat; }
.tl-body.start .content .btn::before { content: ""; position: absolute; left: 0; right: 0; bottom: 0; height: 16px; }
.tl-body.start .content-check:checked + .content { max-height: fit-content; padding-bottom: 16px; }
.tl-body.start .content-check:checked + .content .btn { left: auto; right: calc(50% - 50px); }
.tl-body.start .content-check:checked + .content .btn::after { transform: scaleY(-1) translateY(-18px); }
.tl-body.start .content-check:checked + .content .text { -webkit-mask: none; }
|
main.styl
更新/themes/arch/source/css下的main.styl并添加下面的内容。
1 2 3 4 5 6 7 8 9 10 11
| .time-line .tl-header, .time-line .title { background-color: $theme-color !important; }
.time-line .tl-body, .time-line li:before { border-color: $theme-color !important; }
.time-line .title:before { border-right-color: $theme-color !important; }
|
添加翻译
更新 /themes/arch/languages 下的 en.yml 和 zh-CN.yml 文件,并按如下方式设置内容。
/themes/arch/languages/en.yml 的内容。
1 2 3
| Timeline: Timeline viewByYear: View by Year viewByMonth: View by Month
|
/themes/arch/languages/zh-CN.yml 的内容。
1 2 3
| Timeline: 时间轴 viewByYear: 按年查看 viewByMonth: 按月查看
|
添加导航栏
更新 _config.arch.yml 中的navbar并添加如下内容。
1 2 3 4 5 6
| navbar: - name: Timeline enable: true path: /timeline/ key: timeline
|
截图