629 lines
23 KiB
HTML
629 lines
23 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="zh-CN">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<title>Excel 数据处理工具</title>
|
||
<link href="https://code.soao.net/go/my/myfile/bootstrap.min.css" rel="stylesheet">
|
||
<link href="https://code.soao.net/go/my/myfile/bootstrap-icons.css" rel="stylesheet">
|
||
<style>
|
||
* {
|
||
margin: 0;
|
||
padding: 0;
|
||
box-sizing: border-box;
|
||
}
|
||
|
||
body {
|
||
font-family: 'Microsoft YaHei', '微软雅黑', Arial, sans-serif;
|
||
background: linear-gradient(135deg, #1a1a1a 0%, #2d2d30 100%);
|
||
min-height: 100vh;
|
||
padding: 40px 20px;
|
||
color: #e9ecef;
|
||
}
|
||
|
||
.main-container {
|
||
background: rgba(40, 40, 40, 0.95);
|
||
border-radius: 20px;
|
||
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.3);
|
||
backdrop-filter: blur(10px);
|
||
padding: 40px;
|
||
margin: 40px auto;
|
||
max-width: 700px;
|
||
border: 1px solid rgba(255, 255, 255, 0.1);
|
||
}
|
||
|
||
.header {
|
||
text-align: center;
|
||
margin-bottom: 40px;
|
||
}
|
||
|
||
.header h1 {
|
||
color: #ffffff;
|
||
font-size: 2.5rem;
|
||
font-weight: 700;
|
||
margin-bottom: 10px;
|
||
background: linear-gradient(135deg, #00d4aa, #4ecdc4);
|
||
-webkit-background-clip: text;
|
||
-webkit-text-fill-color: transparent;
|
||
background-clip: text;
|
||
}
|
||
|
||
.header p {
|
||
color: #adb5bd;
|
||
font-size: 1.1rem;
|
||
}
|
||
|
||
.upload-area {
|
||
background: linear-gradient(135deg, #2a2a2a 0%, #1e1e1e 100%);
|
||
border: 3px dashed #00d4aa;
|
||
border-radius: 15px;
|
||
padding: 40px;
|
||
text-align: center;
|
||
margin-bottom: 30px;
|
||
transition: all 0.3s ease;
|
||
cursor: pointer;
|
||
position: relative;
|
||
overflow: hidden;
|
||
}
|
||
|
||
.upload-area:hover {
|
||
border-color: #4ecdc4;
|
||
background: linear-gradient(135deg, #333333 0%, #2a2a2a 100%);
|
||
transform: translateY(-2px);
|
||
box-shadow: 0 10px 20px rgba(0, 212, 170, 0.2);
|
||
}
|
||
|
||
.upload-area.dragover {
|
||
border-color: #4ecdc4;
|
||
background: linear-gradient(135deg, #2a2a2a 0%, #333333 100%);
|
||
transform: scale(1.02);
|
||
}
|
||
|
||
.upload-icon {
|
||
font-size: 4rem;
|
||
color: #00d4aa;
|
||
margin-bottom: 20px;
|
||
display: block;
|
||
}
|
||
|
||
.upload-text {
|
||
font-size: 1.2rem;
|
||
color: #ffffff;
|
||
font-weight: 600;
|
||
margin-bottom: 10px;
|
||
}
|
||
|
||
.upload-subtitle {
|
||
color: #adb5bd;
|
||
font-size: 0.95rem;
|
||
}
|
||
|
||
.file-input {
|
||
display: none;
|
||
}
|
||
|
||
.form-group {
|
||
margin-bottom: 25px;
|
||
}
|
||
|
||
.form-label {
|
||
font-weight: 600;
|
||
color: #ffffff;
|
||
margin-bottom: 10px;
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 8px;
|
||
}
|
||
|
||
.form-control, .form-select {
|
||
border: 2px solid #495057;
|
||
border-radius: 10px;
|
||
padding: 12px 16px;
|
||
font-size: 1rem;
|
||
transition: all 0.3s ease;
|
||
background: #2d2d30;
|
||
color: #ffffff;
|
||
}
|
||
|
||
.form-control:focus, .form-select:focus {
|
||
border-color: #00d4aa;
|
||
box-shadow: 0 0 0 0.2rem rgba(0, 212, 170, 0.25);
|
||
outline: none;
|
||
background: #2d2d30;
|
||
color: #ffffff;
|
||
}
|
||
|
||
.btn-primary {
|
||
background: linear-gradient(135deg, #00d4aa 0%, #4ecdc4 100%);
|
||
border: none;
|
||
border-radius: 10px;
|
||
padding: 12px 30px;
|
||
font-size: 1.1rem;
|
||
font-weight: 600;
|
||
color: #1a1a1a;
|
||
transition: all 0.3s ease;
|
||
width: 100%;
|
||
margin-top: 20px;
|
||
}
|
||
|
||
.btn-primary:hover:not(:disabled) {
|
||
transform: translateY(-2px);
|
||
box-shadow: 0 8px 15px rgba(0, 212, 170, 0.4);
|
||
background: linear-gradient(135deg, #00c29a 0%, #45c5b8 100%);
|
||
}
|
||
|
||
.btn-primary:disabled {
|
||
opacity: 0.6;
|
||
cursor: not-allowed;
|
||
transform: none;
|
||
}
|
||
|
||
.progress-container {
|
||
position: relative;
|
||
width: 100%;
|
||
height: 25px;
|
||
background: linear-gradient(90deg, #2d2d30, #404040);
|
||
border-radius: 15px;
|
||
overflow: hidden;
|
||
margin-top: 15px;
|
||
box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.3);
|
||
}
|
||
|
||
.progress-bar {
|
||
height: 100%;
|
||
width: 0;
|
||
background: linear-gradient(90deg, #00d4aa, #4ecdc4);
|
||
border-radius: 15px;
|
||
transition: width 0.3s ease-in-out;
|
||
position: relative;
|
||
}
|
||
|
||
.progress-bar::after {
|
||
content: '';
|
||
position: absolute;
|
||
top: 0;
|
||
left: 0;
|
||
bottom: 0;
|
||
right: 0;
|
||
background-image: linear-gradient(
|
||
-45deg,
|
||
rgba(255, 255, 255, .2) 25%,
|
||
transparent 25%,
|
||
transparent 50%,
|
||
rgba(255, 255, 255, .2) 50%,
|
||
rgba(255, 255, 255, .2) 75%,
|
||
transparent 75%,
|
||
transparent
|
||
);
|
||
background-size: 50px 50px;
|
||
animation: move 2s linear infinite;
|
||
}
|
||
|
||
@keyframes move {
|
||
0% {
|
||
background-position: 0 0;
|
||
}
|
||
100% {
|
||
background-position: 50px 50px;
|
||
}
|
||
}
|
||
|
||
.progress-text {
|
||
position: absolute;
|
||
top: 50%;
|
||
left: 50%;
|
||
transform: translate(-50%, -50%);
|
||
font-size: 14px;
|
||
color: white;
|
||
font-weight: bold;
|
||
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.3);
|
||
z-index: 1;
|
||
}
|
||
|
||
.status-message {
|
||
text-align: center;
|
||
margin-top: 15px;
|
||
padding: 10px;
|
||
border-radius: 8px;
|
||
font-weight: 500;
|
||
display: none;
|
||
}
|
||
|
||
.status-message.success {
|
||
background: linear-gradient(135deg, #1a3a2e, #2a4a3e);
|
||
color: #4ecdc4;
|
||
border: 1px solid #00d4aa;
|
||
}
|
||
|
||
.status-message.error {
|
||
background: linear-gradient(135deg, #3a1a1a, #4a2a2a);
|
||
color: #ff6b6b;
|
||
border: 1px solid #ff4757;
|
||
}
|
||
|
||
.status-message.info {
|
||
background: linear-gradient(135deg, #1a2332, #2a3441);
|
||
color: #4ecdc4;
|
||
border: 1px solid #00d4aa;
|
||
}
|
||
|
||
.file-info {
|
||
background: linear-gradient(135deg, #1a2332, #2a3441);
|
||
border: 1px solid #00d4aa;
|
||
border-radius: 10px;
|
||
padding: 15px;
|
||
margin-top: 20px;
|
||
display: none;
|
||
}
|
||
|
||
.file-info-content {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 10px;
|
||
color: #00d4aa;
|
||
font-weight: 500;
|
||
}
|
||
|
||
@media (max-width: 768px) {
|
||
.main-container {
|
||
padding: 20px;
|
||
margin-top: 10px;
|
||
}
|
||
|
||
.header h1 {
|
||
font-size: 2rem;
|
||
}
|
||
|
||
.upload-area {
|
||
padding: 30px 20px;
|
||
}
|
||
|
||
.upload-icon {
|
||
font-size: 3rem;
|
||
}
|
||
}
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<div class="container">
|
||
<div class="row justify-content-center">
|
||
<div class="col-lg-7 col-md-8 col-sm-10">
|
||
<div class="main-container">
|
||
<div class="header">
|
||
<h1><i class="bi bi-table"></i> Excel 数据处理工具</h1>
|
||
<p>12313平台专用</p>
|
||
</div>
|
||
|
||
<div class="upload-area" id="uploadArea">
|
||
<input type="file" class="file-input" id="fileInput" accept=".xlsx, .xls, .csv">
|
||
<i class="bi bi-cloud-upload upload-icon"></i>
|
||
<div class="upload-text">点击上传或拖拽文件到此处</div>
|
||
<div class="upload-subtitle">支持 .xlsx, .xls, .csv 格式文件</div>
|
||
</div>
|
||
|
||
<div class="file-info" id="fileInfo">
|
||
<div class="file-info-content">
|
||
<i class="bi bi-file-earmark-text"></i>
|
||
<span id="fileName"></span>
|
||
<span id="fileSize" class="ms-auto"></span>
|
||
</div>
|
||
</div>
|
||
|
||
<form id="processForm">
|
||
<div class="form-group">
|
||
<label for="sheetSelect" class="form-label">
|
||
<i class="bi bi-list-ul"></i>
|
||
选择工作表
|
||
</label>
|
||
<select class="form-select" id="sheetSelect" disabled>
|
||
<option value="">请先上传文件</option>
|
||
</select>
|
||
</div>
|
||
|
||
<button type="button" class="btn btn-primary" id="processButton" disabled>
|
||
<i class="bi bi-play-circle"></i>
|
||
开始处理数据
|
||
</button>
|
||
</form>
|
||
|
||
<div class="progress-section">
|
||
<div class="progress-container" id="progressContainer" style="display: none;">
|
||
<div class="progress-bar" id="progress-bar"></div>
|
||
<span class="progress-text" id="progress-text">0%</span>
|
||
</div>
|
||
<div class="status-message" id="statusMessage"></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<script src="https://code.soao.net/go/my/myfile/xlsx.full.min.js"></script>
|
||
<script>
|
||
document.addEventListener('DOMContentLoaded', function() {
|
||
if (typeof XLSX === 'undefined') {
|
||
alert('XLSX库未加载!请确保xlsx.full.min.js文件存在。');
|
||
return;
|
||
}
|
||
|
||
const fileInput = document.getElementById('fileInput');
|
||
const sheetSelect = document.getElementById('sheetSelect');
|
||
const processButton = document.getElementById('processButton');
|
||
const progressBar = document.getElementById('progress-bar');
|
||
const progressText = document.getElementById('progress-text');
|
||
const progressContainer = document.getElementById('progressContainer');
|
||
const statusMessage = document.getElementById('statusMessage');
|
||
const uploadArea = document.getElementById('uploadArea');
|
||
const fileInfo = document.getElementById('fileInfo');
|
||
const fileName = document.getElementById('fileName');
|
||
const fileSize = document.getElementById('fileSize');
|
||
|
||
let currentWorkbook = null;
|
||
let currentFile = null;
|
||
|
||
// 文件上传
|
||
uploadArea.addEventListener('click', () => fileInput.click());
|
||
|
||
uploadArea.addEventListener('dragover', (e) => {
|
||
e.preventDefault();
|
||
uploadArea.classList.add('dragover');
|
||
});
|
||
|
||
uploadArea.addEventListener('dragleave', () => uploadArea.classList.remove('dragover'));
|
||
|
||
uploadArea.addEventListener('drop', (e) => {
|
||
e.preventDefault();
|
||
uploadArea.classList.remove('dragover');
|
||
const files = e.dataTransfer.files;
|
||
if (files.length > 0) handleFileSelect(files[0]);
|
||
});
|
||
|
||
fileInput.addEventListener('change', function(event) {
|
||
const file = event.target.files[0];
|
||
if (file) handleFileSelect(file);
|
||
});
|
||
|
||
function handleFileSelect(file) {
|
||
const fileExtension = file.name.toLowerCase().split('.').pop();
|
||
if (!['xlsx', 'xls', 'csv'].includes(fileExtension)) {
|
||
showStatus('error', '文件格式不支持!请上传 .xlsx, .xls 或 .csv 格式的文件。');
|
||
resetForm();
|
||
return;
|
||
}
|
||
|
||
if (file.size > 50 * 1024 * 1024) {
|
||
showStatus('error', '文件过大!请上传小于50MB的文件。');
|
||
resetForm();
|
||
return;
|
||
}
|
||
|
||
currentFile = file;
|
||
showFileInfo(file);
|
||
readFile(file);
|
||
}
|
||
|
||
function showFileInfo(file) {
|
||
fileName.textContent = file.name;
|
||
fileSize.textContent = formatFileSize(file.size);
|
||
fileInfo.style.display = 'block';
|
||
}
|
||
|
||
function formatFileSize(bytes) {
|
||
if (bytes === 0) return '0 Bytes';
|
||
const k = 1024;
|
||
const sizes = ['Bytes', 'KB', 'MB', 'GB'];
|
||
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
||
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
|
||
}
|
||
|
||
function readFile(file) {
|
||
showStatus('info', '正在读取文件...');
|
||
|
||
const reader = new FileReader();
|
||
reader.onload = function(e) {
|
||
try {
|
||
const data = e.target.result;
|
||
currentWorkbook = XLSX.read(data, { type: 'array' });
|
||
populateSheetSelect();
|
||
showStatus('success', '文件读取成功!请选择要处理的工作表。');
|
||
} catch (error) {
|
||
showStatus('error', '文件读取失败!请检查文件格式是否正确。');
|
||
resetForm();
|
||
}
|
||
};
|
||
reader.readAsArrayBuffer(file);
|
||
}
|
||
|
||
function populateSheetSelect() {
|
||
sheetSelect.innerHTML = '';
|
||
|
||
if (currentWorkbook.SheetNames.length === 0) {
|
||
showStatus('error', '未找到有效的工作表!');
|
||
return;
|
||
}
|
||
|
||
const defaultOption = document.createElement('option');
|
||
defaultOption.value = '';
|
||
defaultOption.textContent = '请选择工作表';
|
||
sheetSelect.appendChild(defaultOption);
|
||
|
||
currentWorkbook.SheetNames.forEach(sheetName => {
|
||
const option = document.createElement('option');
|
||
option.value = sheetName;
|
||
option.textContent = sheetName;
|
||
sheetSelect.appendChild(option);
|
||
});
|
||
|
||
sheetSelect.disabled = false;
|
||
processButton.disabled = false;
|
||
}
|
||
|
||
processButton.addEventListener('click', function() {
|
||
if (!currentFile || !sheetSelect.value) {
|
||
showStatus('error', '请先选择文件和工作表!');
|
||
return;
|
||
}
|
||
processData();
|
||
});
|
||
|
||
function processData() {
|
||
const sheetName = sheetSelect.value;
|
||
|
||
progressContainer.style.display = 'block';
|
||
processButton.disabled = true;
|
||
showStatus('info', '开始处理数据...');
|
||
|
||
progressBar.style.width = '0%';
|
||
progressText.textContent = '0%';
|
||
|
||
const reader = new FileReader();
|
||
reader.onload = function(e) {
|
||
try {
|
||
const data = e.target.result;
|
||
const workbook = XLSX.read(data, { type: 'array' });
|
||
const worksheet = workbook.Sheets[sheetName];
|
||
const jsonData = XLSX.utils.sheet_to_json(worksheet, { header: 1 });
|
||
|
||
if (jsonData.length === 0) {
|
||
throw new Error('工作表为空');
|
||
}
|
||
|
||
const colList = [2, 5, 6, 7, 8, 9, 11, 12, 13, 16, 18, 19, 20, 22, 27, 28, 29, 30, 31, 40, 51];
|
||
const insertProvince = "陕西省";
|
||
const insertCity = "宝鸡市";
|
||
|
||
const processedData = [];
|
||
const headers = jsonData[0];
|
||
const newHeaders = [];
|
||
|
||
headers.forEach((header, colIndex) => {
|
||
if (colList.includes(colIndex + 1)) {
|
||
if (colIndex + 1 === 16) {
|
||
newHeaders.push(header + "1(分类1)");
|
||
newHeaders.push(header + "2(分类2)");
|
||
newHeaders.push(header + "3(分类3)");
|
||
} else if (colIndex + 1 === 18) {
|
||
newHeaders.push("省");
|
||
newHeaders.push("市");
|
||
newHeaders.push(header);
|
||
} else {
|
||
newHeaders.push(header);
|
||
}
|
||
}
|
||
});
|
||
|
||
processedData.push(newHeaders);
|
||
|
||
const totalRows = jsonData.length - 1;
|
||
let processedRows = 0;
|
||
|
||
const processBatch = (startIndex, batchSize = 100) => {
|
||
const endIndex = Math.min(startIndex + batchSize, jsonData.length);
|
||
|
||
for (let i = startIndex; i < endIndex; i++) {
|
||
if (i === 0) continue;
|
||
|
||
const row = jsonData[i];
|
||
const newRow = [];
|
||
|
||
colList.forEach(colIndex => {
|
||
if (colIndex === 16) {
|
||
const splitData = (row[colIndex - 1] || '').split('-');
|
||
const paddedSplitData = splitData.concat(new Array(3 - splitData.length).fill(''));
|
||
newRow.push(...paddedSplitData);
|
||
} else if (colIndex === 18) {
|
||
newRow.push(insertProvince);
|
||
newRow.push(insertCity);
|
||
newRow.push(row[colIndex - 1]);
|
||
} else if (colIndex === 9 || colIndex === 27) {
|
||
if (typeof row[colIndex - 1] === 'number') {
|
||
const date = new Date(Math.round((row[colIndex - 1] - 25569) * 86400000));
|
||
newRow.push(date.toISOString().split('T')[0] + ' ' + date.toTimeString().split(' ')[0]);
|
||
} else {
|
||
newRow.push(row[colIndex - 1]);
|
||
}
|
||
} else {
|
||
newRow.push(row[colIndex - 1] === "陕西省12313分中心" ? "12313" : row[colIndex - 1]);
|
||
}
|
||
});
|
||
|
||
processedData.push(newRow);
|
||
}
|
||
|
||
processedRows += (endIndex - startIndex - 1);
|
||
|
||
const percentage = Math.round((processedRows / totalRows) * 100);
|
||
progressBar.style.width = `${percentage}%`;
|
||
progressText.textContent = `${percentage}%`;
|
||
|
||
if (endIndex < jsonData.length) {
|
||
setTimeout(() => processBatch(endIndex, batchSize), 10);
|
||
} else {
|
||
finalizeProcessing(processedData);
|
||
}
|
||
};
|
||
|
||
processBatch(0);
|
||
|
||
} catch (error) {
|
||
showStatus('error', '数据处理失败!' + error.message);
|
||
resetProgress();
|
||
}
|
||
};
|
||
|
||
reader.readAsArrayBuffer(currentFile);
|
||
}
|
||
|
||
function finalizeProcessing(processedData) {
|
||
try {
|
||
const newWorksheet = XLSX.utils.aoa_to_sheet(processedData);
|
||
const newWorkbook = XLSX.utils.book_new();
|
||
XLSX.utils.book_append_sheet(newWorkbook, newWorksheet, 'ProcessedData');
|
||
|
||
const timestamp = new Date().toLocaleString().replace(/[/:]/g, '-');
|
||
const fileName = `ProcessedData_${timestamp}.xlsx`;
|
||
XLSX.writeFile(newWorkbook, fileName);
|
||
|
||
progressBar.style.width = '100%';
|
||
progressText.textContent = '100% 完成';
|
||
showStatus('success', `数据处理完成!文件已保存为 ${fileName}`);
|
||
|
||
setTimeout(() => {
|
||
hideProgress();
|
||
resetForm();
|
||
}, 3000);
|
||
|
||
} catch (error) {
|
||
showStatus('error', '文件保存失败!请重试。');
|
||
resetProgress();
|
||
}
|
||
}
|
||
|
||
function showStatus(type, message) {
|
||
statusMessage.className = `status-message ${type}`;
|
||
statusMessage.textContent = message;
|
||
statusMessage.style.display = 'block';
|
||
}
|
||
|
||
function hideProgress() {
|
||
progressContainer.style.display = 'none';
|
||
statusMessage.style.display = 'none';
|
||
}
|
||
|
||
function resetProgress() {
|
||
hideProgress();
|
||
processButton.disabled = false;
|
||
}
|
||
|
||
function resetForm() {
|
||
fileInput.value = '';
|
||
sheetSelect.innerHTML = '<option value="">请先上传文件</option>';
|
||
sheetSelect.disabled = true;
|
||
processButton.disabled = true;
|
||
fileInfo.style.display = 'none';
|
||
hideProgress();
|
||
}
|
||
});
|
||
</script>
|
||
</body>
|
||
</html> |