diff --git a/app/Http/Controllers/MarriageController.php b/app/Http/Controllers/MarriageController.php index 8d9a6bb..9a736dd 100644 --- a/app/Http/Controllers/MarriageController.php +++ b/app/Http/Controllers/MarriageController.php @@ -83,17 +83,23 @@ class MarriageController extends Controller ->first(); if (! $marriage) { - return response()->json(['married' => false]); + return response()->json(['married' => false, 'marriage' => ['status' => 'none']]); } + $partner = $marriage->user_id === $target->id ? $marriage->partner : $marriage->user; + return response()->json([ 'married' => true, - 'level_icon' => \App\Services\MarriageIntimacyService::levelIcon($marriage->level), - 'level_name' => \App\Services\MarriageIntimacyService::levelName($marriage->level), - 'partner' => $marriage->user_id === $target->id ? $marriage->partner : $marriage->user, - 'ring' => $marriage->ringItem?->only(['name', 'icon']), - 'days' => $marriage->married_at?->diffInDays(now()), - 'intimacy' => $marriage->intimacy, + 'marriage' => [ + 'status' => $marriage->status, + 'marriage_id' => $marriage->id, + 'partner_name' => $partner?->username, + 'ring' => $marriage->ringItem?->only(['name', 'icon']), + 'level_icon' => \App\Services\MarriageIntimacyService::levelIcon($marriage->level), + 'level_name' => \App\Services\MarriageIntimacyService::levelName($marriage->level), + 'days' => $marriage->married_at?->diffInDays(now()), + 'intimacy' => $marriage->intimacy, + ], ]); } @@ -134,16 +140,18 @@ class MarriageController extends Controller ->where('user_id', $request->user()->id) ->where('status', 'active') ->whereHas('item', fn ($q) => $q->where('type', 'ring')) - ->with('item:id,name,slug,icon,price') + ->with('item:id,name,slug,icon,price,intimacy_bonus,charm_bonus') ->get() ->map(fn ($p) => [ 'purchase_id' => $p->id, 'name' => $p->item->name, 'icon' => $p->item->icon, 'slug' => $p->item->slug, + 'intimacy_bonus' => (int) ($p->item->intimacy_bonus ?? 0), + 'charm_bonus' => (int) ($p->item->charm_bonus ?? 0), ]); - return response()->json(['rings' => $rings]); + return response()->json(['status' => 'success', 'rings' => $rings]); } /** diff --git a/resources/js/chat.js b/resources/js/chat.js index 91c93b3..9531cbb 100644 --- a/resources/js/chat.js +++ b/resources/js/chat.js @@ -75,8 +75,75 @@ export function initChat(roomId) { window.dispatchEvent( new CustomEvent("chat:appointment-announced", { detail: e }), ); + }) + // ─── 婚姻系统:全局事件(广播给整个房间) ──────────────── + .listen("MarriageAccepted", (e) => { + console.log("结婚公告:", e); + window.dispatchEvent( + new CustomEvent("chat:marriage-accepted", { detail: e }), + ); + }) + .listen("MarriageDivorced", (e) => { + console.log("离婚公告:", e); + window.dispatchEvent( + new CustomEvent("chat:marriage-divorced", { detail: e }), + ); + }) + .listen("WeddingCelebration", (e) => { + console.log("婚礼庆典:", e); + window.dispatchEvent( + new CustomEvent("chat:wedding-celebration", { detail: e }), + ); + }); +} + +/** + * 初始化婚姻私人频道监听(求婚通知 / 拒绝通知 / 红包领取通知) + * 在用户登录成功后调用,监听 user.{id} 私人频道的婚姻事件。 + * + * @param {number} userId 当前登录用户 ID + */ +export function initMarriagePrivateChannel(userId) { + if (!userId || !window.Echo) return; + + window.Echo.private(`user.${userId}`) + // 收到求婚通知(目标方) + .listen(".marriage.proposed", (e) => { + console.log("收到求婚:", e); + window.dispatchEvent( + new CustomEvent("chat:marriage-proposed", { detail: e }), + ); + }) + // 求婚被拒绝(发起方) + .listen(".marriage.rejected", (e) => { + console.log("求婚被拒:", e); + window.dispatchEvent( + new CustomEvent("chat:marriage-rejected", { detail: e }), + ); + }) + // 求婚过期(发起方) + .listen(".marriage.expired", (e) => { + console.log("求婚超时:", e); + window.dispatchEvent( + new CustomEvent("chat:marriage-expired", { detail: e }), + ); + }) + // 协议离婚申请通知(对方) + .listen(".marriage.divorce-requested", (e) => { + console.log("离婚申请:", e); + window.dispatchEvent( + new CustomEvent("chat:divorce-requested", { detail: e }), + ); + }) + // 红包领取成功通知 + .listen(".envelope.claimed", (e) => { + console.log("红包到账:", e); + window.dispatchEvent( + new CustomEvent("chat:envelope-claimed", { detail: e }), + ); }); } // 供全局调用 window.initChat = initChat; +window.initMarriagePrivateChannel = initMarriagePrivateChannel; diff --git a/resources/views/chat/frame.blade.php b/resources/views/chat/frame.blade.php index e0b0bdc..a7bf4cd 100644 --- a/resources/views/chat/frame.blade.php +++ b/resources/views/chat/frame.blade.php @@ -60,7 +60,22 @@ appointUrl: "{{ route('chat.appoint.appoint') }}", revokeUrl: "{{ route('chat.appoint.revoke') }}", rewardUrl: "{{ route('command.reward') }}", - rewardQuotaUrl: "{{ route('command.reward_quota') }}" + rewardQuotaUrl: "{{ route('command.reward_quota') }}", + // ─── 婚姻系统 ────────────────────────────── + marriage: { + proposeUrl: "{{ route('marriage.propose') }}", + statusUrl: "{{ route('marriage.status') }}", + targteStatusUrl: "/marriage/target", + myRingsUrl: "{{ route('marriage.rings') }}", + acceptUrl: (id) => `/marriage/${id}/accept`, + rejectUrl: (id) => `/marriage/${id}/reject`, + divorceUrl: (id) => `/marriage/${id}/divorce`, + confirmDivorceUrl: (id) => `/marriage/${id}/confirm-divorce`, + weddingTiersUrl: "/wedding/tiers", + weddingSetupUrl: (id) => `/wedding/${id}/setup`, + claimEnvelopeUrl: (id, ceremonyId) => `/wedding/${id}/claim`, + envelopeStatusUrl: (id) => `/wedding/${id}/envelope-status`, + } }; @vite(['resources/css/app.css', 'resources/js/app.js', 'resources/js/chat.js']) @@ -113,6 +128,8 @@ @include('chat.partials.toast-notification') {{-- ═══════════ 聊天室交互脚本(独立文件维护) ═══════════ --}} @include('chat.partials.user-actions') + {{-- ═══════════ 婚姻系统弹窗组件 ═══════════ --}} + @include('chat.partials.marriage-modals') {{-- 全屏特效系统:管理员烟花/下雨/雷电/下雪 --}} diff --git a/resources/views/chat/partials/marriage-modals.blade.php b/resources/views/chat/partials/marriage-modals.blade.php new file mode 100644 index 0000000..46da528 --- /dev/null +++ b/resources/views/chat/partials/marriage-modals.blade.php @@ -0,0 +1,843 @@ +{{-- + 文件功能:婚姻系统前端弹窗组件集合 + + 包含: + 1. 求婚弹窗(#marriage-propose-modal):选戒指→求婚 + 2. 求婚接收弹窗(#marriage-incoming-modal):被求婚方同意/拒绝 + 3. 结婚成功弹窗(#marriage-accepted-modal):恭喜UI + 婚礼设置入口 + 4. 婚礼设置弹窗(#wedding-setup-modal):选档位→立即/定时→支付 + 5. 婚礼红包弹窗(#wedding-envelope-modal):全局弹出,点击领取 + 6. 婚姻状态弹窗(#marriage-status-modal):查看自己的婚姻信息 + 7. 名片婚姻信息区(注入到 user-card 的 JS 组件中) + + @author ChatRoom Laravel + @version 1.0.0 +--}} + +{{-- ═══════════ 1. 求婚弹窗 ═══════════ --}} + + +{{-- ═══════════ 2. 收到求婚弹窗 ═══════════ --}} + + +{{-- ═══════════ 3. 结婚成功全屏公告 ═══════════ --}} + + +{{-- ═══════════ 4. 婚礼设置弹窗 ═══════════ --}} + + +{{-- ═══════════ 5. 婚礼红包弹窗(全局触发) ═══════════ --}} + + + + +{{-- ═══════════ Alpine.js 组件脚本 ═══════════ --}} + diff --git a/resources/views/chat/partials/user-actions.blade.php b/resources/views/chat/partials/user-actions.blade.php index d02783a..c714443 100644 --- a/resources/views/chat/partials/user-actions.blade.php +++ b/resources/views/chat/partials/user-actions.blade.php @@ -113,6 +113,10 @@ showPositionHistory: false, // 职务履历 showAdminPanel: false, // 管理操作(管理操作+职务操作合并) + // 婚姻状态 + targetMarriage: null, // 对方婚姻状态 { status, partner_name, marriage_id } + marriageLoading: false, + // 自定义弹窗:直接代理到全局 window.chatDialog $alert: (...args) => window.chatDialog.alert(...args), $confirm: (...args) => window.chatDialog.confirm(...args), @@ -193,6 +197,20 @@ }).then(r => r.json()).then(s => { this.is_friend = s.is_friend ?? false; }); + + // 加载对方婚姻状态 + this.targetMarriage = null; + this.marriageLoading = true; + fetch(`/marriage/target?username=${encodeURIComponent(data.data.username)}`, { + headers: { + 'Accept': 'application/json', + 'X-Requested-With': 'XMLHttpRequest' + } + }).then(r => r.json()).then(m => { + this.targetMarriage = m.marriage ?? null; + }).catch(() => {}).finally(() => { + this.marriageLoading = false; + }); } this.showUserModal = true; this.isMuting = false; @@ -690,6 +708,23 @@ x-on:click="openRewardModal(userInfo.username)"> 🪙 送金币 + + {{-- 求婚按钮(对方未婚 且 我也未婚时显示) --}} + + + {{-- 对方已婚时显示提示 --}} +
+ 💑 +
{{-- 内联礼物面板 --}}