From 91ddfbb408adeab95947220ae0473c705c8a7380 Mon Sep 17 00:00:00 2001 From: lkddi Date: Sat, 25 Apr 2026 13:16:23 +0800 Subject: [PATCH] =?UTF-8?q?=E8=BF=81=E7=A7=BB=E9=92=93=E9=B1=BC=E4=BA=8B?= =?UTF-8?q?=E4=BB=B6=E5=90=8E=E5=8F=B0=E8=84=9A=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- resources/js/admin/fishing-events.js | 196 ++++++++++++++++++ resources/js/app.js | 2 + resources/views/admin/fishing/index.blade.php | 89 +++----- 3 files changed, 225 insertions(+), 62 deletions(-) create mode 100644 resources/js/admin/fishing-events.js diff --git a/resources/js/admin/fishing-events.js b/resources/js/admin/fishing-events.js new file mode 100644 index 0000000..a9ac487 --- /dev/null +++ b/resources/js/admin/fishing-events.js @@ -0,0 +1,196 @@ +// 钓鱼事件后台管理页事件代理,替代 Blade 内联编辑和启停函数。 + +let adminFishingEventsControlsBound = false; +let fishingEventsCache = null; + +/** + * 读取后台 layout 注入的 CSRF token。 + * + * @returns {string} + */ +function getCsrfToken() { + return document.querySelector('meta[name="csrf-token"]')?.getAttribute("content") || ""; +} + +/** + * 读取 Blade 注入的钓鱼事件快照。 + * + * @returns {Record>} + */ +function getFishingEvents() { + if (fishingEventsCache !== null) { + return fishingEventsCache; + } + + const dataNode = document.getElementById("admin-fishing-events-data"); + if (!dataNode?.textContent) { + fishingEventsCache = {}; + return fishingEventsCache; + } + + try { + fishingEventsCache = JSON.parse(dataNode.textContent); + } catch (error) { + // JSON 被异常截断时保持页面可用,只是不再打开编辑弹窗。 + fishingEventsCache = {}; + } + + return fishingEventsCache; +} + +/** + * 设置表单字段值。 + * + * @param {string} id 字段 ID + * @param {unknown} value 字段值 + * @returns {void} + */ +function setInputValue(id, value) { + const input = document.getElementById(id); + if (input instanceof HTMLInputElement) { + input.value = String(value ?? ""); + } +} + +/** + * 打开钓鱼事件编辑弹窗并填充表单。 + * + * @param {string} eventId 事件 ID + * @returns {void} + */ +function openFishingEditModal(eventId) { + const eventData = getFishingEvents()[eventId]; + const form = document.getElementById("edit-form"); + const modal = document.getElementById("edit-modal"); + + if (!eventData || !(form instanceof HTMLFormElement) || !modal) { + return; + } + + form.action = String(eventData.update_url || ""); + setInputValue("edit-emoji", eventData.emoji); + setInputValue("edit-name", eventData.name); + setInputValue("edit-message", eventData.message); + setInputValue("edit-exp", eventData.exp); + setInputValue("edit-jjb", eventData.jjb); + setInputValue("edit-weight", eventData.weight); + setInputValue("edit-sort", eventData.sort); + + const activeInput = document.getElementById("edit-is-active"); + if (activeInput instanceof HTMLInputElement) { + activeInput.checked = Boolean(eventData.is_active); + } + + modal.classList.remove("hidden"); +} + +/** + * 关闭钓鱼事件编辑弹窗。 + * + * @returns {void} + */ +function closeFishingEditModal() { + document.getElementById("edit-modal")?.classList.add("hidden"); +} + +/** + * 根据接口返回状态更新钓鱼事件行和缓存。 + * + * @param {string} eventId 事件 ID + * @param {boolean} active 是否启用 + * @returns {void} + */ +function updateFishingToggleState(eventId, active) { + const button = document.querySelector(`[data-fishing-toggle-id="${CSS.escape(eventId)}"]`); + const row = document.getElementById(`row-${eventId}`); + + if (button instanceof HTMLButtonElement) { + button.textContent = active ? "启用" : "禁用"; + button.className = `px-2 py-1 rounded-full text-xs font-bold transition ${ + active + ? "bg-emerald-100 text-emerald-700 hover:bg-emerald-200" + : "bg-gray-100 text-gray-500 hover:bg-gray-200" + }`; + } + + row?.classList.toggle("opacity-50", !active); + + // 缓存状态要同步,否则后续打开编辑弹窗时会显示旧的启用状态。 + const eventData = getFishingEvents()[eventId]; + if (eventData) { + eventData.is_active = active; + } +} + +/** + * 切换钓鱼事件启用状态。 + * + * @param {HTMLButtonElement} button 状态切换按钮 + * @returns {Promise} + */ +async function toggleFishingEvent(button) { + const eventId = button.getAttribute("data-fishing-toggle-id") || ""; + const eventData = getFishingEvents()[eventId]; + const toggleUrl = String(eventData?.toggle_url || ""); + + if (!eventId || !toggleUrl || button.disabled) { + return; + } + + button.disabled = true; + + try { + const response = await fetch(toggleUrl, { + method: "POST", + headers: { + "X-CSRF-TOKEN": getCsrfToken(), + "Accept": "application/json", + }, + }); + const data = await response.json(); + + if (data?.ok) { + updateFishingToggleState(eventId, Boolean(data.is_active)); + } + } finally { + button.disabled = false; + } +} + +/** + * 绑定钓鱼事件管理页操作按钮。 + * + * @returns {void} + */ +export function bindAdminFishingEventsControls() { + if (adminFishingEventsControlsBound || typeof document === "undefined") { + return; + } + + adminFishingEventsControlsBound = true; + document.addEventListener("click", (event) => { + if (!(event.target instanceof Element)) { + return; + } + + const editButton = event.target.closest("[data-fishing-edit-id]"); + if (editButton) { + event.preventDefault(); + openFishingEditModal(editButton.getAttribute("data-fishing-edit-id") || ""); + return; + } + + const closeButton = event.target.closest("[data-fishing-edit-close]"); + if (closeButton) { + event.preventDefault(); + closeFishingEditModal(); + return; + } + + const toggleButton = event.target.closest("[data-fishing-toggle-id]"); + if (toggleButton instanceof HTMLButtonElement) { + event.preventDefault(); + void toggleFishingEvent(toggleButton); + } + }); +} diff --git a/resources/js/app.js b/resources/js/app.js index ade837d..0e26c4c 100644 --- a/resources/js/app.js +++ b/resources/js/app.js @@ -1,5 +1,7 @@ import './bootstrap'; import { bindAdminAutoactControls } from './admin/autoact.js'; +import { bindAdminFishingEventsControls } from './admin/fishing-events.js'; // 后台共用入口只注册轻量事件代理,具体页面通过 data-* 属性决定是否响应。 bindAdminAutoactControls(); +bindAdminFishingEventsControls(); diff --git a/resources/views/admin/fishing/index.blade.php b/resources/views/admin/fishing/index.blade.php index c3d8963..71e35f6 100644 --- a/resources/views/admin/fishing/index.blade.php +++ b/resources/views/admin/fishing/index.blade.php @@ -3,6 +3,28 @@ @section('title', '钓鱼事件管理') @section('content') + @php + $fishingEventPayload = $events->mapWithKeys( + fn($event) => [ + (string) $event->id => [ + 'id' => $event->id, + 'emoji' => $event->emoji, + 'name' => $event->name, + 'message' => $event->message, + 'exp' => $event->exp, + 'jjb' => $event->jjb, + 'weight' => $event->weight, + 'sort' => $event->sort, + 'is_active' => (bool) $event->is_active, + 'update_url' => route('admin.fishing.update', $event->id), + 'toggle_url' => route('admin.fishing.toggle', $event->id), + ], + ], + ); + @endphp + + +
{{-- 页头 --}} @@ -63,14 +85,15 @@ @endif - - @@ -153,7 +176,7 @@

✏️ 编辑钓鱼事件

- +
@csrf @method('PUT') @@ -205,7 +228,7 @@ class="px-5 py-2 bg-indigo-600 text-white rounded-lg font-bold hover:bg-indigo-700 transition text-sm"> 💾 保存修改 - @@ -213,62 +236,4 @@
- - @endsection