| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380 |
- <div class="min-h-screen bg-gradient-to-br from-blue-50 via-indigo-50 to-purple-50">
- @push('styles')
- <style>
- .glass-card {
- @apply bg-white/80 backdrop-blur-xl rounded-2xl border border-white/20 shadow-xl;
- }
- .gradient-text {
- @apply bg-gradient-to-r from-blue-600 via-purple-600 to-indigo-600 bg-clip-text text-transparent;
- }
- .hover-lift {
- @apply transition-all duration-300 hover:transform hover:-translate-y-1 hover:shadow-2xl;
- }
- .pulse-animation {
- animation: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;
- }
- @keyframes pulse {
- 0%, 100% {
- opacity: 1;
- }
- 50% {
- opacity: .8;
- }
- }
- .import-step-card {
- @apply glass-card p-6 hover-lift cursor-pointer;
- }
- .import-step-icon {
- @apply w-12 h-12 rounded-full flex items-center justify-center text-white font-bold text-lg shadow-lg;
- }
- .upload-zone {
- @apply border-2 border-dashed border-blue-300 rounded-xl p-8 text-center transition-all duration-300 hover:border-blue-500 hover:bg-blue-50/50;
- }
- .upload-zone.active {
- @apply border-blue-500 bg-blue-50 ring-4 ring-blue-200 ring-opacity-50;
- }
- .result-card {
- @apply glass-card p-6 border-l-4;
- }
- .result-success {
- @apply border-l-green-500 bg-green-50/50;
- }
- .result-error {
- @apply border-l-red-500 bg-red-50/50;
- }
- .floating-action {
- @apply fixed bottom-8 right-8 z-50;
- }
- .btn-primary {
- @apply bg-gradient-to-r from-blue-600 to-indigo-600 hover:from-blue-700 hover:to-indigo-700 text-white font-medium px-6 py-3 rounded-lg shadow-lg hover:shadow-xl transform transition-all duration-200 hover:-translate-y-0.5;
- }
- .btn-secondary {
- @apply bg-white text-slate-700 border border-slate-300 hover:bg-slate-50 font-medium px-6 py-3 rounded-lg shadow hover:shadow-md transition-all duration-200;
- }
- </style>
- @endpush
- <div class="container mx-auto px-4 py-8 max-w-6xl">
- <!-- 页面标题 -->
- <div class="text-center mb-12">
- <div class="glass-card inline-block px-8 py-4 mb-6">
- <h1 class="text-4xl font-bold gradient-text mb-2">
- Excel 数据导入中心
- </h1>
- <p class="text-slate-600">
- 高效批量导入教材数据到题库系统
- </p>
- </div>
- <!-- 操作按钮 -->
- <div class="flex justify-center gap-4 mt-6">
- <button
- wire:click="downloadTemplate"
- class="btn-primary inline-flex items-center gap-2"
- >
- <svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
- <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4"></path>
- </svg>
- 下载模板
- </button>
- <button
- wire:click="importData"
- class="bg-gradient-to-r from-green-600 to-emerald-600 hover:from-green-700 hover:to-emerald-700 text-white font-medium px-6 py-3 rounded-lg shadow-lg hover:shadow-xl transform transition-all duration-200 hover:-translate-y-0.5 inline-flex items-center gap-2"
- @if(!$file) disabled @endif
- >
- <svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
- <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M7 16a4 4 0 01-.88-7.903A5 5 0 1115.9 6L16 6a5 5 0 011 9.9M15 13l-3-3m0 0l-3 3m3-3v12"></path>
- </svg>
- 导入数据
- </button>
- </div>
- </div>
- <!-- 导入流程步骤 -->
- <div class="grid grid-cols-1 md:grid-cols-3 gap-6 mb-12">
- <div class="import-step-card">
- <div class="import-step-icon bg-gradient-to-r from-blue-500 to-blue-600 pulse-animation" style="animation-delay: 0s;">
- 1
- </div>
- <h3 class="text-xl font-semibold text-slate-800 mt-4 mb-2">下载模板</h3>
- <p class="text-slate-600 text-sm">
- 点击上方"下载模板"按钮获取标准Excel模板,按照说明填写数据
- </p>
- </div>
- <div class="import-step-card">
- <div class="import-step-icon bg-gradient-to-r from-purple-500 to-purple-600 pulse-animation" style="animation-delay: 0.5s;">
- 2
- </div>
- <h3 class="text-xl font-semibold text-slate-800 mt-4 mb-2">填写数据</h3>
- <p class="text-slate-600 text-sm">
- 根据模板说明填写教材信息,确保数据格式正确
- </p>
- </div>
- <div class="import-step-card">
- <div class="import-step-icon bg-gradient-to-r from-indigo-500 to-indigo-600 pulse-animation" style="animation-delay: 1s;">
- 3
- </div>
- <h3 class="text-xl font-semibold text-slate-800 mt-4 mb-2">上传导入</h3>
- <p class="text-slate-600 text-sm">
- 选择填写好的Excel文件,系统将自动处理并同步到题库
- </p>
- </div>
- </div>
- <!-- 主表单卡片 -->
- <div class="glass-card p-8 mb-8">
- <form wire:submit.prevent="importData" class="space-y-8">
- <!-- 导入类型选择 -->
- <div class="space-y-3">
- <label class="block text-lg font-semibold text-slate-800">
- 选择导入类型
- </label>
- <div class="grid grid-cols-1 md:grid-cols-3 gap-4">
- <!-- 教材系列 -->
- <label class="cursor-pointer">
- <input
- type="radio"
- wire:model.live="selectedType"
- value="textbook_series"
- class="sr-only"
- >
- <div class="relative p-6 rounded-xl shadow-md transition-all duration-300 hover:shadow-xl hover:-translate-y-1 border-2 {{ $selectedType === 'textbook_series' ? 'border-blue-500 bg-gradient-to-br from-blue-50 to-blue-100 ring-4 ring-blue-100 shadow-xl' : 'border-slate-200 bg-white' }}">
- <!-- 选中标记 -->
- <div class="absolute top-3 right-3 w-7 h-7 rounded-full border-2 flex items-center justify-center transition-all {{ $selectedType === 'textbook_series' ? 'border-blue-500 bg-blue-500' : 'border-slate-300 bg-white' }}">
- @if($selectedType === 'textbook_series')
- <svg class="w-4 h-4 text-white" fill="currentColor" viewBox="0 0 20 20">
- <path fill-rule="evenodd" d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z" clip-rule="evenodd"></path>
- </svg>
- @endif
- </div>
- <div class="flex items-center space-x-4">
- <div class="w-14 h-14 bg-gradient-to-br from-blue-400 to-blue-600 rounded-xl flex items-center justify-center shadow-lg">
- <span class="text-white text-2xl">📚</span>
- </div>
- <div>
- <h4 class="text-lg font-bold text-slate-800">教材系列</h4>
- <p class="text-sm text-slate-500">导入教材系列信息</p>
- </div>
- </div>
- </div>
- </label>
- <!-- 教材 -->
- <label class="cursor-pointer">
- <input
- type="radio"
- wire:model.live="selectedType"
- value="textbook"
- class="sr-only"
- >
- <div class="relative p-6 rounded-xl shadow-md transition-all duration-300 hover:shadow-xl hover:-translate-y-1 border-2 {{ $selectedType === 'textbook' ? 'border-purple-500 bg-gradient-to-br from-purple-50 to-purple-100 ring-4 ring-purple-100 shadow-xl' : 'border-slate-200 bg-white' }}">
- <!-- 选中标记 -->
- <div class="absolute top-3 right-3 w-7 h-7 rounded-full border-2 flex items-center justify-center transition-all {{ $selectedType === 'textbook' ? 'border-purple-500 bg-purple-500' : 'border-slate-300 bg-white' }}">
- @if($selectedType === 'textbook')
- <svg class="w-4 h-4 text-white" fill="currentColor" viewBox="0 0 20 20">
- <path fill-rule="evenodd" d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z" clip-rule="evenodd"></path>
- </svg>
- @endif
- </div>
- <div class="flex items-center space-x-4">
- <div class="w-14 h-14 bg-gradient-to-br from-purple-400 to-purple-600 rounded-xl flex items-center justify-center shadow-lg">
- <span class="text-white text-2xl">📖</span>
- </div>
- <div>
- <h4 class="text-lg font-bold text-slate-800">教材</h4>
- <p class="text-sm text-slate-500">导入具体教材信息</p>
- </div>
- </div>
- </div>
- </label>
- <!-- 教材目录 -->
- <label class="cursor-pointer">
- <input
- type="radio"
- wire:model.live="selectedType"
- value="textbook_catalog"
- class="sr-only"
- >
- <div class="relative p-6 rounded-xl shadow-md transition-all duration-300 hover:shadow-xl hover:-translate-y-1 border-2 {{ $selectedType === 'textbook_catalog' ? 'border-indigo-500 bg-gradient-to-br from-indigo-50 to-indigo-100 ring-4 ring-indigo-100 shadow-xl' : 'border-slate-200 bg-white' }}">
- <!-- 选中标记 -->
- <div class="absolute top-3 right-3 w-7 h-7 rounded-full border-2 flex items-center justify-center transition-all {{ $selectedType === 'textbook_catalog' ? 'border-indigo-500 bg-indigo-500' : 'border-slate-300 bg-white' }}">
- @if($selectedType === 'textbook_catalog')
- <svg class="w-4 h-4 text-white" fill="currentColor" viewBox="0 0 20 20">
- <path fill-rule="evenodd" d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z" clip-rule="evenodd"></path>
- </svg>
- @endif
- </div>
- <div class="flex items-center space-x-4">
- <div class="w-14 h-14 bg-gradient-to-br from-indigo-400 to-indigo-600 rounded-xl flex items-center justify-center shadow-lg">
- <span class="text-white text-2xl">📑</span>
- </div>
- <div>
- <h4 class="text-lg font-bold text-slate-800">教材目录</h4>
- <p class="text-sm text-slate-500">导入教材目录结构</p>
- </div>
- </div>
- </div>
- </label>
- </div>
- </div>
- <!-- 文件上传区域 -->
- <div class="space-y-3">
- <label class="block text-lg font-semibold text-slate-800">
- 上传 Excel 文件
- </label>
- <div
- x-data="{ isDragging: false }"
- x-on:dragover.prevent="isDragging = true"
- x-on:dragleave.prevent="isDragging = false"
- x-on:drop.prevent="isDragging = false; $event.dataTransfer.files[0] && ($event.target.files = $event.dataTransfer.files)"
- class="upload-zone"
- :class="{ 'active': isDragging }"
- >
- <input
- type="file"
- wire:model="file"
- accept=".xlsx,.xls"
- class="hidden"
- id="file-upload"
- >
- <label for="file-upload" class="cursor-pointer">
- <div class="space-y-4">
- <div class="mx-auto w-16 h-16 bg-gradient-to-r from-blue-100 to-indigo-100 rounded-full flex items-center justify-center">
- <svg class="w-8 h-8 text-blue-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
- <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M7 16a4 4 0 01-.88-7.903A5 5 0 1115.9 6L16 6a5 5 0 011 9.9M15 13l-3-3m0 0l-3 3m3-3v12"></path>
- </svg>
- </div>
- <div>
- <p class="text-lg font-semibold text-slate-700">
- <span class="text-blue-600">点击上传</span> 或拖拽文件到此处
- </p>
- <p class="text-sm text-slate-500 mt-2">
- 支持 .xlsx, .xls 格式,最大 10MB
- </p>
- </div>
- </div>
- </label>
- <!-- 文件预览 -->
- @if($file)
- <div class="mt-4 p-4 bg-green-50 rounded-lg border border-green-200">
- <div class="flex items-center space-x-3">
- <span class="text-green-600 text-2xl">📄</span>
- <div>
- <p class="font-medium text-green-800">{{ $file->getClientOriginalName() }}</p>
- <p class="text-sm text-green-600">
- {{ number_format($file->getSize() / 1024, 2) }} KB
- </p>
- </div>
- </div>
- </div>
- @endif
- @error('file')
- <div class="mt-4 p-4 bg-red-50 rounded-lg border border-red-200">
- <p class="text-red-600 text-sm">{{ $message }}</p>
- </div>
- @enderror
- </div>
- </div>
- </form>
- </div>
- <!-- 导入结果展示 -->
- @if($importResult)
- <div class="mb-8">
- @if($importResult['success'])
- <div class="result-card result-success">
- <div class="flex items-start space-x-4">
- <div class="w-12 h-12 bg-green-100 rounded-full flex items-center justify-center flex-shrink-0">
- <span class="text-green-600 text-xl">✓</span>
- </div>
- <div class="flex-1">
- <h3 class="text-xl font-semibold text-green-800 mb-3">导入完成!</h3>
- <div class="grid grid-cols-2 gap-4 mb-4">
- <div class="bg-white/60 rounded-lg p-4">
- <p class="text-sm text-green-700 mb-1">成功导入</p>
- <p class="text-2xl font-bold text-green-900">{{ $importResult['success_count'] }}</p>
- </div>
- <div class="bg-white/60 rounded-lg p-4">
- <p class="text-sm text-red-700 mb-1">导入失败</p>
- <p class="text-2xl font-bold text-red-600">{{ $importResult['error_count'] }}</p>
- </div>
- </div>
- @if(!empty($importResult['errors']))
- <div class="bg-white/60 rounded-lg p-4">
- <h4 class="font-semibold text-red-800 mb-2">错误详情:</h4>
- <div class="max-h-40 overflow-y-auto space-y-1">
- @foreach($importResult['errors'] as $error)
- <p class="text-sm text-red-700">• {{ $error }}</p>
- @endforeach
- </div>
- </div>
- @endif
- </div>
- </div>
- </div>
- @else
- <div class="result-card result-error">
- <div class="flex items-start space-x-4">
- <div class="w-12 h-12 bg-red-100 rounded-full flex items-center justify-center flex-shrink-0">
- <span class="text-red-600 text-xl">⚠</span>
- </div>
- <div>
- <h3 class="text-xl font-semibold text-red-800 mb-2">导入失败</h3>
- <p class="text-red-700">{{ $importResult['message'] }}</p>
- </div>
- </div>
- </div>
- @endif
- </div>
- @endif
- <!-- 帮助信息 -->
- <div class="glass-card p-6">
- <div class="flex items-start space-x-3">
- <span class="text-blue-600 text-2xl">💡</span>
- <div>
- <h3 class="text-lg font-semibold text-slate-800 mb-2">使用帮助</h3>
- <ul class="space-y-2 text-sm text-slate-600">
- <li class="flex items-start space-x-2">
- <span class="text-blue-600 mt-1">•</span>
- <span>确保Excel文件格式与模板一致,遵循字段说明</span>
- </li>
- <li class="flex items-start space-x-2">
- <span class="text-blue-600 mt-1">•</span>
- <span>教材系列的别名字段必须唯一,创建后不可修改</span>
- </li>
- <li class="flex items-start space-x-2">
- <span class="text-blue-600 mt-1">•</span>
- <span>导入的数据将实时同步到题库服务,请谨慎操作</span>
- </li>
- <li class="flex items-start space-x-2">
- <span class="text-blue-600 mt-1">•</span>
- <span>如遇问题,请查看错误详情并修正后重新导入</span>
- </li>
- </ul>
- </div>
- </div>
- </div>
- </div>
- </div>
|