diff --git a/public/css/admin-components.css b/public/css/admin-components.css index 8b6d68c..3fb5797 100644 --- a/public/css/admin-components.css +++ b/public/css/admin-components.css @@ -1,8 +1,63 @@ /* * SaaSShop Admin Components * 说明:用于承接 Blade 中零散的 inline style,便于后续统一美化与治理。 + * + * 美化方向:Ant Design Pro 风格(规整、企业中后台范式) */ +/* Toast(渐进增强:配合 admin.js 展示更友好的反馈提示) */ +.toast-container{ + position:fixed; + top:16px; + right:16px; + z-index:999; + display:flex; + flex-direction:column; + gap:10px; + width:min(420px, calc(100vw - 32px)); +} + +.toast{ + background:#111827; + border:1px solid #334155; + border-left-width:4px; + border-radius:12px; + padding:12px 12px; + color:#e5e7eb; + box-shadow:0 14px 30px rgba(0,0,0,.35); + display:flex; + align-items:flex-start; + justify-content:space-between; + gap:12px; +} + +.toast-success{border-left-color:#16a34a;} +.toast-warning{border-left-color:#f59e0b;} +.toast-error{border-left-color:#ef4444;} + +.toast-content{ + font-size:13px; + line-height:1.45; + word-break:break-word; +} + +.toast-close{ + appearance:none; + border:none; + background:transparent; + color:#94a3b8; + font-size:18px; + line-height:1; + cursor:pointer; + padding:2px 6px; + border-radius:8px; +} + +.toast-close:hover{ + background:#1f2937; + color:#e5e7eb; +} + .form-inline-row{ display:flex; align-items:center; diff --git a/public/js/admin.js b/public/js/admin.js index 073ad0c..f92c803 100644 --- a/public/js/admin.js +++ b/public/js/admin.js @@ -43,6 +43,79 @@ }); })(); + // 通用:将后端 flash 信息同步到 toast(更像 Ant Design Pro 的反馈方式) + // 说明:渐进增强。页面仍保留原本的提示块,不依赖 JS。 + (function () { + var container = qs('[data-role="toast-container"]'); + if (!container) { + return; + } + + var flashNodes = document.querySelectorAll('[data-flash]'); + if (!flashNodes || flashNodes.length === 0) { + return; + } + + function createToast(type, text) { + var div = document.createElement('div'); + div.className = 'toast toast-' + type; + div.setAttribute('role', 'status'); + + var content = document.createElement('div'); + content.className = 'toast-content'; + content.textContent = text; + + var close = document.createElement('button'); + close.type = 'button'; + close.className = 'toast-close'; + close.textContent = '×'; + close.addEventListener('click', function () { + try { + container.removeChild(div); + } catch (e) {} + }); + + div.appendChild(content); + div.appendChild(close); + return div; + } + + flashNodes.forEach(function (node) { + var type = node.getAttribute('data-flash') || 'info'; + var text = (node.textContent || '').trim(); + if (!text) { + return; + } + + container.appendChild(createToast(type, text)); + }); + + // 自动消失(success/warning),error 保留更久 + setTimeout(function () { + try { + var toasts = container.querySelectorAll('.toast'); + toasts.forEach(function (t) { + var cls = t.className || ''; + var isError = cls.indexOf('toast-error') >= 0; + if (!isError) { + container.removeChild(t); + } + }); + } catch (e) {} + }, 4500); + + setTimeout(function () { + try { + var toasts = container.querySelectorAll('.toast'); + toasts.forEach(function (t) { + try { + container.removeChild(t); + } catch (e) {} + }); + } catch (e) {} + }, 9000); + })(); + // 续费缺订阅治理:订单详情页“绑定订阅ID”输入框,小交互增强: // - 输入后按 Enter 直接提交 // - 自动聚焦,减少点击 diff --git a/resources/views/admin/layouts/app.blade.php b/resources/views/admin/layouts/app.blade.php index dd0899d..07d6982 100644 --- a/resources/views/admin/layouts/app.blade.php +++ b/resources/views/admin/layouts/app.blade.php @@ -69,6 +69,8 @@
+
+

@yield('page_title', '总台管理')

@@ -77,13 +79,13 @@
@if(session('success')) -
{{ session('success') }}
+
{{ session('success') }}
@endif @if(session('warning')) -
{{ session('warning') }}
+
{{ session('warning') }}
@endif @if(session('error')) -
{{ session('error') }}
+
{{ session('error') }}
@endif @if($errors->any())
diff --git a/tests/Feature/AdminComponentsCssToastStylesShouldExistTest.php b/tests/Feature/AdminComponentsCssToastStylesShouldExistTest.php new file mode 100644 index 0000000..d10feb5 --- /dev/null +++ b/tests/Feature/AdminComponentsCssToastStylesShouldExistTest.php @@ -0,0 +1,35 @@ +seed(); + + $this->post('/admin/login', [ + 'email' => 'platform.admin@demo.local', + 'password' => 'Platform@123456', + ])->assertRedirect('/admin'); + } + + public function test_admin_components_css_should_contain_toast_styles(): void + { + $this->loginAsPlatformAdmin(); + + $css = file_get_contents(public_path('css/admin-components.css')); + $this->assertIsString($css); + + $this->assertStringContainsString(".toast-container{\n", $css); + $this->assertStringContainsString(".toast{\n", $css); + $this->assertStringContainsString(".toast-success", $css); + $this->assertStringContainsString(".toast-warning", $css); + $this->assertStringContainsString(".toast-error", $css); + } +}