Files

327 lines
19 KiB
PHP
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{{-- 文件功能:后台节日福利活动列表页,展示节日福利模板并提供启用、立即触发、编辑、删除等操作。 --}}
@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="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>
</div>
{{-- 列表 --}}
<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="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 class="divide-y divide-gray-100">
@forelse ($events as $event)
@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>
<div class="max-w-md text-xs leading-6 text-slate-500">
{{ Str::limit($event->description ?: '未填写节日祝福语,当前活动将广播默认福利提示。', 32) }}
</div>
</div>
</td>
<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="px-4 py-4">
<div class="space-y-1.5">
<span
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>
</div>
</td>
<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="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="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="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="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>
</div>
</td>
</tr>
@empty
<tr>
<td colspan="8" class="px-6 py-16 text-center text-slate-400">
暂无节日福利活动,<a href="{{ route('admin.holiday-events.create') }}"
class="font-bold text-amber-500 hover:text-amber-600">立即创建一个</a>
</td>
</tr>
@endforelse
</tbody>
</table>
</div>
@if ($events->hasPages())
<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`, {
method: 'POST',
headers: {
'X-CSRF-TOKEN': '{{ csrf_token() }}',
'Accept': 'application/json',
},
})
.then(r => r.json())
.then(data => {
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);
}
});
}
</script>
@endsection