textbook-import.blade.php 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380
  1. <div class="min-h-screen bg-gradient-to-br from-blue-50 via-indigo-50 to-purple-50">
  2. @push('styles')
  3. <style>
  4. .glass-card {
  5. @apply bg-white/80 backdrop-blur-xl rounded-2xl border border-white/20 shadow-xl;
  6. }
  7. .gradient-text {
  8. @apply bg-gradient-to-r from-blue-600 via-purple-600 to-indigo-600 bg-clip-text text-transparent;
  9. }
  10. .hover-lift {
  11. @apply transition-all duration-300 hover:transform hover:-translate-y-1 hover:shadow-2xl;
  12. }
  13. .pulse-animation {
  14. animation: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;
  15. }
  16. @keyframes pulse {
  17. 0%, 100% {
  18. opacity: 1;
  19. }
  20. 50% {
  21. opacity: .8;
  22. }
  23. }
  24. .import-step-card {
  25. @apply glass-card p-6 hover-lift cursor-pointer;
  26. }
  27. .import-step-icon {
  28. @apply w-12 h-12 rounded-full flex items-center justify-center text-white font-bold text-lg shadow-lg;
  29. }
  30. .upload-zone {
  31. @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;
  32. }
  33. .upload-zone.active {
  34. @apply border-blue-500 bg-blue-50 ring-4 ring-blue-200 ring-opacity-50;
  35. }
  36. .result-card {
  37. @apply glass-card p-6 border-l-4;
  38. }
  39. .result-success {
  40. @apply border-l-green-500 bg-green-50/50;
  41. }
  42. .result-error {
  43. @apply border-l-red-500 bg-red-50/50;
  44. }
  45. .floating-action {
  46. @apply fixed bottom-8 right-8 z-50;
  47. }
  48. .btn-primary {
  49. @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;
  50. }
  51. .btn-secondary {
  52. @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;
  53. }
  54. </style>
  55. @endpush
  56. <div class="container mx-auto px-4 py-8 max-w-6xl">
  57. <!-- 页面标题 -->
  58. <div class="text-center mb-12">
  59. <div class="glass-card inline-block px-8 py-4 mb-6">
  60. <h1 class="text-4xl font-bold gradient-text mb-2">
  61. Excel 数据导入中心
  62. </h1>
  63. <p class="text-slate-600">
  64. 高效批量导入教材数据到题库系统
  65. </p>
  66. </div>
  67. <!-- 操作按钮 -->
  68. <div class="flex justify-center gap-4 mt-6">
  69. <button
  70. wire:click="downloadTemplate"
  71. class="btn-primary inline-flex items-center gap-2"
  72. >
  73. <svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
  74. <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>
  75. </svg>
  76. 下载模板
  77. </button>
  78. <button
  79. wire:click="importData"
  80. 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"
  81. @if(!$file) disabled @endif
  82. >
  83. <svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
  84. <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>
  85. </svg>
  86. 导入数据
  87. </button>
  88. </div>
  89. </div>
  90. <!-- 导入流程步骤 -->
  91. <div class="grid grid-cols-1 md:grid-cols-3 gap-6 mb-12">
  92. <div class="import-step-card">
  93. <div class="import-step-icon bg-gradient-to-r from-blue-500 to-blue-600 pulse-animation" style="animation-delay: 0s;">
  94. 1
  95. </div>
  96. <h3 class="text-xl font-semibold text-slate-800 mt-4 mb-2">下载模板</h3>
  97. <p class="text-slate-600 text-sm">
  98. 点击上方"下载模板"按钮获取标准Excel模板,按照说明填写数据
  99. </p>
  100. </div>
  101. <div class="import-step-card">
  102. <div class="import-step-icon bg-gradient-to-r from-purple-500 to-purple-600 pulse-animation" style="animation-delay: 0.5s;">
  103. 2
  104. </div>
  105. <h3 class="text-xl font-semibold text-slate-800 mt-4 mb-2">填写数据</h3>
  106. <p class="text-slate-600 text-sm">
  107. 根据模板说明填写教材信息,确保数据格式正确
  108. </p>
  109. </div>
  110. <div class="import-step-card">
  111. <div class="import-step-icon bg-gradient-to-r from-indigo-500 to-indigo-600 pulse-animation" style="animation-delay: 1s;">
  112. 3
  113. </div>
  114. <h3 class="text-xl font-semibold text-slate-800 mt-4 mb-2">上传导入</h3>
  115. <p class="text-slate-600 text-sm">
  116. 选择填写好的Excel文件,系统将自动处理并同步到题库
  117. </p>
  118. </div>
  119. </div>
  120. <!-- 主表单卡片 -->
  121. <div class="glass-card p-8 mb-8">
  122. <form wire:submit.prevent="importData" class="space-y-8">
  123. <!-- 导入类型选择 -->
  124. <div class="space-y-3">
  125. <label class="block text-lg font-semibold text-slate-800">
  126. 选择导入类型
  127. </label>
  128. <div class="grid grid-cols-1 md:grid-cols-3 gap-4">
  129. <!-- 教材系列 -->
  130. <label class="cursor-pointer">
  131. <input
  132. type="radio"
  133. wire:model.live="selectedType"
  134. value="textbook_series"
  135. class="sr-only"
  136. >
  137. <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' }}">
  138. <!-- 选中标记 -->
  139. <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' }}">
  140. @if($selectedType === 'textbook_series')
  141. <svg class="w-4 h-4 text-white" fill="currentColor" viewBox="0 0 20 20">
  142. <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>
  143. </svg>
  144. @endif
  145. </div>
  146. <div class="flex items-center space-x-4">
  147. <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">
  148. <span class="text-white text-2xl">📚</span>
  149. </div>
  150. <div>
  151. <h4 class="text-lg font-bold text-slate-800">教材系列</h4>
  152. <p class="text-sm text-slate-500">导入教材系列信息</p>
  153. </div>
  154. </div>
  155. </div>
  156. </label>
  157. <!-- 教材 -->
  158. <label class="cursor-pointer">
  159. <input
  160. type="radio"
  161. wire:model.live="selectedType"
  162. value="textbook"
  163. class="sr-only"
  164. >
  165. <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' }}">
  166. <!-- 选中标记 -->
  167. <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' }}">
  168. @if($selectedType === 'textbook')
  169. <svg class="w-4 h-4 text-white" fill="currentColor" viewBox="0 0 20 20">
  170. <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>
  171. </svg>
  172. @endif
  173. </div>
  174. <div class="flex items-center space-x-4">
  175. <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">
  176. <span class="text-white text-2xl">📖</span>
  177. </div>
  178. <div>
  179. <h4 class="text-lg font-bold text-slate-800">教材</h4>
  180. <p class="text-sm text-slate-500">导入具体教材信息</p>
  181. </div>
  182. </div>
  183. </div>
  184. </label>
  185. <!-- 教材目录 -->
  186. <label class="cursor-pointer">
  187. <input
  188. type="radio"
  189. wire:model.live="selectedType"
  190. value="textbook_catalog"
  191. class="sr-only"
  192. >
  193. <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' }}">
  194. <!-- 选中标记 -->
  195. <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' }}">
  196. @if($selectedType === 'textbook_catalog')
  197. <svg class="w-4 h-4 text-white" fill="currentColor" viewBox="0 0 20 20">
  198. <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>
  199. </svg>
  200. @endif
  201. </div>
  202. <div class="flex items-center space-x-4">
  203. <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">
  204. <span class="text-white text-2xl">📑</span>
  205. </div>
  206. <div>
  207. <h4 class="text-lg font-bold text-slate-800">教材目录</h4>
  208. <p class="text-sm text-slate-500">导入教材目录结构</p>
  209. </div>
  210. </div>
  211. </div>
  212. </label>
  213. </div>
  214. </div>
  215. <!-- 文件上传区域 -->
  216. <div class="space-y-3">
  217. <label class="block text-lg font-semibold text-slate-800">
  218. 上传 Excel 文件
  219. </label>
  220. <div
  221. x-data="{ isDragging: false }"
  222. x-on:dragover.prevent="isDragging = true"
  223. x-on:dragleave.prevent="isDragging = false"
  224. x-on:drop.prevent="isDragging = false; $event.dataTransfer.files[0] && ($event.target.files = $event.dataTransfer.files)"
  225. class="upload-zone"
  226. :class="{ 'active': isDragging }"
  227. >
  228. <input
  229. type="file"
  230. wire:model="file"
  231. accept=".xlsx,.xls"
  232. class="hidden"
  233. id="file-upload"
  234. >
  235. <label for="file-upload" class="cursor-pointer">
  236. <div class="space-y-4">
  237. <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">
  238. <svg class="w-8 h-8 text-blue-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
  239. <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>
  240. </svg>
  241. </div>
  242. <div>
  243. <p class="text-lg font-semibold text-slate-700">
  244. <span class="text-blue-600">点击上传</span> 或拖拽文件到此处
  245. </p>
  246. <p class="text-sm text-slate-500 mt-2">
  247. 支持 .xlsx, .xls 格式,最大 10MB
  248. </p>
  249. </div>
  250. </div>
  251. </label>
  252. <!-- 文件预览 -->
  253. @if($file)
  254. <div class="mt-4 p-4 bg-green-50 rounded-lg border border-green-200">
  255. <div class="flex items-center space-x-3">
  256. <span class="text-green-600 text-2xl">📄</span>
  257. <div>
  258. <p class="font-medium text-green-800">{{ $file->getClientOriginalName() }}</p>
  259. <p class="text-sm text-green-600">
  260. {{ number_format($file->getSize() / 1024, 2) }} KB
  261. </p>
  262. </div>
  263. </div>
  264. </div>
  265. @endif
  266. @error('file')
  267. <div class="mt-4 p-4 bg-red-50 rounded-lg border border-red-200">
  268. <p class="text-red-600 text-sm">{{ $message }}</p>
  269. </div>
  270. @enderror
  271. </div>
  272. </div>
  273. </form>
  274. </div>
  275. <!-- 导入结果展示 -->
  276. @if($importResult)
  277. <div class="mb-8">
  278. @if($importResult['success'])
  279. <div class="result-card result-success">
  280. <div class="flex items-start space-x-4">
  281. <div class="w-12 h-12 bg-green-100 rounded-full flex items-center justify-center flex-shrink-0">
  282. <span class="text-green-600 text-xl">✓</span>
  283. </div>
  284. <div class="flex-1">
  285. <h3 class="text-xl font-semibold text-green-800 mb-3">导入完成!</h3>
  286. <div class="grid grid-cols-2 gap-4 mb-4">
  287. <div class="bg-white/60 rounded-lg p-4">
  288. <p class="text-sm text-green-700 mb-1">成功导入</p>
  289. <p class="text-2xl font-bold text-green-900">{{ $importResult['success_count'] }}</p>
  290. </div>
  291. <div class="bg-white/60 rounded-lg p-4">
  292. <p class="text-sm text-red-700 mb-1">导入失败</p>
  293. <p class="text-2xl font-bold text-red-600">{{ $importResult['error_count'] }}</p>
  294. </div>
  295. </div>
  296. @if(!empty($importResult['errors']))
  297. <div class="bg-white/60 rounded-lg p-4">
  298. <h4 class="font-semibold text-red-800 mb-2">错误详情:</h4>
  299. <div class="max-h-40 overflow-y-auto space-y-1">
  300. @foreach($importResult['errors'] as $error)
  301. <p class="text-sm text-red-700">• {{ $error }}</p>
  302. @endforeach
  303. </div>
  304. </div>
  305. @endif
  306. </div>
  307. </div>
  308. </div>
  309. @else
  310. <div class="result-card result-error">
  311. <div class="flex items-start space-x-4">
  312. <div class="w-12 h-12 bg-red-100 rounded-full flex items-center justify-center flex-shrink-0">
  313. <span class="text-red-600 text-xl">⚠</span>
  314. </div>
  315. <div>
  316. <h3 class="text-xl font-semibold text-red-800 mb-2">导入失败</h3>
  317. <p class="text-red-700">{{ $importResult['message'] }}</p>
  318. </div>
  319. </div>
  320. </div>
  321. @endif
  322. </div>
  323. @endif
  324. <!-- 帮助信息 -->
  325. <div class="glass-card p-6">
  326. <div class="flex items-start space-x-3">
  327. <span class="text-blue-600 text-2xl">💡</span>
  328. <div>
  329. <h3 class="text-lg font-semibold text-slate-800 mb-2">使用帮助</h3>
  330. <ul class="space-y-2 text-sm text-slate-600">
  331. <li class="flex items-start space-x-2">
  332. <span class="text-blue-600 mt-1">•</span>
  333. <span>确保Excel文件格式与模板一致,遵循字段说明</span>
  334. </li>
  335. <li class="flex items-start space-x-2">
  336. <span class="text-blue-600 mt-1">•</span>
  337. <span>教材系列的别名字段必须唯一,创建后不可修改</span>
  338. </li>
  339. <li class="flex items-start space-x-2">
  340. <span class="text-blue-600 mt-1">•</span>
  341. <span>导入的数据将实时同步到题库服务,请谨慎操作</span>
  342. </li>
  343. <li class="flex items-start space-x-2">
  344. <span class="text-blue-600 mt-1">•</span>
  345. <span>如遇问题,请查看错误详情并修正后重新导入</span>
  346. </li>
  347. </ul>
  348. </div>
  349. </div>
  350. </div>
  351. </div>
  352. </div>