离婚流程全面升级:①发起方专属确认弹窗(含对方拒绝后果+魅力/金币惩罚实时值)②被申请方三选弹窗(同意/不同意/稍后)③不同意=强制离婚申请人赔一半金币④所有惩罚数值从后台实时查询

This commit is contained in:
2026-03-01 19:02:43 +08:00
parent 9b55b5558b
commit 84a4b42f31
7 changed files with 444 additions and 51 deletions
+2
View File
@@ -74,6 +74,8 @@
rejectUrl: (id) => `/marriage/${id}/reject`,
divorceUrl: (id) => `/marriage/${id}/divorce`,
confirmDivorceUrl: (id) => `/marriage/${id}/confirm-divorce`,
rejectDivorceUrl: (id) => `/marriage/${id}/reject-divorce`,
divorceConfigUrl: '/marriage/divorce-config',
weddingTiersUrl: "/wedding/tiers",
weddingSetupUrl: (id) => `/wedding/${id}/setup`,
claimEnvelopeUrl: (id, ceremonyId) => `/wedding/${id}/claim`,
@@ -340,6 +340,181 @@
</style>
{{-- ═══════════ 3.5. 发起离婚确认弹窗(发起方专用 + 后果说明) ═══════════ --}}
<div id="divorce-confirm-modal" x-data="divorceConfirmModal()" x-show="show" x-cloak>
<div x-transition
style="position:fixed; inset:0; background:rgba(10,14,21,.87); backdrop-filter:blur(3px);
z-index:9925; display:flex; align-items:center; justify-content:center; padding:16px;">
<div
style="width:480px; max-width:95vw; background:#1a1f2e; border:1px solid rgba(100,116,140,.3);
border-radius:24px; box-shadow:0 32px 80px rgba(0,0,0,.8); overflow:hidden;">
{{-- 头部 --}}
<div
style="padding:24px 28px 16px; background:linear-gradient(160deg,#252d3a,#1e2736); text-align:center;">
<div style="font-size:44px; margin-bottom:10px;">📄</div>
<div style="color:#f1f5f9; font-weight:bold; font-size:18px; margin-bottom:4px;">发起协议离婚申请</div>
<div style="color:#64748b; font-size:12px;">请仔细阅读以下后果,确认后再提交申请</div>
</div>
<div style="padding:16px 24px;">
{{-- 对方同意:协议离婚 --}}
<div
style="background:rgba(100,116,140,.08); border:1px solid rgba(100,116,140,.25); border-radius:12px; padding:12px 16px; margin-bottom:10px;">
<div style="color:#94a3b8; font-weight:bold; font-size:13px; margin-bottom:6px;"> 若对方同意(协议离婚)
</div>
<ul style="color:#94a3b8; font-size:12px; line-height:2; margin:0; padding-left:20px;">
<li>婚姻关系<strong style="color:#e2e8f0;">立即解除</strong>,亲密度清零</li>
<li>双方各被扣除 <strong style="color:#fca5a5;" x-text="mutualPenalty + ' 点魅力值'"></strong></li>
<li>双方进入 <strong style="color:#e2e8f0;" x-text="mutualCooldown + ' 天'"></strong>
离婚冷静期,期间无法再次结婚</li>
</ul>
</div>
{{-- 对方拒绝:强制离婚,自己赔钱 --}}
<div
style="background:rgba(239,68,68,.08); border:1px solid rgba(239,68,68,.3); border-radius:12px; padding:12px 16px; margin-bottom:16px;">
<div style="color:#fca5a5; font-weight:bold; font-size:13px; margin-bottom:6px;">⚠️
若对方拒绝(视为强制离婚,后果由你承担)</div>
<ul style="color:#94a3b8; font-size:12px; line-height:2; margin:0; padding-left:20px;">
<li>婚姻关系<strong style="color:#fca5a5;">立即强制解除</strong></li>
<li><strong style="color:#fca5a5;">你将被扣除 <span x-text="forcedPenalty"></span>
点魅力值</strong>作为惩罚</li>
<li><strong style="color:#fca5a5;">你当前一半的金币将赔偿给对方</strong></li>
<li>你进入 <strong style="color:#fca5a5;" x-text="forcedCooldown + ' 天'"></strong>
强制离婚冷静期,期间无法再次结婚</li>
</ul>
</div>
{{-- 操作按钮 --}}
<div style="display:flex; gap:10px; justify-content:center;">
<button x-on:click="doConfirm()" :disabled="acting"
style="flex:1; padding:12px 0; border-radius:12px; border:none; font-size:14px; font-weight:bold; cursor:pointer; transition:all .2s;
background:linear-gradient(135deg,#475569,#334155); color:#e2e8f0; box-shadow:0 4px 12px rgba(0,0,0,.3);"
onmouseover="this.style.opacity='0.85'" onmouseout="this.style.opacity='1'">
<span x-text="acting ? '发送中…' : '📄 确认发起申请'"></span>
</button>
<button x-on:click="close()" :disabled="acting"
style="flex:0 0 auto; padding:12px 24px; border-radius:12px; font-size:13px; cursor:pointer; transition:all .2s;
background:rgba(100,116,140,.12); border:1px solid rgba(100,116,140,.3); color:#64748b;"
onmouseover="this.style.background='rgba(100,116,140,.22)'"
onmouseout="this.style.background='rgba(100,116,140,.12)'">
取消
</button>
</div>
<div x-show="error" x-transition
style="display:none; margin-top:10px; background:rgba(239,68,68,.1); border:1px solid rgba(239,68,68,.3); border-radius:8px; padding:8px 12px; font-size:12px; color:#fca5a5; text-align:center;"
x-text="error"></div>
</div>
</div>
</div>
</div>
{{-- ═══════════ 3.6. 离婚申请通知弹窗(三选按钮 + 后果说明) ═══════════ --}}
<div id="divorce-request-modal" x-data="divorceRequestModal()" x-show="show" x-cloak>
<div x-transition
style="position:fixed; inset:0; background:rgba(10,14,21,.85); backdrop-filter:blur(3px);
z-index:9920; display:flex; align-items:center; justify-content:center; padding:16px;">
<div
style="width:480px; max-width:95vw; background:#1a1f2e; border:1px solid rgba(100,116,140,.3);
border-radius:24px; box-shadow:0 32px 80px rgba(0,0,0,.8); overflow:hidden;">
{{-- 头部 --}}
<div
style="padding:28px 28px 20px; background:linear-gradient(160deg,#252d3a,#1e2736); text-align:center;">
<div style="font-size:48px; margin-bottom:12px; animation:divorce-shake 0.7s ease 0.2s both;">💔</div>
<div style="color:#f1f5f9; font-weight:bold; font-size:19px; margin-bottom:6px;">收到离婚申请</div>
<div style="color:#94a3b8; font-size:14px; line-height:1.6;" x-text="initiatorName + ' 向你提出了协议离婚申请。'">
</div>
<div style="color:#64748b; font-size:12px; margin-top:4px;">请仔细阅读以下后果说明,做出你的选择:</div>
</div>
{{-- 后果说明区 --}}
<div style="padding:16px 24px;">
{{-- 同意后果 --}}
<div
style="background:rgba(239,68,68,.08); border:1px solid rgba(239,68,68,.25); border-radius:12px; padding:12px 16px; margin-bottom:10px;">
<div style="display:flex; align-items:center; gap:8px; margin-bottom:6px;">
<span
style="background:#ef4444; border-radius:50%; width:22px; height:22px; display:flex; align-items:center; justify-content:center; font-size:12px; font-weight:bold; color:#fff; flex-shrink:0;"></span>
<span style="color:#fca5a5; font-weight:bold; font-size:13px;">同意离婚</span>
</div>
<ul style="color:#94a3b8; font-size:12px; line-height:2; margin:0; padding-left:30px;">
<li>婚姻关系<strong style="color:#fca5a5;">立即解除</strong>,所有亲密度清零</li>
<li>双方各被扣除 <strong style="color:#fca5a5;" x-text="mutualPenalty + ' 点魅力值'">? 点魅力值</strong>
</li>
<li>双方都将进入<strong style="color:#fca5a5;">离婚冷静期</strong>,期间无法再次结婚</li>
</ul>
</div>
{{-- 拒绝后果 --}}
<div
style="background:rgba(234,179,8,.08); border:1px solid rgba(234,179,8,.25); border-radius:12px; padding:12px 16px; margin-bottom:10px;">
<div style="display:flex; align-items:center; gap:8px; margin-bottom:6px;">
<span
style="background:#ca8a04; border-radius:50%; width:22px; height:22px; display:flex; align-items:center; justify-content:center; font-size:12px; font-weight:bold; color:#fff; flex-shrink:0;"></span>
<span style="color:#fde68a; font-weight:bold; font-size:13px;">不同意(强制离婚)</span>
</div>
<ul style="color:#94a3b8; font-size:12px; line-height:2; margin:0; padding-left:30px;">
<li>视同<strong style="color:#fde68a;">强制离婚</strong>,婚姻<strong
style="color:#fde68a;">立即解除</strong></li>
<li>申请方被扣除大量魅力值作为<strong style="color:#fde68a;">惩罚</strong></li>
<li>申请方当前<strong style="color:#fde68a;">一半金币赔偿给你</strong>(入账到你账户)</li>
<li>申请方进入<strong style="color:#fde68a;">强制离婚冷静期</strong>,期间无法再次结婚</li>
</ul>
</div>
{{-- 取消(稍后处理) --}}
<div
style="background:rgba(100,116,140,.06); border:1px solid rgba(100,116,140,.2); border-radius:12px; padding:10px 16px; margin-bottom:16px;">
<div style="display:flex; align-items:center; gap:8px; margin-bottom:4px;">
<span
style="background:#475569; border-radius:50%; width:22px; height:22px; display:flex; align-items:center; justify-content:center; font-size:12px; font-weight:bold; color:#fff; flex-shrink:0;"></span>
<span style="color:#94a3b8; font-weight:bold; font-size:13px;">稍后决定</span>
</div>
<ul style="color:#64748b; font-size:12px; line-height:2; margin:0; padding-left:30px;">
<li>关闭此弹窗,<strong style="color:#94a3b8;">暂不做决定</strong></li>
<li>下次登录或刷新页面时<strong style="color:#94a3b8;">仍会再次弹出</strong>提示</li>
<li>72 小时内若不处理,系统将<strong style="color:#94a3b8;">自动视为对方强制离婚</strong></li>
</ul>
</div>
{{-- 三个按钮 --}}
<div style="display:flex; gap:10px; justify-content:center;">
{{-- 同意 --}}
<button x-on:click="doAgree()" :disabled="acting"
style="flex:1; padding:12px 0; border-radius:12px; border:none; font-size:14px; font-weight:bold; cursor:pointer; transition:all .2s;
background:linear-gradient(135deg,#dc2626,#b91c1c); color:#fff; box-shadow:0 4px 12px rgba(220,38,38,.35);"
onmouseover="this.style.opacity='0.85'" onmouseout="this.style.opacity='1'">
<span x-text="acting ? '处理中…' : '✓ 同意离婚'"></span>
</button>
{{-- 拒绝 --}}
<button x-on:click="doReject()" :disabled="acting"
style="flex:1; padding:12px 0; border-radius:12px; border:none; font-size:14px; font-weight:bold; cursor:pointer; transition:all .2s;
background:linear-gradient(135deg,#d97706,#b45309); color:#fff; box-shadow:0 4px 12px rgba(217,119,6,.3);"
onmouseover="this.style.opacity='0.85'" onmouseout="this.style.opacity='1'">
<span x-text="acting ? '处理中…' : '✕ 拒绝离婚'"></span>
</button>
{{-- 取消 --}}
<button x-on:click="close()" :disabled="acting"
style="flex:0 0 auto; padding:12px 20px; border-radius:12px; font-size:13px; cursor:pointer; transition:all .2s;
background:rgba(100,116,140,.15); border:1px solid rgba(100,116,140,.3); color:#94a3b8;"
onmouseover="this.style.background='rgba(100,116,140,.25)'"
onmouseout="this.style.background='rgba(100,116,140,.15)'">
稍后
</button>
</div>
<div x-show="error" x-transition
style="display:none; background:rgba(239,68,68,.1); border:1px solid rgba(239,68,68,.3); border-radius:8px; padding:8px 12px; margin-top:12px; font-size:12px; color:#fca5a5; text-align:center;"
x-text="error"></div>
</div>
</div>
</div>
</div>
{{-- ═══════════ 4. 婚礼设置弹窗 ═══════════ --}}
<div id="wedding-setup-modal" x-data="weddingSetupModal()" x-show="show" x-cloak>
<div
@@ -809,10 +984,150 @@
};
}
/**
* 发起离婚确认弹窗(发起方专用:展示双方结果 + 实时惩罚值)
*/
function divorceConfirmModal() {
return {
show: false,
marriageId: null,
mutualPenalty: 0,
forcedPenalty: 0,
mutualCooldown: 0,
forcedCooldown: 0,
acting: false,
error: '',
open(marriageId, config) {
this.marriageId = marriageId;
this.mutualPenalty = config.mutual_charm_penalty ?? 0;
this.forcedPenalty = config.forced_charm_penalty ?? 0;
this.mutualCooldown = config.mutual_cooldown_days ?? 0;
this.forcedCooldown = config.forced_cooldown_days ?? 0;
this.acting = false;
this.error = '';
this.show = true;
},
close() {
this.show = false;
},
/** 确认发起离婚申请 */
async doConfirm() {
if (this.acting) return;
this.acting = true;
this.error = '';
try {
const res = await fetch(window.chatContext.marriage.divorceUrl(this.marriageId), {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
'X-CSRF-TOKEN': document.querySelector('meta[name=csrf-token]').content
}
});
const data = await res.json();
this.close();
if (data.ok) {
window.chatDialog?.alert(data.message, '申请已发出 📩', '#6b7280');
} else {
window.chatDialog?.alert(data.message || '操作失败', '错误', '#dc2626');
}
} catch (e) {
this.error = '网络请求失败,请重试。';
} finally {
this.acting = false;
}
}
};
}
/**
* 婚礼设置弹窗组件
* 离婚申请通知弹窗(被申请方专用:三选 + 真实惩罚值)
*/
function divorceRequestModal() {
return {
show: false,
marriageId: null,
initiatorName: '',
mutualPenalty: 0, // 同意后双方各扣魅力
forcedPenalty: 0, // 不同意后申请方被扣魅力
acting: false,
error: '',
open(detail) {
this.marriageId = detail.marriage_id;
this.initiatorName = detail.initiator_name ?? detail.divorcer_username ?? '对方';
this.mutualPenalty = detail.mutual_charm_penalty ?? 0;
this.forcedPenalty = detail.forced_charm_penalty ?? 0;
this.acting = false;
this.error = '';
this.show = true;
},
close() {
this.show = false;
},
/** 同意:协议离婚,双方各扣 mutualPenalty 魅力 */
async doAgree() {
if (this.acting) return;
this.acting = true;
this.error = '';
try {
const res = await fetch(window.chatContext.marriage.confirmDivorceUrl(this.marriageId), {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
'X-CSRF-TOKEN': document.querySelector('meta[name=csrf-token]').content
},
body: JSON.stringify({
room_id: window.chatContext.roomId
})
});
const data = await res.json();
this.close();
window.chatDialog?.alert(data.message, data.ok ? '操作完成' : '失败', data.ok ? '#6b7280' :
'#cc4444');
} catch (e) {
this.error = '网络请求失败,请重试。';
} finally {
this.acting = false;
}
},
/** 不同意:视为强制离婚,申请方扣魅力 + 赔一半金币 */
async doReject() {
if (this.acting) return;
this.acting = true;
this.error = '';
try {
const res = await fetch(window.chatContext.marriage.rejectDivorceUrl(this.marriageId), {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
'X-CSRF-TOKEN': document.querySelector('meta[name=csrf-token]').content
},
body: JSON.stringify({
room_id: window.chatContext.roomId
})
});
const data = await res.json();
this.close();
window.chatDialog?.alert(data.message, data.ok ? '已处理' : '失败', data.ok ? '#d97706' : '#cc4444');
} catch (e) {
this.error = '网络请求失败,请重试。';
} finally {
this.acting = false;
}
}
};
}
function weddingSetupModal() {
return {
show: false,
@@ -1085,38 +1400,15 @@
);
});
/** 接到协议离婚申请(私人频道,对方) */
/** 接到协议离婚申请(私人频道,对方)→ 打开专属三选弹窗 */
window.addEventListener('chat:divorce-requested', (e) => {
const {
initiator_name,
marriage_id,
mutual_charm_penalty
} = e.detail;
const penaltyTip = mutual_charm_penalty > 0 ?
`\n\n⚠️ 双方各将被扣除 ${mutual_charm_penalty} 点魅力值作为惩罚。` :
'';
window.chatDialog?.confirm(
`${initiator_name} 申请与你协议离婚,是否同意?${penaltyTip}`,
'离婚申请 💔'
).then(ok => {
if (!ok) return;
fetch(window.chatContext.marriage.confirmDivorceUrl(marriage_id), {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json',
'X-CSRF-TOKEN': document.querySelector('meta[name=csrf-token]').content
},
body: JSON.stringify({
room_id: window.chatContext.roomId
})
}).then(r => r.json()).then(data => {
window.chatDialog?.alert(data.message, data.status === 'success' ? '操作完成' :
'失败', data.status === 'success' ? '#6b7280' : '#cc4444');
});
});
const detail = e.detail;
const el = document.getElementById('divorce-request-modal');
if (el) Alpine.$data(el).open(detail);
});
/** 红包领取成功通知(私人频道) */
window.addEventListener('chat:envelope-claimed', (e) => {
const {
@@ -172,40 +172,37 @@
return;
},
/** 发起协议离婚 */
/** 发起协议离婚(先拉惩罚配置,再弹专属全屏确认弹窗) */
async doDivorce(marriageId) {
if (!marriageId) return;
this.showUserModal = false;
const confirmed = await window.chatDialog?.confirm(
'提出离婚后,对方将在私人频道收到通知。\n\n如果对方在 24 小时内未作决定,此申请将自动升级为“强制离婚”,并扣除发起方一定的魅力值作为单方面解除契约的惩罚。\n\n您确定要继续发起离婚申请吗?',
'发起离婚',
'#475569'
);
if (!confirmed) return;
window.chatDialog?.alert('请稍候…', '发送申请中', '#9ca3af');
// 从后台实时拉取最新惩罚配置
let divorceConfig = {
mutual_charm_penalty: 0,
forced_charm_penalty: 0,
mutual_cooldown_days: 0,
forced_cooldown_days: 0
};
try {
const res = await fetch(window.chatContext.marriage.divorceUrl(marriageId), {
method: 'POST',
const cfgRes = await fetch(window.chatContext.marriage.divorceConfigUrl, {
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
'X-CSRF-TOKEN': document.querySelector('meta[name=csrf-token]').content
'X-Requested-With': 'XMLHttpRequest'
}
});
const data = await res.json();
if (data.ok) {
window.chatDialog?.alert(data.message, '已发出', '#6b7280');
} else {
window.chatDialog?.alert(data.message || '操作失败', '错误', '#dc2626');
}
if (cfgRes.ok) divorceConfig = await cfgRes.json();
} catch (e) {
window.chatDialog?.alert('网络请求失败', '错误', '#dc2626');
/* 网络异常则使用默认值 */ }
// 打开专属离婚确认弹窗
const modal = document.getElementById('divorce-confirm-modal');
if (modal && window.Alpine) {
Alpine.$data(modal).open(marriageId, divorceConfig);
}
},
/** 获取用户资料 */
async fetchUser(username) {
try {