Files
chatroom/resources/views/admin/appointments/index.blade.php
T

247 lines
14 KiB
PHP
Raw 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.
{{--
文件功能:后台任命管理页面
展示当前所有在职人员,支持新增任命和撤销职务
任命时可搜索用户并选择目标职务
@author ChatRoom Laravel
@version 1.0.0
--}}
@extends('admin.layouts.app')
@section('title', '任命管理')
@section('content')
@php require resource_path('views/admin/partials/list-theme.php'); @endphp
<div x-data="{
showForm: false,
username: '',
position_id: '',
remark: '',
searchResults: [],
showDropdown: false,
searching: false,
searchTimer: null,
openAppoint() {
this.username = '';
this.position_id = '';
this.remark = '';
this.searchResults = [];
this.showDropdown = false;
this.showForm = true;
},
async doSearch(val) {
this.username = val;
clearTimeout(this.searchTimer);
if (val.length < 1) {
this.searchResults = [];
this.showDropdown = false;
return;
}
this.searching = true;
this.searchTimer = setTimeout(async () => {
const res = await fetch(`{{ route('admin.appointments.search-users') }}?q=${encodeURIComponent(val)}`);
this.searchResults = await res.json();
this.showDropdown = this.searchResults.length > 0;
this.searching = false;
}, 250);
},
selectUser(u) {
this.username = u.username;
this.showDropdown = false;
this.searchResults = [];
}
}">
<div class="{{ $adminListPageClass }}">
{{-- 头部 --}}
<div class="{{ $adminListHeaderCardClass }}">
<div class="flex flex-col gap-4 lg:flex-row lg:items-center lg:justify-between">
<div>
<h2 class="{{ $adminListHeaderTitleClass }}">任命管理</h2>
<p class="{{ $adminListHeaderSubtitleClass }}">管理当前所有在职职位人员,执行任命或撤销操作</p>
</div>
<div class="flex flex-wrap items-center gap-2">
<a href="{{ route('admin.appointments.history') }}"
class="{{ $adminListSecondaryButtonClass }} inline-flex items-center justify-center">
历史记录
</a>
<button @click="openAppoint()"
class="inline-flex items-center justify-center rounded-lg bg-orange-500 px-4 py-2 text-sm font-semibold text-white shadow-sm transition hover:bg-orange-600">
+ 新增任命
</button>
</div>
</div>
</div>
{{-- 在职人员列表 --}}
<div class="{{ $adminListCardClass }}">
<div class="{{ $adminListSectionHeadClass }}">
<h3 class="{{ $adminListSectionTitleClass }}">在职人员列表</h3>
<p class="{{ $adminListSectionDescClass }}">展示当前所有已任命且仍在职的人员,可直接查看履职日志或执行撤销。</p>
</div>
<div class="{{ $adminListTableWrapClass }}">
<table class="{{ $adminListTableClass }} whitespace-nowrap">
<thead>
<tr class="{{ $adminListTableHeadRowClass }}">
<th class="{{ $adminListTableHeadCellClass }}">用户</th>
<th class="{{ $adminListTableHeadCellClass }}">部门·职务</th>
<th class="{{ $adminListTableHeadCellClass }} text-center">等级</th>
<th class="{{ $adminListTableHeadCellClass }}">任命人</th>
<th class="{{ $adminListTableHeadCellClass }} text-center">任命时间</th>
<th class="{{ $adminListTableHeadCellClass }} text-center">在职天数</th>
<th class="{{ $adminListTableHeadCellClass }} text-right">操作</th>
</tr>
</thead>
<tbody class="{{ $adminListTableBodyClass }}">
@forelse ($activePositions as $up)
<tr class="{{ $adminListTableRowClass }}">
<td class="px-4 py-3">
<div class="{{ $adminListPrimaryTextClass }}">{{ $up->user->username }}</div>
<div class="{{ $adminListSecondaryTextClass }}">Lv.{{ $up->user->user_level }}</div>
</td>
<td class="px-4 py-3">
<div class="flex items-center space-x-1">
<span class="text-lg">{{ $up->position->icon }}</span>
<div>
<div class="{{ $adminListSecondaryTextClass }}">{{ $up->position->department->name }}</div>
<div class="text-sm font-semibold" style="color: {{ $up->position->department->color }}">
{{ $up->position->name }}
</div>
</div>
</div>
</td>
<td class="px-4 py-3 text-center">
<span class="inline-flex items-center rounded-full bg-orange-100 px-2 py-0.5 text-xs font-mono font-semibold text-orange-700">
Lv.{{ $up->position->level }}
</span>
</td>
<td class="px-4 py-3 {{ $adminListBodyTextClass }}">{{ $up->appointedBy?->username ?? '系统' }}</td>
<td class="px-4 py-3 text-center {{ $adminListSecondaryTextClass }}">
{{ $up->appointed_at->format('Y-m-d') }}
</td>
<td class="px-4 py-3 text-center">
<span class="inline-flex items-center rounded-full bg-blue-100 px-2 py-0.5 text-xs font-semibold text-blue-700">
{{ $up->duration_days }}
</span>
</td>
<td class="px-4 py-3 text-right">
<div class="flex flex-wrap items-center justify-end gap-2">
<a href="{{ route('admin.appointments.duty-logs', $up->id) }}"
class="{{ $adminListActionButtonClass }} bg-blue-50 text-blue-600 hover:bg-blue-600 hover:text-white">
登录日志
</a>
<a href="{{ route('admin.appointments.authority-logs', $up->id) }}"
class="{{ $adminListActionButtonClass }} bg-purple-50 text-purple-600 hover:bg-purple-600 hover:text-white">
操作日志
</a>
<form action="{{ route('admin.appointments.revoke', $up->id) }}" method="POST"
class="inline"
data-admin-confirm="确定撤销【{{ $up->user->username }}】的【{{ $up->position->name }}】职务?撤销后其等级将归 1。">
@csrf @method('DELETE')
<button type="submit"
class="{{ $adminListActionButtonClass }} bg-red-50 text-red-600 hover:bg-red-600 hover:text-white">
撤销职务
</button>
</form>
</div>
</td>
</tr>
@empty
<tr>
<td colspan="7" class="{{ $adminListEmptyClass }}">暂无在职人员</td>
</tr>
@endforelse
</tbody>
</table>
</div>
</div>
{{-- 新增任命弹窗 --}}
<div x-show="showForm" style="display: none;"
class="fixed inset-0 z-50 bg-black/60 flex items-center justify-center p-4">
<div @click.away="showForm = false" class="bg-white rounded-xl shadow-2xl w-full max-w-md" x-transition>
<div class="flex items-center justify-between rounded-t-xl border-b border-orange-800 bg-orange-700 px-6 py-4">
<h3 class="text-lg font-bold text-white">新增任命</h3>
<button @click="showForm = false"
class="text-2xl leading-none text-orange-200 transition hover:text-white">&times;</button>
</div>
<div class="p-6">
<form action="{{ route('admin.appointments.store') }}" method="POST">
@csrf
<div class="space-y-4">
<div>
<label class="{{ $adminListFilterLabelClass }}">用户名</label>
<div class="relative">
<input type="text" name="username" x-model="username" required
placeholder="输入关键字搜索用户..." @input="doSearch($event.target.value)"
@blur="setTimeout(() => showDropdown = false, 200)"
@focus="if(username.length > 0) doSearch(username)"
class="w-full {{ $adminListFilterInputClass }}" autocomplete="off">
{{-- 搜索中指示 --}}
<span x-show="searching"
class="absolute right-2 top-2.5 text-gray-400 text-xs">搜索中…</span>
{{-- 下拉结果 --}}
<div x-show="showDropdown" style="display:none;"
class="absolute z-50 mt-1 max-h-48 w-full overflow-y-auto rounded-lg border border-orange-100 bg-white shadow-lg">
<template x-for="u in searchResults" :key="u.id">
<div @mousedown="selectUser(u)"
class="flex cursor-pointer items-center justify-between px-3 py-2 hover:bg-orange-50">
<span class="font-bold text-sm" x-text="u.username"></span>
<span class="text-xs text-gray-400" x-text="'Lv.' + u.user_level"></span>
</div>
</template>
<div x-show="searchResults.length === 0 && !searching"
class="px-3 py-2 text-xs text-gray-400">无匹配用户(或已有职务)</div>
</div>
</div>
</div>
<div>
<label class="{{ $adminListFilterLabelClass }}">目标职务</label>
<select name="position_id" x-model="position_id" required
class="w-full {{ $adminListFilterInputClass }}">
<option value="">-- 请选择职务 --</option>
@foreach ($departments as $dept)
<optgroup label="{{ $dept->name }}">
@foreach ($dept->positions as $pos)
@php
$current = $pos->active_user_positions_count ?? 0;
$max = $pos->max_persons;
$isFull = $max && $current >= $max;
$cap = $max
? "在职 {$current}/{$max}" . ($isFull ? ' ⚠️满' : '')
: "在职 {$current} 人·不限额";
@endphp
<option value="{{ $pos->id }}"
{{ $isFull ? 'style=color:#dc2626' : '' }}>
{{ $pos->icon }}
{{ $pos->name }}Lv.{{ $pos->level }}{{ $cap }}
</option>
@endforeach
</optgroup>
@endforeach
</select>
</div>
<div>
<label class="{{ $adminListFilterLabelClass }}">任命备注(可选)</label>
<input type="text" name="remark" x-model="remark" maxlength="255"
placeholder="例:表现优秀,特此提拔" class="w-full {{ $adminListFilterInputClass }}">
</div>
</div>
<div class="mt-4 flex justify-end gap-3 border-t border-gray-100 pt-4">
<button type="button" @click="showForm = false"
class="{{ $adminListSecondaryButtonClass }}">取消</button>
<button type="submit"
class="rounded-lg bg-orange-500 px-4 py-2 text-sm font-semibold text-white shadow-sm transition hover:bg-orange-600">
确认任命
</button>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
@endsection