上传文件至「/」
This commit is contained in:
629
excel_tool_12313.html
Normal file
629
excel_tool_12313.html
Normal file
@@ -0,0 +1,629 @@
|
||||
<!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>
|
||||
Reference in New Issue
Block a user