Files
MES/app/views/Admin/asdTestDataImport.php
2026-08-08 18:28:49 +08:00

806 lines
44 KiB
PHP
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?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'; ?>