新增银行功能:存取金币、流水记录、PC/手机端双入口;迁移 bank_jjb 字段和 bank_logs 表

This commit is contained in:
2026-03-18 20:31:19 +08:00
parent 6c4183e175
commit d7a575d8c8
7 changed files with 605 additions and 2 deletions

View File

@@ -18,7 +18,7 @@
<div class="tool-btn" onclick="openShopModal()" title="购买道具">商店</div>
<div class="tool-btn" onclick="saveExp()" title="手动存经验点">存点</div>
<div class="tool-btn" onclick="openGameHall()" title="娱乐游戏大厅">娱乐</div>
<div class="tool-btn" onclick="window.chatDialog.alert('银行功能开发中,敬请期待!', '开发中', '#78716c')" title="银行(待开发)">银行</div>
<div class="tool-btn" onclick="openBankModal()" title="银行存取金币">银行</div>
<div class="tool-btn" onclick="openMarriageStatusModal()" title="婚姻状态">婚姻</div>
<div class="tool-btn" onclick="openFriendPanel()" title="好友列表">好友</div>
<div class="tool-btn" onclick="openAvatarPicker()" title="修改头像">头像</div>
@@ -1568,3 +1568,353 @@
})();
</script>
{{-- ═══════════ 银行弹窗 ═══════════ --}}
<style>
/* 银行弹窗遮罩 */
#bank-modal {
display: none;
position: fixed;
inset: 0;
background: rgba(0, 0, 0, .5);
z-index: 9999;
justify-content: center;
align-items: center;
}
/* 弹窗主体 */
#bank-modal-inner {
background: #fff;
border-radius: 8px;
width: 480px;
max-width: 95vw;
max-height: 86vh;
display: flex;
flex-direction: column;
box-shadow: 0 8px 32px rgba(0, 0, 0, .3);
overflow: hidden;
}
/* 标题栏 */
#bank-modal-header {
background: linear-gradient(135deg, #78350f, #b45309);
color: #fff;
padding: 10px 16px;
display: flex;
align-items: center;
gap: 10px;
flex-shrink: 0;
}
/* 余额卡片区 */
.bank-balance-row {
display: flex;
gap: 10px;
padding: 14px 16px 10px;
background: #fffbeb;
border-bottom: 1px solid #fde68a;
flex-shrink: 0;
}
.bank-balance-card {
flex: 1;
background: #fff;
border: 1px solid #fcd34d;
border-radius: 8px;
padding: 10px 12px;
text-align: center;
}
.bank-balance-card .label {
font-size: 11px;
color: #92400e;
margin-bottom: 4px;
}
.bank-balance-card .value {
font-size: 20px;
font-weight: bold;
color: #d97706;
}
/* 操作区 */
.bank-op-section {
padding: 12px 16px;
border-bottom: 1px solid #fde68a;
background: #fffbeb;
flex-shrink: 0;
}
.bank-op-row {
display: flex;
gap: 8px;
align-items: center;
margin-bottom: 8px;
}
.bank-op-row:last-child {
margin-bottom: 0;
}
.bank-op-label {
font-size: 12px;
font-weight: bold;
color: #78350f;
width: 44px;
flex-shrink: 0;
}
.bank-op-input {
flex: 1;
height: 34px;
padding: 0 10px;
border: 1px solid #fbbf24;
border-radius: 6px;
font-size: 13px;
color: #334155;
outline: none;
}
.bank-op-input:focus {
border-color: #d97706;
}
.bank-op-btn {
height: 34px;
padding: 0 14px;
border: none;
border-radius: 6px;
font-size: 12px;
font-weight: bold;
cursor: pointer;
white-space: nowrap;
}
.bank-op-btn-deposit {
background: linear-gradient(135deg, #b45309, #d97706);
color: #fff;
}
.bank-op-btn-withdraw {
background: linear-gradient(135deg, #16a34a, #22c55e);
color: #fff;
}
.bank-op-btn:disabled {
opacity: .6;
cursor: not-allowed;
}
/* 流水区 */
#bank-logs-list {
flex: 1;
overflow-y: auto;
padding: 10px 16px;
background: #fff;
}
.bank-log-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 6px 0;
border-bottom: 1px solid #f3f4f6;
font-size: 12px;
}
.bank-log-item:last-child {
border-bottom: none;
}
.bank-log-deposit { color: #b45309; }
.bank-log-withdraw { color: #16a34a; }
</style>
<div id="bank-modal">
<div id="bank-modal-inner">
{{-- 标题栏 --}}
<div id="bank-modal-header">
<span style="font-size:14px; font-weight:bold; flex:1;">🏦 金币银行</span>
<span style="cursor:pointer; font-size:18px; opacity:.8;" onclick="closeBankModal()">&times;</span>
</div>
{{-- 余额展示 --}}
<div class="bank-balance-row">
<div class="bank-balance-card">
<div class="label">💰 流通金币</div>
<div class="value" id="bank-jjb-display"></div>
</div>
<div class="bank-balance-card">
<div class="label">🏦 银行存款</div>
<div class="value" id="bank-bank-display"></div>
</div>
</div>
{{-- 操作区 --}}
<div class="bank-op-section">
{{-- 存款 --}}
<div class="bank-op-row">
<span class="bank-op-label"> </span>
<input type="number" id="bank-deposit-input" class="bank-op-input" min="1" placeholder="输入存款金额">
<button id="bank-deposit-btn" class="bank-op-btn bank-op-btn-deposit" onclick="bankDeposit()">存入</button>
</div>
{{-- 取款 --}}
<div class="bank-op-row">
<span class="bank-op-label"> </span>
<input type="number" id="bank-withdraw-input" class="bank-op-input" min="1" placeholder="输入取款金额">
<button id="bank-withdraw-btn" class="bank-op-btn bank-op-btn-withdraw" onclick="bankWithdraw()">取出</button>
</div>
<div id="bank-op-msg" style="display:none; font-size:12px; font-weight:bold; padding:6px 10px; border-radius:5px; margin-top:6px;"></div>
</div>
{{-- 流水记录 --}}
<div id="bank-logs-list">
<div style="text-align:center; color:#aaa; font-size:12px; padding:20px;">加载中...</div>
</div>
</div>
</div>
<script>
/**
* 打开银行弹窗并加载数据
*/
function openBankModal() {
document.getElementById('bank-modal').style.display = 'flex';
bankLoadInfo();
}
/**
* 关闭银行弹窗
*/
function closeBankModal() {
document.getElementById('bank-modal').style.display = 'none';
}
/**
* 加载银行数据(余额+流水)
*/
async function bankLoadInfo() {
try {
const res = await fetch('/bank', { headers: { 'Accept': 'application/json' } });
const data = await res.json();
if (data.status !== 'success') { return; }
// 更新余额显示
document.getElementById('bank-jjb-display').textContent = data.jjb.toLocaleString();
document.getElementById('bank-bank-display').textContent = data.bank_jjb.toLocaleString();
// 同步更新全局上下文
window.chatContext.myGold = data.jjb;
window.chatContext.bankGold = data.bank_jjb;
// 渲染流水
const list = document.getElementById('bank-logs-list');
if (data.logs.length === 0) {
list.innerHTML = '<div style="text-align:center;color:#aaa;font-size:12px;padding:20px;">暂无记录</div>';
return;
}
list.innerHTML = data.logs.map(log => {
const isDeposit = log.type === 'deposit';
const sign = isDeposit ? '▼存' : '▲取';
const cls = isDeposit ? 'bank-log-deposit' : 'bank-log-withdraw';
const dt = log.created_at.replace('T', ' ').substring(0, 16);
return `<div class="bank-log-item">
<span class="${cls}">${sign} ${log.amount.toLocaleString()} 金币</span>
<span style="color:#6b7280; font-size:11px;">存款余额:${log.balance_after.toLocaleString()}</span>
<span style="color:#9ca3af; font-size:10px;">${dt}</span>
</div>`;
}).join('');
} catch (e) {
document.getElementById('bank-logs-list').innerHTML =
'<div style="text-align:center;color:red;font-size:12px;padding:20px;">加载失败</div>';
}
}
/**
* 存款操作
*/
async function bankDeposit() {
const amount = parseInt(document.getElementById('bank-deposit-input').value, 10);
if (!amount || amount <= 0) {
bankShowMsg('请输入有效的存款金额', false);
return;
}
const btn = document.getElementById('bank-deposit-btn');
btn.disabled = true;
try {
const res = await fetch('/bank/deposit', {
method: 'POST',
headers: {
'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').content,
'Content-Type': 'application/json',
'Accept': 'application/json',
},
body: JSON.stringify({ amount }),
});
const data = await res.json();
bankShowMsg(data.message, data.status === 'success');
if (data.status === 'success') {
document.getElementById('bank-deposit-input').value = '';
bankLoadInfo();
}
} catch (e) {
bankShowMsg('网络异常,请稍后重试', false);
}
btn.disabled = false;
}
/**
* 取款操作
*/
async function bankWithdraw() {
const amount = parseInt(document.getElementById('bank-withdraw-input').value, 10);
if (!amount || amount <= 0) {
bankShowMsg('请输入有效的取款金额', false);
return;
}
const btn = document.getElementById('bank-withdraw-btn');
btn.disabled = true;
try {
const res = await fetch('/bank/withdraw', {
method: 'POST',
headers: {
'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').content,
'Content-Type': 'application/json',
'Accept': 'application/json',
},
body: JSON.stringify({ amount }),
});
const data = await res.json();
bankShowMsg(data.message, data.status === 'success');
if (data.status === 'success') {
document.getElementById('bank-withdraw-input').value = '';
bankLoadInfo();
}
} catch (e) {
bankShowMsg('网络异常,请稍后重试', false);
}
btn.disabled = false;
}
/**
* 显示操作结果提示3秒后自动消失
*
* @param {string} msg 提示内容
* @param {boolean} success 是否成功
*/
function bankShowMsg(msg, success) {
const el = document.getElementById('bank-op-msg');
el.textContent = msg;
el.style.background = success ? '#f0fdf4' : '#fff5f5';
el.style.border = success ? '1px solid #86efac' : '1px solid #fecaca';
el.style.color = success ? '#16a34a' : '#dc2626';
el.style.display = 'block';
clearTimeout(el._t);
el._t = setTimeout(() => { el.style.display = 'none'; }, 3000);
}
// 点击遮罩关闭银行弹窗
document.getElementById('bank-modal').addEventListener('click', function (e) {
if (e.target === this) { closeBankModal(); }
});
</script>