迁移婚姻状态脚本
This commit is contained in:
@@ -18,7 +18,7 @@
|
|||||||
* - lightbox.js:处理聊天图片预览灯箱。
|
* - lightbox.js:处理聊天图片预览灯箱。
|
||||||
* - lottery-panel.js:提供双色球彩票 lotteryPanel Alpine 组件和全局开关入口。
|
* - lottery-panel.js:提供双色球彩票 lotteryPanel Alpine 组件和全局开关入口。
|
||||||
* - mobile-drawer.js:处理移动端抽屉、房间列表和在线名单。
|
* - mobile-drawer.js:处理移动端抽屉、房间列表和在线名单。
|
||||||
* - marriage-status.js:处理婚姻状态展示与用户名片联动。
|
* - marriage-status.js:处理婚姻状态弹窗、已婚列表、接受拒绝和离婚申请。
|
||||||
* - toolbar.js:处理工具栏按钮和功能快捷入口。
|
* - toolbar.js:处理工具栏按钮和功能快捷入口。
|
||||||
* - user-target-actions.js:处理点击用户名切换私聊目标和打开名片。
|
* - user-target-actions.js:处理点击用户名切换私聊目标和打开名片。
|
||||||
* - welcome-menu.js:处理欢迎菜单交互。
|
* - welcome-menu.js:处理欢迎菜单交互。
|
||||||
@@ -70,7 +70,18 @@ export {
|
|||||||
scheduleRenderMobileUserList,
|
scheduleRenderMobileUserList,
|
||||||
switchMobileTab,
|
switchMobileTab,
|
||||||
} from "./chat-room/mobile-drawer.js";
|
} from "./chat-room/mobile-drawer.js";
|
||||||
export { bindMarriageStatusControls } from "./chat-room/marriage-status.js";
|
export {
|
||||||
|
bindMarriageStatusControls,
|
||||||
|
closeMarriageStatusModal,
|
||||||
|
fetchMarriedList,
|
||||||
|
fetchMyMarriageStatus,
|
||||||
|
marriageAction,
|
||||||
|
openMarriageStatusModal,
|
||||||
|
renderMarriedList,
|
||||||
|
renderMarriageStatus,
|
||||||
|
switchMarriageTab,
|
||||||
|
tryDivorce,
|
||||||
|
} from "./chat-room/marriage-status.js";
|
||||||
export { bindToolbarControls, runFeatureShortcut, runToolbarAction } from "./chat-room/toolbar.js";
|
export { bindToolbarControls, runFeatureShortcut, runToolbarAction } from "./chat-room/toolbar.js";
|
||||||
export { bindUserTargetActions, openUserCard, switchTarget } from "./chat-room/user-target-actions.js";
|
export { bindUserTargetActions, openUserCard, switchTarget } from "./chat-room/user-target-actions.js";
|
||||||
export { bindWelcomeMenuControls } from "./chat-room/welcome-menu.js";
|
export { bindWelcomeMenuControls } from "./chat-room/welcome-menu.js";
|
||||||
@@ -202,7 +213,18 @@ import {
|
|||||||
scheduleRenderMobileUserList,
|
scheduleRenderMobileUserList,
|
||||||
switchMobileTab,
|
switchMobileTab,
|
||||||
} from "./chat-room/mobile-drawer.js";
|
} from "./chat-room/mobile-drawer.js";
|
||||||
import { bindMarriageStatusControls } from "./chat-room/marriage-status.js";
|
import {
|
||||||
|
bindMarriageStatusControls,
|
||||||
|
closeMarriageStatusModal,
|
||||||
|
fetchMarriedList,
|
||||||
|
fetchMyMarriageStatus,
|
||||||
|
marriageAction,
|
||||||
|
openMarriageStatusModal,
|
||||||
|
renderMarriedList,
|
||||||
|
renderMarriageStatus,
|
||||||
|
switchMarriageTab,
|
||||||
|
tryDivorce,
|
||||||
|
} from "./chat-room/marriage-status.js";
|
||||||
import { bindToolbarControls, runFeatureShortcut, runToolbarAction } from "./chat-room/toolbar.js";
|
import { bindToolbarControls, runFeatureShortcut, runToolbarAction } from "./chat-room/toolbar.js";
|
||||||
import { bindUserTargetActions, openUserCard, switchTarget } from "./chat-room/user-target-actions.js";
|
import { bindUserTargetActions, openUserCard, switchTarget } from "./chat-room/user-target-actions.js";
|
||||||
import { bindWelcomeMenuControls } from "./chat-room/welcome-menu.js";
|
import { bindWelcomeMenuControls } from "./chat-room/welcome-menu.js";
|
||||||
@@ -402,7 +424,6 @@ if (typeof window !== "undefined") {
|
|||||||
stopAutoFishing,
|
stopAutoFishing,
|
||||||
bindFortunePanelControls,
|
bindFortunePanelControls,
|
||||||
fortunePanel,
|
fortunePanel,
|
||||||
bindMarriageStatusControls,
|
|
||||||
bindProfileControls,
|
bindProfileControls,
|
||||||
closeAvatarPicker,
|
closeAvatarPicker,
|
||||||
closeSettingsModal,
|
closeSettingsModal,
|
||||||
@@ -419,6 +440,16 @@ if (typeof window !== "undefined") {
|
|||||||
sendEmailCode,
|
sendEmailCode,
|
||||||
showInlineMsg,
|
showInlineMsg,
|
||||||
unbindWechat,
|
unbindWechat,
|
||||||
|
bindMarriageStatusControls,
|
||||||
|
closeMarriageStatusModal,
|
||||||
|
fetchMarriedList,
|
||||||
|
fetchMyMarriageStatus,
|
||||||
|
marriageAction,
|
||||||
|
openMarriageStatusModal,
|
||||||
|
renderMarriedList,
|
||||||
|
renderMarriageStatus,
|
||||||
|
switchMarriageTab,
|
||||||
|
tryDivorce,
|
||||||
bindShopControls,
|
bindShopControls,
|
||||||
buyItem,
|
buyItem,
|
||||||
closeGiftDialog,
|
closeGiftDialog,
|
||||||
@@ -555,6 +586,15 @@ if (typeof window !== "undefined") {
|
|||||||
window.sendEmailCode = sendEmailCode;
|
window.sendEmailCode = sendEmailCode;
|
||||||
window.showInlineMsg = showInlineMsg;
|
window.showInlineMsg = showInlineMsg;
|
||||||
window.unbindWechat = unbindWechat;
|
window.unbindWechat = unbindWechat;
|
||||||
|
window.closeMarriageStatusModal = closeMarriageStatusModal;
|
||||||
|
window.fetchMarriedList = fetchMarriedList;
|
||||||
|
window.fetchMyMarriageStatus = fetchMyMarriageStatus;
|
||||||
|
window.marriageAction = marriageAction;
|
||||||
|
window.openMarriageStatusModal = openMarriageStatusModal;
|
||||||
|
window.renderMarriedList = renderMarriedList;
|
||||||
|
window.renderMarriageStatus = renderMarriageStatus;
|
||||||
|
window.switchMarriageTab = switchMarriageTab;
|
||||||
|
window.tryDivorce = tryDivorce;
|
||||||
window.buyItem = buyItem;
|
window.buyItem = buyItem;
|
||||||
window.closeGiftDialog = closeGiftDialog;
|
window.closeGiftDialog = closeGiftDialog;
|
||||||
window.closeRenameModal = closeRenameModal;
|
window.closeRenameModal = closeRenameModal;
|
||||||
|
|||||||
@@ -1,19 +1,46 @@
|
|||||||
// 婚姻状态弹窗基础事件绑定,替代 toolbar 婚姻区域内联 onclick。
|
// 婚姻状态弹窗模块,负责我的婚姻、已婚列表、接受/拒绝和离婚申请。
|
||||||
|
|
||||||
|
import { escapeHtml } from "./html.js";
|
||||||
|
|
||||||
|
const DEFAULT_STATUS_URL = "/marriage/status";
|
||||||
|
const DEFAULT_LIST_URL = "/marriage/list";
|
||||||
|
const DEFAULT_HEADFACE_URL = "/images/headface/1.gif";
|
||||||
|
|
||||||
let marriageStatusEventsBound = false;
|
let marriageStatusEventsBound = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 调用婚姻系统存量全局函数。
|
* 快速读取 DOM 节点。
|
||||||
*
|
*
|
||||||
* @param {string} functionName 全局函数名
|
* @param {string} id 节点 ID
|
||||||
* @param {...unknown} args 参数
|
* @returns {HTMLElement|null}
|
||||||
* @returns {void}
|
|
||||||
*/
|
*/
|
||||||
function callMarriageGlobal(functionName, ...args) {
|
function byId(id) {
|
||||||
// 婚姻状态业务仍在 Blade 内维护,当前模块只负责按钮事件和旧函数桥接。
|
return document.getElementById(id);
|
||||||
if (typeof window[functionName] === "function") {
|
}
|
||||||
window[functionName](...args);
|
|
||||||
|
/**
|
||||||
|
* 读取 CSRF Token,给婚姻操作接口使用。
|
||||||
|
*
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
function csrfToken() {
|
||||||
|
return document.querySelector('meta[name="csrf-token"]')?.content ?? "";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 限定头像 URL 来源,避免把异常协议写入 img src。
|
||||||
|
*
|
||||||
|
* @param {unknown} url 头像 URL
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
function normalizeHeadfaceUrl(url) {
|
||||||
|
const value = String(url || "").trim();
|
||||||
|
|
||||||
|
if (value.startsWith("/images/") || value.startsWith("http://") || value.startsWith("https://")) {
|
||||||
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return DEFAULT_HEADFACE_URL;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -22,7 +49,7 @@ function callMarriageGlobal(functionName, ...args) {
|
|||||||
* @returns {number}
|
* @returns {number}
|
||||||
*/
|
*/
|
||||||
function resolveCurrentMarriedPage() {
|
function resolveCurrentMarriedPage() {
|
||||||
const pageInfo = document.getElementById("married-page-info")?.textContent || "1 / 1";
|
const pageInfo = byId("married-page-info")?.textContent || "1 / 1";
|
||||||
const currentPage = Number.parseInt(pageInfo.split("/")[0]?.trim() || "1", 10);
|
const currentPage = Number.parseInt(pageInfo.split("/")[0]?.trim() || "1", 10);
|
||||||
|
|
||||||
return Number.isInteger(currentPage) && currentPage > 0 ? currentPage : 1;
|
return Number.isInteger(currentPage) && currentPage > 0 ? currentPage : 1;
|
||||||
@@ -36,23 +63,534 @@ function resolveCurrentMarriedPage() {
|
|||||||
*/
|
*/
|
||||||
function openWeddingEnvelopeClaim(ceremonyId) {
|
function openWeddingEnvelopeClaim(ceremonyId) {
|
||||||
const detail = window._weddingEnvelopes?.[ceremonyId];
|
const detail = window._weddingEnvelopes?.[ceremonyId];
|
||||||
const modal = document.getElementById("wedding-envelope-modal");
|
const modal = byId("wedding-envelope-modal");
|
||||||
|
|
||||||
if (!detail || !modal || typeof window.Alpine?.$data !== "function") {
|
if (!detail || !modal || typeof window.Alpine?.$data !== "function") {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 红包详情仍由 Blade 旧脚本写入 window._weddingEnvelopes,这里只按 ID 打开现有 Alpine 弹窗。
|
// 红包详情仍由婚礼弹窗模块维护,这里只按 ID 打开现有 Alpine 弹窗。
|
||||||
window.Alpine.$data(modal).open(detail);
|
window.Alpine.$data(modal).open(detail);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 打开婚姻状态弹窗并默认进入“我的婚姻”。
|
||||||
|
*
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
export function openMarriageStatusModal() {
|
||||||
|
const modal = byId("marriage-status-modal");
|
||||||
|
if (!modal) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
modal.style.display = "flex";
|
||||||
|
switchMarriageTab("mine");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 切换婚姻状态弹窗的 tab。
|
||||||
|
*
|
||||||
|
* @param {string} tabName tab 名称
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
export function switchMarriageTab(tabName) {
|
||||||
|
byId("marriage-tabbtn-mine")?.classList.toggle("active", tabName === "mine");
|
||||||
|
byId("marriage-tabbtn-list")?.classList.toggle("active", tabName === "list");
|
||||||
|
|
||||||
|
const mineView = byId("marriage-view-mine");
|
||||||
|
const listView = byId("marriage-view-list");
|
||||||
|
|
||||||
|
if (mineView) {
|
||||||
|
mineView.style.display = tabName === "mine" ? "flex" : "none";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (listView) {
|
||||||
|
listView.style.display = tabName === "list" ? "flex" : "none";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tabName === "mine") {
|
||||||
|
fetchMyMarriageStatus();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
fetchMarriedList(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 拉取当前用户婚姻状态。
|
||||||
|
*
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
*/
|
||||||
|
export async function fetchMyMarriageStatus() {
|
||||||
|
const body = byId("marriage-status-body");
|
||||||
|
const footer = byId("marriage-status-footer");
|
||||||
|
|
||||||
|
if (body) {
|
||||||
|
body.innerHTML = '<div style="text-align:center;color:#aaa;padding:30px 0;font-size:12px;">加载中…</div>';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (footer) {
|
||||||
|
footer.innerHTML = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await fetch(DEFAULT_STATUS_URL, {
|
||||||
|
headers: {
|
||||||
|
Accept: "application/json",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const data = await response.json();
|
||||||
|
renderMarriageStatus(data);
|
||||||
|
} catch (error) {
|
||||||
|
if (body) {
|
||||||
|
body.innerHTML = '<div style="text-align:center;color:#e55;padding:30px 0;font-size:12px;">❌ 加载失败,请稍后重试</div>';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 拉取已婚列表分页数据。
|
||||||
|
*
|
||||||
|
* @param {number} page 页码
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
*/
|
||||||
|
export async function fetchMarriedList(page) {
|
||||||
|
if (page < 1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const container = byId("married-list-container");
|
||||||
|
if (container) {
|
||||||
|
container.innerHTML = '<div style="text-align:center;color:#aaa;padding:30px 0;font-size:12px;">加载中…</div>';
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await fetch(`${DEFAULT_LIST_URL}?page=${encodeURIComponent(page)}`, {
|
||||||
|
headers: {
|
||||||
|
Accept: "application/json",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const json = await response.json();
|
||||||
|
|
||||||
|
if (json.status === "success") {
|
||||||
|
window.marriedListPage = json.pagination.current_page;
|
||||||
|
renderMarriedList(json.data, json.pagination);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
if (container) {
|
||||||
|
container.innerHTML = '<div style="text-align:center;color:#e55;padding:30px 0;font-size:12px;">❌ 加载失败</div>';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 渲染已婚列表。
|
||||||
|
*
|
||||||
|
* @param {Array<Record<string, any>>} data 已婚记录
|
||||||
|
* @param {Record<string, any>} pagination 分页信息
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
export function renderMarriedList(data, pagination) {
|
||||||
|
const container = byId("married-list-container");
|
||||||
|
const paginationElement = byId("married-list-pagination");
|
||||||
|
|
||||||
|
if (!container) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!data || data.length === 0) {
|
||||||
|
container.innerHTML = '<div style="text-align:center;color:#aaa;padding:40px 0;font-size:12px;">💖 暂无婚姻记录,快去寻找你的另一半吧</div>';
|
||||||
|
if (paginationElement) {
|
||||||
|
paginationElement.style.display = "none";
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (paginationElement) {
|
||||||
|
paginationElement.style.display = "flex";
|
||||||
|
}
|
||||||
|
|
||||||
|
updateMarriedPagination(pagination);
|
||||||
|
container.innerHTML = data.map((marriage) => buildMarriedListItemHtml(marriage)).join("");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新已婚列表分页按钮状态。
|
||||||
|
*
|
||||||
|
* @param {Record<string, any>} pagination 分页信息
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
function updateMarriedPagination(pagination) {
|
||||||
|
const currentPage = Number(pagination.current_page || 1);
|
||||||
|
const lastPage = Number(pagination.last_page || 1);
|
||||||
|
const pageInfo = byId("married-page-info");
|
||||||
|
const prevButton = byId("married-prev-btn");
|
||||||
|
const nextButton = byId("married-next-btn");
|
||||||
|
|
||||||
|
if (pageInfo) {
|
||||||
|
pageInfo.textContent = `${currentPage} / ${lastPage}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (prevButton) {
|
||||||
|
prevButton.disabled = currentPage <= 1;
|
||||||
|
prevButton.style.opacity = currentPage <= 1 ? 0.5 : 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nextButton) {
|
||||||
|
nextButton.disabled = currentPage >= lastPage;
|
||||||
|
nextButton.style.opacity = currentPage >= lastPage ? 0.5 : 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构建已婚列表单条记录 HTML。
|
||||||
|
*
|
||||||
|
* @param {Record<string, any>} marriage 婚姻记录
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
function buildMarriedListItemHtml(marriage) {
|
||||||
|
const user = marriage.user || {};
|
||||||
|
const partner = marriage.partner || {};
|
||||||
|
const ring = marriage.ring_item;
|
||||||
|
const date = marriage.married_at ? String(marriage.married_at).substring(0, 10) : "—";
|
||||||
|
const userColor = Number(user.sex || 0) === 2 ? "color:#e91e8c;" : "";
|
||||||
|
const partnerColor = Number(partner.sex || 0) === 2 ? "color:#e91e8c;" : "";
|
||||||
|
|
||||||
|
return `
|
||||||
|
<div class="married-list-item">
|
||||||
|
<div class="married-couple-info">
|
||||||
|
${buildMarriedUserHtml(user, userColor)}
|
||||||
|
<div class="married-heart">💖</div>
|
||||||
|
${buildMarriedUserHtml(partner, partnerColor)}
|
||||||
|
</div>
|
||||||
|
<div class="married-meta-info">
|
||||||
|
<span>💍 ${escapeHtml(ring ? ring.name : "无戒指")}</span>
|
||||||
|
<span>💞 <span class="married-intimacy">${Number(marriage.intimacy || 0).toLocaleString()}</span></span>
|
||||||
|
<span>📅 ${escapeHtml(date)}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构建已婚列表中的用户头像和名称。
|
||||||
|
*
|
||||||
|
* @param {Record<string, any>} user 用户信息
|
||||||
|
* @param {string} colorStyle 性别颜色样式
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
function buildMarriedUserHtml(user, colorStyle) {
|
||||||
|
const username = user.username || "—";
|
||||||
|
const headfaceUrl = normalizeHeadfaceUrl(user.headface_url);
|
||||||
|
|
||||||
|
return `
|
||||||
|
<div class="married-user-box" style="cursor:pointer;" data-marriage-user-card="${escapeHtml(username)}">
|
||||||
|
<img src="${escapeHtml(headfaceUrl)}" class="married-user-avatar" onerror="this.src='${DEFAULT_HEADFACE_URL}'">
|
||||||
|
<span class="married-user-name" style="${colorStyle}" title="${escapeHtml(username)}">${escapeHtml(username)}</span>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 关闭婚姻状态弹窗。
|
||||||
|
*
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
export function closeMarriageStatusModal() {
|
||||||
|
const modal = byId("marriage-status-modal");
|
||||||
|
if (modal) {
|
||||||
|
modal.style.display = "none";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 渲染“我的婚姻”状态。
|
||||||
|
*
|
||||||
|
* @param {Record<string, any>} data 婚姻状态接口返回
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
export function renderMarriageStatus(data) {
|
||||||
|
const body = byId("marriage-status-body");
|
||||||
|
const footer = byId("marriage-status-footer");
|
||||||
|
|
||||||
|
if (!body || !footer) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!data.status || data.status === "none" || !data.marriage) {
|
||||||
|
renderSingleStatus(body, footer);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data.status === "pending") {
|
||||||
|
renderPendingStatus(body, footer, data.marriage);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data.status === "married") {
|
||||||
|
renderMarriedStatus(body, footer, data.marriage);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
body.innerHTML = '<div style="text-align:center;color:#999;padding:30px 0;font-size:12px;">暂无有效婚姻记录</div>';
|
||||||
|
footer.innerHTML = closeButtonHtml();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 渲染单身状态。
|
||||||
|
*
|
||||||
|
* @param {HTMLElement} body 内容容器
|
||||||
|
* @param {HTMLElement} footer 底部容器
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
function renderSingleStatus(body, footer) {
|
||||||
|
body.innerHTML = `
|
||||||
|
<div style="text-align:center; padding:16px 0;">
|
||||||
|
<div style="font-size:40px; margin-bottom:10px;">🕊️</div>
|
||||||
|
<div style="font-size:14px; font-weight:bold; color:#555;">目前单身</div>
|
||||||
|
<div style="font-size:11px; color:#999; margin-top:6px; line-height:1.7;">
|
||||||
|
还没有婚姻记录。<br>可在用户名片上点击「求婚」发起求婚。
|
||||||
|
</div>
|
||||||
|
</div>`;
|
||||||
|
footer.innerHTML = closeButtonHtml();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 渲染求婚中状态。
|
||||||
|
*
|
||||||
|
* @param {HTMLElement} body 内容容器
|
||||||
|
* @param {HTMLElement} footer 底部容器
|
||||||
|
* @param {Record<string, any>} marriage 婚姻记录
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
function renderPendingStatus(body, footer, marriage) {
|
||||||
|
const me = window.__chatUser;
|
||||||
|
const other = marriage.user?.id === me?.id ? marriage.partner : marriage.user;
|
||||||
|
const iProposed = marriage.user?.id === me?.id;
|
||||||
|
const expireAt = marriage.expires_at
|
||||||
|
? new Date(marriage.expires_at).toLocaleString("zh-CN", {
|
||||||
|
hour12: false,
|
||||||
|
})
|
||||||
|
: "—";
|
||||||
|
const ringHtml = marriage.ring ? `<span style="font-size:13px;">${escapeHtml(marriage.ring.icon ?? "💍")} ${escapeHtml(marriage.ring.name)}</span>` : "";
|
||||||
|
const otherName = other?.username ?? "—";
|
||||||
|
|
||||||
|
body.innerHTML = `
|
||||||
|
<div style="text-align:center; padding:8px 0;">
|
||||||
|
<div style="font-size:36px; margin-bottom:8px;">💌</div>
|
||||||
|
<div style="font-size:14px; font-weight:bold; color:#be185d;">
|
||||||
|
${iProposed ? `你向 ${escapeHtml(otherName)} 发出了求婚` : `${escapeHtml(otherName)} 向你求婚啦!`}
|
||||||
|
</div>
|
||||||
|
${ringHtml ? `<div style="margin:8px 0; font-size:12px; color:#666;">戒指:${ringHtml}</div>` : ""}
|
||||||
|
<div style="font-size:11px; color:#999; margin-top:6px;">
|
||||||
|
过期时间:${escapeHtml(expireAt)}
|
||||||
|
</div>
|
||||||
|
</div>`;
|
||||||
|
|
||||||
|
footer.innerHTML = iProposed ? waitingFooterHtml() : pendingActionFooterHtml(marriage.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 渲染已婚状态。
|
||||||
|
*
|
||||||
|
* @param {HTMLElement} body 内容容器
|
||||||
|
* @param {HTMLElement} footer 底部容器
|
||||||
|
* @param {Record<string, any>} marriage 婚姻记录
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
function renderMarriedStatus(body, footer, marriage) {
|
||||||
|
const me = window.__chatUser;
|
||||||
|
const other = marriage.user?.id === me?.id ? marriage.partner : marriage.user;
|
||||||
|
const levelIcon = marriage.level_icon ?? "💑";
|
||||||
|
const levelName = marriage.level_name ?? "新婚";
|
||||||
|
const days = Number(marriage.days || 0);
|
||||||
|
const intimacy = Number(marriage.intimacy || 0);
|
||||||
|
const marriedAt = marriage.married_at ?? "—";
|
||||||
|
const ringHtml = marriage.ring ? `${marriage.ring.icon ?? "💍"} ${marriage.ring.name}` : "无";
|
||||||
|
|
||||||
|
body.innerHTML = `
|
||||||
|
<div style="text-align:center; margin-bottom:12px;">
|
||||||
|
<div style="font-size:36px; margin-bottom:6px;">${escapeHtml(levelIcon)}</div>
|
||||||
|
<div style="font-size:14px; font-weight:bold; color:#be185d;">
|
||||||
|
已与 <strong>${escapeHtml(other?.username ?? "—")}</strong> 成婚 🎉
|
||||||
|
</div>
|
||||||
|
<div style="font-size:12px; color:#999; margin-top:4px;">婚姻等级:${escapeHtml(levelName)}</div>
|
||||||
|
</div>
|
||||||
|
<div style="display:grid; grid-template-columns:1fr 1fr; gap:8px; font-size:12px;">
|
||||||
|
<div style="background:#fdf2f8;border:1px solid #fbcfe8;border-radius:6px;padding:10px;text-align:center;">
|
||||||
|
<div style="color:#be185d;font-weight:bold;font-size:18px;">${days}</div>
|
||||||
|
<div style="color:#888;margin-top:2px;">携手天数</div>
|
||||||
|
</div>
|
||||||
|
<div style="background:#fdf4ff;border:1px solid #e9d5ff;border-radius:6px;padding:10px;text-align:center;">
|
||||||
|
<div style="color:#7c3aed;font-weight:bold;font-size:18px;">${intimacy.toLocaleString()}</div>
|
||||||
|
<div style="color:#888;margin-top:2px;">亲密度</div>
|
||||||
|
</div>
|
||||||
|
<div style="background:#f0fdf4;border:1px solid #bbf7d0;border-radius:6px;padding:8px 10px;grid-column:1/-1;">
|
||||||
|
<span style="color:#666;">💍 戒指:</span><span style="color:#333;">${escapeHtml(ringHtml)}</span>
|
||||||
|
|
|
||||||
|
<span style="color:#666;">📅 婚期:</span><span style="color:#333;">${escapeHtml(marriedAt)}</span>
|
||||||
|
</div>
|
||||||
|
</div>`;
|
||||||
|
|
||||||
|
footer.innerHTML = `
|
||||||
|
${closeButtonHtml()}
|
||||||
|
<button data-marriage-divorce="${escapeHtml(marriage.id)}"
|
||||||
|
style="flex:.8;padding:9px;border:1px solid #fca5a5;background:#fff;
|
||||||
|
color:#dc2626;border-radius:6px;font-size:12px;cursor:pointer;">
|
||||||
|
💔 申请离婚
|
||||||
|
</button>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成关闭按钮 HTML。
|
||||||
|
*
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
function closeButtonHtml() {
|
||||||
|
return `
|
||||||
|
<button data-marriage-modal-close
|
||||||
|
style="flex:1;padding:9px;background:#f3f4f6;color:#555;
|
||||||
|
border:1px solid #d1d5db;border-radius:6px;font-size:13px;cursor:pointer;">
|
||||||
|
关闭
|
||||||
|
</button>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成等待对方回应按钮 HTML。
|
||||||
|
*
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
function waitingFooterHtml() {
|
||||||
|
return `
|
||||||
|
<button data-marriage-modal-close
|
||||||
|
style="flex:1;padding:9px;background:#f3f4f6;color:#555;
|
||||||
|
border:1px solid #d1d5db;border-radius:6px;font-size:13px;cursor:pointer;">
|
||||||
|
关闭(等待对方回应)
|
||||||
|
</button>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成求婚方操作按钮 HTML。
|
||||||
|
*
|
||||||
|
* @param {number|string} marriageId 婚姻记录 ID
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
function pendingActionFooterHtml(marriageId) {
|
||||||
|
const safeMarriageId = escapeHtml(marriageId);
|
||||||
|
|
||||||
|
return `
|
||||||
|
<button data-marriage-action="reject" data-marriage-id="${safeMarriageId}" data-marriage-close-after-action="1"
|
||||||
|
style="flex:1;padding:9px;background:#f3f4f6;color:#555;
|
||||||
|
border:1px solid #d1d5db;border-radius:6px;font-size:13px;cursor:pointer;">
|
||||||
|
😢 婉拒
|
||||||
|
</button>
|
||||||
|
<button data-marriage-action="accept" data-marriage-id="${safeMarriageId}" data-marriage-close-after-action="1"
|
||||||
|
style="flex:1;padding:9px;background:linear-gradient(135deg,#be185d,#f43f5e);
|
||||||
|
color:#fff;border:none;border-radius:6px;font-size:13px;
|
||||||
|
font-weight:bold;cursor:pointer;">
|
||||||
|
💑 答应啦!
|
||||||
|
</button>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 通用婚姻操作,处理接受或拒绝求婚。
|
||||||
|
*
|
||||||
|
* @param {string|number} marriageId 婚姻记录 ID
|
||||||
|
* @param {string} action 操作类型
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
*/
|
||||||
|
export async function marriageAction(marriageId, action) {
|
||||||
|
try {
|
||||||
|
const response = await fetch(`/marriage/${encodeURIComponent(marriageId)}/${encodeURIComponent(action)}`, {
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
"X-CSRF-TOKEN": csrfToken(),
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
Accept: "application/json",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const data = await response.json();
|
||||||
|
|
||||||
|
if (data.ok) {
|
||||||
|
window.chatDialog?.alert(data.message || (action === "accept" ? "已接受求婚!" : "已婉拒求婚"), action === "accept" ? "💑 恭喜!" : "提示", action === "accept" ? "#be185d" : "#6b7280");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
window.chatDialog?.alert(data.message || "操作失败", "提示", "#f59e0b");
|
||||||
|
} catch (error) {
|
||||||
|
window.chatDialog?.alert("网络异常,请稍后重试", "错误", "#ef4444");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 申请协议离婚。
|
||||||
|
*
|
||||||
|
* @param {string|number} marriageId 婚姻记录 ID
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
*/
|
||||||
|
export async function tryDivorce(marriageId) {
|
||||||
|
closeMarriageStatusModal();
|
||||||
|
const confirmed = await window.chatDialog?.confirm(
|
||||||
|
"申请协议离婚后,对方有权同意或拒绝(拒绝即转为强制离婚,双方均扣除魅力值)。\n\n确定要申请吗?",
|
||||||
|
"💔 申请离婚",
|
||||||
|
"#dc2626",
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!confirmed) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await fetch(`/marriage/${encodeURIComponent(marriageId)}/divorce`, {
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
"X-CSRF-TOKEN": csrfToken(),
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
Accept: "application/json",
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
type: "mutual",
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
const data = await response.json();
|
||||||
|
window.chatDialog?.alert(data.message || "申请已发送", "提示", data.ok ? "#10b981" : "#f59e0b");
|
||||||
|
} catch (error) {
|
||||||
|
window.chatDialog?.alert("网络异常,请稍后重试", "错误", "#ef4444");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 把婚姻状态模块函数暴露给仍在 Blade 内的入口。
|
||||||
|
*
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
function exposeMarriageGlobals() {
|
||||||
|
window.marriedListPage = window.marriedListPage || 1;
|
||||||
|
window.openMarriageStatusModal = openMarriageStatusModal;
|
||||||
|
window.switchMarriageTab = switchMarriageTab;
|
||||||
|
window.fetchMyMarriageStatus = fetchMyMarriageStatus;
|
||||||
|
window.fetchMarriedList = fetchMarriedList;
|
||||||
|
window.renderMarriedList = renderMarriedList;
|
||||||
|
window.closeMarriageStatusModal = closeMarriageStatusModal;
|
||||||
|
window.renderMarriageStatus = renderMarriageStatus;
|
||||||
|
window.marriageAction = marriageAction;
|
||||||
|
window.tryDivorce = tryDivorce;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 绑定婚姻弹窗 tab、分页、用户名片、状态操作和婚礼红包领取事件。
|
* 绑定婚姻弹窗 tab、分页、用户名片、状态操作和婚礼红包领取事件。
|
||||||
*
|
*
|
||||||
* @returns {void}
|
* @returns {void}
|
||||||
*/
|
*/
|
||||||
export function bindMarriageStatusControls() {
|
export function bindMarriageStatusControls() {
|
||||||
if (marriageStatusEventsBound || typeof document === "undefined") {
|
if (typeof window === "undefined" || typeof document === "undefined") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
exposeMarriageGlobals();
|
||||||
|
|
||||||
|
if (marriageStatusEventsBound) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -62,26 +600,29 @@ export function bindMarriageStatusControls() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 弹窗 tab 与分页内容由存量脚本渲染,事件统一通过 data-* 分发。
|
const modal = byId("marriage-status-modal");
|
||||||
|
|
||||||
|
if (modal && event.target === modal) {
|
||||||
|
closeMarriageStatusModal();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const tabButton = event.target.closest("[data-marriage-tab]");
|
const tabButton = event.target.closest("[data-marriage-tab]");
|
||||||
if (tabButton) {
|
if (tabButton) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
callMarriageGlobal("switchMarriageTab", tabButton.getAttribute("data-marriage-tab") || "");
|
switchMarriageTab(tabButton.getAttribute("data-marriage-tab") || "");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (event.target.closest("[data-marriage-modal-close]")) {
|
if (event.target.closest("[data-marriage-modal-close]")) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
callMarriageGlobal("closeMarriageStatusModal");
|
closeMarriageStatusModal();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (event.target.closest("[data-marriage-open-shop]")) {
|
if (event.target.closest("[data-marriage-open-shop]")) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
// 求婚戒指缺失时只转发到商店弹窗,不触碰婚姻弹窗业务状态。
|
window.openShopModal?.();
|
||||||
if (typeof window.openShopModal === "function") {
|
|
||||||
window.openShopModal();
|
|
||||||
}
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -95,43 +636,34 @@ export function bindMarriageStatusControls() {
|
|||||||
const pageButton = event.target.closest("[data-marriage-page-delta]");
|
const pageButton = event.target.closest("[data-marriage-page-delta]");
|
||||||
if (pageButton) {
|
if (pageButton) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
|
||||||
// 分页状态仍由存量脚本维护,这里从页面文本推导目标页。
|
|
||||||
const delta = Number.parseInt(pageButton.getAttribute("data-marriage-page-delta") || "0", 10);
|
const delta = Number.parseInt(pageButton.getAttribute("data-marriage-page-delta") || "0", 10);
|
||||||
callMarriageGlobal("fetchMarriedList", resolveCurrentMarriedPage() + delta);
|
fetchMarriedList(resolveCurrentMarriedPage() + delta);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const userCard = event.target.closest("[data-marriage-user-card]");
|
const userCard = event.target.closest("[data-marriage-user-card]");
|
||||||
if (userCard) {
|
if (userCard) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
callMarriageGlobal("openUserCard", userCard.getAttribute("data-marriage-user-card") || "");
|
window.openUserCard?.(userCard.getAttribute("data-marriage-user-card") || "");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const actionButton = event.target.closest("[data-marriage-action]");
|
const actionButton = event.target.closest("[data-marriage-action]");
|
||||||
if (actionButton) {
|
if (actionButton) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
marriageAction(actionButton.getAttribute("data-marriage-id") || "", actionButton.getAttribute("data-marriage-action") || "");
|
||||||
|
|
||||||
// 接受/拒绝沿用原 marriageAction,按钮可声明完成后关闭弹窗。
|
|
||||||
const marriageId = actionButton.getAttribute("data-marriage-id") || "";
|
|
||||||
const action = actionButton.getAttribute("data-marriage-action") || "";
|
|
||||||
callMarriageGlobal("marriageAction", marriageId, action);
|
|
||||||
|
|
||||||
// 部分操作按钮声明完成后立即关闭弹窗,这是按钮级行为约定。
|
|
||||||
if (actionButton.getAttribute("data-marriage-close-after-action") === "1") {
|
if (actionButton.getAttribute("data-marriage-close-after-action") === "1") {
|
||||||
callMarriageGlobal("closeMarriageStatusModal");
|
closeMarriageStatusModal();
|
||||||
}
|
}
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const divorceButton = event.target.closest("[data-marriage-divorce]");
|
const divorceButton = event.target.closest("[data-marriage-divorce]");
|
||||||
if (!divorceButton) {
|
if (divorceButton) {
|
||||||
return;
|
event.preventDefault();
|
||||||
|
tryDivorce(divorceButton.getAttribute("data-marriage-divorce") || "");
|
||||||
}
|
}
|
||||||
|
|
||||||
event.preventDefault();
|
|
||||||
callMarriageGlobal("tryDivorce", divorceButton.getAttribute("data-marriage-divorce") || "");
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -959,339 +959,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script>
|
{{-- 婚姻状态弹窗业务脚本已迁移到 resources/js/chat-room/marriage-status.js --}}
|
||||||
/**
|
|
||||||
* 婚姻状态弹窗——工具栏点击「婚姻」按钮触发。
|
|
||||||
* 调用 /marriage/status 接口,展示当前用户婚姻状态(单身/求婚中/已婚)。
|
|
||||||
*/
|
|
||||||
(function() {
|
|
||||||
|
|
||||||
const CSRF = () => document.querySelector('meta[name="csrf-token"]')?.content ?? '';
|
|
||||||
const $ = id => document.getElementById(id);
|
|
||||||
|
|
||||||
window.marriedListPage = 1;
|
|
||||||
|
|
||||||
/** 打开弹窗并拉取状态 */
|
|
||||||
window.openMarriageStatusModal = function() {
|
|
||||||
$('marriage-status-modal').style.display = 'flex';
|
|
||||||
switchMarriageTab('mine');
|
|
||||||
};
|
|
||||||
|
|
||||||
/** 切换 Tab */
|
|
||||||
window.switchMarriageTab = function(tabName) {
|
|
||||||
$('marriage-tabbtn-mine').classList.toggle('active', tabName === 'mine');
|
|
||||||
$('marriage-tabbtn-list').classList.toggle('active', tabName === 'list');
|
|
||||||
$('marriage-view-mine').style.display = tabName === 'mine' ? 'flex' : 'none';
|
|
||||||
$('marriage-view-list').style.display = tabName === 'list' ? 'flex' : 'none';
|
|
||||||
|
|
||||||
if (tabName === 'mine') {
|
|
||||||
fetchMyMarriageStatus();
|
|
||||||
} else {
|
|
||||||
fetchMarriedList(1);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
async function fetchMyMarriageStatus() {
|
|
||||||
$('marriage-status-body').innerHTML =
|
|
||||||
'<div style="text-align:center;color:#aaa;padding:30px 0;font-size:12px;">加载中…</div>';
|
|
||||||
$('marriage-status-footer').innerHTML = '';
|
|
||||||
|
|
||||||
try {
|
|
||||||
const res = await fetch('/marriage/status', {
|
|
||||||
headers: { Accept: 'application/json' }
|
|
||||||
});
|
|
||||||
const data = await res.json();
|
|
||||||
renderMarriageStatus(data);
|
|
||||||
} catch (e) {
|
|
||||||
$('marriage-status-body').innerHTML =
|
|
||||||
'<div style="text-align:center;color:#e55;padding:30px 0;font-size:12px;">❌ 加载失败,请稍后重试</div>';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
window.fetchMarriedList = async function(page) {
|
|
||||||
if (page < 1) return;
|
|
||||||
|
|
||||||
const container = $('married-list-container');
|
|
||||||
container.innerHTML = '<div style="text-align:center;color:#aaa;padding:30px 0;font-size:12px;">加载中…</div>';
|
|
||||||
|
|
||||||
try {
|
|
||||||
const res = await fetch(`/marriage/list?page=${page}`, {
|
|
||||||
headers: { Accept: 'application/json' }
|
|
||||||
});
|
|
||||||
const json = await res.json();
|
|
||||||
if (json.status === 'success') {
|
|
||||||
marriedListPage = json.pagination.current_page;
|
|
||||||
renderMarriedList(json.data, json.pagination);
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
container.innerHTML = '<div style="text-align:center;color:#e55;padding:30px 0;font-size:12px;">❌ 加载失败</div>';
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
function renderMarriedList(data, pagination) {
|
|
||||||
const container = $('married-list-container');
|
|
||||||
if (!data || data.length === 0) {
|
|
||||||
container.innerHTML = '<div style="text-align:center;color:#aaa;padding:40px 0;font-size:12px;">💖 暂无婚姻记录,快去寻找你的另一半吧</div>';
|
|
||||||
$('married-list-pagination').style.display = 'none';
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$('married-list-pagination').style.display = 'flex';
|
|
||||||
$('married-page-info').textContent = `${pagination.current_page} / ${pagination.last_page}`;
|
|
||||||
$('married-prev-btn').disabled = pagination.current_page <= 1;
|
|
||||||
$('married-prev-btn').style.opacity = pagination.current_page <= 1 ? 0.5 : 1;
|
|
||||||
$('married-next-btn').disabled = pagination.current_page >= pagination.last_page;
|
|
||||||
$('married-next-btn').style.opacity = pagination.current_page >= pagination.last_page ? 0.5 : 1;
|
|
||||||
|
|
||||||
container.innerHTML = data.map(m => {
|
|
||||||
const user = m.user;
|
|
||||||
const partner = m.partner;
|
|
||||||
const ring = m.ring_item;
|
|
||||||
const date = m.married_at ? m.married_at.substring(0, 10) : '—';
|
|
||||||
|
|
||||||
const userColor = (user && user.sex == 2) ? 'color:#e91e8c;' : '';
|
|
||||||
const partnerColor = (partner && partner.sex == 2) ? 'color:#e91e8c;' : '';
|
|
||||||
|
|
||||||
return `
|
|
||||||
<div class="married-list-item">
|
|
||||||
<div class="married-couple-info">
|
|
||||||
<div class="married-user-box" style="cursor:pointer;" data-marriage-user-card="${user.username}">
|
|
||||||
<img src="${user.headface_url || '/images/headface/1.gif'}" class="married-user-avatar" onerror="this.src='/images/headface/1.gif'">
|
|
||||||
<span class="married-user-name" style="${userColor}" title="${user.username}">${user.username}</span>
|
|
||||||
</div>
|
|
||||||
<div class="married-heart">💖</div>
|
|
||||||
<div class="married-user-box" style="cursor:pointer;" data-marriage-user-card="${partner.username}">
|
|
||||||
<img src="${partner.headface_url || '/images/headface/1.gif'}" class="married-user-avatar" onerror="this.src='/images/headface/1.gif'">
|
|
||||||
<span class="married-user-name" style="${partnerColor}" title="${partner.username}">${partner.username}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="married-meta-info">
|
|
||||||
<span>💍 ${ring ? ring.name : '无戒指'}</span>
|
|
||||||
<span>💞 <span class="married-intimacy">${Number(m.intimacy).toLocaleString()}</span></span>
|
|
||||||
<span>📅 ${date}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
}).join('');
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 关闭弹窗 */
|
|
||||||
window.closeMarriageStatusModal = function() {
|
|
||||||
$('marriage-status-modal').style.display = 'none';
|
|
||||||
};
|
|
||||||
|
|
||||||
// 点击遮罩关闭
|
|
||||||
$('marriage-status-modal').addEventListener('click', function(e) {
|
|
||||||
if (e.target === this) closeMarriageStatusModal();
|
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 根据接口返回数据渲染弹窗内容。
|
|
||||||
*
|
|
||||||
* @param {object} data `/marriage/status` 响应 JSON
|
|
||||||
*/
|
|
||||||
function renderMarriageStatus(data) {
|
|
||||||
const body = $('marriage-status-body');
|
|
||||||
const footer = $('marriage-status-footer');
|
|
||||||
|
|
||||||
// ── 单身 ────────────────────────────────────
|
|
||||||
if (!data.status || data.status === 'none' || !data.marriage) {
|
|
||||||
body.innerHTML = `
|
|
||||||
<div style="text-align:center; padding:16px 0;">
|
|
||||||
<div style="font-size:40px; margin-bottom:10px;">🕊️</div>
|
|
||||||
<div style="font-size:14px; font-weight:bold; color:#555;">目前单身</div>
|
|
||||||
<div style="font-size:11px; color:#999; margin-top:6px; line-height:1.7;">
|
|
||||||
还没有婚姻记录。<br>可在用户名片上点击「求婚」发起求婚。
|
|
||||||
</div>
|
|
||||||
</div>`;
|
|
||||||
footer.innerHTML = `
|
|
||||||
<button data-marriage-modal-close
|
|
||||||
style="flex:1; padding:9px; background:#f3f4f6; color:#555;
|
|
||||||
border:1px solid #d1d5db; border-radius:6px; font-size:13px; cursor:pointer;">
|
|
||||||
关闭
|
|
||||||
</button>`;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const m = data.marriage;
|
|
||||||
const isMine = (m.user && m.user.username === window.__chatUser?.username) ||
|
|
||||||
window.__chatUser?.id === m.user?.id ||
|
|
||||||
window.__chatUser?.id === m.partner?.id;
|
|
||||||
|
|
||||||
// 确定"另一方"信息(我可能是 user 也可能是 partner)
|
|
||||||
const me = window.__chatUser;
|
|
||||||
const other = (m.user?.id === me?.id) ? m.partner : m.user;
|
|
||||||
const iAmUser = (m.user?.id === me?.id);
|
|
||||||
|
|
||||||
// ── 求婚中 ──────────────────────────────────
|
|
||||||
if (data.status === 'pending') {
|
|
||||||
const iProposed = iAmUser; // user_id 是发起方
|
|
||||||
const expireAt = m.expires_at ? new Date(m.expires_at).toLocaleString('zh-CN', {
|
|
||||||
hour12: false
|
|
||||||
}) : '—';
|
|
||||||
const ringHtml = m.ring ?
|
|
||||||
`<span style="font-size:13px;">${m.ring.icon ?? '💍'} ${m.ring.name}</span>` : '';
|
|
||||||
|
|
||||||
body.innerHTML = `
|
|
||||||
<div style="text-align:center; padding:8px 0;">
|
|
||||||
<div style="font-size:36px; margin-bottom:8px;">💌</div>
|
|
||||||
<div style="font-size:14px; font-weight:bold; color:#be185d;">
|
|
||||||
${iProposed ? '你向 ' + (other?.username ?? '—') + ' 发出了求婚' : (other?.username ?? '—') + ' 向你求婚啦!'}
|
|
||||||
</div>
|
|
||||||
${ringHtml ? `<div style="margin:8px 0; font-size:12px; color:#666;">戒指:${ringHtml}</div>` : ''}
|
|
||||||
<div style="font-size:11px; color:#999; margin-top:6px;">
|
|
||||||
过期时间:${expireAt}
|
|
||||||
</div>
|
|
||||||
</div>`;
|
|
||||||
|
|
||||||
if (!iProposed) {
|
|
||||||
// 被求婚方:可以接受 / 拒绝
|
|
||||||
footer.innerHTML = `
|
|
||||||
<button data-marriage-action="reject" data-marriage-id="${m.id}" data-marriage-close-after-action="1"
|
|
||||||
style="flex:1;padding:9px;background:#f3f4f6;color:#555;
|
|
||||||
border:1px solid #d1d5db;border-radius:6px;font-size:13px;cursor:pointer;">
|
|
||||||
😢 婉拒
|
|
||||||
</button>
|
|
||||||
<button data-marriage-action="accept" data-marriage-id="${m.id}" data-marriage-close-after-action="1"
|
|
||||||
style="flex:1;padding:9px;background:linear-gradient(135deg,#be185d,#f43f5e);
|
|
||||||
color:#fff;border:none;border-radius:6px;font-size:13px;
|
|
||||||
font-weight:bold;cursor:pointer;">
|
|
||||||
💑 答应啦!
|
|
||||||
</button>`;
|
|
||||||
} else {
|
|
||||||
footer.innerHTML = `
|
|
||||||
<button data-marriage-modal-close
|
|
||||||
style="flex:1;padding:9px;background:#f3f4f6;color:#555;
|
|
||||||
border:1px solid #d1d5db;border-radius:6px;font-size:13px;cursor:pointer;">
|
|
||||||
关闭(等待对方回应)
|
|
||||||
</button>`;
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ── 已婚 ────────────────────────────────────
|
|
||||||
if (data.status === 'married') {
|
|
||||||
const levelIcon = m.level_icon ?? '💑';
|
|
||||||
const levelName = m.level_name ?? '新婚';
|
|
||||||
const days = m.days ?? 0;
|
|
||||||
const intimacy = m.intimacy ?? 0;
|
|
||||||
const marriedAt = m.married_at ?? '—';
|
|
||||||
const ringHtml = m.ring ? `${m.ring.icon ?? '💍'} ${m.ring.name}` : '无';
|
|
||||||
|
|
||||||
body.innerHTML = `
|
|
||||||
<div style="text-align:center; margin-bottom:12px;">
|
|
||||||
<div style="font-size:36px; margin-bottom:6px;">${levelIcon}</div>
|
|
||||||
<div style="font-size:14px; font-weight:bold; color:#be185d;">
|
|
||||||
已与 <strong>${other?.username ?? '—'}</strong> 成婚 🎉
|
|
||||||
</div>
|
|
||||||
<div style="font-size:12px; color:#999; margin-top:4px;">婚姻等级:${levelName}</div>
|
|
||||||
</div>
|
|
||||||
<div style="display:grid; grid-template-columns:1fr 1fr; gap:8px; font-size:12px;">
|
|
||||||
<div style="background:#fdf2f8;border:1px solid #fbcfe8;border-radius:6px;padding:10px;text-align:center;">
|
|
||||||
<div style="color:#be185d;font-weight:bold;font-size:18px;">${days}</div>
|
|
||||||
<div style="color:#888;margin-top:2px;">携手天数</div>
|
|
||||||
</div>
|
|
||||||
<div style="background:#fdf4ff;border:1px solid #e9d5ff;border-radius:6px;padding:10px;text-align:center;">
|
|
||||||
<div style="color:#7c3aed;font-weight:bold;font-size:18px;">${Number(intimacy).toLocaleString()}</div>
|
|
||||||
<div style="color:#888;margin-top:2px;">亲密度</div>
|
|
||||||
</div>
|
|
||||||
<div style="background:#f0fdf4;border:1px solid #bbf7d0;border-radius:6px;padding:8px 10px;grid-column:1/-1;">
|
|
||||||
<span style="color:#666;">💍 戒指:</span><span style="color:#333;">${ringHtml}</span>
|
|
||||||
|
|
|
||||||
<span style="color:#666;">📅 婚期:</span><span style="color:#333;">${marriedAt}</span>
|
|
||||||
</div>
|
|
||||||
</div>`;
|
|
||||||
|
|
||||||
// 已婚底部:离婚入口(需要二次确认)
|
|
||||||
footer.innerHTML = `
|
|
||||||
<button data-marriage-modal-close
|
|
||||||
style="flex:1;padding:9px;background:#f3f4f6;color:#555;
|
|
||||||
border:1px solid #d1d5db;border-radius:6px;font-size:13px;cursor:pointer;">
|
|
||||||
关闭
|
|
||||||
</button>
|
|
||||||
<button data-marriage-divorce="${m.id}"
|
|
||||||
style="flex:.8;padding:9px;border:1px solid #fca5a5;background:#fff;
|
|
||||||
color:#dc2626;border-radius:6px;font-size:12px;cursor:pointer;">
|
|
||||||
💔 申请离婚
|
|
||||||
</button>`;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 其他状态(divorced 等)
|
|
||||||
body.innerHTML =
|
|
||||||
`<div style="text-align:center;color:#999;padding:30px 0;font-size:12px;">暂无有效婚姻记录</div>`;
|
|
||||||
footer.innerHTML = `
|
|
||||||
<button data-marriage-modal-close
|
|
||||||
style="flex:1;padding:9px;background:#f3f4f6;color:#555;
|
|
||||||
border:1px solid #d1d5db;border-radius:6px;font-size:13px;cursor:pointer;">关闭</button>`;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 通用婚姻操作(接受 / 拒绝求婚)
|
|
||||||
*
|
|
||||||
* @param {string|number} marriageId marriage 记录 ID
|
|
||||||
* @param {string} action 'accept' | 'reject'
|
|
||||||
*/
|
|
||||||
window.marriageAction = async function(marriageId, action) {
|
|
||||||
try {
|
|
||||||
const res = await fetch(`/marriage/${marriageId}/${action}`, {
|
|
||||||
method: 'POST',
|
|
||||||
headers: {
|
|
||||||
'X-CSRF-TOKEN': CSRF(),
|
|
||||||
'Content-Type': 'application/json',
|
|
||||||
Accept: 'application/json',
|
|
||||||
},
|
|
||||||
});
|
|
||||||
const data = await res.json();
|
|
||||||
if (data.ok) {
|
|
||||||
window.chatDialog?.alert(data.message || (action === 'accept' ? '已接受求婚!' : '已婉拒求婚'),
|
|
||||||
action === 'accept' ? '💑 恭喜!' : '提示', action === 'accept' ? '#be185d' :
|
|
||||||
'#6b7280');
|
|
||||||
} else {
|
|
||||||
window.chatDialog?.alert(data.message || '操作失败', '提示', '#f59e0b');
|
|
||||||
}
|
|
||||||
} catch {
|
|
||||||
window.chatDialog?.alert('网络异常,请稍后重试', '错误', '#ef4444');
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 申请离婚(先弹确认框,再调接口)
|
|
||||||
*
|
|
||||||
* @param {string|number} marriageId marriage 记录 ID
|
|
||||||
*/
|
|
||||||
window.tryDivorce = async function(marriageId) {
|
|
||||||
closeMarriageStatusModal();
|
|
||||||
const confirmed = await window.chatDialog?.confirm(
|
|
||||||
'申请协议离婚后,对方有权同意或拒绝(拒绝即转为强制离婚,双方均扣除魅力值)。\n\n确定要申请吗?',
|
|
||||||
'💔 申请离婚',
|
|
||||||
'#dc2626',
|
|
||||||
);
|
|
||||||
if (!confirmed) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
const res = await fetch(`/marriage/${marriageId}/divorce`, {
|
|
||||||
method: 'POST',
|
|
||||||
headers: {
|
|
||||||
'X-CSRF-TOKEN': CSRF(),
|
|
||||||
'Content-Type': 'application/json',
|
|
||||||
Accept: 'application/json',
|
|
||||||
},
|
|
||||||
body: JSON.stringify({
|
|
||||||
type: 'mutual'
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
const data = await res.json();
|
|
||||||
window.chatDialog?.alert(data.message || '申请已发送', '提示', data.ok ? '#10b981' : '#f59e0b');
|
|
||||||
} catch {
|
|
||||||
window.chatDialog?.alert('网络异常,请稍后重试', '错误', '#ef4444');
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
})();
|
|
||||||
</script>
|
|
||||||
|
|
||||||
{{-- ═══════════ 银行弹窗 ═══════════ --}}
|
{{-- ═══════════ 银行弹窗 ═══════════ --}}
|
||||||
<style>
|
<style>
|
||||||
|
|||||||
Reference in New Issue
Block a user