功能:职务奖励金币发放系统
数据库: - positions 新增 daily_reward_limit(单日累计上限) - positions 新增 recipient_daily_limit(同一接收者每日次数上限) 后端: - CurrencySource::POSITION_REWARD 新枚举值 - AdminCommandController::reward() 三层限额校验 ① 单次上限 ② 单日累计上限 ③ 同一接收者每日次数 写履职记录(PositionAuthorityLog)+ UserCurrencyService 聊天室悄悄话通知接收者 - POST /command/reward 路由注册 前端(user-actions.blade.php): - 名片按钮行 2+1 布局(加好友/送礼物/送金币) - 送金币仅在 myMaxReward>0 时显示(职务持有者) - 内联奖励金币面板:金额输入 + 确认发放 + 说明文字 - sendReward() 前端校验 + API 调用 + chatDialog 反馈 后台(positions/index): - 编辑表单新增两个奖励限额字段 - PositionController 验证规则同步更新
This commit is contained in:
@@ -6,6 +6,7 @@
|
||||
* 对应数据表:user_currency_logs.source(varchar 字段,非 ENUM,可自由扩展)
|
||||
*
|
||||
* @author ChatRoom Laravel
|
||||
*
|
||||
* @version 1.0.0
|
||||
*/
|
||||
|
||||
@@ -14,7 +15,7 @@ namespace App\Enums;
|
||||
enum CurrencySource: string
|
||||
{
|
||||
/** 自动存点(Horizon 定时任务,每5分钟给在线用户加经验/金币) */
|
||||
case AUTO_SAVE = 'auto_save';
|
||||
case AUTO_SAVE = 'auto_save';
|
||||
|
||||
/** 钓鱼收竿奖励(获得经验或金币) */
|
||||
case FISHING_GAIN = 'fishing_gain';
|
||||
@@ -23,20 +24,23 @@ enum CurrencySource: string
|
||||
case FISHING_COST = 'fishing_cost';
|
||||
|
||||
/** 送出礼物(送方扣金币) */
|
||||
case SEND_GIFT = 'send_gift';
|
||||
case SEND_GIFT = 'send_gift';
|
||||
|
||||
/** 收到礼物(收方魅力增加) */
|
||||
case RECV_GIFT = 'recv_gift';
|
||||
case RECV_GIFT = 'recv_gift';
|
||||
|
||||
/** 新人礼包(首次登录赠送金币) */
|
||||
case NEWBIE_BONUS = 'newbie_bonus';
|
||||
|
||||
/** 商城购买消耗(扣除金币) */
|
||||
case SHOP_BUY = 'shop_buy';
|
||||
case SHOP_BUY = 'shop_buy';
|
||||
|
||||
/** 管理员手动调整(后台直接修改经验/金币/魅力) */
|
||||
case ADMIN_ADJUST = 'admin_adjust';
|
||||
|
||||
/** 职务奖励(在职管理员通过名片弹窗向用户发放奖励金币) */
|
||||
case POSITION_REWARD = 'position_reward';
|
||||
|
||||
// ─── 以后新增活动,在这里加一行即可,数据库无需变更 ───────────
|
||||
// case SIGN_IN = 'sign_in'; // 每日签到
|
||||
// case TASK_REWARD = 'task_reward'; // 任务奖励
|
||||
@@ -48,14 +52,15 @@ enum CurrencySource: string
|
||||
public function label(): string
|
||||
{
|
||||
return match ($this) {
|
||||
self::AUTO_SAVE => '自动存点',
|
||||
self::AUTO_SAVE => '自动存点',
|
||||
self::FISHING_GAIN => '钓鱼奖励',
|
||||
self::FISHING_COST => '钓鱼消耗',
|
||||
self::SEND_GIFT => '送出礼物',
|
||||
self::RECV_GIFT => '收到礼物',
|
||||
self::SEND_GIFT => '送出礼物',
|
||||
self::RECV_GIFT => '收到礼物',
|
||||
self::NEWBIE_BONUS => '新人礼包',
|
||||
self::SHOP_BUY => '商城购买',
|
||||
self::SHOP_BUY => '商城购买',
|
||||
self::ADMIN_ADJUST => '管理员调整',
|
||||
self::POSITION_REWARD => '职务奖励',
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,6 +50,8 @@ class PositionController extends Controller
|
||||
'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',
|
||||
@@ -79,6 +81,8 @@ class PositionController extends Controller
|
||||
'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',
|
||||
|
||||
@@ -15,12 +15,15 @@
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Enums\CurrencySource;
|
||||
use App\Events\MessageSent;
|
||||
use App\Jobs\SaveMessageJob;
|
||||
use App\Models\Message;
|
||||
use App\Models\PositionAuthorityLog;
|
||||
use App\Models\Sysparam;
|
||||
use App\Models\User;
|
||||
use App\Services\ChatStateService;
|
||||
use App\Services\UserCurrencyService;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
@@ -33,6 +36,7 @@ class AdminCommandController extends Controller
|
||||
*/
|
||||
public function __construct(
|
||||
private readonly ChatStateService $chatState,
|
||||
private readonly UserCurrencyService $currencyService,
|
||||
) {}
|
||||
|
||||
/**
|
||||
@@ -386,12 +390,12 @@ class AdminCommandController extends Controller
|
||||
{
|
||||
$request->validate([
|
||||
'room_id' => 'required|integer',
|
||||
'type' => 'required|in:fireworks,rain,lightning,snow',
|
||||
'type' => 'required|in:fireworks,rain,lightning,snow',
|
||||
]);
|
||||
|
||||
$admin = Auth::user();
|
||||
$roomId = $request->input('room_id');
|
||||
$type = $request->input('type');
|
||||
$admin = Auth::user();
|
||||
$roomId = $request->input('room_id');
|
||||
$type = $request->input('type');
|
||||
$superLevel = (int) Sysparam::getValue('superlevel', '100');
|
||||
|
||||
// 仅 superlevel 等级可触发特效
|
||||
@@ -405,6 +409,139 @@ class AdminCommandController extends Controller
|
||||
return response()->json(['status' => 'success', 'message' => "已触发特效:{$type}"]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 职务奖励金币(凭空发放,无需扣操作者余额)
|
||||
*
|
||||
* 三层限额校验:
|
||||
* 1. amount ≤ position.max_reward (单次上限)
|
||||
* 2. 今日累计发放 + amount ≤ position.daily_reward_limit (操作人单日累计上限)
|
||||
* 3. 今日对同一接收者发放次数 < position.recipient_daily_limit(同一接收者每日次数限)
|
||||
*
|
||||
* 成功后:
|
||||
* - 通过 UserCurrencyService 给接收者增加金币
|
||||
* - 写入 PositionAuthorityLog(action_type=reward,记录到履职记录)
|
||||
* - 向房间发送悄悄话通知接收者
|
||||
*
|
||||
* @param Request $request 需包含 username, room_id, amount
|
||||
*/
|
||||
public function reward(Request $request): JsonResponse
|
||||
{
|
||||
$request->validate([
|
||||
'username' => 'required|string',
|
||||
'room_id' => 'required|integer',
|
||||
'amount' => 'required|integer|min:1|max:99999',
|
||||
]);
|
||||
|
||||
$admin = Auth::user();
|
||||
$roomId = (int) $request->input('room_id');
|
||||
$amount = (int) $request->input('amount');
|
||||
$targetUsername = $request->input('username');
|
||||
|
||||
// 不能给自己发放
|
||||
if ($admin->username === $targetUsername) {
|
||||
return response()->json(['status' => 'error', 'message' => '不能给自己发放奖励'], 422);
|
||||
}
|
||||
|
||||
// 必须有在职职务且职务配置了 max_reward
|
||||
$userPosition = $admin->activePosition;
|
||||
if (! $userPosition) {
|
||||
return response()->json(['status' => 'error', 'message' => '你当前没有在职职务,无权发放奖励'], 403);
|
||||
}
|
||||
|
||||
$position = $userPosition->position;
|
||||
if (! $position?->max_reward) {
|
||||
return response()->json(['status' => 'error', 'message' => '你的职务未配置奖励权限'], 403);
|
||||
}
|
||||
|
||||
// 目标用户必须存在
|
||||
$target = User::where('username', $targetUsername)->first();
|
||||
if (! $target) {
|
||||
return response()->json(['status' => 'error', 'message' => '用户不存在'], 404);
|
||||
}
|
||||
|
||||
// ① 单次上限校验
|
||||
if ($amount > $position->max_reward) {
|
||||
return response()->json([
|
||||
'status' => 'error',
|
||||
'message' => "单次奖励上限为 {$position->max_reward} 金币,请调整金额",
|
||||
], 422);
|
||||
}
|
||||
|
||||
// ② 操作人单日累计上限校验
|
||||
if ($position->daily_reward_limit) {
|
||||
$todayTotal = PositionAuthorityLog::where('user_id', $admin->id)
|
||||
->where('action_type', 'reward')
|
||||
->whereDate('created_at', today())
|
||||
->sum('amount');
|
||||
|
||||
if ($todayTotal + $amount > $position->daily_reward_limit) {
|
||||
$remaining = max(0, $position->daily_reward_limit - $todayTotal);
|
||||
|
||||
return response()->json([
|
||||
'status' => 'error',
|
||||
'message' => "今日剩余可发放额度为 {$remaining} 金币,超出单日上限({$position->daily_reward_limit})",
|
||||
], 422);
|
||||
}
|
||||
}
|
||||
|
||||
// ③ 同一接收者每日次数上限校验
|
||||
if ($position->recipient_daily_limit) {
|
||||
$recipientCount = PositionAuthorityLog::where('user_id', $admin->id)
|
||||
->where('target_user_id', $target->id)
|
||||
->where('action_type', 'reward')
|
||||
->whereDate('created_at', today())
|
||||
->count();
|
||||
|
||||
if ($recipientCount >= $position->recipient_daily_limit) {
|
||||
return response()->json([
|
||||
'status' => 'error',
|
||||
'message' => "今日已对 {$targetUsername} 发放过 {$position->recipient_daily_limit} 次奖励,已达上限",
|
||||
], 422);
|
||||
}
|
||||
}
|
||||
|
||||
// 发放金币(通过 UserCurrencyService 原子性更新 + 写流水)
|
||||
$this->currencyService->change(
|
||||
$target,
|
||||
'gold',
|
||||
$amount,
|
||||
CurrencySource::POSITION_REWARD,
|
||||
"{$admin->username}({$position->name})职务奖励",
|
||||
$roomId,
|
||||
);
|
||||
|
||||
// 写履职记录(PositionAuthorityLog)
|
||||
PositionAuthorityLog::create([
|
||||
'user_id' => $admin->id,
|
||||
'user_position_id' => $userPosition->id,
|
||||
'action_type' => 'reward',
|
||||
'target_user_id' => $target->id,
|
||||
'amount' => $amount,
|
||||
'remark' => "发放奖励金币 {$amount} 枚给 {$targetUsername}",
|
||||
]);
|
||||
|
||||
// 向聊天室发送悄悄话通知接收者
|
||||
$msg = [
|
||||
'id' => $this->chatState->nextMessageId($roomId),
|
||||
'room_id' => $roomId,
|
||||
'from_user' => '系统',
|
||||
'to_user' => $targetUsername,
|
||||
'content' => "🎁 <b>{$admin->username}</b>({$position->name})向你发放了 <b>{$amount}</b> 枚金币奖励!当前金币:{$target->fresh()->jjb} 枚。",
|
||||
'is_secret' => true,
|
||||
'font_color' => '#d97706',
|
||||
'action' => '',
|
||||
'sent_at' => now()->toDateTimeString(),
|
||||
];
|
||||
$this->chatState->pushMessage($roomId, $msg);
|
||||
broadcast(new MessageSent($roomId, $msg));
|
||||
SaveMessageJob::dispatch($msg);
|
||||
|
||||
return response()->json([
|
||||
'status' => 'success',
|
||||
'message' => "已向 {$targetUsername} 发放 {$amount} 金币奖励 🎉",
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 权限检查:管理员是否可对目标用户执行指定操作
|
||||
*
|
||||
|
||||
@@ -32,6 +32,8 @@ class Position extends Model
|
||||
'level',
|
||||
'max_persons',
|
||||
'max_reward',
|
||||
'daily_reward_limit',
|
||||
'recipient_daily_limit',
|
||||
'sort_order',
|
||||
];
|
||||
|
||||
@@ -45,6 +47,8 @@ class Position extends Model
|
||||
'level' => 'integer',
|
||||
'max_persons' => 'integer',
|
||||
'max_reward' => 'integer',
|
||||
'daily_reward_limit' => 'integer',
|
||||
'recipient_daily_limit' => 'integer',
|
||||
'sort_order' => 'integer',
|
||||
];
|
||||
}
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* 文件功能:为 positions 表补充奖励限额字段
|
||||
*
|
||||
* 新增字段:
|
||||
* - daily_reward_limit : 操作人单日累计最多可发放的金币总量(null = 不限)
|
||||
* - recipient_daily_limit: 同一接收者每天最多可接收该职务奖励的次数(null = 不限)
|
||||
*/
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* 执行迁移
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('positions', function (Blueprint $table) {
|
||||
// 操作人单日累计发放金币上限(null=不限)
|
||||
$table->unsignedInteger('daily_reward_limit')
|
||||
->nullable()
|
||||
->after('max_reward')
|
||||
->comment('操作人单日累计可发奖励金币上限(null=不限)');
|
||||
|
||||
// 同一接收者每日最多收到本职务奖励的次数(null=不限)
|
||||
$table->unsignedSmallInteger('recipient_daily_limit')
|
||||
->nullable()
|
||||
->after('daily_reward_limit')
|
||||
->comment('同一接收者每天最多可接收本职务奖励次数(null=不限)');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 回滚迁移
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('positions', function (Blueprint $table) {
|
||||
$table->dropColumn(['daily_reward_limit', 'recipient_daily_limit']);
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -24,13 +24,15 @@
|
||||
level: 60,
|
||||
max_persons: 1,
|
||||
max_reward: '',
|
||||
daily_reward_limit: '',
|
||||
recipient_daily_limit: '',
|
||||
sort_order: 0
|
||||
},
|
||||
|
||||
openCreate() {
|
||||
this.editing = null;
|
||||
this.selectedIds = [];
|
||||
this.form = { department_id: '', name: '', icon: '🎖️', rank: 50, level: 60, max_persons: 1, max_reward: '', sort_order: 0 };
|
||||
this.form = { department_id: '', name: '', icon: '🎖️', rank: 50, level: 60, max_persons: 1, max_reward: '', daily_reward_limit: '', recipient_daily_limit: '', sort_order: 0 };
|
||||
this.showForm = true;
|
||||
},
|
||||
openEdit(pos, appointableIds) {
|
||||
@@ -44,6 +46,8 @@
|
||||
level: pos.level,
|
||||
max_persons: pos.max_persons || '',
|
||||
max_reward: pos.max_reward || '',
|
||||
daily_reward_limit: pos.daily_reward_limit || '',
|
||||
recipient_daily_limit: pos.recipient_daily_limit || '',
|
||||
sort_order: pos.sort_order,
|
||||
};
|
||||
this.showForm = true;
|
||||
@@ -170,6 +174,8 @@
|
||||
level: {{ $pos->level }},
|
||||
max_persons: {{ $pos->max_persons ?? 'null' }},
|
||||
max_reward: {{ $pos->max_reward ?? 'null' }},
|
||||
daily_reward_limit: {{ $pos->daily_reward_limit ?? 'null' }},
|
||||
recipient_daily_limit: {{ $pos->recipient_daily_limit ?? 'null' }},
|
||||
sort_order: {{ $pos->sort_order }},
|
||||
requestUrl: '{{ route('admin.positions.update', $pos->id) }}'
|
||||
}, {{ json_encode($appointableIds) }})"
|
||||
@@ -251,9 +257,21 @@
|
||||
class="w-full border rounded-md p-2 text-sm" placeholder="留空不限">
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-xs font-bold text-gray-600 mb-1">单次奖励上限金币(空=不限)</label>
|
||||
<label class="block text-xs font-bold text-gray-600 mb-1">单次奖励上限(空=不限)</label>
|
||||
<input type="number" name="max_reward" x-model="form.max_reward" min="0"
|
||||
class="w-full border rounded-md p-2 text-sm" placeholder="留空不限">
|
||||
class="w-full border rounded-md p-2 text-sm" placeholder="每次最多可发放金币数">
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-xs font-bold text-gray-600 mb-1">单日发放总上限(空=不限)</label>
|
||||
<input type="number" name="daily_reward_limit" x-model="form.daily_reward_limit"
|
||||
min="0" class="w-full border rounded-md p-2 text-sm"
|
||||
placeholder="操作人每日总计可发金币">
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-xs font-bold text-gray-600 mb-1">同人每日次数上限(空=不限)</label>
|
||||
<input type="number" name="recipient_daily_limit" x-model="form.recipient_daily_limit"
|
||||
min="0" class="w-full border rounded-md p-2 text-sm"
|
||||
placeholder="同一接收者每天最多收几次">
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-xs font-bold text-gray-600 mb-1">排序</label>
|
||||
|
||||
@@ -46,9 +46,11 @@
|
||||
chatBotClearUrl: "{{ route('chatbot.clear') }}",
|
||||
chatBotEnabled: {{ \App\Models\Sysparam::getValue('chatbot_enabled', '0') === '1' ? 'true' : 'false' }},
|
||||
hasPosition: {{ Auth::user()->activePosition || Auth::user()->user_level >= $superLevel ? 'true' : 'false' }},
|
||||
myMaxReward: {{ Auth::user()->activePosition?->position?->max_reward ?? 0 }},
|
||||
appointPositionsUrl: "{{ route('chat.appoint.positions') }}",
|
||||
appointUrl: "{{ route('chat.appoint.appoint') }}",
|
||||
revokeUrl: "{{ route('chat.appoint.revoke') }}"
|
||||
revokeUrl: "{{ route('chat.appoint.revoke') }}",
|
||||
rewardUrl: "{{ route('command.reward') }}"
|
||||
};
|
||||
</script>
|
||||
@vite(['resources/css/app.css', 'resources/js/app.js', 'resources/js/chat.js'])
|
||||
|
||||
@@ -93,6 +93,11 @@
|
||||
giftCount: 1,
|
||||
sendingGift: false,
|
||||
|
||||
// 职务奖励金币
|
||||
rewardAmount: 0,
|
||||
sendingReward: false,
|
||||
showRewardPanel: false,
|
||||
|
||||
// 任命相关
|
||||
showAppointPanel: false,
|
||||
appointPositions: [],
|
||||
@@ -447,17 +452,56 @@
|
||||
})
|
||||
});
|
||||
const data = await res.json();
|
||||
alert(data.message);
|
||||
this.$alert(data.message, data.status === 'success' ? '送礼成功 🎁' : '操作失败',
|
||||
data.status === 'success' ? '#e11d48' : '#cc4444');
|
||||
if (data.status === 'success') {
|
||||
this.showUserModal = false;
|
||||
this.giftCount = 1;
|
||||
}
|
||||
} catch (e) {
|
||||
alert('网络异常');
|
||||
this.$alert('网络异常', '错误', '#cc4444');
|
||||
}
|
||||
this.sendingGift = false;
|
||||
},
|
||||
|
||||
/** 职务奖励:向用户发放金币(凭空产生,记入履职记录) */
|
||||
async sendReward() {
|
||||
if (this.sendingReward) return;
|
||||
const maxOnce = window.chatContext?.myMaxReward ?? 0;
|
||||
const amount = parseInt(this.rewardAmount, 10);
|
||||
if (!amount || amount <= 0) {
|
||||
this.$alert('请输入有效的奖励金额', '提示', '#f59e0b');
|
||||
return;
|
||||
}
|
||||
if (amount > maxOnce) {
|
||||
this.$alert(`单次奖励上限为 ${maxOnce} 金币`, '超出上限', '#cc4444');
|
||||
return;
|
||||
}
|
||||
this.sendingReward = true;
|
||||
try {
|
||||
const res = await fetch(window.chatContext.rewardUrl, {
|
||||
method: 'POST',
|
||||
headers: this._headers(),
|
||||
body: JSON.stringify({
|
||||
username: this.userInfo.username,
|
||||
room_id: window.chatContext.roomId,
|
||||
amount,
|
||||
})
|
||||
});
|
||||
const data = await res.json();
|
||||
const ok = data.status === 'success';
|
||||
this.$alert(data.message, ok ? '奖励发放成功 🎉' : '操作失败',
|
||||
ok ? '#d97706' : '#cc4444');
|
||||
if (ok) {
|
||||
this.showRewardPanel = false;
|
||||
this.rewardAmount = 0;
|
||||
}
|
||||
} catch (e) {
|
||||
this.$alert('网络异常,请稍后重试', '错误', '#cc4444');
|
||||
}
|
||||
this.sendingReward = false;
|
||||
},
|
||||
|
||||
/** 通用请求头 */
|
||||
_headers() {
|
||||
return {
|
||||
@@ -610,23 +654,33 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- 普通操作按鈕:写私信 + 送花 + 内联礼物面板 --}}
|
||||
<div x-data="{ showGiftPanel: false }" x-show="userInfo.username !== window.chatContext.username">
|
||||
{{-- 操作按钮区:加好友 + 送礼物 + 送金币(有职务且有奖励权限时显示) --}}
|
||||
<div x-data="{ showGiftPanel: false, showRewardPanel: false }" x-show="userInfo.username !== window.chatContext.username">
|
||||
|
||||
<div class="modal-actions" style="margin-bottom: 0;">
|
||||
{{-- 加好友 / 删好友(替代写私信) --}}
|
||||
<div class="modal-actions" style="margin-bottom: 0; display: flex; gap: 6px; flex-wrap: wrap;">
|
||||
{{-- 加好友 / 删好友 --}}
|
||||
<button x-on:click="toggleFriend()" :disabled="friendLoading"
|
||||
:style="is_friend
|
||||
?
|
||||
'background: #f1f5f9; color: #6b7280; border: 1px solid #d1d5db;' :
|
||||
'background: linear-gradient(135deg,#16a34a,#22c55e); color:#fff; border:none;'"
|
||||
style="padding: 7px 14px; border-radius: 5px; font-size: 12px;
|
||||
style="flex:1; padding: 7px 10px; border-radius: 5px; font-size: 12px;
|
||||
cursor: pointer; font-weight: bold; transition: opacity .15s;"
|
||||
x-text="friendLoading ? '处理中…' : (is_friend ? '✅ 已是好友 (点击删除)' : '➕ 加好友')"></button>
|
||||
{{-- 送花按鈕(与写私信并列) --}}
|
||||
<button class="btn-whisper" x-on:click="showGiftPanel = !showGiftPanel">
|
||||
x-text="friendLoading ? '处理中…' : (is_friend ? '✅ 已是好友' : '➕ 加好友')"></button>
|
||||
|
||||
{{-- 送礼物按钮 --}}
|
||||
<button class="btn-whisper" style="flex:1;"
|
||||
x-on:click="showGiftPanel = !showGiftPanel; showRewardPanel = false;">
|
||||
🎁 送礼物
|
||||
</button>
|
||||
|
||||
{{-- 送金币按钮:仅职务持有者且有 max_reward 时显示 --}}
|
||||
<button x-show="window.chatContext?.myMaxReward > 0"
|
||||
style="flex:1; padding: 7px 10px; border-radius: 5px; font-size: 12px; font-weight: bold; cursor: pointer;
|
||||
background: linear-gradient(135deg,#f59e0b,#d97706); color:#fff; border:none;"
|
||||
x-on:click="showRewardPanel = !showRewardPanel; showGiftPanel = false;">
|
||||
🪙 送金币
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{{-- 内联礼物面板 --}}
|
||||
@@ -679,6 +733,38 @@
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- 内联奖励金币面板(仅职务持有者且有 max_reward 时可用) --}}
|
||||
<div x-show="showRewardPanel" x-transition
|
||||
style="display: none; padding: 12px 16px; background: #fffbeb; border-top: 2px solid #f59e0b;">
|
||||
|
||||
<div
|
||||
style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 10px;">
|
||||
<span style="font-size: 13px; color: #92400e; font-weight: bold;">🪙 发放奖励金币</span>
|
||||
<span style="font-size: 11px; color: #b45309;">
|
||||
单次上限:<b x-text="window.chatContext?.myMaxReward ?? 0"></b> 金币
|
||||
</span>
|
||||
<button x-on:click="showRewardPanel = false"
|
||||
style="background: none; border: none; color: #94a3b8; cursor: pointer; font-size: 18px; line-height: 1;">×</button>
|
||||
</div>
|
||||
|
||||
<div style="display: flex; gap: 8px; align-items: center;">
|
||||
<input type="number" x-model.number="rewardAmount" :min="1"
|
||||
:max="window.chatContext?.myMaxReward ?? 9999" placeholder="输入金额"
|
||||
style="flex:1; height: 36px; padding: 0 10px; border: 1px solid #fcd34d;
|
||||
border-radius: 6px; font-size: 14px; color: #92400e; background: #fff;
|
||||
outline: none; box-sizing: border-box;">
|
||||
<button x-on:click="sendReward()" :disabled="sendingReward || !rewardAmount"
|
||||
style="height: 36px; padding: 0 18px; background: linear-gradient(135deg,#f59e0b,#d97706);
|
||||
color:#fff; border:none; border-radius: 6px; font-size: 13px; font-weight: bold; cursor: pointer; white-space: nowrap;"
|
||||
:style="sendingReward || !rewardAmount ? 'opacity:0.6; cursor:not-allowed;' : ''">
|
||||
<span x-text="sendingReward ? '发放中...' : '🎉 确认发放'"></span>
|
||||
</button>
|
||||
</div>
|
||||
<p style="margin: 6px 0 0; font-size: 10px; color: #b45309; opacity: 0.8;">
|
||||
金币将直接发放给对方账户,本操作记入你的履职记录。
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- 管理操作 + 职务操作 合并折叠区 --}}
|
||||
|
||||
@@ -112,6 +112,7 @@ Route::middleware(['chat.auth'])->group(function () {
|
||||
Route::post('/command/kick', [AdminCommandController::class, 'kick'])->name('command.kick');
|
||||
Route::post('/command/mute', [AdminCommandController::class, 'mute'])->name('command.mute');
|
||||
Route::post('/command/freeze', [AdminCommandController::class, 'freeze'])->name('command.freeze');
|
||||
Route::post('/command/reward', [AdminCommandController::class, 'reward'])->name('command.reward');
|
||||
Route::get('/command/whispers/{username}', [AdminCommandController::class, 'viewWhispers'])->name('command.whispers');
|
||||
Route::post('/command/announce', [AdminCommandController::class, 'announce'])->name('command.announce');
|
||||
Route::post('/command/clear-screen', [AdminCommandController::class, 'clearScreen'])->name('command.clear_screen');
|
||||
|
||||
Reference in New Issue
Block a user