功能:婚姻系统第9步(后台管理页面)
Admin/MarriageManagerController:
- index() 总览统计卡片
- list() 婚姻列表(筛选/强制离婚/取消求婚)
- proposals() 求婚记录
- ceremonies() 婚礼红包记录
- claimDetail() 红包领取明细
- intimacyLogs() 亲密度日志(来源筛选)
- configs/updateConfigs 参数配置(批量保存)
- tiers/updateTier 婚礼档位管理
Views(7个页面):admin/marriages/{index|list|configs|tiers|ceremonies|claim-detail|proposals|intimacy-logs}
侧边栏:superlevel 区块新增「💒 婚姻管理」入口
This commit is contained in:
@@ -1,10 +1,249 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* 文件功能:后台婚姻系统管理控制器
|
||||
*
|
||||
* 提供总览统计、婚姻/求婚明细查询、婚礼档位管理、
|
||||
* 参数配置、亲密度日志审计、强制离婚等管理操作。
|
||||
*
|
||||
* @author ChatRoom Laravel
|
||||
*
|
||||
* @version 1.0.0
|
||||
*/
|
||||
|
||||
namespace App\Http\Controllers\Admin;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Marriage;
|
||||
use App\Models\MarriageIntimacyLog;
|
||||
use App\Models\WeddingCeremony;
|
||||
use App\Models\WeddingEnvelopeClaim;
|
||||
use App\Models\WeddingTier;
|
||||
use App\Services\MarriageConfigService;
|
||||
use App\Services\MarriageService;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\View\View;
|
||||
|
||||
class MarriageManagerController extends Controller
|
||||
{
|
||||
//
|
||||
public function __construct(
|
||||
private readonly MarriageConfigService $config,
|
||||
private readonly MarriageService $marriageService,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* 婚姻管理总览(统计卡片 + 最近记录)。
|
||||
*/
|
||||
public function index(): View
|
||||
{
|
||||
$stats = [
|
||||
'total_married' => Marriage::where('status', 'married')->count(),
|
||||
'total_pending' => Marriage::where('status', 'pending')->count(),
|
||||
'total_divorced' => Marriage::where('status', 'divorced')->count(),
|
||||
'total_weddings' => WeddingCeremony::whereIn('status', ['active', 'completed'])->count(),
|
||||
'total_envelopes' => WeddingEnvelopeClaim::sum('amount'),
|
||||
'claimed_amount' => WeddingEnvelopeClaim::where('claimed', true)->sum('amount'),
|
||||
];
|
||||
|
||||
$recentMarriages = Marriage::with(['user:id,username', 'partner:id,username', 'ringItem:id,name,icon'])
|
||||
->where('status', 'married')
|
||||
->orderByDesc('married_at')
|
||||
->limit(10)
|
||||
->get();
|
||||
|
||||
$recentDivorces = Marriage::with(['user:id,username', 'partner:id,username'])
|
||||
->where('status', 'divorced')
|
||||
->orderByDesc('divorced_at')
|
||||
->limit(8)
|
||||
->get();
|
||||
|
||||
return view('admin.marriages.index', compact('stats', 'recentMarriages', 'recentDivorces'));
|
||||
}
|
||||
|
||||
/**
|
||||
* 婚姻列表(支持按状态/用户名筛选)。
|
||||
*/
|
||||
public function list(Request $request): View
|
||||
{
|
||||
$query = Marriage::with(['user:id,username', 'partner:id,username', 'ringItem:id,name,icon'])
|
||||
->orderByDesc('id');
|
||||
|
||||
if ($status = $request->get('status')) {
|
||||
$query->where('status', $status);
|
||||
}
|
||||
if ($search = $request->get('search')) {
|
||||
$query->where(function ($q) use ($search) {
|
||||
$q->whereHas('user', fn ($u) => $u->where('username', 'like', "%{$search}%"))
|
||||
->orWhereHas('partner', fn ($u) => $u->where('username', 'like', "%{$search}%"));
|
||||
});
|
||||
}
|
||||
|
||||
$marriages = $query->paginate(20)->withQueryString();
|
||||
|
||||
return view('admin.marriages.list', compact('marriages'));
|
||||
}
|
||||
|
||||
/**
|
||||
* 求婚记录列表(含 pending/expired/rejected)。
|
||||
*/
|
||||
public function proposals(Request $request): View
|
||||
{
|
||||
$proposals = Marriage::with(['user:id,username', 'partner:id,username', 'ringItem:id,name,icon'])
|
||||
->whereIn('status', ['pending', 'expired', 'rejected'])
|
||||
->orderByDesc('proposed_at')
|
||||
->paginate(20)
|
||||
->withQueryString();
|
||||
|
||||
return view('admin.marriages.proposals', compact('proposals'));
|
||||
}
|
||||
|
||||
/**
|
||||
* 婚礼红包记录。
|
||||
*/
|
||||
public function ceremonies(Request $request): View
|
||||
{
|
||||
$ceremonies = WeddingCeremony::with([
|
||||
'marriage.user:id,username',
|
||||
'marriage.partner:id,username',
|
||||
'tier:id,name,tier,icon',
|
||||
])
|
||||
->orderByDesc('id')
|
||||
->paginate(20)
|
||||
->withQueryString();
|
||||
|
||||
return view('admin.marriages.ceremonies', compact('ceremonies'));
|
||||
}
|
||||
|
||||
/**
|
||||
* 红包领取明细(某场婚礼)。
|
||||
*/
|
||||
public function claimDetail(WeddingCeremony $ceremony): View
|
||||
{
|
||||
$ceremony->load(['marriage.user:id,username', 'marriage.partner:id,username', 'tier:id,name,icon']);
|
||||
|
||||
$claims = WeddingEnvelopeClaim::with('user:id,username,headface')
|
||||
->where('ceremony_id', $ceremony->id)
|
||||
->orderBy('amount', 'desc')
|
||||
->paginate(30);
|
||||
|
||||
return view('admin.marriages.claim-detail', compact('ceremony', 'claims'));
|
||||
}
|
||||
|
||||
/**
|
||||
* 亲密度日志列表(支持按用户筛选)。
|
||||
*/
|
||||
public function intimacyLogs(Request $request): View
|
||||
{
|
||||
$query = MarriageIntimacyLog::with(['marriage.user:id,username', 'marriage.partner:id,username'])
|
||||
->orderByDesc('id');
|
||||
|
||||
if ($search = $request->get('search')) {
|
||||
$query->whereHas('marriage', function ($q) use ($search) {
|
||||
$q->whereHas('user', fn ($u) => $u->where('username', 'like', "%{$search}%"))
|
||||
->orWhereHas('partner', fn ($u) => $u->where('username', 'like', "%{$search}%"));
|
||||
});
|
||||
}
|
||||
if ($source = $request->get('source')) {
|
||||
$query->where('source', $source);
|
||||
}
|
||||
|
||||
$logs = $query->paginate(30)->withQueryString();
|
||||
|
||||
return view('admin.marriages.intimacy-logs', compact('logs'));
|
||||
}
|
||||
|
||||
/**
|
||||
* 参数配置页面(读取所有分组配置)。
|
||||
*/
|
||||
public function configs(): View
|
||||
{
|
||||
$groups = $this->config->allGrouped();
|
||||
|
||||
return view('admin.marriages.configs', compact('groups'));
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量保存参数配置。
|
||||
*/
|
||||
public function updateConfigs(Request $request): RedirectResponse
|
||||
{
|
||||
$data = $request->validate([
|
||||
'configs' => 'required|array',
|
||||
'configs.*' => 'required|integer',
|
||||
]);
|
||||
|
||||
$this->config->batchSet($data['configs']);
|
||||
|
||||
return redirect()->route('admin.marriages.configs')->with('success', '婚姻参数配置已保存!');
|
||||
}
|
||||
|
||||
/**
|
||||
* 婚礼档位配置页面。
|
||||
*/
|
||||
public function tiers(): View
|
||||
{
|
||||
$tiers = WeddingTier::orderBy('tier')->get();
|
||||
|
||||
return view('admin.marriages.tiers', compact('tiers'));
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新婚礼档位。
|
||||
*/
|
||||
public function updateTier(Request $request, WeddingTier $tier): RedirectResponse
|
||||
{
|
||||
$data = $request->validate([
|
||||
'name' => 'required|string|max:30',
|
||||
'icon' => 'required|string|max:20',
|
||||
'amount' => 'required|integer|min:1',
|
||||
'description' => 'nullable|string|max:100',
|
||||
'is_active' => 'boolean',
|
||||
]);
|
||||
|
||||
$data['is_active'] = $request->boolean('is_active', true);
|
||||
$tier->update($data);
|
||||
|
||||
return redirect()->route('admin.marriages.tiers')->with('success', "档位【{$tier->name}】已更新!");
|
||||
}
|
||||
|
||||
/**
|
||||
* 管理员强制离婚。
|
||||
*/
|
||||
public function forceDissolve(Request $request, Marriage $marriage): RedirectResponse
|
||||
{
|
||||
$data = $request->validate([
|
||||
'admin_note' => 'required|string|max:200',
|
||||
]);
|
||||
|
||||
if ($marriage->status !== 'married') {
|
||||
return back()->with('error', '该婚姻不是已婚状态,无法操作。');
|
||||
}
|
||||
|
||||
$admin = $request->user();
|
||||
$result = $this->marriageService->forceDissolve($marriage, $admin, true);
|
||||
|
||||
// 写入管理员备注
|
||||
$marriage->update(['admin_note' => $data['admin_note']]);
|
||||
|
||||
$msg = $result['ok'] ? '强制离婚已完成。' : $result['message'];
|
||||
$type = $result['ok'] ? 'success' : 'error';
|
||||
|
||||
return back()->with($type, $msg);
|
||||
}
|
||||
|
||||
/**
|
||||
* 管理员取消求婚(释放戒指 → 退还状态 active)。
|
||||
*/
|
||||
public function cancelProposal(Request $request, Marriage $marriage): RedirectResponse
|
||||
{
|
||||
if ($marriage->status !== 'pending') {
|
||||
return back()->with('error', '该求婚不是进行中状态,无法取消。');
|
||||
}
|
||||
|
||||
$this->marriageService->expireProposal($marriage);
|
||||
$marriage->update(['admin_note' => '管理员手动取消求婚:'.($request->input('reason', ''))]);
|
||||
|
||||
return back()->with('success', '求婚已取消,戒指标记遗失。');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -67,6 +67,10 @@
|
||||
class="block px-4 py-3 rounded-md transition {{ request()->routeIs('admin.vip.*') ? 'bg-indigo-600 font-bold' : 'hover:bg-white/10' }}">
|
||||
{!! '👑 VIP 会员等级' . $ro !!}
|
||||
</a>
|
||||
<a href="{{ route('admin.marriages.index') }}"
|
||||
class="block px-4 py-3 rounded-md transition {{ request()->routeIs('admin.marriages.*') ? 'bg-indigo-600 font-bold' : 'hover:bg-white/10' }}">
|
||||
{!! '💒 婚姻管理' . $ro !!}
|
||||
</a>
|
||||
<a href="{{ route('admin.departments.index') }}"
|
||||
class="block px-4 py-3 rounded-md transition {{ request()->routeIs('admin.departments.*') ? 'bg-indigo-600 font-bold' : 'hover:bg-white/10' }}">
|
||||
{!! '🏛️ 部门管理' . $ro !!}
|
||||
|
||||
104
resources/views/admin/marriages/ceremonies.blade.php
Normal file
104
resources/views/admin/marriages/ceremonies.blade.php
Normal file
@@ -0,0 +1,104 @@
|
||||
{{--
|
||||
文件功能:婚礼仪式记录页(含红包分发详情链接)
|
||||
|
||||
@author ChatRoom Laravel
|
||||
@version 1.0.0
|
||||
--}}
|
||||
|
||||
@extends('admin.layouts.app')
|
||||
|
||||
@section('title', '婚礼红包记录')
|
||||
|
||||
@section('content')
|
||||
|
||||
<div class="flex items-center justify-between mb-5">
|
||||
<h2 class="text-xl font-bold text-gray-800">🎊 婚礼红包记录</h2>
|
||||
<a href="{{ route('admin.marriages.index') }}" class="text-sm text-indigo-600 hover:underline">← 返回总览</a>
|
||||
</div>
|
||||
|
||||
<div class="bg-white rounded-xl shadow-sm border overflow-hidden">
|
||||
<div class="overflow-x-auto">
|
||||
<table class="w-full text-sm">
|
||||
<thead class="bg-gray-50 text-gray-600 text-xs uppercase">
|
||||
<tr>
|
||||
<th class="px-4 py-3 text-left">ID</th>
|
||||
<th class="px-4 py-3 text-left">新人</th>
|
||||
<th class="px-4 py-3 text-left">档位</th>
|
||||
<th class="px-4 py-3 text-center">总金额</th>
|
||||
<th class="px-4 py-3 text-center">在线人数</th>
|
||||
<th class="px-4 py-3 text-center">领取进度</th>
|
||||
<th class="px-4 py-3 text-center">状态</th>
|
||||
<th class="px-4 py-3 text-left">时间</th>
|
||||
<th class="px-4 py-3 text-center">详情</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="divide-y">
|
||||
@forelse ($ceremonies as $c)
|
||||
<tr class="hover:bg-gray-50">
|
||||
<td class="px-4 py-3 text-gray-400 font-mono text-xs">{{ $c->id }}</td>
|
||||
<td class="px-4 py-3">
|
||||
<span class="font-bold">{{ $c->marriage?->user?->username }}</span>
|
||||
<span class="text-gray-400 mx-1">×</span>
|
||||
<span class="font-bold">{{ $c->marriage?->partner?->username }}</span>
|
||||
</td>
|
||||
<td class="px-4 py-3">
|
||||
{{ $c->tier?->icon }} {{ $c->tier?->name ?? '—' }}
|
||||
</td>
|
||||
<td class="px-4 py-3 text-center font-bold text-amber-600">
|
||||
{{ number_format($c->total_amount) }} 金
|
||||
</td>
|
||||
<td class="px-4 py-3 text-center text-gray-600">
|
||||
{{ $c->online_count ?? '—' }}
|
||||
</td>
|
||||
<td class="px-4 py-3 text-center">
|
||||
@if ($c->total_amount > 0)
|
||||
<div class="text-xs text-gray-600">
|
||||
{{ $c->claimed_count }} / {{ $c->online_count ?? '?' }} 人
|
||||
</div>
|
||||
<div class="text-xs text-emerald-600 font-bold">
|
||||
{{ number_format($c->claimed_amount) }} / {{ number_format($c->total_amount) }}
|
||||
</div>
|
||||
@else
|
||||
<span class="text-gray-300 text-xs">无红包</span>
|
||||
@endif
|
||||
</td>
|
||||
<td class="px-4 py-3 text-center">
|
||||
<span
|
||||
class="px-2 py-0.5 rounded-full text-xs font-bold
|
||||
{{ match ($c->status) {
|
||||
'completed' => 'bg-green-100 text-green-700',
|
||||
'active' => 'bg-blue-100 text-blue-700',
|
||||
'pending' => 'bg-amber-100 text-amber-700',
|
||||
'expired' => 'bg-gray-100 text-gray-500',
|
||||
'cancelled' => 'bg-red-100 text-red-600',
|
||||
default => 'bg-gray-100 text-gray-600',
|
||||
} }}">
|
||||
{{ ['completed' => '已完成', 'active' => '进行中', 'pending' => '待触发', 'expired' => '已过期', 'cancelled' => '已取消'][$c->status] ?? $c->status }}
|
||||
</span>
|
||||
</td>
|
||||
<td class="px-4 py-3 text-xs text-gray-500">
|
||||
{{ $c->ceremony_at?->format('Y-m-d H:i') }}
|
||||
</td>
|
||||
<td class="px-4 py-3 text-center">
|
||||
@if ($c->total_amount > 0)
|
||||
<a href="{{ route('admin.marriages.claim-detail', $c->id) }}"
|
||||
class="text-xs text-indigo-600 hover:underline font-bold">明细</a>
|
||||
@else
|
||||
<span class="text-gray-300 text-xs">—</span>
|
||||
@endif
|
||||
</td>
|
||||
</tr>
|
||||
@empty
|
||||
<tr>
|
||||
<td colspan="9" class="px-4 py-12 text-center text-gray-400">暂无婚礼记录</td>
|
||||
</tr>
|
||||
@endforelse
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
@if ($ceremonies->hasPages())
|
||||
<div class="px-4 py-4 border-t">{{ $ceremonies->links() }}</div>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
@endsection
|
||||
85
resources/views/admin/marriages/claim-detail.blade.php
Normal file
85
resources/views/admin/marriages/claim-detail.blade.php
Normal file
@@ -0,0 +1,85 @@
|
||||
{{--
|
||||
文件功能:婚礼红包领取明细页
|
||||
|
||||
@author ChatRoom Laravel
|
||||
@version 1.0.0
|
||||
--}}
|
||||
|
||||
@extends('admin.layouts.app')
|
||||
|
||||
@section('title', '婚礼红包明细')
|
||||
|
||||
@section('content')
|
||||
|
||||
<div class="flex items-center justify-between mb-5">
|
||||
<div>
|
||||
<h2 class="text-xl font-bold text-gray-800">🎁 红包领取明细</h2>
|
||||
<p class="text-sm text-gray-500 mt-1">
|
||||
{{ $ceremony->marriage?->user?->username }} × {{ $ceremony->marriage?->partner?->username }}
|
||||
— {{ $ceremony->tier?->icon }} {{ $ceremony->tier?->name ?? '婚礼' }}
|
||||
· 总额 <strong class="text-amber-600">{{ number_format($ceremony->total_amount) }} 金</strong>
|
||||
</p>
|
||||
</div>
|
||||
<a href="{{ route('admin.marriages.ceremonies') }}" class="text-sm text-indigo-600 hover:underline">← 返回婚礼列表</a>
|
||||
</div>
|
||||
|
||||
{{-- 汇总 --}}
|
||||
<div class="grid grid-cols-3 gap-4 mb-5">
|
||||
<div class="bg-white rounded-xl border shadow-sm p-4 text-center">
|
||||
<div class="text-2xl font-bold text-indigo-600">{{ $ceremony->online_count ?? 0 }}</div>
|
||||
<div class="text-xs text-gray-500 mt-1">在线人数</div>
|
||||
</div>
|
||||
<div class="bg-white rounded-xl border shadow-sm p-4 text-center">
|
||||
<div class="text-2xl font-bold text-green-600">{{ $ceremony->claimed_count }}</div>
|
||||
<div class="text-xs text-gray-500 mt-1">已领取人数</div>
|
||||
</div>
|
||||
<div class="bg-white rounded-xl border shadow-sm p-4 text-center">
|
||||
<div class="text-2xl font-bold text-amber-600">{{ number_format($ceremony->claimed_amount) }}</div>
|
||||
<div class="text-xs text-gray-500 mt-1">已领取金额</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="bg-white rounded-xl shadow-sm border overflow-hidden">
|
||||
<div class="overflow-x-auto">
|
||||
<table class="w-full text-sm">
|
||||
<thead class="bg-gray-50 text-xs uppercase text-gray-600">
|
||||
<tr>
|
||||
<th class="px-4 py-3 text-left">用户</th>
|
||||
<th class="px-4 py-3 text-center">分配金额</th>
|
||||
<th class="px-4 py-3 text-center">状态</th>
|
||||
<th class="px-4 py-3 text-left">领取时间</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="divide-y">
|
||||
@forelse ($claims as $claim)
|
||||
<tr class="hover:bg-gray-50">
|
||||
<td class="px-4 py-3 font-bold">{{ $claim->user?->username }}</td>
|
||||
<td class="px-4 py-3 text-center font-bold text-amber-600">
|
||||
{{ number_format($claim->amount) }} 金
|
||||
</td>
|
||||
<td class="px-4 py-3 text-center">
|
||||
@if ($claim->claimed)
|
||||
<span class="px-2 py-0.5 rounded-full text-xs bg-green-100 text-green-700 font-bold">✅
|
||||
已领取</span>
|
||||
@else
|
||||
<span class="px-2 py-0.5 rounded-full text-xs bg-gray-100 text-gray-500">未领取</span>
|
||||
@endif
|
||||
</td>
|
||||
<td class="px-4 py-3 text-xs text-gray-500">
|
||||
{{ $claim->claimed_at?->format('H:i:s') ?? '—' }}
|
||||
</td>
|
||||
</tr>
|
||||
@empty
|
||||
<tr>
|
||||
<td colspan="4" class="px-4 py-12 text-center text-gray-400">暂无领取记录</td>
|
||||
</tr>
|
||||
@endforelse
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
@if ($claims->hasPages())
|
||||
<div class="px-4 py-4 border-t">{{ $claims->links() }}</div>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
@endsection
|
||||
75
resources/views/admin/marriages/configs.blade.php
Normal file
75
resources/views/admin/marriages/configs.blade.php
Normal file
@@ -0,0 +1,75 @@
|
||||
{{--
|
||||
文件功能:婚姻参数配置页(所有参数按分组展示,可批量保存)
|
||||
|
||||
@author ChatRoom Laravel
|
||||
@version 1.0.0
|
||||
--}}
|
||||
|
||||
@extends('admin.layouts.app')
|
||||
|
||||
@section('title', '婚姻参数配置')
|
||||
|
||||
@section('content')
|
||||
|
||||
<div class="flex items-center justify-between mb-5">
|
||||
<div>
|
||||
<h2 class="text-xl font-bold text-gray-800">⚙️ 婚姻参数配置</h2>
|
||||
<p class="text-sm text-gray-500 mt-1">修改参数后点击「保存」生效,参数变更会自动清除缓存</p>
|
||||
</div>
|
||||
<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
|
||||
|
||||
@foreach ($groups as $groupName => $configs)
|
||||
<div class="bg-white rounded-xl shadow-sm border overflow-hidden mb-5">
|
||||
<div class="px-5 py-3 bg-gray-50 border-b flex items-center gap-2">
|
||||
<span class="font-bold text-gray-700">{{ $groupName }}</span>
|
||||
<span class="text-xs text-gray-400">({{ $configs->count() }} 项)</span>
|
||||
</div>
|
||||
<div class="divide-y">
|
||||
@foreach ($configs as $cfg)
|
||||
<div class="px-5 py-4 flex items-center gap-4">
|
||||
<div class="flex-1 min-w-0">
|
||||
<div class="font-bold text-gray-800 text-sm">{{ $cfg->label }}</div>
|
||||
@if ($cfg->description)
|
||||
<div class="text-xs text-gray-400 mt-0.5">{{ $cfg->description }}</div>
|
||||
@endif
|
||||
<div class="text-xs font-mono text-gray-300 mt-0.5">{{ $cfg->key }}</div>
|
||||
</div>
|
||||
<div class="flex items-center gap-2 shrink-0">
|
||||
@if ($cfg->min !== null)
|
||||
<span class="text-xs text-gray-400">最小: {{ $cfg->min }}</span>
|
||||
@endif
|
||||
<input type="number" name="configs[{{ $cfg->key }}]" value="{{ $cfg->value }}"
|
||||
@if ($cfg->min !== null) min="{{ $cfg->min }}" @endif
|
||||
@if ($cfg->max !== null) max="{{ $cfg->max }}" @endif required
|
||||
class="border rounded-lg px-3 py-1.5 text-sm w-28 text-center font-bold focus:outline-none focus:ring-2 focus:ring-indigo-300">
|
||||
@if ($cfg->max !== null)
|
||||
<span class="text-xs text-gray-400">最大: {{ $cfg->max }}</span>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
@endforeach
|
||||
</div>
|
||||
</div>
|
||||
@endforeach
|
||||
|
||||
<div class="flex justify-end gap-3 mt-2 pb-6">
|
||||
<a href="{{ route('admin.marriages.index') }}"
|
||||
class="px-5 py-2.5 border rounded-lg text-sm text-gray-600 hover:bg-gray-50">取消</a>
|
||||
<button type="submit"
|
||||
class="px-6 py-2.5 bg-indigo-600 text-white rounded-lg font-bold text-sm hover:bg-indigo-700 shadow-sm transition">
|
||||
💾 保存所有配置
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
@endsection
|
||||
145
resources/views/admin/marriages/index.blade.php
Normal file
145
resources/views/admin/marriages/index.blade.php
Normal file
@@ -0,0 +1,145 @@
|
||||
{{--
|
||||
文件功能:后台婚姻管理总览页
|
||||
统计卡片 + 最近结婚记录 + 最近离婚记录
|
||||
|
||||
@author ChatRoom Laravel
|
||||
@version 1.0.0
|
||||
--}}
|
||||
|
||||
@extends('admin.layouts.app')
|
||||
|
||||
@section('title', '婚姻管理 - 总览')
|
||||
|
||||
@section('content')
|
||||
|
||||
{{-- 页面标题 --}}
|
||||
<div class="flex items-center justify-between mb-6">
|
||||
<div>
|
||||
<h2 class="text-xl font-bold text-gray-800">💒 婚姻管理</h2>
|
||||
<p class="text-sm text-gray-500 mt-1">婚姻系统总览,管理求婚、结婚、婚礼及参数配置</p>
|
||||
</div>
|
||||
<div class="flex gap-2">
|
||||
<a href="{{ route('admin.marriages.configs') }}"
|
||||
class="bg-indigo-600 text-white px-4 py-2 rounded-lg text-sm font-bold hover:bg-indigo-700 transition">
|
||||
⚙️ 参数配置
|
||||
</a>
|
||||
<a href="{{ route('admin.marriages.tiers') }}"
|
||||
class="bg-pink-600 text-white px-4 py-2 rounded-lg text-sm font-bold hover:bg-pink-700 transition">
|
||||
🎊 婚礼档位
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- 统计卡片 --}}
|
||||
<div class="grid grid-cols-2 md:grid-cols-3 xl:grid-cols-6 gap-4 mb-8">
|
||||
<div class="bg-white rounded-xl shadow-sm border p-4 text-center">
|
||||
<div class="text-2xl mb-1">💑</div>
|
||||
<div class="text-2xl font-bold text-indigo-600">{{ $stats['total_married'] }}</div>
|
||||
<div class="text-xs text-gray-500 mt-1">当前已婚</div>
|
||||
</div>
|
||||
<div class="bg-white rounded-xl shadow-sm border p-4 text-center">
|
||||
<div class="text-2xl mb-1">💍</div>
|
||||
<div class="text-2xl font-bold text-amber-500">{{ $stats['total_pending'] }}</div>
|
||||
<div class="text-xs text-gray-500 mt-1">求婚中</div>
|
||||
</div>
|
||||
<div class="bg-white rounded-xl shadow-sm border p-4 text-center">
|
||||
<div class="text-2xl mb-1">💔</div>
|
||||
<div class="text-2xl font-bold text-red-500">{{ $stats['total_divorced'] }}</div>
|
||||
<div class="text-xs text-gray-500 mt-1">已离婚</div>
|
||||
</div>
|
||||
<div class="bg-white rounded-xl shadow-sm border p-4 text-center">
|
||||
<div class="text-2xl mb-1">🎊</div>
|
||||
<div class="text-2xl font-bold text-pink-500">{{ $stats['total_weddings'] }}</div>
|
||||
<div class="text-xs text-gray-500 mt-1">婚礼场次</div>
|
||||
</div>
|
||||
<div class="bg-white rounded-xl shadow-sm border p-4 text-center">
|
||||
<div class="text-2xl mb-1">🎁</div>
|
||||
<div class="text-2xl font-bold text-green-600">{{ number_format($stats['total_envelopes']) }}</div>
|
||||
<div class="text-xs text-gray-500 mt-1">红包总额(金)</div>
|
||||
</div>
|
||||
<div class="bg-white rounded-xl shadow-sm border p-4 text-center">
|
||||
<div class="text-2xl mb-1">✅</div>
|
||||
<div class="text-2xl font-bold text-emerald-600">{{ number_format($stats['claimed_amount']) }}</div>
|
||||
<div class="text-xs text-gray-500 mt-1">已领取(金)</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- 快捷入口 --}}
|
||||
<div class="grid grid-cols-2 md:grid-cols-4 gap-3 mb-8">
|
||||
@foreach ([['route' => 'admin.marriages.list', 'icon' => '📋', 'label' => '婚姻列表', 'color' => 'blue'], ['route' => 'admin.marriages.proposals', 'icon' => '💌', 'label' => '求婚记录', 'color' => 'amber'], ['route' => 'admin.marriages.ceremonies', 'icon' => '🎊', 'label' => '婚礼红包', 'color' => 'pink'], ['route' => 'admin.marriages.intimacy-logs', 'icon' => '💞', 'label' => '亲密度日志', 'color' => 'purple']] as $item)
|
||||
<a href="{{ route($item['route']) }}"
|
||||
class="bg-white rounded-xl border shadow-sm p-5 flex flex-col items-center gap-2 hover:shadow-md hover:border-{{ $item['color'] }}-300 transition group">
|
||||
<span class="text-3xl">{{ $item['icon'] }}</span>
|
||||
<span
|
||||
class="text-sm font-bold text-gray-700 group-hover:text-{{ $item['color'] }}-600">{{ $item['label'] }}</span>
|
||||
</a>
|
||||
@endforeach
|
||||
</div>
|
||||
|
||||
{{-- 最近已婚 --}}
|
||||
<div class="grid grid-cols-1 xl:grid-cols-2 gap-6">
|
||||
<div class="bg-white rounded-xl shadow-sm border overflow-hidden">
|
||||
<div class="px-5 py-4 flex justify-between items-center border-b bg-gray-50">
|
||||
<h3 class="font-bold text-gray-700">💑 最近结婚</h3>
|
||||
<a href="{{ route('admin.marriages.list') }}" class="text-xs text-indigo-600 hover:underline">查看全部</a>
|
||||
</div>
|
||||
<div class="divide-y">
|
||||
@forelse ($recentMarriages as $m)
|
||||
<div class="px-5 py-3 flex items-center justify-between text-sm">
|
||||
<div class="flex items-center gap-2">
|
||||
<span class="text-lg">{{ $m->ringItem?->icon ?? '💍' }}</span>
|
||||
<div>
|
||||
<span class="font-bold">{{ $m->user?->username }}</span>
|
||||
<span class="text-gray-400 mx-1">×</span>
|
||||
<span class="font-bold">{{ $m->partner?->username }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-right">
|
||||
<div class="text-gray-500">{{ $m->married_at?->format('m/d') }}</div>
|
||||
<div class="text-xs text-indigo-500">
|
||||
{{ \App\Services\MarriageIntimacyService::levelName($m->level) }}</div>
|
||||
</div>
|
||||
</div>
|
||||
@empty
|
||||
<div class="px-5 py-8 text-center text-gray-400">暂无已婚记录</div>
|
||||
@endforelse
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- 最近离婚 --}}
|
||||
<div class="bg-white rounded-xl shadow-sm border overflow-hidden">
|
||||
<div class="px-5 py-4 flex justify-between items-center border-b bg-gray-50">
|
||||
<h3 class="font-bold text-gray-700">💔 最近离婚</h3>
|
||||
<a href="{{ route('admin.marriages.list', ['status' => 'divorced']) }}"
|
||||
class="text-xs text-red-600 hover:underline">查看全部</a>
|
||||
</div>
|
||||
<div class="divide-y">
|
||||
@forelse ($recentDivorces as $m)
|
||||
<div class="px-5 py-3 flex items-center justify-between text-sm">
|
||||
<div>
|
||||
<span class="font-bold">{{ $m->user?->username }}</span>
|
||||
<span class="text-gray-400 mx-1">×</span>
|
||||
<span class="font-bold">{{ $m->partner?->username }}</span>
|
||||
</div>
|
||||
<div class="text-right">
|
||||
<span
|
||||
class="text-xs px-2 py-0.5 rounded-full font-bold
|
||||
{{ match ($m->divorce_type) {
|
||||
'forced' => 'bg-red-100 text-red-600',
|
||||
'auto' => 'bg-orange-100 text-orange-600',
|
||||
'admin' => 'bg-purple-100 text-purple-600',
|
||||
default => 'bg-gray-100 text-gray-600',
|
||||
} }}">
|
||||
{{ match ($m->divorce_type) {'forced' => '强制','auto' => '超时','admin' => '管理员',default => '协议'} }}
|
||||
</span>
|
||||
<div class="text-xs text-gray-400 mt-0.5">{{ $m->divorced_at?->format('m/d') }}</div>
|
||||
</div>
|
||||
</div>
|
||||
@empty
|
||||
<div class="px-5 py-8 text-center text-gray-400">暂无离婚记录</div>
|
||||
@endforelse
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@endsection
|
||||
97
resources/views/admin/marriages/intimacy-logs.blade.php
Normal file
97
resources/views/admin/marriages/intimacy-logs.blade.php
Normal file
@@ -0,0 +1,97 @@
|
||||
{{--
|
||||
文件功能:亲密度变更日志审计页
|
||||
|
||||
@author ChatRoom Laravel
|
||||
@version 1.0.0
|
||||
--}}
|
||||
|
||||
@extends('admin.layouts.app')
|
||||
|
||||
@section('title', '亲密度日志')
|
||||
|
||||
@section('content')
|
||||
|
||||
<div class="flex items-center justify-between mb-5">
|
||||
<h2 class="text-xl font-bold text-gray-800">💞 亲密度变更日志</h2>
|
||||
<a href="{{ route('admin.marriages.index') }}" class="text-sm text-indigo-600 hover:underline">← 返回总览</a>
|
||||
</div>
|
||||
|
||||
{{-- 筛选 --}}
|
||||
<form method="GET" class="bg-white rounded-xl border shadow-sm p-4 mb-5 flex flex-wrap gap-3 items-end">
|
||||
<div>
|
||||
<label class="block text-xs font-bold text-gray-600 mb-1">搜索用户名</label>
|
||||
<input type="text" name="search" value="{{ request('search') }}" placeholder="双方任一用户名..."
|
||||
class="border rounded-lg px-3 py-2 text-sm w-44 focus:outline-none">
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-xs font-bold text-gray-600 mb-1">来源(Source)</label>
|
||||
<select name="source" class="border rounded-lg px-3 py-2 text-sm focus:outline-none">
|
||||
<option value="">全部</option>
|
||||
@foreach (\App\Enums\IntimacySource::cases() as $src)
|
||||
<option value="{{ $src->value }}" @selected(request('source') === $src->value)>
|
||||
{{ $src->label() }}
|
||||
</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
<button type="submit"
|
||||
class="bg-indigo-600 text-white px-4 py-2 rounded-lg text-sm font-bold hover:bg-indigo-700 transition">
|
||||
筛选
|
||||
</button>
|
||||
<a href="{{ route('admin.marriages.intimacy-logs') }}" class="text-sm text-gray-500 hover:underline py-2">重置</a>
|
||||
</form>
|
||||
|
||||
<div class="bg-white rounded-xl shadow-sm border overflow-hidden">
|
||||
<div class="overflow-x-auto">
|
||||
<table class="w-full text-sm">
|
||||
<thead class="bg-gray-50 text-xs uppercase text-gray-600">
|
||||
<tr>
|
||||
<th class="px-4 py-3 text-left">ID</th>
|
||||
<th class="px-4 py-3 text-left">婚姻双方</th>
|
||||
<th class="px-4 py-3 text-center">变动量</th>
|
||||
<th class="px-4 py-3 text-center">变后余额</th>
|
||||
<th class="px-4 py-3 text-left">来源</th>
|
||||
<th class="px-4 py-3 text-left">备注</th>
|
||||
<th class="px-4 py-3 text-left">时间</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="divide-y">
|
||||
@forelse ($logs as $log)
|
||||
<tr class="hover:bg-gray-50">
|
||||
<td class="px-4 py-3 text-gray-400 font-mono text-xs">{{ $log->id }}</td>
|
||||
<td class="px-4 py-3">
|
||||
<span class="font-bold">{{ $log->marriage?->user?->username }}</span>
|
||||
<span class="text-gray-400 mx-1">×</span>
|
||||
<span class="font-bold">{{ $log->marriage?->partner?->username }}</span>
|
||||
</td>
|
||||
<td
|
||||
class="px-4 py-3 text-center font-bold {{ $log->amount >= 0 ? 'text-green-600' : 'text-red-500' }}">
|
||||
{{ $log->amount >= 0 ? '+' : '' }}{{ $log->amount }}
|
||||
</td>
|
||||
<td class="px-4 py-3 text-center text-indigo-600 font-bold">
|
||||
{{ number_format($log->balance_after) }}
|
||||
</td>
|
||||
<td class="px-4 py-3">
|
||||
<span class="text-xs bg-purple-50 text-purple-700 px-2 py-0.5 rounded-full font-mono">
|
||||
{{ \App\Enums\IntimacySource::tryFrom($log->source)?->label() ?? $log->source }}
|
||||
</span>
|
||||
</td>
|
||||
<td class="px-4 py-3 text-xs text-gray-500">{{ $log->remark ?? '—' }}</td>
|
||||
<td class="px-4 py-3 text-xs text-gray-400">
|
||||
{{ $log->created_at?->format('m-d H:i') }}
|
||||
</td>
|
||||
</tr>
|
||||
@empty
|
||||
<tr>
|
||||
<td colspan="7" class="px-4 py-12 text-center text-gray-400">暂无记录</td>
|
||||
</tr>
|
||||
@endforelse
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
@if ($logs->hasPages())
|
||||
<div class="px-4 py-4 border-t">{{ $logs->links() }}</div>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
@endsection
|
||||
162
resources/views/admin/marriages/list.blade.php
Normal file
162
resources/views/admin/marriages/list.blade.php
Normal file
@@ -0,0 +1,162 @@
|
||||
{{--
|
||||
文件功能:后台婚姻列表页(支持状态筛选/用户名搜索)
|
||||
|
||||
@author ChatRoom Laravel
|
||||
@version 1.0.0
|
||||
--}}
|
||||
|
||||
@extends('admin.layouts.app')
|
||||
|
||||
@section('title', '婚姻列表')
|
||||
|
||||
@section('content')
|
||||
|
||||
<div class="flex items-center justify-between mb-5">
|
||||
<h2 class="text-xl font-bold text-gray-800">📋 婚姻列表</h2>
|
||||
<a href="{{ route('admin.marriages.index') }}" class="text-sm text-indigo-600 hover:underline">← 返回总览</a>
|
||||
</div>
|
||||
|
||||
{{-- 筛选栏 --}}
|
||||
<form method="GET" class="bg-white rounded-xl border shadow-sm p-4 mb-5 flex flex-wrap gap-3 items-end">
|
||||
<div>
|
||||
<label class="block text-xs font-bold text-gray-600 mb-1">状态</label>
|
||||
<select name="status" class="border rounded-lg px-3 py-2 text-sm focus:outline-none">
|
||||
<option value="">全部</option>
|
||||
@foreach (['married' => '已婚', 'pending' => '求婚中', 'divorced' => '已离婚', 'rejected' => '被拒', 'expired' => '已过期'] as $val => $label)
|
||||
<option value="{{ $val }}" @selected(request('status') === $val)>{{ $label }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-xs font-bold text-gray-600 mb-1">搜索用户名</label>
|
||||
<input type="text" name="search" value="{{ request('search') }}" placeholder="双方任一用户名..."
|
||||
class="border rounded-lg px-3 py-2 text-sm w-48 focus:outline-none">
|
||||
</div>
|
||||
<button type="submit"
|
||||
class="bg-indigo-600 text-white px-4 py-2 rounded-lg text-sm font-bold hover:bg-indigo-700 transition">
|
||||
筛选
|
||||
</button>
|
||||
<a href="{{ route('admin.marriages.list') }}" class="text-sm text-gray-500 hover:underline py-2">重置</a>
|
||||
</form>
|
||||
|
||||
{{-- 婚姻列表 --}}
|
||||
<div class="bg-white rounded-xl shadow-sm border overflow-hidden">
|
||||
<div class="overflow-x-auto">
|
||||
<table class="w-full text-sm">
|
||||
<thead class="bg-gray-50 text-gray-600 text-xs uppercase">
|
||||
<tr>
|
||||
<th class="px-4 py-3 text-left">ID</th>
|
||||
<th class="px-4 py-3 text-left">双方</th>
|
||||
<th class="px-4 py-3 text-left">戒指</th>
|
||||
<th class="px-4 py-3 text-center">状态</th>
|
||||
<th class="px-4 py-3 text-center">亲密度/等级</th>
|
||||
<th class="px-4 py-3 text-center">结婚天数</th>
|
||||
<th class="px-4 py-3 text-left">时间</th>
|
||||
<th class="px-4 py-3 text-center">操作</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="divide-y">
|
||||
@forelse ($marriages as $m)
|
||||
<tr class="hover:bg-gray-50 transition">
|
||||
<td class="px-4 py-3 text-gray-400 font-mono text-xs">{{ $m->id }}</td>
|
||||
<td class="px-4 py-3">
|
||||
<div class="font-bold">{{ $m->user?->username }}</div>
|
||||
<div class="text-gray-400 text-xs">× {{ $m->partner?->username }}</div>
|
||||
</td>
|
||||
<td class="px-4 py-3">
|
||||
{{ $m->ringItem?->icon }} {{ $m->ringItem?->name ?? '—' }}
|
||||
</td>
|
||||
<td class="px-4 py-3 text-center">
|
||||
<span
|
||||
class="px-2 py-0.5 rounded-full text-xs font-bold
|
||||
{{ match ($m->status) {
|
||||
'married' => 'bg-green-100 text-green-700',
|
||||
'pending' => 'bg-amber-100 text-amber-700',
|
||||
'divorced' => 'bg-red-100 text-red-700',
|
||||
'rejected' => 'bg-gray-100 text-gray-600',
|
||||
'expired' => 'bg-slate-100 text-slate-500',
|
||||
default => 'bg-gray-100 text-gray-600',
|
||||
} }}">
|
||||
{{ ['married' => '💑 已婚', 'pending' => '💍 求婚中', 'divorced' => '💔 已离婚', 'rejected' => '❌ 被拒', 'expired' => '⏰ 已过期'][$m->status] ?? $m->status }}
|
||||
</span>
|
||||
</td>
|
||||
<td class="px-4 py-3 text-center">
|
||||
<div class="font-bold text-indigo-600">{{ number_format($m->intimacy) }}</div>
|
||||
<div class="text-xs text-gray-400">
|
||||
{{ \App\Services\MarriageIntimacyService::levelIcon($m->level) }}
|
||||
Lv{{ $m->level }}</div>
|
||||
</td>
|
||||
<td class="px-4 py-3 text-center text-gray-600">
|
||||
{{ $m->married_at ? $m->married_at->diffInDays(now()) . ' 天' : '—' }}
|
||||
</td>
|
||||
<td class="px-4 py-3 text-xs text-gray-500">
|
||||
@if ($m->married_at)
|
||||
结婚:{{ $m->married_at->format('Y-m-d') }}
|
||||
@elseif ($m->proposed_at)
|
||||
求婚:{{ $m->proposed_at->format('Y-m-d') }}
|
||||
@endif
|
||||
@if ($m->divorced_at)
|
||||
<div>离婚:{{ $m->divorced_at->format('Y-m-d') }}</div>
|
||||
@endif
|
||||
</td>
|
||||
<td class="px-4 py-3 text-center" x-data="{ showDissolve: false }">
|
||||
@if ($m->status === 'married')
|
||||
<button @click="showDissolve = true"
|
||||
class="text-xs bg-red-50 text-red-600 font-bold px-3 py-1.5 rounded hover:bg-red-600 hover:text-white transition">
|
||||
强制离婚
|
||||
</button>
|
||||
{{-- 强制离婚确认弹窗 --}}
|
||||
<div x-show="showDissolve" style="display:none"
|
||||
class="fixed inset-0 z-50 bg-black/60 flex items-center justify-center p-4">
|
||||
<div @click.away="showDissolve = false"
|
||||
class="bg-white rounded-xl shadow-2xl w-full max-w-sm p-6 text-left">
|
||||
<h4 class="font-bold text-red-600 mb-3">⚠️ 强制解除婚姻</h4>
|
||||
<p class="text-sm text-gray-600 mb-4">
|
||||
确认解除 <strong>{{ $m->user?->username }}</strong> 与
|
||||
<strong>{{ $m->partner?->username }}</strong> 的婚姻?<br>
|
||||
<span class="text-red-500 text-xs">管理员强制离婚不扣魅力、不转移财产。</span>
|
||||
</p>
|
||||
<form action="{{ route('admin.marriages.force-dissolve', $m->id) }}"
|
||||
method="POST">
|
||||
@csrf
|
||||
<input type="text" name="admin_note" required placeholder="请填写操作原因(必填)"
|
||||
class="w-full border rounded px-3 py-2 text-sm mb-3">
|
||||
<div class="flex gap-2 justify-end">
|
||||
<button type="button" @click="showDissolve = false"
|
||||
class="px-3 py-1.5 border rounded text-sm text-gray-600">取消</button>
|
||||
<button type="submit"
|
||||
class="px-3 py-1.5 bg-red-600 text-white rounded text-sm font-bold">确认解除</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
@elseif ($m->status === 'pending')
|
||||
<form action="{{ route('admin.marriages.cancel-proposal', $m->id) }}" method="POST"
|
||||
onsubmit="return confirm('确定取消该求婚吗?')">
|
||||
@csrf
|
||||
<button type="submit"
|
||||
class="text-xs bg-amber-50 text-amber-600 font-bold px-3 py-1.5 rounded hover:bg-amber-600 hover:text-white transition">
|
||||
取消求婚
|
||||
</button>
|
||||
</form>
|
||||
@else
|
||||
<span class="text-xs text-gray-300">—</span>
|
||||
@endif
|
||||
</td>
|
||||
</tr>
|
||||
@empty
|
||||
<tr>
|
||||
<td colspan="8" class="px-4 py-12 text-center text-gray-400">暂无记录</td>
|
||||
</tr>
|
||||
@endforelse
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
{{-- 分页 --}}
|
||||
@if ($marriages->hasPages())
|
||||
<div class="px-4 py-4 border-t">{{ $marriages->links() }}</div>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
@endsection
|
||||
89
resources/views/admin/marriages/proposals.blade.php
Normal file
89
resources/views/admin/marriages/proposals.blade.php
Normal file
@@ -0,0 +1,89 @@
|
||||
{{--
|
||||
文件功能:求婚记录页(含取消操作)
|
||||
|
||||
@author ChatRoom Laravel
|
||||
@version 1.0.0
|
||||
--}}
|
||||
|
||||
@extends('admin.layouts.app')
|
||||
|
||||
@section('title', '求婚记录')
|
||||
|
||||
@section('content')
|
||||
|
||||
<div class="flex items-center justify-between mb-5">
|
||||
<h2 class="text-xl font-bold text-gray-800">💌 求婚记录</h2>
|
||||
<a href="{{ route('admin.marriages.index') }}" class="text-sm text-indigo-600 hover:underline">← 返回总览</a>
|
||||
</div>
|
||||
|
||||
<div class="bg-white rounded-xl shadow-sm border overflow-hidden">
|
||||
<div class="overflow-x-auto">
|
||||
<table class="w-full text-sm">
|
||||
<thead class="bg-gray-50 text-xs uppercase text-gray-600">
|
||||
<tr>
|
||||
<th class="px-4 py-3 text-left">ID</th>
|
||||
<th class="px-4 py-3 text-left">发起方</th>
|
||||
<th class="px-4 py-3 text-left">目标</th>
|
||||
<th class="px-4 py-3 text-left">戒指</th>
|
||||
<th class="px-4 py-3 text-center">状态</th>
|
||||
<th class="px-4 py-3 text-left">求婚时间</th>
|
||||
<th class="px-4 py-3 text-left">过期时间</th>
|
||||
<th class="px-4 py-3 text-center">操作</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="divide-y">
|
||||
@forelse ($proposals as $p)
|
||||
<tr class="hover:bg-gray-50">
|
||||
<td class="px-4 py-3 text-gray-400 font-mono text-xs">{{ $p->id }}</td>
|
||||
<td class="px-4 py-3 font-bold">{{ $p->user?->username }}</td>
|
||||
<td class="px-4 py-3 font-bold">{{ $p->partner?->username }}</td>
|
||||
<td class="px-4 py-3">{{ $p->ringItem?->icon }} {{ $p->ringItem?->name ?? '—' }}</td>
|
||||
<td class="px-4 py-3 text-center">
|
||||
<span
|
||||
class="px-2 py-0.5 rounded-full text-xs font-bold
|
||||
{{ match ($p->status) {
|
||||
'pending' => 'bg-amber-100 text-amber-700',
|
||||
'rejected' => 'bg-red-100 text-red-600',
|
||||
'expired' => 'bg-gray-100 text-gray-500',
|
||||
default => 'bg-gray-100 text-gray-600',
|
||||
} }}">
|
||||
{{ ['pending' => '⏳ 等待中', 'rejected' => '❌ 已拒绝', 'expired' => '⏰ 已过期'][$p->status] ?? $p->status }}
|
||||
</span>
|
||||
</td>
|
||||
<td class="px-4 py-3 text-xs text-gray-500">{{ $p->proposed_at?->format('Y-m-d H:i') }}</td>
|
||||
<td
|
||||
class="px-4 py-3 text-xs {{ $p->expires_at?->isPast() ? 'text-red-400' : 'text-gray-500' }}">
|
||||
{{ $p->expires_at?->format('Y-m-d H:i') }}
|
||||
@if ($p->status === 'pending' && $p->expires_at?->isPast())
|
||||
<span class="text-red-500">(已超时)</span>
|
||||
@endif
|
||||
</td>
|
||||
<td class="px-4 py-3 text-center">
|
||||
@if ($p->status === 'pending')
|
||||
<form action="{{ route('admin.marriages.cancel-proposal', $p->id) }}" method="POST"
|
||||
onsubmit="return confirm('确定取消该求婚吗?')">
|
||||
@csrf
|
||||
<button type="submit"
|
||||
class="text-xs bg-amber-50 text-amber-600 font-bold px-3 py-1.5 rounded hover:bg-amber-600 hover:text-white transition">
|
||||
取消求婚
|
||||
</button>
|
||||
</form>
|
||||
@else
|
||||
<span class="text-gray-300 text-xs">—</span>
|
||||
@endif
|
||||
</td>
|
||||
</tr>
|
||||
@empty
|
||||
<tr>
|
||||
<td colspan="8" class="px-4 py-12 text-center text-gray-400">暂无求婚记录</td>
|
||||
</tr>
|
||||
@endforelse
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
@if ($proposals->hasPages())
|
||||
<div class="px-4 py-4 border-t">{{ $proposals->links() }}</div>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
@endsection
|
||||
107
resources/views/admin/marriages/tiers.blade.php
Normal file
107
resources/views/admin/marriages/tiers.blade.php
Normal file
@@ -0,0 +1,107 @@
|
||||
{{--
|
||||
文件功能:婚礼档位配置页(5档固定,可修改名称/图标/金额/描述/启用状态)
|
||||
|
||||
@author ChatRoom Laravel
|
||||
@version 1.0.0
|
||||
--}}
|
||||
|
||||
@extends('admin.layouts.app')
|
||||
|
||||
@section('title', '婚礼档位配置')
|
||||
|
||||
@section('content')
|
||||
|
||||
<div class="flex items-center justify-between mb-5">
|
||||
<div>
|
||||
<h2 class="text-xl font-bold text-gray-800">🎊 婚礼档位配置</h2>
|
||||
<p class="text-sm text-gray-500 mt-1">5个固定档位,可修改名称、金额及启用状态(不可增删)</p>
|
||||
</div>
|
||||
<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' }}"
|
||||
x-data="{ editing: false }">
|
||||
{{-- 档位头部 --}}
|
||||
<div class="px-5 py-4 border-b flex items-center justify-between">
|
||||
<div class="flex items-center gap-2">
|
||||
<span class="text-2xl">{{ $tier->icon }}</span>
|
||||
<div>
|
||||
<div class="font-bold text-gray-800">{{ $tier->name }}</div>
|
||||
<div class="text-xs text-gray-400">第 {{ $tier->tier }} 档</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
@if (!$tier->is_active)
|
||||
<span class="text-xs bg-gray-100 text-gray-500 px-2 py-0.5 rounded-full">已禁用</span>
|
||||
@endif
|
||||
<button @click="editing = !editing"
|
||||
class="text-xs bg-indigo-50 text-indigo-600 font-bold px-3 py-1.5 rounded hover:bg-indigo-600 hover:text-white transition"
|
||||
x-text="editing ? '收起' : '编辑'">
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- 当前值展示 --}}
|
||||
<div class="px-5 py-4 text-sm" x-show="!editing">
|
||||
<div class="flex justify-between mb-2">
|
||||
<span class="text-gray-500">金币总额</span>
|
||||
<span class="font-bold text-amber-600">{{ number_format($tier->amount) }} 金</span>
|
||||
</div>
|
||||
@if ($tier->description)
|
||||
<p class="text-xs text-gray-400">{{ $tier->description }}</p>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
{{-- 编辑表单 --}}
|
||||
<div x-show="editing" style="display:none" class="px-5 py-4">
|
||||
<form action="{{ route('admin.marriages.tiers.update', $tier->id) }}" method="POST" class="space-y-3">
|
||||
@csrf @method('PUT')
|
||||
<div class="grid grid-cols-2 gap-3">
|
||||
<div>
|
||||
<label class="block text-xs font-bold text-gray-600 mb-1">档位名称</label>
|
||||
<input type="text" name="name" value="{{ $tier->name }}" required maxlength="30"
|
||||
class="w-full border rounded-lg px-3 py-1.5 text-sm focus:outline-none focus:ring-2 focus:ring-indigo-300">
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-xs font-bold text-gray-600 mb-1">图标 (Emoji)</label>
|
||||
<input type="text" name="icon" value="{{ $tier->icon }}" required maxlength="20"
|
||||
class="w-full border rounded-lg px-3 py-1.5 text-sm focus:outline-none">
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-xs font-bold text-gray-600 mb-1">金币总额</label>
|
||||
<input type="number" name="amount" value="{{ $tier->amount }}" required min="1"
|
||||
class="w-full border rounded-lg px-3 py-1.5 text-sm focus:outline-none focus:ring-2 focus:ring-indigo-300">
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-xs font-bold text-gray-600 mb-1">描述</label>
|
||||
<input type="text" name="description" value="{{ $tier->description }}" maxlength="100"
|
||||
class="w-full border rounded-lg px-3 py-1.5 text-sm focus:outline-none">
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<input type="hidden" name="is_active" value="0">
|
||||
<input type="checkbox" name="is_active" value="1" id="active_{{ $tier->id }}"
|
||||
@checked($tier->is_active) class="rounded">
|
||||
<label for="active_{{ $tier->id }}" class="text-sm text-gray-700">启用此档位</label>
|
||||
</div>
|
||||
<div class="flex justify-end gap-2 pt-1">
|
||||
<button type="button" @click="editing = false"
|
||||
class="px-3 py-1.5 border rounded text-sm text-gray-600">取消</button>
|
||||
<button type="submit"
|
||||
class="px-4 py-1.5 bg-indigo-600 text-white rounded text-sm font-bold hover:bg-indigo-700 transition">保存</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
@endforeach
|
||||
</div>
|
||||
|
||||
@endsection
|
||||
Reference in New Issue
Block a user