backup codes
This commit is contained in:
@@ -31,7 +31,7 @@ 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 !== '无票' ? '· ' + item.invoice_type : ''}</span>
|
||||
<span>${item.quantity} ${item.invoice_type !== '无票' ? '\u00b7 ' + item.invoice_type : ''}</span>
|
||||
<span class="status-badge ${item.status==='已完成'?'done':''}">${item.status}</span>
|
||||
</div>
|
||||
</div>`;
|
||||
@@ -44,7 +44,7 @@ function renderPurchasePanel(items) {
|
||||
function formatMonth(dateStr) {
|
||||
const clean = dateStr.replace(/-/g, '/');
|
||||
const parts = clean.split('/');
|
||||
if (parts.length >= 2) return parts[0] + '年' + parseInt(parts[1], 10) + '月';
|
||||
if (parts.length >= 2) return parts[0] + '\u5e74' + parseInt(parts[1], 10) + '\u6708';
|
||||
return dateStr.substring(0,7);
|
||||
}
|
||||
|
||||
@@ -60,10 +60,11 @@ async function openDetail(pid) {
|
||||
document.getElementById('edit-quantity').value = p.quantity || 0;
|
||||
document.getElementById('edit-unit-price').value = p.unit_price || 0;
|
||||
document.getElementById('edit-freight').value = p.freight || 0;
|
||||
updateEditAmount();
|
||||
document.getElementById('edit-payment-method').value = p.payment_method || '未指定';
|
||||
document.getElementById('edit-invoice-type').value = p.invoice_type || '无票';
|
||||
document.getElementById('edit-status').value = p.status || '待付款';
|
||||
updateAmountSummary();
|
||||
document.getElementById('edit-payment-method').value = p.payment_method || '\u672a\u6307\u5b9a';
|
||||
document.getElementById('edit-invoice-type').value = p.invoice_type || '\u65e0\u7968';
|
||||
document.getElementById('edit-status').value = p.status || '\u5f85\u4ed8\u6b3e';
|
||||
updateStatusSummary();
|
||||
document.getElementById('edit-applicant').value = p.applicant || '';
|
||||
document.getElementById('edit-time').value = p.created_at || '';
|
||||
document.getElementById('edit-remarks').value = p.remarks || '';
|
||||
@@ -89,6 +90,9 @@ async function openDetail(pid) {
|
||||
delBtn.style.marginLeft = '0';
|
||||
}
|
||||
|
||||
// 折叠所有编辑组
|
||||
document.querySelectorAll('.edit-group-detail').forEach(el => el.classList.add('hidden'));
|
||||
|
||||
document.getElementById('detail-modal').classList.remove('hidden');
|
||||
}
|
||||
|
||||
@@ -108,11 +112,32 @@ function renderEditAttachments(attachments) {
|
||||
).join('');
|
||||
}
|
||||
|
||||
function updateEditAmount() {
|
||||
function updateAmountSummary() {
|
||||
const q = parseFloat(document.getElementById('edit-quantity').value) || 0;
|
||||
const up = parseFloat(document.getElementById('edit-unit-price').value) || 0;
|
||||
const f = parseFloat(document.getElementById('edit-freight').value) || 0;
|
||||
document.getElementById('edit-amount').textContent = (q * up + f).toFixed(2);
|
||||
const total = (q * up + f).toFixed(2);
|
||||
document.getElementById('amount-summary').textContent = total + ', \u6570\u91cf' + q + ', \u5355\u4ef7' + up + ', \u90ae\u8d39' + f;
|
||||
}
|
||||
|
||||
function updateStatusSummary() {
|
||||
const pm = document.getElementById('edit-payment-method').value;
|
||||
const iv = document.getElementById('edit-invoice-type').value;
|
||||
const st = document.getElementById('edit-status').value;
|
||||
document.getElementById('status-summary').textContent = st + ', ' + pm + ', ' + iv;
|
||||
}
|
||||
|
||||
function toggleEditGroup(groupId) {
|
||||
const detail = document.getElementById(groupId);
|
||||
const summary = detail.previousElementSibling;
|
||||
const isOpen = !detail.classList.contains('hidden');
|
||||
if (isOpen) {
|
||||
detail.classList.add('hidden');
|
||||
summary.classList.remove('hidden');
|
||||
} else {
|
||||
detail.classList.remove('hidden');
|
||||
summary.classList.add('hidden');
|
||||
}
|
||||
}
|
||||
|
||||
async function savePurchase() {
|
||||
@@ -140,13 +165,13 @@ async function savePurchase() {
|
||||
closeDetailModal();
|
||||
loadPurchases();
|
||||
} else {
|
||||
alert('保存失败:' + (result.error || '未知错误'));
|
||||
alert('\u4fdd\u5b58\u5931\u8d25\uff1a' + (result.error || '\u672a\u77e5\u9519\u8bef'));
|
||||
}
|
||||
}
|
||||
|
||||
async function deletePurchaseDetail() {
|
||||
if (!editPid) return;
|
||||
if (!confirm('确定要删除此采购记录吗?')) return;
|
||||
if (!confirm('\u786e\u5b9a\u8981\u5220\u9664\u6b64\u91c7\u8d2d\u8bb0\u5f55\u5417\uff1f')) return;
|
||||
const token = Store.getToken();
|
||||
const res = await fetch('/api/purchases/' + editPid, {
|
||||
method: 'DELETE',
|
||||
@@ -157,14 +182,14 @@ async function deletePurchaseDetail() {
|
||||
loadPurchases();
|
||||
} else {
|
||||
const err = await res.json();
|
||||
alert('删除失败:' + (err.error || '未知错误'));
|
||||
alert('\u5220\u9664\u5931\u8d25\uff1a' + (err.error || '\u672a\u77e5\u9519\u8bef'));
|
||||
}
|
||||
}
|
||||
|
||||
async function deleteAttachment(event, encodedPath) {
|
||||
event.stopPropagation();
|
||||
const filePath = decodeURIComponent(encodedPath);
|
||||
if (!confirm('确定删除此附件吗?')) return;
|
||||
if (!confirm('\u786e\u5b9a\u5220\u9664\u6b64\u9644\u4ef6\u5417\uff1f')) return;
|
||||
const token = Store.getToken();
|
||||
const res = await fetch('/api/purchases/' + editPid + '/attachments', {
|
||||
method: 'DELETE',
|
||||
@@ -172,7 +197,6 @@ async function deleteAttachment(event, encodedPath) {
|
||||
body: JSON.stringify({ file_path: filePath })
|
||||
});
|
||||
if (res.ok) {
|
||||
// 重新加载详情
|
||||
openDetail(editPid);
|
||||
}
|
||||
}
|
||||
@@ -199,7 +223,7 @@ async function handleDetailFileUpload(event) {
|
||||
});
|
||||
openDetail(editPid);
|
||||
}
|
||||
} catch(e) { alert('上传失败,请重试'); }
|
||||
} catch(e) { alert('\u4e0a\u4f20\u5931\u8d25\uff0c\u8bf7\u91cd\u8bd5'); }
|
||||
}
|
||||
|
||||
function closeDetailModal() {
|
||||
@@ -222,14 +246,13 @@ async function handlePurchaseFileSelect(event) {
|
||||
const data = await res.json();
|
||||
if (data.path) Store.pendingUploads.push(data.path);
|
||||
}
|
||||
} catch(e) { alert('图片上传失败'); }
|
||||
} catch(e) { alert('\u56fe\u7247\u4e0a\u4f20\u5931\u8d25'); }
|
||||
}
|
||||
|
||||
function sendFromPurchase() {
|
||||
const input = document.getElementById('purchase-msg-input');
|
||||
const text = input.value.trim();
|
||||
if (!text && Store.pendingUploads.length === 0) return;
|
||||
// 记录文本,跳转到聊天页面后发送
|
||||
Store.pendingPurchaseText = text;
|
||||
input.value = '';
|
||||
openChatFromPurchase();
|
||||
@@ -242,13 +265,13 @@ async function openSummary() {
|
||||
const res = await fetch('/api/summary', { headers: { 'Authorization': 'Bearer ' + token } });
|
||||
const summary = await res.json();
|
||||
let html = '';
|
||||
if (summary.length === 0) html = '<p>暂无采购记录</p>';
|
||||
if (summary.length === 0) html = '<p>\u6682\u65e0\u91c7\u8d2d\u8bb0\u5f55</p>';
|
||||
else {
|
||||
summary.forEach(s => {
|
||||
html += '<div class="summary-card" onclick="event.stopPropagation(); closeSummaryModal(); openPurchasePage(\'' + s.room_id + '\')" style="margin:8px 0;"><h4>' + escapeHtml(s.room_name) + '</h4><p>' + s.purchase_count + ' 条,合计 ' + s.total_amount + '</p></div>';
|
||||
html += '<div class="summary-card" onclick="event.stopPropagation(); closeSummaryModal(); openPurchasePage(\'' + s.room_id + '\')" style="margin:8px 0;"><h4>' + escapeHtml(s.room_name) + '</h4><p>' + s.purchase_count + ' \u6761\uff0c\u5408\u8ba1 ' + s.total_amount + '</p></div>';
|
||||
});
|
||||
}
|
||||
document.getElementById('summary-title').textContent = '采购汇总';
|
||||
document.getElementById('summary-title').textContent = '\u91c7\u8d2d\u6c47\u603b';
|
||||
document.getElementById('summary-content').innerHTML = html;
|
||||
document.getElementById('summary-modal').classList.remove('hidden');
|
||||
}
|
||||
@@ -259,7 +282,7 @@ function openCreateRoom() { document.getElementById('create-modal').classList.re
|
||||
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('请输入名称');
|
||||
if (!name) return alert('\u8bf7\u8f93\u5165\u540d\u79f0');
|
||||
const whiteList = document.getElementById('white-list').value.trim();
|
||||
const token = Store.getToken();
|
||||
await fetch('/api/rooms', { method: 'POST', headers: { 'Authorization': 'Bearer ' + token, 'Content-Type': 'application/json' }, body: JSON.stringify({ name, whiteList }) });
|
||||
@@ -270,7 +293,7 @@ async function confirmCreateRoom() {
|
||||
function openManageRoom(roomId) {
|
||||
const room = Store.rooms.find(r => r.id === roomId);
|
||||
if (!room) return;
|
||||
document.getElementById('manage-title').textContent = '编辑群聊';
|
||||
document.getElementById('manage-title').textContent = '\u7f16\u8f91\u7fa4\u804a';
|
||||
document.getElementById('manage-room-name').value = room.name;
|
||||
document.getElementById('manage-white-list').value = room.white_list || '';
|
||||
document.getElementById('manage-modal').dataset.roomId = roomId;
|
||||
@@ -283,18 +306,18 @@ 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('请输入群聊名称');
|
||||
if (!name) return alert('\u8bf7\u8f93\u5165\u7fa4\u804a\u540d\u79f0');
|
||||
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 || '更新失败'); }
|
||||
else { const err = await res.json(); alert(err.error || '\u66f4\u65b0\u5931\u8d25'); }
|
||||
}
|
||||
|
||||
async function deleteRoom() {
|
||||
if (!confirm('确定要删除该群聊吗?所有消息和采购数据将被永久删除。')) return;
|
||||
if (!confirm('\u786e\u5b9a\u8981\u5220\u9664\u8be5\u7fa4\u804a\u5417\uff1f\u6240\u6709\u6d88\u606f\u548c\u91c7\u8d2d\u6570\u636e\u5c06\u88ab\u6c38\u4e45\u5220\u9664\u3002')) return;
|
||||
const roomId = document.getElementById('manage-modal').dataset.roomId;
|
||||
const token = Store.getToken();
|
||||
const res = await fetch('/api/rooms/' + roomId, { method: 'DELETE', headers: { 'Authorization': 'Bearer ' + token } });
|
||||
@@ -302,7 +325,7 @@ async function deleteRoom() {
|
||||
closeManageModal();
|
||||
loadRooms();
|
||||
if (Store.currentRoom === roomId) showList();
|
||||
} else { const err = await res.json(); alert(err.error || '删除失败'); }
|
||||
} else { const err = await res.json(); alert(err.error || '\u5220\u9664\u5931\u8d25'); }
|
||||
}
|
||||
|
||||
// ============ 滑动手势 ============
|
||||
@@ -312,7 +335,6 @@ function initSwipeGestures() {
|
||||
const chatPage = document.getElementById('chat-page');
|
||||
let startX = 0;
|
||||
|
||||
// 采购页:右滑回列表,左滑进聊天
|
||||
purchasePage.addEventListener('touchstart', (e) => { startX = e.touches[0].clientX; }, { passive: true });
|
||||
purchasePage.addEventListener('touchend', (e) => {
|
||||
if (!startX) return;
|
||||
@@ -324,7 +346,6 @@ function initSwipeGestures() {
|
||||
startX = 0;
|
||||
}, { passive: true });
|
||||
|
||||
// 聊天页:左右滑回采购页
|
||||
chatPage.addEventListener('touchstart', (e) => { startX = e.touches[0].clientX; }, { passive: true });
|
||||
chatPage.addEventListener('touchend', (e) => {
|
||||
if (!startX) return;
|
||||
|
||||
Reference in New Issue
Block a user