2026-03-28 17:20:33 +08:00
|
|
|
|
@extends('admin.layouts.app')
|
|
|
|
|
|
|
|
|
|
|
|
@section('title', '金币/积分流水查询')
|
|
|
|
|
|
|
|
|
|
|
|
@section('content')
|
2026-04-26 17:54:24 +08:00
|
|
|
|
@php
|
2026-04-26 18:24:55 +08:00
|
|
|
|
require resource_path('views/admin/partials/list-theme.php');
|
2026-04-26 17:54:24 +08:00
|
|
|
|
$selectedSourceCount = count($selectedSources ?? []);
|
|
|
|
|
|
@endphp
|
|
|
|
|
|
|
2026-04-26 18:24:55 +08:00
|
|
|
|
<div class="{{ $adminListHeaderCardClass }} mb-6">
|
|
|
|
|
|
<div class="{{ $adminListFilterInnerClass }}">
|
|
|
|
|
|
<form action="{{ route('admin.currency-logs.index') }}" method="GET"
|
|
|
|
|
|
class="flex flex-wrap items-end gap-3 2xl:grid 2xl:grid-cols-[minmax(120px,1fr)_8rem_8rem_minmax(160px,1.1fr)_minmax(160px,1.15fr)_9rem_9rem_auto] 2xl:items-end"
|
|
|
|
|
|
data-admin-auto-filter-form>
|
|
|
|
|
|
<div class="min-w-[120px] flex-1">
|
|
|
|
|
|
<label for="username" class="{{ $adminListFilterLabelClass }}">用户名</label>
|
2026-03-28 17:20:33 +08:00
|
|
|
|
<input type="text" name="username" id="username" value="{{ request('username') }}"
|
2026-04-26 18:24:55 +08:00
|
|
|
|
class="w-full {{ $adminListFilterInputClass }}" data-auto-submit="input"
|
2026-03-28 17:20:33 +08:00
|
|
|
|
placeholder="选填">
|
2026-04-26 18:24:55 +08:00
|
|
|
|
</div>
|
2026-03-28 17:20:33 +08:00
|
|
|
|
|
2026-04-26 18:24:55 +08:00
|
|
|
|
<div class="w-32 shrink-0">
|
|
|
|
|
|
<label for="currency" class="{{ $adminListFilterLabelClass }}">货币类型</label>
|
2026-03-28 17:20:33 +08:00
|
|
|
|
<select name="currency" id="currency"
|
2026-04-26 18:24:55 +08:00
|
|
|
|
class="w-full {{ $adminListFilterInputClass }}" data-auto-submit="change">
|
2026-03-28 17:24:31 +08:00
|
|
|
|
<option value="">全部</option>
|
2026-03-28 17:20:33 +08:00
|
|
|
|
<option value="gold" {{ request('currency') == 'gold' ? 'selected' : '' }}>💰 金币</option>
|
|
|
|
|
|
<option value="exp" {{ request('currency') == 'exp' ? 'selected' : '' }}>⭐ 经验</option>
|
|
|
|
|
|
<option value="charm" {{ request('currency') == 'charm' ? 'selected' : '' }}>💖 魅力</option>
|
|
|
|
|
|
</select>
|
2026-04-26 18:24:55 +08:00
|
|
|
|
</div>
|
2026-03-28 17:20:33 +08:00
|
|
|
|
|
2026-04-26 18:24:55 +08:00
|
|
|
|
<div class="w-32 shrink-0">
|
|
|
|
|
|
<label for="direction" class="{{ $adminListFilterLabelClass }}">变动方向</label>
|
2026-03-28 17:20:33 +08:00
|
|
|
|
<select name="direction" id="direction"
|
2026-04-26 18:24:55 +08:00
|
|
|
|
class="w-full {{ $adminListFilterInputClass }}" data-auto-submit="change">
|
2026-03-28 17:20:33 +08:00
|
|
|
|
<option value="">全部</option>
|
|
|
|
|
|
<option value="in" {{ request('direction') == 'in' ? 'selected' : '' }}>➕ 增加</option>
|
|
|
|
|
|
<option value="out" {{ request('direction') == 'out' ? 'selected' : '' }}>➖ 扣除</option>
|
|
|
|
|
|
</select>
|
2026-04-26 18:24:55 +08:00
|
|
|
|
</div>
|
2026-03-28 17:20:33 +08:00
|
|
|
|
|
2026-04-26 18:24:55 +08:00
|
|
|
|
<div class="min-w-[160px] flex-[1.15]">
|
|
|
|
|
|
<label class="{{ $adminListFilterLabelClass }}">来源途径</label>
|
|
|
|
|
|
<details class="relative" data-admin-source-filter>
|
2026-04-26 17:54:24 +08:00
|
|
|
|
<summary
|
2026-04-26 18:24:55 +08:00
|
|
|
|
class="{{ $adminListFilterInputClass }} flex cursor-pointer list-none items-center justify-between bg-white">
|
2026-04-26 17:54:24 +08:00
|
|
|
|
<span class="truncate">{{ $selectedSourceCount > 0 ? '已选 '.$selectedSourceCount.' 项' : '全部来源' }}</span>
|
|
|
|
|
|
<svg class="h-4 w-4 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24"
|
|
|
|
|
|
xmlns="http://www.w3.org/2000/svg">
|
|
|
|
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"></path>
|
|
|
|
|
|
</svg>
|
|
|
|
|
|
</summary>
|
|
|
|
|
|
|
2026-04-26 18:24:55 +08:00
|
|
|
|
<div class="absolute left-0 z-20 mt-2 flex w-full min-w-[220px] max-w-sm flex-col rounded-xl border border-gray-200 bg-white shadow-lg">
|
2026-04-26 17:54:24 +08:00
|
|
|
|
<div class="grid max-h-64 gap-1 overflow-y-auto p-3">
|
|
|
|
|
|
@foreach ($allSources as $src)
|
|
|
|
|
|
<label class="flex items-center gap-2 rounded-md px-2 py-1.5 text-sm text-gray-700 hover:bg-gray-50">
|
|
|
|
|
|
<input type="checkbox" name="sources[]" value="{{ $src->value }}"
|
|
|
|
|
|
class="rounded border-gray-300 text-indigo-600 focus:ring-indigo-500"
|
|
|
|
|
|
@checked(in_array($src->value, $selectedSources ?? [], true))>
|
|
|
|
|
|
<span class="truncate" title="{{ $src->label() }}">{{ $src->label() }}</span>
|
|
|
|
|
|
</label>
|
|
|
|
|
|
@endforeach
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<div class="sticky bottom-0 flex items-center justify-between border-t border-gray-100 bg-white px-3 py-3">
|
|
|
|
|
|
<a href="{{ route('admin.currency-logs.index', request()->except('sources')) }}"
|
2026-04-26 18:24:55 +08:00
|
|
|
|
class="{{ $adminListSecondaryTextClass }} hover:text-gray-600">清空来源</a>
|
2026-04-26 17:54:24 +08:00
|
|
|
|
<span class="text-xs text-gray-400">勾选后点查询生效</span>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
2026-04-26 18:24:55 +08:00
|
|
|
|
</details>
|
|
|
|
|
|
</div>
|
2026-03-28 17:20:33 +08:00
|
|
|
|
|
2026-04-26 18:24:55 +08:00
|
|
|
|
<div class="min-w-[160px] flex-[1.15]">
|
|
|
|
|
|
<label for="remark" class="{{ $adminListFilterLabelClass }}">备注/房间流水</label>
|
2026-03-28 17:20:33 +08:00
|
|
|
|
<input type="text" name="remark" id="remark" value="{{ request('remark') }}"
|
2026-04-26 18:24:55 +08:00
|
|
|
|
class="w-full {{ $adminListFilterInputClass }}" data-auto-submit="input"
|
2026-03-28 17:24:31 +08:00
|
|
|
|
placeholder="包含(可搜房间名)">
|
2026-04-26 18:24:55 +08:00
|
|
|
|
</div>
|
2026-03-28 17:20:33 +08:00
|
|
|
|
|
2026-04-26 18:24:55 +08:00
|
|
|
|
<div class="w-36 shrink-0">
|
|
|
|
|
|
<label for="date_start" class="{{ $adminListFilterLabelClass }}">开始日期</label>
|
2026-03-28 17:20:33 +08:00
|
|
|
|
<input type="date" name="date_start" id="date_start" value="{{ request('date_start') }}"
|
2026-04-26 18:24:55 +08:00
|
|
|
|
class="w-full {{ $adminListFilterInputClass }}" data-auto-submit="change">
|
|
|
|
|
|
</div>
|
2026-03-28 17:20:33 +08:00
|
|
|
|
|
2026-04-26 18:24:55 +08:00
|
|
|
|
<div class="w-36 shrink-0">
|
|
|
|
|
|
<label for="date_end" class="{{ $adminListFilterLabelClass }}">结束日期</label>
|
2026-03-28 17:20:33 +08:00
|
|
|
|
<input type="date" name="date_end" id="date_end" value="{{ request('date_end') }}"
|
2026-04-26 18:24:55 +08:00
|
|
|
|
class="w-full {{ $adminListFilterInputClass }}" data-auto-submit="change">
|
|
|
|
|
|
</div>
|
2026-03-28 17:20:33 +08:00
|
|
|
|
|
2026-04-26 18:24:55 +08:00
|
|
|
|
<div class="shrink-0 2xl:ml-0">
|
|
|
|
|
|
<a href="{{ route('admin.currency-logs.index') }}" class="{{ $adminListSecondaryButtonClass }}">
|
2026-03-28 17:20:33 +08:00
|
|
|
|
重置
|
2026-04-26 18:24:55 +08:00
|
|
|
|
</a>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</form>
|
|
|
|
|
|
</div>
|
2026-03-28 17:20:33 +08:00
|
|
|
|
</div>
|
|
|
|
|
|
|
2026-04-26 18:24:55 +08:00
|
|
|
|
<div class="{{ $adminListCardClass }}">
|
|
|
|
|
|
<div class="{{ $adminListTableWrapClass }}">
|
|
|
|
|
|
<table class="{{ $adminListTableClass }} whitespace-nowrap">
|
2026-03-28 17:20:33 +08:00
|
|
|
|
<thead>
|
2026-04-26 18:24:55 +08:00
|
|
|
|
<tr class="{{ $adminListTableHeadRowClass }}">
|
|
|
|
|
|
<th class="{{ $adminListTableHeadCellClass }}">记录ID</th>
|
|
|
|
|
|
<th class="{{ $adminListTableHeadCellClass }}">用户</th>
|
|
|
|
|
|
<th class="{{ $adminListTableHeadCellClass }}">类型</th>
|
|
|
|
|
|
<th class="{{ $adminListTableHeadCellClass }}">变动数额</th>
|
|
|
|
|
|
<th class="{{ $adminListTableHeadCellClass }}">变后余额</th>
|
|
|
|
|
|
<th class="{{ $adminListTableHeadCellClass }}">来源</th>
|
|
|
|
|
|
<th class="{{ $adminListTableHeadCellClass }} w-1/4">备注信息</th>
|
|
|
|
|
|
<th class="{{ $adminListTableHeadCellClass }} text-right">发生时间</th>
|
2026-03-28 17:20:33 +08:00
|
|
|
|
</tr>
|
|
|
|
|
|
</thead>
|
2026-04-26 18:24:55 +08:00
|
|
|
|
<tbody class="{{ $adminListTableBodyClass }}">
|
2026-03-28 17:20:33 +08:00
|
|
|
|
@forelse ($logs as $log)
|
2026-04-26 18:24:55 +08:00
|
|
|
|
<tr class="{{ $adminListTableRowClass }} cursor-default">
|
|
|
|
|
|
<td class="px-6 py-4 {{ $adminListSecondaryTextClass }}">
|
2026-03-28 17:20:33 +08:00
|
|
|
|
#{{ $log->id }}
|
|
|
|
|
|
</td>
|
|
|
|
|
|
<td class="px-6 py-4">
|
2026-03-28 17:22:11 +08:00
|
|
|
|
<div class="flex flex-col whitespace-nowrap">
|
2026-04-26 18:24:55 +08:00
|
|
|
|
<span class="{{ $adminListPrimaryTextClass }}">{{ $log->username }}</span>
|
2026-03-28 17:20:33 +08:00
|
|
|
|
</div>
|
|
|
|
|
|
</td>
|
|
|
|
|
|
<td class="px-6 py-4">
|
|
|
|
|
|
@if($log->currency === 'gold')
|
2026-04-26 18:24:55 +08:00
|
|
|
|
<span class="inline-flex items-center gap-1 whitespace-nowrap rounded-full bg-yellow-50 px-2 py-0.5 text-xs font-semibold text-yellow-700 ring-1 ring-yellow-200">
|
2026-03-28 17:26:37 +08:00
|
|
|
|
<span>💰</span><span>金币</span>
|
2026-03-28 17:20:33 +08:00
|
|
|
|
</span>
|
|
|
|
|
|
@elseif($log->currency === 'exp')
|
2026-04-26 18:24:55 +08:00
|
|
|
|
<span class="inline-flex items-center gap-1 whitespace-nowrap rounded-full bg-blue-50 px-2 py-0.5 text-xs font-semibold text-blue-700 ring-1 ring-blue-200">
|
2026-03-28 17:26:37 +08:00
|
|
|
|
<span>⭐</span><span>经验</span>
|
2026-03-28 17:20:33 +08:00
|
|
|
|
</span>
|
|
|
|
|
|
@elseif($log->currency === 'charm')
|
2026-04-26 18:24:55 +08:00
|
|
|
|
<span class="inline-flex items-center gap-1 whitespace-nowrap rounded-full bg-pink-50 px-2 py-0.5 text-xs font-semibold text-pink-700 ring-1 ring-pink-200">
|
2026-03-28 17:26:37 +08:00
|
|
|
|
<span>💖</span><span>魅力</span>
|
2026-03-28 17:20:33 +08:00
|
|
|
|
</span>
|
|
|
|
|
|
@else
|
2026-04-26 18:24:55 +08:00
|
|
|
|
<span class="{{ $adminListSecondaryTextClass }}">{{ $log->currency }}</span>
|
2026-03-28 17:20:33 +08:00
|
|
|
|
@endif
|
|
|
|
|
|
</td>
|
2026-04-26 18:24:55 +08:00
|
|
|
|
<td class="px-6 py-4 {{ $adminListNumericTextClass }}">
|
2026-03-28 17:20:33 +08:00
|
|
|
|
@if ($log->amount > 0)
|
|
|
|
|
|
<span class="text-emerald-500">+{{ $log->amount }}</span>
|
|
|
|
|
|
@elseif ($log->amount === 0)
|
|
|
|
|
|
<span class="text-gray-500">0</span>
|
|
|
|
|
|
@else
|
|
|
|
|
|
<span class="text-red-500">{{ $log->amount }}</span>
|
|
|
|
|
|
@endif
|
|
|
|
|
|
</td>
|
2026-04-26 18:24:55 +08:00
|
|
|
|
<td class="px-6 py-4 {{ $adminListNumericTextClass }} text-gray-700">
|
2026-03-28 17:20:33 +08:00
|
|
|
|
{{ $log->balance_after }}
|
|
|
|
|
|
</td>
|
|
|
|
|
|
<td class="px-6 py-4">
|
|
|
|
|
|
@php
|
|
|
|
|
|
$sourceLabel = \App\Enums\CurrencySource::tryFrom($log->source)?->label() ?? $log->source;
|
|
|
|
|
|
@endphp
|
2026-04-26 18:24:55 +08:00
|
|
|
|
<span class="inline-flex whitespace-nowrap rounded-full bg-slate-100 px-2 py-0.5 text-xs font-semibold text-slate-700">
|
2026-03-28 17:20:33 +08:00
|
|
|
|
{{ $sourceLabel }}
|
|
|
|
|
|
</span>
|
|
|
|
|
|
</td>
|
2026-04-26 18:24:55 +08:00
|
|
|
|
<td class="px-6 py-4 {{ $adminListBodyTextClass }}">
|
2026-03-28 17:20:33 +08:00
|
|
|
|
{{ $log->remark }}
|
|
|
|
|
|
</td>
|
2026-04-26 18:24:55 +08:00
|
|
|
|
<td class="px-6 py-4 {{ $adminListSecondaryTextClass }} text-right whitespace-nowrap">
|
2026-03-28 17:20:33 +08:00
|
|
|
|
{{ $log->created_at->format('Y-m-d H:i:s') }}
|
|
|
|
|
|
</td>
|
|
|
|
|
|
</tr>
|
|
|
|
|
|
@empty
|
|
|
|
|
|
<tr>
|
2026-04-26 18:24:55 +08:00
|
|
|
|
<td colspan="8" class="{{ $adminListEmptyClass }}">
|
2026-03-28 17:20:33 +08:00
|
|
|
|
📭 暂无相关流水记录
|
|
|
|
|
|
</td>
|
|
|
|
|
|
</tr>
|
|
|
|
|
|
@endforelse
|
|
|
|
|
|
</tbody>
|
|
|
|
|
|
</table>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
@if ($logs->hasPages())
|
2026-04-26 18:24:55 +08:00
|
|
|
|
<div class="{{ $adminListPaginationClass }}">
|
2026-03-28 17:20:33 +08:00
|
|
|
|
{{ $logs->links() }}
|
|
|
|
|
|
</div>
|
|
|
|
|
|
@endif
|
|
|
|
|
|
</div>
|
2026-04-26 17:54:24 +08:00
|
|
|
|
|
|
|
|
|
|
<script>
|
|
|
|
|
|
document.addEventListener('DOMContentLoaded', function () {
|
2026-04-26 18:24:55 +08:00
|
|
|
|
const autoFilterForm = document.querySelector('[data-admin-auto-filter-form]');
|
2026-04-26 17:54:24 +08:00
|
|
|
|
const sourceFilter = document.querySelector('[data-admin-source-filter]');
|
2026-04-26 18:24:55 +08:00
|
|
|
|
let autoSubmitTimer = null;
|
|
|
|
|
|
|
|
|
|
|
|
const submitAutoFilter = function () {
|
|
|
|
|
|
if (!autoFilterForm) {
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (typeof autoFilterForm.requestSubmit === 'function') {
|
|
|
|
|
|
autoFilterForm.requestSubmit();
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
autoFilterForm.submit();
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
if (autoFilterForm) {
|
|
|
|
|
|
autoFilterForm.querySelectorAll('[data-auto-submit="input"]').forEach(function (inputElement) {
|
|
|
|
|
|
inputElement.addEventListener('input', function () {
|
|
|
|
|
|
window.clearTimeout(autoSubmitTimer);
|
|
|
|
|
|
autoSubmitTimer = window.setTimeout(function () {
|
|
|
|
|
|
submitAutoFilter();
|
|
|
|
|
|
}, 450);
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
inputElement.addEventListener('keydown', function (event) {
|
|
|
|
|
|
if (event.key !== 'Enter') {
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
event.preventDefault();
|
|
|
|
|
|
window.clearTimeout(autoSubmitTimer);
|
|
|
|
|
|
submitAutoFilter();
|
|
|
|
|
|
});
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
autoFilterForm.querySelectorAll('[data-auto-submit="change"]').forEach(function (fieldElement) {
|
|
|
|
|
|
fieldElement.addEventListener('change', function () {
|
|
|
|
|
|
window.clearTimeout(autoSubmitTimer);
|
|
|
|
|
|
submitAutoFilter();
|
|
|
|
|
|
});
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
2026-04-26 17:54:24 +08:00
|
|
|
|
|
|
|
|
|
|
if (!sourceFilter) {
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-26 18:24:55 +08:00
|
|
|
|
sourceFilter.querySelectorAll('input[type="checkbox"]').forEach(function (checkboxElement) {
|
|
|
|
|
|
checkboxElement.addEventListener('change', function () {
|
|
|
|
|
|
window.clearTimeout(autoSubmitTimer);
|
|
|
|
|
|
submitAutoFilter();
|
|
|
|
|
|
});
|
|
|
|
|
|
});
|
|
|
|
|
|
|
2026-04-26 17:54:24 +08:00
|
|
|
|
document.addEventListener('click', function (event) {
|
|
|
|
|
|
if (!sourceFilter.open) {
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (sourceFilter.contains(event.target)) {
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
sourceFilter.removeAttribute('open');
|
|
|
|
|
|
});
|
|
|
|
|
|
});
|
|
|
|
|
|
</script>
|
2026-03-28 17:20:33 +08:00
|
|
|
|
@endsection
|