| ---
|
| import MainGridLayout from "@layouts/MainGridLayout.astro";
|
| import { getSortedPosts } from "@utils/content-utils";
|
| import { formatDateToYYYYMMDD } from "@utils/date-utils";
|
| import { Icon } from "astro-icon/components";
|
| import I18nKey from "@/i18n/i18nKey";
|
| import { i18n } from "@/i18n/translation";
|
|
|
| const posts = await getSortedPosts();
|
| const recentPosts = posts.slice(0, 6);
|
| ---
|
|
|
| <MainGridLayout title={i18n(I18nKey.rss)} description={i18n(I18nKey.rssDescription)}>
|
| <div class="onload-animation">
|
|
|
| <div class="card-base rounded-(--radius-large) p-8 mb-6">
|
| <div class="text-center">
|
| <div class="inline-flex items-center justify-center w-16 h-16 bg-(--primary) rounded-2xl mb-4">
|
| <Icon name="material-symbols:rss-feed" class="text-white text-3xl" />
|
| </div>
|
| <h1 class="text-3xl font-bold text-(--primary) mb-3">{i18n(I18nKey.rss)}</h1>
|
| <p class="text-75 max-w-2xl mx-auto">
|
| {i18n(I18nKey.rssSubtitle)}
|
| </p>
|
| </div>
|
| </div>
|
|
|
|
|
| <div class="card-base rounded-(--radius-large) p-6 mb-6">
|
| <div class="flex flex-col md:flex-row md:items-center md:justify-between gap-4">
|
| <div class="flex items-center">
|
| <div class="w-12 h-12 bg-(--primary) rounded-xl flex items-center justify-center mr-4">
|
| <Icon name="material-symbols:link" class="text-white text-xl" />
|
| </div>
|
| <div>
|
| <h3 class="font-semibold text-90 mb-1">{i18n(I18nKey.rssLink)}</h3>
|
| <p class="text-sm text-75">{i18n(I18nKey.rssCopyToReader)}</p>
|
| </div>
|
| </div>
|
| <div class="flex flex-col sm:flex-row items-stretch sm:items-center gap-3">
|
| <code class="bg-(--card-bg) px-3 py-2 rounded-lg text-sm font-mono text-75 border border-(--line-divider) break-all">
|
| {Astro.site}rss.xml
|
| </code>
|
| <button
|
| id="copy-rss-btn"
|
| class="px-4 py-2 bg-(--primary) text-white rounded-lg hover:opacity-80 transition-all duration-200 font-medium text-sm whitespace-nowrap"
|
| data-url={`${Astro.site}rss.xml`}
|
| data-copied-text={i18n(I18nKey.rssCopied)}
|
| data-failed-text={i18n(I18nKey.rssCopyFailed)}
|
| >
|
| {i18n(I18nKey.rssCopyLink)}
|
| </button>
|
| </div>
|
| </div>
|
| </div>
|
|
|
| <!-- 最新文章预览 -->
|
| <div class="card-base rounded-(--radius-large) p-6 mb-6">
|
| <h2 class="text-xl font-bold text-90 mb-4 flex items-center">
|
| <Icon name="material-symbols:article" class="mr-2 text-(--primary)" />
|
| {i18n(I18nKey.rssLatestPosts)}
|
| </h2>
|
| <div class="space-y-4">
|
| {recentPosts.map((post) => (
|
| <article class="bg-(--card-bg) rounded-xl p-4 border border-(--line-divider) hover:border-(--primary) transition-all duration-300">
|
| <h3 class="text-lg font-semibold text-90 mb-2 hover:text-(--primary) transition-colors">
|
| <a href={`/posts/${post.id}/`} class="hover:underline">
|
| {post.data.title}
|
| </a>
|
| </h3>
|
| {post.data.description && (
|
| <p class="text-75 mb-3 line-clamp-2">
|
| {post.data.description}
|
| </p>
|
| )}
|
| <div class="flex items-center gap-4 text-sm text-60">
|
| <time datetime={post.data.published.toISOString()} class="text-75">
|
| {formatDateToYYYYMMDD(post.data.published)}
|
| </time>
|
| </div>
|
| </article>
|
| ))}
|
| </div>
|
| </div>
|
|
|
| <!-- RSS 说明 -->
|
| <div class="card-base rounded-(--radius-large) p-6">
|
| <h2 class="text-xl font-bold text-90 mb-4 flex items-center">
|
| <Icon name="material-symbols:help-outline" class="mr-2 text-(--primary)" />
|
| {i18n(I18nKey.rssWhatIsRSS)}
|
| </h2>
|
| <div class="text-75 space-y-3">
|
| <p>
|
| {i18n(I18nKey.rssWhatIsRSSDescription)}
|
| </p>
|
| <ul class="list-disc list-inside space-y-1 ml-4">
|
| <li>{i18n(I18nKey.rssBenefit1)}</li>
|
| <li>{i18n(I18nKey.rssBenefit2)}</li>
|
| <li>{i18n(I18nKey.rssBenefit3)}</li>
|
| <li>{i18n(I18nKey.rssBenefit4)}</li>
|
| </ul>
|
| <p class="text-sm">
|
| {i18n(I18nKey.rssHowToUse)}
|
| </p>
|
| </div>
|
| </div>
|
| </div>
|
|
|
| <script>
|
| function initRssCopy() {
|
| const copyBtn = document.getElementById('copy-rss-btn');
|
| if (!copyBtn) return;
|
|
|
| // 移除旧的事件监听器(通过克隆节点的方式)
|
| const newBtn = copyBtn.cloneNode(true);
|
| copyBtn.parentNode?.replaceChild(newBtn, copyBtn);
|
|
|
| // 绑定新的事件监听器
|
| newBtn.addEventListener('click', async function() {
|
| const url = this.getAttribute('data-url');
|
| if (!url) return;
|
| try {
|
| await navigator.clipboard.writeText(url);
|
| const originalText = this.textContent;
|
| this.textContent = this.getAttribute('data-copied-text') || '';
|
| this.style.backgroundColor = 'var(--success-color, #10b981)';
|
|
|
| setTimeout(() => {
|
| this.textContent = originalText;
|
| this.style.backgroundColor = '';
|
| }, 2000);
|
| } catch (err) {
|
| console.error('复制失败:', err);
|
| const originalText = this.textContent;
|
| this.textContent = this.getAttribute('data-failed-text') || '';
|
| setTimeout(() => {
|
| this.textContent = originalText;
|
| }, 2000);
|
| }
|
| });
|
| }
|
|
|
|
|
| if (document.readyState === 'loading') {
|
| document.addEventListener('DOMContentLoaded', initRssCopy);
|
| } else {
|
| // DOM 已加载,直接初始化
|
| setTimeout(initRssCopy, 0);
|
| }
|
|
|
|
|
| if (typeof window !== 'undefined' && (window as any).swup) {
|
| (window as any).swup.hooks.on('content:replace', () => {
|
| // 延迟执行以确保 DOM 已更新
|
| setTimeout(initRssCopy, 100);
|
| });
|
| }
|
| </script>
|
| </MainGridLayout> |