2024-06-17 15:52:01 +08:00
|
|
|
|
<!DOCTYPE html>
|
|
|
|
|
{% load static %}
|
|
|
|
|
|
|
|
|
|
<html lang="en">
|
|
|
|
|
|
|
|
|
|
<head>
|
|
|
|
|
<title>Excel解析工具</title>
|
|
|
|
|
<meta charset="utf-8">
|
|
|
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=0, minimal-ui">
|
|
|
|
|
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
|
|
|
|
|
|
|
|
|
<!-- Favicon icon -->
|
|
|
|
|
<link rel="icon" href="{% static 'images/favicon.svg' %}" type="image/x-icon">
|
|
|
|
|
<!-- fontawesome icon -->
|
|
|
|
|
<link rel="stylesheet" href="{% static 'fonts/fontawesome/css/fontawesome-all.min.css' %}">
|
|
|
|
|
<!-- animation css -->
|
|
|
|
|
<link rel="stylesheet" href="{% static 'plugins/animation/css/animate.min.css' %}">
|
|
|
|
|
<!-- vendor css -->
|
|
|
|
|
<link rel="stylesheet" href="{% static 'css/style.css' %}">
|
2024-06-17 21:16:36 +08:00
|
|
|
|
<style>
|
2024-07-03 23:14:08 +08:00
|
|
|
|
body {
|
|
|
|
|
background-color: #f8f9fa;
|
|
|
|
|
}
|
|
|
|
|
.page-wrapper {
|
|
|
|
|
padding: 20px;
|
|
|
|
|
}
|
2024-06-17 21:16:36 +08:00
|
|
|
|
.table-container {
|
|
|
|
|
position: relative;
|
2024-07-03 23:14:08 +08:00
|
|
|
|
max-height: 70vh;
|
2024-06-17 21:16:36 +08:00
|
|
|
|
overflow-y: auto;
|
2024-07-03 23:14:08 +08:00
|
|
|
|
box-shadow: 0 0 20px rgba(0, 0, 0, 0.1);
|
|
|
|
|
border-radius: 10px;
|
|
|
|
|
background-color: #fff;
|
2024-06-17 21:16:36 +08:00
|
|
|
|
}
|
|
|
|
|
table {
|
|
|
|
|
width: 100%;
|
2024-07-03 23:14:08 +08:00
|
|
|
|
border-collapse: separate;
|
|
|
|
|
border-spacing: 0;
|
2024-06-17 21:16:36 +08:00
|
|
|
|
}
|
|
|
|
|
thead th {
|
|
|
|
|
position: sticky;
|
|
|
|
|
top: 0;
|
2024-07-03 23:14:08 +08:00
|
|
|
|
background: #007bff;
|
|
|
|
|
color: #fff;
|
2024-06-17 21:16:36 +08:00
|
|
|
|
z-index: 1;
|
2024-07-03 23:14:08 +08:00
|
|
|
|
padding: 15px;
|
|
|
|
|
text-align: center;
|
2024-06-17 21:16:36 +08:00
|
|
|
|
}
|
|
|
|
|
th, td {
|
2024-07-03 23:14:08 +08:00
|
|
|
|
border: none;
|
|
|
|
|
padding: 12px;
|
|
|
|
|
}
|
|
|
|
|
tbody tr:nth-child(even) {
|
|
|
|
|
background-color: #f8f9fa;
|
2024-06-17 21:16:36 +08:00
|
|
|
|
}
|
2024-07-03 23:14:08 +08:00
|
|
|
|
.form-control {
|
|
|
|
|
border: 1px solid #ced4da;
|
|
|
|
|
border-radius: 5px;
|
|
|
|
|
transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;
|
|
|
|
|
}
|
|
|
|
|
.form-control:focus {
|
|
|
|
|
border-color: #80bdff;
|
|
|
|
|
box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25);
|
2024-06-17 21:16:36 +08:00
|
|
|
|
}
|
2024-06-17 23:40:16 +08:00
|
|
|
|
.error-cell {
|
2024-07-03 23:14:08 +08:00
|
|
|
|
background-color: #f8d7da;
|
|
|
|
|
border-color: #f5c2c7;
|
|
|
|
|
}
|
|
|
|
|
.btn-primary {
|
|
|
|
|
background-color: #007bff;
|
|
|
|
|
border-color: #007bff;
|
|
|
|
|
}
|
|
|
|
|
.btn-primary:hover {
|
|
|
|
|
background-color: #0056b3;
|
|
|
|
|
border-color: #0056b3;
|
|
|
|
|
}
|
|
|
|
|
#alertContainer {
|
|
|
|
|
position: fixed;
|
|
|
|
|
top: 20px;
|
|
|
|
|
right: 20px;
|
|
|
|
|
z-index: 9999;
|
|
|
|
|
width: 400px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.dynamic-width {
|
|
|
|
|
transition: width 0.3s ease;
|
|
|
|
|
min-width: 50px;
|
|
|
|
|
max-width: 300px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#width-test {
|
|
|
|
|
position: absolute;
|
|
|
|
|
visibility: hidden;
|
|
|
|
|
height: auto;
|
|
|
|
|
width: auto;
|
|
|
|
|
white-space: nowrap;
|
2024-06-17 23:40:16 +08:00
|
|
|
|
}
|
2024-06-17 21:16:36 +08:00
|
|
|
|
</style>
|
2024-06-17 15:52:01 +08:00
|
|
|
|
</head>
|
|
|
|
|
|
2024-06-17 20:52:07 +08:00
|
|
|
|
<body>
|
2024-07-03 23:14:08 +08:00
|
|
|
|
<div id="alertContainer"></div>
|
|
|
|
|
<div class="page-wrapper">
|
|
|
|
|
<form id="preview-form" method="post">
|
|
|
|
|
{% csrf_token %}
|
|
|
|
|
<div class="container">
|
|
|
|
|
<div class="row mb-4">
|
|
|
|
|
<div class="col">
|
|
|
|
|
<h2 class="text-primary"><i class="fas fa-file-excel me-2"></i>上传文件预览</h2>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="col text-end">
|
|
|
|
|
<button type="button" class="btn btn-outline-secondary" onclick="history.back();">
|
|
|
|
|
<i class="fas fa-arrow-left me-2"></i>返回
|
|
|
|
|
</button>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="table-container animate__animated animate__fadeIn">
|
|
|
|
|
<table id="form-input-table" class="table table-hover">
|
|
|
|
|
<thead>
|
2024-06-17 20:52:07 +08:00
|
|
|
|
<tr>
|
2024-07-03 23:14:08 +08:00
|
|
|
|
<th>序号</th>
|
|
|
|
|
{% for column in columns %}
|
|
|
|
|
<th>{{ column }}</th>
|
2024-06-17 20:52:07 +08:00
|
|
|
|
{% endfor %}
|
|
|
|
|
</tr>
|
2024-07-03 23:14:08 +08:00
|
|
|
|
</thead>
|
|
|
|
|
<tbody>
|
|
|
|
|
{% for row in table_data %}
|
|
|
|
|
<tr>
|
|
|
|
|
<td>
|
|
|
|
|
<input type="text" class="form-control text-center" style="width: 50px" value="{{ forloop.counter }}" disabled>
|
|
|
|
|
</td>
|
|
|
|
|
{% for cell in row %}
|
|
|
|
|
<td>
|
|
|
|
|
<input type="text" class="form-control dynamic-width" name="cell_{{ forloop.parentloop.counter0 }}_{{ forloop.counter0 }}" value="{{ cell|default_if_none:'-' }}">
|
|
|
|
|
</td>
|
|
|
|
|
{% endfor %}
|
|
|
|
|
</tr>
|
|
|
|
|
{% endfor %}
|
|
|
|
|
</tbody>
|
|
|
|
|
</table>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="row mt-4">
|
|
|
|
|
<div class="col">
|
|
|
|
|
<span id="totalData" class="text-muted">数据总数: {{ table_data|length }}</span>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="col text-end" id="actions">
|
|
|
|
|
<button id="saveButton" type="button" class="btn btn-primary">
|
|
|
|
|
<i class="fas fa-save me-2"></i>保存
|
|
|
|
|
</button>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
2024-06-17 20:52:07 +08:00
|
|
|
|
</div>
|
2024-07-03 23:14:08 +08:00
|
|
|
|
</form>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div id="width-test"></div>
|
2024-06-17 15:52:01 +08:00
|
|
|
|
|
|
|
|
|
<script src="{% static 'js/vendor-all.min.js' %}"></script>
|
|
|
|
|
<script src="{% static 'plugins/bootstrap/js/bootstrap.min.js' %}"></script>
|
2024-06-17 23:40:16 +08:00
|
|
|
|
<script src="{% static 'js/custom.js' %}"></script>
|
|
|
|
|
<script>
|
2024-07-03 23:14:08 +08:00
|
|
|
|
|
|
|
|
|
function adjustInputWidth(input) {
|
|
|
|
|
const widthTest = document.getElementById('width-test');
|
|
|
|
|
const minWidth = 100; // 最小宽度(像素)
|
|
|
|
|
const maxWidth = 300; // 最大宽度(像素)
|
|
|
|
|
const padding = 20; // 输入框的内边距(左右各10px)
|
|
|
|
|
|
|
|
|
|
// 设置与输入框相同的字体样式
|
|
|
|
|
const inputStyle = window.getComputedStyle(input);
|
|
|
|
|
widthTest.style.font = inputStyle.font;
|
|
|
|
|
widthTest.style.fontSize = inputStyle.fontSize;
|
|
|
|
|
widthTest.style.fontWeight = inputStyle.fontWeight;
|
|
|
|
|
widthTest.style.fontFamily = inputStyle.fontFamily;
|
|
|
|
|
widthTest.style.letterSpacing = inputStyle.letterSpacing;
|
|
|
|
|
|
|
|
|
|
// 测量文本宽度
|
|
|
|
|
widthTest.textContent = input.value || input.placeholder;
|
|
|
|
|
let textWidth = widthTest.offsetWidth;
|
|
|
|
|
|
|
|
|
|
// 设置输入框宽度
|
|
|
|
|
let width = Math.max(minWidth, Math.min(textWidth + padding, maxWidth));
|
|
|
|
|
input.style.width = `${width}px`;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function adjustAllInputWidths() {
|
|
|
|
|
const inputs = document.querySelectorAll('.dynamic-width');
|
|
|
|
|
inputs.forEach(adjustInputWidth);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 页面加载时调用函数
|
|
|
|
|
document.addEventListener('DOMContentLoaded', function () {
|
|
|
|
|
adjustAllInputWidths();
|
|
|
|
|
|
|
|
|
|
// 为所有输入框添加输入事件监听器
|
|
|
|
|
document.querySelectorAll('.dynamic-width').forEach(input => {
|
|
|
|
|
input.addEventListener('input', function () {
|
|
|
|
|
adjustInputWidth(this);
|
|
|
|
|
});
|
|
|
|
|
});
|
2024-06-17 23:40:16 +08:00
|
|
|
|
});
|
|
|
|
|
|
2024-07-03 23:14:08 +08:00
|
|
|
|
document.getElementById('saveButton').addEventListener('click', function () {
|
|
|
|
|
const table = document.getElementById('form-input-table');
|
|
|
|
|
const headers = Array.from(table.querySelectorAll('thead th')).map(th => th.textContent.trim()).slice(1); // 跳过"序号"列
|
|
|
|
|
const data = [];
|
|
|
|
|
|
|
|
|
|
// 清除之前的错误标记
|
|
|
|
|
const inputs = table.querySelectorAll('input');
|
|
|
|
|
inputs.forEach(input => {
|
|
|
|
|
input.classList.remove('is-invalid');
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
for (let i = 0; i < table.tBodies[0].rows.length; i++) {
|
|
|
|
|
const row = table.tBodies[0].rows[i];
|
|
|
|
|
const rowData = {};
|
|
|
|
|
for (let j = 1; j < row.cells.length; j++) { // 跳过序号列
|
|
|
|
|
rowData[headers[j - 1]] = row.cells[j].querySelector('input').value;
|
|
|
|
|
}
|
|
|
|
|
data.push(rowData);
|
2024-06-17 23:40:16 +08:00
|
|
|
|
}
|
|
|
|
|
|
2024-07-03 23:14:08 +08:00
|
|
|
|
fetch("{% url 'ep_common_save' %}", {
|
|
|
|
|
method: 'POST',
|
|
|
|
|
headers: {
|
|
|
|
|
'Content-Type': 'application/json',
|
|
|
|
|
'X-CSRFToken': document.querySelector('[name=csrfmiddlewaretoken]').value
|
|
|
|
|
},
|
|
|
|
|
body: JSON.stringify({data: data})
|
|
|
|
|
})
|
|
|
|
|
.then(response => response.json())
|
|
|
|
|
.then(result => {
|
|
|
|
|
if (result.status === 'success') {
|
|
|
|
|
showAlert('success', result.message);
|
|
|
|
|
setTimeout(() => {
|
|
|
|
|
window.location.href = result.redirect_url;
|
|
|
|
|
}, 2000);
|
|
|
|
|
} else {
|
|
|
|
|
const errorMessage = result.message + '<br>' + result.errors.join('<br>');
|
|
|
|
|
showAlert('error', errorMessage);
|
|
|
|
|
result.errors.forEach(error => {
|
|
|
|
|
const regex = /第【(\d+)】行【(.+?)】字段输入错误/;
|
|
|
|
|
const matches = error.match(regex);
|
|
|
|
|
if (matches) {
|
|
|
|
|
const row = parseInt(matches[1], 10);
|
|
|
|
|
const field = matches[2];
|
|
|
|
|
const column = headers.findIndex(header => header.trim() === field.trim()) + 1; // +1 因为我们跳过了序号列
|
|
|
|
|
|
|
|
|
|
if (column !== -1 && table.tBodies[0].rows[row - 1] && table.tBodies[0].rows[row - 1].cells[column]) {
|
|
|
|
|
table.tBodies[0].rows[row - 1].cells[column].querySelector('input').classList.add('is-invalid');
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
});
|
2024-06-17 23:40:16 +08:00
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
});
|
2024-07-03 23:14:08 +08:00
|
|
|
|
|
2024-06-17 23:40:16 +08:00
|
|
|
|
</script>
|
2024-06-17 15:52:01 +08:00
|
|
|
|
|
2024-06-17 20:52:07 +08:00
|
|
|
|
</body>
|
2024-06-17 15:52:01 +08:00
|
|
|
|
</html>
|