806 lines
44 KiB
PHP
806 lines
44 KiB
PHP
<?php
|
||
$activeMenu = 'asdTestData';
|
||
include APP_PATH . 'app/views/layouts/admin_header.php';
|
||
?>
|
||
|
||
<style>
|
||
/* 设备连接操作区样式 */
|
||
.device-card { border: 1px solid #d2d6de; border-radius: 4px; padding: 15px; margin-bottom: 15px; background: #f9f9f9; }
|
||
.device-card .device-info { margin-bottom: 10px; }
|
||
.device-card .device-info .info-row { display: flex; flex-wrap: wrap; gap: 15px; align-items: center; margin-bottom: 8px; }
|
||
.device-card .device-info .info-label { font-weight: 600; color: #555; min-width: 70px; }
|
||
.device-card .device-info .info-value { color: #333; }
|
||
.status-dot { display: inline-block; width: 10px; height: 10px; border-radius: 50%; margin-right: 4px; }
|
||
.status-dot.online { background: #00a65a; }
|
||
.status-dot.offline { background: #dd4b39; }
|
||
.status-dot.unknown { background: #999; }
|
||
.device-actions { display: flex; gap: 8px; flex-wrap: wrap; margin-bottom: 10px; }
|
||
#scanInput { font-size: 16px; letter-spacing: 1px; }
|
||
.btn-connect { min-width: 100px; }
|
||
.console-log { background: #1e1e1e; color: #d4d4d4; font-family: Consolas, monospace; font-size: 13px; padding: 12px; border-radius: 4px; max-height: 350px; overflow-y: auto; line-height: 1.6; }
|
||
.console-log .log-line { margin: 2px 0; }
|
||
.console-log .log-success { color: #4ec9b0; }
|
||
.console-log .log-error { color: #f44747; }
|
||
.console-log .log-warn { color: #e6a23c; }
|
||
.console-log .log-info { color: #9cdcfe; }
|
||
.console-log .log-time { color: #808080; }
|
||
.batch-result-table { max-height: 400px; overflow-y: auto; margin-top: 10px; }
|
||
.batch-result-table table { font-size: 12px; }
|
||
.device-batch-item { border: 1px solid #e0e0e0; border-radius: 4px; margin-bottom: 10px; }
|
||
.device-batch-item .panel-heading { background: #f5f5f5; padding: 8px 12px; cursor: pointer; }
|
||
.device-batch-item .panel-body { display: none; padding: 10px; }
|
||
.device-batch-item .panel-body.open { display: block; }
|
||
</style>
|
||
|
||
<div class="row">
|
||
<div class="col-md-12">
|
||
<div class="box box-success">
|
||
<div class="box-header with-border">
|
||
<h3 class="box-title"><i class="fa fa-upload"></i> 导入测试数据</h3>
|
||
<div class="box-tools">
|
||
<a href="<?php echo BASE_URL; ?>/Admin/asdTestData" class="btn btn-default btn-sm">
|
||
<i class="fa fa-arrow-left"></i> 返回列表
|
||
</a>
|
||
</div>
|
||
</div>
|
||
|
||
<?php if (isset($success)): ?>
|
||
<div class="alert alert-success alert-dismissible" style="margin:10px;">
|
||
<button type="button" class="close" data-dismiss="alert">×</button>
|
||
<i class="fa fa-check-circle"></i> <?php echo h($success); ?>
|
||
</div>
|
||
<?php endif; ?>
|
||
<?php if (isset($error)): ?>
|
||
<div class="alert alert-danger alert-dismissible" style="margin:10px;">
|
||
<button type="button" class="close" data-dismiss="alert">×</button>
|
||
<i class="fa fa-exclamation-circle"></i> <?php echo h($error); ?>
|
||
</div>
|
||
<?php endif; ?>
|
||
|
||
<!-- Tab 切换 -->
|
||
<div class="nav-tabs-custom">
|
||
<ul class="nav nav-tabs">
|
||
<li class="<?php echo (empty($activeTab) || $activeTab === 'form') ? 'active' : ''; ?>"><a href="#tab-form" data-toggle="tab"><i class="fa fa-edit"></i> 表单导入</a></li>
|
||
<li class="<?php echo (isset($activeTab) && $activeTab === 'json') ? 'active' : ''; ?>"><a href="#tab-json" data-toggle="tab"><i class="fa fa-code"></i> JSON 导入</a></li>
|
||
<li class="<?php echo (isset($activeTab) && $activeTab === 'device') ? 'active' : ''; ?>"><a href="#tab-device" data-toggle="tab"><i class="fa fa-plug"></i> 设备连接与批量操作</a></li>
|
||
</ul>
|
||
<div class="tab-content">
|
||
|
||
<!-- ==================== 表单导入 ==================== -->
|
||
<div class="tab-pane <?php echo (empty($activeTab) || $activeTab === 'form') ? 'active' : ''; ?>" id="tab-form" style="display:<?php echo (empty($activeTab) || $activeTab === 'form') ? 'block' : 'none'; ?>;">
|
||
<form method="post" action="<?php echo BASE_URL; ?>/Admin/asdTestDataImport">
|
||
<div class="box-body">
|
||
<legend>基本信息</legend>
|
||
<div class="row">
|
||
<div class="col-md-3">
|
||
<div class="form-group">
|
||
<label>条码/二维码 <span class="text-red">*</span></label>
|
||
<input type="text" name="qr_code" class="form-control" placeholder="SN6977187913642" required>
|
||
</div>
|
||
</div>
|
||
<div class="col-md-3">
|
||
<div class="form-group">
|
||
<label>项目名称</label>
|
||
<input type="text" name="project_name" class="form-control" placeholder="充电器测试">
|
||
</div>
|
||
</div>
|
||
<div class="col-md-3">
|
||
<div class="form-group">
|
||
<label>工位</label>
|
||
<select name="site" class="form-control">
|
||
<option value="">-- 选择工位 --</option>
|
||
<?php foreach ($stations as $st): ?>
|
||
<option value="<?php echo htmlspecialchars($st['station_name']); ?>"><?php echo htmlspecialchars($st['station_name']); ?></option>
|
||
<?php endforeach; ?>
|
||
</select>
|
||
</div>
|
||
</div>
|
||
<div class="col-md-3">
|
||
<div class="form-group">
|
||
<label>站点</label>
|
||
<input type="text" name="station" class="form-control" placeholder="A区-1线">
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="row">
|
||
<div class="col-md-3">
|
||
<div class="form-group">
|
||
<label>单号</label>
|
||
<input type="text" name="mo" class="form-control" placeholder="MO20240525001">
|
||
</div>
|
||
</div>
|
||
<div class="col-md-3">
|
||
<div class="form-group">
|
||
<label>工单</label>
|
||
<input type="text" name="work_order" class="form-control" placeholder="WO-20240525-01">
|
||
</div>
|
||
</div>
|
||
<div class="col-md-3">
|
||
<div class="form-group">
|
||
<label>组别</label>
|
||
<input type="text" name="group_name" class="form-control" placeholder="A组">
|
||
</div>
|
||
</div>
|
||
<div class="col-md-3">
|
||
<div class="form-group">
|
||
<label>设备号</label>
|
||
<input type="text" name="device" class="form-control" placeholder="ASD-989A-001">
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="row">
|
||
<div class="col-md-3">
|
||
<div class="form-group">
|
||
<label>运行时间</label>
|
||
<input type="text" name="running_time" class="form-control" placeholder="28S">
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<legend>测试工步数据</legend>
|
||
<div id="stepContainer">
|
||
<?php
|
||
$defaultSteps = [
|
||
['name' => '输出测试', 'item' => '电压', 'units' => 'V', 'value' => '5.12', 'lower' => '4.75', 'upper' => '5.25'],
|
||
['name' => '输出测试', 'item' => '电流', 'units' => 'A', 'value' => '2.10', 'lower' => '1.90', 'upper' => '2.20'],
|
||
['name' => '空载功耗', 'item' => '功率', 'units' => 'W', 'value' => '0.08', 'lower' => '0', 'upper' => '0.3'],
|
||
['name' => '效率测试', 'item' => '效率', 'units' => '%', 'value' => '88.5', 'lower' => '85', 'upper' => '95'],
|
||
];
|
||
foreach ($defaultSteps as $idx => $ds):
|
||
?>
|
||
<div class="row step-row" style="margin-bottom:8px;">
|
||
<div class="col-md-2">
|
||
<input type="text" name="step_name[]" class="form-control input-sm" placeholder="工步名称" value="<?php echo $ds['name']; ?>">
|
||
</div>
|
||
<div class="col-md-2">
|
||
<input type="text" name="step_item[]" class="form-control input-sm" placeholder="测试项目" value="<?php echo $ds['item']; ?>">
|
||
</div>
|
||
<div class="col-md-2">
|
||
<input type="text" name="step_value[]" class="form-control input-sm" placeholder="测试值" value="<?php echo $ds['value']; ?>">
|
||
</div>
|
||
<div class="col-md-1">
|
||
<input type="text" name="step_units[]" class="form-control input-sm" placeholder="单位" value="<?php echo $ds['units']; ?>">
|
||
</div>
|
||
<div class="col-md-1">
|
||
<input type="text" name="step_lower[]" class="form-control input-sm" placeholder="下限" value="<?php echo $ds['lower']; ?>">
|
||
</div>
|
||
<div class="col-md-1">
|
||
<input type="text" name="step_upper[]" class="form-control input-sm" placeholder="上限" value="<?php echo $ds['upper']; ?>">
|
||
</div>
|
||
<div class="col-md-2">
|
||
<select name="step_result[]" class="form-control input-sm">
|
||
<option value="PASS" selected>PASS</option>
|
||
<option value="FAIL">FAIL</option>
|
||
</select>
|
||
</div>
|
||
<div class="col-md-1">
|
||
<button type="button" class="btn btn-xs btn-danger remove-step" title="删除此行">
|
||
<i class="fa fa-times"></i>
|
||
</button>
|
||
</div>
|
||
</div>
|
||
<?php endforeach; ?>
|
||
</div>
|
||
<button type="button" id="addStep" class="btn btn-sm btn-info" style="margin-top:5px;">
|
||
<i class="fa fa-plus"></i> 添加工步
|
||
</button>
|
||
</div>
|
||
<div class="box-footer">
|
||
<button type="submit" class="btn btn-primary">
|
||
<i class="fa fa-check"></i> 提交导入
|
||
</button>
|
||
</div>
|
||
</form>
|
||
</div>
|
||
|
||
<!-- ==================== JSON 导入 ==================== -->
|
||
<div class="tab-pane <?php echo (isset($activeTab) && $activeTab === 'json') ? 'active' : ''; ?>" id="tab-json" style="display:<?php echo (isset($activeTab) && $activeTab === 'json') ? 'block' : 'none'; ?>;">
|
||
<form method="post" action="<?php echo BASE_URL; ?>/Admin/asdTestDataImport">
|
||
<input type="hidden" name="action" value="import_json">
|
||
<div class="box-body">
|
||
<div class="form-group">
|
||
<label>粘贴昂盛达设备上报的 JSON 数据:</label>
|
||
<textarea name="json_data" class="form-control" rows="18"
|
||
placeholder='{"Tester":"张三","QrCode":"SN6977187913642","Mo":"MO20240525","Site":"成品测试","Status":"PASS","DataList":[{"Seq":1,"TestName":"输出测试","TestItem":"电压","TestUnits":"V","DataValue":5.12,"LowerLimit":4.75,"UpperLimit":5.25,"TestResult":"PASS"}]}'><?php echo isset($_POST['json_data']) ? htmlspecialchars($_POST['json_data']) : ''; ?></textarea>
|
||
</div>
|
||
<p class="text-muted">
|
||
<i class="fa fa-info-circle"></i>
|
||
JSON 格式请参考接口文档。必填字段:<code>QrCode</code>、<code>Status</code>、<code>DataList</code>
|
||
</p>
|
||
</div>
|
||
<div class="box-footer">
|
||
<button type="submit" class="btn btn-primary">
|
||
<i class="fa fa-upload"></i> 导入 JSON
|
||
</button>
|
||
</div>
|
||
</form>
|
||
</div>
|
||
|
||
<!-- ==================== 设备连接与批量操作 ==================== -->
|
||
<div class="tab-pane <?php echo (isset($activeTab) && $activeTab === 'device') ? 'active' : ''; ?>" id="tab-device" style="display:<?php echo (isset($activeTab) && $activeTab === 'device') ? 'block' : 'none'; ?>;">
|
||
<div class="box-body">
|
||
<div class="row">
|
||
<!-- 左侧:设备选择与连接 -->
|
||
<div class="col-md-6">
|
||
<div class="device-card">
|
||
<h4 style="margin-top:0;"><i class="fa fa-plug"></i> 设备连接</h4>
|
||
|
||
<!-- 扫码 / 手动输入设备编码 -->
|
||
<div class="form-group">
|
||
<label>设备编码(扫码枪 / 手动输入) <span class="text-red">*</span></label>
|
||
<div class="input-group">
|
||
<input type="text" id="scanInput" class="form-control" placeholder="扫描设备二维码 或 输入设备编码...">
|
||
<span class="input-group-btn">
|
||
<button type="button" id="btnScanLookup" class="btn btn-info btn-connect">
|
||
<i class="fa fa-search"></i> 查找
|
||
</button>
|
||
</span>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 设备选择下拉框 -->
|
||
<div class="form-group">
|
||
<label>或从列表选择已注册设备:</label>
|
||
<select id="deviceSelect" class="form-control">
|
||
<option value="">-- 选择设备 --</option>
|
||
<?php foreach ($devices as $dv): ?>
|
||
<option value="<?php echo htmlspecialchars($dv['device_code']); ?>"
|
||
data-ip="<?php echo htmlspecialchars($dv['device_ip'] ?? ''); ?>"
|
||
data-name="<?php echo htmlspecialchars($dv['device_name'] ?? ''); ?>"
|
||
data-group="<?php echo htmlspecialchars($dv['group_name'] ?? ''); ?>"
|
||
data-status="<?php echo $dv['status']; ?>">
|
||
<?php echo htmlspecialchars($dv['device_code']); ?>
|
||
[<?php echo htmlspecialchars($dv['device_name'] ?? $dv['group_name'] ?? '-'); ?>]
|
||
<?php echo !empty($dv['device_ip']) ? ' - IP:' . htmlspecialchars($dv['device_ip']) : ''; ?>
|
||
</option>
|
||
<?php endforeach; ?>
|
||
</select>
|
||
</div>
|
||
|
||
<hr>
|
||
|
||
<!-- 设备信息展示区 -->
|
||
<div id="deviceDetail" class="device-info" style="display:none;">
|
||
<div class="info-row">
|
||
<span class="info-label">设备编码:</span>
|
||
<span class="info-value" id="dispDeviceCode">-</span>
|
||
</div>
|
||
<div class="info-row">
|
||
<span class="info-label">设备名称:</span>
|
||
<span class="info-value" id="dispDeviceName">-</span>
|
||
</div>
|
||
<div class="info-row">
|
||
<span class="info-label">所属组别:</span>
|
||
<span class="info-value" id="dispDeviceGroup">-</span>
|
||
</div>
|
||
<div class="info-row">
|
||
<span class="info-label">设备IP:</span>
|
||
<span class="info-value">
|
||
<input type="text" id="deviceIpInput" class="form-control input-sm" style="width:160px; display:inline-block;" placeholder="192.168.1.101">
|
||
</span>
|
||
</div>
|
||
<div class="info-row">
|
||
<span class="info-label">连接状态:</span>
|
||
<span class="info-value" id="dispConnStatus">
|
||
<span class="status-dot unknown"></span> 未连接
|
||
</span>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 操作按钮 -->
|
||
<div class="device-actions">
|
||
<button type="button" id="btnConnect" class="btn btn-success btn-connect" disabled>
|
||
<i class="fa fa-link"></i> 连接设备
|
||
</button>
|
||
<button type="button" id="btnDisconnect" class="btn btn-default btn-connect" disabled>
|
||
<i class="fa fa-unlink"></i> 断开
|
||
</button>
|
||
</div>
|
||
|
||
<div class="device-actions">
|
||
<button type="button" id="btnBatchRead" class="btn btn-primary" disabled>
|
||
<i class="fa fa-download"></i> 读取设备数据
|
||
</button>
|
||
<button type="button" id="btnBatchImport" class="btn btn-warning">
|
||
<i class="fa fa-cloud-upload"></i> 批量导入选中设备数据
|
||
</button>
|
||
<button type="button" id="btnDeleteDeviceData" class="btn btn-danger" disabled>
|
||
<i class="fa fa-trash"></i> 删除该设备数据
|
||
</button>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 已连接设备列表 -->
|
||
<div class="box box-solid box-default collapsed-box">
|
||
<div class="box-header with-border">
|
||
<h3 class="box-title"><i class="fa fa-list"></i> 已选中设备列表(用于批量读取)</h3>
|
||
<div class="box-tools">
|
||
<span class="badge bg-light-blue" id="selectedCount">0</span>
|
||
<button type="button" class="btn btn-box-tool" data-widget="collapse"><i class="fa fa-plus"></i></button>
|
||
</div>
|
||
</div>
|
||
<div class="box-body">
|
||
<div id="selectedDevicesList" class="text-muted" style="padding:5px;">
|
||
尚未选择设备,请先扫码或从列表选择并连接设备
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 右侧:操作日志和结果 -->
|
||
<div class="col-md-6">
|
||
<div class="box box-solid box-info">
|
||
<div class="box-header with-border">
|
||
<h3 class="box-title"><i class="fa fa-terminal"></i> 操作日志</h3>
|
||
<div class="box-tools">
|
||
<button type="button" id="btnClearLog" class="btn btn-box-tool btn-xs"><i class="fa fa-trash-o"></i> 清空</button>
|
||
</div>
|
||
</div>
|
||
<div class="box-body" style="padding:0;">
|
||
<div id="consoleLog" class="console-log">
|
||
<div class="log-line log-info">系统就绪,请选择设备进行操作...</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 批量读取结果区 -->
|
||
<div id="batchResultArea" style="display:none;">
|
||
<div class="box box-solid box-success">
|
||
<div class="box-header with-border">
|
||
<h3 class="box-title"><i class="fa fa-table"></i> 批量读取结果</h3>
|
||
<span class="pull-right" id="batchResultSummary"></span>
|
||
</div>
|
||
<div class="box-body" style="padding:10px;">
|
||
<div id="batchResultContent" class="batch-result-table"></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
</div><!-- /.tab-content -->
|
||
</div><!-- /.nav-tabs-custom -->
|
||
</div><!-- /.box -->
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 删除确认弹窗 -->
|
||
<div class="modal fade" id="deleteConfirmModal" tabindex="-1" role="dialog">
|
||
<div class="modal-dialog" role="document">
|
||
<div class="modal-content">
|
||
<div class="modal-header">
|
||
<button type="button" class="close" data-dismiss="modal">×</button>
|
||
<h4 class="modal-title"><i class="fa fa-exclamation-triangle text-red"></i> 确认删除</h4>
|
||
</div>
|
||
<div class="modal-body">
|
||
<p id="deleteConfirmMsg">确定要删除该设备的所有测试数据吗?此操作不可撤销!</p>
|
||
<div class="form-group">
|
||
<label>请输入 <strong>DELETE</strong> 确认:</label>
|
||
<input type="text" id="deleteConfirmInput" class="form-control" placeholder="输入 DELETE">
|
||
</div>
|
||
</div>
|
||
<div class="modal-footer">
|
||
<button type="button" class="btn btn-default" data-dismiss="modal">取消</button>
|
||
<button type="button" class="btn btn-danger" id="btnConfirmDelete" disabled>
|
||
<i class="fa fa-trash"></i> 确认删除
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<script nonce="<?php echo csp_nonce(); ?>">
|
||
// ==================== 全局状态 ====================
|
||
var connectedDevices = {}; // { deviceCode: { device info, connected: bool } }
|
||
var currentDevice = null; // 当前选中的设备编码
|
||
|
||
var BASE_URL = '<?php echo BASE_URL; ?>';
|
||
|
||
// CSP:设备批次面板标题点击展开(替代动态生成 HTML 的内联 onclick)
|
||
$(document).on('click', '.device-batch-header', function () {
|
||
$(this).next().toggleClass('open');
|
||
});
|
||
|
||
// ==================== 控制台日志 ====================
|
||
function log(msg, type) {
|
||
type = type || 'info';
|
||
var now = new Date();
|
||
var time = now.toTimeString().substring(0, 8);
|
||
var $log = $('#consoleLog');
|
||
var cls = 'log-' + type;
|
||
$log.append('<div class="log-line ' + cls + '"><span class="log-time">[' + time + ']</span> ' + msg + '</div>');
|
||
$log.scrollTop($log[0].scrollHeight);
|
||
}
|
||
|
||
// ==================== 设备选择处理 ====================
|
||
$('#deviceSelect').on('change', function() {
|
||
var code = $(this).val();
|
||
if (!code) {
|
||
$('#deviceDetail').hide();
|
||
$('#btnConnect').prop('disabled', true);
|
||
currentDevice = null;
|
||
return;
|
||
}
|
||
currentDevice = code;
|
||
$('#scanInput').val(code);
|
||
|
||
var opt = $(this).find('option:selected');
|
||
$('#dispDeviceCode').text(code);
|
||
$('#dispDeviceName').text(opt.data('name') || '-');
|
||
$('#dispDeviceGroup').text(opt.data('group') || '-');
|
||
$('#deviceIpInput').val(opt.data('ip') || '');
|
||
$('#deviceDetail').show();
|
||
$('#btnConnect').prop('disabled', false);
|
||
updateConnectStatus(code);
|
||
});
|
||
|
||
// 扫码查找
|
||
$('#btnScanLookup').on('click', function() {
|
||
var code = $('#scanInput').val().trim();
|
||
if (!code) { alert('请输入设备编码'); return; }
|
||
// 尝试从下拉框匹配
|
||
var found = false;
|
||
$('#deviceSelect option').each(function() {
|
||
if ($(this).val().toUpperCase() === code.toUpperCase()) {
|
||
$('#deviceSelect').val($(this).val()).trigger('change');
|
||
found = true;
|
||
log('扫码识别成功: ' + code, 'success');
|
||
return false;
|
||
}
|
||
});
|
||
if (!found) {
|
||
log('设备编码 ' + code + ' 未在系统中注册,请先录入设备信息', 'warn');
|
||
// 仍允许手动连接
|
||
currentDevice = code;
|
||
$('#dispDeviceCode').text(code);
|
||
$('#dispDeviceName').text('(未注册)');
|
||
$('#dispDeviceGroup').text('-');
|
||
$('#deviceIpInput').val('');
|
||
$('#deviceDetail').show();
|
||
$('#btnConnect').prop('disabled', false);
|
||
}
|
||
});
|
||
|
||
// 更新连接状态显示
|
||
function updateConnectStatus(code) {
|
||
if (connectedDevices[code] && connectedDevices[code].connected) {
|
||
$('#dispConnStatus').html('<span class="status-dot online"></span> 已连接');
|
||
} else {
|
||
$('#dispConnStatus').html('<span class="status-dot unknown"></span> 未连接');
|
||
}
|
||
}
|
||
|
||
// ==================== 连接设备 ====================
|
||
$('#btnConnect').on('click', function() {
|
||
if (!currentDevice) { alert('请先选择设备'); return; }
|
||
|
||
var ip = $('#deviceIpInput').val().trim();
|
||
if (!ip) { alert('请输入设备 IP 地址'); return; }
|
||
|
||
var $btn = $(this);
|
||
$btn.prop('disabled', true).html('<i class="fa fa-spinner fa-spin"></i> 连接中...');
|
||
log('正在连接设备 ' + currentDevice + ' (' + ip + ')...', 'info');
|
||
|
||
$.ajax({
|
||
url: BASE_URL + '/Api/asdDeviceConnect',
|
||
type: 'POST',
|
||
contentType: 'application/json',
|
||
data: JSON.stringify({ DeviceCode: currentDevice, DeviceIp: ip }),
|
||
success: function(resp) {
|
||
if (resp.Result == 1) {
|
||
log('连接成功! 设备: ' + currentDevice + ', IP: ' + ip
|
||
+ (resp.ConnectionInfo.reachable ? ', 延迟: ' + resp.ConnectionInfo.latency_ms + 'ms' : ', 网络不可达(数据库校验通过)'), 'success');
|
||
connectedDevices[currentDevice] = {
|
||
info: resp.Device,
|
||
connected: true
|
||
};
|
||
$('#dispConnStatus').html('<span class="status-dot online"></span> 已连接');
|
||
$('#btnDisconnect').prop('disabled', false);
|
||
$('#btnBatchRead').prop('disabled', false);
|
||
$('#btnDeleteDeviceData').prop('disabled', false);
|
||
updateSelectedList();
|
||
} else {
|
||
log('连接失败: ' + resp.Message, 'error');
|
||
}
|
||
},
|
||
error: function(xhr, status, err) {
|
||
log('连接异常: ' + err, 'error');
|
||
},
|
||
complete: function() {
|
||
$btn.prop('disabled', false).html('<i class="fa fa-link"></i> 连接设备');
|
||
}
|
||
});
|
||
});
|
||
|
||
// 断开连接
|
||
$('#btnDisconnect').on('click', function() {
|
||
if (!currentDevice) return;
|
||
if (connectedDevices[currentDevice]) {
|
||
connectedDevices[currentDevice].connected = false;
|
||
}
|
||
$('#dispConnStatus').html('<span class="status-dot online" style="background:#999;"></span> 已断开');
|
||
log('已断开设备: ' + currentDevice, 'warn');
|
||
updateConnectStatus(currentDevice);
|
||
updateSelectedList();
|
||
});
|
||
|
||
// ==================== 更新已选设备列表 ====================
|
||
function updateSelectedList() {
|
||
var $list = $('#selectedDevicesList');
|
||
var count = 0;
|
||
var html = '';
|
||
for (var code in connectedDevices) {
|
||
if (connectedDevices[code].connected) {
|
||
count++;
|
||
var info = connectedDevices[code].info;
|
||
html += '<span class="label label-success" style="margin:2px;" title="' + (info.device_ip || '') + '">'
|
||
+ '<span class="status-dot online"></span> ' + code + '</span> ';
|
||
}
|
||
}
|
||
if (count === 0) {
|
||
$list.html('尚未选择设备,请先扫码或从列表选择并连接设备');
|
||
} else {
|
||
$list.html(html);
|
||
}
|
||
$('#selectedCount').text(count);
|
||
}
|
||
|
||
// ==================== 批量读取设备数据 ====================
|
||
$('#btnBatchRead').on('click', function() {
|
||
if (!currentDevice) { alert('请先选择设备'); return; }
|
||
|
||
var $btn = $(this);
|
||
$btn.prop('disabled', true).html('<i class="fa fa-spinner fa-spin"></i> 读取中...');
|
||
log('正在读取设备 ' + currentDevice + ' 的测试数据...', 'info');
|
||
|
||
$.ajax({
|
||
url: BASE_URL + '/Api/asdBatchRead',
|
||
type: 'POST',
|
||
contentType: 'application/json',
|
||
data: JSON.stringify({ DeviceCodes: [currentDevice], Limit: 20 }),
|
||
success: function(resp) {
|
||
if (resp.Result == 1) {
|
||
var deviceData = resp.DevicesData[currentDevice];
|
||
if (!deviceData) {
|
||
log('设备 ' + currentDevice + ' 无数据', 'warn');
|
||
return;
|
||
}
|
||
log('读取成功! 设备 ' + currentDevice + ' 共 ' + deviceData.total + ' 条记录,本次返回 ' + deviceData.records.length + ' 条', 'success');
|
||
renderBatchResult(deviceData, currentDevice);
|
||
} else {
|
||
log('读取失败: ' + resp.Message, 'error');
|
||
}
|
||
},
|
||
error: function(xhr, status, err) {
|
||
log('读取异常: ' + err, 'error');
|
||
},
|
||
complete: function() {
|
||
$btn.prop('disabled', false).html('<i class="fa fa-download"></i> 读取设备数据');
|
||
}
|
||
});
|
||
});
|
||
|
||
// 渲染批量读取结果
|
||
function renderBatchResult(deviceData, deviceCode) {
|
||
var $area = $('#batchResultArea');
|
||
var $content = $('#batchResultContent');
|
||
var records = deviceData.records;
|
||
|
||
var html = '<table class="table table-bordered table-condensed table-striped">';
|
||
html += '<thead><tr><th>ID</th><th>条码</th><th>项目名称</th><th>测试员</th><th>状态</th><th>工步数</th><th>失败</th><th>测试时间</th><th>操作</th></tr></thead>';
|
||
html += '<tbody>';
|
||
|
||
if (records.length === 0) {
|
||
html += '<tr><td colspan="9" class="text-center text-muted">该设备暂无测试数据</td></tr>';
|
||
} else {
|
||
for (var i = 0; i < records.length; i++) {
|
||
var r = records[i];
|
||
var statusLabel = r.status === 'PASS' ? 'label-success' : 'label-danger';
|
||
var failBadge = r.fail_count > 0 ? '<span class="badge bg-red">' + r.fail_count + '</span>' : '<span class="text-muted">0</span>';
|
||
html += '<tr>';
|
||
html += '<td>' + r.id + '</td>';
|
||
html += '<td><code>' + escapeHtml(r.qr_code) + '</code></td>';
|
||
html += '<td>' + escapeHtml(r.project_name) + '</td>';
|
||
html += '<td>' + escapeHtml(r.tester) + '</td>';
|
||
html += '<td><span class="label ' + statusLabel + '">' + r.status + '</span></td>';
|
||
html += '<td><span class="badge bg-light-blue">' + r.step_count + '</span></td>';
|
||
html += '<td>' + failBadge + '</td>';
|
||
html += '<td>' + (r.test_time || '-') + '</td>';
|
||
html += '<td><a href="' + BASE_URL + '/Admin/asdTestDataView/' + r.id + '" class="btn btn-xs btn-info" target="_blank"><i class="fa fa-eye"></i></a></td>';
|
||
html += '</tr>';
|
||
}
|
||
}
|
||
html += '</tbody></table>';
|
||
|
||
$('#batchResultSummary').html('设备: <strong>' + deviceCode + '</strong> | 总数: ' + deviceData.total + ' | 显示: ' + records.length + ' 条');
|
||
$content.html(html);
|
||
$area.show();
|
||
}
|
||
|
||
function escapeHtml(str) {
|
||
if (!str) return '';
|
||
return String(str).replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"');
|
||
}
|
||
|
||
// ==================== 删除设备数据 ====================
|
||
$('#btnDeleteDeviceData').on('click', function() {
|
||
if (!currentDevice) { alert('请先选择设备'); return; }
|
||
$('#deleteConfirmMsg').text('确定要删除设备 [' + currentDevice + '] 的所有测试数据吗?此操作不可撤销!');
|
||
$('#deleteConfirmInput').val('');
|
||
$('#btnConfirmDelete').prop('disabled', true);
|
||
$('#deleteConfirmModal').modal('show');
|
||
});
|
||
|
||
$('#deleteConfirmInput').on('input', function() {
|
||
$('#btnConfirmDelete').prop('disabled', $(this).val() !== 'DELETE');
|
||
});
|
||
|
||
$('#btnConfirmDelete').on('click', function() {
|
||
var $btn = $(this);
|
||
$btn.prop('disabled', true).html('<i class="fa fa-spinner fa-spin"></i> 删除中...');
|
||
log('正在删除设备 ' + currentDevice + ' 的所有测试数据...', 'warn');
|
||
|
||
$.ajax({
|
||
url: BASE_URL + '/Api/asdDeviceDataDelete',
|
||
type: 'POST',
|
||
contentType: 'application/json',
|
||
data: JSON.stringify({ DeviceCode: currentDevice, Confirm: 'YES' }),
|
||
success: function(resp) {
|
||
if (resp.Result == 1) {
|
||
log('删除成功! 共删除 ' + resp.DeletedCount + ' 条记录', 'success');
|
||
$('#batchResultArea').hide();
|
||
} else {
|
||
log('删除失败: ' + resp.Message, 'error');
|
||
}
|
||
},
|
||
error: function(xhr, status, err) {
|
||
log('删除异常: ' + err, 'error');
|
||
},
|
||
complete: function() {
|
||
$btn.prop('disabled', false).html('<i class="fa fa-trash"></i> 确认删除');
|
||
$('#deleteConfirmModal').modal('hide');
|
||
}
|
||
});
|
||
});
|
||
|
||
// ==================== 批量导入选中设备数据 ====================
|
||
$('#btnBatchImport').on('click', function() {
|
||
var codes = [];
|
||
for (var code in connectedDevices) {
|
||
if (connectedDevices[code].connected) {
|
||
codes.push(code);
|
||
}
|
||
}
|
||
if (codes.length === 0) {
|
||
alert('请先连接至少一台设备');
|
||
return;
|
||
}
|
||
|
||
var $btn = $(this);
|
||
$btn.prop('disabled', true).html('<i class="fa fa-spinner fa-spin"></i> 读取中...');
|
||
log('批量读取 ' + codes.length + ' 台设备数据...', 'info');
|
||
|
||
$.ajax({
|
||
url: BASE_URL + '/Api/asdBatchRead',
|
||
type: 'POST',
|
||
contentType: 'application/json',
|
||
data: JSON.stringify({ DeviceCodes: codes, Limit: 50 }),
|
||
success: function(resp) {
|
||
if (resp.Result == 1) {
|
||
var totalRecords = 0;
|
||
var html = '';
|
||
for (var code in resp.DevicesData) {
|
||
var dd = resp.DevicesData[code];
|
||
totalRecords += dd.total;
|
||
html += '<div class="device-batch-item">';
|
||
html += '<div class="panel-heading device-batch-header">';
|
||
html += '<strong>' + code + '</strong> ';
|
||
html += '<span class="badge bg-light-blue pull-right">' + dd.records.length + ' / ' + dd.total + ' 条</span>';
|
||
html += '<small class="text-muted">' + (dd.device_info ? dd.device_info.device_name + ' | IP:' + dd.device_info.device_ip : '') + '</small>';
|
||
html += '</div>';
|
||
html += '<div class="panel-body">';
|
||
html += '<table class="table table-condensed table-bordered"><thead><tr><th>ID</th><th>条码</th><th>状态</th><th>工步</th><th>时间</th></tr></thead><tbody>';
|
||
for (var i = 0; i < dd.records.length; i++) {
|
||
var r = dd.records[i];
|
||
html += '<tr><td>' + r.id + '</td><td><code>' + escapeHtml(r.qr_code) + '</code></td>';
|
||
html += '<td><span class="label ' + (r.status === 'PASS' ? 'label-success' : 'label-danger') + '">' + r.status + '</span></td>';
|
||
html += '<td>' + r.step_count + '</td><td>' + (r.test_time || '-') + '</td></tr>';
|
||
}
|
||
html += '</tbody></table></div></div>';
|
||
}
|
||
$('#batchResultContent').html(html);
|
||
$('#batchResultSummary').html('设备数: <strong>' + codes.length + '</strong> | 总记录: ' + totalRecords + ' 条');
|
||
$('#batchResultArea').show();
|
||
log('批量读取完成! ' + codes.length + ' 台设备, 共 ' + totalRecords + ' 条记录', 'success');
|
||
} else {
|
||
log('批量读取失败: ' + resp.Message, 'error');
|
||
}
|
||
},
|
||
error: function(xhr, status, err) {
|
||
log('批量读取异常: ' + err, 'error');
|
||
},
|
||
complete: function() {
|
||
$btn.prop('disabled', false).html('<i class="fa fa-cloud-upload"></i> 批量导入选中设备数据');
|
||
}
|
||
});
|
||
});
|
||
|
||
// ==================== 清空日志 ====================
|
||
$('#btnClearLog').on('click', function() {
|
||
$('#consoleLog').html('<div class="log-line log-info">日志已清空</div>');
|
||
});
|
||
|
||
// ==================== 表单工步操作 ====================
|
||
document.getElementById('addStep').addEventListener('click', function() {
|
||
var container = document.getElementById('stepContainer');
|
||
var row = document.createElement('div');
|
||
row.className = 'row step-row';
|
||
row.style.marginBottom = '8px';
|
||
row.innerHTML =
|
||
'<div class="col-md-2"><input type="text" name="step_name[]" class="form-control input-sm" placeholder="工步名称"></div>' +
|
||
'<div class="col-md-2"><input type="text" name="step_item[]" class="form-control input-sm" placeholder="测试项目"></div>' +
|
||
'<div class="col-md-2"><input type="text" name="step_value[]" class="form-control input-sm" placeholder="测试值"></div>' +
|
||
'<div class="col-md-1"><input type="text" name="step_units[]" class="form-control input-sm" placeholder="单位"></div>' +
|
||
'<div class="col-md-1"><input type="text" name="step_lower[]" class="form-control input-sm" placeholder="下限"></div>' +
|
||
'<div class="col-md-1"><input type="text" name="step_upper[]" class="form-control input-sm" placeholder="上限"></div>' +
|
||
'<div class="col-md-2"><select name="step_result[]" class="form-control input-sm"><option value="PASS" selected>PASS</option><option value="FAIL">FAIL</option></select></div>' +
|
||
'<div class="col-md-1"><button type="button" class="btn btn-xs btn-danger remove-step"><i class="fa fa-times"></i></button></div>';
|
||
container.appendChild(row);
|
||
row.querySelector('.remove-step').addEventListener('click', function() {
|
||
container.removeChild(row);
|
||
});
|
||
});
|
||
|
||
document.querySelectorAll('.remove-step').forEach(function(btn) {
|
||
btn.addEventListener('click', function() {
|
||
var rows = document.querySelectorAll('.step-row');
|
||
if (rows.length > 1) {
|
||
this.closest('.step-row').remove();
|
||
}
|
||
});
|
||
});
|
||
|
||
// 扫码输入框回车自动查找
|
||
$('#scanInput').on('keypress', function(e) {
|
||
if (e.which === 13) {
|
||
e.preventDefault();
|
||
$('#btnScanLookup').trigger('click');
|
||
}
|
||
});
|
||
|
||
// ==================== Tab 切换(直接操作 display,不依赖 Bootstrap JS) ====================
|
||
$(function() {
|
||
// 切换 tab:显示目标,隐藏其余
|
||
function switchTab(hash) {
|
||
if (!hash) return;
|
||
// 隐藏所有 pane,显示目标
|
||
$('.tab-content > .tab-pane').hide();
|
||
$(hash).show();
|
||
// 切换 nav 高亮
|
||
$('.nav-tabs li').removeClass('active');
|
||
$('.nav-tabs a[href="' + hash + '"]').parent('li').addClass('active');
|
||
}
|
||
|
||
// 点击 nav tab
|
||
$('.nav-tabs a').on('click', function(e) {
|
||
e.preventDefault();
|
||
var hash = $(this).attr('href');
|
||
switchTab(hash);
|
||
// 更新 URL hash
|
||
if (history.replaceState) {
|
||
history.replaceState(null, null, hash);
|
||
}
|
||
});
|
||
|
||
// 页面加载时根据 URL hash 切换
|
||
var hash = window.location.hash;
|
||
if (hash && '#tab-form,#tab-json,#tab-device'.indexOf(hash) !== -1) {
|
||
switchTab(hash);
|
||
}
|
||
});
|
||
</script>
|
||
|
||
<?php include APP_PATH . 'app/views/layouts/admin_footer.php'; ?>
|