feat: topbar legacy-sync button, card view mode with cover fallback, year/month/day archives, richer page cache admin, stylevar repeater editor
Build and Deploy / verify (push) Successful in 32s
Build and Deploy / deploy (push) Successful in 17s

- 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:
2026-10-13 09:01:46 +00:00
parent 2d8752f863
commit 16dac8b568
24 changed files with 608 additions and 136 deletions
+8 -2
View File
@@ -14,8 +14,14 @@ class ArchiveController extends Controller
{
$posts = Post::published()->orderByDesc('published_at')->get(['id', 'title', 'slug', 'published_at']);
$archives = $posts->groupBy(fn (Post $post) => $post->published_at->format('Y'))
->map(fn ($yearPosts) => $yearPosts->groupBy(fn (Post $post) => $post->published_at->format('m')));
// 年 → 月(1-12) → 日 → 文章,用于按年/月/日索引
$archives = [];
foreach ($posts as $post) {
$year = $post->published_at->format('Y');
$month = (int) $post->published_at->format('m');
$day = (int) $post->published_at->format('d');
$archives[$year][$month][$day][] = $post;
}
return theme_view('archives', compact('archives'));
}
+4 -1
View File
@@ -15,6 +15,8 @@ class HomeViewMode
public const FULL = 'full';
public const CARD = 'card';
public const COOKIE = 'll_home_view';
/**
@@ -25,12 +27,13 @@ class HomeViewMode
return [
self::LIST => '列表',
self::FULL => '标准',
self::CARD => '图文',
];
}
public static function isValid(mixed $value): bool
{
return is_string($value) && in_array($value, [self::LIST, self::FULL], true);
return is_string($value) && in_array($value, [self::LIST, self::FULL, self::CARD], true);
}
public static function resolve(?Request $request = null): string
+9 -4
View File
@@ -41,11 +41,12 @@ class PageCacheMiddleware
$response = $next($request);
if ($response->getStatusCode() === 200) {
$body = $response->getContent();
Cache::put($key, [
'body' => $response->getContent(),
'body' => $body,
'type' => $response->headers->get('Content-Type') ?: 'text/html; charset=utf-8',
], $ttl);
self::rememberKey($key, $request->fullUrl());
self::rememberKey($key, $request->fullUrl(), strlen($body), $ttl);
}
return $response;
@@ -89,7 +90,7 @@ class PageCacheMiddleware
}
/**
* @return list<array{key: string, url: string, cached_at: string}>
* @return list<array{key: string, url: string, cached_at: string, size: int, expires_at: string}>
*/
public static function entries(): array
{
@@ -100,6 +101,8 @@ class PageCacheMiddleware
'key' => (string) $key,
'url' => is_array($meta) ? (string) ($meta['url'] ?? $key) : (string) $key,
'cached_at' => is_array($meta) ? (string) ($meta['cached_at'] ?? '') : '',
'size' => is_array($meta) ? (int) ($meta['size'] ?? 0) : 0,
'expires_at' => is_array($meta) ? (string) ($meta['expires_at'] ?? '') : '',
];
}
@@ -122,7 +125,7 @@ class PageCacheMiddleware
Cache::put($key, (int) Cache::get($key, 0) + 1, now()->addDay());
}
private static function rememberKey(string $key, ?string $url = null): void
private static function rememberKey(string $key, ?string $url = null, int $size = 0, int $ttl = 0): void
{
$keys = (array) Cache::get(self::TAG.'_keys', []);
$previous = $keys[$key] ?? [];
@@ -130,6 +133,8 @@ class PageCacheMiddleware
$keys[$key] = [
'url' => $url ?: (is_array($previous) ? (string) ($previous['url'] ?? '') : ''),
'cached_at' => now()->toDateTimeString(),
'size' => $size,
'expires_at' => $ttl > 0 ? now()->addSeconds($ttl)->toDateTimeString() : '',
];
Cache::put(self::TAG.'_keys', $keys, now()->addDay());