优化节日福利列表与领取提示展示
This commit is contained in:
@@ -1,155 +1,285 @@
|
||||
{{-- 文件功能:后台节日福利活动列表页,展示节日福利模板并提供启用、立即触发、编辑、删除等操作。 --}}
|
||||
@extends('admin.layouts.app')
|
||||
|
||||
@section('title', '节日福利管理')
|
||||
|
||||
@section('content')
|
||||
@php
|
||||
$visibleEvents = $events->getCollection();
|
||||
$enabledCount = $visibleEvents->where('enabled', true)->count();
|
||||
$yearlyCount = $visibleEvents->where('repeat_type', 'yearly')->count();
|
||||
$statusMap = [
|
||||
'pending' => ['待触发', 'bg-amber-100 text-amber-700'],
|
||||
'active' => ['领取中', 'bg-emerald-100 text-emerald-700'],
|
||||
'completed' => ['已结束', 'bg-slate-100 text-slate-600'],
|
||||
'cancelled' => ['已取消', 'bg-rose-100 text-rose-700'],
|
||||
];
|
||||
$repeatLabels = [
|
||||
'once' => '一次',
|
||||
'daily' => '每天',
|
||||
'weekly' => '每周',
|
||||
'monthly' => '每月',
|
||||
'yearly' => '每年',
|
||||
'cron' => 'CRON',
|
||||
];
|
||||
@endphp
|
||||
|
||||
<div class="space-y-6">
|
||||
{{-- 页头 --}}
|
||||
<div class="bg-white rounded-xl shadow-sm border border-gray-100 p-6 flex justify-between items-center">
|
||||
<div>
|
||||
<h2 class="text-lg font-bold text-gray-800">🎊 节日福利管理</h2>
|
||||
<p class="text-xs text-gray-500 mt-1">配置一次性、周期性与年度节日福利,系统自动触发广播并分配红包。</p>
|
||||
<div class="rounded-xl border border-gray-100 bg-white p-5 shadow-sm">
|
||||
<div class="flex flex-col gap-4 lg:flex-row lg:items-center lg:justify-between">
|
||||
<div class="space-y-2">
|
||||
<div>
|
||||
<h2 class="text-xl font-black tracking-tight text-slate-800">节日福利管理</h2>
|
||||
<p class="mt-1 text-sm text-slate-500">统一维护节日福利模板与触发计划。</p>
|
||||
</div>
|
||||
<div class="flex flex-wrap gap-2 text-xs font-semibold">
|
||||
<span class="rounded-full bg-slate-100 px-3 py-1 text-slate-600">当前页 {{ $visibleEvents->count() }} 个活动</span>
|
||||
<span class="rounded-full bg-emerald-100 px-3 py-1 text-emerald-700">已启用 {{ $enabledCount }} 个</span>
|
||||
<span class="rounded-full bg-amber-100 px-3 py-1 text-amber-700">年度模板 {{ $yearlyCount }} 个</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<a href="{{ route('admin.holiday-events.create') }}"
|
||||
class="inline-flex items-center justify-center gap-2 rounded-lg bg-amber-500 px-4 py-2.5 text-sm font-bold text-white shadow-sm transition hover:bg-amber-600">
|
||||
<span class="text-base leading-none">+</span>
|
||||
<span>创建活动</span>
|
||||
</a>
|
||||
</div>
|
||||
<a href="{{ route('admin.holiday-events.create') }}"
|
||||
class="px-5 py-2 bg-amber-500 text-white rounded-lg font-bold hover:bg-amber-600 transition text-sm shadow-sm">
|
||||
➕ 创建活动
|
||||
</a>
|
||||
</div>
|
||||
|
||||
{{-- 活动列表 --}}
|
||||
<div class="bg-white rounded-xl shadow-sm border border-gray-100 overflow-hidden">
|
||||
{{-- 列表 --}}
|
||||
<div class="overflow-hidden rounded-xl border border-gray-100 bg-white shadow-sm">
|
||||
<div class="border-b border-gray-100 px-6 py-3">
|
||||
<div class="flex flex-col gap-1 lg:flex-row lg:items-center lg:justify-between">
|
||||
<div>
|
||||
<h3 class="text-base font-bold text-slate-800">福利模板列表</h3>
|
||||
<p class="mt-1 text-xs text-slate-500">列表按关键信息压缩展示,适合批量查看。</p>
|
||||
</div>
|
||||
<div class="text-xs text-slate-400">年度模板额外显示月日、持续天数、轮次与轮次间隔</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="overflow-x-auto">
|
||||
<table class="w-full text-sm">
|
||||
<thead>
|
||||
<tr class="bg-gray-50 text-gray-600 text-left border-b text-xs font-bold">
|
||||
<th class="p-3 w-10">ID</th>
|
||||
<th class="p-3">活动名称</th>
|
||||
<th class="p-3 w-24">奖池</th>
|
||||
<th class="p-3 w-20">分配方式</th>
|
||||
<th class="p-3 w-20">限额人数</th>
|
||||
<th class="p-3 w-36">触发时间</th>
|
||||
<th class="p-3 w-16">重复</th>
|
||||
<th class="p-3 w-16">状态</th>
|
||||
<th class="p-3 w-16">启用</th>
|
||||
<th class="p-3 w-40 text-right">操作</th>
|
||||
<table class="min-w-[1120px] w-full table-fixed text-sm">
|
||||
<thead class="bg-slate-50 text-left text-xs font-bold uppercase tracking-[0.16em] text-slate-500">
|
||||
<tr>
|
||||
<th class="px-4 py-3 w-14">编号</th>
|
||||
<th class="px-4 py-3 w-[25%]">活动信息</th>
|
||||
<th class="px-4 py-3 w-[10%]">奖池</th>
|
||||
<th class="px-4 py-3 w-[10%]">分配</th>
|
||||
<th class="px-4 py-3 w-[11%]">限额 / 重复</th>
|
||||
<th class="px-4 py-3 w-[15%]">触发计划</th>
|
||||
<th class="px-4 py-3 w-[13%]">运行状态</th>
|
||||
<th class="px-4 py-3 w-[16%] text-right">操作</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tbody class="divide-y divide-gray-100">
|
||||
@forelse ($events as $event)
|
||||
<tr class="border-b hover:bg-gray-50 {{ !$event->enabled ? 'opacity-50' : '' }}">
|
||||
<td class="p-3 text-gray-400">{{ $event->id }}</td>
|
||||
<td class="p-3">
|
||||
<div class="font-bold text-gray-800">{{ $event->name }}</div>
|
||||
@if ($event->description)
|
||||
<div class="text-xs text-gray-400 mt-0.5">{{ Str::limit($event->description, 40) }}
|
||||
@php
|
||||
[$statusLabel, $statusClass] = $statusMap[$event->status] ?? ['未知', 'bg-slate-100 text-slate-600'];
|
||||
$repeatLabel = $repeatLabels[$event->repeat_type] ?? '-';
|
||||
$isRandomDistribute = $event->distribute_type === 'random';
|
||||
$yearlyScheduleText =
|
||||
data_get($event, 'schedule_month', $event->send_at?->format('n')) .
|
||||
'月' .
|
||||
data_get($event, 'schedule_day', $event->send_at?->format('j')) .
|
||||
'日 · ' .
|
||||
data_get($event, 'duration_days', 1) .
|
||||
'天 · 每天' .
|
||||
data_get($event, 'daily_occurrences', 1) .
|
||||
'次 · 间隔' .
|
||||
data_get($event, 'occurrence_interval_minutes', 60) .
|
||||
'分钟';
|
||||
@endphp
|
||||
<tr class="align-top transition hover:bg-slate-50/70 {{ !$event->enabled ? 'opacity-60' : '' }}">
|
||||
<td class="px-4 py-4">
|
||||
<div class="flex h-9 w-9 items-center justify-center rounded-lg bg-slate-100 text-sm font-bold text-slate-600">
|
||||
{{ $event->id }}
|
||||
</div>
|
||||
</td>
|
||||
|
||||
<td class="px-4 py-4">
|
||||
<div class="space-y-1.5">
|
||||
<div class="text-base font-bold tracking-tight text-slate-800">{{ $event->name }}</div>
|
||||
<div class="flex flex-wrap gap-2 text-[11px] font-semibold">
|
||||
<span class="rounded-full bg-slate-100 px-2.5 py-1 text-slate-600">{{ $repeatLabel }}</span>
|
||||
@if ($event->repeat_type === 'yearly')
|
||||
<span class="rounded-full bg-amber-50 px-2.5 py-1 text-amber-700">年度固定节日模板</span>
|
||||
@endif
|
||||
</div>
|
||||
@endif
|
||||
<div class="max-w-md text-xs leading-6 text-slate-500">
|
||||
{{ Str::limit($event->description ?: '未填写节日祝福语,当前活动将广播默认福利提示。', 32) }}
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td class="p-3 font-bold text-amber-600">
|
||||
💰 {{ number_format($event->total_amount) }}
|
||||
|
||||
<td class="px-4 py-4">
|
||||
<div class="font-black text-2xl tracking-tight text-amber-600">{{ number_format($event->total_amount) }}</div>
|
||||
<div class="mt-1 text-xs text-slate-500">金币奖池</div>
|
||||
</td>
|
||||
<td class="p-3">
|
||||
@if ($event->distribute_type === 'random')
|
||||
|
||||
<td class="px-4 py-4">
|
||||
<div class="space-y-1.5">
|
||||
<span
|
||||
class="px-2 py-0.5 rounded-full bg-purple-100 text-purple-700 text-xs">随机</span>
|
||||
@else
|
||||
<span class="px-2 py-0.5 rounded-full bg-blue-100 text-blue-700 text-xs">定额
|
||||
{{ number_format($event->fixed_amount) }}</span>
|
||||
@endif
|
||||
</td>
|
||||
<td class="p-3 text-gray-600">
|
||||
{{ $event->max_claimants === 0 ? '不限' : $event->max_claimants . ' 人' }}
|
||||
</td>
|
||||
<td class="p-3 text-gray-600 text-xs">
|
||||
{{ $event->send_at->format('m-d H:i') }}
|
||||
@if ($event->repeat_type === 'yearly')
|
||||
<div class="mt-1 text-[11px] text-amber-600">
|
||||
{{ data_get($event, 'schedule_month', $event->send_at?->format('n')) }}月{{ data_get($event, 'schedule_day', $event->send_at?->format('j')) }}日
|
||||
· {{ data_get($event, 'duration_days', 1) }}天
|
||||
· 每天{{ data_get($event, 'daily_occurrences', 1) }}次
|
||||
· 间隔{{ data_get($event, 'occurrence_interval_minutes', 60) }}分钟
|
||||
class="inline-flex rounded-full px-2.5 py-1 text-xs font-bold {{ $isRandomDistribute ? 'bg-fuchsia-100 text-fuchsia-700' : 'bg-sky-100 text-sky-700' }}">
|
||||
{{ $isRandomDistribute ? '随机分配' : '固定金额' }}
|
||||
</span>
|
||||
<div class="text-xs leading-5 text-slate-500">
|
||||
@if ($isRandomDistribute)
|
||||
按本轮人数拆分
|
||||
@else
|
||||
每人 {{ number_format($event->fixed_amount) }} 金币
|
||||
@endif
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
</td>
|
||||
<td class="p-3">
|
||||
@php
|
||||
$repeatLabels = [
|
||||
'once' => '一次',
|
||||
'daily' => '每天',
|
||||
'weekly' => '每周',
|
||||
'monthly' => '每月',
|
||||
'yearly' => '每年',
|
||||
'cron' => 'CRON',
|
||||
];
|
||||
@endphp
|
||||
<span
|
||||
class="text-xs text-gray-500">{{ $repeatLabels[$event->repeat_type] ?? '-' }}</span>
|
||||
|
||||
<td class="px-4 py-4">
|
||||
<div class="space-y-2 text-xs leading-5 text-slate-500">
|
||||
<div>
|
||||
<div class="font-medium text-slate-700">限额人数</div>
|
||||
<div class="mt-0.5 text-sm font-bold text-slate-800">
|
||||
{{ $event->max_claimants === 0 ? '不限人数' : $event->max_claimants . ' 人' }}
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="font-medium text-slate-700">重复策略</div>
|
||||
<div class="mt-0.5">{{ $repeatLabel }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td class="p-3">
|
||||
@php
|
||||
$statusMap = [
|
||||
'pending' => ['待触发', 'bg-yellow-100 text-yellow-700'],
|
||||
'active' => ['领取中', 'bg-green-100 text-green-700'],
|
||||
'completed' => ['已结束', 'bg-gray-100 text-gray-500'],
|
||||
'cancelled' => ['已取消', 'bg-red-100 text-red-600'],
|
||||
];
|
||||
[$label, $cls] = $statusMap[$event->status] ?? [
|
||||
'未知',
|
||||
'bg-gray-100 text-gray-500',
|
||||
];
|
||||
@endphp
|
||||
<span
|
||||
class="px-2 py-0.5 rounded-full text-xs {{ $cls }}">{{ $label }}</span>
|
||||
|
||||
<td class="px-4 py-4">
|
||||
<div class="space-y-2 text-xs leading-5 text-slate-500">
|
||||
<div>
|
||||
<div class="font-medium text-slate-700">最近计划时间</div>
|
||||
<div class="mt-0.5 text-xl font-black tracking-tight text-slate-800">{{ $event->send_at->format('m-d H:i') }}</div>
|
||||
<div class="mt-0.5">{{ $event->send_at->format('Y-m-d') }}</div>
|
||||
</div>
|
||||
@if ($event->repeat_type === 'yearly')
|
||||
<div class="rounded-lg bg-amber-50 px-2.5 py-2 text-amber-700">
|
||||
{{ Str::limit($yearlyScheduleText, 22) }}
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
</td>
|
||||
<td class="p-3">
|
||||
<button onclick="toggleHoliday({{ $event->id }}, this)"
|
||||
class="text-xs px-2 py-1 rounded {{ $event->enabled ? 'bg-green-100 text-green-700' : 'bg-gray-200 text-gray-500' }} cursor-pointer">
|
||||
{{ $event->enabled ? '启用' : '禁用' }}
|
||||
</button>
|
||||
|
||||
<td class="px-4 py-4">
|
||||
<div class="space-y-2">
|
||||
<div class="flex flex-wrap gap-2">
|
||||
<span class="inline-flex rounded-full px-2.5 py-1 text-xs font-bold {{ $statusClass }}">
|
||||
{{ $statusLabel }}
|
||||
</span>
|
||||
<span data-role="enabled-badge"
|
||||
class="inline-flex rounded-full px-2.5 py-1 text-xs font-bold {{ $event->enabled ? 'bg-emerald-100 text-emerald-700' : 'bg-slate-100 text-slate-500' }}">
|
||||
{{ $event->enabled ? '已启用' : '已停用' }}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<button type="button"
|
||||
onclick="toggleHoliday({{ $event->id }}, this)"
|
||||
class="inline-flex items-center gap-2 rounded-lg px-3 py-2 text-xs font-bold transition {{ $event->enabled ? 'bg-emerald-500 text-white hover:bg-emerald-600' : 'bg-slate-200 text-slate-600 hover:bg-slate-300' }}"
|
||||
data-enabled="{{ $event->enabled ? '1' : '0' }}">
|
||||
<span data-role="toggle-dot"
|
||||
class="h-2 w-2 rounded-full {{ $event->enabled ? 'bg-white' : 'bg-slate-500' }}"></span>
|
||||
<span data-role="toggle-label">{{ $event->enabled ? '切换为停用' : '重新启用模板' }}</span>
|
||||
</button>
|
||||
|
||||
<div class="text-xs leading-5 text-slate-500">
|
||||
@if ($event->triggered_at)
|
||||
最近触发:{{ $event->triggered_at->format('m-d H:i') }}
|
||||
@else
|
||||
尚未产生发放批次
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td class="p-3 text-right space-x-2">
|
||||
{{-- 手动触发 --}}
|
||||
@if ($event->status === 'pending')
|
||||
<form action="{{ route('admin.holiday-events.trigger-now', $event) }}"
|
||||
method="POST" class="inline" onsubmit="return confirm('确定立即触发此活动吗?')">
|
||||
|
||||
<td class="px-4 py-4">
|
||||
<div class="flex flex-wrap justify-end gap-2">
|
||||
@if ($event->status === 'pending')
|
||||
<form action="{{ route('admin.holiday-events.trigger-now', $event) }}"
|
||||
method="POST" onsubmit="return confirm('确定立即触发此活动吗?')">
|
||||
@csrf
|
||||
<button type="submit"
|
||||
class="inline-flex items-center justify-center rounded-lg bg-amber-500 px-3 py-2 text-xs font-bold text-white transition hover:bg-amber-600">
|
||||
▶ 立即触发
|
||||
</button>
|
||||
</form>
|
||||
@endif
|
||||
|
||||
<a href="{{ route('admin.holiday-events.edit', $event) }}"
|
||||
class="inline-flex items-center justify-center rounded-lg bg-indigo-50 px-3 py-2 text-xs font-bold text-indigo-700 transition hover:bg-indigo-100">
|
||||
编辑
|
||||
</a>
|
||||
|
||||
<form action="{{ route('admin.holiday-events.destroy', $event) }}" method="POST"
|
||||
onsubmit="return confirm('确定删除此活动吗?')">
|
||||
@csrf
|
||||
@method('DELETE')
|
||||
<button type="submit"
|
||||
class="text-xs px-3 py-1 bg-amber-500 text-white rounded-lg hover:bg-amber-600 font-bold">
|
||||
▶ 立即触发
|
||||
class="inline-flex items-center justify-center rounded-lg bg-rose-50 px-3 py-2 text-xs font-bold text-rose-600 transition hover:bg-rose-100">
|
||||
删除
|
||||
</button>
|
||||
</form>
|
||||
@endif
|
||||
<a href="{{ route('admin.holiday-events.edit', $event) }}"
|
||||
class="text-xs px-3 py-1 bg-indigo-100 text-indigo-700 rounded-lg hover:bg-indigo-200 font-bold">编辑</a>
|
||||
<form action="{{ route('admin.holiday-events.destroy', $event) }}" method="POST"
|
||||
class="inline" onsubmit="return confirm('确定删除此活动吗?')">
|
||||
@csrf
|
||||
@method('DELETE')
|
||||
<button type="submit"
|
||||
class="text-xs px-3 py-1 bg-red-100 text-red-600 rounded-lg hover:bg-red-200 font-bold">删除</button>
|
||||
</form>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
@empty
|
||||
<tr>
|
||||
<td colspan="10" class="p-10 text-center text-gray-400">
|
||||
<td colspan="8" class="px-6 py-16 text-center text-slate-400">
|
||||
暂无节日福利活动,<a href="{{ route('admin.holiday-events.create') }}"
|
||||
class="text-amber-500 font-bold">立即创建一个</a>?
|
||||
class="font-bold text-amber-500 hover:text-amber-600">立即创建一个</a>。
|
||||
</td>
|
||||
</tr>
|
||||
@endforelse
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
@if ($events->hasPages())
|
||||
<div class="p-4 border-t">{{ $events->links() }}</div>
|
||||
<div class="border-t border-gray-100 px-6 py-4">
|
||||
{{ $events->links() }}
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
/**
|
||||
* 切换节日活动启用/禁用状态
|
||||
* 根据启用状态返回开关按钮类名。
|
||||
*/
|
||||
function holidayToggleButtonClasses(enabled) {
|
||||
return enabled ?
|
||||
'inline-flex items-center gap-2 rounded-lg px-3 py-2 text-xs font-bold transition bg-emerald-500 text-white hover:bg-emerald-600' :
|
||||
'inline-flex items-center gap-2 rounded-lg px-3 py-2 text-xs font-bold transition bg-slate-200 text-slate-600 hover:bg-slate-300';
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据启用状态返回开关文案。
|
||||
*/
|
||||
function holidayToggleButtonLabel(enabled) {
|
||||
return enabled ? '切换为停用' : '重新启用模板';
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据启用状态返回状态徽标类名。
|
||||
*/
|
||||
function holidayEnabledBadgeClasses(enabled) {
|
||||
return enabled ? 'inline-flex rounded-full px-2.5 py-1 text-xs font-bold bg-emerald-100 text-emerald-700' :
|
||||
'inline-flex rounded-full px-2.5 py-1 text-xs font-bold bg-slate-100 text-slate-500';
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据启用状态返回状态徽标文案。
|
||||
*/
|
||||
function holidayEnabledBadgeLabel(enabled) {
|
||||
return enabled ? '已启用' : '已停用';
|
||||
}
|
||||
|
||||
/**
|
||||
* 切换节日活动启用/禁用状态,并同步刷新当前行文案与样式。
|
||||
*/
|
||||
function toggleHoliday(id, btn) {
|
||||
fetch(`/admin/holiday-events/${id}/toggle`, {
|
||||
@@ -161,12 +291,34 @@
|
||||
})
|
||||
.then(r => r.json())
|
||||
.then(data => {
|
||||
if (data.ok) {
|
||||
btn.textContent = data.enabled ? '启用' : '禁用';
|
||||
btn.className = data.enabled ?
|
||||
'text-xs px-2 py-1 rounded bg-green-100 text-green-700 cursor-pointer' :
|
||||
'text-xs px-2 py-1 rounded bg-gray-200 text-gray-500 cursor-pointer';
|
||||
btn.closest('tr').classList.toggle('opacity-50', !data.enabled);
|
||||
if (!data.ok) {
|
||||
return;
|
||||
}
|
||||
|
||||
const enabled = Boolean(data.enabled);
|
||||
const row = btn.closest('tr');
|
||||
const dot = btn.querySelector('[data-role="toggle-dot"]');
|
||||
const label = btn.querySelector('[data-role="toggle-label"]');
|
||||
const enabledBadge = row?.querySelector('[data-role="enabled-badge"]');
|
||||
|
||||
btn.dataset.enabled = enabled ? '1' : '0';
|
||||
btn.className = holidayToggleButtonClasses(enabled);
|
||||
|
||||
if (dot) {
|
||||
dot.className = enabled ? 'h-2 w-2 rounded-full bg-white' : 'h-2 w-2 rounded-full bg-slate-500';
|
||||
}
|
||||
|
||||
if (label) {
|
||||
label.textContent = holidayToggleButtonLabel(enabled);
|
||||
}
|
||||
|
||||
if (enabledBadge) {
|
||||
enabledBadge.className = holidayEnabledBadgeClasses(enabled);
|
||||
enabledBadge.textContent = holidayEnabledBadgeLabel(enabled);
|
||||
}
|
||||
|
||||
if (row) {
|
||||
row.classList.toggle('opacity-60', !enabled);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -62,19 +62,20 @@
|
||||
|
||||
{{-- 领取按钮 --}}
|
||||
<div x-show="!claimed">
|
||||
<div style="background:rgba(0,0,0,.3); border-radius:16px; padding:5px;">
|
||||
<button x-on:click="doClaim()" :disabled="claiming || loadingStatus || !claimable"
|
||||
style="display:block; width:100%; padding:14px 0; border:none; border-radius:12px;
|
||||
font-size:16px; font-weight:bold; cursor:pointer; transition:all .15s;
|
||||
letter-spacing:1px; color:#fff;"
|
||||
:style="claiming || loadingStatus || !claimable
|
||||
?
|
||||
'background:#b45309; opacity:.65; cursor:not-allowed;' :
|
||||
'background:#d97706; box-shadow:0 2px 12px rgba(0,0,0,.4);'"
|
||||
onmouseover="if(!this.disabled) this.style.filter='brightness(1.12)'"
|
||||
onmouseout="this.style.filter=''">
|
||||
<span x-text="claimButtonText()"></span>
|
||||
</button>
|
||||
<div style="background:rgba(0,0,0,.26); border-radius:999px; padding:10px 16px; margin-top:6px;">
|
||||
<div style="display:flex; justify-content:center;">
|
||||
<button x-on:click="doClaim()" :disabled="claiming || loadingStatus || !claimable"
|
||||
onmouseover="if(!this.disabled)this.style.transform='translateY(-2px) scale(1.04)'"
|
||||
onmouseout="this.style.transform=''"
|
||||
:style="claiming || loadingStatus || !claimable
|
||||
?
|
||||
'min-width:220px; padding:12px 28px; border-radius:999px; border:none; cursor:not-allowed; display:inline-flex; align-items:center; justify-content:center; gap:8px; box-sizing:border-box; transition:all .25s; font-size:18px; font-weight:900; letter-spacing:1px; background:#c8b89a; color:rgba(100,50,20,.45); box-shadow:none;'
|
||||
:
|
||||
'min-width:220px; padding:12px 28px; border-radius:999px; border:none; cursor:pointer; display:inline-flex; align-items:center; justify-content:center; gap:8px; box-sizing:border-box; transition:all .25s; font-size:18px; font-weight:900; letter-spacing:1px; background:#f5e6c8; color:#8b3a1a; box-shadow:0 8px 28px rgba(0,0,0,.28), inset 0 -4px 10px rgba(139,58,26,.12); animation:holiday-claim-pulse 1.8s ease-in-out infinite;'">
|
||||
<span style="font-size:22px; line-height:1;">🎁</span>
|
||||
<span x-text="claimButtonText()"></span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -117,6 +118,18 @@
|
||||
transform: scale(1.1) rotate(5deg);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes holiday-claim-pulse {
|
||||
|
||||
0%,
|
||||
100% {
|
||||
box-shadow: 0 8px 28px rgba(0, 0, 0, .28), inset 0 -4px 10px rgba(139, 58, 26, .12);
|
||||
}
|
||||
|
||||
50% {
|
||||
box-shadow: 0 10px 34px rgba(245, 230, 200, .18), 0 12px 30px rgba(0, 0, 0, .32), inset 0 -4px 10px rgba(139, 58, 26, .16);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
@@ -238,6 +251,51 @@
|
||||
return detail ?? {};
|
||||
}
|
||||
|
||||
/**
|
||||
* 缓存最近广播过的节日福利批次详情,供公屏消息按钮二次打开弹窗。
|
||||
*/
|
||||
window.__holidayRuns = window.__holidayRuns || {};
|
||||
|
||||
/**
|
||||
* 从公屏系统消息中打开指定节日福利批次弹窗。
|
||||
*
|
||||
* @param {number|string} runId
|
||||
*/
|
||||
window.openHolidayRunFromSystemMessage = function(runId) {
|
||||
const normalizedRunId = String(runId);
|
||||
const detail = window.__holidayRuns?.[normalizedRunId];
|
||||
const el = document.getElementById('holiday-event-modal');
|
||||
|
||||
if (!el || typeof Alpine === 'undefined') {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!detail) {
|
||||
window.chatDialog?.alert('当前福利批次信息未缓存,请等待下一轮广播或刷新页面后重试。', '提示', '#f59e0b');
|
||||
return;
|
||||
}
|
||||
|
||||
Alpine.$data(el).open(detail);
|
||||
};
|
||||
|
||||
/**
|
||||
* 构建公屏系统消息里的领取按钮,便于多个消息场景复用一致视觉。
|
||||
*
|
||||
* @param {number|string|null} runId
|
||||
* @param {string} label
|
||||
* @returns {string}
|
||||
*/
|
||||
function buildHolidayClaimActionButton(runId, label = '🎁 立即领取') {
|
||||
if (!runId) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return ` <button type="button"
|
||||
onclick="openHolidayRunFromSystemMessage(${JSON.stringify(String(runId))})"
|
||||
style="display:inline-flex; align-items:center; gap:4px; margin-left:8px; padding:3px 10px; border:none; border-radius:999px; background:linear-gradient(135deg,#f59e0b,#d97706); color:#fff; font-size:12px; font-weight:bold; cursor:pointer; box-shadow:0 2px 6px rgba(0,0,0,.22); vertical-align:middle;"
|
||||
title="点击领取本轮节日福利">${label}</button>`;
|
||||
}
|
||||
|
||||
function buildHolidaySystemMessage(detail) {
|
||||
const quotaText = detail.max_claimants > 0
|
||||
? `前 ${detail.max_claimants} 名在线用户可领取`
|
||||
@@ -247,6 +305,8 @@
|
||||
: '随机金额发放';
|
||||
const scheduleText = detail.scheduled_for ? `发放时间 ${formatHolidayDate(detail.scheduled_for)}` : null;
|
||||
const roundText = detail.round_label ? ` ${detail.round_label}` : '';
|
||||
const runId = firstHolidayDefined(detail.run_id, detail.id);
|
||||
const claimButtonHtml = buildHolidayClaimActionButton(runId);
|
||||
|
||||
return [
|
||||
`🎊 【${detail.name}】${roundText}开始啦!`,
|
||||
@@ -254,7 +314,7 @@
|
||||
amountText,
|
||||
quotaText,
|
||||
scheduleText,
|
||||
].filter(Boolean).join(',');
|
||||
].filter(Boolean).join(',') + claimButtonHtml;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -269,6 +329,7 @@
|
||||
claimable: true,
|
||||
claimed: false,
|
||||
expiresTimer: null,
|
||||
autoCloseTimer: null,
|
||||
|
||||
// 活动数据
|
||||
runId: null,
|
||||
@@ -326,6 +387,7 @@
|
||||
this.loadingStatus = false;
|
||||
this.claiming = false;
|
||||
this.claimedAmount = 0;
|
||||
this.stopAutoCloseTimer();
|
||||
this.statusHint = holidayDetail.run_id
|
||||
? '当前奖励按本轮福利批次发放,请在有效期内领取。'
|
||||
: '兼容旧活动通道,等待主线广播升级';
|
||||
@@ -341,6 +403,7 @@
|
||||
close() {
|
||||
this.show = false;
|
||||
this.stopExpiresTimer();
|
||||
this.stopAutoCloseTimer();
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -361,6 +424,24 @@
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 启动领取成功后的自动关闭计时器。
|
||||
*/
|
||||
startAutoCloseTimer() {
|
||||
this.stopAutoCloseTimer();
|
||||
this.autoCloseTimer = window.setTimeout(() => this.close(), 3000);
|
||||
},
|
||||
|
||||
/**
|
||||
* 停止自动关闭计时器。
|
||||
*/
|
||||
stopAutoCloseTimer() {
|
||||
if (this.autoCloseTimer) {
|
||||
window.clearTimeout(this.autoCloseTimer);
|
||||
this.autoCloseTimer = null;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 更新有效期显示文字
|
||||
*/
|
||||
@@ -514,7 +595,17 @@
|
||||
this.claimed = true;
|
||||
this.claimable = false;
|
||||
this.claimedAmount = claimedAmount;
|
||||
this.statusHint = data.message ?? '本轮福利已入账。';
|
||||
this.statusHint = `${data.message ?? '本轮福利已入账。'} 3 秒后自动关闭。`;
|
||||
|
||||
if (typeof appendSystemMessage === 'function') {
|
||||
const username = window.chatContext?.username ?? '当前用户';
|
||||
const roundText = this.roundLabel ? `【${this.roundLabel}】` : '';
|
||||
appendSystemMessage(
|
||||
`🌟 <b>${username}</b> 领取了【${this.eventName}】${roundText},获得 <b>${claimedAmount.toLocaleString()}</b> 金币!${buildHolidayClaimActionButton(this.runId)}`
|
||||
);
|
||||
}
|
||||
|
||||
this.startAutoCloseTimer();
|
||||
|
||||
if (window.__chatUser && data.balance !== undefined) {
|
||||
window.__chatUser.jjb = data.balance;
|
||||
@@ -550,6 +641,10 @@
|
||||
window.addEventListener('chat:holiday.started', (e) => {
|
||||
const detail = normalizeHolidayPayload(e.detail);
|
||||
|
||||
if (detail.run_id !== undefined && detail.run_id !== null) {
|
||||
window.__holidayRuns[String(detail.run_id)] = detail;
|
||||
}
|
||||
|
||||
// 公屏追加系统消息
|
||||
if (typeof appendSystemMessage === 'function') {
|
||||
appendSystemMessage(buildHolidaySystemMessage({
|
||||
|
||||
Reference in New Issue
Block a user