feat: add admin env badge, quick post button, and harden plugin enable
Show local/prod in the Filament topbar, jump to create post in one click, tighten sidebar spacing, and stop Hello World from 500ing when its tables are missing. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -45,7 +45,11 @@ class PluginManagerServiceProvider extends ServiceProvider
|
||||
$provider = $manager->resolveProvider($key);
|
||||
|
||||
if ($provider && class_exists($provider)) {
|
||||
$provider::bootPlugin($manager);
|
||||
try {
|
||||
$provider::bootPlugin($manager);
|
||||
} catch (\Throwable $e) {
|
||||
report($e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,6 +49,7 @@ class PostContentRenderer
|
||||
|
||||
// 插件钩子:付费内容过滤、AI 处理等
|
||||
$html = app(\App\Blog\Support\PluginManager::class)->applyFilters('post.rendered', $html, $post);
|
||||
$html = is_string($html) ? $html : '';
|
||||
|
||||
// 兜底:任何情况下不把裸 [paid] 标签输出到页面(如会员插件被停用时)
|
||||
return preg_replace('/\[(\/?paid)\]/i', '', $html);
|
||||
|
||||
@@ -6,6 +6,7 @@ declare(strict_types=1);
|
||||
namespace App\Blog\Support;
|
||||
|
||||
use App\Models\PluginRecord;
|
||||
use Illuminate\Support\Facades\Artisan;
|
||||
use Illuminate\Support\Facades\File;
|
||||
use Illuminate\Support\Str;
|
||||
use RuntimeException;
|
||||
@@ -155,6 +156,8 @@ class PluginManager
|
||||
throw new RuntimeException('无法启用 '.$plugin.':'.implode(';', $problems));
|
||||
}
|
||||
|
||||
$this->migratePlugin($plugin);
|
||||
|
||||
[$vendor, $name] = array_pad(explode('.', $plugin), 2, $plugin);
|
||||
PluginRecord::query()->updateOrCreate(
|
||||
['vendor' => $vendor, 'name' => $name],
|
||||
@@ -162,6 +165,29 @@ class PluginManager
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Run the plugin's own migrations so enabling a demo/third-party plugin
|
||||
* does not 500 on missing tables.
|
||||
*/
|
||||
public function migratePlugin(string $plugin): void
|
||||
{
|
||||
$relativePath = 'plugins/'.$plugin.'/database/migrations';
|
||||
|
||||
if (! is_dir(base_path($relativePath))) {
|
||||
return;
|
||||
}
|
||||
|
||||
$exitCode = Artisan::call('migrate', [
|
||||
'--path' => $relativePath,
|
||||
'--force' => true,
|
||||
'--no-interaction' => true,
|
||||
]);
|
||||
|
||||
if ($exitCode !== 0) {
|
||||
throw new RuntimeException('插件迁移失败:'.$plugin.'。'.trim(Artisan::output()));
|
||||
}
|
||||
}
|
||||
|
||||
public function disable(string $plugin): void
|
||||
{
|
||||
$dependents = $this->dependents($plugin);
|
||||
@@ -243,7 +269,11 @@ class PluginManager
|
||||
{
|
||||
foreach ($this->actions[$hook] ?? [] as $callbacks) {
|
||||
foreach ($callbacks as $callback) {
|
||||
$callback(...$args);
|
||||
try {
|
||||
$callback(...$args);
|
||||
} catch (\Throwable $e) {
|
||||
report($e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -252,7 +282,11 @@ class PluginManager
|
||||
{
|
||||
foreach ($this->filters[$hook] ?? [] as $callbacks) {
|
||||
foreach ($callbacks as $callback) {
|
||||
$value = $callback($value, ...$args);
|
||||
try {
|
||||
$value = $callback($value, ...$args);
|
||||
} catch (\Throwable $e) {
|
||||
report($e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Filament\Support;
|
||||
|
||||
/**
|
||||
* Admin top-right environment badge: local stays local, production shows as prod.
|
||||
*/
|
||||
class AdminEnvironment
|
||||
{
|
||||
public static function name(): string
|
||||
{
|
||||
$environment = app()->environment();
|
||||
|
||||
return match ($environment) {
|
||||
'production', 'prod' => 'prod',
|
||||
default => $environment,
|
||||
};
|
||||
}
|
||||
|
||||
public static function color(): string
|
||||
{
|
||||
return match (app()->environment()) {
|
||||
'production', 'prod' => 'danger',
|
||||
'local' => 'gray',
|
||||
'staging' => 'warning',
|
||||
default => 'info',
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -62,13 +62,21 @@ class AppServiceProvider extends ServiceProvider
|
||||
PanelsRenderHook::STYLES_AFTER,
|
||||
fn () => Blade::render(<<<'HTML'
|
||||
<style>
|
||||
.fi-sidebar { padding-inline: 0.75rem; }
|
||||
.fi-sidebar-nav { padding-inline: 0; }
|
||||
.fi-sidebar-nav-groups { margin-inline: 0; }
|
||||
.fi-sidebar-nav .fi-sidebar-item-button { padding-inline: 0.6rem; border-radius: 0.5rem; }
|
||||
.fi-sidebar-nav .fi-sidebar-item-label { font-size: 0.82rem; }
|
||||
.fi-sidebar-nav .fi-sidebar-group-label { font-size: 0.68rem; letter-spacing: 0.04em; }
|
||||
.fi-sidebar-brand { padding-block: 0.9rem; }
|
||||
.ll-topbar-tools { display: flex; align-items: center; gap: 0.45rem; margin-inline-end: 0.15rem; }
|
||||
.ll-env-badge { text-transform: uppercase; letter-spacing: 0.06em; font-weight: 700; }
|
||||
|
||||
.fi-sidebar { padding-inline: 0.65rem; }
|
||||
.fi-sidebar-nav { padding-inline: 0; padding-block: 0.4rem !important; gap: 0.35rem !important; }
|
||||
.fi-sidebar-nav-groups { margin-inline: 0; gap: 0.35rem !important; }
|
||||
.fi-sidebar-group { gap: 0.08rem !important; }
|
||||
.fi-sidebar-group-items { gap: 0.04rem !important; }
|
||||
.fi-sidebar-nav .fi-sidebar-item-btn,
|
||||
.fi-sidebar-nav .fi-sidebar-item-button { padding-inline: 0.55rem; padding-block: 0.26rem !important; min-height: 0 !important; border-radius: 0.45rem; }
|
||||
.fi-sidebar-nav .fi-sidebar-group-btn { padding-block: 0.12rem !important; padding-inline: 0.55rem; }
|
||||
.fi-sidebar-nav .fi-sidebar-item-label { font-size: 0.82rem; line-height: 1.2; }
|
||||
.fi-sidebar-nav .fi-sidebar-group-label { font-size: 0.66rem; letter-spacing: 0.04em; line-height: 1.2; }
|
||||
.fi-sidebar-brand { padding-block: 0.5rem; }
|
||||
.fi-sidebar-header { height: auto !important; min-height: 2.6rem; }
|
||||
|
||||
/* 表格行紧凑:数据行高度由 .fi-ta-text / .fi-ta-icon 的 padding-block 驱动;操作列按钮禁止换行堆叠 */
|
||||
.fi-ta-text, .fi-ta-icon { padding-block: calc(var(--spacing) * 1.75) !important; }
|
||||
|
||||
@@ -21,6 +21,7 @@ use Filament\Pages\Dashboard;
|
||||
use Filament\Panel;
|
||||
use Filament\PanelProvider;
|
||||
use Filament\Support\Colors\Color;
|
||||
use Filament\View\PanelsRenderHook;
|
||||
use Filament\Widgets\AccountWidget;
|
||||
use Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse;
|
||||
use Illuminate\Cookie\Middleware\EncryptCookies;
|
||||
@@ -75,6 +76,10 @@ class AdminPanelProvider extends PanelProvider
|
||||
->icon('heroicon-o-squares-plus')
|
||||
->url(fn (): string => \App\Filament\Pages\ThemeInject::getUrl()),
|
||||
])
|
||||
->renderHook(
|
||||
PanelsRenderHook::USER_MENU_BEFORE,
|
||||
fn (): \Illuminate\Contracts\View\View => view('filament.hooks.topbar-tools')
|
||||
)
|
||||
->navigationGroups([
|
||||
NavigationGroup::make('内容'),
|
||||
NavigationGroup::make('管理'),
|
||||
|
||||
Reference in New Issue
Block a user