Files
LabelChange-server/batch_query.html
2026-06-01 16:30:29 +08:00

4871 lines
241 KiB
HTML
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>批量查询换单数据</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&family=Noto+Sans+SC:wght@300;400;500;600;700&display=swap" rel="stylesheet">
<script src="https://cdn.jsdelivr.net/npm/jquery@3.7.1/dist/jquery.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/xlsx@0.18.5/dist/xlsx.full.min.js"></script>
<style>
:root {
--primary: #2563eb;
--primary-dark: #1d4ed8;
--primary-light: #3b82f6;
--secondary: #64748b;
--success: #10b981;
--warning: #f59e0b;
--danger: #ef4444;
--bg-primary: #f8fafc;
--bg-secondary: #ffffff;
--text-primary: #1e293b;
--text-secondary: #475569;
--text-muted: #94a3b8;
--border: #e2e8f0;
--shadow-sm: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
--shadow-md: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
--shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
--radius-md: 8px;
--radius-lg: 12px;
--transition-fast: 150ms ease;
--transition-normal: 250ms ease;
}
* {
font-family: 'Inter', 'Noto Sans SC', -apple-system, BlinkMacSystemFont, sans-serif;
}
body {
background: linear-gradient(135deg, #f8fafc 0%, #f1f5f9 100%);
min-height: 100vh;
color: var(--text-primary);
}
.container-main {
max-width: 1600px;
padding: 32px 24px;
margin: 0 auto;
}
.page-header {
text-align: center;
margin-bottom: 40px;
padding: 32px 0;
background: linear-gradient(135deg, var(--primary) 0%, var(--primary-dark) 100%);
border-radius: var(--radius-lg);
box-shadow: var(--shadow-lg);
position: relative;
overflow: hidden;
}
.page-header::before {
content: '';
position: absolute;
top: -50%;
left: -50%;
width: 200%;
height: 200%;
background: radial-gradient(circle, rgba(255,255,255,0.1) 0%, transparent 70%);
animation: pulse 8s ease-in-out infinite;
}
@keyframes pulse {
0%, 100% { transform: scale(1); opacity: 0.5; }
50% { transform: scale(1.1); opacity: 0.8; }
}
.page-header h1 {
color: white;
font-size: 2rem;
font-weight: 600;
margin: 0;
position: relative;
z-index: 1;
letter-spacing: -0.02em;
}
.page-header p {
color: rgba(255,255,255,0.85);
margin: 8px 0 0 0;
font-size: 0.95rem;
position: relative;
z-index: 1;
}
.environment-selector {
background: var(--bg-secondary);
padding: 20px 24px;
border-radius: var(--radius-lg);
box-shadow: var(--shadow-md);
margin-bottom: 24px;
display: flex;
align-items: center;
gap: 16px;
border: 1px solid var(--border);
}
.environment-selector label {
font-weight: 500;
color: var(--text-primary);
margin: 0;
}
.environment-selector select {
flex: 1;
max-width: 400px;
padding: 10px 16px;
border: 2px solid var(--border);
border-radius: var(--radius-md);
background: white;
font-size: 0.95rem;
transition: all var(--transition-fast);
}
.environment-selector select:focus {
outline: none;
border-color: var(--primary);
box-shadow: 0 0 0 3px rgba(37, 99, 235, 0.1);
}
.nav-tabs-custom {
border-bottom: 2px solid var(--border);
background: var(--bg-secondary);
padding: 0 20px;
border-radius: var(--radius-lg) var(--radius-lg) 0 0;
box-shadow: var(--shadow-md);
display: flex;
flex-wrap: nowrap;
overflow-x: auto;
gap: 4px;
}
.nav-tabs-custom::-webkit-scrollbar {
height: 4px;
}
.nav-tabs-custom::-webkit-scrollbar-track {
background: var(--bg-primary);
}
.nav-tabs-custom::-webkit-scrollbar-thumb {
background: var(--border);
border-radius: 2px;
}
.nav-tabs-custom .nav-item {
margin: 0;
flex-shrink: 0;
}
.nav-tabs-custom .nav-link {
border: none;
padding: 16px 20px;
color: var(--text-secondary);
font-weight: 500;
font-size: 0.92rem;
position: relative;
background: transparent;
border-radius: 0;
transition: all var(--transition-fast);
}
.nav-tabs-custom .nav-link:hover {
color: var(--primary);
background: var(--bg-primary);
}
.nav-tabs-custom .nav-link.active {
color: var(--primary);
background: transparent;
}
.nav-tabs-custom .nav-link.active::after {
content: '';
position: absolute;
bottom: -2px;
left: 0;
right: 0;
height: 3px;
background: var(--primary);
border-radius: 3px 3px 0 0;
animation: slideIn 0.3s ease;
}
@keyframes slideIn {
from { transform: scaleX(0); }
to { transform: scaleX(1); }
}
.tab-content-custom {
background: var(--bg-secondary);
border-radius: 0 0 var(--radius-lg) var(--radius-lg);
box-shadow: var(--shadow-md);
padding: 28px;
border: 1px solid var(--border);
border-top: none;
}
.card-custom {
border: none;
border-radius: var(--radius-lg);
box-shadow: var(--shadow-sm);
overflow: hidden;
transition: box-shadow var(--transition-normal);
}
.card-custom:hover {
box-shadow: var(--shadow-md);
}
.card-header-custom {
background: linear-gradient(135deg, #f8fafc 0%, #f1f5f9 100%);
border-bottom: 1px solid var(--border);
padding: 20px 24px;
}
.card-header-custom h2 {
font-size: 1.25rem;
font-weight: 600;
color: var(--text-primary);
margin: 0;
display: flex;
align-items: center;
gap: 10px;
}
.card-header-custom h2::before {
content: '';
width: 4px;
height: 24px;
background: var(--primary);
border-radius: 2px;
}
.card-body-custom {
padding: 28px;
}
.form-label-custom {
font-weight: 500;
color: var(--text-primary);
font-size: 0.9rem;
margin-bottom: 8px;
}
.form-control-custom,
.form-select-custom {
width: 100%;
padding: 10px 14px;
border: 2px solid var(--border);
border-radius: var(--radius-md);
font-size: 0.95rem;
transition: all var(--transition-fast);
background: white;
}
.form-control-custom:focus,
.form-select-custom:focus {
outline: none;
border-color: var(--primary);
box-shadow: 0 0 0 3px rgba(37, 99, 235, 0.1);
}
.btn-custom-primary {
background: linear-gradient(135deg, var(--primary) 0%, var(--primary-dark) 100%);
border: none;
color: white;
padding: 10px 24px;
border-radius: var(--radius-md);
font-weight: 500;
font-size: 0.95rem;
transition: all var(--transition-fast);
box-shadow: var(--shadow-sm);
}
.btn-custom-primary:hover {
transform: translateY(-1px);
box-shadow: var(--shadow-md);
background: linear-gradient(135deg, var(--primary-dark) 0%, #1e40af 100%);
}
.btn-custom-primary:active {
transform: translateY(0);
}
.btn-custom-secondary {
background: white;
border: 2px solid var(--border);
color: var(--text-secondary);
padding: 10px 24px;
border-radius: var(--radius-md);
font-weight: 500;
font-size: 0.95rem;
transition: all var(--transition-fast);
}
.btn-custom-secondary:hover {
border-color: var(--secondary);
color: var(--text-primary);
background: var(--bg-primary);
}
.btn-custom-success {
background: linear-gradient(135deg, var(--success) 0%, #059669 100%);
border: none;
color: white;
padding: 10px 20px;
border-radius: var(--radius-md);
font-weight: 500;
font-size: 0.9rem;
transition: all var(--transition-fast);
box-shadow: var(--shadow-sm);
}
.btn-custom-success:hover {
transform: translateY(-1px);
box-shadow: var(--shadow-md);
}
.btn-custom-danger {
background: linear-gradient(135deg, var(--danger) 0%, #dc2626 100%);
border: none;
color: white;
padding: 10px 20px;
border-radius: var(--radius-md);
font-weight: 500;
font-size: 0.9rem;
transition: all var(--transition-fast);
box-shadow: var(--shadow-sm);
}
.btn-custom-danger:hover {
transform: translateY(-1px);
box-shadow: var(--shadow-md);
}
.table-custom {
width: 100%;
border-collapse: separate;
border-spacing: 0;
margin-bottom: 0;
}
.table-custom thead {
background: linear-gradient(135deg, #f8fafc 0%, #f1f5f9 100%);
}
.table-custom thead th {
padding: 14px 16px;
font-weight: 600;
color: var(--text-primary);
font-size: 0.85rem;
text-transform: uppercase;
letter-spacing: 0.05em;
border-bottom: 2px solid var(--border);
white-space: nowrap;
}
.table-custom thead th:first-child {
border-radius: var(--radius-md) 0 0 0;
}
.table-custom thead th:last-child {
border-radius: 0 var(--radius-md) 0 0;
}
.table-custom tbody tr {
transition: all var(--transition-fast);
background: white;
}
.table-custom tbody tr:hover {
background: var(--bg-primary);
}
.table-custom tbody td {
padding: 14px 16px;
color: var(--text-secondary);
font-size: 0.9rem;
border-bottom: 1px solid var(--border);
vertical-align: middle;
}
.table-custom tbody tr:last-child td:first-child {
border-radius: 0 0 0 var(--radius-md);
}
.table-custom tbody tr:last-child td:last-child {
border-radius: 0 0 var(--radius-md) 0;
}
.table-custom tbody tr:last-child td {
border-bottom: none;
}
.status-badge {
display: inline-block;
padding: 4px 12px;
border-radius: 20px;
font-size: 0.8rem;
font-weight: 500;
}
.status-badge-success {
background: rgba(16, 185, 129, 0.1);
color: var(--success);
}
.status-badge-warning {
background: rgba(245, 158, 11, 0.1);
color: var(--warning);
}
.status-badge-danger {
background: rgba(239, 68, 68, 0.1);
color: var(--danger);
}
.status-badge-info {
background: rgba(37, 99, 235, 0.1);
color: var(--primary);
}
.pagination-custom {
display: flex;
justify-content: center;
gap: 8px;
margin-top: 24px;
}
.pagination-custom .page-link {
border: 2px solid var(--border);
color: var(--text-secondary);
border-radius: var(--radius-md);
padding: 8px 14px;
font-weight: 500;
transition: all var(--transition-fast);
background: white;
}
.pagination-custom .page-link:hover {
border-color: var(--primary);
color: var(--primary);
background: var(--bg-primary);
}
.pagination-custom .page-item.active .page-link {
background: var(--primary);
border-color: var(--primary);
color: white;
}
.result-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
padding-bottom: 16px;
border-bottom: 1px solid var(--border);
}
.result-header h3 {
font-size: 1rem;
font-weight: 600;
color: var(--text-primary);
margin: 0;
display: flex;
align-items: center;
gap: 8px;
}
.pagination-info {
color: var(--text-muted);
font-size: 0.9rem;
}
.loading-container {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 60px 20px;
}
.spinner-custom {
width: 48px;
height: 48px;
border: 4px solid var(--border);
border-top-color: var(--primary);
border-radius: 50%;
animation: spin 0.8s linear infinite;
}
@keyframes spin {
to { transform: rotate(360deg); }
}
.loading-text {
margin-top: 16px;
color: var(--text-muted);
font-size: 0.95rem;
}
.empty-state {
text-align: center;
padding: 60px 20px;
color: var(--text-muted);
}
.empty-state-icon {
font-size: 4rem;
margin-bottom: 16px;
opacity: 0.5;
}
.empty-state-text {
font-size: 1rem;
margin: 0;
}
.info-box {
background: linear-gradient(135deg, rgba(37, 99, 235, 0.05) 0%, rgba(37, 99, 235, 0.1) 100%);
border: 1px solid rgba(37, 99, 235, 0.2);
border-radius: var(--radius-lg);
padding: 20px 24px;
margin-bottom: 24px;
}
.info-box-title {
font-weight: 600;
color: var(--primary);
margin-bottom: 12px;
font-size: 0.95rem;
}
.info-box-content {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 16px;
}
.info-box-item {
display: flex;
flex-direction: column;
}
.info-box-item label {
color: var(--text-muted);
font-size: 0.85rem;
margin-bottom: 4px;
}
.info-box-item span {
color: var(--text-primary);
font-weight: 600;
font-size: 1.25rem;
}
.notification-bubble {
position: fixed;
top: 24px;
right: 24px;
z-index: 9999;
transform: translateX(120%);
transition: transform 0.4s cubic-bezier(0.68, -0.55, 0.265, 1.55);
}
.notification-bubble.show {
transform: translateX(0);
}
.notification-content {
background: white;
padding: 16px 24px;
border-radius: var(--radius-lg);
box-shadow: var(--shadow-lg);
display: flex;
align-items: center;
gap: 12px;
min-width: 300px;
border: 1px solid var(--border);
}
.notification-icon {
width: 40px;
height: 40px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-size: 1.25rem;
flex-shrink: 0;
}
.notification-icon.success {
background: rgba(16, 185, 129, 0.1);
color: var(--success);
}
.notification-icon.error {
background: rgba(239, 68, 68, 0.1);
color: var(--danger);
}
.notification-icon.info {
background: rgba(37, 99, 235, 0.1);
color: var(--primary);
}
.notification-text {
flex: 1;
}
.notification-text-title {
font-weight: 600;
color: var(--text-primary);
font-size: 0.95rem;
margin-bottom: 2px;
}
.notification-text-message {
color: var(--text-secondary);
font-size: 0.85rem;
}
.progress-bar-custom {
background: var(--border);
border-radius: 10px;
height: 8px;
overflow: hidden;
}
.progress-bar-custom-fill {
height: 100%;
background: linear-gradient(90deg, var(--primary) 0%, var(--primary-light) 100%);
border-radius: 10px;
transition: width 0.3s ease;
}
.form-section {
margin-bottom: 24px;
}
.form-section-title {
font-weight: 600;
color: var(--text-primary);
font-size: 0.95rem;
margin-bottom: 12px;
padding-bottom: 8px;
border-bottom: 1px solid var(--border);
}
.table-responsive-custom {
overflow-x: auto;
border-radius: var(--radius-lg);
border: 1px solid var(--border);
box-shadow: var(--shadow-sm);
}
.btn-sm-custom {
padding: 6px 12px;
font-size: 0.85rem;
}
.cursor-pointer {
cursor: pointer;
}
.cursor-pointer:hover {
text-decoration: underline;
}
.upload-area {
border: 2px dashed var(--border);
border-radius: var(--radius-lg);
padding: 32px;
text-align: center;
transition: all var(--transition-fast);
background: var(--bg-primary);
}
.upload-area:hover {
border-color: var(--primary);
background: rgba(37, 99, 235, 0.05);
}
.upload-area.dragover {
border-color: var(--primary);
background: rgba(37, 99, 235, 0.1);
}
@media (max-width: 768px) {
.container-main {
padding: 16px;
}
.page-header {
padding: 24px 16px;
}
.page-header h1 {
font-size: 1.5rem;
}
.environment-selector {
flex-direction: column;
align-items: stretch;
}
.environment-selector select {
max-width: 100%;
}
.tab-content-custom {
padding: 16px;
}
.card-body-custom {
padding: 16px;
}
.result-header {
flex-direction: column;
align-items: flex-start;
gap: 12px;
}
.table-custom thead th,
.table-custom tbody td {
padding: 10px 12px;
font-size: 0.85rem;
}
}
</style>
</head>
<body>
<div class="container-main">
<div class="page-header">
<h1>📦 批量查询换单数据</h1>
<p>高效管理和查询您的换单业务数据</p>
</div>
<div class="environment-selector">
<label for="environment">🌍 环境选择</label>
<select id="environment" class="form-select-custom">
<option value="local">本地环境 (http://localhost:5002)</option>
<option value="test" selected>测试环境 (http://172.232.21.79:5002)</option>
<option value="production">生产环境 (https://lr.tooexp.com)</option>
</select>
</div>
<ul class="nav nav-tabs nav-tabs-custom" id="queryTabs" role="tablist">
<li class="nav-item" role="presentation">
<button class="nav-link active" id="label-replace-tab" data-bs-toggle="tab" data-bs-target="#label-replace" type="button" role="tab" aria-controls="label-replace" aria-selected="true">📋 订单查询</button>
</li>
<li class="nav-item" role="presentation">
<button class="nav-link" id="label-scan-tab" data-bs-toggle="tab" data-bs-target="#label-scan" type="button" role="tab" aria-controls="label-scan" aria-selected="false">🔍 换单扫描记录</button>
</li>
<li class="nav-item" role="presentation">
<button class="nav-link" id="arrival-handover-tab" data-bs-toggle="tab" data-bs-target="#arrival-handover" type="button" role="tab" aria-controls="arrival-handover" aria-selected="false">📥 到货交接单</button>
</li>
<li class="nav-item" role="presentation">
<button class="nav-link" id="shipping-handover-tab" data-bs-toggle="tab" data-bs-target="#shipping-handover" type="button" role="tab" aria-controls="shipping-handover" aria-selected="false">📤 出货交接单</button>
</li>
<li class="nav-item" role="presentation">
<button class="nav-link" id="bag-tag-tab" data-bs-toggle="tab" data-bs-target="#bag-tag" type="button" role="tab" aria-controls="bag-tag" aria-selected="false">🎒 集包数据查询</button>
</li>
<li class="nav-item" role="presentation">
<button class="nav-link" id="order-log-tab" data-bs-toggle="tab" data-bs-target="#order-log" type="button" role="tab" aria-controls="order-log" aria-selected="false">📝 订单日志查询</button>
</li>
<li class="nav-item" role="presentation">
<button class="nav-link" id="data-dashboard-tab" data-bs-toggle="tab" data-bs-target="#data-dashboard" type="button" role="tab" aria-controls="data-dashboard" aria-selected="false">📊 数据看板</button>
</li>
<li class="nav-item" role="presentation">
<button class="nav-link" id="ops-monitor-tab" data-bs-toggle="tab" data-bs-target="#ops-monitor" type="button" role="tab" aria-controls="ops-monitor" aria-selected="false">📈 运维监控</button>
</li>
<li class="nav-item" role="presentation">
<button class="nav-link" id="order-import-tab" data-bs-toggle="tab" data-bs-target="#order-import" type="button" role="tab" aria-controls="order-import" aria-selected="false">📥 订单导入</button>
</li>
</ul>
<div class="tab-content tab-content-custom" id="queryTabsContent">
<div class="tab-pane fade show active" id="label-replace" role="tabpanel" aria-labelledby="label-replace-tab">
<div class="card card-custom">
<div class="card-header card-header-custom">
<h2>订单查询</h2>
</div>
<div class="card-body card-body-custom">
<form id="labelReplaceForm" class="mb-4">
<div class="row g-3">
<div class="col-md-3">
<label for="billOfLadingNumber" class="form-label-custom">提单号</label>
<input type="text" class="form-control-custom" id="billOfLadingNumber" placeholder="请输入提单号">
</div>
<div class="col-md-3">
<label for="masterPackageNumber" class="form-label-custom">大包号</label>
<input type="text" class="form-control-custom" id="masterPackageNumber" placeholder="请输入大包号">
</div>
<div class="col-md-3">
<label for="referenceNumber" class="form-label-custom">参考号</label>
<input type="text" class="form-control-custom" id="referenceNumber" placeholder="请输入参考号">
</div>
<div class="col-md-3">
<label for="neutralWaybillNumber" class="form-label-custom">中性面单单号</label>
<textarea class="form-control-custom" id="neutralWaybillNumber" rows="3" placeholder="请输入中性面单单号,多个单号请换行分隔"></textarea>
</div>
<div class="col-md-3">
<label for="finalMileTrackingNumber" class="form-label-custom">尾程跟踪单号</label>
<input type="text" class="form-control-custom" id="finalMileTrackingNumber" placeholder="请输入尾程跟踪单号">
</div>
<div class="col-md-3">
<label for="replaceStatus" class="form-label-custom">换单状态</label>
<select class="form-select-custom" id="replaceStatus">
<option value="">全部</option>
<option value="Y">正常换单</option>
<option value="N">冻结换单</option>
</select>
</div>
<div class="col-md-3">
<label for="customerId" class="form-label-custom">客户</label>
<select class="form-select-custom" id="customerId">
<option value="">全部</option>
</select>
</div>
<div class="col-md-3">
<label for="startCreatedAt" class="form-label-custom">创建时间开始</label>
<input type="datetime-local" class="form-control-custom" id="startCreatedAt">
</div>
<div class="col-md-3">
<label for="endCreatedAt" class="form-label-custom">创建时间结束</label>
<input type="datetime-local" class="form-control-custom" id="endCreatedAt">
</div>
<div class="col-md-3">
<label for="startReplacedAt" class="form-label-custom">换单完成时间开始</label>
<input type="datetime-local" class="form-control-custom" id="startReplacedAt">
</div>
<div class="col-md-3">
<label for="endReplacedAt" class="form-label-custom">换单完成时间结束</label>
<input type="datetime-local" class="form-control-custom" id="endReplacedAt">
</div>
<div class="col-md-3">
<label for="sortBy" class="form-label-custom">排序字段</label>
<select class="form-select-custom" id="sortBy">
<option value="CreatedAt">创建时间</option>
<option value="Id">ID</option>
<option value="NeutralWaybillNumber">中性面单单号</option>
</select>
</div>
<div class="col-md-3">
<label for="sortOrder" class="form-label-custom">排序顺序</label>
<select class="form-select-custom" id="sortOrder">
<option value="desc">降序</option>
<option value="asc">升序</option>
</select>
</div>
<div class="col-md-3">
<label for="pageSize" class="form-label-custom">每页记录数</label>
<select class="form-select-custom" id="pageSize">
<option value="500">500</option>
<option value="1000">1000</option>
<option value="2000">2000</option>
<option value="custom">自定义</option>
</select>
</div>
<div class="col-md-3" id="customPageSizeContainer" style="display: none;">
<label for="customPageSize" class="form-label-custom">自定义记录数</label>
<input type="number" class="form-control-custom" id="customPageSize" placeholder="请输入记录数" min="1" max="10000">
</div>
<div class="col-md-12 d-flex align-items-end gap-2 pt-2">
<button type="submit" class="btn-custom-primary">🔍 查询</button>
<button type="button" class="btn-custom-secondary" id="resetLabelReplaceForm">🔄 重置</button>
</div>
</div>
</form>
<div id="labelReplaceResult">
<div class="result-header">
<h3>📋 查询结果</h3>
<div class="d-flex align-items-center gap-3">
<button type="button" class="btn-custom-success" id="exportExcelBtn">📥 导出Excel</button>
<div id="labelReplacePaginationInfo" class="pagination-info"></div>
</div>
</div>
<div class="table-responsive-custom">
<table class="table-custom">
<thead>
<tr>
<th>ID</th>
<th>客户简称</th>
<th>提单号</th>
<th>大包号</th>
<th>中性面单单号</th>
<th>尾程跟踪单号</th>
<th>袋牌号</th>
<th>有无面单</th>
<th>换单状态</th>
<th>换单完成时间</th>
<th>标签推送时间</th>
<th>创建时间</th>
</tr>
</thead>
<tbody id="labelReplaceTableBody">
<tr>
<td colspan="12" class="text-center empty-state">
<div class="empty-state-icon">🔍</div>
<div class="empty-state-text">请点击查询按钮获取数据</div>
</td>
</tr>
</tbody>
</table>
</div>
<nav id="labelReplacePagination" class="pagination-custom"></nav>
</div>
</div>
</div>
</div>
<div class="tab-pane fade" id="label-scan" role="tabpanel" aria-labelledby="label-scan-tab">
<div class="card card-custom">
<div class="card-header card-header-custom">
<h2>换单扫描记录查询</h2>
</div>
<div class="card-body card-body-custom">
<form id="labelScanForm" class="mb-4">
<div class="row g-3">
<div class="col-md-3">
<label for="scanCustomerId" class="form-label-custom">客户</label>
<select class="form-select-custom" id="scanCustomerId">
<option value="">全部</option>
</select>
</div>
<div class="col-md-3">
<label for="scanNeutralWaybillNumber" class="form-label-custom">中性面单单号</label>
<input type="text" class="form-control-custom" id="scanNeutralWaybillNumber" placeholder="请输入中性面单单号">
</div>
<div class="col-md-3">
<label for="scanFinalMileTrackingNumber" class="form-label-custom">尾程跟踪单号</label>
<input type="text" class="form-control-custom" id="scanFinalMileTrackingNumber" placeholder="请输入尾程跟踪单号">
</div>
<div class="col-md-3">
<label for="scanResult" class="form-label-custom">扫描结果</label>
<select class="form-select-custom" id="scanResult">
<option value="">全部</option>
<option value="0">已返回面单</option>
<option value="1">无面单数据</option>
<option value="2">无下单数据</option>
<option value="3">订单被冻结</option>
<option value="4">订单已销毁</option>
<option value="5">其他</option>
</select>
</div>
<div class="col-md-3">
<label for="scanStartCreatedAt" class="form-label-custom">创建时间开始</label>
<input type="datetime-local" class="form-control-custom" id="scanStartCreatedAt">
</div>
<div class="col-md-3">
<label for="scanEndCreatedAt" class="form-label-custom">创建时间结束</label>
<input type="datetime-local" class="form-control-custom" id="scanEndCreatedAt">
</div>
<div class="col-md-3">
<label for="scanSortBy" class="form-label-custom">排序字段</label>
<select class="form-select-custom" id="scanSortBy">
<option value="CreatedAt">创建时间</option>
<option value="Id">ID</option>
<option value="NeutralWaybillNumber">中性面单单号</option>
</select>
</div>
<div class="col-md-3">
<label for="scanSortOrder" class="form-label-custom">排序顺序</label>
<select class="form-select-custom" id="scanSortOrder">
<option value="desc">降序</option>
<option value="asc">升序</option>
</select>
</div>
<div class="col-md-3">
<label for="scanPageSize" class="form-label-custom">每页记录数</label>
<select class="form-select-custom" id="scanPageSize">
<option value="500">500</option>
<option value="1000">1000</option>
<option value="2000">2000</option>
<option value="custom">自定义</option>
</select>
</div>
<div class="col-md-3" id="customScanPageSizeContainer" style="display: none;">
<label for="customScanPageSize" class="form-label-custom">自定义记录数</label>
<input type="number" class="form-control-custom" id="customScanPageSize" placeholder="请输入记录数" min="1" max="10000">
</div>
<div class="col-md-12 d-flex align-items-end gap-2 pt-2">
<button type="submit" class="btn-custom-primary">🔍 查询</button>
<button type="button" class="btn-custom-secondary" id="resetLabelScanForm">🔄 重置</button>
</div>
</div>
</form>
<div id="labelScanResult">
<div class="result-header">
<h3>📋 查询结果</h3>
<div class="d-flex align-items-center gap-3">
<button type="button" class="btn-custom-success" id="exportScanExcelBtn">📥 导出Excel</button>
<div id="labelScanPaginationInfo" class="pagination-info"></div>
</div>
</div>
<div class="table-responsive-custom">
<table class="table-custom">
<thead>
<tr>
<th>ID</th>
<th>客户简称</th>
<th>中性面单单号</th>
<th>尾程跟踪单号</th>
<th>扫描结果</th>
<th>描述</th>
<th>创建人</th>
<th>创建时间</th>
</tr>
</thead>
<tbody id="labelScanTableBody">
<tr>
<td colspan="8" class="text-center empty-state">
<div class="empty-state-icon">🔍</div>
<div class="empty-state-text">请点击查询按钮获取数据</div>
</td>
</tr>
</tbody>
</table>
</div>
<nav id="labelScanPagination" class="pagination-custom"></nav>
</div>
</div>
</div>
</div>
<div class="tab-pane fade" id="arrival-handover" role="tabpanel" aria-labelledby="arrival-handover-tab">
<div class="card card-custom">
<div class="card-header card-header-custom">
<h2>到货交接单</h2>
</div>
<div class="card-body card-body-custom">
<div class="mb-4 p-4 border rounded-lg bg-light">
<div class="row g-3">
<div class="col-md-8">
<div class="form-section-title"> 添加到货交接单</div>
<form id="addArrivalHandoverForm" class="mb-4">
<div class="row g-3">
<div class="col-md-4">
<label for="addHandoverNumber" class="form-label-custom">交接单号(头程物流跟踪号,多个请换行)</label>
<textarea class="form-control-custom" id="addHandoverNumber" rows="3" placeholder="请输入交接单号,多个单号请换行分隔" required></textarea>
</div>
<div class="col-md-4">
<label for="addLogisticsProviderArrivalTime" class="form-label-custom">头程物流商送达时间</label>
<input type="datetime-local" class="form-control-custom" id="addLogisticsProviderArrivalTime" required>
</div>
<div class="col-md-4">
<label for="addReceiptTime" class="form-label-custom">收货时间</label>
<input type="datetime-local" class="form-control-custom" id="addReceiptTime" required>
</div>
<div class="col-md-6">
<label for="addPOD" class="form-label-custom">POD图片上传</label>
<input type="file" class="form-control-custom" id="addPOD" multiple accept="image/*">
<small class="form-text text-muted">支持上传多个图片文件</small>
</div>
<div class="col-md-6">
<label class="form-label-custom">已上传图片</label>
<div id="uploadedArrivalPODs" class="upload-area" style="min-height: 100px;">
<p class="text-muted mb-0">点击或粘贴图片到此处</p>
</div>
</div>
<div class="col-md-6">
<label for="addRemarks" class="form-label-custom">备注</label>
<textarea class="form-control-custom" id="addRemarks" rows="2" placeholder="请输入备注"></textarea>
</div>
<div class="col-md-6">
<label for="timezone" class="form-label-custom">时区选择</label>
<select class="form-select-custom" id="timezone">
<option value="America/New_York" selected>美国纽约 (东部时间 UTC-5)</option>
<option value="America/Chicago">美国芝加哥 (中部时间 UTC-6)</option>
<option value="America/Denver">美国丹佛 (山地时间 UTC-7)</option>
<option value="America/Los_Angeles">美国洛杉矶 (太平洋时间 UTC-8)</option>
<option value="America/Anchorage">美国安克雷奇 (阿拉斯加时间 UTC-9)</option>
<option value="America/Honolulu">美国檀香山 (夏威夷时间 UTC-10)</option>
<option value="Asia/Shanghai">中国标准时间 (UTC+8)</option>
<option value="UTC">协调世界时 (UTC)</option>
</select>
</div>
<div class="col-md-12 d-flex justify-content-end pt-2">
<button type="submit" class="btn-custom-primary">✅ 添加</button>
</div>
</div>
</form>
</div>
<div class="col-md-4">
<div class="form-section-title">🔍 查询条件</div>
<form id="arrivalHandoverForm" class="mb-4">
<div class="row g-3">
<div class="col-md-12">
<label for="handoverNumber" class="form-label-custom">交接单号(多个请换行)</label>
<textarea class="form-control-custom" id="handoverNumber" rows="3" placeholder="请输入交接单号,多个单号请换行分隔"></textarea>
</div>
<div class="col-md-6">
<label for="arrivalSortBy" class="form-label-custom">排序字段</label>
<select class="form-select-custom" id="arrivalSortBy">
<option value="CreatedAt">创建时间</option>
<option value="Id">ID</option>
<option value="HandoverNumber">交接单号</option>
</select>
</div>
<div class="col-md-6">
<label for="arrivalSortOrder" class="form-label-custom">排序顺序</label>
<select class="form-select-custom" id="arrivalSortOrder">
<option value="desc">降序</option>
<option value="asc">升序</option>
</select>
</div>
<div class="col-md-6">
<label for="arrivalPageSize" class="form-label-custom">每页记录数</label>
<select class="form-select-custom" id="arrivalPageSize">
<option value="500">500</option>
<option value="1000">1000</option>
<option value="2000">2000</option>
<option value="custom">自定义</option>
</select>
</div>
<div class="col-md-6" id="customArrivalPageSizeContainer" style="display: none;">
<label for="customArrivalPageSize" class="form-label-custom">自定义记录数</label>
<input type="number" class="form-control-custom" id="customArrivalPageSize" placeholder="请输入记录数" min="1" max="10000">
</div>
<div class="col-md-12 d-flex gap-2 pt-2">
<button type="submit" class="btn-custom-primary flex-1">🔍 查询</button>
<button type="button" class="btn-custom-secondary flex-1" id="resetArrivalHandoverForm">🔄 重置</button>
</div>
</div>
</form>
</div>
</div>
</div>
<div id="arrivalHandoverResult">
<div class="result-header">
<h3>📋 查询结果</h3>
<div class="d-flex align-items-center gap-3">
<button type="button" class="btn-custom-danger" id="deleteSelectedHandoverForms">🗑️ 删除选中</button>
<div id="arrivalHandoverPaginationInfo" class="pagination-info"></div>
</div>
</div>
<div class="table-responsive-custom">
<table class="table-custom">
<thead>
<tr>
<th><input type="checkbox" id="selectAllHandoverForms"></th>
<th>ID</th>
<th>客户简称</th>
<th>交接单号</th>
<th>头程物流商送达时间</th>
<th>收货时间</th>
<th>POD</th>
<th>备注</th>
<th>创建人</th>
<th>创建时间</th>
<th>时区</th>
<th>操作</th>
</tr>
</thead>
<tbody id="arrivalHandoverTableBody">
<tr>
<td colspan="12" class="text-center empty-state">
<div class="empty-state-icon">🔍</div>
<div class="empty-state-text">请点击查询按钮获取数据</div>
</td>
</tr>
</tbody>
</table>
</div>
<nav id="arrivalHandoverPagination" class="pagination-custom"></nav>
</div>
</div>
</div>
</div>
<div class="tab-pane fade" id="shipping-handover" role="tabpanel" aria-labelledby="shipping-handover-tab">
<div class="card card-custom">
<div class="card-header card-header-custom">
<h2>出货交接单</h2>
</div>
<div class="card-body card-body-custom">
<div class="mb-4 p-4 border rounded-lg bg-light">
<div class="row g-3">
<div class="col-md-8">
<div class="form-section-title"> 添加出货交接单</div>
<form id="addShippingHandoverForm" class="mb-4">
<div class="row g-3">
<div class="col-md-4">
<label for="addShippingHandoverNumber" class="form-label-custom">交接单号</label>
<input type="text" class="form-control-custom" id="addShippingHandoverNumber" placeholder="请输入交接单号,或留空自动生成">
</div>
<div class="col-md-4">
<label for="addChannel" class="form-label-custom">渠道</label>
<select class="form-select-custom" id="addChannel">
<option value="">请选择渠道</option>
<option value="UPS">UPS</option>
<option value="USPS">USPS</option>
<option value="GOFO">GOFO</option>
<option value="UNIUNI">UNIUNI</option>
<option value="SPEEDX">SPEEDX</option>
<option value="YWX">YWX</option>
<option value="SWIFTX">SWIFTX</option>
<option value="FEDEX">FEDEX</option>
</select>
</div>
<div class="col-md-4">
<label for="addDeliveryTime" class="form-label-custom">交货时间</label>
<input type="datetime-local" class="form-control-custom" id="addDeliveryTime">
</div>
<div class="col-md-6">
<label for="addShippingPOD" class="form-label-custom">POD图片上传</label>
<input type="file" class="form-control-custom" id="addShippingPOD" multiple accept="image/*">
<small class="form-text text-muted">支持上传多个图片文件</small>
</div>
<div class="col-md-6">
<label for="addShippingPODUrls" class="form-label-custom">POD链接多个链接用逗号分隔</label>
<input type="text" class="form-control-custom" id="addShippingPODUrls" placeholder="请输入S3链接多个链接用逗号分隔">
<small class="form-text text-muted">可以直接输入预生成的S3链接</small>
</div>
<div class="col-md-12">
<label class="form-label-custom">已上传图片</label>
<div id="uploadedShippingPODs" class="upload-area" style="min-height: 100px;">
<p class="text-muted mb-0">点击或粘贴图片到此处</p>
</div>
</div>
<div class="col-md-6">
<label for="addShippingRemarks" class="form-label-custom">备注</label>
<textarea class="form-control-custom" id="addShippingRemarks" rows="2" placeholder="请输入备注"></textarea>
</div>
<div class="col-md-6">
<label for="shippingTimezone" class="form-label-custom">时区选择</label>
<select class="form-select-custom" id="shippingTimezone">
<option value="America/New_York" selected>美国纽约 (东部时间 UTC-5)</option>
<option value="America/Chicago">美国芝加哥 (中部时间 UTC-6)</option>
<option value="America/Denver">美国丹佛 (山地时间 UTC-7)</option>
<option value="America/Los_Angeles">美国洛杉矶 (太平洋时间 UTC-8)</option>
<option value="America/Anchorage">美国安克雷奇 (阿拉斯加时间 UTC-9)</option>
<option value="America/Honolulu">美国檀香山 (夏威夷时间 UTC-10)</option>
<option value="Asia/Shanghai">中国标准时间 (UTC+8)</option>
<option value="UTC">协调世界时 (UTC)</option>
</select>
</div>
<div class="col-md-12 d-flex justify-content-end gap-2 pt-2">
<button type="button" class="btn-custom-secondary" id="generateShippingHandoverNumber">🔄 生成交接单号</button>
<button type="submit" class="btn-custom-primary">✅ 添加</button>
</div>
</div>
</form>
</div>
<div class="col-md-4">
<div class="form-section-title">🔗 关联袋牌</div>
<form id="associateBagTagsForm" class="mb-4">
<div class="row g-3">
<div class="col-md-12">
<label for="shippingHandoverFormId" class="form-label-custom">出货交接单ID</label>
<input type="number" class="form-control-custom" id="shippingHandoverFormId" placeholder="请输入出货交接单ID" required>
</div>
<div class="col-md-12">
<label for="bagTagNumbers" class="form-label-custom">袋牌号(多个请换行)</label>
<textarea class="form-control-custom" id="bagTagNumbers" rows="3" placeholder="请输入袋牌号,多个牌号请换行分隔" required></textarea>
</div>
<div class="col-md-12">
<button type="submit" class="btn-custom-primary w-100">🔗 关联</button>
</div>
</div>
</form>
<div class="form-section-title">🔍 查询关联袋牌</div>
<form id="queryAssociatedBagTagsForm" class="mb-4">
<div class="row g-3">
<div class="col-md-12">
<label for="queryShippingHandoverFormId" class="form-label-custom">出货交接单ID</label>
<input type="number" class="form-control-custom" id="queryShippingHandoverFormId" placeholder="请输入出货交接单ID" required>
</div>
<div class="col-md-12">
<button type="submit" class="btn-custom-primary w-100">🔍 查询</button>
</div>
</div>
</form>
</div>
</div>
</div>
<div class="mb-4">
<div class="form-section-title">🔍 查询条件</div>
<form id="shippingHandoverForm" class="p-4 border rounded-lg bg-light">
<div class="row g-3">
<div class="col-md-3">
<label for="shippingHandoverNumber" class="form-label-custom">交接单号</label>
<input type="text" class="form-control-custom" id="shippingHandoverNumber" placeholder="请输入交接单号">
</div>
<div class="col-md-3">
<label for="shippingChannel" class="form-label-custom">渠道</label>
<select class="form-select-custom" id="shippingChannel">
<option value="">全部</option>
<option value="UPS">UPS</option>
<option value="USPS">USPS</option>
<option value="GOFO">GOFO</option>
<option value="UNIUNI">UNIUNI</option>
<option value="SPEEDX">SPEEDX</option>
<option value="YWX">YWX</option>
<option value="SWIFTX">SWIFTX</option>
<option value="FEDEX">FEDEX</option>
</select>
</div>
<div class="col-md-3">
<label for="shippingCreator" class="form-label-custom">创建人</label>
<input type="text" class="form-control-custom" id="shippingCreator" placeholder="请输入创建人">
</div>
<div class="col-md-3">
<label for="startDeliveryTime" class="form-label-custom">交货时间开始</label>
<input type="datetime-local" class="form-control-custom" id="startDeliveryTime">
</div>
<div class="col-md-3">
<label for="endDeliveryTime" class="form-label-custom">交货时间结束</label>
<input type="datetime-local" class="form-control-custom" id="endDeliveryTime">
</div>
<div class="col-md-3">
<label for="shippingSortBy" class="form-label-custom">排序字段</label>
<select class="form-select-custom" id="shippingSortBy">
<option value="CreatedAt">创建时间</option>
<option value="Id">ID</option>
<option value="HandoverNumber">交接单号</option>
</select>
</div>
<div class="col-md-3">
<label for="shippingSortOrder" class="form-label-custom">排序顺序</label>
<select class="form-select-custom" id="shippingSortOrder">
<option value="desc">降序</option>
<option value="asc">升序</option>
</select>
</div>
<div class="col-md-3">
<label for="shippingPageSize" class="form-label-custom">每页记录数</label>
<select class="form-select-custom" id="shippingPageSize">
<option value="500">500</option>
<option value="1000">1000</option>
<option value="2000">2000</option>
<option value="custom">自定义</option>
</select>
</div>
<div class="col-md-3" id="customShippingPageSizeContainer" style="display: none;">
<label for="customShippingPageSize" class="form-label-custom">自定义记录数</label>
<input type="number" class="form-control-custom" id="customShippingPageSize" placeholder="请输入记录数" min="1" max="10000">
</div>
<div class="col-md-12 d-flex align-items-end gap-2 pt-2">
<button type="submit" class="btn-custom-primary">🔍 查询</button>
<button type="button" class="btn-custom-secondary" id="resetShippingHandoverForm">🔄 重置</button>
</div>
</div>
</form>
</div>
<div id="shippingHandoverResult">
<div class="result-header">
<h3>📋 查询结果</h3>
<div class="d-flex align-items-center gap-3">
<button type="button" class="btn-custom-danger" id="deleteSelectedShippingHandoverForms">🗑️ 删除选中</button>
<div id="shippingHandoverPaginationInfo" class="pagination-info"></div>
</div>
</div>
<div class="table-responsive-custom">
<table class="table-custom">
<thead>
<tr>
<th><input type="checkbox" id="selectAllShippingHandoverForms"></th>
<th>ID</th>
<th>交接单号</th>
<th>箱数</th>
<th>交货单量</th>
<th>交货渠道</th>
<th>交货时间</th>
<th>时区</th>
<th>POD</th>
<th>备注</th>
<th>创建人</th>
<th>创建时间</th>
<th>操作</th>
</tr>
</thead>
<tbody id="shippingHandoverTableBody">
<tr>
<td colspan="13" class="text-center empty-state">
<div class="empty-state-icon">🔍</div>
<div class="empty-state-text">请点击查询按钮获取数据</div>
</td>
</tr>
</tbody>
</table>
</div>
<nav id="shippingHandoverPagination" class="pagination-custom"></nav>
</div>
</div>
</div>
</div>
<div class="tab-pane fade" id="data-dashboard" role="tabpanel" aria-labelledby="data-dashboard-tab">
<div class="card card-custom">
<div class="card-header card-header-custom">
<h2>数据看板</h2>
</div>
<div class="card-body card-body-custom">
<form id="dashboardQueryForm" class="mb-4">
<div class="row g-3">
<div class="col-md-4">
<label for="arrivalNumber" class="form-label-custom">交接单号</label>
<input type="text" class="form-control-custom" id="arrivalNumber" placeholder="请输入交接单号">
</div>
<div class="col-md-4">
<label for="dashboardStartDate" class="form-label-custom">开始日期</label>
<input type="date" class="form-control-custom" id="dashboardStartDate">
</div>
<div class="col-md-4">
<label for="dashboardEndDate" class="form-label-custom">结束日期</label>
<input type="date" class="form-control-custom" id="dashboardEndDate">
</div>
<div class="col-md-4">
<label for="dashboardCustomerId" class="form-label-custom">客户</label>
<select class="form-select-custom" id="dashboardCustomerId">
<option value="">全部</option>
</select>
</div>
<div class="col-md-12 d-flex align-items-end gap-2 pt-2">
<button type="submit" class="btn-custom-primary">🔍 查询</button>
<button type="button" class="btn-custom-secondary" id="resetDashboardForm">🔄 重置</button>
<button type="button" class="btn-custom-warning" id="exportDailyStatsChineseBtn">📥 导出运维监控</button>
</div>
</div>
</form>
<div class="info-box">
<div class="info-box-title">📊 当天扫描后未下单数量</div>
<div class="info-box-content">
<div class="info-box-item">
<label>未下单数量</label>
<span id="todayUnorderedCount">0</span>
</div>
<div class="info-box-item">
<label>扫描总数</label>
<span id="todayScanTotal">0</span>
</div>
<div class="info-box-item">
<label>统计日期</label>
<span id="todayDate" style="font-size: 1rem;"></span>
</div>
</div>
</div>
<div id="dataDashboardResult">
<div class="result-header">
<h3>📋 查询结果</h3>
<div class="d-flex align-items-center gap-3">
<button type="button" class="btn-custom-success" id="exportDashboardExcelBtn">📥 导出Excel</button>
<div id="dataDashboardPaginationInfo" class="pagination-info"></div>
</div>
</div>
<div class="table-responsive-custom">
<table class="table-custom">
<thead>
<tr>
<th>客户简称</th>
<th>已有标签率</th>
<th>未换单完成数量</th>
<th>提单号</th>
<th>大箱号</th>
<th>到货订单数量</th>
<th>换单完成数量</th>
<th>无标签数据数量</th>
<th>到货时间</th>
<th>操作</th>
</tr>
</thead>
<tbody id="dataDashboardTableBody">
<tr>
<td colspan="10" class="text-center empty-state">
<div class="empty-state-icon">🔍</div>
<div class="empty-state-text">请点击查询按钮获取数据</div>
</td>
</tr>
</tbody>
</table>
</div>
<nav id="dataDashboardPagination" class="pagination-custom"></nav>
</div>
</div>
</div>
</div>
<div class="tab-pane fade" id="order-import" role="tabpanel" aria-labelledby="order-import-tab">
<div class="card card-custom">
<div class="card-header card-header-custom">
<h2>订单导入</h2>
</div>
<div class="card-body card-body-custom">
<form id="orderImportForm" class="mb-4">
<div class="row g-3">
<div class="col-md-4">
<label for="importCustomerId" class="form-label-custom">客户选择</label>
<select class="form-select-custom" id="importCustomerId" required>
<option value="">请选择客户</option>
</select>
</div>
<div class="col-md-4">
<label for="labelFilenameMapping" class="form-label-custom">标签文件名映射为</label>
<select class="form-select-custom" id="labelFilenameMapping" required>
<option value="none"></option>
<option value="neutralWaybill">中性面单单号</option>
<option value="finalMileTracking">尾程单号</option>
</select>
</div>
<div class="col-md-4">
<label for="excelFile" class="form-label-custom">Excel文件</label>
<input type="file" class="form-control-custom" id="excelFile" accept=".xlsx,.xls" required>
<small class="form-text text-muted">支持.xlsx和.xls格式</small>
<div class="mt-2">
<button type="button" class="btn btn-sm btn-outline-secondary" id="downloadTemplateBtn">下载模板</button>
</div>
</div>
<div class="col-md-12">
<label class="form-label-custom">Excel列要求</label>
<div class="p-3 border rounded bg-light">
<p>Excel文件必须包含以下列</p>
<ul>
<li><strong>BillOfLadingNumber</strong> - 提单号(支持纯数字格式)</li>
<li><strong>MasterPackageNumber</strong> - 大包号(支持纯数字格式)</li>
<li><strong>NeutralWaybillNumber</strong> - 中性面单单号(必填)</li>
<li><strong>FinalMileTrackingNumber</strong> - 尾程单号</li>
<li><strong>Label</strong> - 标签内容可选会根据选择的映射生成URL</li>
</ul>
</div>
</div>
<div class="col-md-12 d-flex justify-content-end">
<button type="submit" class="btn-custom-primary">导入订单</button>
<button type="button" class="btn-custom-secondary ms-2" id="resetOrderImportForm">重置</button>
</div>
</div>
</form>
<div id="importProgress" class="mb-4" style="display: none;">
<div class="card">
<div class="card-header">
<h3 class="h5">导入进度</h3>
</div>
<div class="card-body">
<div class="progress mb-3">
<div class="progress-bar" role="progressbar" style="width: 0%" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100"></div>
</div>
<div class="row">
<div class="col-md-4">
<p><strong>总订单数:</strong><span id="totalOrders">0</span></p>
</div>
<div class="col-md-4">
<p><strong>已处理:</strong><span id="processedOrders">0</span></p>
</div>
<div class="col-md-4">
<p><strong>进度:</strong><span id="progressPercentage">0%</span></p>
</div>
</div>
<div id="importStatus" class="mt-2"></div>
</div>
</div>
</div>
<div id="importResult" class="mt-4" style="display: none;">
<div class="card">
<div class="card-header">
<h3 class="h5">导入结果</h3>
</div>
<div class="card-body">
<div class="row mb-3">
<div class="col-md-3">
<p><strong>总订单数:</strong><span id="resultTotalCount">0</span></p>
</div>
<div class="col-md-3">
<p><strong>成功:</strong><span id="resultSuccessCount" class="text-success">0</span></p>
</div>
<div class="col-md-3">
<p><strong>失败:</strong><span id="resultFailedCount" class="text-danger">0</span></p>
</div>
<div class="col-md-3">
<p><strong>成功率:</strong><span id="resultSuccessRate">0%</span></p>
</div>
</div>
<div class="table-responsive-custom">
<table class="table-custom">
<thead>
<tr>
<th>提单号</th>
<th>大包号</th>
<th>中性面单单号</th>
<th>尾程单号</th>
<th>状态</th>
<th>消息</th>
<th>标签</th>
</tr>
</thead>
<tbody id="importResultTableBody">
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="tab-pane fade" id="order-log" role="tabpanel" aria-labelledby="order-log-tab">
<div class="card card-custom">
<div class="card-header card-header-custom">
<h2>订单日志查询</h2>
</div>
<div class="card-body card-body-custom">
<form id="orderLogForm" class="mb-4">
<div class="row g-3">
<div class="col-md-6">
<label for="orderLogNeutralWaybillNumber" class="form-label-custom">中性面单单号</label>
<input type="text" class="form-control-custom" id="orderLogNeutralWaybillNumber" placeholder="请输入中性面单单号">
</div>
<div class="col-md-6">
<label for="orderLogFinalMileTrackingNumber" class="form-label-custom">尾程单号</label>
<input type="text" class="form-control-custom" id="orderLogFinalMileTrackingNumber" placeholder="请输入尾程单号">
</div>
<div class="col-md-6">
<label for="orderLogSortBy" class="form-label-custom">排序字段</label>
<select class="form-select-custom" id="orderLogSortBy">
<option value="CreatedAt">创建时间</option>
<option value="Id">ID</option>
</select>
</div>
<div class="col-md-6">
<label for="orderLogSortOrder" class="form-label-custom">排序顺序</label>
<select class="form-select-custom" id="orderLogSortOrder">
<option value="desc">降序</option>
<option value="asc">升序</option>
</select>
</div>
<div class="col-md-12 d-flex align-items-end gap-2 pt-2">
<button type="submit" class="btn-custom-primary">🔍 查询</button>
<button type="button" class="btn-custom-secondary" id="resetOrderLogForm">🔄 重置</button>
</div>
</div>
</form>
<div id="orderLogResult">
<div class="result-header">
<h3>📋 查询结果</h3>
<div class="d-flex align-items-center gap-3">
<button type="button" class="btn-custom-success" id="exportOrderLogExcelBtn">📥 导出Excel</button>
<div id="orderLogPaginationInfo" class="pagination-info"></div>
</div>
</div>
<div class="table-responsive-custom">
<table class="table-custom">
<thead>
<tr>
<th>ID</th>
<th>中性面单单号</th>
<th>尾程单号</th>
<th>操作类型</th>
<th>操作详情</th>
<th>操作人</th>
<th>操作时间</th>
</tr>
</thead>
<tbody id="orderLogTableBody">
<tr>
<td colspan="7" class="text-center empty-state">
<div class="empty-state-icon">🔍</div>
<div class="empty-state-text">请点击查询按钮获取数据</div>
</td>
</tr>
</tbody>
</table>
</div>
<nav id="orderLogPagination" class="pagination-custom"></nav>
</div>
</div>
</div>
</div>
<div class="tab-pane fade" id="bag-tag" role="tabpanel" aria-labelledby="bag-tag-tab">
<div class="card card-custom">
<div class="card-header card-header-custom">
<h2>集包数据查询</h2>
</div>
<div class="card-body card-body-custom">
<form id="bagTagForm" class="mb-4">
<div class="row g-3">
<div class="col-md-3">
<label for="bagTagNumber" class="form-label-custom">袋牌号码</label>
<input type="text" class="form-control-custom" id="bagTagNumber" placeholder="请输入袋牌号码">
</div>
<div class="col-md-3">
<label for="bagTagChannel" class="form-label-custom">渠道</label>
<select class="form-select-custom" id="bagTagChannel">
<option value="">全部</option>
<option value="UPS">UPS</option>
<option value="USPS">USPS</option>
<option value="GOFO">GOFO</option>
<option value="UNIUNI">UNIUNI</option>
<option value="SPEEDX">SPEEDX</option>
<option value="YWX">YWX</option>
<option value="SWIFTX">SWIFTX</option>
<option value="FEDEX">FEDEX</option>
</select>
</div>
<div class="col-md-3">
<label for="bagTagStatus" class="form-label-custom">状态</label>
<select class="form-select-custom" id="bagTagStatus">
<option value="">全部</option>
<option value="Generated">已生成</option>
<option value="Opened">已打开</option>
<option value="Closed">已关闭</option>
</select>
</div>
<div class="col-md-3">
<label for="bagTagCreator" class="form-label-custom">创建人</label>
<input type="text" class="form-control-custom" id="bagTagCreator" placeholder="请输入创建人">
</div>
<div class="col-md-3">
<label for="bagTagStartCreatedAt" class="form-label-custom">创建时间开始</label>
<input type="datetime-local" class="form-control-custom" id="bagTagStartCreatedAt">
</div>
<div class="col-md-3">
<label for="bagTagEndCreatedAt" class="form-label-custom">创建时间结束</label>
<input type="datetime-local" class="form-control-custom" id="bagTagEndCreatedAt">
</div>
<div class="col-md-3">
<label for="bagTagSortBy" class="form-label-custom">排序字段</label>
<select class="form-select-custom" id="bagTagSortBy">
<option value="CreatedAt">创建时间</option>
<option value="Id">ID</option>
<option value="TagNumber">袋牌号码</option>
</select>
</div>
<div class="col-md-3">
<label for="bagTagSortOrder" class="form-label-custom">排序顺序</label>
<select class="form-select-custom" id="bagTagSortOrder">
<option value="desc">降序</option>
<option value="asc">升序</option>
</select>
</div>
<div class="col-md-3">
<label for="bagTagPageSize" class="form-label-custom">每页记录数</label>
<select class="form-select-custom" id="bagTagPageSize">
<option value="500">500</option>
<option value="1000">1000</option>
<option value="2000">2000</option>
<option value="custom">自定义</option>
</select>
</div>
<div class="col-md-3" id="customBagTagPageSizeContainer" style="display: none;">
<label for="customBagTagPageSize" class="form-label-custom">自定义记录数</label>
<input type="number" class="form-control-custom" id="customBagTagPageSize" placeholder="请输入记录数" min="1" max="10000">
</div>
<div class="col-md-12 d-flex align-items-end gap-2 pt-2">
<button type="submit" class="btn-custom-primary">🔍 查询</button>
<button type="button" class="btn-custom-secondary" id="resetBagTagForm">🔄 重置</button>
</div>
</div>
</form>
<div id="bagTagResult">
<div class="result-header">
<h3>📋 查询结果</h3>
<div class="d-flex align-items-center gap-3">
<button type="button" class="btn-custom-success" id="exportBagTagExcelBtn">📥 导出Excel</button>
<div id="bagTagPaginationInfo" class="pagination-info"></div>
</div>
</div>
<div class="table-responsive-custom">
<table class="table-custom">
<thead>
<tr>
<th>ID</th>
<th>袋牌号码</th>
<th>渠道</th>
<th>状态</th>
<th>创建人</th>
<th>创建时间</th>
<th>打开时间</th>
<th>关闭时间</th>
<th>关联运单数量</th>
<th>操作</th>
</tr>
</thead>
<tbody id="bagTagTableBody">
<tr>
<td colspan="10" class="text-center empty-state">
<div class="empty-state-icon">🔍</div>
<div class="empty-state-text">请点击查询按钮获取数据</div>
</td>
</tr>
</tbody>
</table>
</div>
<nav id="bagTagPagination" class="pagination-custom"></nav>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="tab-pane fade" id="ops-monitor" role="tabpanel" aria-labelledby="ops-monitor-tab">
<div class="card card-custom">
<div class="card-header card-header-custom">
<h2>📈 运维监控看板</h2>
</div>
<div class="card-body card-body-custom">
<div class="d-flex align-items-center gap-2 mb-3 flex-wrap">
<button type="button" class="btn-custom" id="refreshOpsMonitorBtn">🔄 刷新数据</button>
<button type="button" class="btn-custom-warning" id="exportOpsMonitorBtn">📥 导出Excel</button>
<span class="text-muted ms-2" id="opsMonitorFetchTime"></span>
</div>
<div class="row g-3 mb-4" id="opsMonitorSummaryCards">
<div class="col-6 col-md-3">
<div class="info-box" style="margin-bottom:0">
<div class="info-box-title">今日应换单数</div>
<div class="info-box-content"><span id="opsCard_shouldReplace" style="font-size:2rem;font-weight:700"></span></div>
</div>
</div>
<div class="col-6 col-md-3">
<div class="info-box" style="margin-bottom:0">
<div class="info-box-title">今日换单完成数</div>
<div class="info-box-content"><span id="opsCard_success" style="font-size:2rem;font-weight:700"></span></div>
</div>
</div>
<div class="col-6 col-md-3">
<div class="info-box" style="margin-bottom:0">
<div class="info-box-title">今日24H换单成功数</div>
<div class="info-box-content"><span id="opsCard_24h" style="font-size:2rem;font-weight:700"></span></div>
</div>
</div>
<div class="col-6 col-md-3">
<div class="info-box" style="margin-bottom:0">
<div class="info-box-title">今日换单完成率</div>
<div class="info-box-content"><span id="opsCard_rate" style="font-size:2rem;font-weight:700"></span></div>
</div>
</div>
</div>
<div id="opsMonitorTableWrap">
<div class="table-responsive-custom">
<table class="table-custom" id="opsMonitorTable">
<thead>
<tr>
<th>日期</th>
<th>当天新增换单数</th>
<th>累计要换的总单数</th>
<th>当天应该换单数</th>
<th>当日换单完成数</th>
<th>当日换单失败数</th>
<th>当日STOP数</th>
<th>24H换单成功数</th>
<th>当日标签推送数</th>
<th>当日扫描数</th>
<th>当天换单完成率</th>
<th>24H换单率</th>
<th>数据拉取时间(UTC-5)</th>
</tr>
</thead>
<tbody id="opsMonitorTableBody">
<tr><td colspan="13" style="text-align:center;color:#888">点击刷新加载数据</td></tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
<div class="notification-bubble" id="notificationBubble">
<div class="notification-content">
<div class="notification-icon" id="notificationIcon"></div>
<div class="notification-text">
<div class="notification-text-title" id="notificationTitle">操作完成</div>
<div class="notification-text-message" id="notificationMessage">消息</div>
</div>
</div>
</div>
<div class="modal fade" id="editShippingHandoverModal" tabindex="-1" aria-labelledby="editShippingHandoverModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="editShippingHandoverModalLabel">编辑出货交接单</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<form id="editShippingHandoverForm">
<input type="hidden" id="editId">
<div class="mb-3">
<label for="editDeliveryTime" class="form-label">交货时间</label>
<input type="datetime-local" class="form-control" id="editDeliveryTime">
</div>
<div class="mb-3">
<label for="editTimezone" class="form-label">时区</label>
<select class="form-select" id="editTimezone">
<option value="America/New_York">美国纽约 (东部时间 UTC-5)</option>
<option value="America/Chicago">美国芝加哥 (中部时间 UTC-6)</option>
<option value="America/Denver">美国丹佛 (山地时间 UTC-7)</option>
<option value="America/Los_Angeles">美国洛杉矶 (太平洋时间 UTC-8)</option>
<option value="America/Anchorage">美国安克雷奇 (阿拉斯加时间 UTC-9)</option>
<option value="America/Honolulu">美国檀香山 (夏威夷时间 UTC-10)</option>
<option value="Asia/Shanghai">中国标准时间 (UTC+8)</option>
<option value="UTC">协调世界时 (UTC)</option>
</select>
</div>
<div class="mb-3">
<label for="editShippingPOD" class="form-label">POD图片上传</label>
<input type="file" class="form-control" id="editShippingPOD" multiple accept="image/*">
<small class="form-text text-muted">支持上传多个图片文件</small>
</div>
<div class="mb-3">
<label for="editShippingPODUrls" class="form-label">POD链接多个链接用逗号分隔</label>
<input type="text" class="form-control" id="editShippingPODUrls" placeholder="请输入S3链接多个链接用逗号分隔">
<small class="form-text text-muted">可以直接输入预生成的S3链接</small>
</div>
<div class="mb-3">
<label class="form-label">已上传图片</label>
<div id="editUploadedShippingPODs" class="upload-area" style="min-height: 100px;">
<p class="text-muted text-center">点击或粘贴图片到此处</p>
</div>
</div>
<div class="mb-3">
<label for="editRemarks" class="form-label">备注</label>
<textarea class="form-control" id="editRemarks" rows="3"></textarea>
</div>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">取消</button>
<button type="button" class="btn btn-primary" id="saveEditBtn">保存</button>
</div>
</div>
</div>
</div>
<div class="modal fade" id="editArrivalHandoverModal" tabindex="-1" aria-labelledby="editArrivalHandoverModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="editArrivalHandoverModalLabel">编辑到货交接单</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<form id="editArrivalHandoverForm">
<input type="hidden" id="editArrivalId">
<input type="hidden" id="editArrivalHandoverNumber">
<div class="mb-3">
<label for="editLogisticsProviderArrivalTime" class="form-label">头程物流商送达时间</label>
<input type="datetime-local" class="form-control" id="editLogisticsProviderArrivalTime">
</div>
<div class="mb-3">
<label for="editReceiptTime" class="form-label">收货时间</label>
<input type="datetime-local" class="form-control" id="editReceiptTime">
</div>
<div class="mb-3">
<label for="editArrivalPODUpload" class="form-label">POD图片上传</label>
<input type="file" class="form-control" id="editArrivalPODUpload" multiple accept="image/*">
<small class="form-text text-muted">支持上传多个图片文件</small>
</div>
<div class="mb-3">
<label for="editArrivalPOD" class="form-label">POD链接多个链接用逗号分隔</label>
<input type="text" class="form-control" id="editArrivalPOD" placeholder="请输入S3链接多个链接用逗号分隔">
<small class="form-text text-muted">可以直接输入预生成的S3链接</small>
</div>
<div class="mb-3">
<label class="form-label">已上传图片</label>
<div id="editUploadedArrivalPODs" class="upload-area" style="min-height: 100px;">
<p class="text-muted text-center">点击或粘贴图片到此处</p>
</div>
</div>
<div class="mb-3">
<label for="editArrivalTimezone" class="form-label">时区</label>
<select class="form-select" id="editArrivalTimezone">
<option value="America/New_York">美国纽约 (东部时间 UTC-5)</option>
<option value="America/Chicago">美国芝加哥 (中部时间 UTC-6)</option>
<option value="America/Denver">美国丹佛 (山地时间 UTC-7)</option>
<option value="America/Los_Angeles">美国洛杉矶 (太平洋时间 UTC-8)</option>
<option value="America/Anchorage">美国安克雷奇 (阿拉斯加时间 UTC-9)</option>
<option value="America/Honolulu">美国檀香山 (夏威夷时间 UTC-10)</option>
<option value="Asia/Shanghai">中国标准时间 (UTC+8)</option>
<option value="UTC">协调世界时 (UTC)</option>
</select>
</div>
<div class="mb-3">
<label for="editArrivalRemarks" class="form-label">备注</label>
<textarea class="form-control" id="editArrivalRemarks" rows="3"></textarea>
</div>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">取消</button>
<button type="button" class="btn btn-primary" id="saveArrivalEditBtn">保存</button>
</div>
</div>
</div>
</div>
<div class="modal fade" id="podPreviewModal" tabindex="-1" aria-labelledby="podPreviewModalLabel" aria-hidden="true">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="podPreviewModalLabel">POD 预览</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<div id="podPreviewContent"></div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">关闭</button>
</div>
</div>
</div>
</div>
<div class="modal fade" id="labelPreviewModal" tabindex="-1" aria-labelledby="labelPreviewModalLabel" aria-hidden="true">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="labelPreviewModalLabel">Label 预览</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<div id="labelPreviewContent"></div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">关闭</button>
</div>
</div>
</div>
</div>
<iframe name="uploadFrame" id="uploadFrame" style="display: none;"></iframe>
<script>
const environments = {
local: 'http://localhost:5002',
test: 'http://172.232.21.79:5002',
production: 'https://lr.tooexp.com'
};
function getBaseUrl() {
const env = $('#environment').val();
return environments[env] || environments.test;
}
function showNotification(title, message, type = 'success') {
const bubble = $('#notificationBubble');
const icon = $('#notificationIcon');
const titleEl = $('#notificationTitle');
const messageEl = $('#notificationMessage');
titleEl.text(title);
messageEl.text(message);
if (type === 'success') {
icon.text('✅');
icon.removeClass().addClass('notification-icon success');
} else if (type === 'error') {
icon.text('❌');
icon.removeClass().addClass('notification-icon error');
} else {
icon.text('');
icon.removeClass().addClass('notification-icon info');
}
bubble.addClass('show');
setTimeout(() => {
bubble.removeClass('show');
}, 3000);
}
function generateShippingHandoverNumberCallback(response) {
console.log('Generate shipping handover number response:', response);
if (response && response.code === 0 && response.data) {
$('#addShippingHandoverNumber').val(response.data);
showNotification('操作完成', '成功生成交接单号', 'success');
} else {
showNotification('操作失败', response ? response.message : '未知错误', 'error');
}
}
function loadCustomerList() {
const url = getBaseUrl() + '/api/Label/customers';
const callbackName = 'customerListCallback_' + Date.now();
window[callbackName] = function(response) {
console.log('Customer list loaded:', response);
if (response && response.status === 'ok') {
const customerSelect = $('#customerId');
customerSelect.empty();
customerSelect.append('<option value="">全部</option>');
const scanCustomerSelect = $('#scanCustomerId');
scanCustomerSelect.empty();
scanCustomerSelect.append('<option value="">全部</option>');
scanCustomerSelect.append('<option value="0">无客户</option>');
const importCustomerSelect = $('#importCustomerId');
importCustomerSelect.empty();
importCustomerSelect.append('<option value="">请选择客户</option>');
const dashboardCustomerSelect = $('#dashboardCustomerId');
dashboardCustomerSelect.empty();
dashboardCustomerSelect.append('<option value="">全部</option>');
if (response.data && response.data.length > 0) {
response.data.forEach(customer => {
customerSelect.append('<option value="' + customer.Id + '">' + customer.CustomerCode + '</option>');
scanCustomerSelect.append('<option value="' + customer.Id + '">' + customer.CustomerCode + '</option>');
importCustomerSelect.append('<option value="' + customer.Id + '">' + customer.CustomerCode + '</option>');
dashboardCustomerSelect.append('<option value="' + customer.Id + '">' + customer.CustomerCode + '</option>');
});
console.log('Customer list updated successfully. Total customers:', response.data.length);
} else {
console.warn('No customers found in response');
}
} else {
console.error('Failed to load customer list:', response);
}
delete window[callbackName];
};
$.ajax({
url: url,
type: 'GET',
dataType: 'jsonp',
jsonp: 'callback',
jsonpCallback: callbackName,
cache: false,
timeout: 10000,
success: function(response) {
console.log('AJAX success callback executed');
},
error: function(xhr, status, error) {
console.error('Failed to load customer list:', error, status);
delete window[callbackName];
}
});
}
$(document).ready(function() {
loadCustomerList();
$('#environment').change(function() {
loadCustomerList();
customerInfo = {
id: '',
code: '',
apiKey: ''
};
$('#customerId').val('');
$('#scanCustomerId').val('');
$('#dashboardCustomerId').val('');
$('#importCustomerId').val('');
showNotification('环境已切换', '正在加载对应环境的客户列表...', 'info');
});
$('#customerId').change(function() {
const customerId = $(this).val();
const customerCode = $(this).find('option:selected').text();
if (customerId) {
getCustomerApiKey(customerId, customerCode);
} else {
customerInfo = {
id: '',
code: '',
apiKey: ''
};
}
});
$('#scanCustomerId').change(function() {
const customerId = $(this).val();
const customerCode = $(this).find('option:selected').text();
if (customerId) {
getCustomerApiKey(customerId, customerCode);
} else {
customerInfo = {
id: '',
code: '',
apiKey: ''
};
}
});
$('#dashboardCustomerId').change(function() {
const customerId = $(this).val();
const customerCode = $(this).find('option:selected').text();
if (customerId) {
getCustomerApiKey(customerId, customerCode);
} else {
customerInfo = {
id: '',
code: '',
apiKey: ''
};
}
});
// ============================================
// 订单查询表单事件绑定
// ============================================
$('#labelReplaceForm').submit(function(e) {
e.preventDefault();
queryLabelReplace(1);
});
$('#resetLabelReplaceForm').click(function() {
resetLabelReplaceForm();
});
$('#exportExcelBtn').click(function() {
exportLabelReplaceToExcel();
});
$('#pageSize').change(function() {
if ($(this).val() === 'custom') {
$('#customPageSizeContainer').show();
} else {
$('#customPageSizeContainer').hide();
}
});
// ============================================
// 换单扫描记录查询表单事件绑定
// ============================================
$('#labelScanForm').submit(function(e) {
e.preventDefault();
queryLabelScan(1);
});
$('#resetLabelScanForm').click(function() {
resetLabelScanForm();
});
$('#exportScanExcelBtn').click(function() {
exportLabelScanToExcel();
});
$('#scanPageSize').change(function() {
if ($(this).val() === 'custom') {
$('#customScanPageSizeContainer').show();
} else {
$('#customScanPageSizeContainer').hide();
}
});
// ============================================
// 到货交接单表单事件绑定
// ============================================
$('#arrivalHandoverForm').submit(function(e) {
e.preventDefault();
queryArrivalHandover(1);
});
$('#resetArrivalHandoverForm').click(function() {
resetArrivalHandoverForm();
});
$('#arrivalPageSize').change(function() {
if ($(this).val() === 'custom') {
$('#customArrivalPageSizeContainer').show();
} else {
$('#customArrivalPageSizeContainer').hide();
}
});
// ============================================
// 出货交接单表单事件绑定
// ============================================
$('#shippingHandoverForm').submit(function(e) {
e.preventDefault();
queryShippingHandover(1);
});
$('#resetShippingHandoverForm').click(function() {
resetShippingHandoverForm();
});
$('#shippingPageSize').change(function() {
if ($(this).val() === 'custom') {
$('#customShippingPageSizeContainer').show();
} else {
$('#customShippingPageSizeContainer').hide();
}
});
// ============================================
// 集包数据查询表单事件绑定
// ============================================
$('#bagTagForm').submit(function(e) {
e.preventDefault();
queryBagTag(1);
});
$('#resetBagTagForm').click(function() {
resetBagTagForm();
});
$('#bagTagPageSize').change(function() {
if ($(this).val() === 'custom') {
$('#customBagTagPageSizeContainer').show();
} else {
$('#customBagTagPageSizeContainer').hide();
}
});
// ============================================
// 订单日志查询表单事件绑定
// ============================================
$('#orderLogForm').submit(function(e) {
e.preventDefault();
queryOrderLog();
});
$('#resetOrderLogForm').click(function() {
$('#orderLogNeutralWaybillNumber').val('');
$('#orderLogFinalMileTrackingNumber').val('');
$('#orderLogSortBy').val('CreatedAt');
$('#orderLogSortOrder').val('desc');
});
$('#exportOrderLogExcelBtn').click(function() {
exportOrderLogToExcel();
});
// ============================================
// 数据看板表单事件绑定
// ============================================
$('#dashboardQueryForm').submit(function(e) {
e.preventDefault();
queryDashboard();
});
$('#resetDashboardForm').click(function() {
$('#arrivalNumber').val('');
$('#dashboardStartDate').val('');
$('#dashboardEndDate').val('');
$('#dashboardCustomerId').val('');
});
$('#exportDashboardExcelBtn').click(function() {
exportDashboardToExcel();
});
$('#exportDailyStatsChineseBtn').click(function() {
exportDailyStatsChineseToExcel();
});
// ============================================
// 订单导入相关事件绑定
// ============================================
$('#orderImportForm').submit(function(e) {
e.preventDefault();
importOrders();
});
// 重置按钮
$('#resetOrderImportForm').click(function() {
$('#orderImportForm')[0].reset();
$('#importProgress').hide();
$('#importResult').hide();
});
// 下载模板按钮
$('#downloadTemplateBtn').click(function() {
downloadTemplate();
});
// 客户选择变更事件
$('#importCustomerId').change(function() {
const customerId = $(this).val();
const customerCode = $(this).find('option:selected').text();
if (customerId) {
getCustomerApiKey(customerId, customerCode);
} else {
customerInfo = {
id: '',
code: '',
apiKey: ''
};
}
});
// 页面加载时初始化客户信息
if ($('#importCustomerId').val()) {
const customerId = $('#importCustomerId').val();
const customerCode = $('#importCustomerId').find('option:selected').text();
getCustomerApiKey(customerId, customerCode);
}
// ============================================
// 编辑到货交接单保存
// ============================================
$('#saveArrivalEditBtn').click(function() {
showNotification('提示', '保存编辑功能', 'info');
});
// ============================================
// 编辑出货交接单保存
// ============================================
$('#saveEditBtn').click(function() {
showNotification('提示', '保存编辑功能', 'info');
});
// ============================================
// 生成出货交接单号
// ============================================
$('#generateShippingHandoverNumber').click(function() {
const baseUrl = getBaseUrl();
const url = baseUrl + '/api/shipping-handover/generate-number';
$.ajax({
url: url,
type: 'GET',
dataType: 'jsonp',
jsonp: 'callback',
jsonpCallback: 'generateShippingHandoverNumberCallback',
success: function(response) {
// 回调函数已经处理
},
error: function(xhr, status, error) {
console.error('Failed to generate shipping handover number:', error);
showNotification('生成失败', error, 'error');
}
});
});
// ============================================
// 添加到货交接单
// ============================================
$('#addArrivalHandoverForm').submit(function(e) {
e.preventDefault();
const handoverNumbers = $('#addHandoverNumber').val().trim().split('\n').filter(hn => hn.trim() !== '');
const logisticsProviderArrivalTime = $('#addLogisticsProviderArrivalTime').val();
const receiptTime = $('#addReceiptTime').val();
const files = $('#addPOD')[0].files;
const remarks = $('#addRemarks').val().trim();
const timezone = $('#timezone').val();
if (handoverNumbers.length === 0) {
showNotification('输入错误', '请输入至少一个交接单号', 'error');
return;
}
// 处理每个交接单号
handoverNumbers.forEach(handoverNumber => {
let pod = '';
let uniqueIds = [];
// 如果有文件上传预生成S3链接
if (files.length > 0) {
// 预生成S3链接
const preGeneratedUrls = [];
uniqueIds = [];
for (let i = 0; i < files.length; i++) {
const file = files[i];
const uniqueId = generateGuid();
uniqueIds.push(uniqueId);
const preGeneratedUrl = generateS3Url('arrival', handoverNumber, file.name, uniqueId);
preGeneratedUrls.push(preGeneratedUrl);
}
// 将预生成的链接添加到POD链接输入框
pod = preGeneratedUrls.join(',');
}
// 先处理到货交接单添加不带POD
processArrivalHandoverForm(handoverNumber, logisticsProviderArrivalTime, receiptTime, pod, remarks, timezone, function() {
// 成功创建到货交接单后上传图片
if (files.length > 0) {
// 上传图片完成后再更新POD字段
setTimeout(function() {
uploadFilesWithForm(files, 'arrival', handoverNumber, uniqueIds);
}, 1000); // 延迟1秒确保图片上传完成
}
});
});
});
// 处理单个到货交接单添加
function processArrivalHandoverForm(handoverNumber, logisticsProviderArrivalTime, receiptTime, pod, remarks, timezone, callback) {
const url = getBaseUrl() + '/api/arrival-handover/create';
// 构建请求数据
const data = {
HandoverNumber: handoverNumber,
LogisticsProviderArrivalTime: logisticsProviderArrivalTime,
ReceiptTime: receiptTime,
Remarks: remarks,
Creator: 'system',
TimeZone: timezone
};
// 只有当 POD 不为空时才传递
if (pod && pod.trim() !== '') {
data.POD = pod;
}
$.ajax({
url: url,
type: 'GET',
dataType: 'jsonp',
jsonp: 'callback',
jsonpCallback: 'createArrivalHandoverCallback',
data: data,
success: function(response) {
if (response.code === 0) {
showNotification('添加成功', '成功添加到货交接单', 'success');
// 清空表单
$('#addArrivalHandoverForm')[0].reset();
$('#uploadedArrivalPODs').empty();
// 重新查询
queryArrivalHandover(1);
// 执行回调函数
if (typeof callback === 'function') {
callback();
}
} else {
showNotification('添加失败', response.message || '未知错误', 'error');
}
},
error: function(xhr, status, error) {
showNotification('请求失败', error, 'error');
}
});
}
// 生成S3 URL
function generateS3Url(type, handoverNumber, filename, uniqueId) {
const bucket = 'your-s3-bucket-name';
const region = 'us-east-1';
const key = `${type}/${handoverNumber}/${uniqueId}/${filename}`;
return `https://${bucket}.s3.${region}.amazonaws.com/${key}`;
}
// 上传文件
function uploadFilesWithForm(files, type, handoverNumber, uniqueIds) {
// 这里需要实现实际的文件上传逻辑
// 由于JSONP不支持POST这里只是占位符
console.log('Uploading files:', files, 'for', type, handoverNumber);
}
// ============================================
// 添加出货交接单
// ============================================
$('#addShippingHandoverForm').submit(function(e) {
e.preventDefault();
let handoverNumber = $('#addShippingHandoverNumber').val().trim();
const channel = $('#addChannel').val();
const deliveryTime = $('#addDeliveryTime').val();
const files = $('#addShippingPOD')[0].files;
const podUrls = $('#addShippingPODUrls').val().trim();
const remarks = $('#addShippingRemarks').val().trim();
const timezone = $('#shippingTimezone').val();
// 验证渠道是否选择
if (!channel) {
showNotification('输入错误', '请选择渠道', 'error');
return;
}
// 生成交接单号(如果未提供)
if (!handoverNumber) {
$.ajax({
url: getBaseUrl() + '/api/shipping-handover/generate-number',
type: 'GET',
dataType: 'jsonp',
jsonp: 'callback',
jsonpCallback: 'generateShippingHandoverNumberCallback',
data: { channel: channel },
success: function(response) {
if (response.code === 0) {
handoverNumber = response.data;
$('#addShippingHandoverNumber').val(handoverNumber);
processFormWithHandoverNumber(handoverNumber, channel, deliveryTime, files, podUrls, remarks, timezone);
} else {
showNotification('生成失败', '生成交接单号失败: ' + response.message, 'error');
}
},
error: function(xhr, status, error) {
showNotification('生成失败', '生成交接单号失败: ' + error, 'error');
}
});
} else {
processFormWithHandoverNumber(handoverNumber, channel, deliveryTime, files, podUrls, remarks, timezone);
}
});
// 处理表单提交(带交接单号)
function processFormWithHandoverNumber(handoverNumber, channel, deliveryTime, files, podUrls, remarks, timezone) {
// 优先使用直接输入的POD链接
let pod = podUrls || '';
let uniqueIds = [];
// 如果有文件上传预生成S3链接
if (files.length > 0 && !pod) {
// 预生成S3链接
const preGeneratedUrls = [];
uniqueIds = [];
for (let i = 0; i < files.length; i++) {
const file = files[i];
const uniqueId = generateGuid();
uniqueIds.push(uniqueId);
const preGeneratedUrl = generateS3Url('shipping', handoverNumber, file.name, uniqueId);
preGeneratedUrls.push(preGeneratedUrl);
}
// 将预生成的链接添加到POD链接输入框
pod = preGeneratedUrls.join(',');
$('#addShippingPODUrls').val(pod);
}
// 先处理出货交接单添加不带POD
processShippingHandoverForm(handoverNumber, channel, deliveryTime, '', remarks, timezone, function() {
// 成功创建出货交接单后上传图片
if (files.length > 0) {
// 上传图片完成后再更新POD字段
setTimeout(function() {
uploadFilesWithForm(files, 'shipping', handoverNumber, uniqueIds);
// 上传完成后更新POD字段
if (pod) {
updateShippingHandoverFormPOD(handoverNumber, pod);
}
}, 1000); // 延迟1秒确保图片上传完成
} else if (pod) {
// 没有文件上传但有POD链接直接更新
updateShippingHandoverFormPOD(handoverNumber, pod);
}
});
}
// 处理出货交接单添加
function processShippingHandoverForm(handoverNumber, channel, deliveryTime, pod, remarks, timezone, callback) {
const url = getBaseUrl() + '/api/shipping-handover/create';
// 构建请求数据,只传递非空字段
const data = {
HandoverNumber: handoverNumber,
Channel: channel,
DeliveryTime: deliveryTime,
Creator: 'system',
TimeZone: timezone
};
// 只有当 POD 不为空时才传递
if (pod && pod.trim() !== '') {
data.POD = pod;
}
// 只有当 Remarks 不为空时才传递
if (remarks && remarks.trim() !== '') {
data.Remarks = remarks;
}
$.ajax({
url: url,
type: 'GET',
dataType: 'jsonp',
jsonp: 'callback',
jsonpCallback: 'createShippingHandoverCallback',
data: data,
success: function(response) {
if (response.code === 0) {
showNotification('添加成功', '成功添加出货交接单', 'success');
// 清空表单
$('#addShippingHandoverForm')[0].reset();
$('#uploadedShippingPODs').empty();
// 重新查询
queryShippingHandover(1);
// 执行回调函数
if (typeof callback === 'function') {
callback();
}
} else {
showNotification('添加失败', response.message || '未知错误', 'error');
}
},
error: function(xhr, status, error) {
showNotification('请求失败', error, 'error');
}
});
}
// 更新出货交接单POD字段
function updateShippingHandoverFormPOD(handoverNumber, pod) {
const url = getBaseUrl() + '/api/shipping-handover/update-pod';
$.ajax({
url: url,
type: 'GET',
dataType: 'jsonp',
jsonp: 'callback',
data: {
handoverNumber: handoverNumber,
pod: pod
},
success: function(response) {
if (response.code === 0) {
console.log('POD字段更新成功');
} else {
console.error('POD字段更新失败:', response.message);
}
},
error: function(xhr, status, error) {
console.error('POD字段更新请求失败:', error);
}
});
}
// ============================================
// 关联集包
// ============================================
$('#associateBagTagsForm').submit(function(e) {
e.preventDefault();
showNotification('提示', '关联集包功能', 'info');
});
// ============================================
// 查询关联集包
// ============================================
$('#queryAssociatedBagTagsForm').submit(function(e) {
e.preventDefault();
showNotification('提示', '查询关联集包功能', 'info');
});
// ============================================
// 全选到货交接单
// ============================================
$('#selectAllHandoverForms').change(function() {
$('.arrival-checkbox').prop('checked', $(this).prop('checked'));
});
// ============================================
// 全选出货交接单
// ============================================
$('#selectAllShippingHandoverForms').change(function() {
$('.shipping-checkbox').prop('checked', $(this).prop('checked'));
});
// ============================================
// 批量删除到货交接单
// ============================================
$('#deleteSelectedHandoverForms').click(function() {
const selected = $('.arrival-checkbox:checked');
if (selected.length === 0) {
showNotification('提示', '请先选择要删除的记录', 'info');
return;
}
if (!confirm('确定要删除选中的 ' + selected.length + ' 条记录吗?')) return;
showNotification('提示', '批量删除功能', 'info');
});
// ============================================
// 批量删除出货交接单
// ============================================
$('#deleteSelectedShippingHandoverForms').click(function() {
const selected = $('.shipping-checkbox:checked');
if (selected.length === 0) {
showNotification('提示', '请先选择要删除的记录', 'info');
return;
}
if (!confirm('确定要删除选中的 ' + selected.length + ' 条记录吗?')) return;
showNotification('提示', '批量删除功能', 'info');
});
// ============================================
// 集包导出Excel按钮
// ============================================
$('#exportBagTagExcelBtn').click(function() {
// 检查是否有数据
const bagTagData = window.bagTagData;
if (!bagTagData || bagTagData.length === 0) {
showNotification('提示', '没有数据可导出', 'info');
return;
}
showNotification('提示', '正在准备导出数据,请稍候...', 'info');
// 获取当前查询参数
const sortBy = $('#bagTagSortBy').val();
const sortOrder = $('#bagTagSortOrder').val();
const tagNumber = $('#bagTagNumber').val();
const channel = $('#bagTagChannel').val();
const status = $('#bagTagStatus').val();
const creator = $('#bagTagCreator').val();
const bagTagStartCreatedAt = $('#bagTagStartCreatedAt').val();
const bagTagEndCreatedAt = $('#bagTagEndCreatedAt').val();
// 封装JSONP请求为Promise
function jsonpRequest(url) {
return new Promise((resolve, reject) => {
$.ajax({
url: url,
type: 'GET',
dataType: 'jsonp',
jsonp: 'callback',
success: resolve,
error: reject
});
});
}
// 获取所有袋牌数据
async function getAllBagTagData() {
try {
// 首先获取第一页数据和总页数
const firstPageUrl = getBaseUrl() + '/api/bagtag/batch?page=1&pageSize=100&sortBy=' + sortBy + '&sortOrder=' + sortOrder + '&tagNumber=' + tagNumber + '&channel=' + channel + '&status=' + status + '&creator=' + creator + '&startCreatedAt=' + bagTagStartCreatedAt + '&endCreatedAt=' + bagTagEndCreatedAt;
const firstPageResponse = await jsonpRequest(firstPageUrl);
if (firstPageResponse.status !== 'ok') {
throw new Error('获取第一页数据失败: ' + (firstPageResponse.message || '未知错误'));
}
const totalPages = firstPageResponse.totalPages;
const allBagTagData = [...firstPageResponse.data];
// 获取剩余页的数据
for (let page = 2; page <= totalPages; page++) {
const pageUrl = getBaseUrl() + '/api/bagtag/batch?page=' + page + '&pageSize=100&sortBy=' + sortBy + '&sortOrder=' + sortOrder + '&tagNumber=' + tagNumber + '&channel=' + channel + '&status=' + status + '&creator=' + creator + '&startCreatedAt=' + bagTagStartCreatedAt + '&endCreatedAt=' + bagTagEndCreatedAt;
const pageResponse = await jsonpRequest(pageUrl);
if (pageResponse.status === 'ok') {
allBagTagData.push(...pageResponse.data);
} else {
console.warn('获取第 ' + page + ' 页数据失败,跳过该页');
}
}
return allBagTagData;
} catch (error) {
console.error('获取袋牌数据失败:', error);
throw error;
}
}
// 获取单个袋牌的运单数据
async function getWaybillsForBagTag(bagTag) {
try {
if (bagTag.waybillCount <= 0) {
return [];
}
const url = getBaseUrl() + '/api/bagtag/' + bagTag.tagNumber + '/waybills';
const response = await jsonpRequest(url);
if (response.code === 0 && response.data) {
return response.data;
} else {
console.warn('获取袋牌 ' + bagTag.tagNumber + ' 的运单数据失败:', response.message);
return [];
}
} catch (error) {
console.error('获取袋牌 ' + bagTag.tagNumber + ' 的运单数据失败:', error);
return [];
}
}
// 主导出逻辑
async function exportData() {
try {
// 获取所有袋牌数据
const allBagTagData = await getAllBagTagData();
if (allBagTagData.length === 0) {
showNotification('提示', '没有数据可导出', 'info');
return;
}
// 准备袋牌信息数据
const bagTagSheetData = allBagTagData.map(item => {
return {
'袋牌号码': item.tagNumber || '-',
'渠道': item.channelName || '-',
'状态': item.status || '-',
'创建人': item.creator || '-',
'创建时间': item.createdAt ? new Date(item.createdAt).toLocaleString() : '-',
'打开时间': item.openedAt ? new Date(item.openedAt).toLocaleString() : '-',
'关闭时间': item.closedAt ? new Date(item.closedAt).toLocaleString() : '-',
'关联运单数量': item.waybillCount || 0
};
});
// 准备小包明细数据
const waybillSheetData = [];
// 逐个获取每个袋牌的运单数据
for (const bagTag of allBagTagData) {
const waybills = await getWaybillsForBagTag(bagTag);
// 添加运单数据到导出列表
waybills.forEach(w => {
waybillSheetData.push({
'袋牌号码': bagTag.tagNumber || '-',
'尾程跟踪单号': w.FinalMileTrackingNumber || '-',
'中性面单号': w.NeutralWaybillNumber || '-',
'换单完成时间': w.ReplaceCompletedTime ? new Date(w.ReplaceCompletedTime).toLocaleString() : '-',
'提单号': w.BillOfLadingNumber || '-',
'客户简称': w.CustomerAbbreviation || '-',
'创建人': w.Creator || '-',
'创建时间': w.CreatedAt ? new Date(w.CreatedAt).toLocaleString() : '-',
'备注': w.Remark || '-'
});
});
}
// 验证数据一致性
const expectedWaybillCount = bagTagSheetData.reduce((total, item) => total + (item['关联运单数量'] || 0), 0);
const actualWaybillCount = waybillSheetData.length;
console.log('验证数据一致性:');
console.log('预期运单数量:', expectedWaybillCount);
console.log('实际运单数量:', actualWaybillCount);
if (expectedWaybillCount !== actualWaybillCount) {
console.warn('运单数量不一致,可能存在数据缺失');
showNotification('警告', '运单数量可能存在不一致,导出数据可能不完整', 'warning');
}
// 导出Excel
const wb = XLSX.utils.book_new();
// 添加袋牌信息表
const bagTagWs = XLSX.utils.json_to_sheet(bagTagSheetData);
XLSX.utils.book_append_sheet(wb, bagTagWs, '袋牌信息');
// 添加小包明细表
const waybillWs = XLSX.utils.json_to_sheet(waybillSheetData);
XLSX.utils.book_append_sheet(wb, waybillWs, '小包明细');
// 下载文件
XLSX.writeFile(wb, '集包数据.xlsx');
showNotification('导出完成', '集包数据已成功导出', 'success');
} catch (error) {
console.error('导出失败:', error);
showNotification('导出失败', error.message || '未知错误', 'error');
}
}
// 开始导出过程
exportData();
});
loadTodayUnorderedCount();
});
function generateGuid() {
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
var r = Math.random() * 16 | 0, v = c === 'x' ? r : (r & 0x3 | 0x8);
return v.toString(16);
});
}
function queryOrderLog() {
const neutralWaybillNumber = $('#orderLogNeutralWaybillNumber').val().trim();
const finalMileTrackingNumber = $('#orderLogFinalMileTrackingNumber').val().trim();
const sortBy = $('#orderLogSortBy').val();
const sortOrder = $('#orderLogSortOrder').val();
if (!neutralWaybillNumber && !finalMileTrackingNumber) {
showNotification('输入错误', '请至少输入中性面单单号或尾程单号', 'error');
return;
}
const baseUrl = getBaseUrl();
const url = baseUrl + '/api/order-log/query';
$.ajax({
url: url,
type: 'GET',
data: {
neutralWaybillNumber: neutralWaybillNumber,
finalMileTrackingNumber: finalMileTrackingNumber,
sortBy: sortBy,
sortOrder: sortOrder
},
dataType: 'jsonp',
jsonp: 'callback',
success: function(response) {
console.log('Order log query response:', response);
if (response && response.code === 0) {
displayOrderLogResult(response.data);
showNotification('查询完成', '共找到 ' + response.data.length + ' 条订单日志记录', 'success');
} else {
showNotification('查询失败', response ? response.message : '未知错误', 'error');
$('#orderLogTableBody').html('<tr><td colspan="7" class="text-center empty-state"><div class="empty-state-icon">❌</div><div class="empty-state-text">查询失败</div></td></tr>');
}
},
error: function(xhr, status, error) {
console.error('Failed to query order log:', error);
showNotification('查询失败', error, 'error');
$('#orderLogTableBody').html('<tr><td colspan="7" class="text-center empty-state"><div class="empty-state-icon">❌</div><div class="empty-state-text">查询失败</div></td></tr>');
}
});
}
function displayOrderLogResult(data) {
const tableBody = $('#orderLogTableBody');
tableBody.empty();
if (!data || data.length === 0) {
tableBody.html('<tr><td colspan="7" class="text-center empty-state"><div class="empty-state-icon">📭</div><div class="empty-state-text">没有找到相关记录</div></td></tr>');
return;
}
data.forEach(function(log) {
const row = $('<tr></tr>');
row.append('<td>' + (log.Id || '') + '</td>');
row.append('<td>' + (log.NeutralWaybillNumber || '') + '</td>');
row.append('<td>' + (log.FinalMileTrackingNumber || '') + '</td>');
row.append('<td>' + (log.OperationType || '') + '</td>');
row.append('<td>' + (log.OperationDetail || '') + '</td>');
row.append('<td>' + (log.Creator || '') + '</td>');
row.append('<td>' + (log.CreatedAt || '') + '</td>');
tableBody.append(row);
});
}
function exportOrderLogToExcel() {
showNotification('功能提示', '导出Excel功能暂未实现', 'info');
}
function queryDashboard() {
showNotification('功能提示', '数据看板查询功能暂未实现', 'info');
}
function exportDailyStatsChineseToExcel() {
showNotification('功能提示', '导出运维监控功能暂未实现', 'info');
}
function loadTodayUnorderedCount() {
$('#todayUnorderedCount').text('0');
$('#todayScanTotal').text('0');
$('#todayDate').text(new Date().toLocaleDateString('zh-CN'));
}
// 全局变量
let currentLabelReplacePage = 1;
let currentLabelScanPage = 1;
let currentArrivalHandoverPage = 1;
let currentShippingHandoverPage = 1;
let currentBagTagPage = 1;
// ============================================
// 订单查询相关函数
// ============================================
function labelReplaceCallback(response) {
console.log('Label replace query response:', response);
if (response && response.status === 'ok') {
displayLabelReplaceResult(response);
showNotification('查询完成', '共找到 ' + response.totalCount + ' 条记录', 'success');
} else {
showNotification('查询失败', response ? response.message : '未知错误', 'error');
$('#labelReplaceTableBody').html('<tr><td colspan="12" class="text-center empty-state"><div class="empty-state-icon">❌</div><div class="empty-state-text">查询失败</div></td></tr>');
}
}
function displayLabelReplaceResult(response) {
const tableBody = $('#labelReplaceTableBody');
tableBody.empty();
if (!response || !response.data || response.data.length === 0) {
tableBody.html('<tr><td colspan="12" class="text-center empty-state"><div class="empty-state-icon">📭</div><div class="empty-state-text">没有找到相关记录</div></td></tr>');
$('#labelReplacePagination').empty();
$('#labelReplacePaginationInfo').text('');
return;
}
response.data.forEach(function(item) {
const row = $('<tr></tr>');
row.append('<td>' + (item.Id || '') + '</td>');
row.append('<td>' + (item.CustomerCode || '') + '</td>');
row.append('<td>' + (item.BillOfLadingNumber || '') + '</td>');
row.append('<td>' + (item.MasterPackageNumber || '') + '</td>');
row.append('<td>' + (item.NeutralWaybillNumber || '') + '</td>');
row.append('<td>' + (item.FinalMileTrackingNumber || '') + '</td>');
row.append('<td>' + (item.BagTagNumber || '') + '</td>');
// 有无面单字段 - 根据 Label 字段判断
const hasLabel = item.Label && item.Label.trim() !== '';
if (hasLabel) {
// 检查是否为 URL 资源
if (item.Label.startsWith('http://') || item.Label.startsWith('https://')) {
const encodedUrl = encodeURIComponent(item.Label);
row.append(`<td><span class="text-success cursor-pointer" data-url="${encodedUrl}">有</span></td>`);
} else {
// 非 URL 资源,显示原数据
row.append(`<td><span class="text-success cursor-pointer" data-content="${item.Label}">有</span></td>`);
}
} else {
row.append(`<td><span class="text-muted cursor-pointer" data-content="无">无</span></td>`);
}
row.append('<td>' + (item.ReplaceStatus === 'Y' ? '正常换单' : '冻结换单') + '</td>');
row.append('<td>' + (item.ReplacedAt || '') + '</td>'); // 换单完成时间
row.append('<td>' + (item.LabelRetrievedAt || '') + '</td>'); // 标签推送时间(后端实际返回字段)
row.append('<td>' + (item.CreatedAt || '') + '</td>');
tableBody.append(row);
});
// 添加点击预览事件(使用事件委托,确保动态生成的元素也能触发)
$('#labelReplaceTableBody').off('click', '.cursor-pointer').on('click', '.cursor-pointer', function() {
// 检查是否有 data-url 属性URL 资源)
if ($(this).data('url')) {
const encodedUrl = $(this).data('url');
if (encodedUrl) {
const url = decodeURIComponent(encodedUrl);
// 直接打开 URL 资源
window.open(url, '_blank');
}
}
// 检查是否有 data-content 属性(非 URL 资源)
else if ($(this).data('content')) {
const content = $(this).data('content');
if (content) {
// 显示原数据
$('#labelPreviewContent').html(`<p class="border p-3 rounded bg-light">${content}</p>`);
const labelPreviewModal = new bootstrap.Modal(document.getElementById('labelPreviewModal'));
labelPreviewModal.show();
}
}
});
renderLabelReplacePagination(response);
}
function renderLabelReplacePagination(response) {
const pagination = $('#labelReplacePagination');
pagination.empty();
const totalPages = response.totalPages;
const currentPage = response.page;
$('#labelReplacePaginationInfo').text('第 ' + currentPage + ' 页 / 共 ' + totalPages + ' 页,总计 ' + response.totalCount + ' 条记录');
if (totalPages <= 1) return;
const ul = $('<ul class="pagination"></ul>');
// 上一页
if (currentPage > 1) {
const prevLi = $('<li class="page-item"><a class="page-link" href="#">上一页</a></li>');
prevLi.click(function(e) {
e.preventDefault();
queryLabelReplace(currentPage - 1);
});
ul.append(prevLi);
}
// 页码
const startPage = Math.max(1, currentPage - 2);
const endPage = Math.min(totalPages, currentPage + 2);
for (let i = startPage; i <= endPage; i++) {
const li = $('<li class="page-item' + (i === currentPage ? ' active' : '') + '"><a class="page-link" href="#">' + i + '</a></li>');
li.click(function(e) {
e.preventDefault();
queryLabelReplace(i);
});
ul.append(li);
}
// 下一页
if (currentPage < totalPages) {
const nextLi = $('<li class="page-item"><a class="page-link" href="#">下一页</a></li>');
nextLi.click(function(e) {
e.preventDefault();
queryLabelReplace(currentPage + 1);
});
ul.append(nextLi);
}
pagination.append(ul);
}
function queryLabelReplace(page = 1) {
currentLabelReplacePage = page;
const billOfLadingNumber = $('#billOfLadingNumber').val().trim();
const masterPackageNumber = $('#masterPackageNumber').val().trim();
const referenceNumber = $('#referenceNumber').val().trim();
const neutralWaybillNumber = $('#neutralWaybillNumber').val().trim();
const finalMileTrackingNumber = $('#finalMileTrackingNumber').val().trim();
const replaceStatus = $('#replaceStatus').val();
const customerId = $('#customerId').val();
const startCreatedAt = $('#startCreatedAt').val();
const endCreatedAt = $('#endCreatedAt').val();
const startReplacedAt = $('#startReplacedAt').val();
const endReplacedAt = $('#endReplacedAt').val();
const sortBy = $('#sortBy').val();
const sortOrder = $('#sortOrder').val();
let pageSize = $('#pageSize').val();
if (pageSize === 'custom') {
pageSize = $('#customPageSize').val() || 500;
}
const baseUrl = getBaseUrl();
const url = baseUrl + '/api/Label/label-replace/batch';
const data = {
page: page,
pageSize: pageSize,
sortBy: sortBy,
sortOrder: sortOrder
};
if (billOfLadingNumber) data.billOfLadingNumber = billOfLadingNumber;
if (masterPackageNumber) data.masterPackageNumber = masterPackageNumber;
if (referenceNumber) data.referenceNumber = referenceNumber;
if (neutralWaybillNumber) data.neutralWaybillNumber = neutralWaybillNumber;
if (finalMileTrackingNumber) data.finalMileTrackingNumber = finalMileTrackingNumber;
if (replaceStatus) data.replaceStatus = replaceStatus;
if (customerId) data.customerId = customerId;
if (startCreatedAt) data.startCreatedAt = startCreatedAt;
if (endCreatedAt) data.endCreatedAt = endCreatedAt;
if (startReplacedAt) data.startReplacedAt = startReplacedAt;
if (endReplacedAt) data.endReplacedAt = endReplacedAt;
$('#labelReplaceTableBody').html('<tr><td colspan="12" class="text-center"><div class="loading-container"><div class="spinner-custom"></div><div class="loading-text">正在查询...</div></div></td></tr>');
$.ajax({
url: url,
type: 'GET',
data: data,
dataType: 'jsonp',
jsonp: 'callback',
jsonpCallback: 'labelReplaceCallback',
success: function(response) {
// 回调函数已经处理
},
error: function(xhr, status, error) {
console.error('Failed to query label replace:', error);
showNotification('查询失败', error, 'error');
$('#labelReplaceTableBody').html('<tr><td colspan="12" class="text-center empty-state"><div class="empty-state-icon">❌</div><div class="empty-state-text">查询失败</div></td></tr>');
}
});
}
function resetLabelReplaceForm() {
$('#billOfLadingNumber').val('');
$('#masterPackageNumber').val('');
$('#referenceNumber').val('');
$('#neutralWaybillNumber').val('');
$('#finalMileTrackingNumber').val('');
$('#replaceStatus').val('');
$('#customerId').val('');
$('#startCreatedAt').val('');
$('#endCreatedAt').val('');
$('#startReplacedAt').val('');
$('#endReplacedAt').val('');
$('#sortBy').val('CreatedAt');
$('#sortOrder').val('desc');
$('#pageSize').val('500');
$('#customPageSizeContainer').hide();
$('#customPageSize').val('');
}
// ============================================
// 换单扫描记录查询相关函数
// ============================================
function labelScanCallback(response) {
console.log('Label scan query response:', response);
if (response && response.status === 'ok') {
displayLabelScanResult(response);
showNotification('查询完成', '共找到 ' + response.totalCount + ' 条记录', 'success');
} else {
showNotification('查询失败', response ? response.message : '未知错误', 'error');
$('#labelScanTableBody').html('<tr><td colspan="8" class="text-center empty-state"><div class="empty-state-icon">❌</div><div class="empty-state-text">查询失败</div></td></tr>');
}
}
function displayLabelScanResult(response) {
const tableBody = $('#labelScanTableBody');
tableBody.empty();
if (!response || !response.data || response.data.length === 0) {
tableBody.html('<tr><td colspan="8" class="text-center empty-state"><div class="empty-state-icon">📭</div><div class="empty-state-text">没有找到相关记录</div></td></tr>');
$('#labelScanPagination').empty();
$('#labelScanPaginationInfo').text('');
return;
}
response.data.forEach(function(item) {
const row = $('<tr></tr>');
row.append('<td>' + (item.Id || '') + '</td>');
row.append('<td>' + (item.CustomerCode || '') + '</td>');
row.append('<td>' + (item.NeutralWaybillNumber || '') + '</td>');
row.append('<td>' + (item.FinalMileTrackingNumber || '') + '</td>');
let resultText = '';
switch (item.Result) {
case 0: resultText = '已返回面单'; break;
case 1: resultText = '无面单数据'; break;
case 2: resultText = '无下单数据'; break;
case 3: resultText = '订单被冻结'; break;
case 4: resultText = '订单已销毁'; break;
case 5: resultText = '其他'; break;
default: resultText = item.Result;
}
row.append('<td>' + resultText + '</td>');
row.append('<td>' + (item.Description || '') + '</td>');
row.append('<td>' + (item.CreatedBy || '') + '</td>');
row.append('<td>' + (item.CreatedAt || '') + '</td>');
tableBody.append(row);
});
renderLabelScanPagination(response);
}
function renderLabelScanPagination(response) {
const pagination = $('#labelScanPagination');
pagination.empty();
const totalPages = response.totalPages;
const currentPage = response.page;
$('#labelScanPaginationInfo').text('第 ' + currentPage + ' 页 / 共 ' + totalPages + ' 页,总计 ' + response.totalCount + ' 条记录');
if (totalPages <= 1) return;
const ul = $('<ul class="pagination"></ul>');
if (currentPage > 1) {
const prevLi = $('<li class="page-item"><a class="page-link" href="#">上一页</a></li>');
prevLi.click(function(e) {
e.preventDefault();
queryLabelScan(currentPage - 1);
});
ul.append(prevLi);
}
const startPage = Math.max(1, currentPage - 2);
const endPage = Math.min(totalPages, currentPage + 2);
for (let i = startPage; i <= endPage; i++) {
const li = $('<li class="page-item' + (i === currentPage ? ' active' : '') + '"><a class="page-link" href="#">' + i + '</a></li>');
li.click(function(e) {
e.preventDefault();
queryLabelScan(i);
});
ul.append(li);
}
if (currentPage < totalPages) {
const nextLi = $('<li class="page-item"><a class="page-link" href="#">下一页</a></li>');
nextLi.click(function(e) {
e.preventDefault();
queryLabelScan(currentPage + 1);
});
ul.append(nextLi);
}
pagination.append(ul);
}
function queryLabelScan(page = 1) {
currentLabelScanPage = page;
const customerId = $('#scanCustomerId').val();
const neutralWaybillNumber = $('#scanNeutralWaybillNumber').val().trim();
const finalMileTrackingNumber = $('#scanFinalMileTrackingNumber').val().trim();
const result = $('#scanResult').val();
const startCreatedAt = $('#scanStartCreatedAt').val();
const endCreatedAt = $('#scanEndCreatedAt').val();
const sortBy = $('#scanSortBy').val();
const sortOrder = $('#scanSortOrder').val();
let pageSize = $('#scanPageSize').val();
if (pageSize === 'custom') {
pageSize = $('#customScanPageSize').val() || 500;
}
const baseUrl = getBaseUrl();
const url = baseUrl + '/api/Label/label-scan/batch';
const data = {
page: page,
pageSize: pageSize,
sortBy: sortBy,
sortOrder: sortOrder
};
if (customerId) data.customerId = customerId;
if (neutralWaybillNumber) data.neutralWaybillNumber = neutralWaybillNumber;
if (finalMileTrackingNumber) data.finalMileTrackingNumber = finalMileTrackingNumber;
if (result !== '') data.result = result;
if (startCreatedAt) data.startCreatedAt = startCreatedAt;
if (endCreatedAt) data.endCreatedAt = endCreatedAt;
$('#labelScanTableBody').html('<tr><td colspan="8" class="text-center"><div class="loading-container"><div class="spinner-custom"></div><div class="loading-text">正在查询...</div></div></td></tr>');
$.ajax({
url: url,
type: 'GET',
data: data,
dataType: 'jsonp',
jsonp: 'callback',
jsonpCallback: 'labelScanCallback',
success: function(response) {
// 回调函数已经处理
},
error: function(xhr, status, error) {
console.error('Failed to query label scan:', error);
showNotification('查询失败', error, 'error');
$('#labelScanTableBody').html('<tr><td colspan="8" class="text-center empty-state"><div class="empty-state-icon">❌</div><div class="empty-state-text">查询失败</div></td></tr>');
}
});
}
function resetLabelScanForm() {
$('#scanCustomerId').val('');
$('#scanNeutralWaybillNumber').val('');
$('#scanFinalMileTrackingNumber').val('');
$('#scanResult').val('');
$('#scanStartCreatedAt').val('');
$('#scanEndCreatedAt').val('');
$('#scanSortBy').val('CreatedAt');
$('#scanSortOrder').val('desc');
$('#scanPageSize').val('500');
$('#customScanPageSizeContainer').hide();
$('#customScanPageSize').val('');
}
// ============================================
// 到货交接单相关函数
// ============================================
function arrivalHandoverCallback(response) {
console.log('Arrival handover query response:', response);
if (response && (response.status === 'ok' || response.code === 0)) {
displayArrivalHandoverResult(response);
// 正确获取记录数量优先使用data.totalCount其次使用data.forms的长度
const count = response.data?.totalCount || response.data?.forms?.length || response.data?.length || 0;
showNotification('查询完成', '共找到 ' + count + ' 条记录', 'success');
} else {
showNotification('查询失败', response ? response.message : '未知错误', 'error');
$('#arrivalHandoverTableBody').html('<tr><td colspan="12" class="text-center empty-state"><div class="empty-state-icon">❌</div><div class="empty-state-text">查询失败</div></td></tr>');
}
}
function displayArrivalHandoverResult(response) {
const tableBody = $('#arrivalHandoverTableBody');
tableBody.empty();
// 时间戳转换函数
function formatTimestamp(timestamp) {
if (!timestamp) return '';
// 如果是数字类型,当作毫秒时间戳处理
if (typeof timestamp === 'number') {
return new Date(timestamp).toLocaleString();
}
// 如果是字符串,直接返回
return timestamp;
}
// 旧版接口返回数据在 response.data.forms 里
const data = response.data?.forms || response.data || [];
if (!data || data.length === 0) {
tableBody.html('<tr><td colspan="12" class="text-center empty-state"><div class="empty-state-icon">📭</div><div class="empty-state-text">没有找到相关记录</div></td></tr>');
$('#arrivalHandoverPagination').empty();
return;
}
data.forEach(function(item) {
const row = $('<tr></tr>');
const checkbox = $('<td><input type="checkbox" class="arrival-checkbox" value="' + item.Id + '"></td>');
row.append(checkbox);
row.append('<td>' + (item.Id || '') + '</td>');
row.append('<td>' + (item.CustomerCode || '') + '</td>');
row.append('<td>' + (item.HandoverNumber || '') + '</td>');
// 使用小写字段名并转换时间戳
row.append('<td>' + formatTimestamp(item.logisticsProviderArrivalTime || item.LogisticsProviderArrivalTime) + '</td>');
row.append('<td>' + formatTimestamp(item.receiptTime || item.ReceiptTime) + '</td>');
const podCell = $('<td></td>');
if (item.PodUrls && item.PodUrls.length > 0) {
const podLink = $('<a href="#" class="cursor-pointer">查看 ' + item.PodUrls.length + ' 张</a>');
podLink.click(function(e) {
e.preventDefault();
showPodPreview(item.PodUrls);
});
podCell.append(podLink);
} else {
podCell.text('无');
}
row.append(podCell);
row.append('<td>' + (item.Remarks || '') + '</td>');
row.append('<td>' + (item.Creator || '') + '</td>');
// 创建时间也使用时间戳转换
row.append('<td>' + formatTimestamp(item.createdAt || item.CreatedAt) + '</td>');
row.append('<td>' + (item.TimeZone || item.Timezone || '') + '</td>');
const actionCell = $('<td></td>');
const editBtn = $('<button class="btn btn-sm btn-primary me-1">编辑</button>');
editBtn.click(function() {
editArrivalHandover(item);
});
actionCell.append(editBtn);
const deleteBtn = $('<button class="btn btn-sm btn-danger">删除</button>');
deleteBtn.click(function() {
deleteArrivalHandover(item.Id);
});
actionCell.append(deleteBtn);
row.append(actionCell);
tableBody.append(row);
});
}
function queryArrivalHandover(page = 1) {
currentArrivalHandoverPage = page;
const handoverNumber = $('#handoverNumber').val().trim().replace(/\s+/g, ','); // 将多行输入转换为逗号分隔的字符串
const sortBy = $('#arrivalSortBy').val();
const sortOrder = $('#arrivalSortOrder').val();
let pageSize = $('#arrivalPageSize').val();
if (pageSize === 'custom') {
pageSize = $('#customArrivalPageSize').val() || 500;
}
const baseUrl = getBaseUrl();
const url = baseUrl + '/api/arrival-handover/list';
const data = {
page: page,
pageSize: pageSize,
sortBy: sortBy,
sortOrder: sortOrder
};
if (handoverNumber) data.handoverNumber = handoverNumber;
$('#arrivalHandoverTableBody').html('<tr><td colspan="12" class="text-center"><div class="loading-container"><div class="spinner-custom"></div><div class="loading-text">正在查询...</div></div></td></tr>');
$.ajax({
url: url,
type: 'GET',
data: data,
dataType: 'jsonp',
jsonp: 'callback',
jsonpCallback: 'arrivalHandoverCallback',
success: function(response) {
// 回调函数已经处理
},
error: function(xhr, status, error) {
console.error('Failed to query arrival handover:', error);
showNotification('查询失败', error, 'error');
$('#arrivalHandoverTableBody').html('<tr><td colspan="12" class="text-center empty-state"><div class="empty-state-icon">❌</div><div class="empty-state-text">查询失败</div></td></tr>');
}
});
}
function resetArrivalHandoverForm() {
$('#handoverNumber').val('');
$('#arrivalSortBy').val('CreatedAt');
$('#arrivalSortOrder').val('desc');
$('#arrivalPageSize').val('500');
$('#customArrivalPageSizeContainer').hide();
$('#customArrivalPageSize').val('');
}
function editArrivalHandover(item) {
$('#editArrivalId').val(item.Id);
$('#editArrivalHandoverNumber').val(item.HandoverNumber);
$('#editLogisticsProviderArrivalTime').val(item.LogisticsProviderArrivalTime?.substring(0, 16));
$('#editReceiptTime').val(item.ReceiptTime?.substring(0, 16));
$('#editArrivalPOD').val('');
$('#editArrivalPODUrls').val(item.PodUrls?.join(', ') || '');
$('#editArrivalRemarks').val(item.Remarks || '');
$('#editArrivalTimezone').val(item.Timezone || 'America/New_York');
const previewContainer = $('#editUploadedArrivalPODs');
previewContainer.empty();
if (item.PodUrls && item.PodUrls.length > 0) {
item.PodUrls.forEach(function(url) {
const img = $('<img src="' + url + '" class="img-thumbnail m-1" style="max-width: 100px; max-height: 100px;">');
previewContainer.append(img);
});
} else {
previewContainer.html('<p class="text-muted text-center">点击或粘贴图片到此处</p>');
}
const modal = new bootstrap.Modal(document.getElementById('editArrivalHandoverModal'));
modal.show();
}
function deleteArrivalHandover(id) {
if (!confirm('确定要删除这条记录吗?')) return;
const baseUrl = getBaseUrl();
const url = baseUrl + '/api/arrival-handover/delete';
$.ajax({
url: url,
type: 'POST',
data: { id: id },
dataType: 'jsonp',
jsonp: 'callback',
success: function(response) {
if (response && (response.status === 'ok' || response.code === 0)) {
showNotification('删除成功', '记录已删除', 'success');
queryArrivalHandover(currentArrivalHandoverPage);
} else {
showNotification('删除失败', response ? response.message : '未知错误', 'error');
}
},
error: function(xhr, status, error) {
console.error('Failed to delete arrival handover:', error);
showNotification('删除失败', error, 'error');
}
});
}
// ============================================
// 出货交接单相关函数
// ============================================
function shippingHandoverCallback(response) {
console.log('Shipping handover query response:', response);
if (response && (response.status === 'ok' || response.code === 0)) {
displayShippingHandoverResult(response);
// 正确获取记录数量优先使用data.totalCount其次使用data.forms的长度
const count = response.data?.totalCount || response.data?.forms?.length || response.data?.length || 0;
showNotification('查询完成', '共找到 ' + count + ' 条记录', 'success');
} else {
showNotification('查询失败', response ? response.message : '未知错误', 'error');
$('#shippingHandoverTableBody').html('<tr><td colspan="13" class="text-center empty-state"><div class="empty-state-icon">❌</div><div class="empty-state-text">查询失败</div></td></tr>');
}
}
function displayShippingHandoverResult(response) {
const tableBody = $('#shippingHandoverTableBody');
tableBody.empty();
// 时间戳转换函数
function formatTimestamp(timestamp) {
if (!timestamp) return '';
// 如果是数字类型,当作毫秒时间戳处理
if (typeof timestamp === 'number') {
return new Date(timestamp).toLocaleString();
}
// 如果是字符串,直接返回
return timestamp;
}
// 旧版接口返回数据在 response.data.forms 里
const data = response.data?.forms || response.data || [];
if (!data || data.length === 0) {
tableBody.html('<tr><td colspan="13" class="text-center empty-state"><div class="empty-state-icon">📭</div><div class="empty-state-text">没有找到相关记录</div></td></tr>');
$('#shippingHandoverPagination').empty();
return;
}
data.forEach(function(item) {
const row = $('<tr></tr>');
const checkbox = $('<td><input type="checkbox" class="shipping-checkbox" value="' + item.Id + '"></td>');
row.append(checkbox);
row.append('<td>' + (item.Id || '') + '</td>');
row.append('<td>' + (item.HandoverNumber || '') + '</td>');
row.append('<td>' + (item.BoxCount || '') + '</td>');
row.append('<td>' + (item.OrderCount || '') + '</td>');
row.append('<td>' + (item.Channel || '') + '</td>');
// 使用小写字段名并转换时间戳
row.append('<td>' + formatTimestamp(item.deliveryTime || item.DeliveryTime) + '</td>');
row.append('<td>' + (item.TimeZone || item.Timezone || '') + '</td>');
const podCell = $('<td></td>');
if (item.PodUrls && item.PodUrls.length > 0) {
const podLink = $('<a href="#" class="cursor-pointer">查看 ' + item.PodUrls.length + ' 张</a>');
podLink.click(function(e) {
e.preventDefault();
showPodPreview(item.PodUrls);
});
podCell.append(podLink);
} else {
podCell.text('无');
}
row.append(podCell);
row.append('<td>' + (item.Remarks || '') + '</td>');
row.append('<td>' + (item.Creator || '') + '</td>');
// 创建时间也使用时间戳转换
row.append('<td>' + formatTimestamp(item.createdAt || item.CreatedAt) + '</td>');
const actionCell = $('<td></td>');
const editBtn = $('<button class="btn btn-sm btn-primary me-1">编辑</button>');
editBtn.click(function() {
editShippingHandover(item);
});
actionCell.append(editBtn);
const deleteBtn = $('<button class="btn btn-sm btn-danger">删除</button>');
deleteBtn.click(function() {
deleteShippingHandover(item.Id);
});
actionCell.append(deleteBtn);
row.append(actionCell);
tableBody.append(row);
});
}
function queryShippingHandover(page = 1) {
currentShippingHandoverPage = page;
const handoverNumber = $('#shippingHandoverNumber').val().trim();
const channel = $('#shippingChannel').val();
const creator = $('#shippingCreator').val().trim();
const startDeliveryTime = $('#startDeliveryTime').val();
const endDeliveryTime = $('#endDeliveryTime').val();
const sortBy = $('#shippingSortBy').val();
const sortOrder = $('#shippingSortOrder').val();
let pageSize = $('#shippingPageSize').val();
if (pageSize === 'custom') {
pageSize = $('#customShippingPageSize').val() || 500;
}
const baseUrl = getBaseUrl();
const url = baseUrl + '/api/shipping-handover/list';
const data = {
page: page,
pageSize: pageSize,
sortBy: sortBy,
sortOrder: sortOrder
};
if (handoverNumber) data.handoverNumber = handoverNumber;
if (channel) data.channel = channel;
if (creator) data.creator = creator;
if (startDeliveryTime) data.startDeliveryTime = startDeliveryTime;
if (endDeliveryTime) data.endDeliveryTime = endDeliveryTime;
$('#shippingHandoverTableBody').html('<tr><td colspan="13" class="text-center"><div class="loading-container"><div class="spinner-custom"></div><div class="loading-text">正在查询...</div></div></td></tr>');
$.ajax({
url: url,
type: 'GET',
data: data,
dataType: 'jsonp',
jsonp: 'callback',
jsonpCallback: 'shippingHandoverCallback',
success: function(response) {
// 回调函数已经处理
},
error: function(xhr, status, error) {
console.error('Failed to query shipping handover:', error);
showNotification('查询失败', error, 'error');
$('#shippingHandoverTableBody').html('<tr><td colspan="13" class="text-center empty-state"><div class="empty-state-icon">❌</div><div class="empty-state-text">查询失败</div></td></tr>');
}
});
}
function resetShippingHandoverForm() {
$('#shippingHandoverNumber').val('');
$('#shippingChannel').val('');
$('#shippingCreator').val('');
$('#startDeliveryTime').val('');
$('#endDeliveryTime').val('');
$('#shippingSortBy').val('CreatedAt');
$('#shippingSortOrder').val('desc');
$('#shippingPageSize').val('500');
$('#customShippingPageSizeContainer').hide();
$('#customShippingPageSize').val('');
}
function editShippingHandover(item) {
$('#editId').val(item.Id);
$('#editDeliveryTime').val(item.DeliveryTime?.substring(0, 16));
$('#editTimezone').val(item.Timezone || 'America/New_York');
$('#editShippingPOD').val('');
$('#editShippingPODUrls').val(item.PodUrls?.join(', ') || '');
$('#editRemarks').val(item.Remarks || '');
const previewContainer = $('#editUploadedShippingPODs');
previewContainer.empty();
if (item.PodUrls && item.PodUrls.length > 0) {
item.PodUrls.forEach(function(url) {
const img = $('<img src="' + url + '" class="img-thumbnail m-1" style="max-width: 100px; max-height: 100px;">');
previewContainer.append(img);
});
} else {
previewContainer.html('<p class="text-muted text-center">点击或粘贴图片到此处</p>');
}
const modal = new bootstrap.Modal(document.getElementById('editShippingHandoverModal'));
modal.show();
}
function deleteShippingHandover(id) {
if (!confirm('确定要删除这条记录吗?')) return;
const baseUrl = getBaseUrl();
const url = baseUrl + '/api/shipping-handover/delete';
$.ajax({
url: url,
type: 'POST',
data: { id: id },
dataType: 'jsonp',
jsonp: 'callback',
success: function(response) {
if (response && (response.status === 'ok' || response.code === 0)) {
showNotification('删除成功', '记录已删除', 'success');
queryShippingHandover(currentShippingHandoverPage);
} else {
showNotification('删除失败', response ? response.message : '未知错误', 'error');
}
},
error: function(xhr, status, error) {
console.error('Failed to delete shipping handover:', error);
showNotification('删除失败', error, 'error');
}
});
}
// ============================================
// 集包数据查询相关函数
// ============================================
function bagTagCallback(response) {
console.log('Bag tag query response:', response);
if (response && (response.status === 'ok' || response.code === 0)) {
displayBagTagResult(response);
showNotification('查询完成', '共找到 ' + (response.totalCount || response.data?.length || 0) + ' 条记录', 'success');
} else {
showNotification('查询失败', response ? response.message : '未知错误', 'error');
$('#bagTagTableBody').html('<tr><td colspan="10" class="text-center empty-state"><div class="empty-state-icon">❌</div><div class="empty-state-text">查询失败</div></td></tr>');
}
}
function displayBagTagResult(response) {
const tableBody = $('#bagTagTableBody');
tableBody.empty();
const data = response.data || [];
// 存储数据到全局变量,用于导出
window.bagTagData = data;
if (!data || data.length === 0) {
tableBody.html('<tr><td colspan="10" class="text-center empty-state"><div class="empty-state-icon">📭</div><div class="empty-state-text">没有找到相关记录</div></td></tr>');
$('#bagTagPagination').empty();
return;
}
data.forEach(function(item) {
// 主行
const row = $('<tr class="bag-tag-row"></tr>');
row.append(`<td>${item.id || '-'}</td>`);
row.append(`<td>${item.tagNumber || '-'}</td>`);
row.append(`<td>${item.channelName || '-'}</td>`);
row.append(`<td>${item.status || '-'}</td>`);
row.append(`<td>${item.creator || '-'}</td>`);
row.append(`<td>${item.createdAt ? new Date(item.createdAt).toLocaleString() : '-'}</td>`);
row.append(`<td>${item.openedAt ? new Date(item.openedAt).toLocaleString() : '-'}</td>`);
row.append(`<td>${item.closedAt ? new Date(item.closedAt).toLocaleString() : '-'}</td>`);
row.append(`<td>${item.waybillCount || 0}</td>`);
row.append(`<td><button class="btn btn-sm btn-primary view-waybills" data-tag-number="${item.tagNumber}">${item.waybillCount > 0 ? '查看运单' : '无运单'}</button></td>`);
tableBody.append(row);
// 运单明细行(默认隐藏,动态加载)
const waybillRow = $('<tr class="waybill-detail-row" style="display: none;"></tr>');
waybillRow.append(`<td colspan="10">
<div class="p-3 bg-light rounded">
<h6 class="mb-2">袋牌 ${item.tagNumber} 的运单明细</h6>
<div class="table-responsive" id="waybill-content-${item.tagNumber}">
<div class="text-center p-3">加载中...</div>
</div>
</div>
</td>`);
tableBody.append(waybillRow);
});
// 绑定查看运单按钮事件
$('.view-waybills').click(function() {
const tagNumber = $(this).data('tag-number');
const row = $(this).closest('.bag-tag-row');
const waybillRow = row.next('.waybill-detail-row');
if (waybillRow.length > 0) {
if (waybillRow.is(':visible')) {
waybillRow.hide();
$(this).text('查看运单');
} else {
waybillRow.show();
$(this).text('隐藏运单');
// 加载运单数据
loadWaybillDetails(tagNumber);
}
}
});
}
// 加载运单明细
function loadWaybillDetails(tagNumber) {
const contentDiv = $('#waybill-content-' + tagNumber);
if (contentDiv.data('loaded')) {
return; // 已经加载过,不需要重复加载
}
const baseUrl = getBaseUrl();
const url = baseUrl + '/api/bagtag/' + tagNumber + '/waybills';
$.ajax({
url: url,
type: 'GET',
dataType: 'jsonp',
jsonp: 'callback',
success: function(response) {
if (response.status === 'ok') {
const waybills = response.data;
let html = '<table class="table table-sm table-bordered">';
html += '<thead><tr><th>中性面单单号</th><th>尾程单号</th><th>客户代码</th><th>创建时间</th></tr></thead>';
html += '<tbody>';
if (waybills.length === 0) {
html += '<tr><td colspan="4" class="text-center">暂无运单数据</td></tr>';
} else {
waybills.forEach(waybill => {
html += `<tr>
<td>${waybill.neutralWaybillNumber || '-'}</td>
<td>${waybill.finalMileTrackingNumber || '-'}</td>
<td>${waybill.customerCode || '-'}</td>
<td>${waybill.createdAt ? new Date(waybill.createdAt).toLocaleString() : '-'}</td>
</tr>`;
});
}
html += '</tbody></table>';
contentDiv.html(html);
contentDiv.data('loaded', true);
} else {
contentDiv.html('<div class="text-center text-danger">加载运单明细失败: ' + response.message + '</div>');
}
},
error: function(xhr, status, error) {
contentDiv.html('<div class="text-center text-danger">加载运单明细失败: ' + error + '</div>');
}
});
}
function queryBagTag(page = 1) {
currentBagTagPage = page;
const bagTagNumber = $('#bagTagNumber').val().trim();
const channel = $('#bagTagChannel').val();
const status = $('#bagTagStatus').val();
const creator = $('#bagTagCreator').val().trim();
const startCreatedAt = $('#bagTagStartCreatedAt').val();
const endCreatedAt = $('#bagTagEndCreatedAt').val();
const sortBy = $('#bagTagSortBy').val();
const sortOrder = $('#bagTagSortOrder').val();
let pageSize = $('#bagTagPageSize').val();
if (pageSize === 'custom') {
pageSize = $('#customBagTagPageSize').val() || 500;
}
const baseUrl = getBaseUrl();
const url = baseUrl + '/api/bagtag/batch';
const data = {
page: page,
pageSize: pageSize,
sortBy: sortBy,
sortOrder: sortOrder
};
if (bagTagNumber) data.tagNumber = bagTagNumber;
if (channel) data.channel = channel;
if (status) data.status = status;
if (creator) data.creator = creator;
if (startCreatedAt) data.startCreatedAt = startCreatedAt;
if (endCreatedAt) data.endCreatedAt = endCreatedAt;
$('#bagTagTableBody').html('<tr><td colspan="10" class="text-center"><div class="loading-container"><div class="spinner-custom"></div><div class="loading-text">正在查询...</div></div></td></tr>');
$.ajax({
url: url,
type: 'GET',
data: data,
dataType: 'jsonp',
jsonp: 'callback',
jsonpCallback: 'bagTagCallback',
success: function(response) {
// 回调函数已经处理
},
error: function(xhr, status, error) {
console.error('Failed to query bag tag:', error);
showNotification('查询失败', error, 'error');
$('#bagTagTableBody').html('<tr><td colspan="10" class="text-center empty-state"><div class="empty-state-icon">❌</div><div class="empty-state-text">查询失败</div></td></tr>');
}
});
}
function resetBagTagForm() {
$('#bagTagNumber').val('');
$('#bagTagChannel').val('');
$('#bagTagStatus').val('');
$('#bagTagCreator').val('');
$('#bagTagStartCreatedAt').val('');
$('#bagTagEndCreatedAt').val('');
$('#bagTagSortBy').val('CreatedAt');
$('#bagTagSortOrder').val('desc');
$('#bagTagPageSize').val('500');
$('#customBagTagPageSizeContainer').hide();
$('#customBagTagPageSize').val('');
}
// ============================================
// 数据看板查询
// ============================================
function queryDashboard() {
const arrivalNumber = $('#arrivalNumber').val().trim();
const startDate = $('#dashboardStartDate').val();
const endDate = $('#dashboardEndDate').val();
const customerId = $('#dashboardCustomerId').val();
const baseUrl = getBaseUrl();
const url = baseUrl + '/api/dashboard/query';
$.ajax({
url: url,
type: 'GET',
data: {
arrivalNumber: arrivalNumber,
startDate: startDate,
endDate: endDate,
customerId: customerId
},
dataType: 'jsonp',
jsonp: 'callback',
success: function(response) {
console.log('Dashboard query response:', response);
if (response && response.code === 0) {
displayDataDashboardResult(response.data);
loadTodayUnorderedCount();
showNotification('查询完成', `共找到 ${response.data.length} 条记录`, 'success');
} else {
showNotification('查询失败', response ? response.message : '未知错误', 'error');
$('#dataDashboardTableBody').html('<tr><td colspan="10" class="text-center empty-state"><div class="empty-state-icon">❌</div><div class="empty-state-text">查询失败</div></td></tr>');
}
},
error: function(xhr, status, error) {
console.error('Failed to query dashboard:', error);
showNotification('查询失败', error, 'error');
$('#dataDashboardTableBody').html('<tr><td colspan="10" class="text-center empty-state"><div class="empty-state-icon">❌</div><div class="empty-state-text">查询失败</div></td></tr>');
}
});
}
// 展示数据看板查询结果
function displayDataDashboardResult(data) {
const tableBody = $('#dataDashboardTableBody');
tableBody.empty();
if (!data || data.length === 0) {
tableBody.html('<tr><td colspan="10" class="text-center empty-state"><div class="empty-state-icon">📭</div><div class="empty-state-text">没有找到相关记录</div></td></tr>');
return;
}
data.forEach(item => {
const row = $('<tr></tr>');
row.append(`<td>${item.CustomerCode || ''}</td>`);
row.append(`<td>${item.LabelRate || '0%'}</td>`);
row.append(`<td>${item.ReplacePendingCount || 0}</td>`);
row.append(`<td>${item.BillOfLadingNumber || ''}</td>`);
row.append(`<td>${item.MasterPackageNumber || ''}</td>`);
row.append(`<td>${item.ArrivalOrderCount || 0}</td>`);
row.append(`<td>${item.ReplaceCompletedCount || 0}</td>`);
row.append(`<td>${item.NoLabelDataCount || 0}</td>`);
const arrivalTime = item.ArrivalTime ? item.ArrivalTime.replace('T', ' ') : '';
row.append(`<td>${arrivalTime}</td>`);
row.append(`<td><button class="btn btn-sm btn-primary viewDetails" data-arrival="${item.ArrivalNumber || ''}">查看详情</button></td>`);
tableBody.append(row);
});
$('.viewDetails').click(function() {
const arrival = $(this).data('arrival');
showNotification('提示', `查看交接单 ${arrival} 的详情`, 'info');
});
}
// 加载当天扫描后未下单数量
function loadTodayUnorderedCount() {
const baseUrl = getBaseUrl();
const url = baseUrl + '/api/dashboard/today-unordered';
$.ajax({
url: url,
type: 'GET',
dataType: 'jsonp',
jsonp: 'callback',
success: function(response) {
console.log('Today unordered count response:', response);
if (response && response.code === 0) {
const data = response.data;
$('#todayUnorderedCount').text(data.UnorderedCount || 0);
$('#todayScanTotal').text(data.TotalCount || 0);
$('#todayDate').text(data.Date || '');
}
},
error: function(xhr, status, error) {
console.error('Failed to load today unordered count:', error);
}
});
}
function exportDashboardToExcel() {
let dashboardType, billOfLadingNumber, masterPackageNumber, startDate, endDate, customerId;
if ($('#bill-of-lading-tab').hasClass('active')) {
dashboardType = 'billOfLading';
billOfLadingNumber = $('#billOfLadingNumber').val().trim();
startDate = $('#billOfLadingStartDate').val();
endDate = $('#billOfLadingEndDate').val();
customerId = $('#billOfLadingCustomerId').val();
masterPackageNumber = '';
} else if ($('#master-package-tab').hasClass('active')) {
dashboardType = 'masterPackage';
masterPackageNumber = $('#masterPackageNumber').val().trim();
startDate = $('#masterPackageStartDate').val();
endDate = $('#masterPackageEndDate').val();
customerId = $('#masterPackageCustomerId').val();
billOfLadingNumber = '';
} else {
dashboardType = 'billOfLading';
billOfLadingNumber = $('#billOfLadingNumber').val().trim();
startDate = $('#billOfLadingStartDate').val();
endDate = $('#billOfLadingEndDate').val();
customerId = $('#billOfLadingCustomerId').val();
masterPackageNumber = '';
}
if (dashboardType === 'billOfLading' && !billOfLadingNumber && dashboardType === 'masterPackage' && !masterPackageNumber) {
showNotification('输入错误', '请输入提单号或大箱号', 'error');
return;
}
const baseUrl = getBaseUrl();
const url = baseUrl + '/api/dashboard/export';
let exportUrl = url + '?type=' + encodeURIComponent(dashboardType);
if (billOfLadingNumber) exportUrl += '&billOfLadingNumber=' + encodeURIComponent(billOfLadingNumber);
if (masterPackageNumber) exportUrl += '&masterPackageNumber=' + encodeURIComponent(masterPackageNumber);
if (startDate) exportUrl += '&startDate=' + encodeURIComponent(startDate);
if (endDate) exportUrl += '&endDate=' + encodeURIComponent(endDate);
if (customerId) exportUrl += '&customerId=' + encodeURIComponent(customerId);
console.log('Exporting dashboard with URL:', exportUrl);
console.log('Export parameters:', {
dashboardType: dashboardType,
billOfLadingNumber: billOfLadingNumber,
masterPackageNumber: masterPackageNumber,
startDate: startDate,
endDate: endDate,
customerId: customerId
});
showNotification('提示', '正在导出数据...', 'info');
window.open(exportUrl, '_blank');
}
// 导出运维监控数据Excel
function exportDailyStatsChineseToExcel() {
const baseUrl = getBaseUrl();
const url = baseUrl + '/api/dashboard/daily-stats-chinese/export-excel';
// 打开导出链接
window.open(url, '_blank');
}
// ============================================
// POD预览相关函数
// ============================================
function showPodPreview(urls) {
const content = $('#podPreviewContent');
content.empty();
if (!urls || urls.length === 0) {
content.html('<p class="text-center text-muted">没有图片</p>');
} else {
urls.forEach(function(url) {
const img = $('<img src="' + url + '" class="img-fluid mb-3">');
content.append(img);
});
}
const modal = new bootstrap.Modal(document.getElementById('podPreviewModal'));
modal.show();
}
// ============================================
// 订单导入相关函数
// ============================================
function importCallback(response) {
console.log('Import response:', response);
if (response && (response.status === 'ok' || response.code === 0)) {
showNotification('导入成功', '成功导入 ' + (response.successCount || 0) + ' 条记录', 'success');
if (response.data && response.data.length > 0) {
displayImportResult(response.data);
$('#importResult').show();
}
} else {
showNotification('导入失败', response ? response.message : '未知错误', 'error');
}
}
function displayImportResult(data) {
const tableBody = $('#importResultTableBody');
tableBody.empty();
let totalCount = 0;
let successCount = 0;
let failedCount = 0;
data.forEach(function(item) {
totalCount++;
const isSuccess = item.Status === 'ok'; // 后端用Status字段判断值为ok表示成功
if (isSuccess) {
successCount++;
} else {
failedCount++;
}
const row = $('<tr class="' + (isSuccess ? 'table-success' : 'table-danger') + '"></tr>');
row.append('<td>' + (item.BillOfLadingNumber || '') + '</td>');
row.append('<td>' + (item.MasterPackageNumber || '') + '</td>');
row.append('<td>' + (item.NeutralWaybillNumber || '') + '</td>');
row.append('<td>' + (item.FinalMileTrackingNumber || '') + '</td>');
row.append('<td>' + (isSuccess ? '成功' : '失败') + '</td>');
row.append('<td>' + (item.Message || '') + '</td>');
const labelCell = $('<td></td>');
if (item.Label) { // 后端返回Label字段不是LabelUrl
const labelLink = $('<a href="#" class="cursor-pointer">查看标签</a>');
labelLink.click(function(e) {
e.preventDefault();
showLabelPreview(item.Label);
});
labelCell.append(labelLink);
} else {
labelCell.text('无');
}
row.append(labelCell);
tableBody.append(row);
});
$('#resultTotalCount').text(totalCount);
$('#resultSuccessCount').text(successCount);
$('#resultFailedCount').text(failedCount);
$('#resultSuccessRate').text(totalCount > 0 ? Math.round(successCount / totalCount * 100) + '%' : '0%');
}
function showLabelPreview(url) {
const content = $('#labelPreviewContent');
content.empty();
const img = $('<img src="' + url + '" class="img-fluid">');
content.append(img);
const modal = new bootstrap.Modal(document.getElementById('labelPreviewModal'));
modal.show();
}
// ============================================
// 导出Excel相关函数
// ============================================
function exportLabelReplaceToExcel() {
const billOfLadingNumber = $('#billOfLadingNumber').val().trim();
const masterPackageNumber = $('#masterPackageNumber').val().trim();
const referenceNumber = $('#referenceNumber').val().trim();
const neutralWaybillNumber = $('#neutralWaybillNumber').val().trim();
const finalMileTrackingNumber = $('#finalMileTrackingNumber').val().trim();
const replaceStatus = $('#replaceStatus').val();
const customerId = $('#customerId').val();
const startCreatedAt = $('#startCreatedAt').val();
const endCreatedAt = $('#endCreatedAt').val();
const startReplacedAt = $('#startReplacedAt').val();
const endReplacedAt = $('#endReplacedAt').val();
const sortBy = $('#sortBy').val();
const sortOrder = $('#sortOrder').val();
const baseUrl = getBaseUrl();
let url = baseUrl + '/api/Label/label-replace/export-excel?';
const params = [];
if (billOfLadingNumber) params.push('billOfLadingNumber=' + encodeURIComponent(billOfLadingNumber));
if (masterPackageNumber) params.push('masterPackageNumber=' + encodeURIComponent(masterPackageNumber));
if (referenceNumber) params.push('referenceNumber=' + encodeURIComponent(referenceNumber));
if (neutralWaybillNumber) params.push('neutralWaybillNumber=' + encodeURIComponent(neutralWaybillNumber));
if (finalMileTrackingNumber) params.push('finalMileTrackingNumber=' + encodeURIComponent(finalMileTrackingNumber));
if (replaceStatus) params.push('replaceStatus=' + encodeURIComponent(replaceStatus));
if (customerId) params.push('customerId=' + encodeURIComponent(customerId));
if (startCreatedAt) params.push('startCreatedAt=' + encodeURIComponent(startCreatedAt));
if (endCreatedAt) params.push('endCreatedAt=' + encodeURIComponent(endCreatedAt));
if (startReplacedAt) params.push('startReplacedAt=' + encodeURIComponent(startReplacedAt));
if (endReplacedAt) params.push('endReplacedAt=' + encodeURIComponent(endReplacedAt));
if (sortBy) params.push('sortBy=' + encodeURIComponent(sortBy));
if (sortOrder) params.push('sortOrder=' + encodeURIComponent(sortOrder));
window.location.href = url + params.join('&');
}
function exportLabelScanToExcel() {
const customerId = $('#scanCustomerId').val();
const neutralWaybillNumber = $('#scanNeutralWaybillNumber').val().trim();
const finalMileTrackingNumber = $('#scanFinalMileTrackingNumber').val().trim();
const result = $('#scanResult').val();
const startCreatedAt = $('#scanStartCreatedAt').val();
const endCreatedAt = $('#scanEndCreatedAt').val();
const baseUrl = getBaseUrl();
let url = baseUrl + '/api/Label/label-scan/export-excel?';
const params = [];
if (customerId) params.push('customerId=' + encodeURIComponent(customerId));
if (neutralWaybillNumber) params.push('neutralWaybillNumber=' + encodeURIComponent(neutralWaybillNumber));
if (finalMileTrackingNumber) params.push('finalMileTrackingNumber=' + encodeURIComponent(finalMileTrackingNumber));
if (result) params.push('result=' + encodeURIComponent(result));
if (startCreatedAt) params.push('startCreatedAt=' + encodeURIComponent(startCreatedAt));
if (endCreatedAt) params.push('endCreatedAt=' + encodeURIComponent(endCreatedAt));
window.location.href = url + params.join('&');
}
// ============================================
// 订单导入相关函数
// ============================================
// 存储客户信息
let customerInfo = {
id: '',
code: '',
apiKey: ''
};
// 获取客户API接口地址
function getApiUrlForCustomerApi() {
const environment = $('#environment').val();
let apiUrl = '';
switch (environment) {
case 'local':
apiUrl = 'http://localhost:5002/api/Tag/customer-api';
break;
case 'test':
apiUrl = 'http://172.232.21.79:5002/api/Tag/customer-api';
break;
case 'production':
apiUrl = 'https://lr.tooexp.com/api/Tag/customer-api';
break;
default:
apiUrl = 'http://172.232.21.79:5002/api/Tag/customer-api';
}
return apiUrl;
}
// 获取客户API密钥
function getCustomerApiKey(customerId, customerCode) {
customerInfo.id = customerId;
customerInfo.code = customerCode;
if (!customerId) {
customerInfo.apiKey = '';
return;
}
const url = getApiUrlForCustomerApi();
$.ajax({
url: url,
type: 'GET',
data: {
customerId: customerId
},
dataType: 'jsonp',
jsonp: 'callback',
timeout: 5000,
success: function(response) {
console.log('Customer API response:', response);
if (response && response.status === 'ok' && response.data) {
customerInfo.apiKey = response.data.ApiKey || response.data.apiKey || '';
console.log('Customer API key retrieved successfully:', customerInfo.apiKey);
} else {
customerInfo.apiKey = '';
console.warn('Failed to retrieve customer API key:', response);
}
},
error: function(xhr, status, error) {
console.error('Failed to get customer API key:', error, status);
customerInfo.apiKey = '';
}
});
}
// 更新导入进度
function updateProgress(processed, total) {
const percentage = Math.round((processed / total) * 100);
$('#importProgress .progress-bar').css('width', percentage + '%');
$('#totalOrders').text(total); // 更新总订单数
$('#processedOrders').text(processed); // 更新已处理数
$('#progressPercentage').text(percentage + '%'); // 更新进度百分比
}
// 下载Excel模板
function downloadTemplate() {
// 创建工作簿
const wb = XLSX.utils.book_new();
// 创建工作表数据
const headers = [
'BillOfLadingNumber', // 提单号
'MasterPackageNumber', // 大包号
'NeutralWaybillNumber', // 中性面单单号(必填)
'FinalMileTrackingNumber', // 尾程单号
'Label' // 标签内容(可选)
];
// 创建工作表
const ws = XLSX.utils.aoa_to_sheet([headers]);
// 添加工作表到工作簿
XLSX.utils.book_append_sheet(wb, ws, '订单模板');
// 生成Excel文件并下载
XLSX.writeFile(wb, '订单导入模板.xlsx');
}
// 导入订单
function importOrders() {
const customerId = $('#importCustomerId').val();
const labelMapping = $('#labelFilenameMapping').val();
const fileInput = $('#excelFile')[0];
if (!customerId) {
showNotification('输入错误', '请选择客户', 'error');
return;
}
if (!fileInput.files || fileInput.files.length === 0) {
showNotification('输入错误', '请选择Excel文件', 'error');
return;
}
const file = fileInput.files[0];
const reader = new FileReader();
reader.onload = function(e) {
try {
const data = new Uint8Array(e.target.result);
const workbook = XLSX.read(data, { type: 'array' });
const firstSheetName = workbook.SheetNames[0];
const worksheet = workbook.Sheets[firstSheetName];
const jsonData = XLSX.utils.sheet_to_json(worksheet);
if (jsonData.length === 0) {
showNotification('错误', 'Excel文件中没有数据', 'error');
return;
}
// 处理订单数据
const orders = processOrders(jsonData, labelMapping);
// 显示进度
$('#importProgress').show();
$('#importResult').hide();
// 调用API导入订单
importOrdersToApi(orders);
} catch (error) {
showNotification('错误', '解析Excel文件失败: ' + error.message, 'error');
}
};
reader.onerror = function() {
showNotification('错误', '读取文件失败', 'error');
};
reader.readAsArrayBuffer(file);
}
// 处理订单数据
function processOrders(jsonData, labelMapping) {
const orders = [];
const fixedDate = '20260302';
const maxUrlLength = 2048; // URL最大长度限制
jsonData.forEach(row => {
const order = {
BillOfLadingNumber: row.BillOfLadingNumber || null,
MasterPackageNumber: row.MasterPackageNumber || null,
NeutralWaybillNumber: row.NeutralWaybillNumber || '',
FinalMileTrackingNumber: row.FinalMileTrackingNumber || null,
Label: row.Label || null,
ReplaceStatus: 'Y'
};
// 生成标签URL
if (!order.Label && labelMapping !== 'none') {
let filename = '';
if (labelMapping === 'neutralWaybill' && order.NeutralWaybillNumber) {
filename = order.NeutralWaybillNumber;
} else if (labelMapping === 'finalMileTracking' && order.FinalMileTrackingNumber) {
filename = order.FinalMileTrackingNumber;
}
if (filename) {
// 生成URL
let url = `http://172.232.21.79/api-lable/pdf/${fixedDate}/${filename}.pdf`;
// 检查URL有效性和长度
if (isValidUrl(url) && url.length <= maxUrlLength) {
order.Label = url;
}
}
}
orders.push(order);
});
return orders;
}
// 验证URL有效性
function isValidUrl(url) {
try {
new URL(url);
return true;
} catch (error) {
return false;
}
}
// 调用API导入订单
function importOrdersToApi(orders) {
// 使用从customer_apis表获取的API密钥
const apiKey = customerInfo.apiKey;
const customerCode = customerInfo.code;
// 验证API密钥和客户代码
if (!apiKey || !customerCode) {
// 尝试重新获取API密钥
const customerId = $('#importCustomerId').val();
const selectedCustomerCode = $('#importCustomerId').find('option:selected').text();
if (customerId) {
// 同步获取API密钥
$.ajax({
url: getApiUrlForCustomerApi(),
type: 'GET',
data: {
customerId: customerId
},
dataType: 'jsonp',
jsonp: 'callback',
async: false,
success: function(response) {
if (response.status === 'ok' && response.data) {
customerInfo.apiKey = response.data.ApiKey;
customerInfo.code = selectedCustomerCode;
customerInfo.id = customerId;
console.log('Customer API key retrieved successfully');
} else {
customerInfo.apiKey = '';
console.error('Failed to retrieve customer API key:', response.message);
}
},
error: function(xhr, status, error) {
console.error('Failed to get customer API key:', error);
customerInfo.apiKey = '';
}
});
// 再次检查
if (!customerInfo.apiKey || !customerInfo.code) {
showNotification('错误', '无法获取客户API密钥或客户代码请重新选择客户', 'error');
return;
}
} else {
showNotification('错误', '请选择客户', 'error');
return;
}
}
const environment = $('#environment').val();
let apiUrl = '';
switch (environment) {
case 'local':
apiUrl = 'http://localhost:5002/api/tag/order-import';
break;
case 'test':
apiUrl = 'http://172.232.21.79:5002/api/tag/order-import';
break;
case 'production':
apiUrl = 'https://lr.tooexp.com/api/tag/order-import';
break;
default:
apiUrl = 'http://172.232.21.79:5002/api/tag/order-import';
}
// 分批处理订单每批5个减少URL长度
const batchSize = 5;
const batches = [];
for (let i = 0; i < orders.length; i += batchSize) {
batches.push(orders.slice(i, i + batchSize));
}
// 存储所有批次的结果
const allResults = {
Status: 'ok',
TotalCount: 0,
SuccessCount: 0,
FailedCount: 0,
Results: []
};
let processedBatches = 0;
let processedOrders = 0;
const totalBatches = batches.length;
// 处理每个批次
batches.forEach(function(batch, batchIndex) {
// 使用JSONP进行跨域请求
const callbackName = 'jsonpCallback_' + Date.now() + '_' + batchIndex;
window[callbackName] = function(response) {
// 处理批次响应
if (response.Status === 'ok') {
allResults.TotalCount += response.TotalCount;
allResults.SuccessCount += response.SuccessCount;
allResults.FailedCount += response.FailedCount;
allResults.Results = allResults.Results.concat(response.Results);
}
processedBatches++;
processedOrders += batch.length;
// 更新进度
updateProgress(processedOrders, orders.length);
// 检查是否所有批次都已处理完成
if (processedBatches === totalBatches) {
$('#importProgress').hide();
if (allResults.Status === 'ok') {
showNotification('导入完成', `成功导入 ${allResults.SuccessCount} 条,失败 ${allResults.FailedCount}`, allResults.FailedCount > 0 ? 'warning' : 'success');
// 显示导入结果
displayImportResult(allResults.Results);
$('#importResult').show();
} else {
showNotification('导入失败', allResults.Message || '未知错误', 'error');
}
}
delete window[callbackName];
};
// 构建请求URL
let url = apiUrl + '?callback=' + callbackName;
url += '&customerCode=' + encodeURIComponent(customerInfo.code); // 只需要customerCode不需要customerId
url += '&apiKey=' + encodeURIComponent(customerInfo.apiKey);
// 后端期望的结构是包含Orders属性的对象
const requestData = {
Orders: batch
};
// 将整个请求对象序列化为JSON字符串作为data参数
const dataJson = JSON.stringify(requestData);
url += '&data=' + encodeURIComponent(dataJson);
// 发送JSONP请求
$.ajax({
url: url,
type: 'GET',
dataType: 'jsonp',
jsonp: false,
jsonpCallback: callbackName,
success: function() {
// 回调函数已经处理
},
error: function(xhr, status, error) {
console.error('Failed to import batch ' + batchIndex + ':', error);
// 请求失败时,将该批次所有订单标记为失败
allResults.Status = 'error';
batch.forEach(function(order) {
allResults.Results.push({
BillOfLadingNumber: order.BillOfLadingNumber,
MasterPackageNumber: order.MasterPackageNumber,
NeutralWaybillNumber: order.NeutralWaybillNumber,
FinalMileTrackingNumber: order.FinalMileTrackingNumber,
Success: false,
Message: '请求失败: ' + error
});
allResults.TotalCount++;
allResults.FailedCount++;
});
processedBatches++;
processedOrders += batch.length;
// 更新进度
updateProgress(processedOrders, orders.length);
// 所有批次处理完成
if (processedBatches === totalBatches) {
$('#importProgress').hide();
showNotification('导入失败', '部分批次导入失败,请查看结果详情', 'error');
// 显示导入结果
displayImportResult(allResults.Results);
$('#importResult').show();
}
// 清理回调函数
delete window[callbackName];
}
});
});
}
var _opsMonitorLoaded = false;
var _opsMonitorData = [];
function loadOpsMonitorData() {
const baseUrl = getBaseUrl();
const url = baseUrl + '/api/dashboard/ops-monitor';
$('#opsMonitorTableBody').html('<tr><td colspan="13" style="text-align:center;color:#888">加载中...</td></tr>');
$.ajax({
url: url,
type: 'GET',
dataType: 'jsonp',
jsonp: 'callback',
success: function(response) {
if (response && response.code === 0) {
_opsMonitorData = response.data || [];
displayOpsMonitorTable(_opsMonitorData);
_opsMonitorLoaded = true;
} else {
$('#opsMonitorTableBody').html('<tr><td colspan="13" style="text-align:center;color:#e74c3c">加载失败:' + (response ? response.message : '未知错误') + '</td></tr>');
}
},
error: function(xhr, status, error) {
$('#opsMonitorTableBody').html('<tr><td colspan="13" style="text-align:center;color:#e74c3c">请求失败:' + error + '</td></tr>');
}
});
}
function displayOpsMonitorTable(data) {
var tbody = $('#opsMonitorTableBody');
tbody.empty();
if (!data || data.length === 0) {
tbody.html('<tr><td colspan="13" style="text-align:center;color:#888">暂无数据</td></tr>');
$('#opsCard_shouldReplace').text('—');
$('#opsCard_success').text('—');
$('#opsCard_24h').text('—');
$('#opsCard_rate').text('—');
$('#opsMonitorFetchTime').text('');
return;
}
var today = data[0];
$('#opsCard_shouldReplace').text(today.ShouldReplaceCount || 0);
$('#opsCard_success').text(today.DailySuccessCount || 0);
$('#opsCard_24h').text(today.Rate24HourCount || 0);
var rate = today.DailyCompletionRate || '0.00%';
$('#opsCard_rate').text(rate);
var fetchTime = today.DataFetchTime ? today.DataFetchTime.replace('T', ' ').substring(0, 19) : '';
$('#opsMonitorFetchTime').text(fetchTime ? '数据拉取时间(UTC-5)' + fetchTime : '');
$.each(data, function(i, item) {
var completionRate = item.DailyCompletionRate || '0.00%';
var rate24h = item.Rate24Hour || '0.00%';
var completionVal = parseFloat(completionRate);
var rateStyle = completionVal >= 80 ? 'color:#27ae60;font-weight:600' : (completionVal < 50 ? 'color:#e74c3c;font-weight:600' : '');
var fetchTimeStr = item.DataFetchTime ? item.DataFetchTime.replace('T', ' ').substring(0, 19) : '';
var row = $('<tr>');
row.append($('<td>').text(item.Date || ''));
row.append($('<td>').text(item.DailyNewReplaceCount || 0));
row.append($('<td>').text(item.CumulativeTotalCount || 0));
row.append($('<td>').text(item.ShouldReplaceCount || 0));
row.append($('<td>').text(item.DailySuccessCount || 0));
row.append($('<td>').text(item.DailyFailureCount || 0));
row.append($('<td>').text(item.DailyStopCount || 0));
row.append($('<td>').text(item.Rate24HourCount || 0));
row.append($('<td>').text(item.DailyLabelPushCount || 0));
row.append($('<td>').text(item.DailyScanCount || 0));
row.append($('<td>').attr('style', rateStyle).text(completionRate));
row.append($('<td>').text(rate24h));
row.append($('<td>').text(fetchTimeStr));
tbody.append(row);
});
}
function exportOpsMonitorToExcel() {
if (!_opsMonitorData || _opsMonitorData.length === 0) {
showNotification('导出失败', '暂无数据,请先刷新加载', 'error');
return;
}
var headers = ['日期', '当天新增换单数', '累计要换的总单数', '当天应该换单数', '当日换单完成数', '当日换单失败数', '当日STOP数', '24H换单成功数', '当日标签推送数', '当日扫描数', '当天换单完成率', '24H换单率', '数据拉取时间(UTC-5)'];
var rows = _opsMonitorData.map(function(item) {
var fetchTimeStr = item.DataFetchTime ? item.DataFetchTime.replace('T', ' ').substring(0, 19) : '';
return [
item.Date || '',
item.DailyNewReplaceCount || 0,
item.CumulativeTotalCount || 0,
item.ShouldReplaceCount || 0,
item.DailySuccessCount || 0,
item.DailyFailureCount || 0,
item.DailyStopCount || 0,
item.Rate24HourCount || 0,
item.DailyLabelPushCount || 0,
item.DailyScanCount || 0,
item.DailyCompletionRate || '0.00%',
item.Rate24Hour || '0.00%',
fetchTimeStr
];
});
var wsData = [headers].concat(rows);
var wb = XLSX.utils.book_new();
var ws = XLSX.utils.aoa_to_sheet(wsData);
XLSX.utils.book_append_sheet(wb, ws, '运维监控');
XLSX.writeFile(wb, '运维监控_' + new Date().toISOString().slice(0, 10) + '.xlsx');
}
$('#refreshOpsMonitorBtn').click(function() {
loadOpsMonitorData();
});
$('#exportOpsMonitorBtn').click(function() {
exportOpsMonitorToExcel();
});
$('#ops-monitor-tab').on('shown.bs.tab', function() {
if (!_opsMonitorLoaded) {
loadOpsMonitorData();
}
});
</script>
</body>
</html>