更新前端组件和样式
This commit is contained in:
parent
ed49636d44
commit
0d682a826a
26
frontend/assets/styles.css
Normal file → Executable file
26
frontend/assets/styles.css
Normal file → Executable file
@ -2747,6 +2747,32 @@ input[type="date"]::-webkit-calendar-picker-indicator:hover{
|
||||
.spec-value{font-size:14px;color:var(--text);font-weight:500}
|
||||
@media(max-width:768px){.product-card{flex-direction:column}.product-image-wrapper{flex:none;width:100%}.product-features{grid-template-columns:1fr}}
|
||||
|
||||
/* 历史记录弹窗响应式优化 */
|
||||
@media (max-width: 480px) {
|
||||
.notification-modal-content {
|
||||
width: 95%;
|
||||
max-width: none;
|
||||
}
|
||||
|
||||
#mac-history-modal .notification-modal-content {
|
||||
max-width: 95%;
|
||||
}
|
||||
|
||||
#mac-history-modal-list {
|
||||
max-height: 50vh;
|
||||
}
|
||||
|
||||
#mac-history-modal-list li {
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
#mac-history-modal-list .badge {
|
||||
align-self: flex-start;
|
||||
}
|
||||
}
|
||||
|
||||
[data-theme="light"] body{background:#f1f5f9}
|
||||
#app.trackit-layout{margin:0;height:100vh;border-radius:0;overflow:hidden;background:#ffffff;border:none;box-shadow:none;display:flex;flex-direction:row}
|
||||
#app.trackit-layout .content{background:rgba(248,250,252,.6);height:100%}
|
||||
|
||||
11
frontend/index.html
Normal file → Executable file
11
frontend/index.html
Normal file → Executable file
@ -470,6 +470,17 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="mac-history-modal" class="notification-modal" style="display:none;">
|
||||
<div class="notification-modal-backdrop" data-close="mac-history"></div>
|
||||
<div class="notification-modal-content" role="dialog" aria-modal="true" aria-label="📋" style="max-width:800px">
|
||||
<div class="notification-modal-header">
|
||||
<div class="notification-modal-title">📋 MAC与批次上传历史记录</div>
|
||||
<button id="mac-history-modal-close" class="notification-modal-close" type="button" aria-label="关闭">×</button>
|
||||
</div>
|
||||
<div id="mac-history-modal-list" class="notification-modal-list" style="max-height:60vh;overflow-y:auto"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="./js/router.js" defer></script>
|
||||
<script src="./js/api.js" defer></script>
|
||||
<script src="./js/utils/memory-monitor.js" defer></script>
|
||||
|
||||
124
frontend/js/components/shipment-audit.js
Normal file → Executable file
124
frontend/js/components/shipment-audit.js
Normal file → Executable file
@ -124,44 +124,94 @@ Router.register('/shipments/audit', async () => {
|
||||
});
|
||||
}, 0);
|
||||
|
||||
return `<div class="card">
|
||||
<div style="font-weight:600;margin-bottom:16px">审计查询</div>
|
||||
return `
|
||||
<style>
|
||||
#audit-query-page {
|
||||
padding: 20px;
|
||||
background: var(--bg);
|
||||
}
|
||||
#audit-query-page .page-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
#audit-query-page h1 {
|
||||
margin: 0;
|
||||
font-size: 24px;
|
||||
color: var(--text);
|
||||
}
|
||||
#audit-query-page .content-area {
|
||||
background: var(--surface);
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
||||
}
|
||||
#audit-query-page .query-section {
|
||||
padding: 20px;
|
||||
border-bottom: 1px solid var(--border);
|
||||
}
|
||||
#audit-query-page .result-section {
|
||||
padding: 20px;
|
||||
}
|
||||
#audit-query-page .info-box {
|
||||
padding: 12px;
|
||||
background: rgba(79,140,255,0.08);
|
||||
border: 1px solid rgba(79,140,255,0.2);
|
||||
border-radius: 8px;
|
||||
margin-bottom: 16px;
|
||||
font-size: 14px;
|
||||
color: var(--text);
|
||||
}
|
||||
</style>
|
||||
|
||||
<div style="margin-bottom:16px;padding:12px;background:var(--surface);border:1px solid var(--border);border-radius:8px">
|
||||
<div style="color:var(--text-2);font-size:14px">
|
||||
<strong>说明:</strong>查询 Redis 中存储的审计时间记录,支持通过 MAC 地址或批次号查询。
|
||||
<div id="audit-query-page">
|
||||
<div class="page-header">
|
||||
<h1>审计查询</h1>
|
||||
</div>
|
||||
|
||||
<div class="content-area">
|
||||
<div class="query-section">
|
||||
<div class="info-box">
|
||||
<strong>说明:</strong>查询 Redis 中存储的审计时间记录,支持通过 MAC 地址或批次号查询。
|
||||
</div>
|
||||
|
||||
<div class="field" style="margin-bottom:16px">
|
||||
<label>选择平台</label>
|
||||
<select id="platform-select" class="input">
|
||||
<option value="pdd">拼多多</option>
|
||||
<option value="yt">圆通</option>
|
||||
<option value="tx">兔喜</option>
|
||||
<option value="mt">美团</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="field" style="margin-bottom:16px">
|
||||
<label>查询类型</label>
|
||||
<select id="query-type" class="input">
|
||||
<option value="mac">按 MAC 地址查询</option>
|
||||
<option value="batch">按批次号查询</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="field" style="margin-bottom:16px">
|
||||
<label id="input-label">输入 MAC 地址</label>
|
||||
<input
|
||||
id="query-input"
|
||||
class="input"
|
||||
placeholder="可输入带或不带冒号的MAC地址,如:001122AABBCC 或 00:11:22:AA:BB:CC"
|
||||
style="font-family: monospace"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="actions">
|
||||
<button class="btn btn-primary" id="query-btn">查询</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="result-section">
|
||||
<div id="query-result"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style="margin-bottom:16px">
|
||||
<div class="field">
|
||||
<label>选择平台</label>
|
||||
<select id="platform-select" class="input">
|
||||
<option value="pdd">拼多多</option>
|
||||
<option value="yt">圆通</option>
|
||||
<option value="tx">兔喜</option>
|
||||
<option value="mt">美团</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label>查询类型</label>
|
||||
<select id="query-type" class="input">
|
||||
<option value="mac">按 MAC 地址查询</option>
|
||||
<option value="batch">按批次号查询</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label id="input-label">输入 MAC 地址</label>
|
||||
<input
|
||||
id="query-input"
|
||||
class="input"
|
||||
placeholder="可输入带或不带冒号的MAC地址,如:001122AABBCC 或 00:11:22:AA:BB:CC"
|
||||
style="font-family: monospace"
|
||||
/>
|
||||
</div>
|
||||
<button class="btn btn-primary" id="query-btn">查询</button>
|
||||
</div>
|
||||
|
||||
<div id="query-result"></div>
|
||||
</div>`;
|
||||
`;
|
||||
});
|
||||
|
||||
109
frontend/js/components/shipment-query.js
Normal file → Executable file
109
frontend/js/components/shipment-query.js
Normal file → Executable file
@ -187,38 +187,85 @@ Router.register('/shipments/query', async () => {
|
||||
// 根据用户角色决定是否显示清空按钮
|
||||
const showClearButton = userRole === 'superadmin';
|
||||
|
||||
return `<div class="card">
|
||||
<div style="font-weight:600;margin-bottom:16px">发货详细记录查询</div>
|
||||
return `
|
||||
<style>
|
||||
#shipment-query-page {
|
||||
padding: 20px;
|
||||
background: var(--bg);
|
||||
}
|
||||
#shipment-query-page .page-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
#shipment-query-page h1 {
|
||||
margin: 0;
|
||||
font-size: 24px;
|
||||
color: var(--text);
|
||||
}
|
||||
#shipment-query-page .content-area {
|
||||
background: var(--surface);
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
||||
}
|
||||
#shipment-query-page .query-section {
|
||||
padding: 20px;
|
||||
border-bottom: 1px solid var(--border);
|
||||
}
|
||||
#shipment-query-page .result-section {
|
||||
padding: 20px;
|
||||
}
|
||||
#shipment-query-page .stats-box {
|
||||
padding: 12px;
|
||||
background: rgba(79,140,255,0.08);
|
||||
border: 1px solid rgba(79,140,255,0.2);
|
||||
border-radius: 8px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div style="margin-bottom:20px;padding:12px;background:var(--surface);border:1px solid var(--border);border-radius:8px">
|
||||
<div id="redis-stats" style="display:flex;align-items:center;justify-content:space-between">
|
||||
<div>加载中...</div>
|
||||
<div id="shipment-query-page">
|
||||
<div class="page-header">
|
||||
<h1>发货详细记录查询</h1>
|
||||
${showClearButton ? '<button class="btn" id="clear-redis-btn" style="background:#dc2626;color:white">清空所有记录</button>' : ''}
|
||||
</div>
|
||||
|
||||
<div class="content-area">
|
||||
<div class="query-section">
|
||||
<div class="stats-box">
|
||||
<div id="redis-stats">
|
||||
<div>加载中...</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="field" style="margin-bottom:16px">
|
||||
<label>查询类型</label>
|
||||
<select id="query-type" class="input">
|
||||
<option value="sn">按 SN/MAC 查询</option>
|
||||
<option value="box">按箱号查询</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="field" style="margin-bottom:16px">
|
||||
<label id="input-label">输入 SN/MAC 号</label>
|
||||
<input
|
||||
id="query-input"
|
||||
class="input"
|
||||
placeholder="输入 SN 或 MAC 地址"
|
||||
style="font-family: monospace"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="actions">
|
||||
<button class="btn btn-primary" id="query-btn">查询</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="result-section">
|
||||
<div id="query-result"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style="margin-bottom:16px">
|
||||
<div class="field">
|
||||
<label>查询类型</label>
|
||||
<select id="query-type" class="input">
|
||||
<option value="sn">按 SN/MAC 查询</option>
|
||||
<option value="box">按箱号查询</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label id="input-label">输入 SN/MAC 号</label>
|
||||
<input
|
||||
id="query-input"
|
||||
class="input"
|
||||
placeholder="输入 SN 或 MAC 地址"
|
||||
style="font-family: monospace"
|
||||
/>
|
||||
</div>
|
||||
<div style="display:flex;gap:8px">
|
||||
<button class="btn btn-primary" id="query-btn">查询</button>
|
||||
${showClearButton ? '<button class="btn" id="clear-redis-btn" style="background:#dc2626">清空所有记录</button>' : ''}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="query-result"></div>
|
||||
</div>`;
|
||||
`;
|
||||
});
|
||||
|
||||
96
frontend/js/components/shipment-summary.js
Normal file → Executable file
96
frontend/js/components/shipment-summary.js
Normal file → Executable file
@ -95,31 +95,83 @@ Router.register('/shipments/summary', async () => {
|
||||
performQuery();
|
||||
}, 0);
|
||||
|
||||
return `<div class="card">
|
||||
<div style="font-weight:600;margin-bottom:16px">发货汇总信息查询</div>
|
||||
return `
|
||||
<style>
|
||||
#shipment-summary-page {
|
||||
padding: 20px;
|
||||
background: var(--bg);
|
||||
}
|
||||
#shipment-summary-page .page-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
#shipment-summary-page h1 {
|
||||
margin: 0;
|
||||
font-size: 24px;
|
||||
color: var(--text);
|
||||
}
|
||||
#shipment-summary-page .content-area {
|
||||
background: var(--surface);
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
||||
}
|
||||
#shipment-summary-page .query-section {
|
||||
padding: 20px;
|
||||
border-bottom: 1px solid var(--border);
|
||||
}
|
||||
#shipment-summary-page .result-section {
|
||||
padding: 20px;
|
||||
}
|
||||
#shipment-summary-page .date-row {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
gap: 16px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
@media (max-width: 768px) {
|
||||
#shipment-summary-page .date-row {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<div style="margin-bottom:16px">
|
||||
<div class="grid cols-2" style="margin-bottom:12px">
|
||||
<div class="field">
|
||||
<label>开始日期</label>
|
||||
<input
|
||||
id="start-date"
|
||||
type="date"
|
||||
class="input"
|
||||
/>
|
||||
<div id="shipment-summary-page">
|
||||
<div class="page-header">
|
||||
<h1>发货汇总信息查询</h1>
|
||||
</div>
|
||||
|
||||
<div class="content-area">
|
||||
<div class="query-section">
|
||||
<div class="date-row">
|
||||
<div class="field">
|
||||
<label>开始日期</label>
|
||||
<input
|
||||
id="start-date"
|
||||
type="date"
|
||||
class="input"
|
||||
/>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label>结束日期</label>
|
||||
<input
|
||||
id="end-date"
|
||||
type="date"
|
||||
class="input"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="actions">
|
||||
<button class="btn btn-primary" id="summary-query-btn">查询</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label>结束日期</label>
|
||||
<input
|
||||
id="end-date"
|
||||
type="date"
|
||||
class="input"
|
||||
/>
|
||||
|
||||
<div class="result-section">
|
||||
<div id="summary-result"></div>
|
||||
</div>
|
||||
</div>
|
||||
<button class="btn" id="summary-query-btn">查询</button>
|
||||
</div>
|
||||
|
||||
<div id="summary-result"></div>
|
||||
</div>`;
|
||||
`;
|
||||
});
|
||||
|
||||
196
frontend/js/components/shipments.js
Normal file → Executable file
196
frontend/js/components/shipments.js
Normal file → Executable file
@ -170,25 +170,133 @@ Router.register('/upload/shipments', async () => {
|
||||
}
|
||||
});
|
||||
}, 0);
|
||||
return `<div class="card">
|
||||
<div style="font-weight:600;margin-bottom:16px">发货记录</div>
|
||||
return `
|
||||
<style>
|
||||
#shipments-page {
|
||||
padding: 20px;
|
||||
background: var(--bg);
|
||||
}
|
||||
#shipments-page .page-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
#shipments-page h1 {
|
||||
margin: 0;
|
||||
font-size: 24px;
|
||||
color: var(--text);
|
||||
}
|
||||
#shipments-page .content-area {
|
||||
background: var(--surface);
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
||||
}
|
||||
#shipments-page .section {
|
||||
padding: 20px;
|
||||
}
|
||||
#shipments-page .section + .section {
|
||||
border-top: 1px solid var(--border);
|
||||
}
|
||||
#shipments-page .section-title {
|
||||
font-weight: 600;
|
||||
font-size: 16px;
|
||||
margin-bottom: 12px;
|
||||
color: var(--text);
|
||||
}
|
||||
#shipments-page .section-desc {
|
||||
font-size: 13px;
|
||||
color: var(--text-2);
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
#shipments-page .format-requirements {
|
||||
background: rgba(79,140,255,0.08);
|
||||
border: 1px solid rgba(79,140,255,0.2);
|
||||
border-radius: 8px;
|
||||
padding: 12px;
|
||||
margin-bottom: 16px;
|
||||
font-size: 13px;
|
||||
line-height: 1.6;
|
||||
}
|
||||
#shipments-page .row {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
gap: 16px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
@media (max-width: 768px) {
|
||||
#shipments-page .row {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<div style="margin-bottom:24px;padding-bottom:24px;border-bottom:1px solid var(--border)">
|
||||
<div style="font-weight:500;margin-bottom:12px">手动录入</div>
|
||||
<div style="font-size:13px;color:var(--text-secondary);margin-bottom:12px">
|
||||
用于快速录入发货汇总信息(不含详细SN)
|
||||
<div id="shipments-page">
|
||||
<div class="page-header">
|
||||
<h1>发货记录</h1>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<div class="field">
|
||||
<label>发货日期 <span style="color:var(--danger)">*</span></label>
|
||||
<input id="ship-date" type="date" class="input" />
|
||||
|
||||
<div class="content-area">
|
||||
<div class="section">
|
||||
<div class="section-title">手动录入</div>
|
||||
<div class="section-desc">用于快速录入发货汇总信息(不含详细SN)</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="field">
|
||||
<label>发货日期 <span style="color:var(--danger)">*</span></label>
|
||||
<input id="ship-date" type="date" class="input" />
|
||||
</div>
|
||||
<div class="field">
|
||||
<label>机种类型 <span style="color:var(--danger)">*</span></label>
|
||||
<select id="ship-manual-platform" class="input">
|
||||
<option value="">请选择机种</option>
|
||||
<option value="pdd">拼多多</option>
|
||||
<option value="yt">圆通</option>
|
||||
<option value="tx">兔喜</option>
|
||||
<option value="mt">美团</option>
|
||||
<option value="drf">大润发</option>
|
||||
<option value="std">标准版</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="field">
|
||||
<label>接收方 <span style="color:var(--danger)">*</span></label>
|
||||
<input id="ship-to" class="input" placeholder="客户名称" />
|
||||
</div>
|
||||
<div class="field">
|
||||
<label>数量 <span style="color:var(--danger)">*</span></label>
|
||||
<input id="ship-qty" type="number" min="1" class="input" placeholder="发货数量" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="field" style="margin-bottom:16px">
|
||||
<label>箱号(可选)</label>
|
||||
<input id="ship-box-no" class="input" placeholder="例如:BOX001" />
|
||||
</div>
|
||||
|
||||
<div id="ship-manual-status" style="margin:8px 0;font-size:13px"></div>
|
||||
<div class="actions">
|
||||
<button class="btn btn-primary" id="ship-upload">提交录入</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col">
|
||||
<div class="field">
|
||||
|
||||
<div class="section">
|
||||
<div class="section-title">详细记录批量导入</div>
|
||||
|
||||
<div class="format-requirements">
|
||||
<div style="font-weight:600;margin-bottom:6px;color:var(--primary)">📋 文件格式要求</div>
|
||||
<div style="color:var(--text)">
|
||||
<div>• 必需列:出货日期、箱号、SN1、SN2、...、SN20</div>
|
||||
<div>• 出货日期列支持合并单元格</div>
|
||||
<div>• 支持格式:Excel (.xlsx, .xls) 或 CSV</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="field" style="margin-bottom:16px">
|
||||
<label>机种类型 <span style="color:var(--danger)">*</span></label>
|
||||
<select id="ship-manual-platform" class="input">
|
||||
<select id="ship-platform" class="input">
|
||||
<option value="">请选择机种</option>
|
||||
<option value="pdd">拼多多</option>
|
||||
<option value="yt">圆通</option>
|
||||
@ -198,59 +306,19 @@ Router.register('/upload/shipments', async () => {
|
||||
<option value="std">标准版</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<div class="field">
|
||||
<label>接收方 <span style="color:var(--danger)">*</span></label>
|
||||
<input id="ship-to" class="input" placeholder="客户名称" />
|
||||
|
||||
<div class="field" style="margin-bottom:16px">
|
||||
<label>选择文件</label>
|
||||
<input type="file" id="ship-file" accept=".xlsx,.xls,.csv" class="input" style="padding:6px" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="col">
|
||||
<div class="field">
|
||||
<label>数量 <span style="color:var(--danger)">*</span></label>
|
||||
<input id="ship-qty" type="number" min="1" class="input" placeholder="发货数量" />
|
||||
|
||||
<div id="ship-file-status" style="margin:8px 0;font-size:13px"></div>
|
||||
<div class="actions">
|
||||
<button class="btn btn-secondary" id="ship-validate">验证文件</button>
|
||||
<button class="btn btn-primary" id="ship-upload-file">导入数据</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label>箱号(可选)</label>
|
||||
<input id="ship-box-no" class="input" placeholder="例如:BOX001" />
|
||||
</div>
|
||||
<div id="ship-manual-status" style="margin:8px 0;font-size:13px"></div>
|
||||
<div class="actions"><button class="btn" id="ship-upload">提交录入</button></div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div style="font-weight:500;margin-bottom:12px">详细记录批量导入</div>
|
||||
<div class="format-requirements">
|
||||
<div style="font-weight:500;margin-bottom:4px">文件格式要求:</div>
|
||||
<div>• 必需列:出货日期、箱号、SN1、SN2、...、SN20</div>
|
||||
<div>• 出货日期列支持合并单元格</div>
|
||||
<div>• 支持格式:Excel (.xlsx, .xls) 或 CSV</div>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label>机种类型 <span style="color:var(--danger)">*</span></label>
|
||||
<select id="ship-platform" class="input">
|
||||
<option value="">请选择机种</option>
|
||||
<option value="pdd">拼多多</option>
|
||||
<option value="yt">圆通</option>
|
||||
<option value="tx">兔喜</option>
|
||||
<option value="mt">美团</option>
|
||||
<option value="drf">大润发</option>
|
||||
<option value="std">标准版</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label>选择文件</label>
|
||||
<input type="file" id="ship-file" accept=".xlsx,.xls,.csv" class="input" style="padding:6px" />
|
||||
</div>
|
||||
<div id="ship-file-status" style="margin:8px 0;font-size:13px"></div>
|
||||
<div class="actions">
|
||||
<button class="btn" id="ship-validate" style="background:#6c757d">验证文件</button>
|
||||
<button class="btn" id="ship-upload-file">导入数据</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>`;
|
||||
`;
|
||||
});
|
||||
794
frontend/js/components/upload.js
Normal file → Executable file
794
frontend/js/components/upload.js
Normal file → Executable file
@ -68,100 +68,289 @@ const Upload = (() => {
|
||||
function textarea(id,label,placeholder=''){return `<div class="field"><label>${label}</label><textarea id="${id}" class="input" rows="4" placeholder="${placeholder}"></textarea></div>`}
|
||||
|
||||
async function renderMac(){
|
||||
return section('MAC与批次(MAC与批次对应关系表)',`
|
||||
<div style="background:rgba(79,140,255,0.08);border:1px solid rgba(79,140,255,0.2);border-radius:8px;padding:12px;margin-bottom:12px;font-size:13px;line-height:1.6">
|
||||
<div style="font-weight:600;margin-bottom:6px;color:var(--primary)">📋 Excel文件格式要求</div>
|
||||
<div style="color:var(--text)">
|
||||
<div>• Excel文件必须包含以下列头(按顺序):</div>
|
||||
<div style="margin-top:4px;padding:8px;background:var(--bg);border-radius:4px;font-family:monospace;font-size:12px">
|
||||
拼多多/圆通:<br>
|
||||
第1列: MAC(格式:90A9F7300000)<br>
|
||||
第2列: 批次号<br><br>
|
||||
兔喜:<br>
|
||||
第1列: SN_MAC(格式:TJ251639510533:90A9F73007D0)<br>
|
||||
第2列: 批次号
|
||||
return `
|
||||
<style>
|
||||
#mac-batch-page {
|
||||
padding: 20px;
|
||||
background: var(--bg);
|
||||
}
|
||||
|
||||
#mac-batch-page .page-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
#mac-batch-page h1 {
|
||||
margin: 0;
|
||||
font-size: 24px;
|
||||
color: var(--text);
|
||||
}
|
||||
|
||||
#mac-batch-page .content-area {
|
||||
background: var(--surface);
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
||||
}
|
||||
|
||||
#mac-batch-page .upload-section {
|
||||
padding: 20px;
|
||||
border-bottom: 1px solid var(--border);
|
||||
}
|
||||
|
||||
#mac-batch-page .help-toggle {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
background: rgba(79,140,255,0.08);
|
||||
border: 1px solid rgba(79,140,255,0.2);
|
||||
border-radius: 8px;
|
||||
padding: 10px 12px;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s;
|
||||
font-size: 14px;
|
||||
color: var(--primary);
|
||||
font-weight: 600;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
#mac-batch-page .help-toggle:hover {
|
||||
background: rgba(79,140,255,0.12);
|
||||
border-color: rgba(79,140,255,0.3);
|
||||
}
|
||||
|
||||
#mac-batch-page .help-content {
|
||||
margin-bottom: 16px;
|
||||
padding: 12px;
|
||||
background: var(--bg);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 8px;
|
||||
font-size: 13px;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
#mac-batch-page .form-row {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 16px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
#mac-batch-page .field {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
#mac-batch-page .actions {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
#mac-batch-page .log-section {
|
||||
margin-top: 16px;
|
||||
padding-top: 16px;
|
||||
border-top: 1px solid var(--border);
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
#mac-batch-page .form-row {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
#mac-batch-page .actions {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
#mac-batch-page .actions button {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<div id="mac-batch-page">
|
||||
<div class="page-header">
|
||||
<h1>MAC与批次(MAC与批次对应关系表)</h1>
|
||||
<div class="page-actions">
|
||||
<button class="btn btn-secondary" id="mac-view-history" type="button">📋 查看历史记录</button>
|
||||
</div>
|
||||
<div style="margin-top:6px;color:var(--text-2)">
|
||||
示例数据:<br>
|
||||
拼多多/圆通:90A9F7300001 | D20250000000001<br>
|
||||
兔喜:TJ251639510533:90A9F73007D0 | D20250000000002
|
||||
</div>
|
||||
<div style="margin-top:6px;color:var(--warning)">
|
||||
⚠️ 注意:拼多多和圆通的MAC地址为12位十六进制字符(不包含冒号)<br>
|
||||
兔喜的SN_MAC格式为:SN号:MAC地址
|
||||
</div>
|
||||
|
||||
<div class="content-area">
|
||||
<div class="upload-section">
|
||||
<!-- 可折叠的格式说明 -->
|
||||
<button id="mac-toggle-help" class="help-toggle" type="button">
|
||||
<span>📋 Excel文件格式要求</span>
|
||||
<svg id="mac-help-icon" style="width:20px;height:20px;transition:transform 0.2s" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<polyline points="6 9 12 15 18 9"></polyline>
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
<div id="mac-help-content" class="help-content" style="display:none">
|
||||
<div style="color:var(--text)">
|
||||
<div>• Excel文件必须包含以下列头(按顺序):</div>
|
||||
<div style="margin-top:8px;padding:8px;background:var(--surface);border-radius:4px;font-family:monospace;font-size:12px">
|
||||
拼多多/圆通:<br>
|
||||
第1列: MAC(格式:90A9F7300000)<br>
|
||||
第2列: 批次号<br><br>
|
||||
兔喜:<br>
|
||||
第1列: SN_MAC(格式:TJ251639510533:90A9F73007D0)<br>
|
||||
第2列: 批次号
|
||||
</div>
|
||||
<div style="margin-top:8px;color:var(--text-2)">
|
||||
示例数据:<br>
|
||||
拼多多/圆通:90A9F7300001 | D20250000000001<br>
|
||||
兔喜:TJ251639510533:90A9F73007D0 | D20250000000002
|
||||
</div>
|
||||
<div style="margin-top:8px;color:var(--warning)">
|
||||
⚠️ 注意:拼多多和圆通的MAC地址为12位十六进制字符(不包含冒号)<br>
|
||||
兔喜的SN_MAC格式为:SN号:MAC地址
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-row">
|
||||
<div class="field">
|
||||
<label>上传机种 <span style="color:#ff4444">*</span></label>
|
||||
<select id="mac-type" class="input">
|
||||
<option value="">请选择上传机种</option>
|
||||
<option value="pdd">拼多多</option>
|
||||
<option value="yt">圆通</option>
|
||||
<option value="tx">兔喜</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="field">
|
||||
<label>批量导入(Excel)</label>
|
||||
<input id="mac-file" type="file" class="input" accept="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet,application/vnd.ms-excel" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="actions">
|
||||
<button class="btn btn-primary" id="mac-upload">上传</button>
|
||||
</div>
|
||||
|
||||
<div id="upload-log" class="log-section" style="display:none">
|
||||
<div style="font-weight:600;margin-bottom:8px;color:var(--text)">上传日志</div>
|
||||
<pre style="background:var(--bg);border:1px solid var(--border);border-radius:8px;padding:12px;max-height:300px;overflow-y:auto;font-size:12px;color:var(--text);white-space:pre-wrap"></pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label>上传机种 <span style="color:#ff4444">*</span></label>
|
||||
<select id="mac-type" class="input">
|
||||
<option value="">请选择上传机种</option>
|
||||
<option value="pdd">拼多多</option>
|
||||
<option value="yt">圆通</option>
|
||||
<option value="tx">兔喜</option>
|
||||
</select>
|
||||
</div>
|
||||
${filePicker('mac-file','批量导入(Excel)','application/vnd.openxmlformats-officedocument.spreadsheetml.sheet,application/vnd.ms-excel')}
|
||||
<div class="actions"><button class="btn" id="mac-upload">上传</button></div>
|
||||
<div id="upload-log" style="margin-top:12px;display:none">
|
||||
<div style="font-weight:600;margin-bottom:8px">上传日志</div>
|
||||
<pre style="background:var(--surface);border:1px solid var(--border);border-radius:8px;padding:12px;max-height:300px;overflow-y:auto;font-size:12px;color:var(--text);white-space:pre-wrap"></pre>
|
||||
</div>
|
||||
<div style="margin-top:12px">
|
||||
<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:8px">
|
||||
<span style="font-weight:600">历史记录</span>
|
||||
<div style="display:flex;gap:8px">
|
||||
<button class="btn btn-secondary" id="mac-show-history" style="font-size:12px;padding:4px 8px">刷新</button>
|
||||
<button class="btn btn-secondary" id="mac-clear-display" style="font-size:12px;padding:4px 8px">清空显示</button>
|
||||
</div>
|
||||
</div>
|
||||
<ul id="mac-list" class="list" style="max-height:300px;overflow-y:auto"></ul>
|
||||
</div>
|
||||
`);
|
||||
`;
|
||||
}
|
||||
|
||||
async function renderStats(){
|
||||
return section('良/不良统计',`
|
||||
<div style="background:rgba(79,140,255,0.08);border:1px solid rgba(79,140,255,0.2);border-radius:8px;padding:12px;margin-bottom:12px;font-size:13px;line-height:1.6">
|
||||
<div style="font-weight:600;margin-bottom:6px;color:var(--primary)">📊 数据说明</div>
|
||||
<div style="color:var(--text)">
|
||||
<div><strong>直通良品数:</strong>一次检测就通过的产品数量</div>
|
||||
<div><strong>良品数:</strong>最终通过检测的产品总数(包含直通良品 + 返修后通过的产品)</div>
|
||||
<div><strong>不良品数:</strong>最终未通过检测的产品数量(报废或待返修)</div>
|
||||
<div style="margin-top:6px;padding-top:6px;border-top:1px solid rgba(79,140,255,0.2);color:var(--text-2)">
|
||||
💡 <strong>计算公式:</strong><br>
|
||||
直通良品率 = 直通良品数 / (良品数 + 不良品数) × 100%<br>
|
||||
总良品率 = 良品数 / (良品数 + 不良品数) × 100%
|
||||
return `
|
||||
<style>
|
||||
#stats-page {
|
||||
padding: 20px;
|
||||
background: var(--bg);
|
||||
}
|
||||
#stats-page .page-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
#stats-page h1 {
|
||||
margin: 0;
|
||||
font-size: 24px;
|
||||
color: var(--text);
|
||||
}
|
||||
#stats-page .content-area {
|
||||
background: var(--surface);
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
||||
}
|
||||
#stats-page .upload-section {
|
||||
padding: 20px;
|
||||
}
|
||||
#stats-page .info-box {
|
||||
background: rgba(79,140,255,0.08);
|
||||
border: 1px solid rgba(79,140,255,0.2);
|
||||
border-radius: 8px;
|
||||
padding: 12px;
|
||||
margin-bottom: 16px;
|
||||
font-size: 13px;
|
||||
line-height: 1.6;
|
||||
}
|
||||
#stats-page .form-row {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
gap: 16px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
@media (max-width: 768px) {
|
||||
#stats-page .form-row {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<div id="stats-page">
|
||||
<div class="page-header">
|
||||
<h1>良/不良统计</h1>
|
||||
<div class="page-actions">
|
||||
<button class="btn btn-secondary" id="stats-show-history">查看历史</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="content-area">
|
||||
<div class="upload-section">
|
||||
<div class="info-box">
|
||||
<div style="font-weight:600;margin-bottom:6px;color:var(--primary)">📊 数据说明</div>
|
||||
<div style="color:var(--text)">
|
||||
<div><strong>直通良品数:</strong>一次检测就通过的产品数量</div>
|
||||
<div><strong>良品数:</strong>最终通过检测的产品总数(包含直通良品 + 返修后通过的产品)</div>
|
||||
<div><strong>不良品数:</strong>最终未通过检测的产品数量(报废或待返修)</div>
|
||||
<div style="margin-top:6px;padding-top:6px;border-top:1px solid rgba(79,140,255,0.2);color:var(--text-2)">
|
||||
💡 <strong>计算公式:</strong><br>
|
||||
直通良品率 = 直通良品数 / (良品数 + 不良品数) × 100%<br>
|
||||
总良品率 = 良品数 / (良品数 + 不良品数) × 100%
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-row">
|
||||
<div class="field">
|
||||
<label>平台类型</label>
|
||||
<select id="stats-platform" class="input">
|
||||
<option value="pdd">拼多多</option>
|
||||
<option value="yt">圆通</option>
|
||||
<option value="tx">兔喜</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label>直通良品数量(一次检测通过)</label>
|
||||
<input id="fpy-good-count" type="number" class="input" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-row">
|
||||
<div class="field">
|
||||
<label>良品数量(最终通过检测的总数)</label>
|
||||
<input id="good-count" type="number" class="input" />
|
||||
</div>
|
||||
<div class="field">
|
||||
<label>不良品数量(最终未通过)</label>
|
||||
<input id="bad-count" type="number" class="input" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="field" style="margin-bottom:16px">
|
||||
<label>不良明细(可选)</label>
|
||||
<textarea id="bad-details" class="input" rows="4" placeholder="每行一个不良记录,格式:MAC地址,批次号 例如:90:A9:F7:DD:EE:FF,D20250000000001"></textarea>
|
||||
</div>
|
||||
|
||||
<div class="actions">
|
||||
<button class="btn btn-primary" id="stats-upload">上传</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label>平台类型</label>
|
||||
<select id="stats-platform" class="input">
|
||||
<option value="pdd">拼多多</option>
|
||||
<option value="yt">圆通</option>
|
||||
<option value="tx">兔喜</option>
|
||||
</select>
|
||||
</div>
|
||||
${numberInput('fpy-good-count','直通良品数量(一次检测通过)')}
|
||||
${numberInput('good-count','良品数量(最终通过检测的总数)')}
|
||||
${numberInput('bad-count','不良品数量(最终未通过)')}
|
||||
<div class="field">
|
||||
<label>不良明细(可选)</label>
|
||||
<textarea id="bad-details" class="input" rows="4" placeholder="每行一个不良记录,格式:MAC地址,批次号 例如:90:A9:F7:DD:EE:FF,D20250000000001"></textarea>
|
||||
</div>
|
||||
<div class="actions"><button class="btn" id="stats-upload">上传</button></div>
|
||||
<div style="margin-top:12px">
|
||||
<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:8px">
|
||||
<span style="font-weight:600">最新记录</span>
|
||||
<div style="display:flex;gap:8px">
|
||||
<button class="btn btn-secondary" id="stats-show-history" style="font-size:12px;padding:4px 8px">查看历史</button>
|
||||
<button class="btn btn-secondary" id="stats-clear-display" style="font-size:12px;padding:4px 8px">清空显示</button>
|
||||
</div>
|
||||
</div>
|
||||
<ul id="stats-list" class="list" style="max-height:300px;overflow-y:auto"></ul>
|
||||
</div>
|
||||
`);
|
||||
`;
|
||||
}
|
||||
|
||||
// 不良原因选项
|
||||
@ -187,11 +376,46 @@ const Upload = (() => {
|
||||
];
|
||||
|
||||
async function renderRepairs(){
|
||||
return section('返修记录上传',`
|
||||
<div style="margin-bottom:16px;display:flex;justify-content:flex-start">
|
||||
<a href="#/upload/repairs-history" class="btn btn-secondary" style="font-size:12px;padding:6px 10px">📋 查看历史记录</a>
|
||||
</div>
|
||||
<div id="repairs-upload-form" style="max-height:calc(100vh - 200px);overflow-y:auto;padding-right:8px">
|
||||
return `
|
||||
<style>
|
||||
#repairs-page {
|
||||
padding: 20px;
|
||||
background: var(--bg);
|
||||
}
|
||||
#repairs-page .page-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
#repairs-page h1 {
|
||||
margin: 0;
|
||||
font-size: 24px;
|
||||
color: var(--text);
|
||||
}
|
||||
#repairs-page .content-area {
|
||||
background: var(--surface);
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
||||
}
|
||||
#repairs-page .upload-section {
|
||||
padding: 20px;
|
||||
}
|
||||
@keyframes spin { from { transform: rotate(0deg); } to { transform: rotate(360deg); } }
|
||||
#repair-image-dropzone:hover { background:rgba(79,140,255,0.12); border-color:var(--primary); }
|
||||
#repair-image-dropzone.dragover { background:rgba(79,140,255,0.15); border-color:var(--primary); border-style:solid; }
|
||||
</style>
|
||||
|
||||
<div id="repairs-page">
|
||||
<div class="page-header">
|
||||
<h1>返修记录上传</h1>
|
||||
<div class="page-actions">
|
||||
<a href="#/upload/repairs-history" class="btn btn-secondary">📋 查看历史记录</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="content-area">
|
||||
<div class="upload-section">
|
||||
<div class="form-group" style="margin-bottom:16px">
|
||||
<label style="display:block;font-weight:600;margin-bottom:6px">
|
||||
设备SN <span style="color:#ef4444">*</span>
|
||||
@ -267,17 +491,10 @@ const Upload = (() => {
|
||||
<button class="btn btn-secondary" id="repairs-clear" style="flex:0 0 auto">清空</button>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
@keyframes spin { from { transform: rotate(0deg); } to { transform: rotate(360deg); } }
|
||||
#repair-image-dropzone:hover { background:rgba(79,140,255,0.12); border-color:var(--primary); }
|
||||
#repair-image-dropzone.dragover { background:rgba(79,140,255,0.15); border-color:var(--primary); border-style:solid; }
|
||||
#repairs-upload-form::-webkit-scrollbar { width: 8px; }
|
||||
#repairs-upload-form::-webkit-scrollbar-track { background: var(--bg); border-radius: 4px; }
|
||||
#repairs-upload-form::-webkit-scrollbar-thumb { background: rgba(79,140,255,0.3); border-radius: 4px; }
|
||||
#repairs-upload-form::-webkit-scrollbar-thumb:hover { background: rgba(79,140,255,0.5); }
|
||||
</style>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`);
|
||||
`;
|
||||
}
|
||||
|
||||
async function renderRepairsHistory(){
|
||||
@ -395,39 +612,98 @@ const Upload = (() => {
|
||||
});
|
||||
|
||||
async function renderSop(){
|
||||
return section('SOP 文件管理',`
|
||||
<div style="background:rgba(79,140,255,0.08);border:1px solid rgba(79,140,255,0.2);border-radius:8px;padding:12px;margin-bottom:12px;font-size:13px;line-height:1.6">
|
||||
<div style="font-weight:600;margin-bottom:6px;color:var(--primary)">📄 SOP 说明</div>
|
||||
<div style="color:var(--text)">
|
||||
<div>• 所有用户均可在线查看和下载 SOP 文件</div>
|
||||
<div>• 管理员可以上传新的 SOP 文件(支持 Excel 和 Word 格式)</div>
|
||||
<div>• 建议为每个 SOP 文件添加清晰的描述说明</div>
|
||||
return `
|
||||
<style>
|
||||
#sop-page {
|
||||
padding: 20px;
|
||||
background: var(--bg);
|
||||
}
|
||||
#sop-page .page-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
#sop-page h1 {
|
||||
margin: 0;
|
||||
font-size: 24px;
|
||||
color: var(--text);
|
||||
}
|
||||
#sop-page .content-area {
|
||||
background: var(--surface);
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
||||
}
|
||||
#sop-page .upload-section {
|
||||
padding: 20px;
|
||||
border-bottom: 1px solid var(--border);
|
||||
}
|
||||
#sop-page .list-section {
|
||||
padding: 20px;
|
||||
}
|
||||
#sop-page .info-box {
|
||||
background: rgba(79,140,255,0.08);
|
||||
border: 1px solid rgba(79,140,255,0.2);
|
||||
border-radius: 8px;
|
||||
padding: 12px;
|
||||
margin-bottom: 16px;
|
||||
font-size: 13px;
|
||||
line-height: 1.6;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div id="sop-page">
|
||||
<div class="page-header">
|
||||
<h1>SOP 文件管理</h1>
|
||||
</div>
|
||||
|
||||
<div class="content-area">
|
||||
<div class="upload-section">
|
||||
<div class="info-box">
|
||||
<div style="font-weight:600;margin-bottom:6px;color:var(--primary)">📄 SOP 说明</div>
|
||||
<div style="color:var(--text)">
|
||||
<div>• 所有用户均可在线查看和下载 SOP 文件</div>
|
||||
<div>• 管理员可以上传新的 SOP 文件(支持 Excel 和 Word 格式)</div>
|
||||
<div>• 建议为每个 SOP 文件添加清晰的描述说明</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="field" style="margin-bottom:16px">
|
||||
<label>上传 SOP 文件 (Excel/Word)</label>
|
||||
<input id="sop-file" type="file" class="input" accept="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet,application/vnd.ms-excel,text/csv,application/vnd.openxmlformats-officedocument.wordprocessingml.document,application/msword" />
|
||||
</div>
|
||||
|
||||
<div class="field" style="margin-bottom:16px">
|
||||
<label>文件描述(可选)</label>
|
||||
<textarea id="sop-description" class="input" rows="2" placeholder="例如:拼多多生产流程 SOP v1.0"></textarea>
|
||||
</div>
|
||||
|
||||
<div class="actions">
|
||||
<button class="btn btn-primary" id="sop-upload">上传</button>
|
||||
</div>
|
||||
|
||||
<div id="sop-upload-progress" style="display:none;margin-top:16px;padding:12px;background:var(--bg);border-radius:8px;border:1px solid var(--border)">
|
||||
<div style="display:flex;justify-content:space-between;margin-bottom:8px">
|
||||
<span style="font-weight:500">上传进度</span>
|
||||
<span id="sop-progress-percent">0%</span>
|
||||
</div>
|
||||
<div style="width:100%;height:8px;background:var(--surface);border-radius:4px;overflow:hidden">
|
||||
<div id="sop-progress-bar" style="width:0%;height:100%;background:var(--primary);transition:width 0.3s"></div>
|
||||
</div>
|
||||
<div style="display:flex;justify-content:space-between;margin-top:8px;font-size:12px;color:var(--text-2)">
|
||||
<span id="sop-progress-size">0 MB / 0 MB</span>
|
||||
<span id="sop-progress-speed">0 KB/s</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="list-section">
|
||||
<div style="font-weight:600;margin-bottom:12px;color:var(--text)">SOP 文件列表</div>
|
||||
<ul id="sop-list" class="list" style="max-height:400px;overflow-y:auto"></ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
${filePicker('sop-file','上传 SOP 文件 (Excel/Word)','application/vnd.openxmlformats-officedocument.spreadsheetml.sheet,application/vnd.ms-excel,text/csv,application/vnd.openxmlformats-officedocument.wordprocessingml.document,application/msword')}
|
||||
<div class="field">
|
||||
<label>文件描述(可选)</label>
|
||||
<textarea id="sop-description" class="input" rows="2" placeholder="例如:拼多多生产流程 SOP v1.0"></textarea>
|
||||
</div>
|
||||
<div class="actions"><button class="btn" id="sop-upload">上传</button></div>
|
||||
<div id="sop-upload-progress" style="display:none;margin-top:12px;padding:12px;background:var(--surface);border-radius:8px;border:1px solid var(--border)">
|
||||
<div style="display:flex;justify-content:space-between;margin-bottom:8px">
|
||||
<span style="font-weight:500">上传进度</span>
|
||||
<span id="sop-progress-percent">0%</span>
|
||||
</div>
|
||||
<div style="width:100%;height:8px;background:var(--bg);border-radius:4px;overflow:hidden">
|
||||
<div id="sop-progress-bar" style="width:0%;height:100%;background:var(--primary);transition:width 0.3s"></div>
|
||||
</div>
|
||||
<div style="display:flex;justify-content:space-between;margin-top:8px;font-size:12px;color:var(--text-2)">
|
||||
<span id="sop-progress-size">0 MB / 0 MB</span>
|
||||
<span id="sop-progress-speed">0 KB/s</span>
|
||||
</div>
|
||||
</div>
|
||||
<div style="margin-top:12px">
|
||||
<div style="font-weight:600;margin-bottom:8px">SOP 文件列表</div>
|
||||
<ul id="sop-list" class="list" style="max-height:400px;overflow-y:auto"></ul>
|
||||
</div>
|
||||
`);
|
||||
`;
|
||||
}
|
||||
|
||||
Router.register('/upload/sop', async () => {
|
||||
@ -590,70 +866,98 @@ const Upload = (() => {
|
||||
async function bindMacEvents(){
|
||||
const fileEl=document.getElementById('mac-file');
|
||||
const btn = document.getElementById('mac-upload');
|
||||
const showHistoryBtn = document.getElementById('mac-show-history');
|
||||
const clearDisplayBtn = document.getElementById('mac-clear-display');
|
||||
const viewHistoryBtn = document.getElementById('mac-view-history');
|
||||
const toggleHelpBtn = document.getElementById('mac-toggle-help');
|
||||
const helpContent = document.getElementById('mac-help-content');
|
||||
const helpIcon = document.getElementById('mac-help-icon');
|
||||
|
||||
// 显示历史记录的通用函数
|
||||
const displayHistory = async (showAll = false) => {
|
||||
const listEl = document.getElementById('mac-list');
|
||||
// 格式说明折叠/展开功能
|
||||
addListener(toggleHelpBtn, 'click', () => {
|
||||
const isHidden = helpContent.style.display === 'none';
|
||||
helpContent.style.display = isHidden ? 'block' : 'none';
|
||||
helpIcon.style.transform = isHidden ? 'rotate(180deg)' : 'rotate(0deg)';
|
||||
});
|
||||
|
||||
// 历史记录弹窗功能
|
||||
const showHistoryModal = async () => {
|
||||
const modal = document.getElementById('mac-history-modal');
|
||||
const listEl = document.getElementById('mac-history-modal-list');
|
||||
|
||||
if(!modal || !listEl) return;
|
||||
|
||||
// 显示弹窗
|
||||
modal.style.display = 'block';
|
||||
listEl.innerHTML = '<div style="text-align:center;padding:20px;color:var(--text-2)">加载中...</div>';
|
||||
|
||||
try {
|
||||
const data = await API.listMac();
|
||||
if(listEl){
|
||||
if(data.list && data.list.length > 0){
|
||||
const platformNames = {pdd: '拼多多', yt: '圆通', tx: '兔喜'};
|
||||
if(data.list && data.list.length > 0){
|
||||
const platformNames = {pdd: '拼多多', yt: '圆通', tx: '兔喜'};
|
||||
|
||||
// 按时间和机种分组统计
|
||||
const uploadGroups = {};
|
||||
data.list.forEach(r => {
|
||||
const date = new Date(r.ts).toLocaleDateString('zh-CN');
|
||||
const platform = platformNames[r.platform] || r.platform || '未知';
|
||||
const key = `${date}_${platform}`;
|
||||
|
||||
// 按时间和机种分组统计
|
||||
const uploadGroups = {};
|
||||
data.list.forEach(r => {
|
||||
const date = new Date(r.ts).toLocaleDateString('zh-CN');
|
||||
const platform = platformNames[r.platform] || r.platform || '未知';
|
||||
const key = `${date}_${platform}`;
|
||||
|
||||
if (!uploadGroups[key]) {
|
||||
uploadGroups[key] = {
|
||||
date: date,
|
||||
platform: platform,
|
||||
count: 0,
|
||||
firstTime: r.ts
|
||||
};
|
||||
}
|
||||
uploadGroups[key].count++;
|
||||
});
|
||||
|
||||
// 转换为数组并按时间排序
|
||||
const groupArray = Object.values(uploadGroups)
|
||||
.sort((a, b) => new Date(b.firstTime) - new Date(a.firstTime));
|
||||
|
||||
listEl.innerHTML = groupArray.map(g => {
|
||||
return `<li style="display:flex;justify-content:space-between;align-items:center;padding:8px 12px;background:var(--surface)">
|
||||
<div>
|
||||
<div style="font-weight:600;color:var(--primary)">${g.date}</div>
|
||||
<div style="margin-top:4px">上传 <span style="color:var(--success);font-weight:600">${g.count}</span> 条记录</div>
|
||||
</div>
|
||||
<div>
|
||||
<span class="badge" style="background:var(--primary-light);color:var(--primary)">${g.platform}</span>
|
||||
</div>
|
||||
</li>`;
|
||||
}).join('');
|
||||
|
||||
if(showAll) {
|
||||
const totalGroups = groupArray.length;
|
||||
const totalRecords = data.list.length;
|
||||
API.toast(`显示 ${totalGroups} 次上传记录,共 ${totalRecords} 条数据`);
|
||||
if (!uploadGroups[key]) {
|
||||
uploadGroups[key] = {
|
||||
date: date,
|
||||
platform: platform,
|
||||
count: 0,
|
||||
firstTime: r.ts
|
||||
};
|
||||
}
|
||||
} else {
|
||||
listEl.innerHTML = '<li>暂无历史记录</li>';
|
||||
}
|
||||
uploadGroups[key].count++;
|
||||
});
|
||||
|
||||
// 转换为数组并按时间排序
|
||||
const groupArray = Object.values(uploadGroups)
|
||||
.sort((a, b) => new Date(b.firstTime) - new Date(a.firstTime));
|
||||
|
||||
listEl.innerHTML = `
|
||||
<div style="padding:12px;background:var(--info-bg);border-radius:8px;margin-bottom:12px">
|
||||
<div style="font-size:14px;color:var(--text)">共 <strong>${groupArray.length}</strong> 次上传,总计 <strong>${data.list.length}</strong> 条记录</div>
|
||||
</div>
|
||||
<ul class="list" style="margin:0">
|
||||
${groupArray.map(g => {
|
||||
return `<li style="display:flex;justify-content:space-between;align-items:center;padding:12px;background:var(--surface);margin-bottom:8px;border-radius:8px">
|
||||
<div style="flex:1">
|
||||
<div style="font-weight:600;color:var(--primary);font-size:15px">${g.date}</div>
|
||||
<div style="margin-top:6px;color:var(--text-2);font-size:13px">上传 <span style="color:var(--success);font-weight:600">${g.count}</span> 条记录</div>
|
||||
</div>
|
||||
<div>
|
||||
<span class="badge" style="background:var(--primary);color:white;padding:6px 12px;border-radius:6px;font-size:12px">${g.platform}</span>
|
||||
</div>
|
||||
</li>`;
|
||||
}).join('')}
|
||||
</ul>
|
||||
`;
|
||||
} else {
|
||||
listEl.innerHTML = '<div style="text-align:center;padding:40px;color:var(--text-2)">暂无历史记录</div>';
|
||||
}
|
||||
} catch(e) {
|
||||
listEl.innerHTML = '<div style="text-align:center;padding:40px;color:var(--danger)">加载失败,请重试</div>';
|
||||
API.toast('加载历史记录失败');
|
||||
if(listEl) listEl.innerHTML = '<li>加载失败</li>';
|
||||
}
|
||||
};
|
||||
|
||||
// 查看历史按钮 - 从服务器获取所有用户的上传记录
|
||||
addListener(showHistoryBtn, 'click', async ()=>{
|
||||
await displayHistory(true);
|
||||
// 查看历史记录按钮
|
||||
addListener(viewHistoryBtn, 'click', showHistoryModal);
|
||||
|
||||
// 关闭弹窗
|
||||
const closeBtn = document.getElementById('mac-history-modal-close');
|
||||
const backdrop = document.querySelector('#mac-history-modal .notification-modal-backdrop');
|
||||
|
||||
addListener(closeBtn, 'click', () => {
|
||||
const modal = document.getElementById('mac-history-modal');
|
||||
if(modal) modal.style.display = 'none';
|
||||
});
|
||||
|
||||
addListener(backdrop, 'click', () => {
|
||||
const modal = document.getElementById('mac-history-modal');
|
||||
if(modal) modal.style.display = 'none';
|
||||
});
|
||||
|
||||
// 文件选择后立即验证
|
||||
@ -683,77 +987,6 @@ const Upload = (() => {
|
||||
}
|
||||
});
|
||||
|
||||
// 查看历史按钮 - 从服务器获取所有用户的上传记录
|
||||
addListener(showHistoryBtn, 'click', async ()=>{
|
||||
const listEl = document.getElementById('mac-list');
|
||||
try {
|
||||
const data = await API.listMac();
|
||||
if(listEl){
|
||||
if(data.list && data.list.length > 0){
|
||||
const platformNames = {pdd: '拼多多', yt: '圆通', tx: '兔喜'};
|
||||
|
||||
// 按时间和机种分组统计
|
||||
const uploadGroups = {};
|
||||
data.list.forEach(r => {
|
||||
const date = new Date(r.ts).toLocaleDateString('zh-CN');
|
||||
const platform = platformNames[r.platform] || r.platform || '未知';
|
||||
const key = `${date}_${platform}`;
|
||||
|
||||
if (!uploadGroups[key]) {
|
||||
uploadGroups[key] = {
|
||||
date: date,
|
||||
platform: platform,
|
||||
count: 0,
|
||||
firstTime: r.ts
|
||||
};
|
||||
}
|
||||
uploadGroups[key].count++;
|
||||
});
|
||||
|
||||
// 转换为数组并按时间排序
|
||||
const groupArray = Object.values(uploadGroups)
|
||||
.sort((a, b) => new Date(b.firstTime) - new Date(a.firstTime));
|
||||
|
||||
listEl.innerHTML = groupArray.map(g => {
|
||||
const time = new Date(g.firstTime).toLocaleString('zh-CN', {
|
||||
month: '2-digit',
|
||||
day: '2-digit',
|
||||
hour: '2-digit',
|
||||
minute: '2-digit'
|
||||
});
|
||||
return `<li style="display:flex;justify-content:space-between;align-items:center;padding:8px 12px;background:var(--surface)">
|
||||
<div>
|
||||
<div style="font-weight:600;color:var(--primary)">${g.date}</div>
|
||||
<div style="margin-top:4px">上传 <span style="color:var(--success);font-weight:600">${g.count}</span> 条记录</div>
|
||||
</div>
|
||||
<div>
|
||||
<span class="badge" style="background:var(--primary-light);color:var(--primary)">${g.platform}</span>
|
||||
</div>
|
||||
</li>`;
|
||||
}).join('');
|
||||
|
||||
const totalGroups = groupArray.length;
|
||||
const totalRecords = data.list.length;
|
||||
API.toast(`显示 ${totalGroups} 次上传记录,共 ${totalRecords} 条数据`);
|
||||
} else {
|
||||
listEl.innerHTML = '<li>暂无历史记录</li>';
|
||||
}
|
||||
}
|
||||
} catch(e) {
|
||||
API.toast('加载历史记录失败');
|
||||
if(listEl) listEl.innerHTML = '<li>加载失败</li>';
|
||||
}
|
||||
});
|
||||
|
||||
// 清空显示按钮
|
||||
addListener(clearDisplayBtn, 'click', ()=>{
|
||||
const listEl = document.getElementById('mac-list');
|
||||
if(listEl){
|
||||
listEl.innerHTML = '<li>已清空显示</li>';
|
||||
API.toast('已清空显示(历史记录仍保留)');
|
||||
}
|
||||
});
|
||||
|
||||
addListener(btn, 'click', async ()=>{
|
||||
const file = fileEl.files[0];
|
||||
if(!file){
|
||||
@ -804,64 +1037,15 @@ const Upload = (() => {
|
||||
if(result.ok){
|
||||
API.toast('上传成功');
|
||||
|
||||
// 解析并显示成功上传的记录
|
||||
// 解析成功上传的记录
|
||||
const output = result.output || '';
|
||||
const jsonMatch = output.match(/=== 成功导入的数据 ===\n([\s\S]*?)\n=== 数据输出结束 ===/);
|
||||
if(jsonMatch && jsonMatch[1]){
|
||||
try{
|
||||
const records = JSON.parse(jsonMatch[1].trim());
|
||||
if(records.length > 0){
|
||||
// 保存到本地历史记录(仅用于本地显示)
|
||||
saveToHistory(records);
|
||||
|
||||
// 重新从服务器加载数据并显示
|
||||
const listEl = document.getElementById('mac-list');
|
||||
if(listEl){
|
||||
try {
|
||||
const serverData = await API.listMac();
|
||||
if(serverData.list && serverData.list.length > 0){
|
||||
const platformNames = {pdd: '拼多多', yt: '圆通', tx: '兔喜'};
|
||||
|
||||
// 按时间和机种分组统计
|
||||
const uploadGroups = {};
|
||||
serverData.list.forEach(r => {
|
||||
const date = new Date(r.ts).toLocaleDateString('zh-CN');
|
||||
const platform = platformNames[r.platform] || r.platform || '未知';
|
||||
const key = `${date}_${platform}`;
|
||||
|
||||
if (!uploadGroups[key]) {
|
||||
uploadGroups[key] = {
|
||||
date: date,
|
||||
platform: platform,
|
||||
count: 0,
|
||||
firstTime: r.ts
|
||||
};
|
||||
}
|
||||
uploadGroups[key].count++;
|
||||
});
|
||||
|
||||
// 转换为数组并按时间排序
|
||||
const groupArray = Object.values(uploadGroups)
|
||||
.sort((a, b) => new Date(b.firstTime) - new Date(a.firstTime));
|
||||
|
||||
listEl.innerHTML = groupArray.map(g => {
|
||||
return `<li style="display:flex;justify-content:space-between;align-items:center;padding:8px 12px;background:var(--surface)">
|
||||
<div>
|
||||
<div style="font-weight:600;color:var(--primary)">${g.date}</div>
|
||||
<div style="margin-top:4px">上传 <span style="color:var(--success);font-weight:600">${g.count}</span> 条记录</div>
|
||||
</div>
|
||||
<div>
|
||||
<span class="badge" style="background:var(--primary-light);color:var(--primary)">${g.platform}</span>
|
||||
</div>
|
||||
</li>`;
|
||||
}).join('');
|
||||
|
||||
API.toast(`成功上传 ${records.length} 条记录`);
|
||||
}
|
||||
} catch(e) {
|
||||
console.error('刷新列表失败:', e);
|
||||
}
|
||||
}
|
||||
API.toast(`成功上传 ${records.length} 条记录`);
|
||||
}
|
||||
}catch(e){
|
||||
console.error('解析上传记录失败:', e);
|
||||
|
||||
Loading…
Reference in New Issue
Block a user