上传文件至「/」

This commit is contained in:
lc
2025-11-25 01:36:08 +00:00
parent 18f234d377
commit 502db2feae

629
excel_tool_12313.html Normal file
View 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>