Files
my/excel_tool_12313.html
2025-11-25 01:36:08 +00:00

629 lines
23 KiB
HTML
Raw Blame History

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