初始化

This commit is contained in:
2026-08-08 18:28:49 +08:00
parent 9bef4420e0
commit f082037a6e
854 changed files with 217171 additions and 11 deletions
+805
View File
@@ -0,0 +1,805 @@
<?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">&times;</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">&times;</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">&times;</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, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;');
}
// ==================== 删除设备数据 ====================
$('#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'; ?>