reminder/resources/views/patients/import.blade.php
2026-01-12 12:42:48 +08:00

271 lines
12 KiB
PHP
Raw Permalink 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.

@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