feat: 插件/主题管理卡片改用 Filament 原生组件+中文页面标题;注入点分三组管理(页面/侧边栏/文章);前台 sablog 主题全面语义化(header/nav/main/aside/section/footer)

This commit is contained in:
ak
2026-08-11 23:43:41 +08:00
parent ef8ec75e83
commit 489a145259
44 changed files with 131 additions and 84 deletions
+2
View File
@@ -26,6 +26,8 @@ class BlogSettings extends Page
protected static ?string $navigationLabel = '博客设置'; protected static ?string $navigationLabel = '博客设置';
protected static ?string $title = '博客设置';
protected string $view = 'filament.pages.blog-settings'; protected string $view = 'filament.pages.blog-settings';
public array $data = []; public array $data = [];
+2
View File
@@ -23,6 +23,8 @@ class Plugins extends Page
protected static ?string $navigationLabel = '插件管理'; protected static ?string $navigationLabel = '插件管理';
protected static ?string $title = '插件管理';
protected string $view = 'filament.pages.plugins'; protected string $view = 'filament.pages.plugins';
public $zip; public $zip;
+17 -6
View File
@@ -30,6 +30,8 @@ class ThemeInject extends Page
protected static ?string $navigationLabel = '主题注入'; protected static ?string $navigationLabel = '主题注入';
protected static ?string $title = '主题注入';
protected string $view = 'filament.pages.theme-inject'; protected string $view = 'filament.pages.theme-inject';
public array $data = []; public array $data = [];
@@ -44,14 +46,22 @@ class ThemeInject extends Page
public function form(Schema $schema): Schema public function form(Schema $schema): Schema
{ {
$tabs = []; $groups = [
$i = 0; '页面级' => ['head', 'header', 'content_top', 'content_bottom', 'footer', 'body_end'],
'侧边栏级' => ['sidebar_top', 'sidebar_bottom'],
'文章级' => ['post_content_top', 'post_content_bottom'],
];
foreach (ThemeInjector::POINTS as $point => $label) { $tabs = [];
$tabs[] = Tab::make(($i + 1).'. '.$label)
foreach ($groups as $groupLabel => $points) {
$sections = [];
foreach ($points as $point) {
$sections[] = \Filament\Schemas\Components\Section::make(ThemeInjector::POINTS[$point])
->description($point)
->schema([ ->schema([
Repeater::make("inject.{$point}") Repeater::make("inject.{$point}")
->label('注入块(多条拖拽排序,按顺序渲染)') ->label('注入块(多条拖拽顺序渲染)')
->schema([ ->schema([
TextInput::make('title')->label('名称')->placeholder('如:百度统计')->columnSpan(2), TextInput::make('title')->label('名称')->placeholder('如:百度统计')->columnSpan(2),
Textarea::make('html')->label('内容(HTML')->rows(3)->columnSpanFull(), Textarea::make('html')->label('内容(HTML')->rows(3)->columnSpanFull(),
@@ -64,7 +74,8 @@ class ThemeInject extends Page
->reorderableWithButtons() ->reorderableWithButtons()
->addActionLabel('添加一条'), ->addActionLabel('添加一条'),
]); ]);
$i++; }
$tabs[] = Tab::make($groupLabel)->schema($sections);
} }
$tabs[] = Tab::make('主题变量(stylevar') $tabs[] = Tab::make('主题变量(stylevar')
+2
View File
@@ -21,6 +21,8 @@ class Themes extends Page
protected static ?string $navigationLabel = '主题管理'; protected static ?string $navigationLabel = '主题管理';
protected static ?string $title = '主题管理';
protected string $view = 'filament.pages.themes'; protected string $view = 'filament.pages.themes';
public $zip; public $zip;
@@ -2,25 +2,22 @@
<x-filament::section heading="已安装插件"> <x-filament::section heading="已安装插件">
<div class="grid gap-4 sm:grid-cols-2 lg:grid-cols-3"> <div class="grid gap-4 sm:grid-cols-2 lg:grid-cols-3">
@forelse($this->plugins as $plugin) @forelse($this->plugins as $plugin)
<div class="flex flex-col rounded-xl border border-gray-200 bg-white p-5 shadow-sm dark:border-white/10 dark:bg-white/5"> <x-filament::section :compact="true">
<div class="flex flex-col">
<div class="flex items-start justify-between gap-3"> <div class="flex items-start justify-between gap-3">
<div class="flex h-11 w-11 shrink-0 items-center justify-center rounded-lg bg-primary-50 text-primary-600 dark:bg-primary-500/10"> <x-filament::icon icon="heroicon-o-puzzle-piece" class="h-8 w-8 text-primary-600" />
<x-filament::icon icon="heroicon-o-puzzle-piece" class="h-6 w-6" />
</div>
<div class="text-right">
@if($plugin['enabled']) @if($plugin['enabled'])
<x-filament::badge color="success">已启用</x-filament::badge> <x-filament::badge color="success">已启用</x-filament::badge>
@else @else
<x-filament::badge color="gray">已停用</x-filament::badge> <x-filament::badge color="gray">已停用</x-filament::badge>
@endif @endif
</div> </div>
</div>
<h3 class="mt-3 font-semibold">{{ $plugin['title'] }}</h3> <h3 class="mt-3 text-base font-semibold">{{ $plugin['title'] }}</h3>
<p class="mt-1 flex-1 text-sm text-gray-500 dark:text-gray-400">{{ $plugin['description'] }}</p> <p class="mt-1 flex-1 text-sm text-gray-500 dark:text-gray-400">{{ $plugin['description'] }}</p>
<p class="mt-1 text-xs text-gray-400">v{{ $plugin['version'] }} · {{ $plugin['vendor'] }}.{{ $plugin['name'] }}</p> <p class="mt-2 text-xs text-gray-400">v{{ $plugin['version'] }} · {{ $plugin['vendor'] }}.{{ $plugin['name'] }}</p>
<div class="mt-4 flex gap-2"> <div class="mt-4 flex flex-wrap gap-2">
@if($plugin['enabled']) @if($plugin['enabled'])
<x-filament::button size="sm" color="gray" wire:click="disablePlugin('{{ $plugin['key'] }}')">停用</x-filament::button> <x-filament::button size="sm" color="gray" wire:click="disablePlugin('{{ $plugin['key'] }}')">停用</x-filament::button>
@else @else
@@ -31,6 +28,7 @@
@endif @endif
</div> </div>
</div> </div>
</x-filament::section>
@empty @empty
<p class="text-sm text-gray-500">暂无插件</p> <p class="text-sm text-gray-500">暂无插件</p>
@endforelse @endforelse
@@ -55,17 +53,17 @@
</div> </div>
<div class="grid gap-4 sm:grid-cols-2 lg:grid-cols-3"> <div class="grid gap-4 sm:grid-cols-2 lg:grid-cols-3">
@forelse($marketItems as $index => $item) @forelse($marketItems as $index => $item)
<div class="flex flex-col rounded-xl border border-gray-200 bg-white p-5 shadow-sm dark:border-white/10 dark:bg-white/5"> <x-filament::section :compact="true">
<div class="flex h-11 w-11 items-center justify-center rounded-lg bg-primary-50 text-primary-600 dark:bg-primary-500/10"> <div class="flex flex-col">
<x-filament::icon icon="heroicon-o-cloud-arrow-down" class="h-6 w-6" /> <x-filament::icon icon="heroicon-o-cloud-arrow-down" class="h-8 w-8 text-primary-600" />
</div> <h3 class="mt-3 text-base font-semibold">{{ $item['title'] ?? $item['name'] }}</h3>
<h3 class="mt-3 font-semibold">{{ $item['title'] ?? $item['name'] }}</h3>
<p class="mt-1 flex-1 text-sm text-gray-500 dark:text-gray-400">{{ $item['description'] ?? '' }}</p> <p class="mt-1 flex-1 text-sm text-gray-500 dark:text-gray-400">{{ $item['description'] ?? '' }}</p>
<p class="mt-1 text-xs text-gray-400">{{ $item['version'] ?? '' }} · {{ $item['author'] ?? '' }}</p> <p class="mt-2 text-xs text-gray-400">{{ $item['version'] ?? '' }} · {{ $item['author'] ?? '' }}</p>
<div class="mt-4"> <div class="mt-4">
<x-filament::button size="sm" color="primary" wire:click="installFromMarket({{ $index }})">安装</x-filament::button> <x-filament::button size="sm" color="primary" wire:click="installFromMarket({{ $index }})">安装</x-filament::button>
</div> </div>
</div> </div>
</x-filament::section>
@empty @empty
<p class="text-sm text-gray-500">点击「刷新市场」拉取可用插件/主题</p> <p class="text-sm text-gray-500">点击「刷新市场」拉取可用插件/主题</p>
@endforelse @endforelse
@@ -2,9 +2,10 @@
<x-filament::section heading="主题列表"> <x-filament::section heading="主题列表">
<div class="grid gap-5 sm:grid-cols-2 lg:grid-cols-3"> <div class="grid gap-5 sm:grid-cols-2 lg:grid-cols-3">
@forelse($this->themes as $theme) @forelse($this->themes as $theme)
<div class="flex flex-col overflow-hidden rounded-xl border border-gray-200 bg-white shadow-sm dark:border-white/10 dark:bg-white/5"> <x-filament::section :compact="true" class="!p-0">
<div class="flex flex-col">
{{-- 主题预览图 --}} {{-- 主题预览图 --}}
<div class="relative aspect-[4/3] overflow-hidden bg-gray-100 dark:bg-white/5"> <div class="relative aspect-[4/3] overflow-hidden rounded-t-lg bg-gray-100 dark:bg-white/5">
@if($theme['screenshot']) @if($theme['screenshot'])
<img src="{{ theme('asset', $theme['screenshot'], $theme['name']) }}" <img src="{{ theme('asset', $theme['screenshot'], $theme['name']) }}"
alt="{{ $theme['title'] }} 预览" alt="{{ $theme['title'] }} 预览"
@@ -45,6 +46,7 @@
</div> </div>
</div> </div>
</div> </div>
</x-filament::section>
@empty @empty
<p class="text-sm text-gray-500">暂无主题</p> <p class="text-sm text-gray-500">暂无主题</p>
@endforelse @endforelse
+2 -2
View File
@@ -2,7 +2,7 @@
<body> <body>
@include('partials.header') @include('partials.header')
<div id="page"> <div id="page">
<div id="content"> <main id="content">
@if(session('success')) @if(session('success'))
<div class="alert alert-success">{{ session('success') }}</div> <div class="alert alert-success">{{ session('success') }}</div>
@endif @endif
@@ -17,7 +17,7 @@
@endforelse @endforelse
@include('partials.pagination', ['paginator' => $posts]) @include('partials.pagination', ['paginator' => $posts])
</div> </main>
@include('partials.sidebar') @include('partials.sidebar')
</div>{{-- #page --}} </div>{{-- #page --}}
@include('partials.footer') @include('partials.footer')
+2 -2
View File
@@ -5,7 +5,7 @@
<body> <body>
@include('partials.header') @include('partials.header')
<div id="page"> <div id="page">
<div id="content"> <main id="content">
<h1 class="list-title">{{ $archiveTitle ?? ($category->name ?? '文章列表') }}</h1> <h1 class="list-title">{{ $archiveTitle ?? ($category->name ?? '文章列表') }}</h1>
@forelse($posts as $post) @forelse($posts as $post)
@@ -15,7 +15,7 @@
@endforelse @endforelse
@include('partials.pagination', ['paginator' => $posts]) @include('partials.pagination', ['paginator' => $posts])
</div> </main>
@include('partials.sidebar') @include('partials.sidebar')
</div>{{-- #page --}} </div>{{-- #page --}}
@include('partials.footer') @include('partials.footer')
@@ -1,8 +1,8 @@
@stack('theme:footer') @stack('theme:footer')
<div id="footer"> <footer id="footer">
<p>&copy; {{ date('Y') }} {{ $siteName }} <p>&copy; {{ date('Y') }} {{ $siteName }}
@if($siteIcp)<a href="https://beian.miit.gov.cn/" target="_blank" rel="noopener">{{ $siteIcp }}</a>@endif @if($siteIcp)<a href="https://beian.miit.gov.cn/" target="_blank" rel="noopener">{{ $siteIcp }}</a>@endif
</p> </p>
<p>Powered by LaraLog · Laravel · Filament</p> <p>Powered by LaraLog · Laravel · Filament</p>
</div> </footer>
</div>{{-- #outmain --}} </div>{{-- #outmain --}}
@@ -1,11 +1,11 @@
<div id="outmain"> <div id="outmain">
<div id="header"> <header id="header">
<div class="logo"> <div class="logo">
<a href="{{ route('home') }}">{{ $siteName }}</a> <a href="{{ route('home') }}">{{ $siteName }}</a>
<p class="description">{{ $siteDescription }}</p> <p class="description">{{ $siteDescription }}</p>
</div> </div>
@stack('theme:header') @stack('theme:header')
<div id="nav"> <nav id="nav">
<ul> <ul>
<li class="{{ request()->routeIs('home') ? 'current_page_item' : '' }}"><a href="{{ route('home') }}">首页</a></li> <li class="{{ request()->routeIs('home') ? 'current_page_item' : '' }}"><a href="{{ route('home') }}">首页</a></li>
<li class="{{ request()->routeIs('archives*') ? 'current_page_item' : '' }}"><a href="{{ route('archives') }}">归档</a></li> <li class="{{ request()->routeIs('archives*') ? 'current_page_item' : '' }}"><a href="{{ route('archives') }}">归档</a></li>
@@ -26,5 +26,5 @@
<li><a href="{{ route('register') }}">注册</a></li> <li><a href="{{ route('register') }}">注册</a></li>
@endauth @endauth
</ul> </ul>
</div> </nav>
</div> </header>
+14 -14
View File
@@ -1,4 +1,4 @@
<div class="sidebar"> <aside class="sidebar">
@stack('theme:sidebar_top') @stack('theme:sidebar_top')
<div class="searchbox"> <div class="searchbox">
<form action="{{ route('search') }}" method="get"> <form action="{{ route('search') }}" method="get">
@@ -7,7 +7,7 @@
</form> </form>
</div> </div>
<div class="widget"> <section class="widget">
<h3>分类</h3> <h3>分类</h3>
<ul> <ul>
@forelse($categories as $category) @forelse($categories as $category)
@@ -16,9 +16,9 @@
<li>暂无分类</li> <li>暂无分类</li>
@endforelse @endforelse
</ul> </ul>
</div> </section>
<div class="widget"> <section class="widget">
<h3>最新文章</h3> <h3>最新文章</h3>
<ul> <ul>
@forelse($recentPosts as $post) @forelse($recentPosts as $post)
@@ -27,9 +27,9 @@
<li>暂无文章</li> <li>暂无文章</li>
@endforelse @endforelse
</ul> </ul>
</div> </section>
<div class="widget"> <section class="widget">
<h3>最新评论</h3> <h3>最新评论</h3>
<ul> <ul>
@forelse($recentComments as $comment) @forelse($recentComments as $comment)
@@ -38,9 +38,9 @@
<li>暂无评论</li> <li>暂无评论</li>
@endforelse @endforelse
</ul> </ul>
</div> </section>
<div class="widget"> <section class="widget">
<h3>标签</h3> <h3>标签</h3>
<p class="tagcloud"> <p class="tagcloud">
@forelse($hotTags as $tag) @forelse($hotTags as $tag)
@@ -49,9 +49,9 @@
暂无标签 暂无标签
@endforelse @endforelse
</p> </p>
</div> </section>
<div class="widget"> <section class="widget">
<h3>友情链接</h3> <h3>友情链接</h3>
<ul> <ul>
@forelse($links as $link) @forelse($links as $link)
@@ -60,15 +60,15 @@
<li>暂无链接</li> <li>暂无链接</li>
@endforelse @endforelse
</ul> </ul>
</div> </section>
<div class="widget"> <section class="widget">
<h3>统计</h3> <h3>统计</h3>
<ul> <ul>
<li>文章 {{ $blogStats['posts'] }} </li> <li>文章 {{ $blogStats['posts'] }} </li>
<li>评论 {{ $blogStats['comments'] }} </li> <li>评论 {{ $blogStats['comments'] }} </li>
<li>标签 {{ $blogStats['tags'] }} </li> <li>标签 {{ $blogStats['tags'] }} </li>
</ul> </ul>
</div> </section>
@stack('theme:sidebar_bottom') @stack('theme:sidebar_bottom')
 </aside>
+2 -2
View File
@@ -3,7 +3,7 @@
<body> <body>
@include('partials.header') @include('partials.header')
<div id="page"> <div id="page">
<div id="content"> <main id="content">
<h1 class="list-title">搜索:{{ $keyword ?: '全部' }}</h1> <h1 class="list-title">搜索:{{ $keyword ?: '全部' }}</h1>
<form action="{{ route('search') }}" method="get" class="search-form"> <form action="{{ route('search') }}" method="get" class="search-form">
<input type="search" name="q" value="{{ $keyword }}" placeholder="输入关键词搜索文章"> <input type="search" name="q" value="{{ $keyword }}" placeholder="输入关键词搜索文章">
@@ -17,7 +17,7 @@
@endforelse @endforelse
@include('partials.pagination', ['paginator' => $posts]) @include('partials.pagination', ['paginator' => $posts])
</div> </main>
@include('partials.sidebar') @include('partials.sidebar')
</div>{{-- #page --}} </div>{{-- #page --}}
@include('partials.footer') @include('partials.footer')
+2 -2
View File
@@ -7,7 +7,7 @@
<body> <body>
@include('partials.header') @include('partials.header')
<div id="page"> <div id="page">
<div id="content"> <main id="content">
@if(session('success')) @if(session('success'))
<div class="alert alert-success">{{ session('success') }}</div> <div class="alert alert-success">{{ session('success') }}</div>
@endif @endif
@@ -83,7 +83,7 @@
</form> </form>
</section> </section>
@endif @endif
</div> </main>
@include('partials.sidebar') @include('partials.sidebar')
</div>{{-- #page --}} </div>{{-- #page --}}
@include('partials.footer') @include('partials.footer')
+2 -2
View File
@@ -3,7 +3,7 @@
<body> <body>
@include('partials.header') @include('partials.header')
<div id="page"> <div id="page">
<div id="content"> <main id="content">
<h1 class="list-title">标签:{{ $tag->name }}</h1> <h1 class="list-title">标签:{{ $tag->name }}</h1>
@forelse($posts as $post) @forelse($posts as $post)
@@ -13,7 +13,7 @@
@endforelse @endforelse
@include('partials.pagination', ['paginator' => $posts]) @include('partials.pagination', ['paginator' => $posts])
</div> </main>
@include('partials.sidebar') @include('partials.sidebar')
</div>{{-- #page --}} </div>{{-- #page --}}
@include('partials.footer') @include('partials.footer')