271 lines
12 KiB
PHP
271 lines
12 KiB
PHP
@extends('layouts.app')
|
||
|
||
@section('title', '导入数据 - 病例回访提醒系统')
|
||
|
||
@section('content')
|
||
<div class="page-header">
|
||
<h1 class="page-title">导入患者数据</h1>
|
||
<p class="page-subtitle">支持 Excel (.xlsx) 和 CSV 格式的文件导入</p>
|
||
</div>
|
||
|
||
<!-- .xls 格式提示 -->
|
||
<div class="card" style="background: linear-gradient(135deg, #fffbeb 0%, #fef3c7 100%); border-color: #f59e0b;">
|
||
<div style="display: flex; gap: 14px; align-items: flex-start;">
|
||
<span style="font-size: 28px;">⚠️</span>
|
||
<div>
|
||
<div style="font-weight: 600; color: #b45309; margin-bottom: 8px;">关于 .xls 格式(Excel 97-2003)</div>
|
||
<div style="color: #92400e; font-size: 14px; line-height: 1.8;">
|
||
如果你的文件是 <strong>.xls 格式</strong>,请先转换为新格式:<br>
|
||
1. 在 Excel 中打开 .xls 文件<br>
|
||
2. 点击 <strong>文件 → 另存为</strong><br>
|
||
3. 选择 <strong>"Excel 工作簿 (*.xlsx)"</strong> 或 <strong>"CSV UTF-8"</strong><br>
|
||
4. 保存后重新上传
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="card">
|
||
<div class="card-header">
|
||
<span class="card-title">上传文件</span>
|
||
<a href="{{ route('patients.template') }}" class="btn btn-outline btn-sm">
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||
<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/>
|
||
<polyline points="7 10 12 15 17 10"/>
|
||
<line x1="12" y1="15" x2="12" y2="3"/>
|
||
</svg>
|
||
下载导入模板 (Excel)
|
||
</a>
|
||
</div>
|
||
|
||
<form action="{{ route('patients.import.store') }}" method="POST" enctype="multipart/form-data" id="importForm">
|
||
@csrf
|
||
|
||
<div class="file-upload" id="dropZone">
|
||
<svg class="file-upload-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||
<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/>
|
||
<polyline points="17 8 12 3 7 8"/>
|
||
<line x1="12" y1="3" x2="12" y2="15"/>
|
||
</svg>
|
||
<div class="file-upload-text">点击或拖拽文件到此处上传</div>
|
||
<div class="file-upload-hint">支持 .xlsx 和 .csv 格式,最大 10MB</div>
|
||
<input type="file" name="file" id="fileInput" accept=".xlsx,.csv" required>
|
||
</div>
|
||
|
||
<div id="fileInfo" style="display: none; margin-top: 20px; padding: 16px; background: var(--color-bg-secondary); border-radius: var(--radius-sm); border: 1px solid var(--color-border);">
|
||
<div style="display: flex; align-items: center; gap: 12px;">
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="24" height="24" style="color: var(--color-success);">
|
||
<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/>
|
||
<polyline points="14 2 14 8 20 8"/>
|
||
<line x1="16" y1="13" x2="8" y2="13"/>
|
||
<line x1="16" y1="17" x2="8" y2="17"/>
|
||
<polyline points="10 9 9 9 8 9"/>
|
||
</svg>
|
||
<div>
|
||
<div id="fileName" style="font-weight: 500;"></div>
|
||
<div id="fileSize" style="font-size: 13px; color: var(--color-text-muted);"></div>
|
||
</div>
|
||
<button type="button" id="removeFile" style="margin-left: auto; background: none; border: none; cursor: pointer; color: var(--color-text-muted);">
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="20" height="20">
|
||
<line x1="18" y1="6" x2="6" y2="18"/>
|
||
<line x1="6" y1="6" x2="18" y2="18"/>
|
||
</svg>
|
||
</button>
|
||
</div>
|
||
</div>
|
||
|
||
<div style="margin-top: 24px;">
|
||
<button type="submit" class="btn btn-primary" id="submitBtn" disabled>
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||
<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/>
|
||
<polyline points="17 8 12 3 7 8"/>
|
||
<line x1="12" y1="3" x2="12" y2="15"/>
|
||
</svg>
|
||
开始导入
|
||
</button>
|
||
</div>
|
||
</form>
|
||
</div>
|
||
|
||
<!-- 导入说明 -->
|
||
<div class="card">
|
||
<div class="card-header">
|
||
<span class="card-title">📋 导入说明</span>
|
||
</div>
|
||
|
||
<div style="line-height: 1.8;">
|
||
<h4 style="margin-bottom: 12px; color: var(--color-text);">文件格式要求</h4>
|
||
<p style="color: var(--color-text-secondary); margin-bottom: 16px;">
|
||
Excel 或 CSV 文件需按以下列顺序排列:
|
||
</p>
|
||
|
||
<div class="table-container" style="margin-bottom: 24px;">
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>列序号</th>
|
||
<th>字段名称</th>
|
||
<th>说明</th>
|
||
<th>必填</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td>1</td>
|
||
<td>姓名</td>
|
||
<td>患者姓名</td>
|
||
<td><span class="badge badge-danger">是</span></td>
|
||
</tr>
|
||
<tr>
|
||
<td>2</td>
|
||
<td>性别</td>
|
||
<td>男/女</td>
|
||
<td><span class="badge badge-danger">是</span></td>
|
||
</tr>
|
||
<tr>
|
||
<td>3</td>
|
||
<td>年龄</td>
|
||
<td>数字</td>
|
||
<td><span class="badge badge-danger">是</span></td>
|
||
</tr>
|
||
<tr>
|
||
<td>4</td>
|
||
<td>出院诊断</td>
|
||
<td>脑卒中、心肌梗塞、慢性肾脏病 等</td>
|
||
<td><span class="badge badge-danger">是</span></td>
|
||
</tr>
|
||
<tr>
|
||
<td>5</td>
|
||
<td>转诊时间</td>
|
||
<td>支持格式:2025.12.01、2025-12-01、2025/12/01</td>
|
||
<td><span class="badge badge-danger">是</span></td>
|
||
</tr>
|
||
<tr>
|
||
<td>6</td>
|
||
<td>户籍地址</td>
|
||
<td>详细地址</td>
|
||
<td><span class="badge badge-info">否</span></td>
|
||
</tr>
|
||
<tr>
|
||
<td>7</td>
|
||
<td>联系方式</td>
|
||
<td>电话号码</td>
|
||
<td><span class="badge badge-info">否</span></td>
|
||
</tr>
|
||
<tr>
|
||
<td>8</td>
|
||
<td>备注</td>
|
||
<td>其他备注信息</td>
|
||
<td><span class="badge badge-info">否</span></td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
|
||
<h4 style="margin-bottom: 12px; color: var(--color-text);">随访时间规则</h4>
|
||
<div style="display: grid; gap: 12px; margin-bottom: 24px;">
|
||
<div style="padding: 16px; background: var(--color-bg-secondary); border-radius: var(--radius-sm); border-left: 4px solid var(--color-primary);">
|
||
<div style="font-weight: 600; margin-bottom: 4px;">🧠 脑卒中 / 心肌梗塞</div>
|
||
<div style="color: var(--color-text-secondary);">
|
||
第1次: 起病后 <strong>1个月</strong> ·
|
||
第2次: 起病后 <strong>3个月</strong> ·
|
||
第3次: 起病后 <strong>6个月</strong> ·
|
||
第4次: 起病后 <strong>12个月</strong>
|
||
</div>
|
||
</div>
|
||
<div style="padding: 16px; background: var(--color-bg-secondary); border-radius: var(--radius-sm); border-left: 4px solid var(--color-warning);">
|
||
<div style="font-weight: 600; margin-bottom: 4px;">🫘 慢性肾脏病</div>
|
||
<div style="color: var(--color-text-secondary);">
|
||
第1次: 起病后 <strong>1个月</strong> ·
|
||
第2次: 起病后 <strong>2个月</strong> ·
|
||
第3次: 起病后 <strong>3个月</strong> ·
|
||
第4次: 起病后 <strong>6个月</strong>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div style="padding: 16px; background: rgba(29, 155, 240, 0.1); border-radius: var(--radius-sm);">
|
||
<div style="display: flex; gap: 12px; align-items: flex-start;">
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="20" height="20" style="color: var(--color-primary); flex-shrink: 0; margin-top: 2px;">
|
||
<circle cx="12" cy="12" r="10"/>
|
||
<line x1="12" y1="16" x2="12" y2="12"/>
|
||
<line x1="12" y1="8" x2="12.01" y2="8"/>
|
||
</svg>
|
||
<div style="color: var(--color-text-secondary); font-size: 14px;">
|
||
<strong style="color: var(--color-text);">提示:</strong>系统会根据出院诊断自动判断随访时间规则。
|
||
如果诊断信息中包含"肾"字,将按慢性肾脏病规则处理;
|
||
否则按脑卒中/心肌梗塞规则处理。
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<script>
|
||
document.addEventListener('DOMContentLoaded', function() {
|
||
const dropZone = document.getElementById('dropZone');
|
||
const fileInput = document.getElementById('fileInput');
|
||
const fileInfo = document.getElementById('fileInfo');
|
||
const fileName = document.getElementById('fileName');
|
||
const fileSize = document.getElementById('fileSize');
|
||
const removeFile = document.getElementById('removeFile');
|
||
const submitBtn = document.getElementById('submitBtn');
|
||
|
||
// 点击上传区域触发文件选择
|
||
dropZone.addEventListener('click', () => fileInput.click());
|
||
|
||
// 拖拽事件
|
||
dropZone.addEventListener('dragover', (e) => {
|
||
e.preventDefault();
|
||
dropZone.classList.add('dragover');
|
||
});
|
||
|
||
dropZone.addEventListener('dragleave', () => {
|
||
dropZone.classList.remove('dragover');
|
||
});
|
||
|
||
dropZone.addEventListener('drop', (e) => {
|
||
e.preventDefault();
|
||
dropZone.classList.remove('dragover');
|
||
|
||
const files = e.dataTransfer.files;
|
||
if (files.length > 0) {
|
||
fileInput.files = files;
|
||
updateFileInfo(files[0]);
|
||
}
|
||
});
|
||
|
||
// 文件选择变化
|
||
fileInput.addEventListener('change', function() {
|
||
if (this.files.length > 0) {
|
||
updateFileInfo(this.files[0]);
|
||
}
|
||
});
|
||
|
||
// 更新文件信息显示
|
||
function updateFileInfo(file) {
|
||
fileName.textContent = file.name;
|
||
fileSize.textContent = formatFileSize(file.size);
|
||
fileInfo.style.display = 'block';
|
||
dropZone.style.display = 'none';
|
||
submitBtn.disabled = false;
|
||
}
|
||
|
||
// 格式化文件大小
|
||
function formatFileSize(bytes) {
|
||
if (bytes < 1024) return bytes + ' B';
|
||
if (bytes < 1024 * 1024) return (bytes / 1024).toFixed(1) + ' KB';
|
||
return (bytes / (1024 * 1024)).toFixed(1) + ' MB';
|
||
}
|
||
|
||
// 移除文件
|
||
removeFile.addEventListener('click', () => {
|
||
fileInput.value = '';
|
||
fileInfo.style.display = 'none';
|
||
dropZone.style.display = 'block';
|
||
submitBtn.disabled = true;
|
||
});
|
||
});
|
||
</script>
|
||
@endsection
|