feat: topbar legacy-sync button, card view mode with cover fallback, year/month/day archives, richer page cache admin, stylevar repeater editor
- topbar: Livewire LegacySyncButton next to 写文章 (moved from user menu) - view modes: add 图文(card) grid with cover fallback chain (cover → attachment → first content image → placeholder); list mode now title-only compact - archives: year/month/day index grid - page cache admin: size + expiry columns, total size, cache policy note - theme inject: stylevars switch from KeyValue to Repeater with multi-line HTML editing - import: html_entity_decode for category/post-title/link/keywords/comment author
This commit is contained in:
@@ -8,20 +8,32 @@
|
||||
@include('partials.theme-inject', ['point' => 'content_top'])
|
||||
|
||||
@forelse($archives as $year => $months)
|
||||
<h2 class="archive-year">{{ $year }} 年</h2>
|
||||
<ul class="archive-list">
|
||||
@foreach($months as $month => $monthPosts)
|
||||
<li>
|
||||
<a wire:navigate href="{{ route('archives.month', $year.$month) }}">{{ (int) $month }} 月</a>
|
||||
<span class="count">({{ $monthPosts->count() }} 篇)</span>
|
||||
<ul class="archive-posts">
|
||||
@foreach($monthPosts as $post)
|
||||
<li><a wire:navigate href="{{ route('posts.show', $post->id) }}">{{ $post->title }}</a> <time>{{ $post->published_at->format(blog_setting('listtime', 'm-d')) }}</time></li>
|
||||
@endforeach
|
||||
</ul>
|
||||
</li>
|
||||
@endforeach
|
||||
</ul>
|
||||
<section class="archive-year-block">
|
||||
<h2 class="archive-year">{{ $year }} 年</h2>
|
||||
<div class="archive-months">
|
||||
@foreach(range(1, 12) as $month)
|
||||
@php $days = $months[$month] ?? []; @endphp
|
||||
<div class="archive-month {{ count($days) ? 'has-posts' : 'is-empty' }}">
|
||||
@if(count($days))
|
||||
<a wire:navigate class="archive-month-title" href="{{ route('archives.month', $year.str_pad((string) $month, 2, '0', STR_PAD_LEFT)) }}">
|
||||
{{ $month }} 月 <span class="count">({{ collect($days)->flatten(1)->count() }})</span>
|
||||
</a>
|
||||
<div class="archive-days">
|
||||
@foreach($days as $day => $dayPosts)
|
||||
@if(count($dayPosts) === 1)
|
||||
<a wire:navigate href="{{ route('posts.show', $dayPosts[0]->id) }}" class="archive-day" title="{{ $dayPosts[0]->title }}">{{ $day }}</a>
|
||||
@else
|
||||
<a wire:navigate href="{{ route('archives.month', $year.str_pad((string) $month, 2, '0', STR_PAD_LEFT)) }}" class="archive-day" title="{{ implode('、', array_map(fn ($p) => $p->title, $dayPosts)) }}">{{ $day }}</a>
|
||||
@endif
|
||||
@endforeach
|
||||
</div>
|
||||
@else
|
||||
<span class="archive-month-title">{{ $month }} 月</span>
|
||||
@endif
|
||||
</div>
|
||||
@endforeach
|
||||
</div>
|
||||
</section>
|
||||
@empty
|
||||
<div class="empty">暂无归档</div>
|
||||
@endforelse
|
||||
|
||||
@@ -31,4 +31,6 @@
|
||||
>
|
||||
写文章
|
||||
</x-filament::button>
|
||||
|
||||
<livewire:legacy-sync-button />
|
||||
</div>
|
||||
|
||||
@@ -31,7 +31,9 @@
|
||||
<thead>
|
||||
<tr class="border-b border-gray-200 text-left dark:border-white/10">
|
||||
<th class="py-2 pr-4 font-medium">地址</th>
|
||||
<th class="py-2 pr-4 font-medium">大小</th>
|
||||
<th class="py-2 pr-4 font-medium">缓存时间</th>
|
||||
<th class="py-2 pr-4 font-medium">过期时间</th>
|
||||
<th class="py-2 font-medium">操作</th>
|
||||
</tr>
|
||||
</thead>
|
||||
@@ -43,7 +45,9 @@
|
||||
{{ $entry['url'] }}
|
||||
</a>
|
||||
</td>
|
||||
<td class="py-2 pr-4 whitespace-nowrap">{{ $entry['size'] ? number_format($entry['size'] / 1024, 1).' KB' : '—' }}</td>
|
||||
<td class="py-2 pr-4 whitespace-nowrap">{{ $entry['cached_at'] ?: '—' }}</td>
|
||||
<td class="py-2 pr-4 whitespace-nowrap">{{ $entry['expires_at'] ?: '—' }}</td>
|
||||
<td class="py-2">
|
||||
<x-filament::button
|
||||
size="xs"
|
||||
@@ -58,6 +62,10 @@
|
||||
@endforeach
|
||||
</tbody>
|
||||
</table>
|
||||
<p class="mt-3 text-xs text-gray-500 dark:text-gray-400">
|
||||
共 {{ count($this->entries) }} 条,合计 {{ number_format(collect($this->entries)->sum('size') / 1024 / 1024, 2) }} MB。
|
||||
缓存说明:只缓存未登录访客 GET 的 200 页面;登录用户、评论提交等动态请求不缓存;浏览量统计不受影响。
|
||||
</p>
|
||||
</div>
|
||||
@endif
|
||||
</x-filament::section>
|
||||
|
||||
@@ -14,11 +14,13 @@
|
||||
@include('partials.view-mode-switcher')
|
||||
@include('partials.theme-inject', ['point' => 'content_top'])
|
||||
|
||||
@forelse($posts as $post)
|
||||
@include('partials.post-card', ['post' => $post])
|
||||
@empty
|
||||
<div class="empty">暂无文章</div>
|
||||
@endforelse
|
||||
<div class="post-list post-list-{{ $homeViewMode ?? 'list' }}">
|
||||
@forelse($posts as $post)
|
||||
@include('partials.post-card', ['post' => $post])
|
||||
@empty
|
||||
<div class="empty">暂无文章</div>
|
||||
@endforelse
|
||||
</div>
|
||||
|
||||
@include('partials.pagination', ['paginator' => $posts])
|
||||
@include('partials.theme-inject', ['point' => 'content_bottom'])
|
||||
|
||||
@@ -27,11 +27,13 @@
|
||||
@include('partials.view-mode-switcher')
|
||||
@include('partials.theme-inject', ['point' => 'content_top'])
|
||||
|
||||
@forelse($posts as $post)
|
||||
@include('partials.post-card', ['post' => $post])
|
||||
@empty
|
||||
<div class="empty">该分类暂无文章</div>
|
||||
@endforelse
|
||||
<div class="post-list post-list-{{ $homeViewMode ?? 'list' }}">
|
||||
@forelse($posts as $post)
|
||||
@include('partials.post-card', ['post' => $post])
|
||||
@empty
|
||||
<div class="empty">该分类暂无文章</div>
|
||||
@endforelse
|
||||
</div>
|
||||
|
||||
@include('partials.pagination', ['paginator' => $posts])
|
||||
@include('partials.theme-inject', ['point' => 'content_bottom'])
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
<div>
|
||||
<x-filament::button
|
||||
size="sm"
|
||||
color="info"
|
||||
icon="heroicon-o-arrow-path"
|
||||
wire:click="run"
|
||||
wire:loading.attr="disabled"
|
||||
wire:confirm="将从旧 sablog 库增量导入文章/评论/标签等(幂等,不会产生重复),约需 1 分钟。确定开始?"
|
||||
>
|
||||
<span wire:loading.remove wire:target="run">同步旧站</span>
|
||||
<span wire:loading wire:target="run">同步中…</span>
|
||||
</x-filament::button>
|
||||
</div>
|
||||
@@ -4,38 +4,78 @@
|
||||
&& blank($post->read_password)
|
||||
&& empty(($post->meta ?? [])['members_only']);
|
||||
@endphp
|
||||
<article class="post-card {{ $post->isPinned() ? 'sticky' : '' }} post-card-{{ $viewMode }}">
|
||||
@if($url = $post->coverImageUrl())
|
||||
@if($viewMode === \App\Blog\Support\HomeViewMode::CARD)
|
||||
<article class="post-card post-card-card {{ $post->isPinned() ? 'sticky' : '' }}">
|
||||
<a wire:navigate href="{{ route('posts.show', $post->id) }}" class="post-cover">
|
||||
<img src="{{ $url }}" alt="{{ $post->title }}" loading="lazy">
|
||||
@if($url = $post->coverOrFirstImageUrl())
|
||||
<img src="{{ $url }}" alt="{{ $post->title }}" loading="lazy">
|
||||
@else
|
||||
<span class="post-cover-placeholder">{{ \Illuminate\Support\Str::limit($post->title, 24, '') }}</span>
|
||||
@endif
|
||||
</a>
|
||||
@endif
|
||||
<h2 class="post-title">
|
||||
@if($label = $post->stickyLabel())<span class="badge">{{ $label }}</span>@endif
|
||||
<a wire:navigate href="{{ route('posts.show', $post->id) }}">{{ $post->title }}</a>
|
||||
</h2>
|
||||
<div class="post-meta">
|
||||
<time datetime="{{ $post->published_at->toIso8601String() }}">{{ $post->published_at->format(blog_setting('normaltime', 'Y-m-d H:i')) }}</time>
|
||||
@if($post->category)
|
||||
<span>·</span>
|
||||
<a wire:navigate href="{{ route('category.show', $post->category->id) }}">{{ $post->category->name }}</a>
|
||||
<div class="post-card-body">
|
||||
<h2 class="post-title">
|
||||
@if($label = $post->stickyLabel())<span class="badge">{{ $label }}</span>@endif
|
||||
<a wire:navigate href="{{ route('posts.show', $post->id) }}">{{ $post->title }}</a>
|
||||
</h2>
|
||||
<div class="post-excerpt">{{ \Illuminate\Support\Str::limit($post->excerpt_or_fallback, 80) }}</div>
|
||||
<div class="post-meta">
|
||||
<time datetime="{{ $post->published_at->toIso8601String() }}">{{ $post->published_at->format('Y-m-d') }}</time>
|
||||
@if($post->category)
|
||||
<span>·</span>
|
||||
<a wire:navigate href="{{ route('category.show', $post->category->id) }}">{{ $post->category->name }}</a>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
@elseif($viewMode === \App\Blog\Support\HomeViewMode::LIST)
|
||||
<article class="post-card post-card-list {{ $post->isPinned() ? 'sticky' : '' }}">
|
||||
<h2 class="post-title">
|
||||
@if($label = $post->stickyLabel())<span class="badge">{{ $label }}</span>@endif
|
||||
<a wire:navigate href="{{ route('posts.show', $post->id) }}">{{ $post->title }}</a>
|
||||
</h2>
|
||||
<div class="post-meta">
|
||||
<time datetime="{{ $post->published_at->toIso8601String() }}">{{ $post->published_at->format(blog_setting('listtime', 'Y-m-d')) }}</time>
|
||||
@if($post->category)
|
||||
<span>·</span>
|
||||
<a wire:navigate href="{{ route('category.show', $post->category->id) }}">{{ $post->category->name }}</a>
|
||||
@endif
|
||||
</div>
|
||||
</article>
|
||||
@else
|
||||
<article class="post-card {{ $post->isPinned() ? 'sticky' : '' }} post-card-{{ $viewMode }}">
|
||||
@if($url = $post->coverImageUrl())
|
||||
<a wire:navigate href="{{ route('posts.show', $post->id) }}" class="post-cover">
|
||||
<img src="{{ $url }}" alt="{{ $post->title }}" loading="lazy">
|
||||
</a>
|
||||
@endif
|
||||
<span>· 阅读 {{ $post->views }}</span>
|
||||
<span>· 评论 {{ $post->comment_count }}</span>
|
||||
</div>
|
||||
@if($showFull)
|
||||
<div class="post-content">
|
||||
{!! app(\App\Blog\Services\PostContentRenderer::class)->render($post) !!}
|
||||
<h2 class="post-title">
|
||||
@if($label = $post->stickyLabel())<span class="badge">{{ $label }}</span>@endif
|
||||
<a wire:navigate href="{{ route('posts.show', $post->id) }}">{{ $post->title }}</a>
|
||||
</h2>
|
||||
<div class="post-meta">
|
||||
<time datetime="{{ $post->published_at->toIso8601String() }}">{{ $post->published_at->format(blog_setting('normaltime', 'Y-m-d H:i')) }}</time>
|
||||
@if($post->category)
|
||||
<span>·</span>
|
||||
<a wire:navigate href="{{ route('category.show', $post->category->id) }}">{{ $post->category->name }}</a>
|
||||
@endif
|
||||
<span>· 阅读 {{ $post->views }}</span>
|
||||
<span>· 评论 {{ $post->comment_count }}</span>
|
||||
</div>
|
||||
@else
|
||||
<div class="post-excerpt">
|
||||
{{ \Illuminate\Support\Str::limit($post->excerpt_or_fallback, 200) }}
|
||||
@if($showFull)
|
||||
<div class="post-content">
|
||||
{!! app(\App\Blog\Services\PostContentRenderer::class)->render($post) !!}
|
||||
</div>
|
||||
@else
|
||||
<div class="post-excerpt">
|
||||
{{ \Illuminate\Support\Str::limit($post->excerpt_or_fallback, 200) }}
|
||||
</div>
|
||||
@endif
|
||||
<div class="post-footer">
|
||||
<a wire:navigate href="{{ route('posts.show', $post->id) }}" class="read-more">阅读全文 »</a>
|
||||
@foreach($post->tags as $tag)
|
||||
<a wire:navigate href="{{ route('tags.show', $tag->name) }}" class="tag-link">#{{ $tag->name }}</a>
|
||||
@endforeach
|
||||
</div>
|
||||
@endif
|
||||
<div class="post-footer">
|
||||
<a wire:navigate href="{{ route('posts.show', $post->id) }}" class="read-more">阅读全文 »</a>
|
||||
@foreach($post->tags as $tag)
|
||||
<a wire:navigate href="{{ route('tags.show', $tag->name) }}" class="tag-link">#{{ $tag->name }}</a>
|
||||
@endforeach
|
||||
</div>
|
||||
</article>
|
||||
</article>
|
||||
@endif
|
||||
|
||||
@@ -6,4 +6,6 @@
|
||||
<a href="{{ \App\Blog\Support\HomeViewMode::url(\App\Blog\Support\HomeViewMode::FULL) }}" class="{{ $viewMode === \App\Blog\Support\HomeViewMode::FULL ? 'active' : '' }}">标准</a>
|
||||
<span>|</span>
|
||||
<a href="{{ \App\Blog\Support\HomeViewMode::url(\App\Blog\Support\HomeViewMode::LIST) }}" class="{{ $viewMode === \App\Blog\Support\HomeViewMode::LIST ? 'active' : '' }}">列表</a>
|
||||
<span>|</span>
|
||||
<a href="{{ \App\Blog\Support\HomeViewMode::url(\App\Blog\Support\HomeViewMode::CARD) }}" class="{{ $viewMode === \App\Blog\Support\HomeViewMode::CARD ? 'active' : '' }}">图文</a>
|
||||
</div>
|
||||
|
||||
@@ -13,11 +13,13 @@
|
||||
@include('partials.view-mode-switcher')
|
||||
@include('partials.theme-inject', ['point' => 'content_top'])
|
||||
|
||||
@forelse($posts as $post)
|
||||
@include('partials.post-card', ['post' => $post])
|
||||
@empty
|
||||
<div class="empty">没有找到相关文章</div>
|
||||
@endforelse
|
||||
<div class="post-list post-list-{{ $homeViewMode ?? 'list' }}">
|
||||
@forelse($posts as $post)
|
||||
@include('partials.post-card', ['post' => $post])
|
||||
@empty
|
||||
<div class="empty">没有找到相关文章</div>
|
||||
@endforelse
|
||||
</div>
|
||||
|
||||
@include('partials.pagination', ['paginator' => $posts])
|
||||
@include('partials.theme-inject', ['point' => 'content_bottom'])
|
||||
|
||||
@@ -9,11 +9,13 @@
|
||||
@include('partials.view-mode-switcher')
|
||||
@include('partials.theme-inject', ['point' => 'content_top'])
|
||||
|
||||
@forelse($posts as $post)
|
||||
@include('partials.post-card', ['post' => $post])
|
||||
@empty
|
||||
<div class="empty">该标签暂无文章</div>
|
||||
@endforelse
|
||||
<div class="post-list post-list-{{ $homeViewMode ?? 'list' }}">
|
||||
@forelse($posts as $post)
|
||||
@include('partials.post-card', ['post' => $post])
|
||||
@empty
|
||||
<div class="empty">该标签暂无文章</div>
|
||||
@endforelse
|
||||
</div>
|
||||
|
||||
@include('partials.pagination', ['paginator' => $posts])
|
||||
@include('partials.theme-inject', ['point' => 'content_bottom'])
|
||||
|
||||
Reference in New Issue
Block a user