初始化
This commit is contained in:
@@ -0,0 +1,102 @@
|
||||
<?php
|
||||
/**
|
||||
* 批量删除测试数据
|
||||
*/
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title><?php echo htmlspecialchars($title); ?> - MES系统</title>
|
||||
<link rel="stylesheet" href="<?php echo BASE_URL; ?>/static/css/font-awesome.min.css">
|
||||
<link rel="stylesheet" href="<?php echo BASE_URL; ?>/static/css/admin.css">
|
||||
</head>
|
||||
<body>
|
||||
<?php include APP_PATH . 'app/views/layouts/admin_header.php'; ?>
|
||||
|
||||
<div class="content-wrapper">
|
||||
<section class="content-header">
|
||||
<h1><i class="fa fa-trash"></i> <?php echo htmlspecialchars($title); ?></h1>
|
||||
</section>
|
||||
|
||||
<section class="content">
|
||||
<?php if (!empty($error)): ?>
|
||||
<div class="alert alert-danger"><?php echo htmlspecialchars($error); ?></div>
|
||||
<?php endif; ?>
|
||||
<?php if (!empty($success)): ?>
|
||||
<div class="alert alert-success"><?php echo htmlspecialchars($success); ?></div>
|
||||
<?php endif; ?>
|
||||
|
||||
<!-- 按设备删除 -->
|
||||
<div class="box">
|
||||
<div class="box-header">
|
||||
<h3 class="box-title"><i class="fa fa-laptop"></i> 按设备编码删除</h3>
|
||||
</div>
|
||||
<div class="box-body">
|
||||
<form method="post" action="" class="confirm-form" data-confirm="确定删除该设备的所有测试数据?此操作不可恢复!">
|
||||
<input type="hidden" name="csrf_token" value="<?php echo htmlspecialchars($csrfToken); ?>">
|
||||
<input type="hidden" name="action" value="by_device">
|
||||
<div class="form-group">
|
||||
<label>设备编码</label>
|
||||
<select name="device_code" class="form-control">
|
||||
<option value="">请选择设备</option>
|
||||
<?php foreach ($devices as $dev): ?>
|
||||
<option value="<?php echo htmlspecialchars($dev['device_code']); ?>">
|
||||
<?php echo htmlspecialchars($dev['device_code'] . ' - ' . ($dev['device_name'] ?? '')); ?>
|
||||
</option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-danger">
|
||||
<i class="fa fa-trash"></i> 按设备删除
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 按ID批量删除 -->
|
||||
<div class="box">
|
||||
<div class="box-header">
|
||||
<h3 class="box-title"><i class="fa fa-list-ol"></i> 按记录ID批量删除</h3>
|
||||
</div>
|
||||
<div class="box-body">
|
||||
<form method="post" action="" class="confirm-form" data-confirm="确定删除这些记录?此操作不可恢复!">
|
||||
<input type="hidden" name="csrf_token" value="<?php echo htmlspecialchars($csrfToken); ?>">
|
||||
<input type="hidden" name="action" value="by_ids">
|
||||
<div class="form-group">
|
||||
<label>记录ID列表(多个ID用逗号分隔,如:1,2,3)</label>
|
||||
<input type="text" name="ids" class="form-control" placeholder="例如:1,2,3">
|
||||
</div>
|
||||
<button type="submit" class="btn btn-danger">
|
||||
<i class="fa fa-trash"></i> 按ID删除
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 清空全部 -->
|
||||
<div class="box box-danger">
|
||||
<div class="box-header">
|
||||
<h3 class="box-title"><i class="fa fa-exclamation-triangle"></i> 危险操作</h3>
|
||||
</div>
|
||||
<div class="box-body">
|
||||
<form method="post" action="" class="confirm-form" data-confirm="确定清空全部测试数据?此操作不可恢复!请确认您已备份数据。">
|
||||
<input type="hidden" name="csrf_token" value="<?php echo htmlspecialchars($csrfToken); ?>">
|
||||
<input type="hidden" name="action" value="clear_all">
|
||||
<button type="submit" class="btn btn-danger btn-lg">
|
||||
<i class="fa fa-bomb"></i> 清空全部测试数据
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<a href="<?php echo BASE_URL; ?>/Admin/asdTestData" class="btn btn-default">
|
||||
<i class="fa fa-arrow-left"></i> 返回测试数据列表
|
||||
</a>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<?php include APP_PATH . 'app/views/layouts/admin_footer.php'; ?>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,221 @@
|
||||
<?php
|
||||
$activeMenu = 'asdDeviceDebug';
|
||||
include APP_PATH . 'app/views/layouts/admin_header.php';
|
||||
?>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<div class="box box-primary">
|
||||
<div class="box-header with-border">
|
||||
<h3 class="box-title"><i class="fa fa-plug"></i> 昂盛达设备调试</h3>
|
||||
<div class="box-tools">
|
||||
<span class="badge bg-blue">共 <?php echo count($devices ?? []); ?> 台设备</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-body">
|
||||
<form method="post" action="<?php echo BASE_URL; ?>/Admin/asdDeviceDebug" class="form-inline">
|
||||
<input type="hidden" name="csrf_token" value="<?php echo htmlspecialchars($csrfToken); ?>">
|
||||
<div class="form-group" style="margin-right:8px;">
|
||||
<label class="sr-only">查找设备</label>
|
||||
<input type="text" name="keyword" class="form-control" style="width:260px;"
|
||||
placeholder="按名称 / IP / 编号查找" value="<?php echo htmlspecialchars($keyword ?? ''); ?>">
|
||||
</div>
|
||||
<button type="submit" name="action" value="search" class="btn btn-default">
|
||||
<i class="fa fa-search"></i> 查找设备
|
||||
</button>
|
||||
<span class="text-muted" style="margin-left:10px;">共 <?php echo count($devices ?? []); ?> 台</span>
|
||||
</form>
|
||||
<hr>
|
||||
<form method="post" action="<?php echo BASE_URL; ?>/Admin/asdDeviceDebug">
|
||||
<input type="hidden" name="csrf_token" value="<?php echo htmlspecialchars($csrfToken); ?>">
|
||||
<div class="form-group">
|
||||
<label>选择设备</label>
|
||||
<select name="device_id" class="form-control" style="max-width:480px;">
|
||||
<option value="0">— 请选择设备 —</option>
|
||||
<?php foreach ($devices ?? [] as $dev): ?>
|
||||
<option value="<?php echo intval($dev['id']); ?>" <?php echo (isset($selDeviceId) && $selDeviceId == $dev['id']) ? 'selected' : ''; ?>>
|
||||
<?php echo htmlspecialchars($dev['device_name'] ?? '设备' . $dev['id']); ?>
|
||||
(<?php echo htmlspecialchars($dev['device_ip'] ?? '无IP'); ?>:<?php echo intval($dev['device_port'] ?? 502); ?>)
|
||||
- <?php echo ($dev['status'] == 1) ? '启用' : '禁用'; ?>
|
||||
</option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</div>
|
||||
<div class="btn-group" style="margin-left:8px;">
|
||||
<button type="submit" name="action" value="testLink" class="btn btn-info">
|
||||
<i class="fa fa-wifi"></i> 测试链接状态
|
||||
</button>
|
||||
<button type="submit" name="action" value="readData" class="btn btn-success">
|
||||
<i class="fa fa-download"></i> 数据读取
|
||||
</button>
|
||||
<button type="submit" name="action" value="writeData" class="btn btn-warning">
|
||||
<i class="fa fa-upload"></i> 数据写入(探针)
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<hr>
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="well" style="margin-bottom:0;">
|
||||
<h4><i class="fa fa-plug"></i> 手动连接(IP:端口)</h4>
|
||||
<form method="post" action="<?php echo BASE_URL; ?>/Admin/asdDeviceDebug" class="form-inline">
|
||||
<input type="hidden" name="csrf_token" value="<?php echo htmlspecialchars($csrfToken); ?>">
|
||||
<div class="form-group" style="margin-right:6px;">
|
||||
<input type="text" name="manual_ip" class="form-control" style="width:160px;"
|
||||
placeholder="设备 IP,如 192.168.1.10" value="<?php echo htmlspecialchars($_POST['manual_ip'] ?? ''); ?>">
|
||||
</div>
|
||||
<div class="form-group" style="margin-right:6px;">
|
||||
<input type="number" name="manual_port" class="form-control" style="width:90px;"
|
||||
placeholder="端口" value="<?php echo htmlspecialchars($_POST['manual_port'] ?? '502'); ?>">
|
||||
</div>
|
||||
<button type="submit" name="action" value="manualConnect" class="btn btn-primary">
|
||||
<i class="fa fa-wifi"></i> 测试连接
|
||||
</button>
|
||||
</form>
|
||||
<p class="text-muted" style="margin-top:8px;margin-bottom:0;">不依赖数据库记录,直接对任意 IP:端口做 TCP 连通测试。</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php if (!empty($result)): ?>
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<div class="box box-default">
|
||||
<div class="box-header with-border">
|
||||
<h3 class="box-title">
|
||||
<i class="fa fa-terminal"></i>
|
||||
<?php
|
||||
$titles = [
|
||||
'testLink' => '链接状态测试结果',
|
||||
'readData' => '数据读取结果',
|
||||
'writeData' => '数据写入(探针)结果',
|
||||
'manualConnect' => '手动连接测试结果',
|
||||
];
|
||||
echo htmlspecialchars($titles[$result['action']] ?? '测试结果');
|
||||
?>
|
||||
</h3>
|
||||
</div>
|
||||
<div class="box-body">
|
||||
|
||||
<?php if (!empty($result['error'])): ?>
|
||||
<div class="alert alert-danger">
|
||||
<i class="fa fa-times-circle"></i> <?php echo htmlspecialchars($result['error']); ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if ($result['action'] === 'testLink'): ?>
|
||||
<table class="table table-bordered" style="max-width:600px;">
|
||||
<tr><th style="width:160px;">设备</th><td><?php echo htmlspecialchars(($result['device']['device_code'] ?? '') . ' / ' . ($result['device']['device_name'] ?? '')); ?></td></tr>
|
||||
<tr><th>目标地址</th><td><?php echo htmlspecialchars($result['ip']); ?> : <?php echo intval($result['port']); ?></td></tr>
|
||||
<tr><th>连接结果</th>
|
||||
<td>
|
||||
<?php if ($result['reachable']): ?>
|
||||
<span class="label label-success"><i class="fa fa-check"></i> 可达</span>
|
||||
<?php else: ?>
|
||||
<span class="label label-danger"><i class="fa fa-times"></i> 不可达</span>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
</tr>
|
||||
<tr><th>延迟</th><td><?php echo $result['latency']; ?> ms</td></tr>
|
||||
<?php if ($result['error']): ?>
|
||||
<tr><th>错误信息</th><td class="text-danger"><?php echo htmlspecialchars($result['error']); ?></td></tr>
|
||||
<?php endif; ?>
|
||||
</table>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if ($result['action'] === 'readData'): ?>
|
||||
<p>
|
||||
设备编号:<code><?php echo htmlspecialchars($result['device_code']); ?></code>
|
||||
| 测试记录总数:<b><?php echo intval($result['counts']['rec_cnt'] ?? 0); ?></b>
|
||||
| 测试数据总数:<b><?php echo intval($result['counts']['data_cnt'] ?? 0); ?></b>
|
||||
| 设备总数:<b><?php echo intval($result['counts']['dev_cnt'] ?? 0); ?></b>
|
||||
</p>
|
||||
<p class="text-muted">最近 10 条该设备的测试记录(device_code 匹配):</p>
|
||||
<?php if (empty($result['rows'])): ?>
|
||||
<div class="alert alert-info">该设备暂无测试记录。</div>
|
||||
<?php else: ?>
|
||||
<table class="table table-bordered table-hover">
|
||||
<thead>
|
||||
<tr><th>ID</th><th>二维码/条码</th><th>产品型号</th><th>测试结果</th><th>测试时间</th></tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ($result['rows'] as $r): ?>
|
||||
<tr>
|
||||
<td><?php echo intval($r['id']); ?></td>
|
||||
<td><?php echo htmlspecialchars($r['qr_code'] ?? ''); ?></td>
|
||||
<td><?php echo htmlspecialchars($r['product_model'] ?? ''); ?></td>
|
||||
<td><?php echo htmlspecialchars($r['test_result'] ?? ''); ?></td>
|
||||
<td><?php echo htmlspecialchars($r['test_date'] ?? ''); ?></td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
<?php endif; ?>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if ($result['action'] === 'manualConnect'): ?>
|
||||
<table class="table table-bordered" style="max-width:600px;">
|
||||
<tr><th style="width:160px;">目标</th><td><?php echo htmlspecialchars($result['device']['device_name'] ?? ''); ?></td></tr>
|
||||
<tr><th>地址</th><td><?php echo htmlspecialchars($result['ip']); ?> : <?php echo intval($result['port']); ?></td></tr>
|
||||
<tr><th>连接结果</th>
|
||||
<td>
|
||||
<?php if ($result['reachable']): ?>
|
||||
<span class="label label-success"><i class="fa fa-check"></i> 可达</span>
|
||||
<?php else: ?>
|
||||
<span class="label label-danger"><i class="fa fa-times"></i> 不可达</span>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
</tr>
|
||||
<tr><th>延迟</th><td><?php echo $result['latency']; ?> ms</td></tr>
|
||||
<?php if ($result['error']): ?>
|
||||
<tr><th>错误信息</th><td class="text-danger"><?php echo htmlspecialchars($result['error']); ?></td></tr>
|
||||
<?php endif; ?>
|
||||
</table>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if ($result['action'] === 'writeData'): ?>
|
||||
<?php if (empty($result['error'])): ?>
|
||||
<div class="alert alert-success">
|
||||
<i class="fa fa-check-circle"></i> 数据读写链路正常(探针已写入并回读、清理)
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
<table class="table table-bordered" style="max-width:700px;">
|
||||
<tr><th style="width:180px;">设备编号</th><td><?php echo htmlspecialchars($result['device_code']); ?></td></tr>
|
||||
<tr><th>探针二维码</th><td><code><?php echo htmlspecialchars($result['probe_code']); ?></code></td></tr>
|
||||
<tr><th>写入 ID</th><td><?php echo intval($result['written_id']); ?></td></tr>
|
||||
<tr><th>回读一致</th>
|
||||
<td>
|
||||
<?php if ($result['read_back_match']): ?>
|
||||
<span class="label label-success">一致</span>
|
||||
<?php else: ?>
|
||||
<span class="label label-danger">不一致</span>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
</tr>
|
||||
<tr><th>探针记录已清理</th>
|
||||
<td>
|
||||
<?php if ($result['deleted']): ?>
|
||||
<span class="label label-success">已删除</span>
|
||||
<?php else: ?>
|
||||
<span class="label label-warning">未删除(请检查)</span>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
</tr>
|
||||
<?php if ($result['error']): ?>
|
||||
<tr><th>错误信息</th><td class="text-danger"><?php echo htmlspecialchars($result['error']); ?></td></tr>
|
||||
<?php endif; ?>
|
||||
</table>
|
||||
<?php endif; ?>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php include APP_PATH . 'app/views/layouts/admin_footer.php'; ?>
|
||||
@@ -0,0 +1,92 @@
|
||||
<?php
|
||||
$activeMenu = 'asdTestData';
|
||||
include APP_PATH . 'app/views/layouts/admin_header.php';
|
||||
?>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<div class="box box-primary">
|
||||
<div class="box-header with-border">
|
||||
<h3 class="box-title"><i class="fa fa-flask"></i> 测试数据管理</h3>
|
||||
<div class="box-tools">
|
||||
<a href="<?php echo BASE_URL; ?>/Admin/asdTestDataImport" class="btn btn-success btn-sm">
|
||||
<i class="fa fa-plus"></i> 导入测试数据
|
||||
</a>
|
||||
<span class="badge bg-blue" style="margin-left:10px;">共 <?php echo $totalCount; ?> 条记录</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-header">
|
||||
<form method="get" action="<?php echo BASE_URL; ?>/Admin/asdTestData" class="form-inline">
|
||||
<div class="input-group input-group-sm" style="width:300px;">
|
||||
<input type="text" name="keyword" class="form-control" placeholder="输入条码搜索..." value="<?php echo htmlspecialchars($keyword ?? ''); ?>">
|
||||
<span class="input-group-btn">
|
||||
<button type="submit" class="btn btn-primary"><i class="fa fa-search"></i> 搜索</button>
|
||||
</span>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="box-body table-responsive no-padding">
|
||||
<table class="table table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>ID</th>
|
||||
<th>条码</th>
|
||||
<th>项目名称</th>
|
||||
<th>测试员</th>
|
||||
<th>工位</th>
|
||||
<th>设备</th>
|
||||
<th>状态</th>
|
||||
<th>工步数</th>
|
||||
<th>失败数</th>
|
||||
<th>测试时间</th>
|
||||
<th>操作</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php if (empty($records)): ?>
|
||||
<tr><td colspan="11" class="text-center text-muted">暂无测试数据</td></tr>
|
||||
<?php else: ?>
|
||||
<?php foreach ($records as $r): ?>
|
||||
<tr>
|
||||
<td><?php echo $r['id']; ?></td>
|
||||
<td><code><?php echo htmlspecialchars($r['qr_code']); ?></code></td>
|
||||
<td><?php echo htmlspecialchars($r['project_name']); ?></td>
|
||||
<td><?php echo htmlspecialchars($r['tester']); ?></td>
|
||||
<td><?php echo htmlspecialchars($r['site']); ?></td>
|
||||
<td><?php echo htmlspecialchars($r['device']); ?></td>
|
||||
<td>
|
||||
<?php if ($r['status'] === 'PASS'): ?>
|
||||
<span class="label label-success">PASS</span>
|
||||
<?php else: ?>
|
||||
<span class="label label-danger"><?php echo htmlspecialchars($r['status']); ?></span>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
<td><span class="badge bg-light-blue"><?php echo $r['step_count']; ?></span></td>
|
||||
<td>
|
||||
<?php if ($r['fail_count'] > 0): ?>
|
||||
<span class="badge bg-red"><?php echo $r['fail_count']; ?></span>
|
||||
<?php else: ?>
|
||||
<span class="text-muted">0</span>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
<td><?php echo $r['test_time'] ?? '-'; ?></td>
|
||||
<td>
|
||||
<a href="<?php echo BASE_URL; ?>/Admin/asdTestDataView/<?php echo $r['id']; ?>" class="btn btn-xs btn-info" title="查看详情">
|
||||
<i class="fa fa-eye"></i>
|
||||
</a>
|
||||
<form method="post" action="<?php echo BASE_URL; ?>/Admin/asdTestDataDelete/<?php echo $r['id']; ?>" style="display:inline;" onsubmit="return confirm('确定删除该测试记录及所有工步数据吗?');">
|
||||
<input type="hidden" name="csrf_token" value="<?php echo htmlspecialchars($csrfToken); ?>">
|
||||
<button type="submit" class="btn btn-xs btn-danger" title="删除"><i class="fa fa-trash"></i></button>
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
<?php endif; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php include APP_PATH . 'app/views/layouts/admin_footer.php'; ?>
|
||||
@@ -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">×</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'; ?>
|
||||
@@ -0,0 +1,126 @@
|
||||
<?php
|
||||
$activeMenu = 'asdTestData';
|
||||
include APP_PATH . 'app/views/layouts/admin_header.php';
|
||||
?>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<div class="box box-info">
|
||||
<div class="box-header with-border">
|
||||
<h3 class="box-title"><i class="fa fa-info-circle"></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>
|
||||
<div class="box-body">
|
||||
<table class="table table-bordered table-striped" style="width:auto;">
|
||||
<tr>
|
||||
<th style="width:120px;">记录ID</th>
|
||||
<td><?php echo $record['id']; ?></td>
|
||||
<th style="width:120px;">条码</th>
|
||||
<td><code><?php echo htmlspecialchars($record['qr_code']); ?></code></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>测试员</th>
|
||||
<td><?php echo htmlspecialchars($record['tester']); ?></td>
|
||||
<th>项目名称</th>
|
||||
<td><?php echo htmlspecialchars($record['project_name']); ?></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>工位</th>
|
||||
<td><?php echo htmlspecialchars($record['site']); ?></td>
|
||||
<th>站点</th>
|
||||
<td><?php echo htmlspecialchars($record['station']); ?></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>单号</th>
|
||||
<td><?php echo htmlspecialchars($record['mo']); ?></td>
|
||||
<th>工单</th>
|
||||
<td><?php echo htmlspecialchars($record['work_order']); ?></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>组别</th>
|
||||
<td><?php echo htmlspecialchars($record['group_name']); ?></td>
|
||||
<th>设备</th>
|
||||
<td><?php echo htmlspecialchars($record['device']); ?></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>运行时间</th>
|
||||
<td><?php echo htmlspecialchars($record['running_time']); ?></td>
|
||||
<th>测试时间</th>
|
||||
<td><?php echo $record['test_time']; ?></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>整体状态</th>
|
||||
<td colspan="3">
|
||||
<?php if ($record['status'] === 'PASS'): ?>
|
||||
<span class="label label-success">PASS</span>
|
||||
<?php else: ?>
|
||||
<span class="label label-danger"><?php echo htmlspecialchars($record['status']); ?></span>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 工步详情 -->
|
||||
<div class="box box-primary">
|
||||
<div class="box-header with-border">
|
||||
<h3 class="box-title"><i class="fa fa-list-ol"></i> 测试工步详情(共 <?php echo count($details); ?> 步)</h3>
|
||||
</div>
|
||||
<div class="box-body table-responsive no-padding">
|
||||
<table class="table table-hover table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width:50px;">序号</th>
|
||||
<th>工步名称</th>
|
||||
<th>测试项目</th>
|
||||
<th>测试值</th>
|
||||
<th>单位</th>
|
||||
<th>下限</th>
|
||||
<th>上限</th>
|
||||
<th>字符串值</th>
|
||||
<th>结果</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php if (empty($details)): ?>
|
||||
<tr><td colspan="9" class="text-center text-muted">无工步数据</td></tr>
|
||||
<?php else: ?>
|
||||
<?php foreach ($details as $d): ?>
|
||||
<tr>
|
||||
<td><?php echo $d['seq']; ?></td>
|
||||
<td><?php echo htmlspecialchars($d['test_name']); ?></td>
|
||||
<td><?php echo htmlspecialchars($d['test_item']); ?></td>
|
||||
<td>
|
||||
<?php if ($d['data_value'] !== null): ?>
|
||||
<strong><?php echo rtrim(rtrim($d['data_value'], '0'), '.'); ?></strong>
|
||||
<?php else: ?>
|
||||
-
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
<td><?php echo htmlspecialchars($d['test_units']); ?></td>
|
||||
<td><?php echo $d['lower_limit'] !== null ? htmlspecialchars($d['lower_limit']) : '-'; ?></td>
|
||||
<td><?php echo $d['upper_limit'] !== null ? htmlspecialchars($d['upper_limit']) : '-'; ?></td>
|
||||
<td><code><?php echo htmlspecialchars($d['test_value']); ?></code></td>
|
||||
<td>
|
||||
<?php if ($d['test_result'] === 'PASS'): ?>
|
||||
<span class="label label-success">PASS</span>
|
||||
<?php else: ?>
|
||||
<span class="label label-danger"><?php echo htmlspecialchars($d['test_result']); ?></span>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
<?php endif; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php include APP_PATH . 'app/views/layouts/admin_footer.php'; ?>
|
||||
@@ -0,0 +1,133 @@
|
||||
<?php
|
||||
$activeMenu = 'config';
|
||||
include APP_PATH . 'app/views/layouts/admin_header.php';
|
||||
?>
|
||||
|
||||
<?php if (!empty($msg)): ?>
|
||||
<div class="alert alert-success alert-dismissible">
|
||||
<button type="button" class="close" data-dismiss="alert">×</button>
|
||||
<i class="fa fa-check-circle"></i> <?php echo htmlspecialchars($msg); ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if (!empty($error)): ?>
|
||||
<div class="alert alert-danger alert-dismissible">
|
||||
<button type="button" class="close" data-dismiss="alert">×</button>
|
||||
<i class="fa fa-exclamation-circle"></i> <?php echo htmlspecialchars($error); ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="box box-primary">
|
||||
<div class="box-header with-border">
|
||||
<h3 class="box-title"><i class="fa fa-cog"></i> 系统配置</h3>
|
||||
</div>
|
||||
<form method="post" action="">
|
||||
<input type="hidden" name="csrf_token" value="<?php echo htmlspecialchars($csrfToken); ?>">
|
||||
<div class="box-body">
|
||||
<div class="form-group">
|
||||
<label>公司名称</label>
|
||||
<input type="text" name="company_name" class="form-control" value="<?php echo htmlspecialchars($sysConfig['company_name'] ?? ''); ?>">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>MES 展示名称</label>
|
||||
<input type="text" name="mes_name" class="form-control" value="<?php echo htmlspecialchars($sysConfig['mes_name'] ?? ''); ?>">
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-footer">
|
||||
<button type="submit" class="btn btn-primary"><i class="fa fa-save"></i> 保存配置</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 代码前缀规则配置 -->
|
||||
<div class="box box-warning">
|
||||
<div class="box-header with-border">
|
||||
<h3 class="box-title"><i class="fa fa-barcode"></i> 序列号前缀规则</h3>
|
||||
<div class="box-tools pull-right">
|
||||
<button type="button" class="btn btn-box-tool" data-widget="collapse"><i class="fa fa-minus"></i></button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-body">
|
||||
<p class="text-muted" style="margin-bottom:12px;">
|
||||
<i class="fa fa-info-circle"></i>
|
||||
设置各产品类型的序列号前缀。工位录入时,序列号必须匹配对应前缀,否则将报错。
|
||||
留空表示不检查前缀。
|
||||
</p>
|
||||
<div id="prefixRulesContainer">
|
||||
<?php
|
||||
$ruleIndex = 0;
|
||||
if (!empty($codePrefixRules)):
|
||||
foreach ($codePrefixRules as $typeName => $prefixVal):
|
||||
?>
|
||||
<div class="prefix-rule-row" style="display:flex; gap:10px; margin-bottom:8px; align-items:center;">
|
||||
<input type="text" name="prefix_types[]" class="form-control" style="flex:1;"
|
||||
placeholder="产品类型(如:成品、电芯、PCBA)"
|
||||
value="<?php echo htmlspecialchars($typeName); ?>">
|
||||
<input type="text" name="prefix_values[]" class="form-control" style="width:120px;"
|
||||
placeholder="前缀(如:CP)"
|
||||
value="<?php echo htmlspecialchars($prefixVal); ?>">
|
||||
<button type="button" class="btn btn-danger btn-sm remove-prefix-row" title="删除">
|
||||
<i class="fa fa-trash"></i>
|
||||
</button>
|
||||
</div>
|
||||
<?php
|
||||
$ruleIndex++;
|
||||
endforeach;
|
||||
endif;
|
||||
?>
|
||||
</div>
|
||||
<button type="button" class="btn btn-success btn-sm" id="addPrefixRow" style="margin-top:8px;">
|
||||
<i class="fa fa-plus"></i> 添加规则
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="box box-info">
|
||||
<div class="box-header with-border">
|
||||
<h3 class="box-title"><i class="fa fa-info-circle"></i> 当前信息</h3>
|
||||
</div>
|
||||
<div class="box-body">
|
||||
<dl class="dl-horizontal">
|
||||
<dt>公司名称</dt>
|
||||
<dd><?php echo htmlspecialchars($sysConfig['company_name'] ?? ''); ?></dd>
|
||||
<dt>系统名称</dt>
|
||||
<dd><?php echo htmlspecialchars($sysConfig['mes_name'] ?? ''); ?></dd>
|
||||
<dt>前缀规则</dt>
|
||||
<dd>
|
||||
<?php if (!empty($codePrefixRules)): ?>
|
||||
<table class="table table-condensed table-bordered" style="margin-bottom:0;">
|
||||
<thead><tr><th>产品类型</th><th>前缀</th></tr></thead>
|
||||
<tbody>
|
||||
<?php foreach ($codePrefixRules as $typeName => $prefixVal): ?>
|
||||
<tr>
|
||||
<td><?php echo htmlspecialchars($typeName); ?></td>
|
||||
<td>
|
||||
<?php if ($prefixVal !== ''): ?>
|
||||
<code><?php echo htmlspecialchars($prefixVal); ?></code>
|
||||
<?php else: ?>
|
||||
<span class="text-muted">无限制</span>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
<?php else: ?>
|
||||
<span class="text-muted">未配置</span>
|
||||
<?php endif; ?>
|
||||
</dd>
|
||||
<dt>PHP 版本</dt>
|
||||
<dd><?php echo phpversion(); ?></dd>
|
||||
</dl>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php // v3-P2:原内联 <script> 已抽离为 external JS,并以 nonce 加载,配合 CSP script-src 'nonce-***' ?>
|
||||
<script src="<?php echo BASE_URL; ?>/static/js/admin_config.js" nonce="<?php echo csp_nonce(); ?>"></script>
|
||||
|
||||
<?php include APP_PATH . 'app/views/layouts/admin_footer.php'; ?>
|
||||
@@ -0,0 +1,55 @@
|
||||
<?php
|
||||
$activeMenu = 'customer';
|
||||
include APP_PATH . 'app/views/layouts/admin_header.php';
|
||||
?>
|
||||
|
||||
<div class="box">
|
||||
<div class="box-header with-border">
|
||||
<h3 class="box-title"><i class="fa fa-list"></i> 客户列表</h3>
|
||||
<div class="box-tools">
|
||||
<a href="<?php echo BASE_URL; ?>/Admin/customerAdd" class="btn btn-primary btn-sm"><i class="fa fa-plus"></i> 添加客户</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-body table-responsive no-padding">
|
||||
<table class="table table-hover table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width:60px">序号</th>
|
||||
<th>公司名称</th>
|
||||
<th>联系人1</th>
|
||||
<th>联系方式1</th>
|
||||
<th>联系人2</th>
|
||||
<th>联系方式2</th>
|
||||
<th>操作人</th>
|
||||
<th style="width:150px">操作</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php if (!empty($customers)): ?>
|
||||
<?php foreach ($customers as $cust): ?>
|
||||
<tr>
|
||||
<td><?php echo $cust['id']; ?></td>
|
||||
<td><strong><?php echo htmlspecialchars($cust['company_name']); ?></strong></td>
|
||||
<td><?php echo htmlspecialchars($cust['contact1'] ?? '-'); ?></td>
|
||||
<td><?php echo htmlspecialchars($cust['phone1'] ?? '-'); ?></td>
|
||||
<td><?php echo htmlspecialchars($cust['contact2'] ?? '-'); ?></td>
|
||||
<td><?php echo htmlspecialchars($cust['phone2'] ?? '-'); ?></td>
|
||||
<td><?php echo htmlspecialchars($cust['created_by'] ?? '-'); ?></td>
|
||||
<td>
|
||||
<a href="<?php echo BASE_URL; ?>/Admin/customerEdit/<?php echo $cust['id']; ?>" class="btn btn-xs btn-info"><i class="fa fa-edit"></i> 编辑</a>
|
||||
<form method="post" action="<?php echo BASE_URL; ?>/Admin/customerDelete/<?php echo $cust['id']; ?>" style="display:inline;" onsubmit="return confirm('确定删除该客户?')">
|
||||
<input type="hidden" name="csrf_token" value="<?php echo htmlspecialchars($csrfToken); ?>">
|
||||
<button type="submit" class="btn btn-xs btn-danger"><i class="fa fa-trash"></i> 删除</button>
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
<?php else: ?>
|
||||
<tr><td colspan="8" class="text-center text-muted">暂无客户数据</td></tr>
|
||||
<?php endif; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php include APP_PATH . 'app/views/layouts/admin_footer.php'; ?>
|
||||
@@ -0,0 +1,45 @@
|
||||
<?php
|
||||
$activeMenu = 'customer';
|
||||
include APP_PATH . 'app/views/layouts/admin_header.php';
|
||||
?>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6 col-md-offset-3">
|
||||
<div class="box box-primary">
|
||||
<div class="box-header with-border">
|
||||
<h3 class="box-title"><i class="fa fa-plus"></i> 添加客户</h3>
|
||||
</div>
|
||||
<form method="post" action="">
|
||||
<input type="hidden" name="csrf_token" value="<?php echo htmlspecialchars($csrfToken); ?>">
|
||||
<div class="box-body">
|
||||
<div class="form-group">
|
||||
<label>公司名称 <span class="text-danger">*</span></label>
|
||||
<input type="text" name="company_name" class="form-control" placeholder="公司全称" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>联系人1</label>
|
||||
<input type="text" name="contact1" class="form-control" placeholder="主要联系人">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>联系方式1</label>
|
||||
<input type="text" name="phone1" class="form-control" placeholder="电话/邮箱">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>联系人2</label>
|
||||
<input type="text" name="contact2" class="form-control" placeholder="备用联系人">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>联系方式2</label>
|
||||
<input type="text" name="phone2" class="form-control" placeholder="电话/邮箱">
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-footer">
|
||||
<button type="submit" class="btn btn-primary"><i class="fa fa-save"></i> 保存</button>
|
||||
<a href="<?php echo BASE_URL; ?>/Admin/customer" class="btn btn-default"><i class="fa fa-arrow-left"></i> 返回</a>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php include APP_PATH . 'app/views/layouts/admin_footer.php'; ?>
|
||||
@@ -0,0 +1,45 @@
|
||||
<?php
|
||||
$activeMenu = 'customer';
|
||||
include APP_PATH . 'app/views/layouts/admin_header.php';
|
||||
?>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6 col-md-offset-3">
|
||||
<div class="box box-warning">
|
||||
<div class="box-header with-border">
|
||||
<h3 class="box-title"><i class="fa fa-edit"></i> 编辑客户</h3>
|
||||
</div>
|
||||
<form method="post" action="">
|
||||
<input type="hidden" name="csrf_token" value="<?php echo htmlspecialchars($csrfToken); ?>">
|
||||
<div class="box-body">
|
||||
<div class="form-group">
|
||||
<label>公司名称 <span class="text-danger">*</span></label>
|
||||
<input type="text" name="company_name" class="form-control" value="<?php echo htmlspecialchars($cust['company_name']); ?>" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>联系人1</label>
|
||||
<input type="text" name="contact1" class="form-control" value="<?php echo htmlspecialchars($cust['contact1'] ?? ''); ?>">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>联系方式1</label>
|
||||
<input type="text" name="phone1" class="form-control" value="<?php echo htmlspecialchars($cust['phone1'] ?? ''); ?>">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>联系人2</label>
|
||||
<input type="text" name="contact2" class="form-control" value="<?php echo htmlspecialchars($cust['contact2'] ?? ''); ?>">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>联系方式2</label>
|
||||
<input type="text" name="phone2" class="form-control" value="<?php echo htmlspecialchars($cust['phone2'] ?? ''); ?>">
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-footer">
|
||||
<button type="submit" class="btn btn-warning"><i class="fa fa-save"></i> 更新</button>
|
||||
<a href="<?php echo BASE_URL; ?>/Admin/customer" class="btn btn-default"><i class="fa fa-arrow-left"></i> 返回</a>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php include APP_PATH . 'app/views/layouts/admin_footer.php'; ?>
|
||||
@@ -0,0 +1,48 @@
|
||||
<?php
|
||||
$activeMenu = 'database';
|
||||
include APP_PATH . 'app/views/layouts/admin_header.php';
|
||||
?>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<div class="box box-primary">
|
||||
<div class="box-header with-border">
|
||||
<h3 class="box-title"><i class="fa fa-database"></i> 数据库表管理</h3>
|
||||
<div class="box-tools">
|
||||
<span class="badge bg-blue">共 <?php echo count($tables); ?> 张表</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-body table-responsive no-padding">
|
||||
<table class="table table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>序号</th>
|
||||
<th>表名</th>
|
||||
<th>记录数</th>
|
||||
<th>操作</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ($tables as $idx => $tbl): ?>
|
||||
<tr>
|
||||
<td><?php echo $idx + 1; ?></td>
|
||||
<td>
|
||||
<strong><?php echo htmlspecialchars($tbl['business_name']); ?></strong>
|
||||
<br><code style="color:#999;"><?php echo htmlspecialchars($tbl['name']); ?></code>
|
||||
</td>
|
||||
<td><span class="badge bg-light-blue"><?php echo $tbl['count']; ?></span></td>
|
||||
<td>
|
||||
<a href="<?php echo BASE_URL; ?>/Admin/databaseView/<?php echo $idx; ?>" class="btn btn-sm btn-primary">
|
||||
<i class="fa fa-eye"></i> 查看/编辑
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php include APP_PATH . 'app/views/layouts/admin_footer.php'; ?>
|
||||
@@ -0,0 +1,130 @@
|
||||
<?php
|
||||
$activeMenu = 'databaseBackup';
|
||||
include APP_PATH . 'app/views/layouts/admin_header.php';
|
||||
?>
|
||||
|
||||
<?php if (!empty($message)): ?>
|
||||
<div class="alert alert-<?php echo $messageType; ?> alert-dismissible">
|
||||
<button type="button" class="close" data-dismiss="alert" aria-hidden="true">×</button>
|
||||
<h4><i class="icon fa <?php echo $messageType === 'success' ? 'fa-check' : ($messageType === 'danger' ? 'fa-ban' : 'fa-warning'); ?>"></i> 操作结果</h4>
|
||||
<?php echo nl2br(htmlspecialchars($message)); ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<div class="row">
|
||||
<!-- 备份操作区 -->
|
||||
<div class="col-md-5">
|
||||
<div class="box box-success">
|
||||
<div class="box-header with-border">
|
||||
<h3 class="box-title"><i class="fa fa-cloud-download"></i> 创建备份</h3>
|
||||
</div>
|
||||
<div class="box-body">
|
||||
<p class="text-muted">将当前数据库导出为 SQL 文件,保存到服务器 <code>backups/</code> 目录。</p>
|
||||
<p>数据库:<strong><?php echo htmlspecialchars(DB_NAME); ?></strong></p>
|
||||
<p>主机:<strong><?php echo htmlspecialchars(DB_HOST); ?></strong></p>
|
||||
<form method="post" onsubmit="return confirm('确定要创建数据库备份吗?');">
|
||||
<input type="hidden" name="csrf_token" value="<?php echo htmlspecialchars($csrfToken); ?>">
|
||||
<input type="hidden" name="action" value="backup">
|
||||
<button type="submit" class="btn btn-success btn-lg btn-block">
|
||||
<i class="fa fa-database"></i> 立即备份数据库
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
<div class="box-footer text-muted">
|
||||
<small><i class="fa fa-info-circle"></i> 备份将包含所有表结构和数据(排除 jp_ 前缀的表)</small>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="box box-default">
|
||||
<div class="box-header with-border">
|
||||
<h3 class="box-title"><i class="fa fa-info-circle"></i> 说明</h3>
|
||||
</div>
|
||||
<div class="box-body">
|
||||
<ul>
|
||||
<li><strong>备份</strong>:将数据库完整导出为 .sql 文件保存到服务器本地</li>
|
||||
<li><strong>恢复</strong>:选择一个备份文件,将其数据恢复到当前数据库</li>
|
||||
<li><strong>下载</strong>:将备份文件下载到本地电脑保存</li>
|
||||
<li><strong>删除</strong>:删除服务器上的备份文件</li>
|
||||
</ul>
|
||||
<div class="callout callout-warning">
|
||||
<h4><i class="fa fa-warning"></i> 注意</h4>
|
||||
<p>恢复操作会<strong>覆盖当前数据库</strong>中已存在的同名表!请在恢复前确认已做好最新备份。</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 备份文件列表 -->
|
||||
<div class="col-md-7">
|
||||
<div class="box box-primary">
|
||||
<div class="box-header with-border">
|
||||
<h3 class="box-title"><i class="fa fa-files-o"></i> 备份文件列表</h3>
|
||||
<div class="box-tools">
|
||||
<span class="badge bg-blue">共 <?php echo count($backupFiles); ?> 个备份</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-body table-responsive no-padding">
|
||||
<?php if (empty($backupFiles)): ?>
|
||||
<div class="text-center" style="padding:40px;">
|
||||
<i class="fa fa-inbox" style="font-size:48px; color:#ddd;"></i>
|
||||
<p class="text-muted" style="margin-top:10px;">暂无备份文件,请先创建备份</p>
|
||||
</div>
|
||||
<?php else: ?>
|
||||
<table class="table table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>文件名</th>
|
||||
<th>大小</th>
|
||||
<th>备份时间</th>
|
||||
<th>操作</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ($backupFiles as $file): ?>
|
||||
<tr>
|
||||
<td>
|
||||
<i class="fa fa-file-code-o text-primary"></i>
|
||||
<code><?php echo htmlspecialchars($file['name']); ?></code>
|
||||
</td>
|
||||
<td><span class="badge bg-light-blue"><?php echo $file['size']; ?></span></td>
|
||||
<td><?php echo $file['time']; ?></td>
|
||||
<td>
|
||||
<!-- 恢复 -->
|
||||
<form method="post" style="display:inline;" onsubmit="return confirm('确定要恢复此备份吗?\n\n⚠ 此操作会覆盖当前数据库中的数据!\n建议先创建最新备份。');">
|
||||
<input type="hidden" name="csrf_token" value="<?php echo htmlspecialchars($csrfToken); ?>">
|
||||
<input type="hidden" name="action" value="restore">
|
||||
<input type="hidden" name="file" value="<?php echo htmlspecialchars($file['name']); ?>">
|
||||
<button type="submit" class="btn btn-sm btn-warning" title="恢复此备份">
|
||||
<i class="fa fa-cloud-upload"></i> 恢复
|
||||
</button>
|
||||
</form>
|
||||
<!-- 下载 -->
|
||||
<form method="post" style="display:inline;">
|
||||
<input type="hidden" name="csrf_token" value="<?php echo htmlspecialchars($csrfToken); ?>">
|
||||
<input type="hidden" name="action" value="download">
|
||||
<input type="hidden" name="file" value="<?php echo htmlspecialchars($file['name']); ?>">
|
||||
<button type="submit" class="btn btn-sm btn-primary" title="下载到本地">
|
||||
<i class="fa fa-download"></i> 下载
|
||||
</button>
|
||||
</form>
|
||||
<!-- 删除 -->
|
||||
<form method="post" style="display:inline;" onsubmit="return confirm('确定要删除备份文件 <?php echo htmlspecialchars($file['name']); ?> 吗?');">
|
||||
<input type="hidden" name="csrf_token" value="<?php echo htmlspecialchars($csrfToken); ?>">
|
||||
<input type="hidden" name="action" value="delete">
|
||||
<input type="hidden" name="file" value="<?php echo htmlspecialchars($file['name']); ?>">
|
||||
<button type="submit" class="btn btn-sm btn-danger" title="删除备份文件">
|
||||
<i class="fa fa-trash"></i> 删除
|
||||
</button>
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php include APP_PATH . 'app/views/layouts/admin_footer.php'; ?>
|
||||
@@ -0,0 +1,74 @@
|
||||
<?php
|
||||
$activeMenu = 'database';
|
||||
include APP_PATH . 'app/views/layouts/admin_header.php';
|
||||
?>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-8 col-md-offset-2">
|
||||
<div class="box box-warning">
|
||||
<div class="box-header with-border">
|
||||
<h3 class="box-title">
|
||||
<i class="fa fa-edit"></i> 编辑记录 — 表:<code><?php echo htmlspecialchars($table); ?></code> — ID:<?php echo $row['id']; ?>
|
||||
</h3>
|
||||
<div class="box-tools">
|
||||
<a href="<?php echo BASE_URL; ?>/Admin/databaseView/<?php echo $idx; ?>" class="btn btn-default btn-sm">
|
||||
<i class="fa fa-arrow-left"></i> 返回
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<form method="post" action="">
|
||||
<input type="hidden" name="csrf_token" value="<?php echo htmlspecialchars($csrfToken); ?>">
|
||||
<div class="box-body">
|
||||
<?php foreach ($columns as $col): ?>
|
||||
<?php $field = $col['Field']; ?>
|
||||
<?php $isSensitive = in_array($field, $sensitiveFields ?? [], true); ?>
|
||||
<div class="form-group">
|
||||
<label>
|
||||
<strong><?php echo htmlspecialchars($field); ?></strong>
|
||||
<small class="text-muted">(<?php echo htmlspecialchars($col['Type']); ?><?php echo $col['Key'] === 'PRI' ? ', 主键' : ''; ?><?php echo $isSensitive ? ', 已脱敏不可编辑' : ''; ?>)</small>
|
||||
</label>
|
||||
<?php if ($field === 'id'): ?>
|
||||
<input type="text" class="form-control" value="<?php echo htmlspecialchars($row[$field] ?? ''); ?>" disabled>
|
||||
<?php elseif ($isSensitive): ?>
|
||||
<input type="text" class="form-control" value="<?php echo htmlspecialchars($row[$field] ?? ''); ?>" disabled>
|
||||
<p class="text-warning" style="margin-top:4px;"><i class="fa fa-lock"></i> 敏感字段已脱敏,无法在此查看或修改</p>
|
||||
<?php elseif (stripos($col['Type'], 'text') !== false): ?>
|
||||
<textarea name="<?php echo htmlspecialchars($field); ?>" class="form-control" rows="4"><?php echo htmlspecialchars($row[$field] ?? ''); ?></textarea>
|
||||
<?php elseif (stripos($col['Type'], 'enum') !== false): ?>
|
||||
<?php
|
||||
// 解析 ENUM 值
|
||||
preg_match("/^enum\((.*)\)$/i", $col['Type'], $m);
|
||||
$enumVals = [];
|
||||
if (!empty($m[1])) {
|
||||
foreach (explode(',', $m[1]) as $ev) {
|
||||
$enumVals[] = trim($ev, "'\"");
|
||||
}
|
||||
}
|
||||
?>
|
||||
<?php if (!empty($enumVals)): ?>
|
||||
<select name="<?php echo htmlspecialchars($field); ?>" class="form-control">
|
||||
<?php foreach ($enumVals as $ev): ?>
|
||||
<option value="<?php echo htmlspecialchars($ev); ?>" <?php echo ($row[$field] ?? '') === $ev ? 'selected' : ''; ?>>
|
||||
<?php echo htmlspecialchars($ev); ?>
|
||||
</option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
<?php else: ?>
|
||||
<input type="text" name="<?php echo htmlspecialchars($field); ?>" class="form-control" value="<?php echo htmlspecialchars($row[$field] ?? ''); ?>">
|
||||
<?php endif; ?>
|
||||
<?php else: ?>
|
||||
<input type="text" name="<?php echo htmlspecialchars($field); ?>" class="form-control" value="<?php echo htmlspecialchars($row[$field] ?? ''); ?>">
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
<div class="box-footer">
|
||||
<button type="submit" class="btn btn-warning"><i class="fa fa-save"></i> 保存修改</button>
|
||||
<a href="<?php echo BASE_URL; ?>/Admin/databaseView/<?php echo $idx; ?>" class="btn btn-default">取消</a>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php include APP_PATH . 'app/views/layouts/admin_footer.php'; ?>
|
||||
@@ -0,0 +1,173 @@
|
||||
<?php
|
||||
$activeMenu = 'databaseUpgrade';
|
||||
include APP_PATH . 'app/views/layouts/admin_header.php';
|
||||
?>
|
||||
|
||||
<style>
|
||||
.sql-preview {
|
||||
background: #1e1e1e;
|
||||
color: #d4d4d4;
|
||||
font-family: 'Consolas', 'Courier New', monospace;
|
||||
font-size: 12px;
|
||||
padding: 10px 15px;
|
||||
border-radius: 4px;
|
||||
max-height: 300px;
|
||||
overflow-y: auto;
|
||||
white-space: pre-wrap;
|
||||
word-break: break-all;
|
||||
line-height: 1.5;
|
||||
margin: 0;
|
||||
}
|
||||
.sql-preview::-webkit-scrollbar { width: 6px; }
|
||||
.sql-preview::-webkit-scrollbar-track { background: #2d2d2d; }
|
||||
.sql-preview::-webkit-scrollbar-thumb { background: #555; border-radius: 3px; }
|
||||
.badge-status { font-size: 12px; }
|
||||
.btn-execute-all { margin-left: 10px; }
|
||||
</style>
|
||||
|
||||
<?php if (!empty($message)): ?>
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<div class="alert alert-<?php echo $messageType; ?> alert-dismissible">
|
||||
<button type="button" class="close" data-dismiss="alert" aria-hidden="true">×</button>
|
||||
<h4><i class="icon fa fa-<?php echo $messageType === 'success' ? 'check' : ($messageType === 'danger' ? 'ban' : 'warning'); ?>"></i> 提示</h4>
|
||||
<?php echo nl2br(htmlspecialchars($message)); ?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<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-wrench"></i> 数据库升级/迁移</h3>
|
||||
<div class="box-tools">
|
||||
<?php if ($pendingCount > 0): ?>
|
||||
<span class="badge bg-yellow" style="font-size:13px;"><?php echo $pendingCount; ?> 个待执行</span>
|
||||
<form method="post" style="display:inline;" class="confirm-form" data-confirm="确定要一键执行所有未执行的迁移脚本吗?此操作将按文件名顺序依次执行所有未执行过的 .sql 文件。">
|
||||
<input type="hidden" name="csrf_token" value="<?php echo htmlspecialchars($csrfToken); ?>">
|
||||
<input type="hidden" name="action" value="execute_all">
|
||||
<button type="submit" class="btn btn-sm btn-success btn-execute-all">
|
||||
<i class="fa fa-bolt"></i> 一键全部执行
|
||||
</button>
|
||||
</form>
|
||||
<?php else: ?>
|
||||
<span class="badge bg-green" style="font-size:13px;">全部已执行</span>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-body">
|
||||
<div class="callout callout-info" style="margin-bottom: 15px;">
|
||||
<h4><i class="icon fa fa-info-circle"></i> 使用说明</h4>
|
||||
<p style="margin-bottom:0;">
|
||||
1. 系统会自动扫描 <code>sql/</code> 目录下的所有 <code>.sql</code> 文件。<br>
|
||||
2. 已执行过的脚本会标记为 <span class="label label-success">已执行</span>,不会重复执行。<br>
|
||||
3. 每个脚本在事务中执行,失败会自动回滚。<br>
|
||||
4. 点击文件名可展开查看 SQL 内容预览。
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<?php if (empty($fileList)): ?>
|
||||
<div class="text-center" style="padding: 40px;">
|
||||
<i class="fa fa-inbox" style="font-size: 48px; color: #ccc;"></i>
|
||||
<p style="color: #999; margin-top: 10px;">sql/ 目录下没有找到 .sql 迁移文件</p>
|
||||
</div>
|
||||
<?php else: ?>
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width: 40px;">#</th>
|
||||
<th>文件名</th>
|
||||
<th style="width: 100px;">状态</th>
|
||||
<th style="width: 160px;">执行时间</th>
|
||||
<th style="width: 120px;">操作</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php $idx = 1; foreach ($fileList as $file): ?>
|
||||
<tr>
|
||||
<td><?php echo $idx++; ?></td>
|
||||
<td>
|
||||
<a href="#" class="sql-preview-link" data-preview="preview-<?php echo $idx; ?>" style="font-family: monospace;">
|
||||
<i class="fa fa-file-code-o"></i> <?php echo htmlspecialchars($file['name']); ?>
|
||||
</a>
|
||||
<div id="preview-<?php echo $idx; ?>" class="sql-preview" style="display:none; margin-top:8px;">
|
||||
<?php echo $file['preview']; ?>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<?php if ($file['is_migrated']): ?>
|
||||
<?php if ($file['status'] == 1): ?>
|
||||
<span class="label label-success">已执行</span>
|
||||
<?php else: ?>
|
||||
<span class="label label-danger" title="<?php echo htmlspecialchars($file['error_msg']); ?>">执行失败</span>
|
||||
<?php endif; ?>
|
||||
<?php else: ?>
|
||||
<span class="label label-warning">待执行</span>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
<td>
|
||||
<?php if ($file['is_migrated'] && $file['executed_at']): ?>
|
||||
<small><?php echo htmlspecialchars($file['executed_at']); ?></small>
|
||||
<?php else: ?>
|
||||
<small style="color:#999;">—</small>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
<td>
|
||||
<?php if (!$file['is_migrated'] || $file['status'] == 0): ?>
|
||||
<form method="post" style="display:inline;" class="confirm-form" data-confirm="确定要执行该迁移脚本吗?">
|
||||
<input type="hidden" name="csrf_token" value="<?php echo htmlspecialchars($csrfToken); ?>">
|
||||
<input type="hidden" name="action" value="execute">
|
||||
<input type="hidden" name="file" value="<?php echo htmlspecialchars($file['name']); ?>">
|
||||
<button type="submit" class="btn btn-sm btn-primary">
|
||||
<i class="fa fa-play"></i> 执行
|
||||
</button>
|
||||
</form>
|
||||
<?php else: ?>
|
||||
<button class="btn btn-sm btn-default" disabled>
|
||||
<i class="fa fa-check"></i> 已完成
|
||||
</button>
|
||||
<?php endif; ?>
|
||||
<?php if ($file['is_migrated'] && $file['status'] == 0 && !empty($file['error_msg'])): ?>
|
||||
<br><small class="text-danger" style="display:inline-block;max-width:120px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;" title="<?php echo htmlspecialchars($file['error_msg']); ?>"><?php echo htmlspecialchars($file['error_msg']); ?></small>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script nonce="<?php echo csp_nonce(); ?>">
|
||||
function togglePreview(link, previewId) {
|
||||
var preview = document.getElementById(previewId);
|
||||
var icon = link.querySelector('i');
|
||||
if (preview.style.display === 'none' || preview.style.display === '') {
|
||||
preview.style.display = 'block';
|
||||
if (icon) {
|
||||
icon.className = 'fa fa-file-code-o';
|
||||
}
|
||||
} else {
|
||||
preview.style.display = 'none';
|
||||
if (icon) {
|
||||
icon.className = 'fa fa-file-code-o';
|
||||
}
|
||||
}
|
||||
}
|
||||
// CSP:文件名预览切换改为事件委托(替代内联 onclick / javascript:)
|
||||
document.addEventListener('click', function (e) {
|
||||
var link = e.target.closest ? e.target.closest('.sql-preview-link') : null;
|
||||
if (!link) return;
|
||||
e.preventDefault();
|
||||
togglePreview(link, link.getAttribute('data-preview'));
|
||||
});
|
||||
</script>
|
||||
|
||||
<?php include APP_PATH . 'app/views/layouts/admin_footer.php'; ?>
|
||||
@@ -0,0 +1,98 @@
|
||||
<?php
|
||||
$activeMenu = 'database';
|
||||
include APP_PATH . 'app/views/layouts/admin_header.php';
|
||||
?>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<div class="box box-info">
|
||||
<div class="box-header with-border">
|
||||
<h3 class="box-title">
|
||||
<i class="fa fa-table"></i> 表:<code><?php echo htmlspecialchars($table); ?></code>
|
||||
</h3>
|
||||
<div class="box-tools">
|
||||
<a href="<?php echo BASE_URL; ?>/Admin/database" class="btn btn-default btn-sm"><i class="fa fa-arrow-left"></i> 返回列表</a>
|
||||
<span class="badge bg-blue" style="margin-left:10px;">共 <?php echo $totalCount; ?> 条</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-body table-responsive no-padding">
|
||||
<table class="table table-hover table-bordered">
|
||||
<thead>
|
||||
<tr>
|
||||
<?php foreach ($columns as $col): ?>
|
||||
<th title="<?php echo htmlspecialchars($col['Type']); ?>">
|
||||
<?php echo htmlspecialchars($col['Field']); ?>
|
||||
</th>
|
||||
<?php endforeach; ?>
|
||||
<th style="width:140px;">操作</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ($rows as $row): ?>
|
||||
<tr>
|
||||
<?php foreach ($columns as $col): ?>
|
||||
<td style="max-width:200px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap;">
|
||||
<?php
|
||||
$val = $row[$col['Field']] ?? '';
|
||||
if ($val === null) {
|
||||
echo '<span class="text-muted">NULL</span>';
|
||||
} elseif ($val === '') {
|
||||
echo '<span class="text-muted">-</span>';
|
||||
} else {
|
||||
echo htmlspecialchars($val);
|
||||
}
|
||||
?>
|
||||
</td>
|
||||
<?php endforeach; ?>
|
||||
<td>
|
||||
<a href="<?php echo BASE_URL; ?>/Admin/databaseEdit/<?php echo $idx; ?>/<?php echo $row['id']; ?>"
|
||||
class="btn btn-xs btn-warning"><i class="fa fa-edit"></i> 编辑</a>
|
||||
<form method="post" action="<?php echo BASE_URL; ?>/Admin/databaseDelete/<?php echo $idx; ?>/<?php echo $row['id']; ?>" style="display:inline;" onsubmit="return confirm('确定删除 ID=<?php echo $row['id']; ?> 的记录吗?此操作不可恢复!');">
|
||||
<input type="hidden" name="csrf_token" value="<?php echo htmlspecialchars($csrfToken); ?>">
|
||||
<button type="submit" class="btn btn-xs btn-danger"><i class="fa fa-trash"></i> 删除</button>
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
<?php if (empty($rows)): ?>
|
||||
<tr>
|
||||
<td colspan="<?php echo count($columns) + 1; ?>" class="text-center text-muted">
|
||||
暂无数据
|
||||
</td>
|
||||
</tr>
|
||||
<?php endif; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<?php if ($totalPages > 1): ?>
|
||||
<div class="box-footer clearfix">
|
||||
<ul class="pagination pagination-sm no-margin pull-right">
|
||||
<?php if ($page > 1): ?>
|
||||
<li><a href="<?php echo BASE_URL; ?>/Admin/databaseView/<?php echo $idx; ?>?page=<?php echo $page - 1; ?>">«</a></li>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php
|
||||
$range = 2;
|
||||
$start = max(1, $page - $range);
|
||||
$end = min($totalPages, $page + $range);
|
||||
if ($start > 1) { echo '<li><a href="<?php echo BASE_URL; ?>/Admin/databaseView/'.$idx.'?page=1">1</a></li>'; if ($start > 2) echo '<li><span>...</span></li>'; }
|
||||
for ($p = $start; $p <= $end; $p++):
|
||||
?>
|
||||
<li class="<?php echo $p == $page ? 'active' : ''; ?>">
|
||||
<a href="<?php echo BASE_URL; ?>/Admin/databaseView/<?php echo $idx; ?>?page=<?php echo $p; ?>"><?php echo $p; ?></a>
|
||||
</li>
|
||||
<?php endfor;
|
||||
if ($end < $totalPages) { if ($end < $totalPages - 1) echo '<li><span>...</span></li>'; echo '<li><a href="<?php echo BASE_URL; ?>/Admin/databaseView/'.$idx.'?page='.$totalPages.'">'.$totalPages.'</a></li>'; }
|
||||
?>
|
||||
|
||||
<?php if ($page < $totalPages): ?>
|
||||
<li><a href="<?php echo BASE_URL; ?>/Admin/databaseView/<?php echo $idx; ?>?page=<?php echo $page + 1; ?>">»</a></li>
|
||||
<?php endif; ?>
|
||||
</ul>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php include APP_PATH . 'app/views/layouts/admin_footer.php'; ?>
|
||||
@@ -0,0 +1,90 @@
|
||||
<?php
|
||||
$activeMenu = 'employee';
|
||||
include APP_PATH . 'app/views/layouts/admin_header.php';
|
||||
?>
|
||||
|
||||
<div class="box">
|
||||
<div class="box-header with-border">
|
||||
<h3 class="box-title"><i class="fa fa-list"></i> 员工列表</h3>
|
||||
<div class="box-tools">
|
||||
<a href="<?php echo BASE_URL; ?>/Admin/employeeAdd" class="btn btn-primary btn-sm"><i class="fa fa-plus"></i> 添加员工</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-body table-responsive no-padding">
|
||||
<table class="table table-hover table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width:60px">序号</th>
|
||||
<th>员工编号</th>
|
||||
<th>姓名</th>
|
||||
<th>权限</th>
|
||||
<th>管理类目</th>
|
||||
<th>操作人</th>
|
||||
<th style="width:200px">操作</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php if (!empty($employees)): ?>
|
||||
<?php foreach ($employees as $emp): ?>
|
||||
<tr>
|
||||
<td><?php echo $emp['id']; ?></td>
|
||||
<td><code><?php echo htmlspecialchars($emp['emp_no']); ?></code></td>
|
||||
<td><?php echo htmlspecialchars($emp['emp_name']); ?></td>
|
||||
<td>
|
||||
<?php
|
||||
$roleNames = \core\base\Controller::ROLE_NAMES;
|
||||
$roleClass = [
|
||||
\core\base\Controller::ROLE_SUPER_ADMIN => 'danger',
|
||||
\core\base\Controller::ROLE_ADMIN => 'warning',
|
||||
\core\base\Controller::ROLE_OPERATOR => 'info',
|
||||
];
|
||||
$cls = $roleClass[$emp['role']] ?? 'default';
|
||||
?>
|
||||
<span class="label label-<?php echo $cls; ?>">
|
||||
<?php echo htmlspecialchars($roleNames[$emp['role']] ?? $emp['role']); ?>
|
||||
</span>
|
||||
</td>
|
||||
<td>
|
||||
<?php
|
||||
$catNames = \core\base\Controller::CATEGORY_NAMES;
|
||||
$cat = $emp['category'] ?? 'mes';
|
||||
if ($emp['role'] === \core\base\Controller::ROLE_SUPER_ADMIN) {
|
||||
echo '<span class="label label-danger">全部类目</span>';
|
||||
} else {
|
||||
echo '<span class="label label-primary">' . htmlspecialchars($catNames[$cat] ?? $cat) . '</span>';
|
||||
}
|
||||
?>
|
||||
</td>
|
||||
<td><?php echo htmlspecialchars($emp['created_by'] ?? '-'); ?></td>
|
||||
<td>
|
||||
<?php
|
||||
// 只有超级管理员可以操作
|
||||
$canOperate = ($user['role'] === \core\base\Controller::ROLE_SUPER_ADMIN);
|
||||
// 不能操作自己
|
||||
if ($canOperate && (int)$emp['id'] === (int)$user['id']) {
|
||||
$canOperate = false;
|
||||
}
|
||||
?>
|
||||
<?php if ($canOperate): ?>
|
||||
<a href="<?php echo BASE_URL; ?>/Admin/employeePermission/<?php echo $emp['id']; ?>" class="btn btn-xs btn-success"><i class="fa fa-shield"></i> 工位权限</a>
|
||||
<a href="<?php echo BASE_URL; ?>/Admin/employeeEdit/<?php echo $emp['id']; ?>" class="btn btn-xs btn-info"><i class="fa fa-edit"></i> 编辑</a>
|
||||
<a href="<?php echo BASE_URL; ?>/Admin/employeeResetPwd/<?php echo $emp['id']; ?>" class="btn btn-xs btn-warning"><i class="fa fa-key"></i> 重置密码</a>
|
||||
<form method="post" action="<?php echo BASE_URL; ?>/Admin/employeeDelete/<?php echo $emp['id']; ?>" style="display:inline;" onsubmit="return confirm('确定删除该员工?')">
|
||||
<input type="hidden" name="csrf_token" value="<?php echo htmlspecialchars($csrfToken); ?>">
|
||||
<button type="submit" class="btn btn-xs btn-danger"><i class="fa fa-trash"></i> 删除</button>
|
||||
</form>
|
||||
<?php else: ?>
|
||||
<span class="text-muted">无操作权限</span>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
<?php else: ?>
|
||||
<tr><td colspan="7" class="text-center text-muted">暂无员工数据</td></tr>
|
||||
<?php endif; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php include APP_PATH . 'app/views/layouts/admin_footer.php'; ?>
|
||||
@@ -0,0 +1,79 @@
|
||||
<?php
|
||||
$activeMenu = 'employee';
|
||||
include APP_PATH . 'app/views/layouts/admin_header.php';
|
||||
?>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6 col-md-offset-3">
|
||||
<div class="box box-primary">
|
||||
<div class="box-header with-border">
|
||||
<h3 class="box-title"><i class="fa fa-user-plus"></i> 添加员工</h3>
|
||||
</div>
|
||||
<form method="post" action="">
|
||||
<input type="hidden" name="csrf_token" value="<?php echo htmlspecialchars($csrfToken); ?>">
|
||||
<div class="box-body">
|
||||
<?php if (!empty($error)): ?>
|
||||
<div class="alert alert-danger alert-dismissible">
|
||||
<button type="button" class="close" data-dismiss="alert">×</button>
|
||||
<?php echo htmlspecialchars($error); ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
<div class="form-group">
|
||||
<label>员工编号 <small class="text-muted">(自动生成)</small></label>
|
||||
<input type="text" class="form-control" value="<?php echo htmlspecialchars($nextEmpNo); ?>" readonly style="background:#eee;">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>姓名 <span class="text-danger">*</span></label>
|
||||
<input type="text" name="emp_name" class="form-control" placeholder="如:张三" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>密码 <span class="text-danger">*</span></label>
|
||||
<input type="password" name="password" class="form-control" placeholder="默认密码 123456" value="123456" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>权限 <span class="text-danger">*</span></label>
|
||||
<select name="role" id="roleSelect" class="form-control" onchange="toggleCategory()">
|
||||
<?php
|
||||
$roleNames = \core\base\Controller::ROLE_NAMES;
|
||||
$manageableRoles = $manageableRoles ?? [\core\base\Controller::ROLE_OPERATOR, \core\base\Controller::ROLE_ADMIN];
|
||||
foreach ($manageableRoles as $r):
|
||||
?>
|
||||
<option value="<?php echo $r; ?>"><?php echo htmlspecialchars($roleNames[$r] ?? $r); ?></option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group" id="categoryGroup">
|
||||
<label>管理类目 <span class="text-danger">*</span> <small class="text-muted">(管理员/操作员必须选择单一类目)</small></label>
|
||||
<select name="category" class="form-control">
|
||||
<?php
|
||||
$catNames = \core\base\Controller::CATEGORY_NAMES;
|
||||
foreach ($catNames as $key => $name):
|
||||
?>
|
||||
<option value="<?php echo $key; ?>"><?php echo htmlspecialchars($name); ?></option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</div>
|
||||
<script>
|
||||
function toggleCategory() {
|
||||
var role = document.getElementById('roleSelect').value;
|
||||
var catGroup = document.getElementById('categoryGroup');
|
||||
if (role === 'super_admin') {
|
||||
catGroup.style.display = 'none';
|
||||
} else {
|
||||
catGroup.style.display = 'block';
|
||||
}
|
||||
}
|
||||
// 页面加载时执行一次
|
||||
toggleCategory();
|
||||
</script>
|
||||
</div>
|
||||
<div class="box-footer">
|
||||
<button type="submit" class="btn btn-primary"><i class="fa fa-save"></i> 保存</button>
|
||||
<a href="<?php echo BASE_URL; ?>/Admin/employee" class="btn btn-default"><i class="fa fa-arrow-left"></i> 返回</a>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php include APP_PATH . 'app/views/layouts/admin_footer.php'; ?>
|
||||
@@ -0,0 +1,68 @@
|
||||
<?php
|
||||
$activeMenu = 'employee';
|
||||
include APP_PATH . 'app/views/layouts/admin_header.php';
|
||||
?>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6 col-md-offset-3">
|
||||
<div class="box box-warning">
|
||||
<div class="box-header with-border">
|
||||
<h3 class="box-title"><i class="fa fa-edit"></i> 编辑员工</h3>
|
||||
</div>
|
||||
<form method="post" action="">
|
||||
<input type="hidden" name="csrf_token" value="<?php echo htmlspecialchars($csrfToken); ?>">
|
||||
<div class="box-body">
|
||||
<div class="form-group">
|
||||
<label>员工编号</label>
|
||||
<input type="text" class="form-control" value="<?php echo htmlspecialchars($emp['emp_no']); ?>" disabled>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>姓名</label>
|
||||
<input type="text" name="emp_name" class="form-control" value="<?php echo htmlspecialchars($emp['emp_name']); ?>" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>权限 <span class="text-danger">*</span></label>
|
||||
<select name="role" id="roleSelect" class="form-control" onchange="toggleCategory()">
|
||||
<?php
|
||||
$roleNames = \core\base\Controller::ROLE_NAMES;
|
||||
$manageableRoles = $manageableRoles ?? [\core\base\Controller::ROLE_OPERATOR, \core\base\Controller::ROLE_ADMIN];
|
||||
foreach ($manageableRoles as $r):
|
||||
?>
|
||||
<option value="<?php echo $r; ?>" <?php echo ($emp['role'] ?? '') === $r ? 'selected' : ''; ?>><?php echo htmlspecialchars($roleNames[$r] ?? $r); ?></option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group" id="categoryGroup">
|
||||
<label>管理类目 <span class="text-danger">*</span></label>
|
||||
<select name="category" class="form-control">
|
||||
<?php
|
||||
$catNames = \core\base\Controller::CATEGORY_NAMES;
|
||||
foreach ($catNames as $key => $name):
|
||||
?>
|
||||
<option value="<?php echo $key; ?>" <?php echo ($emp['category'] ?? 'mes') === $key ? 'selected' : ''; ?>><?php echo htmlspecialchars($name); ?></option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</div>
|
||||
<script>
|
||||
function toggleCategory() {
|
||||
var role = document.getElementById('roleSelect').value;
|
||||
var catGroup = document.getElementById('categoryGroup');
|
||||
if (role === 'super_admin') {
|
||||
catGroup.style.display = 'none';
|
||||
} else {
|
||||
catGroup.style.display = 'block';
|
||||
}
|
||||
}
|
||||
toggleCategory();
|
||||
</script>
|
||||
</div>
|
||||
<div class="box-footer">
|
||||
<button type="submit" class="btn btn-warning"><i class="fa fa-save"></i> 更新</button>
|
||||
<a href="<?php echo BASE_URL; ?>/Admin/employee" class="btn btn-default"><i class="fa fa-arrow-left"></i> 返回</a>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php include APP_PATH . 'app/views/layouts/admin_footer.php'; ?>
|
||||
@@ -0,0 +1,129 @@
|
||||
<?php
|
||||
$activeMenu = 'employee';
|
||||
include APP_PATH . 'app/views/layouts/admin_header.php';
|
||||
?>
|
||||
|
||||
<?php if ($saved): ?>
|
||||
<div class="alert alert-success alert-dismissible">
|
||||
<button type="button" class="close" data-dismiss="alert" aria-hidden="true">×</button>
|
||||
<h4><i class="icon fa fa-check"></i> 保存成功!</h4>
|
||||
员工 <strong><?php echo htmlspecialchars($emp['emp_name']); ?></strong> 的工位权限已更新。
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-8 col-md-offset-2">
|
||||
<div class="box box-primary">
|
||||
<div class="box-header with-border">
|
||||
<h3 class="box-title">
|
||||
<i class="fa fa-lock"></i>
|
||||
工位权限设置 -
|
||||
<strong><?php echo htmlspecialchars($emp['emp_name']); ?></strong>
|
||||
<small class="text-muted">(<?php echo htmlspecialchars($emp['emp_no']); ?>)</small>
|
||||
</h3>
|
||||
<div class="box-tools">
|
||||
<?php
|
||||
$roleNames = \core\base\Controller::ROLE_NAMES;
|
||||
$isAdminRole = ($emp['role'] === \core\base\Controller::ROLE_SUPER_ADMIN || $emp['role'] === \core\base\Controller::ROLE_ADMIN);
|
||||
$labelClass = $emp['role'] === \core\base\Controller::ROLE_SUPER_ADMIN ? 'danger' : ($emp['role'] === \core\base\Controller::ROLE_ADMIN ? 'warning' : 'info');
|
||||
?>
|
||||
<span class="label label-<?php echo $labelClass; ?>">
|
||||
<?php echo htmlspecialchars($roleNames[$emp['role']] ?? $emp['role']); ?>
|
||||
<?php echo $isAdminRole ? '(默认拥有全部权限)' : ''; ?>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<form method="post" action="">
|
||||
<input type="hidden" name="csrf_token" value="<?php echo htmlspecialchars($csrfToken); ?>">
|
||||
<div class="box-body">
|
||||
<?php if ($isAdminRole): ?>
|
||||
<div class="callout callout-info">
|
||||
<h4><i class="icon fa fa-info"></i> 提示</h4>
|
||||
<p>超级管理员和管理员默认拥有所有工位的访问权限,无需单独设置。</p>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<div class="callout callout-warning">
|
||||
<h4><i class="icon fa fa-exclamation-triangle"></i> 权限说明</h4>
|
||||
<ul style="margin-bottom:0;">
|
||||
<li><strong>勾选</strong>:允许该员工访问对应工位</li>
|
||||
<li><strong>不勾选</strong>:该员工无法进入对应工位(前台左侧菜单不显示,直接访问会提示无权限)</li>
|
||||
<li>如果员工没有任何工位被勾选,则默认允许访问所有<strong>已启用</strong>工位</li>
|
||||
<li><span class="label label-danger">禁用</span> 状态的工位<strong>所有人都无法访问</strong>,即使勾选了也无效</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<table class="table table-hover table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width:60px;text-align:center;">
|
||||
<input type="checkbox" id="checkAll" onclick="toggleAll(this)">
|
||||
</th>
|
||||
<th style="width:60px;">#</th>
|
||||
<th>工位编码</th>
|
||||
<th>工位名称</th>
|
||||
<th>工位类型</th>
|
||||
<th style="width:80px;">状态</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php if (!empty($stations)): ?>
|
||||
<?php foreach ($stations as $i => $station):
|
||||
$stype = $station['station_type'];
|
||||
$isDisabled = $station['status'] == 0;
|
||||
$isChecked = isset($currentPermissions[$stype])
|
||||
? ($currentPermissions[$stype] === 1)
|
||||
: true; // 未配置时默认允许
|
||||
?>
|
||||
<tr class="<?php echo $isDisabled ? 'text-muted' : ''; ?>" style="<?php echo $isDisabled ? 'background:#f5f5f5;' : ''; ?>">
|
||||
<td style="text-align:center;">
|
||||
<input type="checkbox" name="allow[]" value="<?php echo htmlspecialchars($stype); ?>"
|
||||
class="station-check" <?php echo $isChecked ? 'checked' : ''; ?>>
|
||||
</td>
|
||||
<td><?php echo $i + 1; ?></td>
|
||||
<td><code><?php echo htmlspecialchars($station['station_code']); ?></code></td>
|
||||
<td>
|
||||
<?php echo htmlspecialchars($station['station_name']); ?>
|
||||
<?php if ($isDisabled): ?>
|
||||
<small class="text-danger"><i class="fa fa-ban"></i> 已禁用,权限无效</small>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
<td><span class="label label-default"><?php echo htmlspecialchars($stype); ?></span></td>
|
||||
<td>
|
||||
<?php if ($station['status'] == 1): ?>
|
||||
<span class="label label-success">启用</span>
|
||||
<?php else: ?>
|
||||
<span class="label label-danger">禁用</span>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
<?php else: ?>
|
||||
<tr><td colspan="6" class="text-center text-muted">暂无工位数据,请先在「工位管理」中添加</td></tr>
|
||||
<?php endif; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="box-footer">
|
||||
<button type="submit" class="btn btn-primary btn-lg">
|
||||
<i class="fa fa-save"></i> 保存权限设置
|
||||
</button>
|
||||
<a href="<?php echo BASE_URL; ?>/Admin/employee" class="btn btn-default btn-lg">
|
||||
<i class="fa fa-arrow-left"></i> 返回员工列表
|
||||
</a>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function toggleAll(source) {
|
||||
var checkboxes = document.querySelectorAll('.station-check');
|
||||
for (var i = 0; i < checkboxes.length; i++) {
|
||||
checkboxes[i].checked = source.checked;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<?php include APP_PATH . 'app/views/layouts/admin_footer.php'; ?>
|
||||
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
$activeMenu = 'employee';
|
||||
include APP_PATH . 'app/views/layouts/admin_header.php';
|
||||
?>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6 col-md-offset-3">
|
||||
<div class="box box-warning">
|
||||
<div class="box-header with-border">
|
||||
<h3 class="box-title"><i class="fa fa-key"></i> 重置密码</h3>
|
||||
</div>
|
||||
<form method="post" action="">
|
||||
<input type="hidden" name="csrf_token" value="<?php echo htmlspecialchars($csrfToken); ?>">
|
||||
<div class="box-body">
|
||||
<div class="form-group">
|
||||
<label>员工编号</label>
|
||||
<input type="text" class="form-control" value="<?php echo htmlspecialchars($emp['emp_no']); ?>" disabled>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>姓名</label>
|
||||
<input type="text" class="form-control" value="<?php echo htmlspecialchars($emp['emp_name']); ?>" disabled>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>新密码</label>
|
||||
<input type="password" name="new_password" class="form-control" placeholder="请输入新密码" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>确认密码</label>
|
||||
<input type="password" name="confirm_password" class="form-control" placeholder="请再次输入新密码" required>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-footer">
|
||||
<button type="submit" class="btn btn-warning"><i class="fa fa-key"></i> 重置密码</button>
|
||||
<a href="<?php echo BASE_URL; ?>/Admin/employee" class="btn btn-default"><i class="fa fa-arrow-left"></i> 返回</a>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php include APP_PATH . 'app/views/layouts/admin_footer.php'; ?>
|
||||
@@ -0,0 +1,77 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title><?php echo $title; ?> - MES</title>
|
||||
<link rel="stylesheet" href="<?php echo BASE_URL; ?>/static/css/bootstrap.min.css">
|
||||
<link rel="stylesheet" href="<?php echo BASE_URL; ?>/static/css/font-awesome.min.css">
|
||||
<link rel="stylesheet" href="<?php echo BASE_URL; ?>/static/css/AdminLTE.min.css">
|
||||
<link rel="stylesheet" href="<?php echo BASE_URL; ?>/static/css/_all-skins.min.css">
|
||||
</head>
|
||||
<body class="hold-transition skin-blue sidebar-mini">
|
||||
<div class="wrapper">
|
||||
<?php include 'header.php'; ?>
|
||||
|
||||
<div class="content-wrapper">
|
||||
<section class="content-header">
|
||||
<h1><?php echo $title; ?></h1>
|
||||
</section>
|
||||
|
||||
<section class="content">
|
||||
<div class="box box-primary">
|
||||
<div class="box-header with-border">
|
||||
<h3 class="box-title">员工列表</h3>
|
||||
<div class="box-tools">
|
||||
<a href="<?php echo BASE_URL; ?>/Admin/employeeAdd" class="btn btn-primary btn-sm">
|
||||
<i class="fa fa-plus"></i> 添加员工
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-body">
|
||||
<table class="table table-bordered table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>序号</th>
|
||||
<th>员工编号</th>
|
||||
<th>姓名</th>
|
||||
<th>权限</th>
|
||||
<th>操作人</th>
|
||||
<th>操作</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ($employees as $i => $emp): ?>
|
||||
<tr>
|
||||
<td><?php echo $emp['id']; ?></td>
|
||||
<td><?php echo htmlspecialchars($emp['emp_no']); ?></td>
|
||||
<td><?php echo htmlspecialchars($emp['emp_name']); ?></td>
|
||||
<td>
|
||||
<span class="label label-<?php echo $emp['role'] === 'admin' ? 'danger' : 'info'; ?>">
|
||||
<?php echo $emp['role'] === 'admin' ? '管理员' : '操作员'; ?>
|
||||
</span>
|
||||
</td>
|
||||
<td><?php echo htmlspecialchars($emp['created_by']); ?></td>
|
||||
<td>
|
||||
<a href="<?php echo BASE_URL; ?>/Admin/employeeEdit/<?php echo $emp['id']; ?>" class="btn btn-xs btn-info">编辑</a>
|
||||
<a href="<?php echo BASE_URL; ?>/Admin/employeeResetPwd/<?php echo $emp['id']; ?>" class="btn btn-xs btn-warning">重置密码</a>
|
||||
<a href="<?php echo BASE_URL; ?>/Admin/employeeDelete/<?php echo $emp['id']; ?>" class="btn btn-xs btn-danger confirm-link" data-confirm="确定删除?">删除</a>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<?php include 'footer.php'; ?>
|
||||
</div>
|
||||
|
||||
<script src="../static/js/jquery-3.7.1.min.js"></script>
|
||||
<script src="../static/js/bootstrap.min.js"></script>
|
||||
<script src="../static/js/app.min.js"></script>
|
||||
<?php // v3-P2 CSP:全局事件委托(替代内联 onclick 等) ?>
|
||||
<script src="<?php echo BASE_URL; ?>/static/js/csp_global.js" nonce="<?php echo csp_nonce(); ?>"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,8 @@
|
||||
<footer class="main-footer">
|
||||
<div class="pull-right hidden-xs"><b>Version</b> 1.0.0</div>
|
||||
<strong>Copyright © 2024 MES.</strong> All rights reserved.
|
||||
</footer>
|
||||
|
||||
<script src="<?php echo BASE_URL; ?>/static/js/jquery-3.7.1.min.js"></script>
|
||||
<script src="<?php echo BASE_URL; ?>/static/js/bootstrap.min.js"></script>
|
||||
<script src="<?php echo BASE_URL; ?>/static/js/app.min.js"></script>
|
||||
@@ -0,0 +1,39 @@
|
||||
<header class="main-header">
|
||||
<a href="<?php echo BASE_URL; ?>/Admin/index" class="logo">
|
||||
<span class="logo-mini"><b>MES</b></span>
|
||||
<span class="logo-lg"><b>MES</b> 后台管理</span>
|
||||
</a>
|
||||
<nav class="navbar navbar-static-top">
|
||||
<a href="#" class="sidebar-toggle" data-toggle="offcanvas" role="button">
|
||||
<span class="sr-only">切换导航</span>
|
||||
</a>
|
||||
<div class="navbar-custom-menu">
|
||||
<ul class="nav navbar-nav">
|
||||
<li class="dropdown user user-menu">
|
||||
<a href="#" class="dropdown-toggle" data-toggle="dropdown">
|
||||
<span class="hidden-xs"><?php echo htmlspecialchars($user['emp_name']); ?></span>
|
||||
</a>
|
||||
<ul class="dropdown-menu">
|
||||
<li><a href="<?php echo BASE_URL; ?>/Login/logout"><i class="fa fa-power-off"></i> 退出登录</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</nav>
|
||||
</header>
|
||||
|
||||
<aside class="main-sidebar">
|
||||
<section class="sidebar">
|
||||
<ul class="sidebar-menu">
|
||||
<li class="header">导航菜单</li>
|
||||
<li><a href="<?php echo BASE_URL; ?>/Admin/index"><i class="fa fa-dashboard"></i> <span>控制台</span></a></li>
|
||||
<li><a href="<?php echo BASE_URL; ?>/Admin/employee"><i class="fa fa-users"></i> <span>员工管理</span></a></li>
|
||||
<li><a href="<?php echo BASE_URL; ?>/Admin/customer"><i class="fa fa-building"></i> <span>客户管理</span></a></li>
|
||||
<li><a href="<?php echo BASE_URL; ?>/Admin/productModel"><i class="fa fa-cube"></i> <span>产品型号</span></a></li>
|
||||
<li><a href="<?php echo BASE_URL; ?>/Admin/workstation"><i class="fa fa-th"></i> <span>工位管理</span></a></li>
|
||||
<li><a href="<?php echo BASE_URL; ?>/Admin/config"><i class="fa fa-cog"></i> <span>系统配置</span></a></li>
|
||||
<li><a href="<?php echo BASE_URL; ?>/Admin/import"><i class="fa fa-upload"></i> <span>批量导入</span></a></li>
|
||||
<li><a href="<?php echo BASE_URL; ?>/Front/index"><i class="fa fa-desktop"></i> <span>前台工位</span></a></li>
|
||||
</ul>
|
||||
</section>
|
||||
</aside>
|
||||
@@ -0,0 +1,81 @@
|
||||
<?php
|
||||
$activeMenu = 'import';
|
||||
include APP_PATH . 'app/views/layouts/admin_header.php';
|
||||
?>
|
||||
|
||||
<?php if (!empty($msg)): ?>
|
||||
<div class="alert alert-<?php echo strpos($msg, '成功') !== false ? 'success' : 'danger'; ?> alert-dismissible">
|
||||
<button type="button" class="close" data-dismiss="alert">×</button>
|
||||
<i class="fa fa-info-circle"></i> <?php echo htmlspecialchars($msg); ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="box box-primary">
|
||||
<div class="box-header with-border">
|
||||
<h3 class="box-title"><i class="fa fa-upload"></i> 批量导入</h3>
|
||||
</div>
|
||||
<form method="post" action="" enctype="multipart/form-data">
|
||||
<input type="hidden" name="csrf_token" value="<?php echo htmlspecialchars($csrfToken); ?>">
|
||||
<div class="box-body">
|
||||
<div class="form-group">
|
||||
<label>数据表类型</label>
|
||||
<select name="table_type" class="form-control" required>
|
||||
<option value="">请选择</option>
|
||||
<option value="employee">员工数据</option>
|
||||
<option value="customer">客户数据</option>
|
||||
<option value="product_model">产品型号</option>
|
||||
<option value="inbound">半成品入库工位数据</option>
|
||||
<option value="pcb_test">PCB 测试数据</option>
|
||||
<option value="battery">电池状态数据</option>
|
||||
<option value="assembly">产品组装数据</option>
|
||||
<option value="finished_test">成品测试数据</option>
|
||||
<option value="warehouse">成品入库数据</option>
|
||||
<option value="delivery">出货登记数据</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>选择文件 (CSV)</label>
|
||||
<input type="file" name="import_file" class="form-control" accept=".csv" required>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-footer">
|
||||
<button type="submit" class="btn btn-primary"><i class="fa fa-upload"></i> 开始导入</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="box box-info">
|
||||
<div class="box-header with-border">
|
||||
<h3 class="box-title"><i class="fa fa-download"></i> 导入模板下载</h3>
|
||||
</div>
|
||||
<div class="box-body">
|
||||
<div class="list-group">
|
||||
<?php
|
||||
$templates = [
|
||||
'employee' => '员工数据模板',
|
||||
'customer' => '客户数据模板',
|
||||
'product_model' => '产品型号模板',
|
||||
'inbound' => '入库工位数据模板',
|
||||
'pcb_test' => 'PCB 测试数据模板',
|
||||
'battery' => '电池状态数据模板',
|
||||
'assembly' => '产品组装数据模板',
|
||||
'finished_test' => '成品测试数据模板',
|
||||
'warehouse' => '入库数据模板',
|
||||
'delivery' => '出货登记数据模板',
|
||||
];
|
||||
foreach ($templates as $key => $label):
|
||||
?>
|
||||
<a href="<?php echo BASE_URL; ?>/Admin/importTemplate/<?php echo $key; ?>" class="list-group-item">
|
||||
<i class="fa fa-download text-primary"></i> <?php echo $label; ?> (CSV)
|
||||
</a>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php include APP_PATH . 'app/views/layouts/admin_footer.php'; ?>
|
||||
@@ -0,0 +1,80 @@
|
||||
<?php
|
||||
$activeMenu = 'index';
|
||||
include APP_PATH . 'app/views/layouts/admin_header.php';
|
||||
?>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-3 col-xs-6">
|
||||
<div class="small-box bg-aqua">
|
||||
<div class="inner">
|
||||
<h3><?php echo isset($employeeCount) ? $employeeCount : 0; ?></h3>
|
||||
<p>员工总数</p>
|
||||
</div>
|
||||
<div class="icon"><i class="fa fa-users"></i></div>
|
||||
<a href="<?php echo BASE_URL; ?>/Admin/employee" class="small-box-footer">进入管理 <i class="fa fa-arrow-circle-right"></i></a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-3 col-xs-6">
|
||||
<div class="small-box bg-green">
|
||||
<div class="inner">
|
||||
<h3><?php echo isset($customerCount) ? $customerCount : 0; ?></h3>
|
||||
<p>客户数量</p>
|
||||
</div>
|
||||
<div class="icon"><i class="fa fa-building"></i></div>
|
||||
<a href="<?php echo BASE_URL; ?>/Admin/customer" class="small-box-footer">进入管理 <i class="fa fa-arrow-circle-right"></i></a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-3 col-xs-6">
|
||||
<div class="small-box bg-yellow">
|
||||
<div class="inner">
|
||||
<h3><?php echo isset($modelCount) ? $modelCount : 0; ?></h3>
|
||||
<p>产品型号</p>
|
||||
</div>
|
||||
<div class="icon"><i class="fa fa-cube"></i></div>
|
||||
<a href="<?php echo BASE_URL; ?>/Admin/productModel" class="small-box-footer">进入管理 <i class="fa fa-arrow-circle-right"></i></a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-3 col-xs-6">
|
||||
<div class="small-box bg-red">
|
||||
<div class="inner">
|
||||
<h3><?php echo isset($stationCount) ? $stationCount : 0; ?></h3>
|
||||
<p>工位数量</p>
|
||||
</div>
|
||||
<div class="icon"><i class="fa fa-th"></i></div>
|
||||
<a href="<?php echo BASE_URL; ?>/Admin/workstation" class="small-box-footer">进入管理 <i class="fa fa-arrow-circle-right"></i></a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="box box-primary">
|
||||
<div class="box-header with-border">
|
||||
<h3 class="box-title"><i class="fa fa-info-circle"></i> 系统信息</h3>
|
||||
</div>
|
||||
<div class="box-body">
|
||||
<dl class="dl-horizontal">
|
||||
<dt>公司名称</dt><dd><?php echo isset($sysConfig) ? htmlspecialchars($sysConfig['company_name']) : '-'; ?></dd>
|
||||
<dt>系统名称</dt><dd><?php echo isset($sysConfig) ? htmlspecialchars($sysConfig['mes_name']) : '-'; ?></dd>
|
||||
<dt>当前用户</dt><dd><?php echo htmlspecialchars($user['emp_name']); ?></dd>
|
||||
<dt>用户角色</dt><dd><span class="label label-info"><?php echo $user['role'] === 'admin' ? '管理员' : '操作员'; ?></span></dd>
|
||||
</dl>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="box box-success">
|
||||
<div class="box-header with-border">
|
||||
<h3 class="box-title"><i class="fa fa-rocket"></i> 快捷操作</h3>
|
||||
</div>
|
||||
<div class="box-body">
|
||||
<a href="<?php echo BASE_URL; ?>/Admin/employeeAdd" class="btn btn-primary"><i class="fa fa-user-plus"></i> 添加员工</a>
|
||||
<a href="<?php echo BASE_URL; ?>/Admin/workstationAdd" class="btn btn-success"><i class="fa fa-plus-square"></i> 添加工位</a>
|
||||
<a href="<?php echo BASE_URL; ?>/Admin/import" class="btn btn-warning"><i class="fa fa-upload"></i> 批量导入</a>
|
||||
<a href="<?php echo BASE_URL; ?>/Front/index" class="btn btn-info"><i class="fa fa-desktop"></i> 进入前台</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php include APP_PATH . 'app/views/layouts/admin_footer.php'; ?>
|
||||
@@ -0,0 +1,158 @@
|
||||
<?php
|
||||
$activeMenu = 'productModel';
|
||||
include APP_PATH . 'app/views/layouts/admin_header.php';
|
||||
|
||||
// 构建类型颜色和列定义(统一从 typeConfigs 或 getDefaultConfigs 获取)
|
||||
$typeColors = [];
|
||||
$typeColumns = [];
|
||||
|
||||
// 统一数据源:typeConfigs 为空时回退到默认配置
|
||||
$allConfigs = !empty($typeConfigs) ? $typeConfigs : \app\models\ProductTypeConfig::getDefaultConfigs();
|
||||
|
||||
foreach ($allConfigs as $cfg) {
|
||||
$typeColors[$cfg['type_name']] = $cfg['color'] ?? 'default';
|
||||
$cols = [];
|
||||
if (!empty($cfg['display_columns'])) {
|
||||
$decoded = json_decode($cfg['display_columns'], true);
|
||||
if (is_array($decoded)) {
|
||||
$cols = $decoded;
|
||||
}
|
||||
}
|
||||
// 确保有序号列在最前
|
||||
$hasId = false;
|
||||
foreach ($cols as $c) {
|
||||
if (($c['key'] ?? '') === 'id') { $hasId = true; break; }
|
||||
}
|
||||
if (!$hasId) {
|
||||
array_unshift($cols, ['key'=>'id','label'=>'序号','width'=>'60px']);
|
||||
}
|
||||
// 补充 width 字段
|
||||
foreach ($cols as &$c) {
|
||||
if (!isset($c['width'])) $c['width'] = '';
|
||||
}
|
||||
unset($c);
|
||||
$typeColumns[$cfg['type_name']] = $cols;
|
||||
}
|
||||
|
||||
// 通用 fallback 列(所有类型都可用的字段集合)
|
||||
$defaultColumns = [
|
||||
['key'=>'id','label'=>'序号','width'=>'60px'],
|
||||
['key'=>'type','label'=>'类型','width'=>'80px'],
|
||||
['key'=>'model_code','label'=>'产品型号','width'=>''],
|
||||
['key'=>'model_desc','label'=>'产品说明','width'=>''],
|
||||
];
|
||||
|
||||
function renderModelTableRow($model, $columns, $typeName, $idx, $csrfToken) {
|
||||
$html = '<tr>';
|
||||
foreach ($columns as $col) {
|
||||
$key = $col['key'];
|
||||
$val = '';
|
||||
if ($key === 'id') {
|
||||
$val = $idx;
|
||||
} elseif ($key === 'type') {
|
||||
$val = htmlspecialchars($typeName);
|
||||
} elseif (isset($model[$key])) {
|
||||
$val = htmlspecialchars($model[$key]);
|
||||
} else {
|
||||
$val = '-';
|
||||
}
|
||||
$html .= '<td>' . $val . '</td>';
|
||||
}
|
||||
$html .= '<td style="width:150px">
|
||||
<a href="' . BASE_URL . '/Admin/productModelEdit/' . $model['id'] . '" class="btn btn-xs btn-info"><i class="fa fa-edit"></i> 编辑</a>
|
||||
<form method="POST" action="' . BASE_URL . '/Admin/productModelDelete/' . $model['id'] . '" style="display:inline" onsubmit="return confirm(\'确定删除该型号?\')">
|
||||
<input type="hidden" name="csrf_token" value="' . ($csrfToken ?? '') . '">
|
||||
<button type="submit" class="btn btn-xs btn-danger"><i class="fa fa-trash"></i> 删除</button>
|
||||
</form>
|
||||
</td>';
|
||||
$html .= '</tr>';
|
||||
return $html;
|
||||
}
|
||||
?>
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<div class="box">
|
||||
<div class="box-header with-border">
|
||||
<h3 class="box-title"><i class="fa fa-list"></i> 产品型号列表</h3>
|
||||
<div class="box-tools" style="display:flex; align-items:center; gap:8px;">
|
||||
<span class="column-adjuster" title="调整类型卡片列数" style="display:inline-flex; gap:4px;">
|
||||
<button type="button" class="btn btn-default btn-xs col-btn" data-cols="1" title="1列"><i class="fa fa-align-justify"></i></button>
|
||||
<button type="button" class="btn btn-default btn-xs col-btn active" data-cols="2" title="2列"><i class="fa fa-columns"></i> 2</button>
|
||||
<button type="button" class="btn btn-default btn-xs col-btn" data-cols="3" title="3列"><i class="fa fa-columns"></i> 3</button>
|
||||
<button type="button" class="btn btn-default btn-xs col-btn" data-cols="auto" title="自适应"><i class="fa fa-arrows-h"></i> 自动</button>
|
||||
</span>
|
||||
<a href="<?php echo BASE_URL; ?>/Admin/productTypeConfig" class="btn btn-warning btn-sm"><i class="fa fa-cogs"></i> 类型配置</a>
|
||||
<a href="<?php echo BASE_URL; ?>/Admin/productModelAdd" class="btn btn-primary btn-sm"><i class="fa fa-plus"></i> 添加产品型号</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php if (!empty($groupedModels)): ?>
|
||||
<div class="type-grid" id="typeGrid">
|
||||
<?php foreach ($groupedModels as $typeName => $models): ?>
|
||||
<div class="box box-<?php echo $typeColors[$typeName] ?? 'default'; ?> type-grid-item">
|
||||
<div class="box-header with-border">
|
||||
<h3 class="box-title">
|
||||
<i class="fa fa-tags"></i>
|
||||
<?php echo htmlspecialchars($typeName); ?>
|
||||
<span class="badge bg-<?php echo $typeColors[$typeName] ?? 'default'; ?>"><?php echo count($models); ?></span>
|
||||
</h3>
|
||||
</div>
|
||||
<div class="box-body table-responsive no-padding">
|
||||
<table class="table table-hover table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<?php $cols = $typeColumns[$typeName] ?? $defaultColumns; ?>
|
||||
<?php foreach ($cols as $col): ?>
|
||||
<th<?php if ($col['width']) echo ' style="width:' . $col['width'] . '"'; ?>><?php echo $col['label']; ?></th>
|
||||
<?php endforeach; ?>
|
||||
<th style="width:150px">操作</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php $idx = 0; ?>
|
||||
<?php foreach ($models as $model): ?>
|
||||
<?php echo renderModelTableRow($model, $cols, $typeName, ++$idx, $csrfToken); ?>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
<?php else: ?>
|
||||
<div class="box">
|
||||
<div class="box-body text-center text-muted" style="padding:40px;">
|
||||
<i class="fa fa-inbox fa-3x"></i>
|
||||
<p style="margin-top:10px;">暂无产品型号数据</p>
|
||||
<a href="<?php echo BASE_URL; ?>/Admin/productModelAdd" class="btn btn-primary"><i class="fa fa-plus"></i> 添加产品型号</a>
|
||||
</div>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// ========== 类型卡片列数调节 ==========
|
||||
$(function() {
|
||||
var $grid = $('#typeGrid');
|
||||
if (!$grid.length) return;
|
||||
var COL_KEY = 'admin_type_grid_cols';
|
||||
|
||||
function applyColumns(cols) {
|
||||
$grid.attr('data-columns', cols);
|
||||
$('.column-adjuster .col-btn').removeClass('active');
|
||||
$('.column-adjuster .col-btn[data-cols="' + cols + '"]').addClass('active');
|
||||
try { localStorage.setItem(COL_KEY, cols); } catch(e) {}
|
||||
}
|
||||
|
||||
var savedCols = localStorage.getItem(COL_KEY) || '2';
|
||||
applyColumns(savedCols);
|
||||
|
||||
$('.column-adjuster .col-btn').click(function() {
|
||||
applyColumns($(this).data('cols'));
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
<?php include APP_PATH . 'app/views/layouts/admin_footer.php'; ?>
|
||||
@@ -0,0 +1,151 @@
|
||||
<?php
|
||||
$activeMenu = 'productModel';
|
||||
include APP_PATH . 'app/views/layouts/admin_header.php';
|
||||
?>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6 col-md-offset-3">
|
||||
<div class="box box-primary">
|
||||
<div class="box-header with-border">
|
||||
<h3 class="box-title"><i class="fa fa-plus"></i> 添加产品型号</h3>
|
||||
</div>
|
||||
<form method="post" action="">
|
||||
<input type="hidden" name="csrf_token" value="<?php echo htmlspecialchars($csrfToken); ?>">
|
||||
<div class="box-body">
|
||||
<?php if (!empty($error)): ?>
|
||||
<div class="alert alert-danger alert-dismissible">
|
||||
<button type="button" class="close" data-dismiss="alert" aria-hidden="true">×</button>
|
||||
<i class="icon fa fa-ban"></i> <?php echo htmlspecialchars($error); ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
<div class="form-group">
|
||||
<label>类型 <span class="text-danger">*</span></label>
|
||||
<select name="type" id="typeSelect" class="form-control" required>
|
||||
<option value="">-- 请选择类型 --</option>
|
||||
<?php if (!empty($typeConfigs)): ?>
|
||||
<?php foreach ($typeConfigs as $cfg): ?>
|
||||
<option value="<?php echo htmlspecialchars($cfg['type_name']); ?>"><?php echo htmlspecialchars($cfg['type_name']); ?></option>
|
||||
<?php endforeach; ?>
|
||||
<?php else: ?>
|
||||
<option value="成品">成品</option>
|
||||
<option value="电池">电池</option>
|
||||
<option value="外壳">外壳</option>
|
||||
<option value="外壳颜色">外壳颜色</option>
|
||||
<option value="包装">包装</option>
|
||||
<option value="半成品">半成品</option>
|
||||
<option value="配件">配件</option>
|
||||
<?php endif; ?>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group" id="productTypeGroup">
|
||||
<label id="productTypeLabel">产品类型 <span class="text-danger">*</span></label>
|
||||
<input type="text" name="product_type" id="productTypeInput" class="form-control" placeholder="如:电池、外壳,成品" required>
|
||||
</div>
|
||||
<div class="form-group" id="modelCodeGroup">
|
||||
<label>产品型号 <span class="text-danger">*</span></label>
|
||||
<input type="text" name="model_code" id="modelCodeInput" class="form-control" placeholder="如:T8" required>
|
||||
</div>
|
||||
<div class="form-group" id="colorGroup">
|
||||
<label>颜色</label>
|
||||
<input type="text" name="color" id="colorInput" class="form-control" placeholder="如:珍珠白、深空灰">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>产品说明</label>
|
||||
<textarea name="description" class="form-control" rows="3" placeholder="产品描述信息"></textarea>
|
||||
</div>
|
||||
<script>
|
||||
(function(){
|
||||
var $typeSel = $('#typeSelect');
|
||||
var $ptGroup = $('#productTypeGroup');
|
||||
var $ptLabel = $('#productTypeLabel');
|
||||
var $ptInput = $('#productTypeInput');
|
||||
var $modelCodeGroup = $('#modelCodeGroup');
|
||||
var $modelCodeInput = $('#modelCodeInput');
|
||||
var $colorGroup = $('#colorGroup');
|
||||
var $colorInput = $('#colorInput');
|
||||
|
||||
// 类型配置数据(从服务端传入)
|
||||
var typeConfigs = <?php echo json_encode($typeConfigs ?? [], JSON_UNESCAPED_UNICODE); ?>;
|
||||
|
||||
// 构建类型名→配置映射
|
||||
var typeConfigMap = {};
|
||||
if (Array.isArray(typeConfigs)) {
|
||||
typeConfigs.forEach(function(cfg) {
|
||||
typeConfigMap[cfg.type_name] = cfg;
|
||||
});
|
||||
}
|
||||
|
||||
function updateProductTypeField() {
|
||||
var t = $typeSel.val();
|
||||
var cfg = typeConfigMap[t] || {};
|
||||
|
||||
// 产品类型输入框(三态:hidden/readonly/editable)
|
||||
var ptState = cfg.product_type_state || 'editable';
|
||||
var field = cfg.product_type_field || 'product_type';
|
||||
// 当 product_type_field === 'color' 且颜色框可见时,
|
||||
// 隐藏产品类型输入框,避免与颜色输入框 name="color" 冲突
|
||||
var cState = cfg.color_state || 'editable';
|
||||
var hidePtForColor = (field === 'color' && cState !== 'hidden');
|
||||
if (ptState === 'hidden' || hidePtForColor) {
|
||||
$ptGroup.hide();
|
||||
$ptInput.removeAttr('required');
|
||||
} else {
|
||||
$ptGroup.show();
|
||||
var label = cfg.product_type_label || '产品类型';
|
||||
var placeholder = cfg.product_type_placeholder || '';
|
||||
if (!placeholder) {
|
||||
placeholder = '如:' + label;
|
||||
}
|
||||
$ptLabel.html(label + (ptState === 'editable' ? ' <span class="text-danger">*</span>' : ''));
|
||||
$ptInput.attr({name: field, placeholder: placeholder});
|
||||
if (ptState === 'readonly') {
|
||||
$ptInput.prop({readonly: true, required: false});
|
||||
} else {
|
||||
$ptInput.prop({readonly: false, required: true});
|
||||
}
|
||||
}
|
||||
|
||||
// 产品型号输入框(三态:hidden/readonly/editable)
|
||||
var mcState = cfg.model_code_state || 'editable';
|
||||
if (mcState === 'hidden') {
|
||||
$modelCodeGroup.hide();
|
||||
$modelCodeInput.removeAttr('required').val('');
|
||||
} else {
|
||||
$modelCodeGroup.show();
|
||||
if (mcState === 'readonly') {
|
||||
$modelCodeInput.prop({readonly: true, required: false});
|
||||
} else {
|
||||
$modelCodeInput.prop({readonly: false, required: true});
|
||||
}
|
||||
}
|
||||
|
||||
// 颜色输入框(三态:hidden/readonly/editable)
|
||||
var cState = cfg.color_state || 'editable';
|
||||
if (cState === 'hidden') {
|
||||
$colorGroup.hide();
|
||||
$colorInput.val('');
|
||||
} else {
|
||||
$colorGroup.show();
|
||||
if (cState === 'readonly') {
|
||||
$colorInput.prop('readonly', true);
|
||||
} else {
|
||||
$colorInput.prop('readonly', false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$typeSel.on('change', updateProductTypeField);
|
||||
updateProductTypeField();
|
||||
})();
|
||||
</script>
|
||||
</div>
|
||||
<div class="box-footer">
|
||||
<button type="submit" class="btn btn-primary"><i class="fa fa-save"></i> 保存</button>
|
||||
<a href="<?php echo BASE_URL; ?>/Admin/productModel" class="btn btn-default"><i class="fa fa-arrow-left"></i> 返回</a>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php include APP_PATH . 'app/views/layouts/admin_footer.php'; ?>
|
||||
@@ -0,0 +1,162 @@
|
||||
<?php
|
||||
$activeMenu = 'productModel';
|
||||
include APP_PATH . 'app/views/layouts/admin_header.php';
|
||||
?>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6 col-md-offset-3">
|
||||
<div class="box box-warning">
|
||||
<div class="box-header with-border">
|
||||
<h3 class="box-title"><i class="fa fa-edit"></i> 编辑产品型号</h3>
|
||||
</div>
|
||||
<form method="post" action="">
|
||||
<input type="hidden" name="csrf_token" value="<?php echo htmlspecialchars($csrfToken); ?>">
|
||||
<div class="box-body">
|
||||
<?php if (!empty($error)): ?>
|
||||
<div class="alert alert-danger alert-dismissible">
|
||||
<button type="button" class="close" data-dismiss="alert" aria-hidden="true">×</button>
|
||||
<i class="icon fa fa-ban"></i> <?php echo htmlspecialchars($error); ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
<div class="form-group">
|
||||
<label>类型 <span class="text-danger">*</span></label>
|
||||
<select name="type" id="typeSelect" class="form-control" required>
|
||||
<option value="">-- 请选择类型 --</option>
|
||||
<?php if (!empty($typeConfigs)): ?>
|
||||
<?php foreach ($typeConfigs as $cfg): ?>
|
||||
<option value="<?php echo htmlspecialchars($cfg['type_name']); ?>" <?php echo (($model['type'] ?? '') === $cfg['type_name']) ? 'selected' : ''; ?>>
|
||||
<?php echo htmlspecialchars($cfg['type_name']); ?>
|
||||
</option>
|
||||
<?php endforeach; ?>
|
||||
<?php else: ?>
|
||||
<?php foreach (['成品', '电池', '外壳', '外壳颜色', '包装', '半成品', '配件'] as $t): ?>
|
||||
<option value="<?php echo $t; ?>" <?php echo (($model['type'] ?? '') === $t) ? 'selected' : ''; ?>>
|
||||
<?php echo $t; ?>
|
||||
</option>
|
||||
<?php endforeach; ?>
|
||||
<?php endif; ?>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group" id="productTypeGroup">
|
||||
<label id="productTypeLabel">产品类型 <span class="text-danger">*</span></label>
|
||||
<input type="text" name="product_type" id="productTypeInput" class="form-control" value="<?php echo htmlspecialchars($model['product_type'] ?? ''); ?>" placeholder="如:电池、外壳,成品" required>
|
||||
</div>
|
||||
<div class="form-group" id="modelCodeGroup">
|
||||
<label>产品型号 <span class="text-danger">*</span></label>
|
||||
<input type="text" name="model_code" id="modelCodeInput" class="form-control" value="<?php echo htmlspecialchars($model['model_code']); ?>" required>
|
||||
</div>
|
||||
<div class="form-group" id="colorGroup">
|
||||
<label>颜色</label>
|
||||
<input type="text" name="color" id="colorInput" class="form-control" value="<?php echo htmlspecialchars($model['color'] ?? ''); ?>" placeholder="如:珍珠白、深空灰">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>产品说明</label>
|
||||
<textarea name="description" class="form-control" rows="3"><?php echo htmlspecialchars($model['model_desc'] ?? ''); ?></textarea>
|
||||
</div>
|
||||
<script>
|
||||
(function(){
|
||||
var $typeSel = $('#typeSelect');
|
||||
var $ptGroup = $('#productTypeGroup');
|
||||
var $ptLabel = $('#productTypeLabel');
|
||||
var $ptInput = $('#productTypeInput');
|
||||
var $modelCodeGroup = $('#modelCodeGroup');
|
||||
var $modelCodeInput = $('#modelCodeInput');
|
||||
var $colorGroup = $('#colorGroup');
|
||||
var $colorInput = $('#colorInput');
|
||||
|
||||
// 编辑时原始值
|
||||
var editProductType = <?php echo json_encode($model['product_type'] ?? '', JSON_UNESCAPED_UNICODE); ?>;
|
||||
var editModelCode = <?php echo json_encode($model['model_code'] ?? '', JSON_UNESCAPED_UNICODE); ?>;
|
||||
var editColor = <?php echo json_encode($model['color'] ?? '', JSON_UNESCAPED_UNICODE); ?>;
|
||||
|
||||
// 类型配置数据(从服务端传入)
|
||||
var typeConfigs = <?php echo json_encode($typeConfigs ?? [], JSON_UNESCAPED_UNICODE); ?>;
|
||||
|
||||
// 构建类型名→配置映射
|
||||
var typeConfigMap = {};
|
||||
if (Array.isArray(typeConfigs)) {
|
||||
typeConfigs.forEach(function(cfg) {
|
||||
typeConfigMap[cfg.type_name] = cfg;
|
||||
});
|
||||
}
|
||||
|
||||
function updateProductTypeField() {
|
||||
var t = $typeSel.val();
|
||||
var cfg = typeConfigMap[t] || {};
|
||||
|
||||
// 产品类型输入框(三态:hidden/readonly/editable)
|
||||
var ptState = cfg.product_type_state || 'editable';
|
||||
if (ptState === 'hidden') {
|
||||
$ptGroup.hide();
|
||||
$ptInput.removeAttr('required').val('');
|
||||
} else {
|
||||
$ptGroup.show();
|
||||
var label = cfg.product_type_label || '产品类型';
|
||||
var field = cfg.product_type_field || 'product_type';
|
||||
var placeholder = cfg.product_type_placeholder || '';
|
||||
if (!placeholder) {
|
||||
placeholder = '如:' + label;
|
||||
}
|
||||
$ptLabel.html(label + (ptState === 'editable' ? ' <span class="text-danger">*</span>' : ''));
|
||||
$ptInput.attr({name: field, placeholder: placeholder});
|
||||
if (ptState === 'readonly') {
|
||||
$ptInput.prop({readonly: true, required: false});
|
||||
} else {
|
||||
$ptInput.prop({readonly: false, required: true});
|
||||
}
|
||||
// 编辑时恢复原始值
|
||||
if (editProductType) {
|
||||
$ptInput.val(editProductType);
|
||||
}
|
||||
}
|
||||
|
||||
// 产品型号输入框(三态:hidden/readonly/editable)
|
||||
var mcState = cfg.model_code_state || 'editable';
|
||||
if (mcState === 'hidden') {
|
||||
$modelCodeGroup.hide();
|
||||
$modelCodeInput.removeAttr('required').val('');
|
||||
} else {
|
||||
$modelCodeGroup.show();
|
||||
if (mcState === 'readonly') {
|
||||
$modelCodeInput.prop({readonly: true, required: false});
|
||||
} else {
|
||||
$modelCodeInput.prop({readonly: false, required: true});
|
||||
}
|
||||
if (editModelCode) {
|
||||
$modelCodeInput.val(editModelCode);
|
||||
}
|
||||
}
|
||||
|
||||
// 颜色输入框(三态:hidden/readonly/editable)
|
||||
var cState = cfg.color_state || 'editable';
|
||||
if (cState === 'hidden') {
|
||||
$colorGroup.hide();
|
||||
$colorInput.val('');
|
||||
} else {
|
||||
$colorGroup.show();
|
||||
if (cState === 'readonly') {
|
||||
$colorInput.prop('readonly', true);
|
||||
} else {
|
||||
$colorInput.prop('readonly', false);
|
||||
}
|
||||
if (editColor) {
|
||||
$colorInput.val(editColor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$typeSel.on('change', updateProductTypeField);
|
||||
updateProductTypeField();
|
||||
})();
|
||||
</script>
|
||||
</div>
|
||||
<div class="box-footer">
|
||||
<button type="submit" class="btn btn-warning"><i class="fa fa-save"></i> 更新</button>
|
||||
<a href="<?php echo BASE_URL; ?>/Admin/productModel" class="btn btn-default"><i class="fa fa-arrow-left"></i> 返回</a>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php include APP_PATH . 'app/views/layouts/admin_footer.php'; ?>
|
||||
@@ -0,0 +1,128 @@
|
||||
<?php
|
||||
$activeMenu = 'productSearch';
|
||||
include APP_PATH . 'app/views/layouts/admin_header.php';
|
||||
?>
|
||||
<!-- 产品追溯搜索 -->
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<div class="box box-primary">
|
||||
<div class="box-header with-border">
|
||||
<h3 class="box-title"><i class="fa fa-search"></i> 产品追溯查询</h3>
|
||||
</div>
|
||||
<div class="box-body">
|
||||
<form method="GET" action="<?php echo BASE_URL; ?>/Admin/productSearch" class="form-inline">
|
||||
<div class="form-group" style="width:70%;">
|
||||
<div class="input-group" style="width:100%;">
|
||||
<input type="text" name="keyword" class="form-control input-lg"
|
||||
placeholder="输入成品序列号 / 电池序列号 / PCB序列号 / 产品型号 进行查询..."
|
||||
value="<?php echo htmlspecialchars($keyword); ?>" autofocus>
|
||||
<span class="input-group-btn">
|
||||
<button type="submit" class="btn btn-primary btn-lg"><i class="fa fa-search"></i> 查询</button>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<small class="help-block" style="margin-left:5px; color:#999;">
|
||||
<i class="fa fa-info-circle"></i> 支持模糊搜索:成品序列号、电池序列号、PCB序列号、产品型号
|
||||
</small>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php if ($searched): ?>
|
||||
<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-list"></i> 查询结果
|
||||
<small class="label label-primary">共 <?php echo count($results); ?> 条记录</small>
|
||||
</h3>
|
||||
</div>
|
||||
<div class="box-body table-responsive no-padding">
|
||||
<?php if (empty($results)): ?>
|
||||
<div class="callout callout-warning" style="margin:15px;">
|
||||
<h4><i class="fa fa-warning"></i> 未找到匹配记录</h4>
|
||||
<p>关键词 "<?php echo htmlspecialchars($keyword); ?>" 未匹配到任何产品记录,请检查输入。</p>
|
||||
</div>
|
||||
<?php else: ?>
|
||||
<table class="table table-bordered table-hover table-striped" style="font-size:13px;">
|
||||
<thead>
|
||||
<tr style="background:#3c8dbc; color:#fff;">
|
||||
<th style="text-align:center;">#</th>
|
||||
<th style="text-align:center;">成品序列号</th>
|
||||
<th style="text-align:center;">产品型号</th>
|
||||
<th style="text-align:center;">电池序列号</th>
|
||||
<th style="text-align:center;">PCB序列号</th>
|
||||
<th style="text-align:center;">操作员</th>
|
||||
<th style="text-align:center;">组装时间</th>
|
||||
<th style="text-align:center;">电池电压平台</th>
|
||||
<th style="text-align:center;">PCB测试结果</th>
|
||||
<th style="text-align:center;">成品测试结果</th>
|
||||
<th style="text-align:center;">入库时间</th>
|
||||
<th style="text-align:center;">出货客户</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php $i = 1; foreach ($results as $row): ?>
|
||||
<tr>
|
||||
<td style="text-align:center;"><?php echo $i++; ?></td>
|
||||
<td style="text-align:center; font-weight:bold; color:#3c8dbc;">
|
||||
<?php echo htmlspecialchars($row['finished_serial']); ?>
|
||||
</td>
|
||||
<td style="text-align:center;">
|
||||
<span class="label label-info"><?php echo htmlspecialchars($row['product_model']); ?></span>
|
||||
</td>
|
||||
<td style="text-align:center;">
|
||||
<?php echo htmlspecialchars($row['battery_serial']); ?>
|
||||
</td>
|
||||
<td style="text-align:center;">
|
||||
<?php echo htmlspecialchars($row['pcb_serial']); ?>
|
||||
</td>
|
||||
<td style="text-align:center;">
|
||||
<?php echo htmlspecialchars($row['operator']); ?>
|
||||
</td>
|
||||
<td style="text-align:center;">
|
||||
<?php echo htmlspecialchars($row['assembly_time']); ?>
|
||||
</td>
|
||||
<td style="text-align:center;">
|
||||
<?php echo htmlspecialchars($row['voltage_platform']); ?>
|
||||
</td>
|
||||
<td style="text-align:center;">
|
||||
<?php if (!empty($row['pcb_test_status'])): ?>
|
||||
<span class="label label-<?php echo stripos($row['pcb_test_status'], 'OK') !== false || stripos($row['pcb_test_status'], '合格') !== false || stripos($row['pcb_test_status'], 'pass') !== false ? 'success' : 'danger'; ?>">
|
||||
<?php echo htmlspecialchars($row['pcb_test_status']); ?>
|
||||
</span>
|
||||
<?php else: ?>
|
||||
<span class="text-muted">-</span>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
<td style="text-align:center;">
|
||||
<?php if (!empty($row['finished_test_result'])): ?>
|
||||
<span class="label label-<?php echo stripos($row['finished_test_result'], 'OK') !== false || stripos($row['finished_test_result'], '合格') !== false || stripos($row['finished_test_result'], 'pass') !== false ? 'success' : 'danger'; ?>">
|
||||
<?php echo htmlspecialchars($row['finished_test_result']); ?>
|
||||
</span>
|
||||
<?php else: ?>
|
||||
<span class="text-muted">-</span>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
<td style="text-align:center;">
|
||||
<?php echo !empty($row['warehouse_time']) ? htmlspecialchars($row['warehouse_time']) : '<span class="text-muted">未入库</span>'; ?>
|
||||
</td>
|
||||
<td style="text-align:center;">
|
||||
<?php echo !empty($row['delivery_customer']) ? htmlspecialchars($row['delivery_customer']) : '<span class="text-muted">未出货</span>'; ?>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php include APP_PATH . 'app/views/layouts/admin_footer.php'; ?>
|
||||
|
||||
@@ -0,0 +1,568 @@
|
||||
<?php
|
||||
$activeMenu = 'productModel';
|
||||
include APP_PATH . 'app/views/layouts/admin_header.php';
|
||||
|
||||
// 构建类型配置完整数据,包含解析后的 display_columns
|
||||
$configsWithParsed = [];
|
||||
if (!empty($configs)) {
|
||||
foreach ($configs as $cfg) {
|
||||
$cfg['_columns'] = [];
|
||||
if (!empty($cfg['display_columns'])) {
|
||||
$decoded = json_decode($cfg['display_columns'], true);
|
||||
if (is_array($decoded)) {
|
||||
$cfg['_columns'] = $decoded;
|
||||
}
|
||||
}
|
||||
// 确保有序号列
|
||||
$hasId = false;
|
||||
foreach ($cfg['_columns'] as $c) {
|
||||
if (($c['key'] ?? '') === 'id') { $hasId = true; break; }
|
||||
}
|
||||
if (!$hasId) {
|
||||
array_unshift($cfg['_columns'], ['key' => 'id', 'label' => '序号', 'width' => '60px']);
|
||||
}
|
||||
$configsWithParsed[] = $cfg;
|
||||
}
|
||||
}
|
||||
?>
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<div class="box">
|
||||
<div class="box-header with-border">
|
||||
<h3 class="box-title"><i class="fa fa-cogs"></i> 产品类型配置</h3>
|
||||
<div class="box-tools">
|
||||
<button type="button" class="btn btn-success btn-sm" id="addTypeBtn"><i class="fa fa-plus"></i> 添加类型</button>
|
||||
<a href="<?php echo BASE_URL; ?>/Admin/productModel" class="btn btn-default btn-sm"><i class="fa fa-arrow-left"></i> 返回型号列表</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-body table-responsive no-padding">
|
||||
<table class="table table-hover table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width:50px">排序</th>
|
||||
<th>类型名称</th>
|
||||
<th>列表显示列</th>
|
||||
<th style="width:90px">产品类型输入框</th>
|
||||
<th>输入框标签</th>
|
||||
<th>提交字段</th>
|
||||
<th style="width:70px">颜色</th>
|
||||
<th style="width:60px">启用</th>
|
||||
<th style="width:80px">入库显示</th>
|
||||
<th style="width:120px">操作</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php if (!empty($configsWithParsed)): ?>
|
||||
<?php foreach ($configsWithParsed as $cfg): ?>
|
||||
<tr>
|
||||
<td><?php echo $cfg['sort_order']; ?></td>
|
||||
<td><strong><?php echo htmlspecialchars($cfg['type_name']); ?></strong></td>
|
||||
<td>
|
||||
<?php
|
||||
$colLabels = [];
|
||||
foreach ($cfg['_columns'] as $col) {
|
||||
$colLabels[] = htmlspecialchars($col['label'] ?? $col['key']);
|
||||
}
|
||||
echo implode(' → ', $colLabels);
|
||||
?>
|
||||
</td>
|
||||
<td>
|
||||
<?php
|
||||
$ptState = $cfg['product_type_state'] ?? 'editable';
|
||||
if ($ptState === 'hidden'): ?>
|
||||
<span class="label label-default">隐藏</span>
|
||||
<?php elseif ($ptState === 'readonly'): ?>
|
||||
<span class="label label-warning">只读</span>
|
||||
<?php else: ?>
|
||||
<span class="label label-success">可编辑</span>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
<td><?php echo htmlspecialchars($cfg['product_type_label'] ?? '产品类型'); ?></td>
|
||||
<td><code><?php echo htmlspecialchars($cfg['product_type_field'] ?? 'product_type'); ?></code></td>
|
||||
<td><span class="label label-<?php echo htmlspecialchars($cfg['color'] ?? 'default'); ?>"><?php echo htmlspecialchars($cfg['color'] ?? 'default'); ?></span></td>
|
||||
<td><?php echo $cfg['enabled'] ? '<span class="label label-success">启用</span>' : '<span class="label label-danger">禁用</span>'; ?></td>
|
||||
<td><?php echo ($cfg['show_in_inbound'] ?? 1) ? '<span class="label label-success">显示</span>' : '<span class="label label-default">隐藏</span>'; ?></td>
|
||||
<td>
|
||||
<button type="button" class="btn btn-xs btn-info pt-edit-btn" data-id="<?php echo $cfg['id']; ?>"><i class="fa fa-edit"></i> 编辑</button>
|
||||
<button type="button" class="btn btn-xs btn-danger pt-del-btn" data-id="<?php echo $cfg['id']; ?>" data-name="<?php echo htmlspecialchars($cfg['type_name'], ENT_QUOTES); ?>"><i class="fa fa-trash"></i> 删除</button>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
<?php else: ?>
|
||||
<tr>
|
||||
<td colspan="10" class="text-center text-muted" style="padding:30px;">暂无配置,请先执行 SQL 初始化或点击"添加类型"</td>
|
||||
</tr>
|
||||
<?php endif; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 编辑/添加弹窗 -->
|
||||
<div class="modal fade" id="configModal" tabindex="-1">
|
||||
<div class="modal-dialog modal-lg">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal">×</button>
|
||||
<h4 class="modal-title" id="configModalTitle">编辑类型配置</h4>
|
||||
</div>
|
||||
<form id="configForm">
|
||||
<input type="hidden" name="csrf_token" value="<?php echo htmlspecialchars($csrfToken); ?>">
|
||||
<input type="hidden" name="id" id="cfgId" value="0">
|
||||
<div class="modal-body">
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="form-group">
|
||||
<label>类型名称 <span class="text-danger">*</span></label>
|
||||
<input type="text" name="type_name" id="cfgTypeName" class="form-control" required>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<div class="form-group">
|
||||
<label>排序</label>
|
||||
<input type="number" name="sort_order" id="cfgSortOrder" class="form-control" value="0" min="0">
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<div class="form-group">
|
||||
<label>Box颜色</label>
|
||||
<select name="color" id="cfgColor" class="form-control">
|
||||
<option value="default">default(灰)</option>
|
||||
<option value="success">success(绿)</option>
|
||||
<option value="primary">primary(蓝)</option>
|
||||
<option value="info">info(青)</option>
|
||||
<option value="warning">warning(黄)</option>
|
||||
<option value="danger">danger(红)</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="form-group">
|
||||
<label>输入框标签</label>
|
||||
<input type="text" name="product_type_label" id="cfgPtLabel" class="form-control" placeholder="如:容量、颜色">
|
||||
<small class="text-muted">添加/编辑页面第二个输入框的标签文字</small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="form-group">
|
||||
<label>提交字段名</label>
|
||||
<input type="text" name="product_type_field" id="cfgPtField" class="form-control" placeholder="product_type / capacity / color">
|
||||
<small class="text-muted">表单提交时的字段名(后端据此识别)</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="form-group">
|
||||
<label>输入框占位提示</label>
|
||||
<input type="text" name="product_type_placeholder" id="cfgPtPlaceholder" class="form-control" placeholder="如:2000mAh">
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="form-group">
|
||||
<label>产品类型输入框</label>
|
||||
<select name="product_type_state" id="cfgProductTypeState" class="form-control">
|
||||
<option value="editable">可编辑</option>
|
||||
<option value="readonly">只读</option>
|
||||
<option value="hidden">隐藏</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-3">
|
||||
<div class="form-group">
|
||||
<label>产品型号输入框</label>
|
||||
<select name="model_code_state" id="cfgModelCodeState" class="form-control">
|
||||
<option value="editable">可编辑</option>
|
||||
<option value="readonly">只读</option>
|
||||
<option value="hidden">隐藏</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<div class="form-group">
|
||||
<label>颜色输入框</label>
|
||||
<select name="color_state" id="cfgColorState" class="form-control">
|
||||
<option value="editable">可编辑</option>
|
||||
<option value="readonly">只读</option>
|
||||
<option value="hidden">隐藏</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<div class="form-group">
|
||||
<label>启用</label>
|
||||
<select name="enabled" id="cfgEnabled" class="form-control">
|
||||
<option value="1">启用</option>
|
||||
<option value="0">禁用</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<div class="form-group">
|
||||
<label>入库页显示</label>
|
||||
<select name="show_in_inbound" id="cfgShowInInbound" class="form-control">
|
||||
<option value="1">显示</option>
|
||||
<option value="0">隐藏</option>
|
||||
</select>
|
||||
<small class="text-muted">控制该类型在入库工位的下拉选项中是否可见</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 列表显示列 - 复选框编辑器 -->
|
||||
<div class="form-group">
|
||||
<label>列表显示列</label>
|
||||
<div class="well well-sm" style="margin-bottom:8px; padding:8px 10px;">
|
||||
<small class="text-muted"><i class="fa fa-info-circle"></i> 勾选需要在列表页显示的列。序号列自动包含,无需选择。拖拽调整顺序。</small>
|
||||
</div>
|
||||
|
||||
<!-- 可选字段区 -->
|
||||
<div style="margin-bottom:8px;">
|
||||
<label class="text-muted" style="font-weight:normal; margin-right:8px;">可选字段:</label>
|
||||
<label class="checkbox-inline" style="margin-right:10px;">
|
||||
<input type="checkbox" class="col-checkbox" data-key="type" data-label="类型" data-width="80px"> 类型
|
||||
</label>
|
||||
<label class="checkbox-inline" style="margin-right:10px;">
|
||||
<input type="checkbox" class="col-checkbox" data-key="model_code" data-label="产品型号" data-width=""> 产品型号
|
||||
</label>
|
||||
<label class="checkbox-inline" style="margin-right:10px;">
|
||||
<input type="checkbox" class="col-checkbox" data-key="color" data-label="颜色" data-width="120px"> 颜色
|
||||
</label>
|
||||
<label class="checkbox-inline" style="margin-right:10px;">
|
||||
<input type="checkbox" class="col-checkbox" data-key="capacity" data-label="容量" data-width="120px"> 容量
|
||||
</label>
|
||||
<label class="checkbox-inline" style="margin-right:10px;">
|
||||
<input type="checkbox" class="col-checkbox" data-key="model_desc" data-label="产品说明" data-width=""> 产品说明
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<!-- 已选列预览 -->
|
||||
<div id="selectedColumnsPreview" style="min-height:36px; padding:6px 8px; border:1px dashed #ccc; border-radius:4px; background:#fafafa;">
|
||||
<span id="noColumnsHint" class="text-muted">暂未选择列</span>
|
||||
</div>
|
||||
<div style="margin-top:5px;">
|
||||
<button type="button" class="btn btn-xs btn-warning" id="resetColsBtn"><i class="fa fa-refresh"></i> 恢复默认</button>
|
||||
<button type="button" class="btn btn-xs btn-default" id="clearColsBtn"><i class="fa fa-times"></i> 清空</button>
|
||||
</div>
|
||||
</div>
|
||||
<input type="hidden" name="display_columns" id="cfgDisplayCols" value="">
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal">取消</button>
|
||||
<button type="submit" class="btn btn-primary"><i class="fa fa-save"></i> 保存</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script nonce="<?php echo csp_nonce(); ?>">
|
||||
var BASE_URL = '<?php echo BASE_URL; ?>';
|
||||
var configsData = <?php echo json_encode($configs ?? [], JSON_UNESCAPED_UNICODE); ?>;
|
||||
|
||||
// 当前编辑中的列数据(按显示顺序)
|
||||
var editColumns = [];
|
||||
|
||||
// 所有可用字段定义
|
||||
var allFields = [
|
||||
{key:'type', label:'类型', width:'80px'},
|
||||
{key:'model_code', label:'产品型号', width:''},
|
||||
{key:'color', label:'颜色', width:'120px'},
|
||||
{key:'capacity', label:'容量', width:'120px'},
|
||||
{key:'model_desc', label:'产品说明', width:''}
|
||||
];
|
||||
|
||||
// 构建 key→field 映射
|
||||
var fieldMap = {};
|
||||
allFields.forEach(function(f) { fieldMap[f.key] = f; });
|
||||
|
||||
// 默认列模板(按类型名,返回 key 数组)
|
||||
function getDefaultKeys(typeName) {
|
||||
var tn = (typeName || '').trim();
|
||||
if (tn === '电池') {
|
||||
return ['model_code', 'capacity', 'model_desc'];
|
||||
} else if (tn === '外壳') {
|
||||
return ['model_code', 'color', 'model_desc'];
|
||||
} else if (tn === '外壳颜色') {
|
||||
return ['color', 'model_desc'];
|
||||
} else {
|
||||
return ['type', 'model_code', 'model_desc'];
|
||||
}
|
||||
}
|
||||
|
||||
function getDefaultColumns(typeName) {
|
||||
var keys = getDefaultKeys(typeName);
|
||||
return keys.map(function(k) {
|
||||
var f = fieldMap[k] || {key:k, label:k, width:''};
|
||||
return {key: f.key, label: f.label, width: f.width};
|
||||
});
|
||||
}
|
||||
|
||||
// 根据 editColumns 刷新复选框状态和预览
|
||||
function syncCheckboxesFromColumns() {
|
||||
var selectedKeys = {};
|
||||
editColumns.forEach(function(c) { selectedKeys[c.key] = true; });
|
||||
|
||||
$('.col-checkbox').each(function() {
|
||||
$(this).prop('checked', !!selectedKeys[$(this).data('key')]);
|
||||
});
|
||||
renderPreview();
|
||||
updateHiddenInput();
|
||||
}
|
||||
|
||||
// 根据复选框状态重建 editColumns(保持已有顺序,新增追加末尾,取消的移除)
|
||||
function syncColumnsFromCheckboxes() {
|
||||
var checkedKeys = {};
|
||||
$('.col-checkbox:checked').each(function() {
|
||||
checkedKeys[$(this).data('key')] = true;
|
||||
});
|
||||
|
||||
// 移除已取消的
|
||||
editColumns = editColumns.filter(function(c) { return checkedKeys[c.key]; });
|
||||
|
||||
// 追加新勾选的
|
||||
$('.col-checkbox:checked').each(function() {
|
||||
var key = $(this).data('key');
|
||||
if (!editColumns.some(function(c) { return c.key === key; })) {
|
||||
var label = $(this).data('label');
|
||||
var width = $(this).data('width') || '';
|
||||
editColumns.push({key: key, label: label, width: width});
|
||||
}
|
||||
});
|
||||
|
||||
renderPreview();
|
||||
updateHiddenInput();
|
||||
}
|
||||
|
||||
// 渲染已选列预览(可拖拽排序的标签)
|
||||
function renderPreview() {
|
||||
var container = document.getElementById('selectedColumnsPreview');
|
||||
var hint = document.getElementById('noColumnsHint');
|
||||
|
||||
if (!editColumns.length) {
|
||||
container.innerHTML = '';
|
||||
container.appendChild(hint || createHint());
|
||||
return;
|
||||
}
|
||||
|
||||
var html = '';
|
||||
editColumns.forEach(function(col, i) {
|
||||
html += '<span class="label label-primary column-tag" draggable="true" data-idx="' + i + '" style="display:inline-block; margin:2px 4px; padding:6px 10px; font-size:13px; cursor:grab; user-select:none;">';
|
||||
html += escHtml(col.label);
|
||||
html += ' <a href="#" class="column-remove" data-idx="' + i + '" style="color:#fff; opacity:0.7; margin-left:4px;" title="移除">×</a>';
|
||||
html += '</span>';
|
||||
});
|
||||
container.innerHTML = html;
|
||||
|
||||
// 拖拽排序
|
||||
var tags = container.querySelectorAll('.column-tag');
|
||||
var dragSrc = null;
|
||||
|
||||
tags.forEach(function(tag) {
|
||||
tag.addEventListener('dragstart', function(e) {
|
||||
dragSrc = this;
|
||||
this.style.opacity = '0.4';
|
||||
e.dataTransfer.effectAllowed = 'move';
|
||||
});
|
||||
tag.addEventListener('dragend', function(e) {
|
||||
this.style.opacity = '1';
|
||||
tags.forEach(function(t) { t.style.borderTop = ''; });
|
||||
});
|
||||
tag.addEventListener('dragover', function(e) {
|
||||
e.preventDefault();
|
||||
e.dataTransfer.dropEffect = 'move';
|
||||
this.style.borderTop = '2px solid #3c8dbc';
|
||||
});
|
||||
tag.addEventListener('dragleave', function(e) {
|
||||
this.style.borderTop = '';
|
||||
});
|
||||
tag.addEventListener('drop', function(e) {
|
||||
e.preventDefault();
|
||||
this.style.borderTop = '';
|
||||
if (dragSrc !== this) {
|
||||
var fromIdx = parseInt(dragSrc.getAttribute('data-idx'));
|
||||
var toIdx = parseInt(this.getAttribute('data-idx'));
|
||||
var moved = editColumns.splice(fromIdx, 1)[0];
|
||||
editColumns.splice(toIdx, 0, moved);
|
||||
renderPreview();
|
||||
updateHiddenInput();
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function createHint() {
|
||||
var span = document.createElement('span');
|
||||
span.id = 'noColumnsHint';
|
||||
span.className = 'text-muted';
|
||||
span.textContent = '暂未选择列';
|
||||
return span;
|
||||
}
|
||||
|
||||
function removeColumn(idx) {
|
||||
var key = editColumns[idx].key;
|
||||
editColumns.splice(idx, 1);
|
||||
// 同步取消复选框
|
||||
$('.col-checkbox[data-key="' + key + '"]').prop('checked', false);
|
||||
renderPreview();
|
||||
updateHiddenInput();
|
||||
}
|
||||
|
||||
function clearAllColumns() {
|
||||
editColumns = [];
|
||||
$('.col-checkbox').prop('checked', false);
|
||||
renderPreview();
|
||||
updateHiddenInput();
|
||||
}
|
||||
|
||||
function resetColumnsToDefault() {
|
||||
var typeName = $('#cfgTypeName').val().trim();
|
||||
editColumns = JSON.parse(JSON.stringify(getDefaultColumns(typeName)));
|
||||
syncCheckboxesFromColumns();
|
||||
}
|
||||
|
||||
function updateHiddenInput() {
|
||||
document.getElementById('cfgDisplayCols').value = JSON.stringify(editColumns);
|
||||
}
|
||||
|
||||
function escHtml(s) { return (s||'').replace(/&/g,'&').replace(/"/g,'"').replace(/</g,'<').replace(/>/g,'>'); }
|
||||
|
||||
// ===== 轻量 Modal Polyfill(不依赖 Bootstrap JS) =====
|
||||
var _modalPolyfill = {
|
||||
show: function(modalId) {
|
||||
var $modal = $(modalId);
|
||||
if (!$modal.length) return;
|
||||
if (!$('.modal-backdrop').length) {
|
||||
$('body').append('<div class="modal-backdrop fade in" style="z-index:1040;"></div>');
|
||||
}
|
||||
$modal.css({display:'block', 'z-index':1050}).addClass('in');
|
||||
$('body').addClass('modal-open');
|
||||
$modal.find('[data-dismiss="modal"]').off('click.modalPolyfill').on('click.modalPolyfill', function(){
|
||||
_modalPolyfill.hide(modalId);
|
||||
});
|
||||
$('.modal-backdrop').off('click.modalPolyfill').on('click.modalPolyfill', function(){
|
||||
_modalPolyfill.hide(modalId);
|
||||
});
|
||||
$(document).off('keydown.modalPolyfill').on('keydown.modalPolyfill', function(e){
|
||||
if (e.keyCode === 27) _modalPolyfill.hide(modalId);
|
||||
});
|
||||
},
|
||||
hide: function(modalId) {
|
||||
var $modal = $(modalId);
|
||||
$modal.removeClass('in').css('display', '');
|
||||
$('.modal-backdrop').remove();
|
||||
$('body').removeClass('modal-open');
|
||||
$(document).off('keydown.modalPolyfill');
|
||||
}
|
||||
};
|
||||
|
||||
function safeModal(action, modalId) {
|
||||
if (typeof $.fn.modal === 'function') {
|
||||
$(modalId).modal(action);
|
||||
} else {
|
||||
if (action === 'show') _modalPolyfill.show(modalId);
|
||||
else if (action === 'hide') _modalPolyfill.hide(modalId);
|
||||
}
|
||||
}
|
||||
|
||||
function openEditModal(id) {
|
||||
var cfg = {};
|
||||
if (id > 0) {
|
||||
for (var i = 0; i < configsData.length; i++) {
|
||||
if (configsData[i].id == id) { cfg = configsData[i]; break; }
|
||||
}
|
||||
$('#configModalTitle').text('编辑类型配置');
|
||||
} else {
|
||||
$('#configModalTitle').text('添加类型配置');
|
||||
cfg = {
|
||||
id: 0, type_name: '', sort_order: configsData.length + 1,
|
||||
display_columns: '', product_type_state: 'editable',
|
||||
product_type_label: '产品类型', product_type_field: 'product_type',
|
||||
product_type_placeholder: '', model_code_state: 'editable',
|
||||
color_state: 'editable', color: 'default', enabled: 1,
|
||||
show_in_inbound: 1
|
||||
};
|
||||
}
|
||||
$('#cfgId').val(cfg.id || 0);
|
||||
$('#cfgTypeName').val(cfg.type_name || '');
|
||||
$('#cfgSortOrder').val(cfg.sort_order || 0);
|
||||
$('#cfgProductTypeState').val(cfg.product_type_state || 'editable');
|
||||
$('#cfgPtLabel').val(cfg.product_type_label || '产品类型');
|
||||
$('#cfgPtField').val(cfg.product_type_field || 'product_type');
|
||||
$('#cfgPtPlaceholder').val(cfg.product_type_placeholder || '');
|
||||
$('#cfgModelCodeState').val(cfg.model_code_state || 'editable');
|
||||
$('#cfgColorState').val(cfg.color_state || 'editable');
|
||||
$('#cfgColor').val(cfg.color || 'default');
|
||||
$('#cfgEnabled').val(cfg.enabled !== undefined ? cfg.enabled : 1);
|
||||
$('#cfgShowInInbound').val(cfg.show_in_inbound !== undefined ? cfg.show_in_inbound : 1);
|
||||
|
||||
// 解析 display_columns JSON
|
||||
editColumns = [];
|
||||
if (cfg.display_columns) {
|
||||
try {
|
||||
var parsed = JSON.parse(cfg.display_columns);
|
||||
if (Array.isArray(parsed)) {
|
||||
editColumns = parsed.filter(function(c) { return c.key !== 'id'; });
|
||||
}
|
||||
} catch(e) {}
|
||||
}
|
||||
if (!editColumns.length) {
|
||||
editColumns = JSON.parse(JSON.stringify(getDefaultColumns(cfg.type_name || '')));
|
||||
}
|
||||
syncCheckboxesFromColumns();
|
||||
|
||||
safeModal('show', '#configModal');
|
||||
}
|
||||
|
||||
// 复选框变化时同步
|
||||
$(document).on('change', '.col-checkbox', function() {
|
||||
syncColumnsFromCheckboxes();
|
||||
});
|
||||
|
||||
// ===== CSP:事件绑定(替代内联 onclick/javascript:) =====
|
||||
$('#addTypeBtn').on('click', function() { openEditModal(0); });
|
||||
$('#resetColsBtn').on('click', resetColumnsToDefault);
|
||||
$('#clearColsBtn').on('click', clearAllColumns);
|
||||
$(document).on('click', '.pt-edit-btn', function() { openEditModal($(this).data('id')); });
|
||||
$(document).on('click', '.pt-del-btn', function() { deleteConfig($(this).data('id'), $(this).data('name')); });
|
||||
$('#selectedColumnsPreview').on('click', '.column-remove', function(e) {
|
||||
e.preventDefault();
|
||||
removeColumn(parseInt($(this).attr('data-idx'), 10));
|
||||
});
|
||||
|
||||
function deleteConfig(id, name) {
|
||||
if (!confirm('确定删除类型「' + name + '」吗?\n注意:已使用该类型的产品型号不会受影响,但添加新型号时不再显示此类型。')) return;
|
||||
$.ajax({
|
||||
url: window.location.href, type: 'POST',
|
||||
data: { csrf_token: $('input[name="csrf_token"]').val(), action: 'delete', id: id },
|
||||
dataType: 'json',
|
||||
success: function(res) {
|
||||
if (res.success) location.reload();
|
||||
else alert('删除失败: ' + (res.error || '未知错误'));
|
||||
},
|
||||
error: function() { alert('网络错误'); }
|
||||
});
|
||||
}
|
||||
|
||||
$('#configForm').on('submit', function(e) {
|
||||
e.preventDefault();
|
||||
updateHiddenInput();
|
||||
var data = $(this).serialize() + '&action=save';
|
||||
$.ajax({
|
||||
url: window.location.href, type: 'POST', data: data, dataType: 'json',
|
||||
success: function(res) {
|
||||
if (res.success) location.reload();
|
||||
else alert('保存失败: ' + (res.error || '未知错误'));
|
||||
},
|
||||
error: function() { alert('网络错误'); }
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
<?php include APP_PATH . 'app/views/layouts/admin_footer.php'; ?>
|
||||
@@ -0,0 +1,57 @@
|
||||
<?php
|
||||
$activeMenu = 'workstation';
|
||||
include APP_PATH . 'app/views/layouts/admin_header.php';
|
||||
?>
|
||||
|
||||
<div class="box">
|
||||
<div class="box-header with-border">
|
||||
<h3 class="box-title"><i class="fa fa-list"></i> 工位列表</h3>
|
||||
<div class="box-tools">
|
||||
<a href="<?php echo BASE_URL; ?>/Admin/workstationAdd" class="btn btn-primary btn-sm"><i class="fa fa-plus"></i> 添加工位</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-body table-responsive no-padding">
|
||||
<table class="table table-hover table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width:60px">序号</th>
|
||||
<th>工位名称</th>
|
||||
<th>工位编码</th>
|
||||
<th>工位类型</th>
|
||||
<th>排序</th>
|
||||
<th>状态</th>
|
||||
<th style="width:150px">操作</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php if (!empty($stations)): ?>
|
||||
<?php foreach ($stations as $station): ?>
|
||||
<tr>
|
||||
<td><?php echo $station['id']; ?></td>
|
||||
<td><?php echo htmlspecialchars($station['station_name']); ?></td>
|
||||
<td><code><?php echo htmlspecialchars($station['station_code']); ?></code></td>
|
||||
<td><?php echo htmlspecialchars($station['station_type']); ?></td>
|
||||
<td><?php echo $station['sort_order']; ?></td>
|
||||
<td>
|
||||
<span class="label label-<?php echo $station['status'] == 1 ? 'success' : 'danger'; ?>">
|
||||
<?php echo $station['status'] == 1 ? '启用' : '禁用'; ?>
|
||||
</span>
|
||||
</td>
|
||||
<td>
|
||||
<a href="<?php echo BASE_URL; ?>/Admin/workstationEdit/<?php echo $station['id']; ?>" class="btn btn-xs btn-info"><i class="fa fa-edit"></i> 编辑</a>
|
||||
<form method="post" action="<?php echo BASE_URL; ?>/Admin/workstationDelete/<?php echo $station['id']; ?>" style="display:inline;" onsubmit="return confirm('确定删除该工位?')">
|
||||
<input type="hidden" name="csrf_token" value="<?php echo htmlspecialchars($csrfToken); ?>">
|
||||
<button type="submit" class="btn btn-xs btn-danger"><i class="fa fa-trash"></i> 删除</button>
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
<?php else: ?>
|
||||
<tr><td colspan="7" class="text-center text-muted">暂无工位数据</td></tr>
|
||||
<?php endif; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php include APP_PATH . 'app/views/layouts/admin_footer.php'; ?>
|
||||
@@ -0,0 +1,100 @@
|
||||
<?php
|
||||
$activeMenu = 'workstation';
|
||||
include APP_PATH . 'app/views/layouts/admin_header.php';
|
||||
?>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6 col-md-offset-3">
|
||||
<div class="box box-primary">
|
||||
<div class="box-header with-border">
|
||||
<h3 class="box-title"><i class="fa fa-plus"></i> 添加工位</h3>
|
||||
</div>
|
||||
<form method="post" action="">
|
||||
<input type="hidden" name="csrf_token" value="<?php echo htmlspecialchars($csrfToken); ?>">
|
||||
<div class="box-body">
|
||||
<?php if (!empty($error)): ?>
|
||||
<div class="alert alert-danger alert-dismissible">
|
||||
<button type="button" class="close" data-dismiss="alert" aria-hidden="true">×</button>
|
||||
<h4><i class="icon fa fa-ban"></i> 操作失败</h4>
|
||||
<?php echo htmlspecialchars($error); ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
<div class="form-group">
|
||||
<label>工位名称 <span class="text-danger">*</span></label>
|
||||
<input type="text" name="station_name" class="form-control" placeholder="如:入库工位" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>工位编码 <span class="text-danger">*</span></label>
|
||||
<input type="text" name="station_code" class="form-control" placeholder="如:STATION_INBOUND" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>工位类型 <span class="text-danger">*</span></label>
|
||||
<input type="text" name="station_type" class="form-control" placeholder="英文标识,如:painting、welding" required
|
||||
pattern="[a-zA-Z][a-zA-Z0-9_]*" title="必须以字母开头,只能包含字母、数字和下划线">
|
||||
<small class="text-muted">用于系统路由和权限识别,必须以字母开头,建议使用英文小写+下划线</small>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>字段配置模板 <small class="text-muted">(可选,从已有工位复制)</small></label>
|
||||
<select name="template_station" class="form-control" onchange="handleTemplateChange(this.value)">
|
||||
<option value="">不复制(空白工位)</option>
|
||||
<option value="inbound">半成品入库工位</option>
|
||||
<option value="pcb_test">PCB 测试</option>
|
||||
<option value="battery">电池状态</option>
|
||||
<option value="assembly">产品组装</option>
|
||||
<option value="finished_test">成品测试</option>
|
||||
<option value="warehouse">成品入库工位</option>
|
||||
<option value="delivery">出货登记</option>
|
||||
</select>
|
||||
<small class="text-muted">选择后将复制该工位的字段配置,后续可在编辑中修改</small>
|
||||
</div>
|
||||
<div id="templatePreview" style="display:none; margin-top:10px; padding:10px; background:#f9f9f9; border-radius:4px;">
|
||||
<strong><i class="fa fa-clone"></i> 将复制的字段:</strong>
|
||||
<span id="templateFieldsPreview" style="margin-left:8px; color:#666;"></span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>排序</label>
|
||||
<input type="number" name="sort_order" class="form-control" value="1" min="1">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>状态</label>
|
||||
<select name="status" class="form-control">
|
||||
<option value="1">启用</option>
|
||||
<option value="0">禁用</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-footer">
|
||||
<button type="submit" class="btn btn-primary"><i class="fa fa-save"></i> 保存</button>
|
||||
<a href="<?php echo BASE_URL; ?>/Admin/workstation" class="btn btn-default"><i class="fa fa-arrow-left"></i> 返回</a>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// 工位类型模板预览
|
||||
var templateFieldsConfig = <?php echo json_encode($templateFieldsMap ?? [], JSON_UNESCAPED_UNICODE); ?>;
|
||||
|
||||
function handleTemplateChange(templateType) {
|
||||
var preview = document.getElementById('templatePreview');
|
||||
var fieldsSpan = document.getElementById('templateFieldsPreview');
|
||||
|
||||
if (!templateType) {
|
||||
preview.style.display = 'none';
|
||||
return;
|
||||
}
|
||||
|
||||
var fields = templateFieldsConfig[templateType] || [];
|
||||
if (fields.length > 0) {
|
||||
var names = fields.map(function(f) { return f.label || f.name; });
|
||||
fieldsSpan.textContent = names.join('、');
|
||||
preview.style.display = 'block';
|
||||
} else {
|
||||
fieldsSpan.textContent = '(该工位暂无字段配置)';
|
||||
preview.style.display = 'block';
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<?php include APP_PATH . 'app/views/layouts/admin_footer.php'; ?>
|
||||
@@ -0,0 +1,74 @@
|
||||
<?php
|
||||
$activeMenu = 'workstation';
|
||||
include APP_PATH . 'app/views/layouts/admin_header.php';
|
||||
?>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-8 col-md-offset-2">
|
||||
<div class="box box-warning">
|
||||
<div class="box-header with-border">
|
||||
<h3 class="box-title"><i class="fa fa-edit"></i> 编辑工位</h3>
|
||||
</div>
|
||||
<form method="post" action="">
|
||||
<input type="hidden" name="csrf_token" value="<?php echo htmlspecialchars($csrfToken); ?>">
|
||||
<div class="box-body">
|
||||
<?php if (!empty($error)): ?>
|
||||
<div class="alert alert-danger alert-dismissible">
|
||||
<button type="button" class="close" data-dismiss="alert" aria-hidden="true">×</button>
|
||||
<h4><i class="icon fa fa-ban"></i> 操作失败</h4>
|
||||
<?php echo htmlspecialchars($error); ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="form-group">
|
||||
<label>工位名称 <span class="text-danger">*</span></label>
|
||||
<input type="text" name="station_name" class="form-control" value="<?php echo htmlspecialchars($station['station_name']); ?>" required>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="form-group">
|
||||
<label>工位编码</label>
|
||||
<input type="text" class="form-control" value="<?php echo htmlspecialchars($station['station_code']); ?>" disabled>
|
||||
<small class="text-muted">编码不可修改</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="form-group">
|
||||
<label>工位类型</label>
|
||||
<input type="text" class="form-control" value="<?php echo htmlspecialchars($station['station_type']); ?>" disabled>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<div class="form-group">
|
||||
<label>排序</label>
|
||||
<input type="number" name="sort_order" class="form-control" value="<?php echo $station['sort_order']; ?>" min="1">
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<div class="form-group">
|
||||
<label>状态</label>
|
||||
<select name="status" class="form-control">
|
||||
<option value="1" <?php echo $station['status'] == 1 ? 'selected' : ''; ?>>启用</option>
|
||||
<option value="0" <?php echo $station['status'] == 0 ? 'selected' : ''; ?>>禁用</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 字段配置编辑器 -->
|
||||
<?php echo $fieldEditor ?? ''; ?>
|
||||
|
||||
</div>
|
||||
<div class="box-footer">
|
||||
<button type="submit" class="btn btn-warning"><i class="fa fa-save"></i> 更新</button>
|
||||
<a href="<?php echo BASE_URL; ?>/Admin/workstation" class="btn btn-default"><i class="fa fa-arrow-left"></i> 返回</a>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php include APP_PATH . 'app/views/layouts/admin_footer.php'; ?>
|
||||
Reference in New Issue
Block a user