mirror of
https://github.com/afoim/fuwari.git
synced 2026-01-31 09:03:18 +08:00
refactor: 移除灵感组件并优化文章卡片布局
- 删除灵感组件及相关数据文件 - 重构文章卡片布局,移除移动端特定样式 - 统一文章统计信息显示方式 - 简化文章卡片交互元素
This commit is contained in:
@@ -42,14 +42,14 @@ const coverWidth = "28%";
|
||||
|
||||
const { remarkPluginFrontmatter } = await entry.render();
|
||||
---
|
||||
<div class:list={["card-base flex flex-col-reverse md:flex-col w-full rounded-[var(--radius-large)] overflow-hidden relative", className]} style={style}>
|
||||
<div class:list={["pl-6 md:pl-9 pr-6 md:pr-2 pt-6 md:pt-7 pb-6 relative", {"w-full md:w-[calc(100%_-_52px_-_12px)]": !hasCover, "w-full md:w-[calc(100%_-_var(--coverWidth)_-_12px)]": hasCover}]}>
|
||||
<div class:list={["card-base flex flex-col w-full rounded-[var(--radius-large)] overflow-hidden relative", className]} style={style}>
|
||||
<div class:list={["pl-9 pr-2 pt-6 pb-6 relative", {"w-[calc(100%_-_52px_-_12px)]": !hasCover, "w-[calc(100%_-_var(--coverWidth)_-_12px)]": hasCover}]}>
|
||||
<a href={url}
|
||||
class="transition group w-full block font-bold mb-3 text-3xl text-90
|
||||
class="transition group w-full block font-bold mb-2 md:mb-3 text-xl md:text-3xl text-90
|
||||
hover:text-[var(--primary)] dark:hover:text-[var(--primary)]
|
||||
active:text-[var(--title-active)] dark:active:text-[var(--title-active)]
|
||||
before:w-1 before:h-5 before:rounded-md before:bg-[var(--primary)]
|
||||
before:absolute before:top-[35px] before:left-[18px] before:hidden md:before:block
|
||||
before:absolute before:top-[28px] md:before:top-[32px] before:left-[18px] before:block
|
||||
">
|
||||
{isPinned && (
|
||||
<span class="inline-flex items-center mr-2 px-2 py-0.5 text-sm font-medium bg-[oklch(95%_0.2_var(--hue))] dark:bg-[oklch(25%_0.2_var(--hue))] text-[oklch(55%_0.2_var(--hue))] dark:text-[oklch(85%_0.2_var(--hue))] rounded">
|
||||
@@ -57,15 +57,14 @@ const { remarkPluginFrontmatter } = await entry.render();
|
||||
</span>
|
||||
)}
|
||||
{title}
|
||||
<Icon class="inline text-[2rem] text-[var(--primary)] md:hidden translate-y-0.5 absolute" name="material-symbols:chevron-right-rounded" ></Icon>
|
||||
<Icon class="text-[var(--primary)] text-[2rem] transition hidden md:inline absolute translate-y-0.5 opacity-0 group-hover:opacity-100 -translate-x-1 group-hover:translate-x-0" name="material-symbols:chevron-right-rounded"></Icon>
|
||||
<Icon class="text-[var(--primary)] text-[2rem] transition inline absolute translate-y-0.5 opacity-0 group-hover:opacity-100 -translate-x-1 group-hover:translate-x-0" name="material-symbols:chevron-right-rounded"></Icon>
|
||||
</a>
|
||||
|
||||
<!-- metadata -->
|
||||
<PostMetadata published={published} updated={updated} tags={tags} hideTagsForMobile={true} hideUpdateDate={true} class="mb-4"></PostMetadata>
|
||||
<PostMetadata published={published} updated={updated} tags={tags} hideTagsForMobile={true} hideUpdateDate={true} slug={entry.slug} class="mb-2 md:mb-4"></PostMetadata>
|
||||
|
||||
<!-- description -->
|
||||
<div class:list={["transition text-75 mb-3.5 pr-4", {"line-clamp-2 md:line-clamp-1": !description}]}>
|
||||
<div class:list={["transition text-75 mb-3.5 pr-4 line-clamp-1 md:line-clamp-2"]}>
|
||||
{ description || remarkPluginFrontmatter.excerpt }
|
||||
</div>
|
||||
|
||||
@@ -74,25 +73,14 @@ const { remarkPluginFrontmatter } = await entry.render();
|
||||
<div>{remarkPluginFrontmatter.words} 字</div>
|
||||
<div>|</div>
|
||||
<div>{remarkPluginFrontmatter.minutes} 分钟</div>
|
||||
<div>|</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<div class="flex items-center gap-1">
|
||||
<Icon name="material-symbols:visibility-outline" class="text-base" />
|
||||
<span class="text-50 text-sm font-medium" id={`page-views-${entry.slug}`}>-</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-1">
|
||||
<Icon name="material-symbols:person" class="text-base" />
|
||||
<span class="text-50 text-sm font-medium" id={`page-visitors-${entry.slug}`}>-</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
{hasCover && <a href={url} aria-label={title}
|
||||
class:list={["group",
|
||||
"max-h-[20vh] md:max-h-none mx-4 mt-4 -mb-2 md:mb-0 md:mx-0 md:mt-0",
|
||||
"md:w-[var(--coverWidth)] relative md:absolute md:top-3 md:bottom-3 md:right-3 rounded-xl overflow-hidden active:scale-95"
|
||||
"max-h-none mx-0 mt-0",
|
||||
"w-[var(--coverWidth)] absolute top-3 bottom-3 right-3 rounded-xl overflow-hidden active:scale-95"
|
||||
]} >
|
||||
<div class="absolute pointer-events-none z-10 w-full h-full group-hover:bg-black/30 group-active:bg-black/50 transition"></div>
|
||||
<!-- 封面图上的箭头 -->
|
||||
@@ -107,7 +95,7 @@ const { remarkPluginFrontmatter } = await entry.render();
|
||||
</a>}
|
||||
|
||||
{!hasCover &&
|
||||
<a href={url} aria-label={title} class="!hidden md:!flex btn-regular w-[3.25rem]
|
||||
<a href={url} aria-label={title} class="!flex btn-regular w-[3.25rem]
|
||||
absolute right-3 top-3 bottom-3 rounded-xl bg-[var(--enter-btn-bg)]
|
||||
hover:bg-[var(--enter-btn-bg-hover)] active:bg-[var(--enter-btn-bg-active)] active:scale-95
|
||||
">
|
||||
@@ -117,77 +105,9 @@ const { remarkPluginFrontmatter } = await entry.render();
|
||||
</a>
|
||||
}
|
||||
</div>
|
||||
<div class="transition border-t-[1px] border-dashed mx-6 border-black/10 dark:border-white/[0.15] last:border-t-0 md:hidden"></div>
|
||||
<div class="transition border-t-[1px] border-dashed mx-6 border-black/10 dark:border-white/[0.15] last:border-t-0 hidden"></div>
|
||||
|
||||
<script define:vars={{ entry, umamiConfig}}>
|
||||
// 获取文章浏览量统计
|
||||
async function fetchPostCardViews(slug) {
|
||||
if (!umamiConfig.enable) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// 调用全局工具获取 Umami 分享数据
|
||||
const { websiteId, token } = await getUmamiShareData(umamiConfig.baseUrl, umamiConfig.shareId);
|
||||
|
||||
// 第二步:获取统计数据
|
||||
const currentTimestamp = Date.now();
|
||||
const statsUrl = `${umamiConfig.baseUrl}/api/websites/${websiteId}/stats?startAt=0&endAt=${currentTimestamp}&unit=hour&timezone=${encodeURIComponent(umamiConfig.timezone)}&path=eq.%2Fposts%2F${slug}%2F&compare=false`;
|
||||
|
||||
const statsResponse = await fetch(statsUrl, {
|
||||
headers: {
|
||||
'x-umami-share-token': token
|
||||
}
|
||||
});
|
||||
|
||||
if (statsResponse.status === 401) {
|
||||
clearUmamiShareCache();
|
||||
return await fetchPostCardViews(slug);
|
||||
}
|
||||
|
||||
if (!statsResponse.ok) {
|
||||
throw new Error('获取统计数据失败');
|
||||
}
|
||||
|
||||
const statsData = await statsResponse.json();
|
||||
const pageViews = statsData.pageviews || 0;
|
||||
const visits = statsData.visitors || 0;
|
||||
|
||||
const viewsElement = document.getElementById(`page-views-${slug}`);
|
||||
const visitorsElement = document.getElementById(`page-visitors-${slug}`);
|
||||
if (viewsElement) {
|
||||
viewsElement.textContent = pageViews;
|
||||
}
|
||||
if (visitorsElement) {
|
||||
visitorsElement.textContent = visits;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error fetching page views for', slug, ':', error);
|
||||
const viewsElement = document.getElementById(`page-views-${slug}`);
|
||||
const visitorsElement = document.getElementById(`page-visitors-${slug}`);
|
||||
if (viewsElement) {
|
||||
viewsElement.textContent = '-';
|
||||
}
|
||||
if (visitorsElement) {
|
||||
visitorsElement.textContent = '-';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 页面加载完成后获取统计数据
|
||||
function initPostCardStats() {
|
||||
const slug = entry.slug;
|
||||
if (slug) {
|
||||
fetchPostCardViews(slug);
|
||||
}
|
||||
}
|
||||
|
||||
if (document.readyState === 'loading') {
|
||||
document.addEventListener('DOMContentLoaded', initPostCardStats);
|
||||
} else {
|
||||
initPostCardStats();
|
||||
}
|
||||
</script>
|
||||
|
||||
<style define:vars={{coverWidth}}>
|
||||
</style>
|
||||
@@ -72,13 +72,13 @@ const className = Astro.props.class;
|
||||
<div class="meta-icon">
|
||||
<Icon name="material-symbols:visibility-outline-rounded" class="text-xl"></Icon>
|
||||
</div>
|
||||
<span class="text-50 text-sm font-medium" id="page-views-display">-</span>
|
||||
<span class="text-50 text-sm font-medium" id={`page-views-${slug}`}>-</span>
|
||||
</div>
|
||||
<div class="flex items-center">
|
||||
<div class="meta-icon">
|
||||
<Icon name="material-symbols:person" class="text-xl"></Icon>
|
||||
</div>
|
||||
<span class="text-50 text-sm font-medium" id="page-visitors-display">-</span>
|
||||
<span class="text-50 text-sm font-medium" id={`page-visitors-${slug}`}>-</span>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
@@ -119,8 +119,8 @@ const className = Astro.props.class;
|
||||
const pageViews = statsData.pageviews || 0;
|
||||
const visits = statsData.visitors || 0;
|
||||
|
||||
const viewsElement = document.getElementById('page-views-display');
|
||||
const visitorsElement = document.getElementById('page-visitors-display');
|
||||
const viewsElement = document.getElementById(`page-views-${slug}`);
|
||||
const visitorsElement = document.getElementById(`page-visitors-${slug}`);
|
||||
if (viewsElement) {
|
||||
viewsElement.textContent = pageViews;
|
||||
}
|
||||
@@ -129,8 +129,8 @@ const className = Astro.props.class;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error fetching page views:', error);
|
||||
const viewsElement = document.getElementById('page-views-display');
|
||||
const visitorsElement = document.getElementById('page-visitors-display');
|
||||
const viewsElement = document.getElementById(`page-views-${slug}`);
|
||||
const visitorsElement = document.getElementById(`page-visitors-${slug}`);
|
||||
if (viewsElement) {
|
||||
viewsElement.textContent = '-';
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ let delay = 0;
|
||||
const interval = 50;
|
||||
---
|
||||
|
||||
<div class="transition flex flex-col rounded-[var(--radius-large)] bg-transparent py-1 md:py-0 md:gap-4 mb-4">
|
||||
<div class="transition flex flex-col rounded-[var(--radius-large)] bg-transparent gap-4 mb-4">
|
||||
|
||||
{page.data.map((entry: CollectionEntry<"posts">) => (
|
||||
<PostCard
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
---
|
||||
import WidgetLayout from "./WidgetLayout.astro";
|
||||
import { Icon } from "astro-icon/components";
|
||||
import inspirationContent from "@/data/inspiration.txt?raw";
|
||||
|
||||
const items = inspirationContent.split('\n').filter(line => line.trim() !== '');
|
||||
|
||||
interface Props {
|
||||
class?: string;
|
||||
style?: string;
|
||||
}
|
||||
const className = Astro.props.class;
|
||||
const style = Astro.props.style;
|
||||
---
|
||||
|
||||
{items.length > 0 && (
|
||||
<WidgetLayout name="灵感一刻" id="inspiration-list" class={className} style={style}>
|
||||
<div class="flex flex-col gap-2">
|
||||
{items.map((item, index) => (
|
||||
<div class="flex items-start gap-2 text-sm text-neutral-700 dark:text-neutral-300">
|
||||
<span class="font-bold text-[var(--primary)] flex-shrink-0 select-none w-5 text-right">{index + 1}.</span>
|
||||
<span class="leading-5">{item}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<div class="mt-4 flex justify-center">
|
||||
<a href="https://github.com/afoim/fuwari/blob/main/src/data/inspiration.txt" target="_blank" rel="noopener noreferrer" class="btn-plain rounded-lg h-9 px-4 flex items-center gap-2 text-[var(--primary)] text-sm">
|
||||
<Icon name="material-symbols:edit-square-outline" class="text-lg" />
|
||||
记录新灵感
|
||||
</a>
|
||||
</div>
|
||||
</WidgetLayout>
|
||||
)}
|
||||
@@ -4,7 +4,6 @@ import type { MarkdownHeading } from "astro";
|
||||
import Profile from "./Profile.astro";
|
||||
import Tag from "./Tags.astro";
|
||||
import DomainSwitcher from "./DomainSwitcher.astro";
|
||||
import Inspiration from "./Inspiration.astro";
|
||||
|
||||
interface Props {
|
||||
class?: string;
|
||||
@@ -20,7 +19,6 @@ const className = Astro.props.class;
|
||||
<div id="sidebar-sticky" class="transition-all duration-700 flex flex-col w-full gap-4 top-4 sticky top-4">
|
||||
<DomainSwitcher class="onload-animation" style="animation-delay: 150ms"></DomainSwitcher>
|
||||
<Tag class="onload-animation" style="animation-delay: 200ms"></Tag>
|
||||
<Inspiration class="onload-animation" style="animation-delay: 250ms"></Inspiration>
|
||||
<!-- 赞助标 -->
|
||||
<div class="overflow-hidden flex justify-center">
|
||||
<a href="https://secbit.ai/" target="_blank" rel="noopener noreferrer">
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
教你用OBS,AV1编码录制
|
||||
教你配置Cloudflare Turnstile
|
||||
博客开发日志
|
||||
OneDrive Index搭建,随时随地存取文件
|
||||
Rybbit网站统计搭建教程
|
||||
@@ -45,7 +45,7 @@ const mainPanelTop = siteConfig.banner.enable
|
||||
|
||||
<!-- Navbar -->
|
||||
<slot slot="head" name="head"></slot>
|
||||
<div id="top-row" class="z-50 pointer-events-none relative transition-all duration-700 max-w-[var(--page-width)] px-0 md:px-4 mx-auto" class:list={[""]}>
|
||||
<div id="top-row" class="z-50 pointer-events-none relative transition-all duration-700 max-w-[var(--page-width)] px-4 md:px-4 mx-auto" class:list={[""]}>
|
||||
<div id="navbar-wrapper" class="pointer-events-auto sticky top-0 transition-all">
|
||||
<Navbar></Navbar>
|
||||
</div>
|
||||
@@ -64,7 +64,7 @@ const mainPanelTop = siteConfig.banner.enable
|
||||
<!-- The pointer-events-none here prevent blocking the click event of the TOC -->
|
||||
<div class="relative max-w-[var(--page-width)] mx-auto pointer-events-auto">
|
||||
<div id="main-grid" class="transition duration-700 w-full left-0 right-0 grid grid-cols-[17.5rem_auto] grid-rows-[auto_1fr_auto] lg:grid-rows-[auto]
|
||||
mx-auto gap-4 px-0 md:px-4"
|
||||
mx-auto gap-4 px-4 md:px-4"
|
||||
>
|
||||
<!-- Banner image credit -->
|
||||
{hasBannerCredit && <a href={siteConfig.banner.credit.url} id="banner-credit" target="_blank" rel="noopener" aria-label="Visit image source"
|
||||
|
||||
Reference in New Issue
Block a user