826 lines
24 KiB
HTML
826 lines
24 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="zh-CN">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
|
||
<title>🍽️ 点餐助手 - 小妈厨房</title>
|
||
<style>
|
||
:root {
|
||
--primary: #007AFF;
|
||
--primary-light: rgba(0,122,255,0.1);
|
||
--green: #34C759;
|
||
--green-light: rgba(52,199,89,0.1);
|
||
--orange: #FF9500;
|
||
--orange-light: rgba(255,149,0,0.1);
|
||
--red: #FF3B30;
|
||
--red-light: rgba(255,59,48,0.1);
|
||
--bg: #F2F2F7;
|
||
--card: rgba(255,255,255,0.72);
|
||
--text: #1C1C1E;
|
||
--text2: #3C3C43;
|
||
--text3: #8E8E93;
|
||
--border: rgba(60,60,67,0.08);
|
||
--radius-sm: 8px;
|
||
--radius-md: 12px;
|
||
--radius-lg: 16px;
|
||
--shadow: 0 2px 12px rgba(0,0,0,0.06);
|
||
--font: -apple-system, BlinkMacSystemFont, 'SF Pro Display', 'Helvetica Neue', sans-serif;
|
||
}
|
||
|
||
@media (prefers-color-scheme: dark) {
|
||
:root {
|
||
--bg: #000000;
|
||
--card: rgba(44,44,46,0.72);
|
||
--text: #FFFFFF;
|
||
--text2: #EBEBF5;
|
||
--text3: #8E8E93;
|
||
--border: rgba(84,84,88,0.65);
|
||
--shadow: 0 2px 12px rgba(0,0,0,0.3);
|
||
}
|
||
}
|
||
|
||
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||
|
||
body {
|
||
font-family: var(--font);
|
||
background: var(--bg);
|
||
color: var(--text);
|
||
min-height: 100vh;
|
||
-webkit-font-smoothing: antialiased;
|
||
}
|
||
|
||
.container {
|
||
max-width: 600px;
|
||
margin: 0 auto;
|
||
padding: 16px;
|
||
}
|
||
|
||
.header {
|
||
text-align: center;
|
||
padding: 24px 0 16px;
|
||
}
|
||
|
||
.header h1 {
|
||
font-size: 24px;
|
||
font-weight: 700;
|
||
letter-spacing: -0.5px;
|
||
}
|
||
|
||
.header .subtitle {
|
||
font-size: 14px;
|
||
color: var(--text3);
|
||
margin-top: 4px;
|
||
}
|
||
|
||
.card {
|
||
background: var(--card);
|
||
backdrop-filter: blur(20px);
|
||
-webkit-backdrop-filter: blur(20px);
|
||
border-radius: var(--radius-lg);
|
||
border: 0.5px solid var(--border);
|
||
padding: 16px;
|
||
margin-bottom: 12px;
|
||
box-shadow: var(--shadow);
|
||
animation: fadeIn 0.3s ease-out;
|
||
}
|
||
|
||
.order-header {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
}
|
||
|
||
.order-header-left {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 8px;
|
||
}
|
||
|
||
.status-badge {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
gap: 4px;
|
||
padding: 4px 10px;
|
||
border-radius: var(--radius-sm);
|
||
font-size: 13px;
|
||
font-weight: 600;
|
||
}
|
||
|
||
.status-draft { background: var(--orange-light); color: var(--orange); }
|
||
.status-active { background: var(--green-light); color: var(--green); }
|
||
.status-completed { background: var(--primary-light); color: var(--primary); }
|
||
.status-cancelled { background: var(--red-light); color: var(--red); }
|
||
|
||
.order-no {
|
||
font-size: 12px;
|
||
color: var(--text3);
|
||
font-family: 'SF Mono', Menlo, monospace;
|
||
}
|
||
|
||
.order-time {
|
||
font-size: 12px;
|
||
color: var(--text3);
|
||
}
|
||
|
||
.info-grid {
|
||
display: grid;
|
||
grid-template-columns: 1fr 1fr;
|
||
gap: 8px;
|
||
margin-top: 12px;
|
||
}
|
||
|
||
.info-chip {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 6px;
|
||
padding: 8px 12px;
|
||
background: rgba(142,142,147,0.06);
|
||
border-radius: var(--radius-sm);
|
||
font-size: 14px;
|
||
}
|
||
|
||
.info-chip-icon {
|
||
font-size: 16px;
|
||
}
|
||
|
||
.info-chip-label {
|
||
font-size: 11px;
|
||
color: var(--text3);
|
||
}
|
||
|
||
.info-chip-value {
|
||
font-weight: 600;
|
||
font-size: 14px;
|
||
}
|
||
|
||
.item-list { list-style: none; }
|
||
|
||
.item-row {
|
||
display: flex;
|
||
align-items: center;
|
||
padding: 10px 0;
|
||
border-bottom: 0.5px solid var(--border);
|
||
}
|
||
|
||
.item-row:last-child { border-bottom: none; }
|
||
|
||
.item-source {
|
||
font-size: 11px;
|
||
padding: 2px 6px;
|
||
border-radius: 4px;
|
||
background: var(--primary-light);
|
||
color: var(--primary);
|
||
margin-right: 8px;
|
||
white-space: nowrap;
|
||
}
|
||
|
||
.item-name {
|
||
flex: 1;
|
||
font-size: 15px;
|
||
font-weight: 500;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
white-space: nowrap;
|
||
}
|
||
|
||
.item-qty {
|
||
font-size: 14px;
|
||
color: var(--text2);
|
||
margin: 0 8px;
|
||
min-width: 30px;
|
||
text-align: center;
|
||
}
|
||
|
||
.item-price {
|
||
font-size: 15px;
|
||
font-weight: 600;
|
||
color: var(--primary);
|
||
}
|
||
|
||
.item-detail {
|
||
font-size: 12px;
|
||
color: var(--text3);
|
||
margin-top: 2px;
|
||
padding-left: 0;
|
||
}
|
||
|
||
.total-section {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
padding-top: 12px;
|
||
border-top: 1px solid var(--border);
|
||
margin-top: 8px;
|
||
}
|
||
|
||
.total-label {
|
||
font-size: 15px;
|
||
color: var(--text2);
|
||
}
|
||
|
||
.total-amount {
|
||
font-size: 22px;
|
||
font-weight: 700;
|
||
color: var(--primary);
|
||
}
|
||
|
||
.note-section {
|
||
margin-top: 8px;
|
||
padding: 10px;
|
||
background: rgba(142,142,147,0.06);
|
||
border-radius: var(--radius-sm);
|
||
}
|
||
|
||
.note-label {
|
||
font-size: 12px;
|
||
color: var(--text3);
|
||
margin-bottom: 4px;
|
||
}
|
||
|
||
.note-text {
|
||
font-size: 14px;
|
||
color: var(--text2);
|
||
}
|
||
|
||
.empty-state {
|
||
text-align: center;
|
||
padding: 60px 20px;
|
||
}
|
||
|
||
.empty-icon {
|
||
font-size: 56px;
|
||
margin-bottom: 16px;
|
||
}
|
||
|
||
.empty-text {
|
||
font-size: 16px;
|
||
color: var(--text3);
|
||
}
|
||
|
||
.action-bar {
|
||
display: flex;
|
||
gap: 8px;
|
||
margin-top: 12px;
|
||
}
|
||
|
||
.action-btn {
|
||
flex: 1;
|
||
padding: 14px;
|
||
border: none;
|
||
border-radius: var(--radius-md);
|
||
font-size: 15px;
|
||
font-weight: 600;
|
||
cursor: pointer;
|
||
-webkit-tap-highlight-color: transparent;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
gap: 6px;
|
||
}
|
||
|
||
.action-btn:active { opacity: 0.8; }
|
||
|
||
.action-btn-primary {
|
||
background: var(--primary);
|
||
color: white;
|
||
}
|
||
|
||
.action-btn-secondary {
|
||
background: var(--primary-light);
|
||
color: var(--primary);
|
||
}
|
||
|
||
.action-btn-green {
|
||
background: var(--green);
|
||
color: white;
|
||
}
|
||
|
||
.meta-row {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
font-size: 12px;
|
||
color: var(--text3);
|
||
margin-top: 8px;
|
||
}
|
||
|
||
.debug-panel {
|
||
margin-top: 16px;
|
||
padding: 12px;
|
||
background: rgba(142,142,147,0.06);
|
||
border-radius: var(--radius-md);
|
||
font-family: 'SF Mono', Menlo, monospace;
|
||
font-size: 11px;
|
||
color: var(--text3);
|
||
word-break: break-all;
|
||
}
|
||
|
||
.debug-panel summary {
|
||
cursor: pointer;
|
||
font-weight: 600;
|
||
margin-bottom: 8px;
|
||
}
|
||
|
||
.sse-indicator {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
gap: 6px;
|
||
font-size: 12px;
|
||
color: var(--text3);
|
||
margin-top: 4px;
|
||
}
|
||
|
||
.sse-dot {
|
||
width: 8px;
|
||
height: 8px;
|
||
border-radius: 50%;
|
||
background: var(--green);
|
||
animation: pulse 2s infinite;
|
||
}
|
||
|
||
.sse-dot.disconnected { background: var(--red); animation: none; }
|
||
|
||
.screenshot-area {
|
||
background: var(--bg);
|
||
padding: 16px;
|
||
border-radius: var(--radius-lg);
|
||
margin-bottom: 12px;
|
||
}
|
||
|
||
.screenshot-area .card {
|
||
margin-bottom: 8px;
|
||
}
|
||
|
||
.screenshot-area .card:last-child {
|
||
margin-bottom: 0;
|
||
}
|
||
|
||
.divider {
|
||
height: 0.5px;
|
||
background: var(--border);
|
||
margin: 8px 0;
|
||
}
|
||
|
||
.desc-text {
|
||
font-size: 13px;
|
||
color: var(--text3);
|
||
line-height: 1.5;
|
||
margin-top: 4px;
|
||
}
|
||
|
||
.people-badge {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
gap: 4px;
|
||
padding: 4px 10px;
|
||
border-radius: var(--radius-sm);
|
||
background: var(--green-light);
|
||
color: var(--green);
|
||
font-size: 13px;
|
||
font-weight: 600;
|
||
}
|
||
|
||
@keyframes pulse {
|
||
0%, 100% { opacity: 1; }
|
||
50% { opacity: 0.4; }
|
||
}
|
||
|
||
@keyframes fadeIn {
|
||
from { opacity: 0; transform: translateY(8px); }
|
||
to { opacity: 1; transform: translateY(0); }
|
||
}
|
||
|
||
.toast {
|
||
position: fixed;
|
||
bottom: 80px;
|
||
left: 50%;
|
||
transform: translateX(-50%) translateY(20px);
|
||
background: rgba(0,0,0,0.75);
|
||
color: white;
|
||
padding: 10px 20px;
|
||
border-radius: 20px;
|
||
font-size: 14px;
|
||
opacity: 0;
|
||
transition: all 0.3s ease;
|
||
z-index: 9999;
|
||
pointer-events: none;
|
||
}
|
||
|
||
.toast.show {
|
||
opacity: 1;
|
||
transform: translateX(-50%) translateY(0);
|
||
}
|
||
|
||
@media print {
|
||
.action-bar, .debug-panel, .sse-indicator, .refresh-btn { display: none !important; }
|
||
body { background: white; }
|
||
.card { box-shadow: none; border: 1px solid #eee; }
|
||
}
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<div class="container">
|
||
<div class="header">
|
||
<h1>🍽️ 点餐助手</h1>
|
||
<div class="subtitle" id="subtitle">加载中...</div>
|
||
<div class="sse-indicator">
|
||
<span class="sse-dot" id="sseDot"></span>
|
||
<span id="sseStatus">连接中...</span>
|
||
</div>
|
||
</div>
|
||
|
||
<div id="screenshotArea" class="screenshot-area">
|
||
<div id="content">
|
||
<div class="empty-state">
|
||
<div class="empty-icon">⏳</div>
|
||
<div class="empty-text">正在加载点单信息...</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="action-bar">
|
||
<button class="action-btn action-btn-secondary" onclick="loadOrder()">🔄 刷新</button>
|
||
<button class="action-btn action-btn-primary" onclick="screenshotOrder()">📸 截图保存</button>
|
||
<button class="action-btn action-btn-green" onclick="shareOrder()">📤 分享</button>
|
||
</div>
|
||
|
||
<details class="debug-panel">
|
||
<summary>🔧 调试信息</summary>
|
||
<div id="debug-info">等待数据...</div>
|
||
</details>
|
||
</div>
|
||
|
||
<div class="toast" id="toast"></div>
|
||
|
||
<script>
|
||
var API_BASE = 'https://eat.wktyl.com/api/kitchen/kitchen.php';
|
||
var SSE_URL = 'https://eat.wktyl.com/api/kitchen/kitchen_sse.php';
|
||
var eventSource = null;
|
||
var pollTimer = null;
|
||
var currentOrder = null;
|
||
|
||
function showToast(msg) {
|
||
var t = document.getElementById('toast');
|
||
t.textContent = msg;
|
||
t.classList.add('show');
|
||
setTimeout(function() { t.classList.remove('show'); }, 2000);
|
||
}
|
||
|
||
function getQueryParam(name) {
|
||
var url = new URL(window.location.href);
|
||
return url.searchParams.get(name);
|
||
}
|
||
|
||
function formatTime(isoStr) {
|
||
if (!isoStr) return '';
|
||
try {
|
||
var d = new Date(isoStr);
|
||
var y = d.getFullYear();
|
||
var m = String(d.getMonth()+1).padStart(2,'0');
|
||
var day = String(d.getDate()).padStart(2,'0');
|
||
var h = String(d.getHours()).padStart(2,'0');
|
||
var min = String(d.getMinutes()).padStart(2,'0');
|
||
return y + '-' + m + '-' + day + ' ' + h + ':' + min;
|
||
} catch(e) { return isoStr; }
|
||
}
|
||
|
||
function formatRelativeTime(isoStr) {
|
||
if (!isoStr) return '';
|
||
try {
|
||
var d = new Date(isoStr);
|
||
var now = new Date();
|
||
var diff = now - d;
|
||
var mins = Math.floor(diff / 60000);
|
||
if (mins < 1) return '刚刚';
|
||
if (mins < 60) return mins + '分钟前';
|
||
var hours = Math.floor(mins / 60);
|
||
if (hours < 24) return hours + '小时前';
|
||
var days = Math.floor(hours / 24);
|
||
if (days === 1) return '昨天';
|
||
if (days < 7) return days + '天前';
|
||
return (d.getMonth()+1) + '月' + d.getDate() + '日';
|
||
} catch(e) { return isoStr; }
|
||
}
|
||
|
||
function renderOrder(order) {
|
||
currentOrder = order;
|
||
var statusMap = {
|
||
0: { label: '草稿', icon: '📝', cls: 'status-draft' },
|
||
1: { label: '进行中', icon: '🟢', cls: 'status-active' },
|
||
2: { label: '已完成', icon: '✅', cls: 'status-completed' },
|
||
3: { label: '已取消', icon: '❌', cls: 'status-cancelled' },
|
||
};
|
||
var typeMap = { 0: '🧑 用户点餐', 1: '🏪 商家推单' };
|
||
var sourceMap = {
|
||
0: { label: '浏览', icon: '📖' },
|
||
1: { label: '搜索', icon: '🔍' },
|
||
2: { label: '手动', icon: '✏️' },
|
||
3: { label: '推荐', icon: '⭐' },
|
||
};
|
||
|
||
var st = statusMap[order.status] || statusMap[0];
|
||
var items = order.items || [];
|
||
var total = items.reduce(function(s, i) { return s + (i.price || 0) * i.quantity; }, 0);
|
||
var totalQty = items.reduce(function(s, i) { return s + i.quantity; }, 0);
|
||
|
||
document.getElementById('subtitle').textContent =
|
||
typeMap[order.type] + ' · ' + formatRelativeTime(order.createdAt);
|
||
|
||
var html = '';
|
||
|
||
// 订单状态卡片
|
||
html += '<div class="card">';
|
||
html += '<div class="order-header">';
|
||
html += '<div class="order-header-left">';
|
||
html += '<span class="status-badge ' + st.cls + '">' + st.icon + ' ' + st.label + '</span>';
|
||
html += '<span class="order-no">' + escapeHtml(order.orderNo) + '</span>';
|
||
html += '</div>';
|
||
html += '<span class="order-time">' + formatTime(order.createdAt) + '</span>';
|
||
html += '</div>';
|
||
|
||
// 信息网格:桌号 + 人数
|
||
html += '<div class="info-grid">';
|
||
if (order.tableNo) {
|
||
html += '<div class="info-chip">';
|
||
html += '<span class="info-chip-icon">🪑</span>';
|
||
html += '<div><div class="info-chip-label">桌号</div>';
|
||
html += '<div class="info-chip-value">' + escapeHtml(order.tableNo) + '</div></div>';
|
||
html += '</div>';
|
||
}
|
||
if (order.peopleCount) {
|
||
html += '<div class="info-chip">';
|
||
html += '<span class="info-chip-icon">👥</span>';
|
||
html += '<div><div class="info-chip-label">用餐人数</div>';
|
||
html += '<div class="info-chip-value">' + order.peopleCount + ' 人</div></div>';
|
||
html += '</div>';
|
||
}
|
||
if (!order.tableNo && !order.peopleCount) {
|
||
html += '<div class="info-chip">';
|
||
html += '<span class="info-chip-icon">📋</span>';
|
||
html += '<div><div class="info-chip-label">记录</div>';
|
||
html += '<div class="info-chip-value">#' + (order.recordCount || 0) + '</div></div>';
|
||
html += '</div>';
|
||
}
|
||
if (order.tableNo && !order.peopleCount) {
|
||
html += '<div class="info-chip">';
|
||
html += '<span class="info-chip-icon">📋</span>';
|
||
html += '<div><div class="info-chip-label">记录</div>';
|
||
html += '<div class="info-chip-value">#' + (order.recordCount || 0) + '</div></div>';
|
||
html += '</div>';
|
||
}
|
||
html += '</div>';
|
||
html += '</div>';
|
||
|
||
// 菜品列表
|
||
if (items.length === 0) {
|
||
html += '<div class="card"><div class="empty-state">';
|
||
html += '<div class="empty-icon">📭</div>';
|
||
html += '<div class="empty-text">暂无菜品</div>';
|
||
html += '</div></div>';
|
||
} else {
|
||
html += '<div class="card">';
|
||
html += '<div style="font-size:15px;font-weight:600;margin-bottom:8px;">🥘 菜品明细</div>';
|
||
html += '<ul class="item-list">';
|
||
items.forEach(function(item, idx) {
|
||
var src = sourceMap[item.source] || sourceMap[2];
|
||
html += '<li class="item-row">';
|
||
html += '<span class="item-source">' + src.icon + ' ' + src.label + '</span>';
|
||
html += '<span class="item-name">' + escapeHtml(item.name) + '</span>';
|
||
html += '<span class="item-qty">×' + item.quantity + '</span>';
|
||
html += '<span class="item-price">' + (item.price != null ? '¥' + (item.price * item.quantity).toFixed(1) : '-') + '</span>';
|
||
html += '</li>';
|
||
if (item.ingredients || item.note) {
|
||
html += '<li class="item-detail">';
|
||
if (item.ingredients) html += '🥘 ' + escapeHtml(item.ingredients) + ' ';
|
||
if (item.note) html += '💬 ' + escapeHtml(item.note);
|
||
html += '</li>';
|
||
}
|
||
});
|
||
html += '</ul>';
|
||
html += '<div class="total-section">';
|
||
html += '<span class="total-label">共 ' + totalQty + ' 道菜' + (order.peopleCount ? ' · ' + order.peopleCount + '人用餐' : '') + '</span>';
|
||
html += '<span class="total-amount">¥' + total.toFixed(1) + '</span>';
|
||
html += '</div>';
|
||
html += '</div>';
|
||
}
|
||
|
||
// 备注
|
||
if (order.note) {
|
||
html += '<div class="card"><div class="note-section">';
|
||
html += '<div class="note-label">💬 备注</div>';
|
||
html += '<div class="note-text">' + escapeHtml(order.note) + '</div>';
|
||
html += '</div></div>';
|
||
}
|
||
|
||
// 底部描述
|
||
html += '<div class="card">';
|
||
html += '<div style="font-size:13px;color:var(--text3);line-height:1.6;">';
|
||
html += '📱 由「小妈厨房」点餐助手生成<br>';
|
||
html += '🕐 下单时间: ' + formatTime(order.createdAt);
|
||
if (order.updatedAt && order.updatedAt !== order.createdAt) {
|
||
html += '<br>🔄 更新时间: ' + formatTime(order.updatedAt);
|
||
}
|
||
html += '</div>';
|
||
html += '</div>';
|
||
|
||
document.getElementById('content').innerHTML = html;
|
||
document.getElementById('debug-info').textContent = JSON.stringify(order, null, 2);
|
||
}
|
||
|
||
function escapeHtml(str) {
|
||
if (!str) return '';
|
||
return str.replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>').replace(/"/g,'"');
|
||
}
|
||
|
||
function screenshotOrder() {
|
||
showToast('📸 请使用系统截图功能保存');
|
||
// 尝试使用 Web Share API
|
||
if (navigator.share && currentOrder) {
|
||
var items = currentOrder.items || [];
|
||
var total = items.reduce(function(s, i) { return s + (i.price || 0) * i.quantity; }, 0);
|
||
var text = '🍽️ 点餐助手 - ' + (currentOrder.orderNo || '') + '\n';
|
||
if (currentOrder.tableNo) text += '🪑 桌号: ' + currentOrder.tableNo + '\n';
|
||
if (currentOrder.peopleCount) text += '👥 人数: ' + currentOrder.peopleCount + '\n';
|
||
text += '\n';
|
||
items.forEach(function(item) {
|
||
text += '· ' + item.name + ' ×' + item.quantity;
|
||
if (item.price) text += ' ¥' + (item.price * item.quantity).toFixed(1);
|
||
text += '\n';
|
||
});
|
||
text += '\n合计: ¥' + total.toFixed(1) + '\n';
|
||
if (currentOrder.note) text += '备注: ' + currentOrder.note + '\n';
|
||
|
||
navigator.share({
|
||
title: '点餐助手 - ' + currentOrder.orderNo,
|
||
text: text,
|
||
}).catch(function() {});
|
||
return;
|
||
}
|
||
// Fallback: 尝试打印
|
||
window.print();
|
||
}
|
||
|
||
function shareOrder() {
|
||
if (navigator.share && currentOrder) {
|
||
var items = currentOrder.items || [];
|
||
var total = items.reduce(function(s, i) { return s + (i.price || 0) * i.quantity; }, 0);
|
||
var text = '🍽️ 点餐助手 - ' + (currentOrder.orderNo || '') + '\n';
|
||
if (currentOrder.tableNo) text += '🪑 桌号: ' + currentOrder.tableNo + '\n';
|
||
if (currentOrder.peopleCount) text += '👥 人数: ' + currentOrder.peopleCount + '\n';
|
||
text += '\n';
|
||
items.forEach(function(item) {
|
||
text += '· ' + item.name + ' ×' + item.quantity;
|
||
if (item.price) text += ' ¥' + (item.price * item.quantity).toFixed(1);
|
||
text += '\n';
|
||
});
|
||
text += '\n合计: ¥' + total.toFixed(1) + '\n';
|
||
if (currentOrder.note) text += '备注: ' + currentOrder.note + '\n';
|
||
|
||
navigator.share({
|
||
title: '点餐助手 - ' + currentOrder.orderNo,
|
||
text: text,
|
||
url: window.location.href,
|
||
}).catch(function() {});
|
||
} else {
|
||
// Fallback: 复制链接
|
||
navigator.clipboard.writeText(window.location.href).then(function() {
|
||
showToast('✅ 链接已复制到剪贴板');
|
||
}).catch(function() {
|
||
showToast('❌ 复制失败,请手动复制链接');
|
||
});
|
||
}
|
||
}
|
||
|
||
async function loadOrder() {
|
||
var orderId = getQueryParam('id');
|
||
if (!orderId) {
|
||
document.getElementById('content').innerHTML =
|
||
'<div class="card"><div class="empty-state">' +
|
||
'<div class="empty-icon">🔍</div>' +
|
||
'<div class="empty-text">缺少订单ID参数<br><small>请从App端扫码打开</small></div>' +
|
||
'</div></div>';
|
||
document.getElementById('debug-info').textContent = 'URL: ' + window.location.href + '\n缺少 id 参数';
|
||
return;
|
||
}
|
||
|
||
document.getElementById('subtitle').textContent = '加载中...';
|
||
|
||
try {
|
||
var resp = await fetch(API_BASE + '?act=get&id=' + encodeURIComponent(orderId));
|
||
if (resp.ok) {
|
||
var result = await resp.json();
|
||
if (result.code === 200 && result.data) {
|
||
renderOrder(result.data);
|
||
} else {
|
||
throw new Error(result.message || '订单不存在');
|
||
}
|
||
} else {
|
||
throw new Error('HTTP ' + resp.status);
|
||
}
|
||
} catch(e) {
|
||
document.getElementById('debug-info').textContent = 'API请求失败: ' + e.message + '\n尝试本地参数...';
|
||
tryLocalFallback(orderId);
|
||
}
|
||
}
|
||
|
||
function tryLocalFallback(orderId) {
|
||
var dataStr = getQueryParam('data');
|
||
if (dataStr) {
|
||
try {
|
||
var order = JSON.parse(decodeURIComponent(dataStr));
|
||
renderOrder(order);
|
||
return;
|
||
} catch(e2) {
|
||
document.getElementById('debug-info').textContent += '\n本地参数解析失败: ' + e2.message;
|
||
}
|
||
}
|
||
|
||
var demoOrder = {
|
||
id: orderId,
|
||
orderNo: 'OD_DEMO_' + Date.now().toString().slice(-6),
|
||
type: 0,
|
||
status: 1,
|
||
items: [
|
||
{ id: '1', name: '红烧肉', source: 0, quantity: 1, price: 38, ingredients: '五花肉、酱油、冰糖', note: null },
|
||
{ id: '2', name: '番茄炒蛋', source: 2, quantity: 2, price: 18, ingredients: null, note: '少盐' },
|
||
{ id: '3', name: '紫菜蛋花汤', source: 3, quantity: 1, price: 12, ingredients: null, note: null },
|
||
],
|
||
note: '三人用餐',
|
||
createdAt: new Date().toISOString(),
|
||
updatedAt: new Date().toISOString(),
|
||
createdBy: 'demo',
|
||
tableNo: 'A3',
|
||
peopleCount: 3,
|
||
recordCount: 1,
|
||
};
|
||
renderOrder(demoOrder);
|
||
document.getElementById('debug-info').textContent += '\n使用Demo数据展示';
|
||
}
|
||
|
||
function initSSE() {
|
||
var orderId = getQueryParam('id');
|
||
if (!orderId) return;
|
||
|
||
try {
|
||
eventSource = new EventSource(SSE_URL + '?order_id=' + encodeURIComponent(orderId));
|
||
|
||
eventSource.addEventListener('connected', function(e) {
|
||
updateSSEStatus(true, 'SSE已连接');
|
||
});
|
||
|
||
eventSource.addEventListener('order_update', function(e) {
|
||
try {
|
||
var order = JSON.parse(e.data);
|
||
renderOrder(order);
|
||
} catch(err) {
|
||
console.error('SSE数据解析失败:', err);
|
||
}
|
||
});
|
||
|
||
eventSource.addEventListener('order_deleted', function(e) {
|
||
document.getElementById('content').innerHTML =
|
||
'<div class="card"><div class="empty-state">' +
|
||
'<div class="empty-icon">🗑️</div>' +
|
||
'<div class="empty-text">订单已被删除</div>' +
|
||
'</div></div>';
|
||
});
|
||
|
||
eventSource.addEventListener('heartbeat', function(e) {
|
||
updateSSEStatus(true, 'SSE连接中');
|
||
});
|
||
|
||
eventSource.addEventListener('close', function(e) {
|
||
updateSSEStatus(false, 'SSE超时,重连中...');
|
||
setTimeout(initSSE, 3000);
|
||
});
|
||
|
||
eventSource.onerror = function() {
|
||
updateSSEStatus(false, 'SSE断开');
|
||
eventSource.close();
|
||
eventSource = null;
|
||
setTimeout(initSSE, 5000);
|
||
};
|
||
} catch(e) {
|
||
console.error('SSE初始化失败:', e);
|
||
updateSSEStatus(false, 'SSE不可用');
|
||
}
|
||
}
|
||
|
||
function updateSSEStatus(connected, text) {
|
||
var dot = document.getElementById('sseDot');
|
||
var status = document.getElementById('sseStatus');
|
||
if (dot) dot.className = 'sse-dot' + (connected ? '' : ' disconnected');
|
||
if (status) status.textContent = text;
|
||
}
|
||
|
||
loadOrder();
|
||
initSSE();
|
||
|
||
pollTimer = setInterval(function() {
|
||
if (!eventSource || eventSource.readyState === EventSource.CLOSED) {
|
||
loadOrder();
|
||
}
|
||
}, 30000);
|
||
</script>
|
||
</body>
|
||
</html>
|