4871 lines
241 KiB
HTML
4871 lines
241 KiB
HTML
<!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> |