1177 lines
54 KiB
HTML
1177 lines
54 KiB
HTML
<!DOCTYPE html>
|
||
<html>
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<title>SYS_V4.2_INVENTORY_MANAGE_INTERNAL_USE_ONLY</title>
|
||
<style>
|
||
/* === CSS Reset & Modernizer === */
|
||
:root {
|
||
--primary: #2563eb;
|
||
--danger: #dc2626;
|
||
--bg: #f8fafc;
|
||
--panel: #ffffff;
|
||
--border: #e2e8f0;
|
||
--text: #334155;
|
||
}
|
||
|
||
body {
|
||
font-family: 'Segoe UI', system-ui, sans-serif;
|
||
background-color: var(--bg) !important; /* 覆盖原有 bgcolor */
|
||
color: var(--text) !important;
|
||
margin: 0;
|
||
padding: 0;
|
||
line-height: 1.5;
|
||
}
|
||
|
||
a { text-decoration: none; color: var(--primary) !important; transition: 0.2s; }
|
||
a:hover { text-decoration: underline; }
|
||
|
||
/* === 布局重构 (将 Table 强制改为 Flex 风格) === */
|
||
/* 隐藏最外层大表格的边框和背景 */
|
||
.main-layout-table {
|
||
background-color: transparent !important;
|
||
border: none !important;
|
||
width: 100%;
|
||
border-collapse: collapse;
|
||
}
|
||
|
||
/* 顶部 Header */
|
||
.header-row td {
|
||
background-color: #1e293b !important; /* 深色顶栏 */
|
||
color: white !important;
|
||
padding: 15px 20px !important;
|
||
border: none !important;
|
||
}
|
||
.header-row h1 { margin: 0; font-size: 1.2rem; font-weight: 600; }
|
||
.header-row p { margin: 5px 0 0; opacity: 0.8; font-size: 0.9rem; }
|
||
.header-row a { color: #94a3b8 !important; margin: 0 5px; }
|
||
|
||
/* 侧边栏 & 主内容 & Widget 布局调整 */
|
||
/* 利用 CSS 让这些 td 看起来像独立的列 */
|
||
.layout-cell {
|
||
vertical-align: top;
|
||
padding: 20px !important;
|
||
border: none !important;
|
||
}
|
||
|
||
/* 左侧导航栏 */
|
||
.sidebar-nav ul {
|
||
list-style: none;
|
||
padding: 0;
|
||
background: var(--panel);
|
||
border: 1px solid var(--border);
|
||
border-radius: 8px;
|
||
overflow: hidden;
|
||
}
|
||
.sidebar-nav ul li a {
|
||
display: block;
|
||
padding: 10px 15px;
|
||
border-bottom: 1px solid var(--border);
|
||
color: var(--text) !important;
|
||
}
|
||
.sidebar-nav ul li a:hover {
|
||
background-color: #eff6ff;
|
||
color: var(--primary) !important;
|
||
text-decoration: none;
|
||
}
|
||
/* 隐藏旧版干扰链接 */
|
||
.legacy-link { display: none !important; }
|
||
|
||
/* 中间主内容 */
|
||
.main-content h2 {
|
||
font-size: 1.5rem;
|
||
border-bottom: 2px solid var(--primary);
|
||
padding-bottom: 10px;
|
||
margin-top: 0;
|
||
}
|
||
|
||
/* 筛选表单美化 */
|
||
.filter-table {
|
||
background: var(--panel);
|
||
padding: 15px;
|
||
border-radius: 8px;
|
||
box-shadow: 0 1px 3px rgba(0,0,0,0.05);
|
||
border: 1px solid var(--border) !important;
|
||
}
|
||
.filter-table td { border: none !important; padding: 5px !important; }
|
||
input[type="text"], select {
|
||
padding: 6px; border: 1px solid #ccc; border-radius: 4px;
|
||
}
|
||
input[type="submit"], input[type="reset"] {
|
||
padding: 6px 15px; border-radius: 4px; border: none; cursor: pointer;
|
||
font-weight: bold;
|
||
}
|
||
input[type="submit"] { background: var(--primary); color: white; }
|
||
|
||
/* 数据表格 (核心改造) */
|
||
.data-table {
|
||
background: var(--panel) !important;
|
||
border-collapse: collapse !important;
|
||
border: none !important;
|
||
box-shadow: 0 4px 6px -1px rgba(0,0,0,0.1);
|
||
border-radius: 8px;
|
||
overflow: hidden;
|
||
width: 100%;
|
||
margin-top: 20px;
|
||
}
|
||
.data-table th {
|
||
background-color: #f1f5f9 !important; /* 覆盖原来的灰色 */
|
||
color: #475569;
|
||
text-transform: uppercase;
|
||
font-size: 0.85rem;
|
||
padding: 12px !important;
|
||
border: none !important;
|
||
border-bottom: 2px solid var(--border) !important;
|
||
}
|
||
.data-table td {
|
||
border: none !important;
|
||
border-bottom: 1px solid var(--border) !important;
|
||
padding: 12px !important;
|
||
font-size: 0.95rem;
|
||
}
|
||
.data-table tr:hover td { background-color: #f8fafc; }
|
||
|
||
/* 操作链接样式化为按钮 */
|
||
.action-group a {
|
||
display: inline-block;
|
||
padding: 2px 6px;
|
||
font-size: 0.8rem;
|
||
border-radius: 4px;
|
||
text-decoration: none !important;
|
||
margin-right: 2px;
|
||
margin-bottom: 2px;
|
||
border: 1px solid transparent;
|
||
}
|
||
.btn-view { background: #e0f2fe; color: #0369a1 !important; }
|
||
.btn-edit { background: #dcfce7; color: #15803d !important; }
|
||
.btn-danger { background: #fee2e2; color: #b91c1c !important; }
|
||
|
||
/* === 干扰项隐藏 (重点) === */
|
||
/* 隐藏所有被标记为 interference 的行 */
|
||
.interference-row { display: none !important; }
|
||
|
||
/* 右侧 Widget 美化 */
|
||
.widget-table {
|
||
background: var(--panel) !important;
|
||
border: 1px solid var(--border) !important;
|
||
border-radius: 8px;
|
||
margin-bottom: 20px;
|
||
border-collapse: collapse;
|
||
}
|
||
.widget-header {
|
||
background-color: #334155 !important;
|
||
color: white !important;
|
||
padding: 10px !important;
|
||
font-weight: bold;
|
||
border-radius: 7px 7px 0 0;
|
||
}
|
||
.widget-content td { padding: 8px 15px !important; border-bottom: 1px solid #eee !important; }
|
||
|
||
/* 页脚 */
|
||
.footer-row td {
|
||
background-color: var(--panel) !important;
|
||
color: #94a3b8 !important;
|
||
border-top: 1px solid var(--border) !important;
|
||
padding: 20px !important;
|
||
}
|
||
|
||
/* === 新增样式: 模态框 (Modal) === */
|
||
.modal-overlay {
|
||
position: fixed; top: 0; left: 0; width: 100%; height: 100%;
|
||
background: rgba(0, 0, 0, 0.5);
|
||
display: none; justify-content: center; align-items: center;
|
||
z-index: 1000; opacity: 0; transition: opacity 0.3s ease;
|
||
}
|
||
.modal-overlay.show { display: flex; opacity: 1; }
|
||
.modal-window {
|
||
background: white; width: 600px; max-width: 90%;
|
||
border-radius: 8px; box-shadow: 0 10px 25px rgba(0,0,0,0.2);
|
||
transform: translateY(-20px); transition: transform 0.3s ease;
|
||
display: flex; flex-direction: column; overflow: hidden;
|
||
}
|
||
.modal-overlay.show .modal-window { transform: translateY(0); }
|
||
.modal-header {
|
||
padding: 15px 20px; background: #f8fafc; border-bottom: 1px solid #e2e8f0;
|
||
display: flex; justify-content: space-between; align-items: center;
|
||
}
|
||
.modal-title { margin: 0; font-size: 1.1rem; font-weight: 600; color: #334155; }
|
||
.modal-close { cursor: pointer; font-size: 1.5rem; color: #94a3b8; line-height: 1; }
|
||
.modal-body { padding: 20px; overflow-y: auto; max-height: 70vh; }
|
||
.modal-footer {
|
||
padding: 15px 20px; background: #f8fafc; border-top: 1px solid #e2e8f0;
|
||
text-align: right;
|
||
}
|
||
|
||
/* 表单组样式 */
|
||
.form-group { margin-bottom: 15px; }
|
||
.form-group label { display: block; margin-bottom: 5px; font-weight: 500; font-size: 0.9rem; }
|
||
.form-group input, .form-group select, .form-group textarea {
|
||
width: 100%; padding: 8px; border: 1px solid #cbd5e1; border-radius: 4px;
|
||
box-sizing: border-box; font-family: inherit;
|
||
}
|
||
.form-group .hint { font-size: 0.8rem; color: #64748b; margin-top: 4px; }
|
||
|
||
/* === 新增样式: 分页组件 (Pagination) === */
|
||
.pagination-container {
|
||
display: flex; justify-content: space-between; align-items: center;
|
||
margin-top: 20px; padding: 10px; background: #fff; border-radius: 8px;
|
||
border: 1px solid #e2e8f0;
|
||
}
|
||
.pagination-info { font-size: 0.9rem; color: #64748b; }
|
||
.pagination-controls { display: flex; gap: 5px; }
|
||
.page-btn {
|
||
padding: 5px 10px; border: 1px solid #e2e8f0; background: white;
|
||
cursor: pointer; border-radius: 4px; font-size: 0.9rem; color: #475569;
|
||
}
|
||
.page-btn:hover { background: #f1f5f9; border-color: #cbd5e1; }
|
||
.page-btn.active { background: var(--primary); color: white; border-color: var(--primary); }
|
||
.page-btn:disabled { opacity: 0.5; cursor: not-allowed; }
|
||
|
||
/* === 新增样式: Toast 通知 === */
|
||
.toast-container {
|
||
position: fixed; bottom: 20px; right: 20px; z-index: 2000;
|
||
display: flex; flex-direction: column; gap: 10px;
|
||
}
|
||
.toast {
|
||
background: white; border-left: 4px solid var(--primary);
|
||
padding: 15px 20px; border-radius: 4px; box-shadow: 0 4px 6px rgba(0,0,0,0.1);
|
||
min-width: 250px; transform: translateX(100%); transition: transform 0.3s ease;
|
||
display: flex; align-items: center; justify-content: space-between;
|
||
}
|
||
.toast.show { transform: translateX(0); }
|
||
.toast-success { border-left-color: #10b981; }
|
||
.toast-error { border-left-color: #ef4444; }
|
||
.toast-warning { border-left-color: #f59e0b; }
|
||
|
||
/* === 新增样式: 只有 CSS 的加载动画 === */
|
||
.spinner {
|
||
width: 20px; height: 20px; border: 2px solid #f3f3f3;
|
||
border-top: 2px solid var(--primary); border-radius: 50%;
|
||
animation: spin 1s linear infinite; display: inline-block; vertical-align: middle;
|
||
}
|
||
@keyframes spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } }
|
||
</style>
|
||
</head>
|
||
<body bgcolor="#e0e0e0" text="#000000" link="#0000FF" vlink="#800080" alink="#FF0000">
|
||
|
||
<table width="100%" border="1" cellpadding="5" cellspacing="0" bgcolor="#cccccc" class="main-layout-table">
|
||
<tr class="header-row">
|
||
<td colspan="3" align="center">
|
||
<h1>内部库存管控系统 V4.2 (非密级)</h1>
|
||
<p>当前登录: OPERATOR_8821 | <a href="#" onclick="alert('模拟注销')">[注销]</a> | <a href="#">[切换节点]</a> | <a href="#">[系统日志]</a> | <a href="#">[报错]</a></p>
|
||
</td>
|
||
</tr>
|
||
<tr>
|
||
<td width="20%" valign="top" class="layout-cell sidebar-nav">
|
||
<h3>快速导航</h3>
|
||
<ul id="sidebarList">
|
||
<li><a href="javascript:void(0)" onclick="filterCategory('all')">📌 概览面板</a></li>
|
||
<li><a href="javascript:void(0)" onclick="filterCategory('urgent')">🔥 入库申请 (急)</a></li>
|
||
<li><a href="javascript:void(0)" onclick="filterCategory('normal')">📥 入库申请 (常规)</a></li>
|
||
<li><a href="javascript:void(0)" onclick="showToast('归档数据需管理员权限', 'warning')">🗄️ 入库申请 (存档)</a></li>
|
||
<li><a href="javascript:void(0)" onclick="filterCategory('out_a')">📤 出库审批 (A区)</a></li>
|
||
<li><a href="javascript:void(0)" onclick="filterCategory('out_b')">📤 出库审批 (B区)</a></li>
|
||
<li><a href="javascript:void(0)" onclick="showToast('C区系统维护中', 'error')">🚫 出库审批 (C区)</a></li>
|
||
<li><a href="javascript:void(0)" onclick="openModal('reportModal')">📉 报损登记</a></li>
|
||
<li><a href="javascript:void(0)" onclick="showToast('权限不足: 仅HR可访问', 'error')">👥 人员管理</a></li>
|
||
<li><a href="javascript:void(0)" onclick="showToast('正在连接财务接口...', 'info')">💰 财务对接</a></li>
|
||
<li><a href="javascript:void(0)" onclick="openModal('settingsModal')">⚙️ 系统设置</a></li>
|
||
<li><a href="javascript:void(0)" onclick="window.print()">🖨️ 打印测试页</a></li>
|
||
<li class="legacy-link"><a href="#">旧版入口 (已停用)</a></li>
|
||
<li class="legacy-link"><a href="#">旧版入口 V2 (已停用)</a></li>
|
||
<li class="legacy-link"><a href="#">帮助文档 1998版</a></li>
|
||
<li><a href="javascript:void(0)" onclick="openHelp()">❓ 帮助与支持</a></li>
|
||
<li><a href="javascript:void(0)" onclick="showToast('无需下载,浏览器已支持', 'success')">💾 下载控件</a></li>
|
||
<li><a href="javascript:void(0)" onclick="location.reload()">🔄 刷新系统</a></li>
|
||
</ul>
|
||
<hr>
|
||
<div style="background:#fff7ed; padding:10px; border-radius:4px; font-size:0.9em; border:1px solid #ffedd5;">
|
||
<p style="margin:0; color:#c2410c;"><b>系统广播:</b><br>请注意,服务器将于今晚 03:00 进行维护,请勿在此期间提交表单。</p>
|
||
</div>
|
||
<p style="text-align:center; color:#888; margin-top:10px;"><b>今日格言:</b><br>安全生产,效率第一。</p>
|
||
</td>
|
||
|
||
<td width="60%" valign="top" class="layout-cell main-content">
|
||
<h2>库存列表 - 区域 A1</h2>
|
||
|
||
<form id="searchForm" onsubmit="event.preventDefault(); handleSearch();">
|
||
<table border="0" width="100%" class="filter-table">
|
||
<tr>
|
||
<td>关键词: <input type="text" id="searchInput" name="kw" size="30" placeholder="输入 ID 或名称"></td>
|
||
<td>
|
||
类型:
|
||
<select id="typeSelect" name="type">
|
||
<option value="all">-- 所有 --</option>
|
||
<option value="res">电阻 (Resistor)</option>
|
||
<option value="cap">电容 (Capacitor)</option>
|
||
<option value="ind">电感 (Inductor)</option>
|
||
<option value="chip">芯片 (Chip)</option>
|
||
<option value="conn">连接器 (Connector)</option>
|
||
<option value="other">其他 (Other)</option>
|
||
</select>
|
||
</td>
|
||
<td>
|
||
状态:
|
||
<label><input type="checkbox" name="st_normal" value="NORMAL" checked> 正常</label>
|
||
<label><input type="checkbox" name="st_low" value="LOW" checked> 警告</label>
|
||
<label><input type="checkbox" name="st_crit" value="CRITICAL" checked> 严重</label>
|
||
</td>
|
||
</tr>
|
||
<tr>
|
||
<td colspan="3" style="padding-top:10px;">
|
||
排序:
|
||
<label><input type="radio" name="sort" value="date" checked> 按更新日期</label>
|
||
<label><input type="radio" name="sort" value="id"> 按ID</label>
|
||
<label><input type="radio" name="sort" value="qty"> 按库存量</label>
|
||
<br><br>
|
||
<button type="submit" class="page-btn active" style="padding:6px 20px;">
|
||
<span id="search-spinner" class="spinner" style="display:none; width:12px; height:12px; border-width:2px; vertical-align:middle; margin-right:5px;"></span>
|
||
开始检索
|
||
</button>
|
||
<button type="button" class="page-btn" onclick="resetSearch()">重置条件</button>
|
||
<button type="button" class="page-btn" disabled style="opacity:0.6; cursor:not-allowed;">导出Excel</button>
|
||
</td>
|
||
</tr>
|
||
</table>
|
||
</form>
|
||
<hr style="border:0; margin:20px 0;">
|
||
|
||
<table border="1" width="100%" cellpadding="3" cellspacing="1" class="data-table" id="inventoryTable">
|
||
<thead>
|
||
<tr bgcolor="#999999">
|
||
<th width="40"><input type="checkbox" id="selectAll"></th>
|
||
<th width="100">ID</th>
|
||
<th>物料名称</th>
|
||
<th width="120">批次号</th>
|
||
<th width="80">库存量</th>
|
||
<th width="80">重量(g)</th>
|
||
<th width="100">入库日期</th>
|
||
<th width="100">状态码</th>
|
||
<th width="180">操作</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody id="tableBody">
|
||
<!-- 数据将由 JS 动态生成 -->
|
||
<tr><td colspan="9" align="center" style="padding: 50px;">正在加载库存数据... <div class="spinner"></div></td></tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
<!-- 新版分页组件 -->
|
||
<div class="pagination-container">
|
||
<div class="pagination-info">
|
||
显示第 <span id="startRecord">0</span> 到 <span id="endRecord">0</span> 条,共 <span id="totalRecords">0</span> 条记录
|
||
</div>
|
||
<div style="display:flex; align-items:center; gap:10px;">
|
||
<select id="pageSizeSelect" onchange="changePageSize()">
|
||
<option value="10">10条/页</option>
|
||
<option value="20">20条/页</option>
|
||
<option value="50">50条/页</option>
|
||
<option value="100">100条/页</option>
|
||
</select>
|
||
<div class="pagination-controls" id="paginationControls">
|
||
<!-- 分页按钮由 JS 生成 -->
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<br><br>
|
||
<table border="1" width="100%" bgcolor="#eeeeee" style="border:none; border-radius:8px; overflow:hidden; background:#f1f5f9;">
|
||
<tr>
|
||
<td style="padding:20px; border:none;">
|
||
<h4 style="margin-top:0;">批量操作控制台</h4>
|
||
<p>选中项操作:
|
||
<select id="batchActionSelect">
|
||
<option>-- 请选择操作 --</option>
|
||
<option>批量导出</option>
|
||
<option>批量删除 (需管理员权限)</option>
|
||
<option>转移仓库</option>
|
||
</select>
|
||
<input type="button" value="执行" onclick="executeBatchAction()">
|
||
</p>
|
||
<p style="font-size:0.9em; color:#666;">
|
||
<label><input type="checkbox"> 我已阅读并同意《数据安全操作规范 v9.0》</label><br>
|
||
<label><input type="checkbox"> 确认非误操作</label>
|
||
</p>
|
||
</td>
|
||
</tr>
|
||
</table>
|
||
|
||
</td>
|
||
|
||
<td width="20%" valign="top" class="layout-cell sidebar-widgets">
|
||
<table border="1" width="100%" class="widget-table">
|
||
<tr><td bgcolor="#000000" class="widget-header"><font color="white" align="center"><b>服务器负载</b></font></td></tr>
|
||
<tbody class="widget-content">
|
||
<tr><td>CPU: <div style="display:inline-block; width:50px; height:8px; background:#e2e8f0; border-radius:4px;"><div style="width:12%; height:100%; background:green; border-radius:4px;"></div></div> 12%</td></tr>
|
||
<tr><td>RAM: <div style="display:inline-block; width:50px; height:8px; background:#e2e8f0; border-radius:4px;"><div style="width:64%; height:100%; background:orange; border-radius:4px;"></div></div> 64%</td></tr>
|
||
<tr><td>DISK: <div style="display:inline-block; width:50px; height:8px; background:#e2e8f0; border-radius:4px;"><div style="width:98%; height:100%; background:red; border-radius:4px;"></div></div> <span style="color:red">98% (警告)</span></td></tr>
|
||
</tbody>
|
||
</table>
|
||
<br>
|
||
<table border="1" width="100%" class="widget-table">
|
||
<tr><td bgcolor="#000000" class="widget-header"><font color="white"><b>待办事项</b></font></td></tr>
|
||
<tbody class="widget-content">
|
||
<tr><td><label><input type="checkbox"> 审批张三的请假</label></td></tr>
|
||
<tr><td><label><input type="checkbox"> 订购咖啡豆</label></td></tr>
|
||
<tr><td><label><input type="checkbox"> 修复打印机</label></td></tr>
|
||
<tr><td><label><input type="checkbox"> 更新防火墙</label></td></tr>
|
||
<tr><td><label><input type="checkbox"> 年底报表汇总</label></td></tr>
|
||
</tbody>
|
||
</table>
|
||
<br>
|
||
<center>
|
||
<p style="font-size:0.8rem; color:#888;">扫码下载APP</p>
|
||
<div style="background:white; padding:10px; border-radius:8px; display:inline-block; border:1px solid #ddd;">
|
||
<table border="1" width="100" height="100" style="border:none;">
|
||
<tr><td align="center" style="border:none;">QR CODE</td></tr>
|
||
</table>
|
||
</div>
|
||
</center>
|
||
</td>
|
||
</tr>
|
||
|
||
<tr class="footer-row">
|
||
<td colspan="3" bgcolor="#333333" align="center">
|
||
<font color="#ffffff" size="2">
|
||
© 2005-2025 Galactic Logistics Corp. All Rights Reserved.<br>
|
||
Address: Sector 7G, Industrial Zone, Mars Colony.<br>
|
||
<a href="#" style="color: #aaaaaa">Privacy Policy</a> |
|
||
<a href="#" style="color: #aaaaaa">Terms of Service</a> |
|
||
<a href="#" style="color: #aaaaaa">Sitemap</a> |
|
||
<a href="#" style="color: #aaaaaa">Report Abuse</a>
|
||
<br>
|
||
<span style="opacity:0.5;">Render Time: 0.04s | SQL Queries: 142 | Memory: 4MB</span>
|
||
</font>
|
||
</td>
|
||
</tr>
|
||
</table>
|
||
|
||
|
||
<!-- === 隐藏的模态框 (HTML 结构) === -->
|
||
|
||
<!-- 1. 编辑/新增 模态框 -->
|
||
<div class="modal-overlay" id="editModal">
|
||
<div class="modal-window">
|
||
<div class="modal-header">
|
||
<h3 class="modal-title">库存条目编辑</h3>
|
||
<span class="modal-close" onclick="closeModal('editModal')">×</span>
|
||
</div>
|
||
<div class="modal-body">
|
||
<form id="editForm">
|
||
<div class="form-group">
|
||
<label>物料 ID (只读)</label>
|
||
<input type="text" id="edit_id" readonly style="background:#f1f5f9; color:#64748b;">
|
||
</div>
|
||
<div class="form-group">
|
||
<label>物料名称 <span style="color:red">*</span></label>
|
||
<input type="text" id="edit_name" required>
|
||
<div class="hint">请输入完整的器件型号,例如: Resistor 0603 10k 1%</div>
|
||
</div>
|
||
<div class="form-group">
|
||
<label>供应商</label>
|
||
<select id="edit_supplier">
|
||
<option value="Alpha Corp">Alpha Corp - 优质供应商</option>
|
||
<option value="Beta Ltd">Beta Ltd - 长期合作</option>
|
||
<option value="Gamma Inc">Gamma Inc</option>
|
||
<option value="ConnWorld">ConnWorld - 连接器专供</option>
|
||
<option value="ST Micro">ST Micro - 芯片原厂</option>
|
||
<option value="Other">其他 (需备注)</option>
|
||
</select>
|
||
</div>
|
||
<div class="form-group">
|
||
<div style="display:flex; gap:10px;">
|
||
<div style="flex:1;">
|
||
<label>库存数量</label>
|
||
<input type="number" id="edit_qty" min="0">
|
||
</div>
|
||
<div style="flex:1;">
|
||
<label>单重 (g)</label>
|
||
<input type="number" id="edit_weight" step="0.01">
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="form-group">
|
||
<label>状态</label>
|
||
<select id="edit_status">
|
||
<option value="NORMAL">正常 (NORMAL)</option>
|
||
<option value="LOW">库存不足 (LOW)</option>
|
||
<option value="CRITICAL">严重短缺 (CRITICAL)</option>
|
||
<option value="DAMAGED">已损坏 (DAMAGED)</option>
|
||
<option value="PENDING">待检 (PENDING)</option>
|
||
</select>
|
||
</div>
|
||
<div class="form-group">
|
||
<label>备注说明</label>
|
||
<textarea id="edit_notes" rows="3"></textarea>
|
||
</div>
|
||
</form>
|
||
</div>
|
||
<div class="modal-footer">
|
||
<button class="page-btn" onclick="closeModal('editModal')">取消</button>
|
||
<button class="page-btn active" onclick="saveEdit()">保存更改</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 2. 详情 模态框 -->
|
||
<div class="modal-overlay" id="detailModal">
|
||
<div class="modal-window">
|
||
<div class="modal-header">
|
||
<h3 class="modal-title">物料详情档案</h3>
|
||
<span class="modal-close" onclick="closeModal('detailModal')">×</span>
|
||
</div>
|
||
<div class="modal-body">
|
||
<table width="100%" border="0" cellpadding="8" style="background:#f8fafc; border-radius:8px;">
|
||
<tr>
|
||
<td width="30%" align="right" style="color:#64748b;">内部编号:</td>
|
||
<td width="70%"><b id="detail_id">#---</b></td>
|
||
</tr>
|
||
<tr>
|
||
<td align="right" style="color:#64748b;">物料名称:</td>
|
||
<td><span id="detail_name">---</span></td>
|
||
</tr>
|
||
<tr>
|
||
<td align="right" style="color:#64748b;">批次号:</td>
|
||
<td><code id="detail_batch" style="background:#e2e8f0; padding:2px 4px; border-radius:3px;">---</code></td>
|
||
</tr>
|
||
<tr>
|
||
<td align="right" style="color:#64748b;">实时库存:</td>
|
||
<td><span id="detail_qty">0</span> 单位</td>
|
||
</tr>
|
||
<tr>
|
||
<td align="right" style="color:#64748b;">入库时间:</td>
|
||
<td><span id="detail_date">---</span></td>
|
||
</tr>
|
||
<tr>
|
||
<td align="right" style="color:#64748b;">生命周期状态:</td>
|
||
<td><span id="detail_status">---</span></td>
|
||
</tr>
|
||
</table>
|
||
<div style="margin-top:20px;">
|
||
<h4 style="border-bottom:1px solid #ddd; padding-bottom:5px;">最近流转记录</h4>
|
||
<ul style="font-size:0.9rem; color:#475569; padding-left:20px;" id="detail_history">
|
||
<li>暂无记录</li>
|
||
</ul>
|
||
</div>
|
||
<div style="margin-top:20px; background:#fff7ed; padding:10px; border-left:4px solid #f97316; font-size:0.9rem;">
|
||
<strong>注意:</strong> 该物料属于精密器件,请轻拿轻放,存储温度控制在 20-25℃。
|
||
</div>
|
||
</div>
|
||
<div class="modal-footer">
|
||
<button class="page-btn" onclick="printDetail()">打印档案</button>
|
||
<button class="page-btn" onclick="closeModal('detailModal')">关闭</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 3. 系统设置 模态框 -->
|
||
<div class="modal-overlay" id="settingsModal">
|
||
<div class="modal-window">
|
||
<div class="modal-header">
|
||
<h3 class="modal-title">系统偏好设置</h3>
|
||
<span class="modal-close" onclick="closeModal('settingsModal')">×</span>
|
||
</div>
|
||
<div class="modal-body">
|
||
<h4 style="border-bottom: 2px solid var(--primary); padding-bottom: 5px;">界面显示</h4>
|
||
<div class="form-group">
|
||
<label>主题模式</label>
|
||
<select id="themeSelect" onchange="showToast('主题切换功能开发中...', 'warning')">
|
||
<option value="light">明亮 (默认)</option>
|
||
<option value="dark">深色 (Dark Mode)</option>
|
||
<option value="auto">跟随系统</option>
|
||
</select>
|
||
</div>
|
||
<div class="form-group">
|
||
<label><input type="checkbox" checked> 启用动画效果</label><br>
|
||
<label><input type="checkbox" checked> 显示实时时钟</label><br>
|
||
<label><input type="checkbox"> 紧凑表格模式</label>
|
||
</div>
|
||
|
||
<h4 style="border-bottom: 2px solid var(--primary); padding-bottom: 5px; margin-top:20px;">数据与隐私</h4>
|
||
<div class="form-group">
|
||
<label>自动刷新间隔</label>
|
||
<select>
|
||
<option value="0">关闭</option>
|
||
<option value="30">30 秒</option>
|
||
<option value="60">1 分钟</option>
|
||
<option value="300">5 分钟</option>
|
||
</select>
|
||
</div>
|
||
<div class="form-group">
|
||
<label><input type="checkbox" checked> 允许本地缓存 (LocalStorage)</label><br>
|
||
<label><input type="checkbox"> 发送匿名使用统计</label>
|
||
</div>
|
||
|
||
<h4 style="border-bottom: 2px solid var(--primary); padding-bottom: 5px; margin-top:20px;">通知设置</h4>
|
||
<div class="form-group">
|
||
<label><input type="checkbox" checked> 库存不足警告</label><br>
|
||
<label><input type="checkbox" checked> 审批通过通知</label><br>
|
||
<label><input type="checkbox"> 每日报表推送</label>
|
||
</div>
|
||
</div>
|
||
<div class="modal-footer">
|
||
<button class="page-btn" onclick="closeModal('settingsModal')">取消</button>
|
||
<button class="page-btn active" onclick="closeModal('settingsModal'); showToast('设置已保存', 'success')">保存配置</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 4. 帮助文档 模态框 (长文本) -->
|
||
<div class="modal-overlay" id="helpModal">
|
||
<div class="modal-window" style="width:800px;">
|
||
<div class="modal-header">
|
||
<h3 class="modal-title">用户操作指南 v4.2</h3>
|
||
<span class="modal-close" onclick="closeModal('helpModal')">×</span>
|
||
</div>
|
||
<div class="modal-body" style="line-height:1.8; color:#334155;">
|
||
<h3>1. 系统概述</h3>
|
||
<p>本系统(SYS_V4.2)旨在为 Galactic Logistics Corp 提供高效、透明的内部库存管理解决方案。系统集成了物资入库、库存监控、出库审批及报表生成四大核心模块。</p>
|
||
|
||
<h3>2. 快速入门</h3>
|
||
<ul>
|
||
<li><strong>登录系统:</strong> 使用分配的工号(如 OPERATOR_8821)和动态令牌登录。</li>
|
||
<li><strong>浏览库存:</strong> 在主界面查看当前仓库的实时库存状态。支持按名称、ID、状态筛选。</li>
|
||
<li><strong>数据编辑:</strong> 点击表格右侧的 [编辑] 按钮修改库存数量或状态。</li>
|
||
<li><strong>导出报表:</strong> 勾选需要的数据行,从底部“批量操作控制台”选择“批量导出”。</li>
|
||
</ul>
|
||
|
||
<h3>3. 状态码说明</h3>
|
||
<table width="100%" border="1" cellpadding="5" style="border-collapse:collapse; border-color:#e2e8f0;">
|
||
<tr bgcolor="#f1f5f9"><th>状态码</th><th>含义</th><th>处理建议</th></tr>
|
||
<tr><td>NORMAL</td><td>库存正常</td><td>无需操作,定期盘点。</td></tr>
|
||
<tr><td style="color:orange">LOW</td><td>库存偏低</td><td>建议发起采购申请,补充库存至安全水位。</td></tr>
|
||
<tr><td style="color:red">CRITICAL</td><td>严重短缺</td><td>立即联系供应商紧急补货,可能会影响生产线。</td></tr>
|
||
<tr><td style="color:gray">DAMAGED</td><td>已损坏</td><td>物资已不可用,请发起报废流程并隔离存放。</td></tr>
|
||
<tr><td>PENDING</td><td>待检</td><td>物资刚入库或等待质检,暂时冻结出库。</td></tr>
|
||
</table>
|
||
|
||
<h3>4. 常见问题 (FAQ)</h3>
|
||
<p><strong>Q: 为什么找不到刚才添加的数据?</strong><br>
|
||
A: 请确筛选器设置正确。如果问题依旧,请尝试点击左侧导航栏的“刷新系统”。</p>
|
||
|
||
<p><strong>Q: 可以在手机上使用本系统吗?</strong><br>
|
||
A: 可以。本系统采用响应式设计(虽然主要针对桌面优化),支持主流移动浏览器。</p>
|
||
|
||
<p><strong>Q: 如何申请管理员权限?</strong><br>
|
||
A: 请填写《IT权限变更申请表》并提交至 Sector 7G 信息中心,审批周期约 3 个工作日。</p>
|
||
|
||
<hr>
|
||
<p style="font-size:0.8rem; color:#94a3b8;">最后更新时间: 2025-10-15 | 文档版本: 4.2.1-beta</p>
|
||
</div>
|
||
<div class="modal-footer">
|
||
<button class="page-btn" onclick="window.open('https://example.com/manual.pdf')">下载 PDF 版</button>
|
||
<button class="page-btn" onclick="closeModal('helpModal')">关闭</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 5. 报损登记 模态框 -->
|
||
<div class="modal-overlay" id="reportModal">
|
||
<div class="modal-window">
|
||
<div class="modal-header">
|
||
<h3 class="modal-title">报损登记表</h3>
|
||
<span class="modal-close" onclick="closeModal('reportModal')">×</span>
|
||
</div>
|
||
<div class="modal-body">
|
||
<p style="background:#fee2e2; padding:10px; color:#b91c1c; border-radius:4px;">
|
||
警告: 虚假报损属于严重违规行为。
|
||
</p>
|
||
<form>
|
||
<div class="form-group">
|
||
<label>关联物料 ID</label>
|
||
<input type="text" placeholder="输入 ID 或扫描条码">
|
||
</div>
|
||
<div class="form-group">
|
||
<label>报损原因</label>
|
||
<select>
|
||
<option>运输损坏</option>
|
||
<option>存储过期</option>
|
||
<option>受潮/氧化</option>
|
||
<option>人为损坏</option>
|
||
<option>盘点丢失</option>
|
||
</select>
|
||
</div>
|
||
<div class="form-group">
|
||
<label>责任人</label>
|
||
<input type="text" value="OPERATOR_8821 (当前用户)">
|
||
</div>
|
||
<div class="form-group">
|
||
<label>现场照片证据 (URL)</label>
|
||
<input type="text" placeholder="http://...">
|
||
</div>
|
||
</form>
|
||
</div>
|
||
<div class="modal-footer">
|
||
<button class="page-btn" onclick="closeModal('reportModal')">取消</button>
|
||
<button class="page-btn btn-danger" onclick="closeModal('reportModal'); showToast('报损单已提交审核', 'warning')">提交报损</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Toast 通知容器 -->
|
||
<div class="toast-container" id="toastContainer"></div>
|
||
|
||
<script>
|
||
// === JavaScript 核心逻辑重构 (SPA模式) ===
|
||
|
||
// 配置与状态
|
||
const CONFIG = {
|
||
totalItems: 358, // 模拟总数据量
|
||
defaultPageSize: 10,
|
||
suppliers: ["Alpha Corp", "Beta Ltd", "Gamma Inc", "ConnWorld", "ST Micro", "Texas Inst", "NXP", "Murata"],
|
||
statuses: ["NORMAL", "LOW", "CRITICAL", "DAMAGED", "PENDING"],
|
||
components: [
|
||
"Resistor 10k 0603", "Capacitor 100nF", "Inductor 22uH", "MCU STM32F103", "Connector USB-C",
|
||
"LED Red 0805", "Diode 1N4148", "Transistor 2N2222", "Regulator 3.3V", "Crystal 16MHz",
|
||
"Battery Holder", "Fuse 500mA", "Switch Tactile", "Header 2.54mm", "PCB Stiffener"
|
||
]
|
||
};
|
||
|
||
let state = {
|
||
data: [], // 所有数据
|
||
filteredData: [], // 筛选后的数据
|
||
currentPage: 1,
|
||
pageSize: 10,
|
||
sortBy: 'id', // id, date, qty
|
||
sortDesc: false
|
||
};
|
||
|
||
// --- 导航与通用模态框 ---
|
||
|
||
// --- 搜索逻辑 ---
|
||
function handleSearch() {
|
||
const spinner = document.getElementById('search-spinner');
|
||
spinner.style.display = 'inline-block';
|
||
|
||
// 获取筛选条件
|
||
const kw = document.getElementById('searchInput').value.toLowerCase().trim();
|
||
const typeFilter = document.getElementById('typeSelect').value;
|
||
const checkedStatuses = [];
|
||
if(document.querySelector('input[name="st_normal"]').checked) checkedStatuses.push('NORMAL');
|
||
if(document.querySelector('input[name="st_low"]').checked) checkedStatuses.push('LOW');
|
||
if(document.querySelector('input[name="st_crit"]').checked) checkedStatuses.push('CRITICAL');
|
||
|
||
// 获取排序
|
||
const sortVal = document.querySelector('input[name="sort"]:checked').value;
|
||
|
||
// 模拟网络延迟
|
||
setTimeout(() => {
|
||
state.filteredData = state.data.filter(item => {
|
||
// 1. 关键词匹配 (ID, Name, Supplier, Batch)
|
||
const matchKw = !kw ||
|
||
item.id.toLowerCase().includes(kw) ||
|
||
item.name.toLowerCase().includes(kw) ||
|
||
item.supplier.toLowerCase().includes(kw) ||
|
||
item.batch.toLowerCase().includes(kw);
|
||
|
||
// 2. 类型匹配 (简单的字符串包含判断)
|
||
let matchType = true;
|
||
if (typeFilter !== 'all') {
|
||
if (typeFilter === 'res') matchType = item.name.includes('Resistor');
|
||
else if (typeFilter === 'cap') matchType = item.name.includes('Capacitor');
|
||
else if (typeFilter === 'ind') matchType = item.name.includes('Inductor');
|
||
else if (typeFilter === 'chip') matchType = item.name.includes('MCU') || item.name.includes('Regulator') || item.name.includes('Transistor') || item.name.includes('Diode');
|
||
else if (typeFilter === 'conn') matchType = item.name.includes('Connector') || item.name.includes('Header');
|
||
else if (typeFilter === 'other') matchType = !item.name.includes('Resist') && !item.name.includes('Capacit') && !item.name.includes('Induct') && !item.name.includes('Connect');
|
||
}
|
||
|
||
// 3. 状态匹配 (注意: 之前生成的 Mock Status 包含 DAMAGED/PENDING,但Checkbox只有三种,需兼容)
|
||
// 这里只筛选三种主要显示状态,如果用户没选也没事
|
||
let matchStatus = false;
|
||
if (checkedStatuses.length === 0) matchStatus = true; // 如果全不选,默认全显示?或者全不显示?通常全不选意味着什么都不看,或者看全部...
|
||
// 为了用户体验,全不选就当全选吧 (或者严谨点就是空)
|
||
// 这里逻辑:只要item.status在checkedStatuses里就显示
|
||
// 特殊处理: CRITICAL 包含了 Mock 里的 CRITICAL 和 DAMAGED (假设)
|
||
if (checkedStatuses.includes(item.status)) matchStatus = true;
|
||
|
||
// 修正逻辑:如果勾选了CRITICAL,我们把DAMAGED也算进去?
|
||
if (checkedStatuses.includes('CRITICAL') && item.status === 'DAMAGED') matchStatus = true;
|
||
|
||
return matchKw && matchType && matchStatus;
|
||
});
|
||
|
||
// 排序
|
||
if (sortVal === 'id') {
|
||
state.filteredData.sort((a,b) => a.id.localeCompare(b.id));
|
||
} else if (sortVal === 'qty') {
|
||
state.filteredData.sort((a,b) => b.qty - a.qty); // Fixed: property name is qty
|
||
} else if (sortVal === 'date') {
|
||
state.filteredData.sort((a,b) => new Date(b.date) - new Date(a.date));
|
||
}
|
||
|
||
state.currentPage = 1;
|
||
renderTable();
|
||
spinner.style.display = 'none';
|
||
showToast(`检索完成,找到 ${state.filteredData.length} 条记录`, 'success');
|
||
}, 400);
|
||
}
|
||
|
||
function resetSearch() {
|
||
document.getElementById('searchInput').value = '';
|
||
document.getElementById('typeSelect').value = 'all';
|
||
document.querySelectorAll('input[type="checkbox"]').forEach(c => c.checked = true);
|
||
document.querySelector('input[value="date"]').checked = true;
|
||
handleSearch();
|
||
}
|
||
|
||
function openModal(id) {
|
||
const modal = document.getElementById(id);
|
||
if (modal) {
|
||
modal.style.display = 'flex';
|
||
modal.offsetHeight; // force reflow
|
||
modal.classList.add('show');
|
||
}
|
||
}
|
||
|
||
function openHelp() {
|
||
openModal('helpModal');
|
||
}
|
||
|
||
// 侧边栏分类筛选模拟
|
||
function filterCategory(category) {
|
||
// 重置所有选中状态
|
||
document.querySelectorAll('#sidebarList a').forEach(a => {
|
||
a.style.color = '';
|
||
a.style.backgroundColor = '';
|
||
});
|
||
// 高亮当前(简单处理,实际应传this)
|
||
|
||
showToast(`正在切换至视图: ${category}`, 'info');
|
||
|
||
// 模拟数据筛选逻辑
|
||
if (category === 'all') {
|
||
state.filteredData = [...state.data];
|
||
document.querySelector('.main-content h2').innerText = '库存列表 - 所有';
|
||
} else if (category === 'urgent') {
|
||
document.querySelector('.main-content h2').innerText = '库存列表 - 急需补货';
|
||
state.filteredData = state.data.filter(d => d.status === 'CRITICAL' || d.status === 'LOW');
|
||
} else if (category === 'normal') {
|
||
document.querySelector('.main-content h2').innerText = '库存列表 - 正常状态';
|
||
state.filteredData = state.data.filter(d => d.status === 'NORMAL');
|
||
} else if (category === 'out_a') {
|
||
document.querySelector('.main-content h2').innerText = '出库审批 - A 区';
|
||
// 随机造一点假象
|
||
state.filteredData = state.data.filter(d => d.id.endsWith('1') || d.id.endsWith('3'));
|
||
} else if (category === 'out_b') {
|
||
document.querySelector('.main-content h2').innerText = '出库审批 - B 区';
|
||
state.filteredData = state.data.filter(d => d.id.endsWith('2') || d.id.endsWith('4'));
|
||
}
|
||
|
||
// 重置分页
|
||
state.currentPage = 1;
|
||
renderTable();
|
||
}
|
||
|
||
// --- 1. 数据生成器 (模拟后端数据库) ---
|
||
function generateMockData() {
|
||
console.time("DataGeneration");
|
||
const data = [];
|
||
for (let i = 1; i <= CONFIG.totalItems; i++) {
|
||
const padId = String(i).padStart(4, '0');
|
||
const typeIdx = Math.floor(Math.random() * CONFIG.components.length);
|
||
const supplIdx = Math.floor(Math.random() * CONFIG.suppliers.length);
|
||
const statusIdx = Math.random() > 0.8 ? (Math.random() > 0.5 ? 2 : 1) : 0; // 大部分正常
|
||
|
||
// 随机日期 (过去2年内)
|
||
const date = new Date(Date.now() - Math.floor(Math.random() * 63072000000));
|
||
const batchYear = date.getFullYear();
|
||
const batchCode = `BATCH_${batchYear}_${String.fromCharCode(65 + Math.floor(Math.random() * 26))}${Math.floor(Math.random()*100)}`;
|
||
|
||
data.push({
|
||
id: `8821${padId}`,
|
||
name: CONFIG.components[typeIdx],
|
||
supplier: CONFIG.suppliers[supplIdx],
|
||
batch: batchCode,
|
||
qty: Math.floor(Math.random() * 10000),
|
||
weight: (Math.random() * 10).toFixed(2),
|
||
date: date.toISOString().split('T')[0],
|
||
status: CONFIG.statuses[statusIdx],
|
||
notes: "自动生成的模拟数据记录..."
|
||
});
|
||
}
|
||
console.timeEnd("DataGeneration");
|
||
return data;
|
||
}
|
||
|
||
// --- 2. 核心渲染逻辑 ---
|
||
function init() {
|
||
showToast("系统初始化中...", "info");
|
||
state.data = generateMockData();
|
||
applyFilter(); // 初始筛选(就是全量)
|
||
console.log(`Loaded ${state.data.length} records.`);
|
||
|
||
// 绑定事件
|
||
document.getElementById('editForm').addEventListener('submit', (e) => { e.preventDefault(); saveEdit(); });
|
||
}
|
||
|
||
// 筛选与排序
|
||
function applyFilter() {
|
||
// 简单模拟:获取搜索框的值 (这里简化了,实际可以读取DOM)
|
||
// 省略复杂的DOM读取,直接用全部数据演示分页
|
||
state.filteredData = [...state.data];
|
||
|
||
// 排序
|
||
state.filteredData.sort((a, b) => {
|
||
let valA = a[state.sortBy];
|
||
let valB = b[state.sortBy];
|
||
if (state.sortBy === 'qty' || state.sortBy === 'weight') {
|
||
valA = parseFloat(valA);
|
||
valB = parseFloat(valB);
|
||
}
|
||
if (valA < valB) return state.sortDesc ? 1 : -1;
|
||
if (valA > valB) return state.sortDesc ? -1 : 1;
|
||
return 0;
|
||
});
|
||
|
||
state.currentPage = 1;
|
||
renderTable();
|
||
}
|
||
|
||
function renderTable() {
|
||
const tbody = document.getElementById('tableBody');
|
||
tbody.innerHTML = ''; // 清空
|
||
|
||
const start = (state.currentPage - 1) * state.pageSize;
|
||
const end = Math.min(start + state.pageSize, state.filteredData.length);
|
||
const pageData = state.filteredData.slice(start, end);
|
||
|
||
if (pageData.length === 0) {
|
||
tbody.innerHTML = '<tr><td colspan="9" align="center" style="padding:20px;">没有找到匹配的数据</td></tr>';
|
||
return;
|
||
}
|
||
|
||
pageData.forEach(item => {
|
||
const tr = document.createElement('tr');
|
||
tr.className = 'data-row';
|
||
|
||
// 状态样式
|
||
let statusColor = 'green';
|
||
if (item.status === 'LOW') statusColor = 'orange';
|
||
if (item.status === 'CRITICAL' || item.status === 'DAMAGED') statusColor = 'red';
|
||
|
||
tr.innerHTML = `
|
||
<td align="center"><input type="checkbox" class="row-check" value="${item.id}"></td>
|
||
<td><font face="monospace">#${item.id}</font></td>
|
||
<td>
|
||
<strong>${item.name}</strong><br>
|
||
<small style="color:#666">供应商: ${item.supplier}</small>
|
||
</td>
|
||
<td><code style="background:#f1f5f9; padding:2px;">${item.batch}</code></td>
|
||
<td>${item.qty.toLocaleString()}</td>
|
||
<td>${item.weight}g</td>
|
||
<td>${item.date}</td>
|
||
<td><span style="color:${statusColor}; font-weight:bold; font-size:0.85rem;">${item.status}</span></td>
|
||
<td class="action-group">
|
||
<a href="javascript:void(0)" class="btn-view" onclick="openDetail('${item.id}')">[详情]</a>
|
||
<a href="javascript:void(0)" class="btn-edit" onclick="openEdit('${item.id}')">[编辑]</a>
|
||
<a href="javascript:void(0)" class="btn-danger" onclick="deleteItem('${item.id}', this)">[删]</a>
|
||
</td>
|
||
`;
|
||
tbody.appendChild(tr);
|
||
});
|
||
|
||
renderPagination(start, end, state.filteredData.length);
|
||
}
|
||
|
||
function renderPagination(start, end, total) {
|
||
document.getElementById('startRecord').innerText = total === 0 ? 0 : start + 1;
|
||
document.getElementById('endRecord').innerText = end;
|
||
document.getElementById('totalRecords').innerText = total;
|
||
|
||
const controls = document.getElementById('paginationControls');
|
||
controls.innerHTML = '';
|
||
|
||
const totalPages = Math.ceil(total / state.pageSize);
|
||
|
||
// 简单的分页算法:首页,上一页,当前页-1, 当前页, 当前页+1, 下一页,末页
|
||
const createBtn = (text, page, active = false, disabled = false) => {
|
||
const btn = document.createElement('button');
|
||
btn.className = `page-btn ${active ? 'active' : ''}`;
|
||
btn.innerText = text;
|
||
btn.disabled = disabled;
|
||
if (!disabled) btn.onclick = () => { state.currentPage = page; renderTable(); };
|
||
return btn;
|
||
};
|
||
|
||
controls.appendChild(createBtn('<<', 1, false, state.currentPage === 1));
|
||
controls.appendChild(createBtn('<', state.currentPage - 1, false, state.currentPage === 1));
|
||
|
||
// 显示当前页附近
|
||
let pStart = Math.max(1, state.currentPage - 2);
|
||
let pEnd = Math.min(totalPages, state.currentPage + 2);
|
||
|
||
for (let p = pStart; p <= pEnd; p++) {
|
||
controls.appendChild(createBtn(p, p, p === state.currentPage));
|
||
}
|
||
|
||
controls.appendChild(createBtn('>', state.currentPage + 1, false, state.currentPage === totalPages));
|
||
controls.appendChild(createBtn('>>', totalPages, false, state.currentPage === totalPages));
|
||
}
|
||
|
||
// --- 3. 交互逻辑 ---
|
||
|
||
// 改变每页显示数量
|
||
function changePageSize() {
|
||
const select = document.getElementById('pageSizeSelect');
|
||
state.pageSize = parseInt(select.value);
|
||
state.currentPage = 1;
|
||
renderTable();
|
||
showToast(`每页显示 ${state.pageSize} 条`, "info");
|
||
}
|
||
|
||
// 模态框控制
|
||
function closeModal(result) {
|
||
const modal = document.getElementById(result); // 这里 result 实际上是 ID
|
||
modal.classList.remove('show');
|
||
setTimeout(() => { modal.style.display = 'none'; }, 300);
|
||
}
|
||
|
||
// 打开编辑模态框
|
||
function openEdit(id) {
|
||
const item = state.data.find(d => d.id === id);
|
||
if (!item) return;
|
||
|
||
// 填充表单
|
||
document.getElementById('edit_id').value = item.id;
|
||
document.getElementById('edit_name').value = item.name;
|
||
document.getElementById('edit_supplier').value = item.supplier;
|
||
document.getElementById('edit_qty').value = item.qty;
|
||
document.getElementById('edit_weight').value = item.weight;
|
||
document.getElementById('edit_status').value = item.status;
|
||
document.getElementById('edit_notes').value = item.notes || "";
|
||
|
||
const modal = document.getElementById('editModal');
|
||
modal.style.display = 'flex';
|
||
// 强制重绘以触发 transition
|
||
modal.offsetHeight;
|
||
modal.classList.add('show');
|
||
}
|
||
|
||
function saveEdit() {
|
||
const id = document.getElementById('edit_id').value;
|
||
const newName = document.getElementById('edit_name').value;
|
||
const newQty = parseInt(document.getElementById('edit_qty').value);
|
||
|
||
// 更新本地数据
|
||
const idx = state.data.findIndex(d => d.id === id);
|
||
if (idx !== -1) {
|
||
state.data[idx].name = newName;
|
||
state.data[idx].qty = newQty;
|
||
state.data[idx].supplier = document.getElementById('edit_supplier').value;
|
||
state.data[idx].status = document.getElementById('edit_status').value;
|
||
state.data[idx].weight = document.getElementById('edit_weight').value;
|
||
// 重要:更新 filteredData 里的引用(其实是同一个对象,但如果重新过滤可能需要刷新)
|
||
}
|
||
|
||
closeModal('editModal');
|
||
showToast("保存成功!数据已更新。", "success");
|
||
renderTable(); // 重新渲染当前页
|
||
}
|
||
|
||
// 打开详情模态框
|
||
function openDetail(id) {
|
||
const item = state.data.find(d => d.id === id);
|
||
if(!item) return;
|
||
|
||
document.getElementById('detail_id').innerText = item.id;
|
||
document.getElementById('detail_name').innerText = item.name;
|
||
document.getElementById('detail_batch').innerText = item.batch;
|
||
document.getElementById('detail_qty').innerText = item.qty.toLocaleString();
|
||
document.getElementById('detail_date').innerText = item.date;
|
||
document.getElementById('detail_status').innerText = item.status;
|
||
|
||
// 模拟生成一些历史记录
|
||
const historyList = document.getElementById('detail_history');
|
||
historyList.innerHTML = '';
|
||
for(let i=0; i<3; i++) {
|
||
historyList.innerHTML += `<li>${new Date().toLocaleDateString()} - 系统自动审核通过 (Audit Log #${Math.floor(Math.random()*9000)})</li>`;
|
||
}
|
||
|
||
const modal = document.getElementById('detailModal');
|
||
modal.style.display = 'flex';
|
||
modal.offsetHeight;
|
||
modal.classList.add('show');
|
||
}
|
||
|
||
function printDetail() {
|
||
alert("正在连接打印机... \n(模拟: 发送指令 PCL_PRINT_JOB_001)");
|
||
}
|
||
|
||
// 删除项目
|
||
function deleteItem(id, btnElement) {
|
||
if(confirm(`确定要删除物料 ${id} 吗?`)) {
|
||
// 这只是前端模拟,实际应该发请求
|
||
state.data = state.data.filter(d => d.id !== id);
|
||
applyFilter(); // 重新走一遍筛选和排序
|
||
showToast(`物料 ${id} 已删除`, "warning");
|
||
}
|
||
}
|
||
|
||
// 批量操作
|
||
function executeBatchAction() {
|
||
const select = document.getElementById('batchActionSelect');
|
||
const action = select.value;
|
||
const checks = document.querySelectorAll('.row-check:checked');
|
||
|
||
if (checks.length === 0) {
|
||
showToast("请先选择至少一项数据!", "error");
|
||
return;
|
||
}
|
||
|
||
if (select.selectedIndex === 0) {
|
||
showToast("请选择一个有效的操作类型", "warning");
|
||
} else {
|
||
// 模拟进度条
|
||
showToast(`正在处理 ${checks.length} 条数据: ${action}...`, "info");
|
||
setTimeout(() => {
|
||
showToast("批量操作完成!", "success");
|
||
// 取消所有勾选
|
||
checks.forEach(c => c.checked = false);
|
||
document.getElementById('selectAll').checked = false;
|
||
}, 1500);
|
||
}
|
||
}
|
||
|
||
// Toast 提示工具
|
||
function showToast(message, type = 'info') {
|
||
const container = document.getElementById('toastContainer');
|
||
const toast = document.createElement('div');
|
||
toast.className = `toast toast-${type}`;
|
||
|
||
let icon = 'ℹ️';
|
||
if (type === 'success') icon = '✅';
|
||
if (type === 'error') icon = '❌';
|
||
if (type === 'warning') icon = '⚠️';
|
||
|
||
toast.innerHTML = `
|
||
<div style="display:flex; align-items:center; gap:10px;">
|
||
<span style="font-size:1.2rem;">${icon}</span>
|
||
<span>${message}</span>
|
||
</div>
|
||
<span style="cursor:pointer; opacity:0.5;" onclick="this.parentElement.remove()">×</span>
|
||
`;
|
||
|
||
container.appendChild(toast);
|
||
// 动画
|
||
requestAnimationFrame(() => toast.classList.add('show'));
|
||
|
||
// 自动消失
|
||
setTimeout(() => {
|
||
toast.classList.remove('show');
|
||
setTimeout(() => toast.remove(), 300);
|
||
}, 3000);
|
||
}
|
||
|
||
// 全选功能
|
||
document.getElementById('selectAll').addEventListener('change', function(e) {
|
||
const isChecked = e.target.checked;
|
||
document.querySelectorAll('.row-check').forEach(cb => cb.checked = isChecked);
|
||
});
|
||
|
||
// 启动
|
||
window.onload = init;
|
||
|
||
</script>
|
||
|
||
</body>
|
||
</html> |