普通用户也支持新建群聊
This commit is contained in:
@@ -4,13 +4,12 @@ body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-
|
||||
.hidden { display: none !important; }
|
||||
|
||||
.header { background: linear-gradient(135deg, #1e3c72 0%, #2a5298 100%); color: #fff; padding: 0 16px; display: flex; justify-content: space-between; align-items: center; min-height: 48px; }
|
||||
.header-left { display: flex; align-items: center; gap: 8px; }
|
||||
.header-left { display: flex; align-items: center; gap: 8px; cursor: pointer; }
|
||||
.header-left h2 { font-size: 18px; font-weight: 500; }
|
||||
.icon-btn { background: none; border: none; color: #fff; cursor: pointer; display: flex; align-items: center; justify-content: center; padding: 4px; border-radius: 50%; }
|
||||
.icon-btn svg { width: 22px; height: 22px; stroke: #fff; fill: none; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
|
||||
|
||||
#create-room-btn { visibility: hidden; }
|
||||
#create-room-btn.visible { visibility: visible; }
|
||||
|
||||
|
||||
.chat-list { flex: 1; overflow-y: auto; padding-bottom: 8px; }
|
||||
.chat-item { padding: 14px 16px; border-bottom: 1px solid #f0f0f0; display: flex; align-items: center; cursor: pointer; }
|
||||
@@ -58,6 +57,10 @@ body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-
|
||||
|
||||
.purchase-panel-body { flex: 1; overflow-y: auto; padding: 12px; }
|
||||
.purchase-month-group { margin-bottom: 20px; }
|
||||
.purchase-month-group summary { list-style: none; cursor: pointer; }
|
||||
.purchase-month-group summary::-webkit-details-marker { display: none; }
|
||||
.purchase-month-group summary::after { content: '▼'; font-size: 11px; margin-left: 8px; color: #888; }
|
||||
.purchase-month-group[open] summary::after { content: '▲'; }
|
||||
.purchase-month-header { background: #f5f5f5; font-size: 16px; font-weight: 600; padding: 10px 8px; border-radius: 4px; display: flex; justify-content: space-between; }
|
||||
.purchase-item { padding: 12px 8px; border-bottom: 1px solid #f0f0f0; cursor: pointer; }
|
||||
.purchase-item:active { background: #f9f9f9; }
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
<input type="password" id="login-pass" placeholder="密码">
|
||||
<button class="primary-btn" onclick="login()" style="width:100%;">登录</button>
|
||||
<p id="login-error" style="color:red; margin-top:8px;"></p>
|
||||
<div class="version">v2.7</div>
|
||||
<div class="version" id="version-text"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -29,19 +29,16 @@
|
||||
<!-- 群聊列表页 -->
|
||||
<div id="list-page" style="display:flex; flex-direction:column; height:100%;">
|
||||
<div class="header">
|
||||
<div class="header-left">
|
||||
<div class="header-left" onclick="Store.logout()">
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" stroke="#fff" fill="none" stroke-width="1.5">
|
||||
<rect x="3" y="3" width="18" height="18" rx="3"/><line x1="9" y1="9" x2="15" y2="9"/><line x1="9" y1="13" x2="15" y2="13"/><line x1="9" y1="17" x2="12" y2="17"/>
|
||||
</svg>
|
||||
<h2>小财记账</h2>
|
||||
</div>
|
||||
<div style="display:flex; gap:2px;">
|
||||
<button class="icon-btn" id="create-room-btn" onclick="openCreateRoom()" title="新建群聊">
|
||||
<button class="icon-btn visible" id="create-room-btn" onclick="openCreateRoom()" title="新建群聊">
|
||||
<svg viewBox="0 0 24 24"><line x1="12" y1="5" x2="12" y2="19"></line><line x1="5" y1="12" x2="19" y2="12"></line></svg>
|
||||
</button>
|
||||
<button class="icon-btn" id="logout-btn" onclick="Store.logout()" title="注销登录">
|
||||
<svg viewBox="0 0 24 24"><path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4"/><polyline points="16 17 21 12 16 7"/><line x1="21" y1="12" x2="9" y2="12"/></svg>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="chat-list" id="chat-list-container"></div>
|
||||
@@ -238,6 +235,12 @@
|
||||
<script src="/js/purchases.js"></script>
|
||||
<script src="/js/auth.js"></script>
|
||||
<script>
|
||||
// 版本号
|
||||
(function() {
|
||||
var d = new Date();
|
||||
var pad = function(n) { return n < 10 ? '0' + n : '' + n; };
|
||||
document.getElementById('version-text').textContent = 'v2.8-' + (d.getFullYear() % 100) + pad(d.getMonth() + 1) + pad(d.getDate());
|
||||
})();
|
||||
document.addEventListener('input', function(e) {
|
||||
if (e.target.id === 'msg-input' || e.target.id === 'purchase-msg-input') {
|
||||
e.target.style.height = 'auto';
|
||||
|
||||
@@ -3,16 +3,10 @@ async function init() {
|
||||
const token = Store.getToken();
|
||||
if (token) {
|
||||
try {
|
||||
const res = await fetch('/api/user', { headers: { 'Authorization': `Bearer ${token}` } });
|
||||
const res = await fetch('/api/user', { headers: { 'Authorization': 'Bearer ' + token } });
|
||||
if (res.ok) {
|
||||
const user = await res.json();
|
||||
document.getElementById('login-page').classList.add('hidden');
|
||||
document.getElementById('main-page').classList.remove('hidden');
|
||||
if (localStorage.getItem('isAdmin') === 'true') {
|
||||
document.getElementById('create-room-btn').classList.add('visible');
|
||||
} else {
|
||||
document.getElementById('create-room-btn').classList.remove('visible');
|
||||
}
|
||||
initWebSocket();
|
||||
loadRooms();
|
||||
updateSummaryPreview();
|
||||
@@ -38,17 +32,12 @@ async function login() {
|
||||
Store.saveAuth(data);
|
||||
document.getElementById('login-page').classList.add('hidden');
|
||||
document.getElementById('main-page').classList.remove('hidden');
|
||||
if (data.isAdmin) {
|
||||
document.getElementById('create-room-btn').classList.add('visible');
|
||||
} else {
|
||||
document.getElementById('create-room-btn').classList.remove('visible');
|
||||
}
|
||||
initWebSocket();
|
||||
loadRooms();
|
||||
updateSummaryPreview();
|
||||
initSwipeGestures();
|
||||
} else {
|
||||
const err = await res.json();
|
||||
document.getElementById('login-error').textContent = err.error || '登录失败';
|
||||
document.getElementById('login-error').textContent = err.error || '\u767b\u5f55\u5931\u8d25';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ function renderPurchasePanel(items) {
|
||||
Object.keys(groups).sort().reverse().forEach(month => {
|
||||
const monthItems = groups[month];
|
||||
const monthTotal = monthItems.reduce((s, i) => s + (i.amount||0), 0);
|
||||
html += '<div class="purchase-month-group"><div class="purchase-month-header"><span>' + month + '</span><span>' + monthTotal + '</span></div>';
|
||||
html += '<details class="purchase-month-group" open><summary class="purchase-month-header"><span>' + month + '</span><span>¥' + monthTotal + '</span></summary>';
|
||||
monthItems.forEach(item => {
|
||||
const timeShort = item.created_at.split(' ')[1]?.substring(0,5) || '';
|
||||
const dateShort = item.created_at.split(' ')[0]?.substring(5) || '';
|
||||
@@ -31,12 +31,12 @@ function renderPurchasePanel(items) {
|
||||
<div class="item-main"><span class="item-name">${escapeHtml(item.item)}</span><span class="item-amount">${item.amount}</span></div>
|
||||
<div class="item-meta">
|
||||
<span>${dateShort} ${timeShort}</span>
|
||||
<span>${item.quantity} ${item.invoice_type !== '无票' ? '\u00b7 ' + item.invoice_type : ''}</span>
|
||||
<span>x${item.quantity} ${item.invoice_type !== '无票' ? '· ' + item.invoice_type : ''}</span>
|
||||
<span class="status-badge ${item.status==='已完成'?'done':''}">${item.status}</span>
|
||||
</div>
|
||||
</div>`;
|
||||
});
|
||||
html += '</div>';
|
||||
html += '</details>';
|
||||
});
|
||||
container.innerHTML = html;
|
||||
}
|
||||
@@ -279,12 +279,17 @@ async function openSummary() {
|
||||
|
||||
function closeSummaryModal() { document.getElementById('summary-modal').classList.add('hidden'); }
|
||||
|
||||
function openCreateRoom() { document.getElementById('create-modal').classList.remove('hidden'); }
|
||||
function openCreateRoom() {
|
||||
document.getElementById('create-modal').classList.remove('hidden');
|
||||
const isAdmin = localStorage.getItem('isAdmin') === 'true';
|
||||
document.getElementById('white-list').style.display = isAdmin ? '' : 'none';
|
||||
}
|
||||
function closeCreateModal() { document.getElementById('create-modal').classList.add('hidden'); }
|
||||
async function confirmCreateRoom() {
|
||||
const name = document.getElementById('new-room-name').value.trim();
|
||||
if (!name) return alert('\u8bf7\u8f93\u5165\u540d\u79f0');
|
||||
const whiteList = document.getElementById('white-list').value.trim();
|
||||
if (!name) return alert('请输入名称');
|
||||
const isAdmin = localStorage.getItem('isAdmin') === 'true';
|
||||
const whiteList = isAdmin ? document.getElementById('white-list').value.trim() : localStorage.getItem('currentUser');
|
||||
const token = Store.getToken();
|
||||
await fetch('/api/rooms', { method: 'POST', headers: { 'Authorization': 'Bearer ' + token, 'Content-Type': 'application/json' }, body: JSON.stringify({ name, whiteList }) });
|
||||
closeCreateModal();
|
||||
@@ -294,9 +299,11 @@ async function confirmCreateRoom() {
|
||||
function openManageRoom(roomId) {
|
||||
const room = Store.rooms.find(r => r.id === roomId);
|
||||
if (!room) return;
|
||||
document.getElementById('manage-title').textContent = '\u7f16\u8f91\u7fa4\u804a';
|
||||
const isAdmin = localStorage.getItem('isAdmin') === 'true';
|
||||
document.getElementById('manage-title').textContent = '编辑群聊';
|
||||
document.getElementById('manage-room-name').value = room.name;
|
||||
document.getElementById('manage-white-list').value = room.white_list || '';
|
||||
document.getElementById('manage-white-list').style.display = isAdmin ? '' : 'none';
|
||||
document.getElementById('manage-modal').dataset.roomId = roomId;
|
||||
document.getElementById('manage-modal').classList.remove('hidden');
|
||||
}
|
||||
@@ -306,15 +313,16 @@ function closeManageModal() { document.getElementById('manage-modal').classList.
|
||||
async function saveRoom() {
|
||||
const roomId = document.getElementById('manage-modal').dataset.roomId;
|
||||
const name = document.getElementById('manage-room-name').value.trim();
|
||||
const whiteList = document.getElementById('manage-white-list').value.trim();
|
||||
if (!name) return alert('\u8bf7\u8f93\u5165\u7fa4\u804a\u540d\u79f0');
|
||||
const isAdmin = localStorage.getItem('isAdmin') === 'true';
|
||||
const whiteList = isAdmin ? document.getElementById('manage-white-list').value.trim() : undefined;
|
||||
if (!name) return alert('请输入群聊名称');
|
||||
const token = Store.getToken();
|
||||
const res = await fetch('/api/rooms/' + roomId, {
|
||||
method: 'PUT', headers: { 'Authorization': 'Bearer ' + token, 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ name, whiteList })
|
||||
});
|
||||
if (res.ok) { closeManageModal(); loadRooms(); }
|
||||
else { const err = await res.json(); alert(err.error || '\u66f4\u65b0\u5931\u8d25'); }
|
||||
else { const err = await res.json(); alert(err.error || '更新失败'); }
|
||||
}
|
||||
|
||||
async function deleteRoom() {
|
||||
|
||||
Reference in New Issue
Block a user