优化后台提示展示与聊天室公告样式
This commit is contained in:
@@ -72,15 +72,6 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@if (session('success'))
|
||||
<div class="mb-4 px-4 py-3 bg-green-50 border border-green-200 text-green-700 rounded-lg text-sm">
|
||||
{{ session('success') }}</div>
|
||||
@endif
|
||||
@if (session('error'))
|
||||
<div class="mb-4 px-4 py-3 bg-red-50 border border-red-200 text-red-700 rounded-lg text-sm">
|
||||
{{ session('error') }}</div>
|
||||
@endif
|
||||
|
||||
{{-- 在职人员列表 --}}
|
||||
<div class="bg-white rounded-xl shadow-sm border overflow-hidden">
|
||||
<table class="w-full text-sm">
|
||||
|
||||
@@ -51,17 +51,6 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@if (session('success'))
|
||||
<div class="mb-4 px-4 py-3 bg-green-50 border border-green-200 text-green-700 rounded-lg text-sm">
|
||||
{{ session('success') }}
|
||||
</div>
|
||||
@endif
|
||||
@if (session('error'))
|
||||
<div class="mb-4 px-4 py-3 bg-red-50 border border-red-200 text-red-700 rounded-lg text-sm">
|
||||
{{ session('error') }}
|
||||
</div>
|
||||
@endif
|
||||
|
||||
{{-- 部门卡片列表 --}}
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 xl:grid-cols-4 gap-4">
|
||||
@foreach ($departments as $dept)
|
||||
|
||||
@@ -20,13 +20,6 @@
|
||||
</a>
|
||||
</div>
|
||||
|
||||
{{-- Flash --}}
|
||||
@if (session('success'))
|
||||
<div class="bg-green-50 border border-green-200 text-green-700 px-4 py-3 rounded-lg text-sm">
|
||||
✅ {{ session('success') }}
|
||||
</div>
|
||||
@endif
|
||||
|
||||
{{-- 钓鱼事件列表 --}}
|
||||
<div class="bg-white rounded-xl shadow-sm border border-gray-100 overflow-hidden">
|
||||
<table class="w-full text-sm">
|
||||
|
||||
@@ -24,12 +24,6 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@if (session('success'))
|
||||
<div class="bg-green-50 border border-green-200 text-green-700 px-4 py-3 rounded-lg text-sm">
|
||||
✅ {{ session('success') }}
|
||||
</div>
|
||||
@endif
|
||||
|
||||
{{-- 游戏卡片列表 --}}
|
||||
<div class="grid grid-cols-1 gap-6">
|
||||
@foreach ($games as $game)
|
||||
|
||||
@@ -16,18 +16,6 @@
|
||||
</a>
|
||||
</div>
|
||||
|
||||
{{-- 成功/错误提示 --}}
|
||||
@if (session('success'))
|
||||
<div class="bg-green-50 border border-green-200 text-green-700 px-4 py-3 rounded-lg text-sm">
|
||||
✅ {{ session('success') }}
|
||||
</div>
|
||||
@endif
|
||||
@if (session('error'))
|
||||
<div class="bg-red-50 border border-red-200 text-red-700 px-4 py-3 rounded-lg text-sm">
|
||||
❌ {{ session('error') }}
|
||||
</div>
|
||||
@endif
|
||||
|
||||
{{-- 活动列表 --}}
|
||||
<div class="bg-white rounded-xl shadow-sm border border-gray-100 overflow-hidden">
|
||||
<div class="overflow-x-auto">
|
||||
|
||||
@@ -11,6 +11,16 @@
|
||||
</head>
|
||||
|
||||
<body class="bg-gray-100 flex h-screen text-gray-800">
|
||||
@php
|
||||
$adminFlashToasts = collect([
|
||||
session('success') ? ['type' => 'success', 'message' => session('success')] : null,
|
||||
session('ops_success') ? ['type' => 'success', 'message' => session('ops_success')] : null,
|
||||
session('error') ? ['type' => 'error', 'message' => session('error')] : null,
|
||||
])
|
||||
->filter()
|
||||
->values();
|
||||
@endphp
|
||||
|
||||
<!-- 左侧侧边栏 -->
|
||||
<aside class="w-64 bg-slate-900 text-white flex flex-col">
|
||||
<div class="p-6 text-center border-b border-white/10">
|
||||
@@ -168,16 +178,6 @@
|
||||
|
||||
<!-- 内容滚动区 -->
|
||||
<div class="flex-1 overflow-y-auto p-6 relative">
|
||||
@if (session('success'))
|
||||
<div class="mb-6 bg-emerald-100 border-l-4 border-emerald-500 text-emerald-700 p-4 rounded shadow-sm">
|
||||
{{ session('success') }}
|
||||
</div>
|
||||
@endif
|
||||
@if (session('error'))
|
||||
<div class="mb-6 bg-red-100 border-l-4 border-red-500 text-red-700 p-4 rounded shadow-sm">
|
||||
{{ session('error') }}
|
||||
</div>
|
||||
@endif
|
||||
@if ($errors->any())
|
||||
<div class="mb-6 bg-red-100 border-l-4 border-red-500 text-red-700 p-4 rounded shadow-sm">
|
||||
<ul class="list-disc list-inside text-sm">
|
||||
@@ -192,6 +192,53 @@
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<div x-data="createAdminToastStore(@js($adminFlashToasts))" x-init="boot()"
|
||||
class="pointer-events-none fixed right-6 top-6 z-[100] flex w-full max-w-sm flex-col gap-3">
|
||||
<template x-for="toast in toasts" :key="toast.id">
|
||||
<div x-cloak x-show="visibleIds.includes(toast.id)"
|
||||
x-transition:enter="transform transition duration-300 ease-out"
|
||||
x-transition:enter-start="translate-y-2 opacity-0 sm:translate-x-6 sm:translate-y-0"
|
||||
x-transition:enter-end="translate-y-0 opacity-100 sm:translate-x-0"
|
||||
x-transition:leave="transform transition duration-200 ease-in"
|
||||
x-transition:leave-start="translate-y-0 opacity-100"
|
||||
x-transition:leave-end="translate-y-2 opacity-0 sm:translate-x-6 sm:translate-y-0"
|
||||
class="pointer-events-auto overflow-hidden rounded-2xl border border-slate-200/80 bg-white/95 shadow-2xl ring-1 ring-slate-200/60 backdrop-blur">
|
||||
<div class="flex items-start gap-3 p-4">
|
||||
<div class="flex h-10 w-10 shrink-0 items-center justify-center rounded-2xl text-lg font-bold"
|
||||
:class="toast.type === 'error'
|
||||
? 'bg-red-50 text-red-500 ring-1 ring-red-100'
|
||||
: 'bg-emerald-50 text-emerald-500 ring-1 ring-emerald-100'">
|
||||
<span x-text="toast.type === 'error' ? '!' : '✓'"></span>
|
||||
</div>
|
||||
<div class="min-w-0 flex-1">
|
||||
<div class="flex items-start justify-between gap-3">
|
||||
<div>
|
||||
<p class="text-sm font-bold text-slate-800"
|
||||
x-text="toast.title || (toast.type === 'error' ? '操作失败' : '操作成功')"></p>
|
||||
<p class="mt-1 break-words text-sm leading-6 text-slate-600" x-text="toast.message"></p>
|
||||
</div>
|
||||
<button type="button" @click="remove(toast.id)"
|
||||
class="rounded-full p-1 text-slate-400 transition hover:bg-slate-100 hover:text-slate-600"
|
||||
aria-label="关闭提示">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor"
|
||||
class="h-4 w-4">
|
||||
<path
|
||||
d="M6.28 5.22a.75.75 0 0 1 1.06 0L10 7.94l2.66-2.72a.75.75 0 0 1 1.08 1.04L11.06 9l2.68 2.74a.75.75 0 1 1-1.08 1.04L10 10.06l-2.66 2.72a.75.75 0 1 1-1.08-1.04L8.94 9 6.28 6.26a.75.75 0 0 1 0-1.04Z" />
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
<div class="mt-3 h-1 overflow-hidden rounded-full bg-slate-100">
|
||||
<div class="h-full rounded-full"
|
||||
:class="toast.type === 'error' ? 'bg-red-400' : 'bg-emerald-400'"
|
||||
:style="`animation: admin-toast-progress ${toast.duration || 4200}ms linear forwards;`">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
|
||||
{{-- ══════════════════════════════════════════════════════════
|
||||
全局弹窗组件:window.adminDialog.alert / window.adminDialog.confirm
|
||||
用法:
|
||||
@@ -214,6 +261,10 @@
|
||||
</div>
|
||||
</div>
|
||||
<style>
|
||||
[x-cloak] {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
@keyframes admin-dialog-pop {
|
||||
0% {
|
||||
opacity: 0;
|
||||
@@ -229,8 +280,101 @@
|
||||
transform: scale(1);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes admin-toast-progress {
|
||||
from {
|
||||
transform: translateX(0);
|
||||
}
|
||||
|
||||
to {
|
||||
transform: translateX(-100%);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<script>
|
||||
/**
|
||||
* 后台全局 Toast 工厂。
|
||||
*
|
||||
* 负责统一渲染右上角成功/失败提示,并提供全局调用入口。
|
||||
*
|
||||
* @param initialToasts 初始提示列表
|
||||
* @returns {object}
|
||||
*/
|
||||
function createAdminToastStore(initialToasts = []) {
|
||||
return {
|
||||
toasts: [],
|
||||
visibleIds: [],
|
||||
nextToastId: Date.now(),
|
||||
|
||||
/**
|
||||
* 初始化 Toast 列表,并挂载全局调用方法。
|
||||
*/
|
||||
boot() {
|
||||
initialToasts.forEach((toast) => this.push(toast));
|
||||
|
||||
window.adminToast = {
|
||||
show: (message, type = 'success', title = null, duration = 4200) => this.push({
|
||||
message,
|
||||
type,
|
||||
title,
|
||||
duration
|
||||
}),
|
||||
success: (message, title = '操作成功', duration = 4200) => this.push({
|
||||
message,
|
||||
type: 'success',
|
||||
title,
|
||||
duration
|
||||
}),
|
||||
error: (message, title = '操作失败', duration = 4200) => this.push({
|
||||
message,
|
||||
type: 'error',
|
||||
title,
|
||||
duration
|
||||
}),
|
||||
};
|
||||
},
|
||||
|
||||
/**
|
||||
* 追加一条 Toast,并在指定时长后自动关闭。
|
||||
*
|
||||
* @param toast 待展示的提示对象
|
||||
*/
|
||||
push(toast) {
|
||||
if (!toast?.message) {
|
||||
return;
|
||||
}
|
||||
|
||||
const normalizedToast = {
|
||||
id: this.nextToastId++,
|
||||
type: toast.type === 'error' ? 'error' : 'success',
|
||||
title: toast.title,
|
||||
message: toast.message,
|
||||
duration: Number(toast.duration) > 0 ? Number(toast.duration) : 4200,
|
||||
};
|
||||
|
||||
this.toasts.push(normalizedToast);
|
||||
this.visibleIds.push(normalizedToast.id);
|
||||
|
||||
window.setTimeout(() => {
|
||||
this.remove(normalizedToast.id);
|
||||
}, normalizedToast.duration);
|
||||
},
|
||||
|
||||
/**
|
||||
* 关闭指定 Toast,并在退场动画后清理节点。
|
||||
*
|
||||
* @param {number} toastId
|
||||
*/
|
||||
remove(toastId) {
|
||||
this.visibleIds = this.visibleIds.filter((visibleId) => visibleId !== toastId);
|
||||
|
||||
window.setTimeout(() => {
|
||||
this.toasts = this.toasts.filter((toast) => toast.id !== toastId);
|
||||
}, 220);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 后台全局弹窗组件。
|
||||
*
|
||||
|
||||
@@ -19,12 +19,6 @@
|
||||
<a href="{{ route('admin.marriages.index') }}" class="text-sm text-indigo-600 hover:underline">← 返回总览</a>
|
||||
</div>
|
||||
|
||||
@if (session('success'))
|
||||
<div class="bg-green-50 border border-green-200 text-green-700 rounded-lg px-4 py-3 mb-4 text-sm">
|
||||
✅ {{ session('success') }}
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<form action="{{ route('admin.marriages.configs.update') }}" method="POST">
|
||||
@csrf
|
||||
|
||||
|
||||
@@ -19,12 +19,6 @@
|
||||
<a href="{{ route('admin.marriages.index') }}" class="text-sm text-indigo-600 hover:underline">← 返回总览</a>
|
||||
</div>
|
||||
|
||||
@if (session('success'))
|
||||
<div class="bg-green-50 border border-green-200 text-green-700 rounded-lg px-4 py-3 mb-5 text-sm">
|
||||
✅ {{ session('success') }}
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 xl:grid-cols-3 gap-5">
|
||||
@foreach ($tiers as $tier)
|
||||
<div class="bg-white rounded-xl shadow-sm border overflow-hidden {{ $tier->is_active ? '' : 'opacity-60' }}"
|
||||
|
||||
@@ -9,13 +9,6 @@
|
||||
<p class="text-xs text-gray-500 mt-1">仅站长可见。每项操作不可撤销,请确认后执行。</p>
|
||||
</div>
|
||||
|
||||
{{-- 操作结果提示 --}}
|
||||
@if (session('ops_success'))
|
||||
<div class="mx-6 mt-5 p-4 bg-blue-50 border border-blue-200 rounded-lg text-blue-700 text-sm font-medium">
|
||||
{{ session('ops_success') }}
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<div class="p-6">
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-5">
|
||||
|
||||
|
||||
@@ -76,18 +76,6 @@
|
||||
</div>
|
||||
@endif
|
||||
|
||||
{{-- Flash 消息 --}}
|
||||
@if (session('success'))
|
||||
<div class="mx-6 mt-4 p-3 bg-green-50 border border-green-200 rounded-lg text-green-700 text-sm">
|
||||
✅ {{ session('success') }}
|
||||
</div>
|
||||
@endif
|
||||
@if (session('error'))
|
||||
<div class="mx-6 mt-4 p-3 bg-red-50 border border-red-200 rounded-lg text-red-700 text-sm">
|
||||
❌ {{ session('error') }}
|
||||
</div>
|
||||
@endif
|
||||
|
||||
{{-- 新增房间表单展开时自动滚到顶部校验错误 --}}
|
||||
@if ($errors->any())
|
||||
<script>
|
||||
|
||||
@@ -11,12 +11,6 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@if (session('success'))
|
||||
<div class="mx-6 mt-4 p-3 bg-green-50 border border-green-200 rounded-lg text-green-700 text-sm">
|
||||
✅ {{ session('success') }}
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<div class="mx-6 mt-4 p-3 bg-amber-50 border border-amber-200 rounded-lg text-amber-800 text-sm">
|
||||
通用系统参数页仅维护低敏公共配置;SMTP、VIP 支付、微信机器人、AI 机器人等站长专属敏感项已迁移到各自独立页面。
|
||||
</div>
|
||||
|
||||
@@ -11,12 +11,6 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@if (session('success'))
|
||||
<div class="mx-6 mt-4 p-3 bg-green-50 border border-green-200 rounded-lg text-green-700 text-sm">
|
||||
✅ {{ session('success') }}
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<div class="p-6">
|
||||
<form action="{{ route('admin.wechat_bot.update') }}" method="POST" enctype="multipart/form-data">
|
||||
@csrf
|
||||
|
||||
@@ -1140,7 +1140,7 @@
|
||||
if (msg.from_user === '系统公告') {
|
||||
// 管理员公告:大字醒目红框样式
|
||||
div.style.cssText =
|
||||
'background: linear-gradient(135deg, #fef2f2, #fff1f2); border: 2px solid #ef4444; border-radius: 6px; padding: 8px 12px; margin: 4px 0; box-shadow: 0 2px 4px rgba(239,68,68,0.15);';
|
||||
'background: linear-gradient(135deg, #fef2f2, #fff1f2); border: 2px solid #ef4444; border-radius: 8px; padding: 10px 14px; margin: 6px 0; box-shadow: 0 3px 8px rgba(239,68,68,0.16);';
|
||||
|
||||
let parsedContent = msg.content;
|
||||
parsedContent = parsedContent.replace(/【([^】]+)】/g, function(match, uName) {
|
||||
@@ -1148,7 +1148,7 @@
|
||||
});
|
||||
|
||||
html =
|
||||
`<div style="font-weight: bold; color: #dc2626;">${parsedContent} <span style="color: #999; font-weight: normal;">(${timeStr})</span></div>`;
|
||||
`<div style="font-size: 18px; line-height: 1.75; font-weight: 800; color: #dc2626;">${parsedContent} <span style="color: #999; font-size: 14px; font-weight: 500;">(${timeStr})</span></div>`;
|
||||
timeStrOverride = true;
|
||||
} else if (msg.from_user === '系统传音') {
|
||||
// 自动升级播报 / 赠礼通知 / 彩票购买广播:金色左边框,轻量提示样式,不喧宾夺主
|
||||
|
||||
Reference in New Issue
Block a user