功能:后台「我的履职记录」页面
- 侧边栏「我的履职记录」链接,位于「任命管理」上方 - 路由:GET /admin/my-duty-logs → appointments.my-duty-logs - 控制器:AppointmentController::myDutyLogs() 支持按操作类型、日期范围筛选,分页,withQueryString() - 视图:admin/appointments/my-duty-logs.blade.php 顶部 6 格汇总统计(奖励/踢出/禁言/警告/任命/撤职) 每张卡片可点击快速按类型筛选 表格显示:操作时间、类型 Badge、操作对象、所属部门·职务、金币金额、备注
This commit is contained in:
@@ -182,6 +182,42 @@ class AppointmentController extends Controller
|
||||
return view('admin.appointments.history', compact('history'));
|
||||
}
|
||||
|
||||
/**
|
||||
* 我的履职记录:展示当前登录者自己所有的权限操作记录
|
||||
*
|
||||
* 不限于某一任职周期,展示全部历史操作,支持按操作类型和日期筛选。
|
||||
*/
|
||||
public function myDutyLogs(Request $request): View
|
||||
{
|
||||
$user = Auth::user();
|
||||
$query = \App\Models\PositionAuthorityLog::where('user_id', $user->id)
|
||||
->with(['targetUser:id,username', 'targetPosition:id,name', 'userPosition.position.department']);
|
||||
|
||||
// 按操作类型筛选
|
||||
if ($request->filled('type')) {
|
||||
$query->where('action_type', $request->type);
|
||||
}
|
||||
|
||||
// 按日期范围筛选
|
||||
if ($request->filled('date_from')) {
|
||||
$query->whereDate('created_at', '>=', $request->date_from);
|
||||
}
|
||||
if ($request->filled('date_to')) {
|
||||
$query->whereDate('created_at', '<=', $request->date_to);
|
||||
}
|
||||
|
||||
$logs = $query->orderByDesc('created_at')->paginate(30)->withQueryString();
|
||||
|
||||
// 汇总统计
|
||||
$summary = \App\Models\PositionAuthorityLog::where('user_id', $user->id)
|
||||
->selectRaw('action_type, COUNT(*) as total, COALESCE(SUM(amount),0) as amount_sum')
|
||||
->groupBy('action_type')
|
||||
->get()
|
||||
->keyBy('action_type');
|
||||
|
||||
return view('admin.appointments.my-duty-logs', compact('logs', 'summary', 'user'));
|
||||
}
|
||||
|
||||
/**
|
||||
* 搜索用户(供任命弹窗 Ajax 快速查找)
|
||||
*/
|
||||
|
||||
206
resources/views/admin/appointments/my-duty-logs.blade.php
Normal file
206
resources/views/admin/appointments/my-duty-logs.blade.php
Normal file
@@ -0,0 +1,206 @@
|
||||
{{--
|
||||
文件功能:我的履职记录页面
|
||||
展示当前登录者自己的全部权限操作记录(奖励、踢人、禁言、任命等)
|
||||
支持按操作类型和日期范围筛选,顶部显示各类操作汇总统计卡片
|
||||
|
||||
@author ChatRoom Laravel
|
||||
@version 1.0.0
|
||||
--}}
|
||||
|
||||
@extends('admin.layouts.app')
|
||||
|
||||
@section('title', '我的履职记录')
|
||||
|
||||
@section('content')
|
||||
|
||||
{{-- ── 页面标题 ── --}}
|
||||
<div class="mb-6">
|
||||
<h2 class="text-xl font-bold text-gray-800">📝 我的履职记录</h2>
|
||||
<p class="text-sm text-gray-500 mt-1">
|
||||
{{ $user->username }} 的全部职务操作历史,共
|
||||
<span class="font-bold text-gray-700">{{ $logs->total() }}</span> 条记录
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{{-- ── 汇总统计卡片 ── --}}
|
||||
@php
|
||||
$statCards = [
|
||||
'reward' => ['label' => '奖励发放', 'icon' => '🪙', 'color' => 'yellow'],
|
||||
'kick' => ['label' => '踢出操作', 'icon' => '🚫', 'color' => 'red'],
|
||||
'mute' => ['label' => '禁言操作', 'icon' => '🔇', 'color' => 'purple'],
|
||||
'warn' => ['label' => '警告操作', 'icon' => '⚠️', 'color' => 'orange'],
|
||||
'appoint' => ['label' => '任命操作', 'icon' => '🎖️', 'color' => 'green'],
|
||||
'revoke' => ['label' => '撤职操作', 'icon' => '❌', 'color' => 'gray'],
|
||||
];
|
||||
$colorMap = [
|
||||
'yellow' => [
|
||||
'bg' => 'bg-yellow-50',
|
||||
'border' => 'border-yellow-200',
|
||||
'text' => 'text-yellow-700',
|
||||
'badge' => 'bg-yellow-100 text-yellow-800',
|
||||
],
|
||||
'red' => [
|
||||
'bg' => 'bg-red-50',
|
||||
'border' => 'border-red-200',
|
||||
'text' => 'text-red-700',
|
||||
'badge' => 'bg-red-100 text-red-800',
|
||||
],
|
||||
'purple' => [
|
||||
'bg' => 'bg-purple-50',
|
||||
'border' => 'border-purple-200',
|
||||
'text' => 'text-purple-700',
|
||||
'badge' => 'bg-purple-100 text-purple-800',
|
||||
],
|
||||
'orange' => [
|
||||
'bg' => 'bg-orange-50',
|
||||
'border' => 'border-orange-200',
|
||||
'text' => 'text-orange-700',
|
||||
'badge' => 'bg-orange-100 text-orange-800',
|
||||
],
|
||||
'green' => [
|
||||
'bg' => 'bg-green-50',
|
||||
'border' => 'border-green-200',
|
||||
'text' => 'text-green-700',
|
||||
'badge' => 'bg-green-100 text-green-800',
|
||||
],
|
||||
'gray' => [
|
||||
'bg' => 'bg-gray-50',
|
||||
'border' => 'border-gray-200',
|
||||
'text' => 'text-gray-700',
|
||||
'badge' => 'bg-gray-100 text-gray-800',
|
||||
],
|
||||
];
|
||||
@endphp
|
||||
|
||||
<div class="grid grid-cols-2 md:grid-cols-3 lg:grid-cols-6 gap-3 mb-6">
|
||||
@foreach ($statCards as $type => $card)
|
||||
@php
|
||||
$stat = $summary->get($type);
|
||||
$c = $colorMap[$card['color']];
|
||||
@endphp
|
||||
<a href="{{ request()->fullUrlWithQuery(['type' => $type, 'page' => 1]) }}"
|
||||
class="rounded-xl border {{ $c['bg'] }} {{ $c['border'] }} p-4 text-center hover:shadow-md transition
|
||||
{{ request('type') === $type ? 'ring-2 ring-offset-1 ring-indigo-400 shadow-md' : '' }}">
|
||||
<div class="text-2xl mb-1">{{ $card['icon'] }}</div>
|
||||
<div class="text-xs text-gray-500 mb-1">{{ $card['label'] }}</div>
|
||||
<div class="text-2xl font-black {{ $c['text'] }}">{{ $stat?->total ?? 0 }}</div>
|
||||
@if ($type === 'reward' && ($stat?->amount_sum ?? 0) > 0)
|
||||
<div class="text-xs {{ $c['text'] }} mt-0.5">共 {{ number_format($stat->amount_sum) }} 金币</div>
|
||||
@endif
|
||||
</a>
|
||||
@endforeach
|
||||
</div>
|
||||
|
||||
{{-- ── 筛选栏 ── --}}
|
||||
<form method="GET" class="bg-white rounded-xl shadow-sm border p-4 mb-4 flex flex-wrap gap-3 items-end">
|
||||
<div>
|
||||
<label class="block text-xs text-gray-500 mb-1">操作类型</label>
|
||||
<select name="type"
|
||||
class="border rounded-lg px-3 py-2 text-sm focus:ring-2 focus:ring-indigo-300 outline-none">
|
||||
<option value="">全部类型</option>
|
||||
@foreach ($statCards as $type => $card)
|
||||
<option value="{{ $type }}" {{ request('type') === $type ? 'selected' : '' }}>
|
||||
{{ $card['icon'] }} {{ $card['label'] }}
|
||||
</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-xs text-gray-500 mb-1">开始日期</label>
|
||||
<input type="date" name="date_from" value="{{ request('date_from') }}"
|
||||
class="border rounded-lg px-3 py-2 text-sm focus:ring-2 focus:ring-indigo-300 outline-none">
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-xs text-gray-500 mb-1">结束日期</label>
|
||||
<input type="date" name="date_to" value="{{ request('date_to') }}"
|
||||
class="border rounded-lg px-3 py-2 text-sm focus:ring-2 focus:ring-indigo-300 outline-none">
|
||||
</div>
|
||||
<button type="submit"
|
||||
class="px-5 py-2 bg-indigo-600 text-white text-sm font-bold rounded-lg hover:bg-indigo-700 transition">
|
||||
🔍 查询
|
||||
</button>
|
||||
@if (request()->hasAny(['type', 'date_from', 'date_to']))
|
||||
<a href="{{ route('admin.appointments.my-duty-logs') }}"
|
||||
class="px-5 py-2 bg-gray-100 text-gray-600 text-sm rounded-lg hover:bg-gray-200 transition">
|
||||
✖ 清除筛选
|
||||
</a>
|
||||
@endif
|
||||
</form>
|
||||
|
||||
{{-- ── 记录表格 ── --}}
|
||||
@php
|
||||
$actionColors = [
|
||||
'appoint' => 'bg-green-100 text-green-700',
|
||||
'revoke' => 'bg-red-100 text-red-700',
|
||||
'reward' => 'bg-yellow-100 text-yellow-700',
|
||||
'warn' => 'bg-orange-100 text-orange-700',
|
||||
'kick' => 'bg-red-100 text-red-700',
|
||||
'mute' => 'bg-purple-100 text-purple-700',
|
||||
'banip' => 'bg-gray-200 text-gray-700',
|
||||
'other' => 'bg-gray-100 text-gray-600',
|
||||
];
|
||||
@endphp
|
||||
|
||||
<div class="bg-white rounded-xl shadow-sm border overflow-hidden">
|
||||
<table class="w-full text-sm">
|
||||
<thead class="bg-gray-50 text-gray-600 text-xs">
|
||||
<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-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>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="divide-y divide-gray-100">
|
||||
@forelse ($logs as $log)
|
||||
@php $colorClass = $actionColors[$log->action_type] ?? 'bg-gray-100 text-gray-600'; @endphp
|
||||
<tr class="hover:bg-gray-50 transition">
|
||||
<td class="px-4 py-3 text-gray-400 text-xs whitespace-nowrap">
|
||||
{{ $log->created_at->format('Y-m-d H:i') }}
|
||||
</td>
|
||||
<td class="px-4 py-3 text-center">
|
||||
<span class="text-xs px-2 py-0.5 rounded-full font-bold {{ $colorClass }}">
|
||||
{{ $log->action_label }}
|
||||
</span>
|
||||
</td>
|
||||
<td class="px-4 py-3 font-bold text-gray-700">
|
||||
{{ $log->targetUser?->username ?? '—' }}
|
||||
</td>
|
||||
<td class="px-4 py-3 text-gray-500 text-xs">
|
||||
@if ($log->userPosition?->position)
|
||||
<span class="text-gray-400">{{ $log->userPosition->position->department?->name }}</span>
|
||||
@if ($log->userPosition->position->department)
|
||||
<span class="text-gray-300 mx-1">·</span>
|
||||
@endif
|
||||
{{ $log->userPosition->position->name }}
|
||||
@else
|
||||
<span class="text-gray-300">超级管理员</span>
|
||||
@endif
|
||||
</td>
|
||||
<td class="px-4 py-3 text-center">
|
||||
@if ($log->amount)
|
||||
<span class="text-yellow-600 font-bold">+{{ number_format($log->amount) }}</span>
|
||||
<span class="text-gray-400 text-xs">金币</span>
|
||||
@else
|
||||
<span class="text-gray-300">—</span>
|
||||
@endif
|
||||
</td>
|
||||
<td class="px-4 py-3 text-gray-400 text-xs">{{ $log->remark ?: '—' }}</td>
|
||||
</tr>
|
||||
@empty
|
||||
<tr>
|
||||
<td colspan="6" class="px-4 py-16 text-center text-gray-400">
|
||||
<div class="text-4xl mb-3">📋</div>
|
||||
<div>暂无履职操作记录</div>
|
||||
</td>
|
||||
</tr>
|
||||
@endforelse
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div class="mt-4">{{ $logs->links() }}</div>
|
||||
|
||||
@endsection
|
||||
@@ -34,8 +34,12 @@
|
||||
</a>
|
||||
{{-- ──────── 部门职务任命系统 ──────── --}}
|
||||
<div class="border-t border-white/10 my-2"></div>
|
||||
<a href="{{ route('admin.appointments.my-duty-logs') }}"
|
||||
class="block px-4 py-3 rounded-md transition {{ request()->routeIs('admin.appointments.my-duty-logs') ? 'bg-indigo-600 font-bold' : 'hover:bg-white/10' }}">
|
||||
📝 我的履职记录
|
||||
</a>
|
||||
<a href="{{ route('admin.appointments.index') }}"
|
||||
class="block px-4 py-3 rounded-md transition {{ request()->routeIs('admin.appointments.*') ? 'bg-indigo-600 font-bold' : 'hover:bg-white/10' }}">
|
||||
class="block px-4 py-3 rounded-md transition {{ request()->routeIs('admin.appointments.*') && !request()->routeIs('admin.appointments.my-duty-logs') ? 'bg-indigo-600 font-bold' : 'hover:bg-white/10' }}">
|
||||
🎖️ 任命管理
|
||||
</a>
|
||||
|
||||
|
||||
@@ -176,6 +176,9 @@ Route::middleware(['chat.auth', 'chat.has_position'])->prefix('admin')->name('ad
|
||||
return view('admin.whispers');
|
||||
})->name('whispers.index');
|
||||
|
||||
// 我的履职记录(当前登录者自己的权限操作记录)
|
||||
Route::get('/my-duty-logs', [\App\Http\Controllers\Admin\AppointmentController::class, 'myDutyLogs'])->name('appointments.my-duty-logs');
|
||||
|
||||
// 任命管理(任命权限由 AppointmentService 内部校验)
|
||||
Route::get('/appointments', [\App\Http\Controllers\Admin\AppointmentController::class, 'index'])->name('appointments.index');
|
||||
Route::post('/appointments', [\App\Http\Controllers\Admin\AppointmentController::class, 'store'])->name('appointments.store');
|
||||
|
||||
Reference in New Issue
Block a user