默认进入采购清单页
This commit is contained in:
@@ -37,7 +37,11 @@ function buildSystemPrompt(username, isAdmin) {
|
|||||||
|
|
||||||
const purchaseRules = `
|
const purchaseRules = `
|
||||||
⚠️ 采购规则:
|
⚠️ 采购规则:
|
||||||
- 系统会在 Prompt 末尾提供"当前房间采购清单",你必须根据清单判断用户是要更新已有记录还是新建。
|
- 系统会在 Prompt 末尾提供“当前房间采购清单”,你必须根据清单和对话历史判断用户是要更新已有记录还是新建。
|
||||||
|
- **新建采购**(用户首次购买、重复购买、明确说“再买/又/新/另一批/这次”等)→ 必须返回 "is_new": true。
|
||||||
|
- **更新已有记录**(用户要求修改、更正、调整,如“改一下/更新/修改/更正/搞错了/不是XX是YY”)→ 返回 "is_new": false。
|
||||||
|
- **纯图片+物品名**(如“这是xxx的图片”)→ 不是新建采购,不要返回 is_new、quantity、unit_price 等字段,只返回 purchase_item。
|
||||||
|
- 如果无法明确判断新建/更新,优先按新建处理 → 返回 "is_new": true。
|
||||||
- 采购状态(status)只能从以下 4 个值中选择,绝不允许自创:
|
- 采购状态(status)只能从以下 4 个值中选择,绝不允许自创:
|
||||||
* "待付款" — 采购已记录,等待付款
|
* "待付款" — 采购已记录,等待付款
|
||||||
* "已付款" — 已完成支付,等待收货
|
* "已付款" — 已完成支付,等待收货
|
||||||
@@ -49,13 +53,8 @@ function buildSystemPrompt(username, isAdmin) {
|
|||||||
* "发票到/全搞定/完结" → "已完成"
|
* "发票到/全搞定/完结" → "已完成"
|
||||||
* 新建不指定 → "待付款"
|
* 新建不指定 → "待付款"
|
||||||
- 只有明确消费意图(购买/采购/下单/付款/买了/花了)才返回 action="purchase"
|
- 只有明确消费意图(购买/采购/下单/付款/买了/花了)才返回 action="purchase"
|
||||||
- 纯陈述(如"前天deep")→ action="ignore"
|
- 纯陈述 → action="ignore"
|
||||||
- 信息不完整 → action="ask" 追问
|
- 信息不完整 → action="ask" 追问
|
||||||
- 新建 vs 更新:
|
|
||||||
* "昨天/今天/又/再/新/另一批"等重复购买 → is_new: true(强制新建)
|
|
||||||
* "改一下/更新/修改/调整/更正/搞错了/不对/错了/不是XX是YY"等纠正意图 → 不提供 is_new(匹配已有更新),**购买清单中的名称是你的匹配线索**
|
|
||||||
* "纯图片+物品名"不是新建,不要返回 is_new
|
|
||||||
* 无法确定 → 默认 is_new: true
|
|
||||||
- amount 由系统自动计算(quantity × unit_price + freight),AI 无需提供
|
- amount 由系统自动计算(quantity × unit_price + freight),AI 无需提供
|
||||||
- 无法确定物品名 → action="ask" 追问`;
|
- 无法确定物品名 → action="ask" 追问`;
|
||||||
|
|
||||||
|
|||||||
@@ -56,8 +56,6 @@ body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-
|
|||||||
.send-btn { background: #3b82f6; border: none; color: #fff; width: 32px; height: 32px; border-radius: 50%; display: flex; align-items: center; justify-content: center; cursor: pointer; flex-shrink: 0; }
|
.send-btn { background: #3b82f6; border: none; color: #fff; width: 32px; height: 32px; border-radius: 50%; display: flex; align-items: center; justify-content: center; cursor: pointer; flex-shrink: 0; }
|
||||||
.send-btn svg { width: 18px; height: 18px; fill: #fff; }
|
.send-btn svg { width: 18px; height: 18px; fill: #fff; }
|
||||||
|
|
||||||
.purchase-panel-overlay { position: absolute; top: 0; left: 0; right: 0; bottom: 0; background: #fff; z-index: 10; display: flex; flex-direction: column; }
|
|
||||||
.purchase-panel-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; }
|
|
||||||
.purchase-panel-body { flex: 1; overflow-y: auto; padding: 12px; }
|
.purchase-panel-body { flex: 1; overflow-y: auto; padding: 12px; }
|
||||||
.purchase-month-group { margin-bottom: 20px; }
|
.purchase-month-group { margin-bottom: 20px; }
|
||||||
.purchase-month-header { background: #f5f5f5; font-size: 16px; font-weight: 600; padding: 10px 8px; border-radius: 4px; display: flex; justify-content: space-between; }
|
.purchase-month-header { background: #f5f5f5; font-size: 16px; font-weight: 600; padding: 10px 8px; border-radius: 4px; display: flex; justify-content: space-between; }
|
||||||
|
|||||||
@@ -54,17 +54,28 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 聊天窗口 -->
|
<!-- 采购清单页 -->
|
||||||
<div id="chat-page" class="chat-window hidden">
|
<div id="purchase-page" class="hidden" style="display:flex; flex-direction:column; height:100%;">
|
||||||
<div class="chat-header">
|
<div class="header">
|
||||||
<button class="icon-btn back-btn" onclick="showList()">
|
<button class="icon-btn back-btn" onclick="showList()">
|
||||||
<svg viewBox="0 0 24 24"><polyline points="15 18 9 12 15 6"></polyline></svg>
|
<svg viewBox="0 0 24 24"><polyline points="15 18 9 12 15 6"></polyline></svg>
|
||||||
</button>
|
</button>
|
||||||
<span class="chat-title" id="current-chat-name"></span>
|
<span class="chat-title">采购清单</span>
|
||||||
<button class="icon-btn" onclick="openPurchasePanel()" title="采购清单">
|
<button class="icon-btn" onclick="openChatFromPurchase()" title="聊天">
|
||||||
<svg viewBox="0 0 24 24"><line x1="8" y1="6" x2="21" y2="6"></line><line x1="8" y1="12" x2="21" y2="12"></line><line x1="8" y1="18" x2="21" y2="18"></line><line x1="3" y1="6" x2="3.01" y2="6"></line><line x1="3" y1="12" x2="3.01" y2="12"></line><line x1="3" y1="18" x2="3.01" y2="18"></line></svg>
|
<svg viewBox="0 0 24 24"><path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"></path></svg>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="purchase-panel-body" id="purchase-panel-body"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 聊天窗口 -->
|
||||||
|
<div id="chat-page" class="chat-window hidden">
|
||||||
|
<div class="chat-header">
|
||||||
|
<button class="icon-btn back-btn" onclick="backToPurchase()">
|
||||||
|
<svg viewBox="0 0 24 24"><polyline points="15 18 9 12 15 6"></polyline></svg>
|
||||||
|
</button>
|
||||||
|
<span class="chat-title" id="current-chat-name"></span>
|
||||||
|
</div>
|
||||||
<div class="messages" id="messages-container"></div>
|
<div class="messages" id="messages-container"></div>
|
||||||
<div class="input-area">
|
<div class="input-area">
|
||||||
<label class="file-upload-btn" title="上传图片">
|
<label class="file-upload-btn" title="上传图片">
|
||||||
@@ -76,15 +87,6 @@
|
|||||||
<svg viewBox="0 0 24 24"><line x1="22" y1="2" x2="11" y2="13"></line><polygon points="22 2 15 22 11 13 2 9 22 2"></polygon></svg>
|
<svg viewBox="0 0 24 24"><line x1="22" y1="2" x2="11" y2="13"></line><polygon points="22 2 15 22 11 13 2 9 22 2"></polygon></svg>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div id="purchase-panel-overlay" class="purchase-panel-overlay hidden">
|
|
||||||
<div class="purchase-panel-header">
|
|
||||||
<span>采购清单</span>
|
|
||||||
<button class="icon-btn close-btn" onclick="closePurchasePanel()">
|
|
||||||
<svg viewBox="0 0 24 24"><line x1="18" y1="6" x2="6" y2="18"></line><line x1="6" y1="6" x2="18" y2="18"></line></svg>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<div class="purchase-panel-body" id="purchase-panel-body"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -46,14 +46,6 @@ function formatMonth(dateStr) {
|
|||||||
return dateStr.substring(0,7);
|
return dateStr.substring(0,7);
|
||||||
}
|
}
|
||||||
|
|
||||||
function openPurchasePanel() {
|
|
||||||
document.getElementById('purchase-panel-body').innerHTML = '<p style="text-align:center;color:#888;">加载中...</p>';
|
|
||||||
document.getElementById('purchase-panel-overlay').classList.remove('hidden');
|
|
||||||
loadPurchases();
|
|
||||||
}
|
|
||||||
|
|
||||||
function closePurchasePanel() { document.getElementById('purchase-panel-overlay').classList.add('hidden'); }
|
|
||||||
|
|
||||||
async function openDetail(pid) {
|
async function openDetail(pid) {
|
||||||
const token = Store.getToken();
|
const token = Store.getToken();
|
||||||
const res = await fetch('/api/purchases/' + pid, { headers: { 'Authorization': `Bearer ${token}` } });
|
const res = await fetch('/api/purchases/' + pid, { headers: { 'Authorization': `Bearer ${token}` } });
|
||||||
@@ -89,7 +81,7 @@ async function openSummary() {
|
|||||||
if (summary.length === 0) html = '<p>暂无采购记录</p>';
|
if (summary.length === 0) html = '<p>暂无采购记录</p>';
|
||||||
else {
|
else {
|
||||||
summary.forEach(s => {
|
summary.forEach(s => {
|
||||||
html += `<div class="summary-card" onclick="event.stopPropagation(); closeDetailModal(); openChat('${s.room_id}', true)" 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(); closeDetailModal(); openPurchasePage('${s.room_id}')" style="margin:8px 0;"><h4>${escapeHtml(s.room_name)}</h4><p>${s.purchase_count} 条,合计 ¥${s.total_amount}</p></div>`;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
document.getElementById('detail-title').textContent = '采购汇总';
|
document.getElementById('detail-title').textContent = '采购汇总';
|
||||||
@@ -151,32 +143,14 @@ async function deleteRoom() {
|
|||||||
// 滑动手势
|
// 滑动手势
|
||||||
function initSwipeGestures() {
|
function initSwipeGestures() {
|
||||||
const chatPage = document.getElementById('chat-page');
|
const chatPage = document.getElementById('chat-page');
|
||||||
const purchaseOverlay = document.getElementById('purchase-panel-overlay');
|
let startX = 0;
|
||||||
let startX = 0, startY = 0;
|
|
||||||
|
|
||||||
function handleTouchStart(e) { startX = e.touches[0].clientX; startY = e.touches[0].clientY; }
|
chatPage.addEventListener('touchstart', (e) => { startX = e.touches[0].clientX; }, { passive: true });
|
||||||
|
chatPage.addEventListener('touchend', (e) => {
|
||||||
function handleTouchEnd(e, target) {
|
|
||||||
if (!startX) return;
|
if (!startX) return;
|
||||||
const endX = e.changedTouches[0].clientX;
|
const endX = e.changedTouches[0].clientX;
|
||||||
const endY = e.changedTouches[0].clientY;
|
|
||||||
const diffX = endX - startX;
|
const diffX = endX - startX;
|
||||||
const diffY = endY - startY;
|
if (diffX > 50) backToPurchase();
|
||||||
if (Math.abs(diffX) > Math.abs(diffY) && Math.abs(diffX) > 50) {
|
startX = 0;
|
||||||
if (target === chatPage && !purchaseOverlay.classList.contains('hidden')) {
|
}, { passive: true });
|
||||||
if (Math.abs(diffX) > 30) closePurchasePanel();
|
|
||||||
} else if (target === chatPage) {
|
|
||||||
if (diffX > 30) showList();
|
|
||||||
else if (diffX < -30) openPurchasePanel();
|
|
||||||
} else if (target === purchaseOverlay) {
|
|
||||||
if (Math.abs(diffX) > 30) closePurchasePanel();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
startX = 0; startY = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
chatPage.addEventListener('touchstart', handleTouchStart, { passive: true });
|
|
||||||
chatPage.addEventListener('touchend', (e) => handleTouchEnd(e, chatPage), { passive: true });
|
|
||||||
purchaseOverlay.addEventListener('touchstart', handleTouchStart, { passive: true });
|
|
||||||
purchaseOverlay.addEventListener('touchend', (e) => handleTouchEnd(e, purchaseOverlay), { passive: true });
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -80,13 +80,13 @@ function renderChatList() {
|
|||||||
}
|
}
|
||||||
return `
|
return `
|
||||||
<div class="chat-item" data-room-id="${room.id}">
|
<div class="chat-item" data-room-id="${room.id}">
|
||||||
<div class="avatar" onclick="openChat('${room.id}')">${escapeHtml(room.name.charAt(0))}</div>
|
<div class="avatar" onclick="openPurchasePage('${room.id}')">${escapeHtml(room.name.charAt(0))}</div>
|
||||||
<div class="chat-info" onclick="openChat('${room.id}')">
|
<div class="chat-info" onclick="openPurchasePage('${room.id}')">
|
||||||
<div class="chat-name">${escapeHtml(room.name)}</div>
|
<div class="chat-name">${escapeHtml(room.name)}</div>
|
||||||
<div class="last-msg">${escapeHtml(lastMsg)}</div>
|
<div class="last-msg">${escapeHtml(lastMsg)}</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="chat-right">
|
<div class="chat-right">
|
||||||
<div class="chat-time" onclick="openChat('${room.id}')">${lastTime}</div>
|
<div class="chat-time" onclick="openPurchasePage('${room.id}')">${lastTime}</div>
|
||||||
${isAdmin ? `<div class="room-actions">
|
${isAdmin ? `<div class="room-actions">
|
||||||
${whiteListHtml}
|
${whiteListHtml}
|
||||||
<button class="edit-room-btn" onclick="event.stopPropagation(); openManageRoom('${room.id}')" title="编辑群聊">
|
<button class="edit-room-btn" onclick="event.stopPropagation(); openManageRoom('${room.id}')" title="编辑群聊">
|
||||||
@@ -98,15 +98,35 @@ function renderChatList() {
|
|||||||
}).join('');
|
}).join('');
|
||||||
}
|
}
|
||||||
|
|
||||||
async function openChat(roomId, autoOpenPurchase = false) {
|
async function openPurchasePage(roomId) {
|
||||||
|
Store.currentRoom = roomId;
|
||||||
|
document.getElementById('list-page').classList.add('hidden');
|
||||||
|
document.getElementById('chat-page').classList.add('hidden');
|
||||||
|
document.getElementById('purchase-page').classList.remove('hidden');
|
||||||
|
document.getElementById('purchase-panel-body').innerHTML = '<p style="text-align:center;color:#888;">加载中...</p>';
|
||||||
|
loadPurchases();
|
||||||
|
}
|
||||||
|
|
||||||
|
async function openChatFromPurchase() {
|
||||||
|
if (!Store.currentRoom) return;
|
||||||
|
openChat(Store.currentRoom);
|
||||||
|
}
|
||||||
|
|
||||||
|
function backToPurchase() {
|
||||||
|
clearTempBubble();
|
||||||
|
document.getElementById('chat-page').classList.add('hidden');
|
||||||
|
document.getElementById('purchase-page').classList.remove('hidden');
|
||||||
|
loadPurchases();
|
||||||
|
}
|
||||||
|
|
||||||
|
async function openChat(roomId) {
|
||||||
Store.currentRoom = roomId;
|
Store.currentRoom = roomId;
|
||||||
clearTempBubble();
|
clearTempBubble();
|
||||||
document.getElementById('list-page').classList.add('hidden');
|
document.getElementById('list-page').classList.add('hidden');
|
||||||
|
document.getElementById('purchase-page').classList.add('hidden');
|
||||||
document.getElementById('chat-page').classList.remove('hidden');
|
document.getElementById('chat-page').classList.remove('hidden');
|
||||||
const room = Store.rooms.find(r => r.id === roomId);
|
const room = Store.rooms.find(r => r.id === roomId);
|
||||||
document.getElementById('current-chat-name').textContent = room?.name || '';
|
document.getElementById('current-chat-name').textContent = room?.name || '';
|
||||||
document.getElementById('purchase-panel-overlay').classList.add('hidden');
|
|
||||||
if (autoOpenPurchase) setTimeout(() => openPurchasePanel(), 200);
|
|
||||||
const token = Store.getToken();
|
const token = Store.getToken();
|
||||||
const res = await fetch('/api/rooms/' + roomId + '/messages', { headers: { 'Authorization': `Bearer ${token}` } });
|
const res = await fetch('/api/rooms/' + roomId + '/messages', { headers: { 'Authorization': `Bearer ${token}` } });
|
||||||
const msgs = await res.json();
|
const msgs = await res.json();
|
||||||
@@ -117,6 +137,7 @@ async function openChat(roomId, autoOpenPurchase = false) {
|
|||||||
function showList() {
|
function showList() {
|
||||||
clearTempBubble();
|
clearTempBubble();
|
||||||
document.getElementById('chat-page').classList.add('hidden');
|
document.getElementById('chat-page').classList.add('hidden');
|
||||||
|
document.getElementById('purchase-page').classList.add('hidden');
|
||||||
document.getElementById('list-page').classList.remove('hidden');
|
document.getElementById('list-page').classList.remove('hidden');
|
||||||
Store.currentRoom = null;
|
Store.currentRoom = null;
|
||||||
loadRooms();
|
loadRooms();
|
||||||
|
|||||||
Reference in New Issue
Block a user