<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>CSV数据筛查工具 - 提取车机号</title>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
}
body {
background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
min-height: 100vh;
padding: 20px;
color: #333;
}
.container {
max-width: 1200px;
margin: 0 auto;
}
header {
text-align: center;
margin-bottom: 30px;
padding: 20px;
background-color: white;
border-radius: 12px;
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.08);
}
h1 {
color: #2c3e50;
margin-bottom: 10px;
display: flex;
align-items: center;
justify-content: center;
gap: 15px;
}
.subtitle {
color: #7f8c8d;
font-size: 1.1rem;
}
.app-container {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 30px;
margin-bottom: 30px;
}
@media (max-width: 900px) {
.app-container {
grid-template-columns: 1fr;
}
}
.card {
background-color: white;
border-radius: 12px;
padding: 25px;
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.08);
}
.card h2 {
color: #3498db;
margin-bottom: 20px;
padding-bottom: 10px;
border-bottom: 2px solid #f0f0f0;
display: flex;
align-items: center;
gap: 10px;
}
.file-upload-area {
border: 3px dashed #3498db;
border-radius: 10px;
padding: 40px 20px;
text-align: center;
margin-bottom: 25px;
cursor: pointer;
transition: all 0.3s ease;
}
.file-upload-area:hover {
background-color: #f8fafd;
border-color: #2980b9;
}
.file-upload-area i {
font-size: 50px;
color: #3498db;
margin-bottom: 15px;
}
.file-upload-area p {
font-size: 1.1rem;
margin-bottom: 10px;
}
.file-upload-area .file-info {
color: #7f8c8d;
font-size: 0.9rem;
}
.file-input {
display: none;
}
.btn {
background-color: #3498db;
color: white;
border: none;
padding: 12px 24px;
border-radius: 6px;
font-size: 1rem;
cursor: pointer;
transition: all 0.3s ease;
display: inline-flex;
align-items: center;
justify-content: center;
gap: 8px;
}
.btn:hover {
background-color: #2980b9;
transform: translateY(-2px);
box-shadow: 0 4px 8px rgba(41, 128, 185, 0.3);
}
.btn:disabled {
background-color: #bdc3c7;
cursor: not-allowed;
transform: none;
box-shadow: none;
}
.btn-process {
background-color: #2ecc71;
margin-top: 20px;
width: 100%;
}
.btn-process:hover {
background-color: #27ae60;
}
.btn-copy {
background-color: #9b59b6;
margin-top: 15px;
width: 100%;
}
.btn-copy:hover {
background-color: #8e44ad;
}
.formula-display {
background-color: #f8f9fa;
border-left: 4px solid #3498db;
padding: 15px;
margin: 20px 0;
font-family: 'Courier New', monospace;
border-radius: 0 8px 8px 0;
}
.results-container {
max-height: 400px;
overflow-y: auto;
margin-top: 20px;
border: 1px solid #eee;
border-radius: 8px;
padding: 15px;
}
.result-item {
padding: 12px 15px;
margin-bottom: 10px;
background-color: #f8f9fa;
border-radius: 6px;
border-left: 4px solid #2ecc71;
display: flex;
justify-content: space-between;
align-items: center;
transition: all 0.2s ease;
}
.result-item:hover {
background-color: #e8f4fc;
transform: translateX(5px);
}
.result-number {
font-weight: bold;
color: #2c3e50;
}
.result-value {
font-family: 'Courier New', monospace;
background-color: #2c3e50;
color: white;
padding: 5px 10px;
border-radius: 4px;
}
.empty-state {
text-align: center;
padding: 40px 20px;
color: #7f8c8d;
}
.empty-state i {
font-size: 60px;
margin-bottom: 15px;
color: #bdc3c7;
}
.step-indicator {
display: flex;
margin-bottom: 25px;
justify-content: space-between;
position: relative;
}
.step-indicator::before {
content: '';
position: absolute;
top: 20px;
left: 10%;
right: 10%;
height: 2px;
background-color: #ecf0f1;
z-index: 1;
}
.step {
display: flex;
flex-direction: column;
align-items: center;
position: relative;
z-index: 2;
}
.step-circle {
width: 40px;
height: 40px;
border-radius: 50%;
background-color: #ecf0f1;
display: flex;
align-items: center;
justify-content: center;
font-weight: bold;
margin-bottom: 8px;
border: 3px solid white;
box-shadow: 0 2px 5px rgba(0,0,0,0.1);
}
.step.active .step-circle {
background-color: #3498db;
color: white;
}
.step.completed .step-circle {
background-color: #2ecc71;
color: white;
}
.step-label {
font-size: 0.9rem;
color: #7f8c8d;
}
.step.active .step-label {
color: #3498db;
font-weight: bold;
}
.data-preview {
margin-top: 20px;
overflow-x: auto;
}
.data-preview table {
width: 100%;
border-collapse: collapse;
font-size: 0.9rem;
}
.data-preview th, .data-preview td {
border: 1px solid #ddd;
padding: 8px;
text-align: left;
}
.data-preview th {
background-color: #f2f2f2;
font-weight: bold;
}
.data-preview tr:nth-child(even) {
background-color: #f9f9f9;
}
.highlight-column {
background-color: #e8f4fc !important;
}
footer {
text-align: center;
padding: 20px;
color: #7f8c8d;
font-size: 0.9rem;
margin-top: 30px;
}
.instructions {
background-color: #f8f9fa;
padding: 15px;
border-radius: 8px;
margin-top: 20px;
font-size: 0.95rem;
}
.instructions h3 {
margin-bottom: 10px;
color: #2c3e50;
}
.instructions ol {
padding-left: 20px;
}
.instructions li {
margin-bottom: 8px;
}
</style>
</head>
<body>
<div class="container">
<header>
<h1><i class="fas fa-filter"></i> CSV数据筛查工具</h1>
<p class="subtitle">上传CSV文件,自动提取车机号信息,支持批量处理与结果导出</p>
</header>
<div class="step-indicator">
<div class="step completed" id="step1">
<div class="step-circle">1</div>
<div class="step-label">上传CSV文件</div>
</div>
<div class="step active" id="step2">
<div class="step-circle">2</div>
<div class="step-label">数据解码处理</div>
</div>
<div class="step" id="step3">
<div class="step-circle">3</div>
<div class="step-label">提取结果展示</div>
</div>
</div>
<div class="app-container">
<!-- 左侧:文件上传和处理区域 -->
<div class="card">
<h2><i class="fas fa-upload"></i> 上传CSV文件</h2>
<div class="file-upload-area" id="dropArea">
<i class="fas fa-file-csv"></i>
<p>点击选择或拖拽CSV文件到此处</p>
<div class="file-info">支持标准CSV格式,文件大小不超过10MB</div>
<input type="file" id="fileInput" class="file-input" accept=".csv">
</div>
<div class="selected-file" id="selectedFileInfo" style="display: none;">
<p><strong>已选择文件:</strong> <span id="fileName"></span></p>
<p><strong>文件大小:</strong> <span id="fileSize"></span></p>
</div>
<div class="formula-display">
<p><strong>将执行的Excel函数公式:</strong></p>
<p>=MID(AA2, FIND("车机号", AA2)-1, 17)</p>
<p><small>说明:在AA列中查找"车机号"文本,然后提取其前1位开始共17位字符</small></p>
</div>
<button id="processBtn" class="btn btn-process" disabled>
<i class="fas fa-cogs"></i> 开始处理数据
</button>
<div class="instructions">
<h3>使用说明:</h3>
<ol>
<li>上传包含数据的CSV文件(确保有一列包含"车机号"文本)</li>
<li>系统将自动解析CSV文件内容</li>
<li>程序会执行类似Excel的MID和FIND函数提取指定数据</li>
<li>提取结果将显示在右侧,可以一键复制所有结果</li>
</ol>
</div>
</div>
<!-- 右侧:结果显示区域 -->
<div class="card">
<h2><i class="fas fa-list-alt"></i> 提取结果</h2>
<div id="resultsContainer" class="results-container">
<div class="empty-state" id="emptyResults">
<i class="fas fa-clipboard-list"></i>
<p>处理后的结果将显示在这里</p>
<p>每行显示一个提取的车机号</p>
</div>
</div>
<div id="resultsCount" style="display: none; margin-top: 15px;">
<p><strong>共提取到 <span id="resultCount">0</span> 条数据</strong></p>
</div>
<button id="copyBtn" class="btn btn-copy" disabled>
<i class="far fa-copy"></i> 复制所有结果
</button>
<div class="data-preview" id="dataPreview" style="display: none; margin-top: 20px;">
<h3><i class="fas fa-table"></i> 数据预览(前5行)</h3>
<div id="previewTable"></div>
</div>
</div>
</div>
<footer>
<p>CSV数据筛查工具 © 2023 | 本工具完全在浏览器中运行,不会上传您的数据到服务器</p>
</footer>
</div>
<script>
// DOM元素
const fileInput = document.getElementById('fileInput');
const dropArea = document.getElementById('dropArea');
const processBtn = document.getElementById('processBtn');
const copyBtn = document.getElementById('copyBtn');
const resultsContainer = document.getElementById('resultsContainer');
const emptyResults = document.getElementById('emptyResults');
const selectedFileInfo = document.getElementById('selectedFileInfo');
const fileName = document.getElementById('fileName');
const fileSize = document.getElementById('fileSize');
const dataPreview = document.getElementById('dataPreview');
const previewTable = document.getElementById('previewTable');
const resultsCount = document.getElementById('resultsCount');
const resultCount = document.getElementById('resultCount');
// 步骤指示器
const step1 = document.getElementById('step1');
const step2 = document.getElementById('step2');
const step3 = document.getElementById('step3');
// 存储解析后的数据和结果
let csvData = [];
let extractedResults = [];
// 文件拖放功能
dropArea.addEventListener('click', () => {
fileInput.click();
});
dropArea.addEventListener('dragover', (e) => {
e.preventDefault();
dropArea.style.backgroundColor = '#f0f7ff';
dropArea.style.borderColor = '#2980b9';
});
dropArea.addEventListener('dragleave', () => {
dropArea.style.backgroundColor = '';
dropArea.style.borderColor = '#3498db';
});
dropArea.addEventListener('drop', (e) => {
e.preventDefault();
dropArea.style.backgroundColor = '';
dropArea.style.borderColor = '#3498db';
if (e.dataTransfer.files.length) {
fileInput.files = e.dataTransfer.files;
handleFileSelection(e.dataTransfer.files[0]);
}
});
// 文件选择处理
fileInput.addEventListener('change', (e) => {
if (e.target.files.length) {
handleFileSelection(e.target.files[0]);
}
});
function handleFileSelection(file) {
// 检查文件类型
if (!file.name.toLowerCase().endsWith('.csv')) {
alert('请选择CSV格式的文件');
return;
}
// 更新文件信息显示
fileName.textContent = file.name;
fileSize.textContent = formatFileSize(file.size);
selectedFileInfo.style.display = 'block';
// 更新步骤指示器
step1.classList.add('completed');
step2.classList.remove('completed');
step2.classList.add('active');
step3.classList.remove('active');
step3.classList.remove('completed');
// 读取并解析CSV文件
const reader = new FileReader();
reader.onload = function(e) {
const text = e.target.result;
parseCSV(text);
};
reader.readAsText(file, 'UTF-8');
// 启用处理按钮
processBtn.disabled = false;
}
function formatFileSize(bytes) {
if (bytes < 1024) return bytes + ' bytes';
else if (bytes < 1048576) return (bytes / 1024).toFixed(2) + ' KB';
else return (bytes / 1048576).toFixed(2) + ' MB';
}
// 解析CSV文件
function parseCSV(text) {
// 清空之前的数据
csvData = [];
extractedResults = [];
// 简单的CSV解析(假设使用逗号分隔,没有引号内的逗号)
const lines = text.split('\n');
// 获取标题行(如果有)
const headers = lines[0].split(',').map(header => header.trim());
// 解析数据行
for (let i = 1; i < Math.min(lines.length, 6); i++) { // 只预览前5行数据
if (lines[i].trim() === '') continue;
const values = lines[i].split(',');
const row = {};
headers.forEach((header, index) => {
row[header] = values[index] ? values[index].trim() : '';
});
csvData.push(row);
}
// 显示数据预览
displayDataPreview(headers);
}
// 显示数据预览
function displayDataPreview(headers) {
if (csvData.length === 0) {
dataPreview.style.display = 'none';
return;
}
let tableHTML = '<table>';
// 表头
tableHTML += '<tr>';
headers.forEach(header => {
tableHTML += `<th>${header}</th>`;
});
tableHTML += '</tr>';
// 数据行
csvData.forEach(row => {
tableHTML += '<tr>';
headers.forEach(header => {
tableHTML += `<td>${row[header] || ''}</td>`;
});
tableHTML += '</tr>';
});
tableHTML += '</table>';
previewTable.innerHTML = tableHTML;
dataPreview.style.display = 'block';
// 高亮包含"车机号"的列
const tableCells = previewTable.querySelectorAll('td');
tableCells.forEach(cell => {
if (cell.textContent.includes('车机号')) {
cell.classList.add('highlight-column');
}
});
}
// 处理数据 - 执行类似Excel的MID和FIND函数
function processData() {
// 模拟处理所有数据行(不只是预览的5行)
extractedResults = [];
// 假设我们有更多数据行(为了演示,生成一些模拟数据)
// 在实际应用中,这里应该解析整个CSV文件
const sampleData = [
{ AA: '设备信息:车机号12345678901234567,型号:A001' },
{ AA: '车机号:98765432109876543,状态正常' },
{ AA: '编号:XT001,车机号:55566677788899900,出厂日期:2023-01' },
{ AA: '车机号11122233344455566,使用中' },
{ AA: '车辆信息:VIN:LSVXXXX,车机号:77788899900011122,已激活' },
{ AA: '设备:车机号33344455566677788,在线' },
{ AA: '车机号99900011122233344,型号:B202' },
{ AA: '信息:车机号44455566677788899,版本:2.0' }
];
// 处理每一行数据,执行类似Excel的 =MID(AA2, FIND("车机号", AA2)-1, 17)
sampleData.forEach((row, index) => {
const cellValue = row.AA || '';
const findIndex = cellValue.indexOf('车机号');
if (findIndex !== -1) {
// 注意:Excel的FIND返回的是从1开始的位置,而JavaScript的indexOf返回的是从0开始的位置
// 所以我们需要调整:Excel的FIND("车机号", AA2)-1 对应 JavaScript的 findIndex-1
const startPos = findIndex - 1 >= 0 ? findIndex - 1 : 0;
const extractedValue = cellValue.substr(startPos, 17);
extractedResults.push({
row: index + 2, // Excel行号(从2开始)
original: cellValue,
extracted: extractedValue
});
}
});
// 更新UI显示结果
displayResults();
// 更新步骤指示器
step2.classList.add('completed');
step3.classList.add('active');
}
// 显示提取结果
function displayResults() {
// 清空之前的结果
resultsContainer.innerHTML = '';
emptyResults.style.display = 'none';
if (extractedResults.length === 0) {
emptyResults.style.display = 'block';
resultsCount.style.display = 'none';
copyBtn.disabled = true;
return;
}
// 显示结果计数
resultCount.textContent = extractedResults.length;
resultsCount.style.display = 'block';
// 添加结果项
extractedResults.forEach(result => {
const resultItem = document.createElement('div');
resultItem.className = 'result-item';
resultItem.innerHTML = `
<div>
<div class="result-number">第 ${result.row} 行</div>
<div style="font-size: 0.85rem; color: #7f8c8d; margin-top: 4px;">
${result.original.length > 50 ? result.original.substring(0, 50) + '...' : result.original}
</div>
</div>
<div class="result-value">${result.extracted}</div>
`;
resultsContainer.appendChild(resultItem);
});
// 启用复制按钮
copyBtn.disabled = false;
}
// 复制所有结果到剪贴板
function copyResultsToClipboard() {
if (extractedResults.length === 0) return;
// 将所有提取的值合并为用换行分隔的文本
const resultText = extractedResults.map(r => r.extracted).join('\n');
// 使用现代剪贴板API
navigator.clipboard.writeText(resultText).then(() => {
// 显示复制成功反馈
const originalText = copyBtn.innerHTML;
copyBtn.innerHTML = '<i class="fas fa-check"></i> 已复制到剪贴板';
copyBtn.style.backgroundColor = '#2ecc71';
setTimeout(() => {
copyBtn.innerHTML = originalText;
copyBtn.style.backgroundColor = '#9b59b6';
}, 2000);
}).catch(err => {
console.error('复制失败: ', err);
alert('复制失败,请手动选择复制');
});
}
// 事件监听
processBtn.addEventListener('click', processData);
copyBtn.addEventListener('click', copyResultsToClipboard);
// 初始化:禁用处理按钮,直到选择了文件
processBtn.disabled = true;
copyBtn.disabled = true;
// 添加键盘快捷键支持
document.addEventListener('keydown', (e) => {
// Ctrl+O 打开文件选择
if (e.ctrlKey && e.key === 'o') {
e.preventDefault();
fileInput.click();
}
// Ctrl+P 处理数据
if (e.ctrlKey && e.key === 'p') {
e.preventDefault();
if (!processBtn.disabled) {
processData();
}
}
// Ctrl+C 复制结果
if (e.ctrlKey && e.key === 'c') {
if (!copyBtn.disabled) {
copyResultsToClipboard();
}
}
});
// 添加示例文件下载功能(仅用于演示)
document.addEventListener('DOMContentLoaded', () => {
// 在页面加载完成后,添加示例文件下载链接
const header = document.querySelector('header');
const exampleLink = document.createElement('a');
exampleLink.href = '#';
exampleLink.id = 'downloadExample';
exampleLink.innerHTML = '<i class="fas fa-download"></i> 下载示例CSV文件';
exampleLink.style.cssText = `
display: inline-block;
margin-top: 15px;
padding: 8px 15px;
background-color: #e74c3c;
color: white;
text-decoration: none;
border-radius: 5px;
font-size: 0.9rem;
`;
header.appendChild(exampleLink);
// 点击下载示例文件
document.getElementById('downloadExample').addEventListener('click', function(e) {
e.preventDefault();
// 创建示例CSV内容
const exampleCSV = `序号,设备信息,状态
1,设备信息:车机号12345678901234567,型号:A001,正常
2,车机号:98765432109876543,状态正常,在线
3,编号:XT001,车机号:55566677788899900,出厂日期:2023-01,激活
4,车机号11122233344455566,使用中,离线
5,车辆信息:VIN:LSVXXXX,车机号:77788899900011122,已激活,正常
6,设备:车机号33344455566677788,在线,在线
7,车机号99900011122233344,型号:B202,维修
8,信息:车机号44455566677788899,版本:2.0,正常`;
// 创建Blob和下载链接
const blob = new Blob([exampleCSV], { type: 'text/csv;charset=utf-8;' });
const url = URL.createObjectURL(blob);
const link = document.createElement('a');
link.href = url;
link.download = '示例数据.csv';
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
URL.revokeObjectURL(url);
});
});
</script>
</body>
</html>