职务列表三列内联编辑(失焦/回车自动保存,无需打开编辑弹窗):
- 人数上限:PATCH max_persons
- 单次上限:PATCH max_reward
- 单日上限:PATCH daily_reward_limit
保存成功显示短暂绿色 ✓,失败显示红色错误提示
全局奖励接收次数配置改为 AJAX 自动保存,失焦/回车触发,
无需保存按钮(原表单已移除)
新增接口:
- PATCH /admin/positions/{position}/patch(quickPatch)
- POST /admin/positions/reward-config(saveRewardConfig,兼容 JSON + 重定向)
176 lines
6.5 KiB
PHP
176 lines
6.5 KiB
PHP
<?php
|
||
|
||
/**
|
||
* 文件功能:后台职务管理控制器
|
||
* 提供职务的 CRUD 功能,包含任命权限白名单(多选 position_appoint_limits)的同步
|
||
* 职务属于部门,包含等级、图标、人数上限、奖励上限等配置
|
||
*
|
||
* @author ChatRoom Laravel
|
||
*
|
||
* @version 1.0.0
|
||
*/
|
||
|
||
namespace App\Http\Controllers\Admin;
|
||
|
||
use App\Http\Controllers\Controller;
|
||
use App\Models\Department;
|
||
use App\Models\Position;
|
||
use App\Models\Sysparam;
|
||
use Illuminate\Http\RedirectResponse;
|
||
use Illuminate\Http\Request;
|
||
use Illuminate\View\View;
|
||
|
||
class PositionController extends Controller
|
||
{
|
||
/**
|
||
* 职务列表页
|
||
*/
|
||
public function index(): View
|
||
{
|
||
// 按部门分组展示
|
||
$departments = Department::with([
|
||
'positions' => fn ($q) => $q->withCount(['activeUserPositions'])->ordered(),
|
||
])->ordered()->get();
|
||
|
||
// 全部职务(供任命白名单多选框使用)
|
||
$allPositions = Position::with('department')->orderByDesc('rank')->get();
|
||
|
||
// 全局奖励接收次数上限(0 = 不限)
|
||
$globalRecipientDailyMax = (int) Sysparam::getValue('reward_recipient_daily_max', '0');
|
||
|
||
return view('admin.positions.index', compact('departments', 'allPositions', 'globalRecipientDailyMax'));
|
||
}
|
||
|
||
/**
|
||
* 创建职务(同时同步任命白名单)
|
||
*/
|
||
public function store(Request $request): RedirectResponse
|
||
{
|
||
$data = $request->validate([
|
||
'department_id' => 'required|exists:departments,id',
|
||
'name' => 'required|string|max:50',
|
||
'icon' => 'nullable|string|max:10',
|
||
'rank' => 'required|integer|min:0|max:99',
|
||
'level' => 'required|integer|min:1|max:100',
|
||
'max_persons' => 'nullable|integer|min:1',
|
||
'max_reward' => 'nullable|integer|min:0',
|
||
'daily_reward_limit' => 'nullable|integer|min:0',
|
||
'recipient_daily_limit' => 'nullable|integer|min:0',
|
||
'sort_order' => 'required|integer|min:0',
|
||
'appointable_ids' => 'nullable|array',
|
||
'appointable_ids.*' => 'exists:positions,id',
|
||
]);
|
||
|
||
$appointableIds = $data['appointable_ids'] ?? [];
|
||
unset($data['appointable_ids']);
|
||
|
||
$position = Position::create($data);
|
||
|
||
// 同步任命白名单(有选则写,没选则清空=无任命权)
|
||
$position->appointablePositions()->sync($appointableIds);
|
||
|
||
return redirect()->route('admin.positions.index')->with('success', "职务【{$data['name']}】创建成功!");
|
||
}
|
||
|
||
/**
|
||
* 快速补丁:仅更新职务的数值限额字段(内联编辑专用)
|
||
*
|
||
* 允许修改的字段:max_persons / max_reward / daily_reward_limit。
|
||
* 只接受 JSON AJAX 请求,只更新提交的字段,其余字段保持不变。
|
||
*
|
||
* @param Position $position 目标职务
|
||
*/
|
||
public function quickPatch(Request $request, Position $position): \Illuminate\Http\JsonResponse
|
||
{
|
||
$data = $request->validate([
|
||
'max_persons' => 'sometimes|nullable|integer|min:1|max:9999',
|
||
'max_reward' => 'sometimes|nullable|integer|min:0|max:999999',
|
||
'daily_reward_limit' => 'sometimes|nullable|integer|min:0|max:999999',
|
||
]);
|
||
|
||
// 用 fill+save 确保 null 值(不限)也能正确写入
|
||
$position->fill($data)->save();
|
||
|
||
return response()->json(['status' => 'success']);
|
||
}
|
||
|
||
/**
|
||
* 保存全局奖励金币接收次数上限
|
||
*
|
||
* 控制每位用户单日内可从所有职务持有者处累计接收奖励的最高次数。
|
||
* 0 表示不限制,保存到 sysparam 表中(key: reward_recipient_daily_max)。
|
||
*/
|
||
public function saveRewardConfig(Request $request): \Illuminate\Http\JsonResponse|RedirectResponse
|
||
{
|
||
$request->validate([
|
||
'reward_recipient_daily_max' => 'required|integer|min:0|max:9999',
|
||
]);
|
||
|
||
$value = (string) $request->integer('reward_recipient_daily_max');
|
||
|
||
Sysparam::updateOrCreate(
|
||
['alias' => 'reward_recipient_daily_max'],
|
||
[
|
||
'body' => $value,
|
||
'guidetxt' => '用户单日最多接收奖励金币次数(0=不限,统计所有职务持有者的发放总次数)',
|
||
]
|
||
);
|
||
|
||
Sysparam::clearCache('reward_recipient_daily_max');
|
||
|
||
$label = $value === '0' ? '不限' : "{$value} 次";
|
||
|
||
// AJAX 请求返回 JSON,普通表单提交返回重定向
|
||
if ($request->expectsJson()) {
|
||
return response()->json(['status' => 'success', 'message' => "全局接收次数上限已更新为:{$label}"]);
|
||
}
|
||
|
||
return redirect()->route('admin.positions.index')
|
||
->with('success', "全局接收次数上限已更新为:{$label}");
|
||
}
|
||
|
||
/**
|
||
* 更新职务(含任命白名单同步)
|
||
*/
|
||
public function update(Request $request, Position $position): RedirectResponse
|
||
{
|
||
$data = $request->validate([
|
||
'department_id' => 'required|exists:departments,id',
|
||
'name' => 'required|string|max:50',
|
||
'icon' => 'nullable|string|max:10',
|
||
'rank' => 'required|integer|min:0|max:99',
|
||
'level' => 'required|integer|min:1|max:100',
|
||
'max_persons' => 'nullable|integer|min:1',
|
||
'max_reward' => 'nullable|integer|min:0',
|
||
'daily_reward_limit' => 'nullable|integer|min:0',
|
||
'recipient_daily_limit' => 'nullable|integer|min:0',
|
||
'sort_order' => 'required|integer|min:0',
|
||
'appointable_ids' => 'nullable|array',
|
||
'appointable_ids.*' => 'exists:positions,id',
|
||
]);
|
||
|
||
$appointableIds = $data['appointable_ids'] ?? [];
|
||
unset($data['appointable_ids']);
|
||
|
||
$position->update($data);
|
||
$position->appointablePositions()->sync($appointableIds);
|
||
|
||
return redirect()->route('admin.positions.index')->with('success', "职务【{$data['name']}】更新成功!");
|
||
}
|
||
|
||
/**
|
||
* 删除职务(有在职人员时拒绝)
|
||
*/
|
||
public function destroy(Position $position): RedirectResponse
|
||
{
|
||
if ($position->isFull() || $position->activeUserPositions()->exists()) {
|
||
return redirect()->route('admin.positions.index')
|
||
->with('error', "职务【{$position->name}】尚有在职人员,请先撤销后再删除。");
|
||
}
|
||
|
||
$position->delete();
|
||
|
||
return redirect()->route('admin.positions.index')->with('success', "职务【{$position->name}】已删除!");
|
||
}
|
||
}
|