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:
@@ -133,6 +133,35 @@ class Post extends Model implements HasMedia
|
||||
return $cover->getUrl($conversion) ?: $cover->getUrl();
|
||||
}
|
||||
|
||||
/**
|
||||
* 封面回退链:显式封面 → 附件/正文里的第一张图。
|
||||
* 都没有时返回 null,由调用方决定兜底(占位图或不显示)。
|
||||
*/
|
||||
public function coverOrFirstImageUrl(): ?string
|
||||
{
|
||||
if ($url = $this->coverImageUrl()) {
|
||||
return $url;
|
||||
}
|
||||
|
||||
// 附件里的第一张图片
|
||||
$attachment = $this->getMedia('attachments')
|
||||
->first(fn ($m) => str_starts_with((string) $m->mime_type, 'image/'));
|
||||
if ($attachment) {
|
||||
return $attachment->getUrl();
|
||||
}
|
||||
|
||||
// 正文 HTML/Markdown 里的第一张图片地址
|
||||
$content = (string) $this->content;
|
||||
if (preg_match('/<img[^>]+src=["\']([^"\']+)["\']/i', $content, $m)) {
|
||||
return $m[1];
|
||||
}
|
||||
if (preg_match('/!\[[^\]]*\]\((https?:\/\/[^)\s]+)[^)]*\)/i', $content, $m)) {
|
||||
return $m[1];
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public function comments(): HasMany
|
||||
{
|
||||
return $this->hasMany(Comment::class);
|
||||
|
||||
Reference in New Issue
Block a user