student-dashboard.blade.php 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481
  1. <div class="p-6 space-y-6">
  2. {{-- 页面标题 --}}
  3. <div class="flex items-center justify-between">
  4. <div>
  5. <h1 class="text-3xl font-bold text-gray-900">学生仪表板</h1>
  6. <p class="mt-1 text-sm text-gray-500">
  7. 全面展示学生的学习分析数据,包括掌握度、技能熟练度、提分预测和学习路径
  8. </p>
  9. </div>
  10. <div class="flex items-center space-x-4">
  11. <input
  12. type="text"
  13. wire:model.live="studentId"
  14. placeholder="输入学生ID"
  15. class="block w-40 rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 sm:text-sm"
  16. />
  17. <button
  18. wire:click="loadDashboardData"
  19. wire:loading.attr="disabled"
  20. class="inline-flex items-center px-4 py-2 border border-transparent text-sm font-medium rounded-md shadow-sm text-white bg-indigo-600 hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500"
  21. >
  22. <svg wire:loading class="animate-spin -ml-1 mr-3 h-5 w-5 text-white" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
  23. <circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
  24. <path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
  25. </svg>
  26. 刷新数据
  27. </button>
  28. </div>
  29. </div>
  30. {{-- 错误提示 --}}
  31. @if ($errorMessage)
  32. <div class="rounded-md bg-red-50 p-4">
  33. <div class="flex">
  34. <div class="flex-shrink-0">
  35. <svg class="h-5 w-5 text-red-400" viewBox="0 0 20 20" fill="currentColor">
  36. <path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zM8.707 7.293a1 1 0 00-1.414 1.414L8.586 10l-1.293 1.293a1 1 0 101.414 1.414L10 11.414l1.293 1.293a1 1 0 001.414-1.414L11.414 10l1.293-1.293a1 1 0 00-1.414-1.414L10 8.586 8.707 7.293z" clip-rule="evenodd" />
  37. </svg>
  38. </div>
  39. <div class="ml-3">
  40. <h3 class="text-sm font-medium text-red-800">加载错误</h3>
  41. <div class="mt-2 text-sm text-red-700">
  42. <p>{{ $errorMessage }}</p>
  43. </div>
  44. </div>
  45. </div>
  46. </div>
  47. @endif
  48. {{-- 加载状态 --}}
  49. @if ($isLoading)
  50. <div class="flex items-center justify-center py-12">
  51. <svg class="animate-spin h-8 w-8 text-indigo-600" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
  52. <circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
  53. <path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
  54. </svg>
  55. <span class="ml-3 text-gray-600">正在加载数据...</span>
  56. </div>
  57. @else
  58. {{-- 快速概览卡片 --}}
  59. <div class="grid grid-cols-1 gap-5 sm:grid-cols-2 lg:grid-cols-4">
  60. {{-- 掌握度概览 --}}
  61. @if (isset($dashboardData['mastery']['overview']))
  62. <div class="bg-white overflow-hidden shadow rounded-lg">
  63. <div class="p-5">
  64. <div class="flex items-center">
  65. <div class="flex-shrink-0">
  66. <div class="w-8 h-8 bg-indigo-500 rounded-md flex items-center justify-center">
  67. <svg class="w-5 h-5 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
  68. <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z"></path>
  69. </svg>
  70. </div>
  71. </div>
  72. <div class="ml-5 w-0 flex-1">
  73. <dl>
  74. <dt class="text-sm font-medium text-gray-500 truncate">平均掌握度</dt>
  75. <dd class="flex items-baseline">
  76. <div class="text-2xl font-semibold text-gray-900">
  77. {{ number_format($dashboardData['mastery']['overview']['average_mastery_level'] * 100, 1) }}%
  78. </div>
  79. </dd>
  80. </dl>
  81. </div>
  82. </div>
  83. </div>
  84. <div class="bg-gray-50 px-5 py-3">
  85. <div class="text-sm">
  86. <div class="flex justify-between text-xs text-gray-600">
  87. <span>已掌握: {{ $dashboardData['mastery']['overview']['mastered_knowledge_points'] }}</span>
  88. <span>薄弱点: {{ $dashboardData['mastery']['overview']['weak_knowledge_points'] }}</span>
  89. </div>
  90. </div>
  91. </div>
  92. </div>
  93. @endif
  94. {{-- 技能熟练度概览 --}}
  95. @if (isset($dashboardData['skill']['summary']))
  96. <div class="bg-white overflow-hidden shadow rounded-lg">
  97. <div class="p-5">
  98. <div class="flex items-center">
  99. <div class="flex-shrink-0">
  100. <div class="w-8 h-8 bg-green-500 rounded-md flex items-center justify-center">
  101. <svg class="w-5 h-5 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
  102. <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 10V3L4 14h7v7l9-11h-7z"></path>
  103. </svg>
  104. </div>
  105. </div>
  106. <div class="ml-5 w-0 flex-1">
  107. <dl>
  108. <dt class="text-sm font-medium text-gray-500 truncate">技能熟练度</dt>
  109. <dd class="flex items-baseline">
  110. <div class="text-2xl font-semibold text-gray-900">
  111. {{ number_format($dashboardData['skill']['summary']['average_proficiency_level'] * 100, 1) }}%
  112. </div>
  113. </dd>
  114. </dl>
  115. </div>
  116. </div>
  117. </div>
  118. <div class="bg-gray-50 px-5 py-3">
  119. <div class="text-sm">
  120. <div class="flex justify-between text-xs text-gray-600">
  121. <span>技能总数: {{ $dashboardData['skill']['summary']['total_skills'] }}</span>
  122. <span>练习题: {{ $dashboardData['skill']['summary']['total_questions_attempted'] }}</span>
  123. </div>
  124. </div>
  125. </div>
  126. </div>
  127. @endif
  128. {{-- 提分潜力 --}}
  129. @if (isset($dashboardData['prediction']['quick']))
  130. <div class="bg-white overflow-hidden shadow rounded-lg">
  131. <div class="p-5">
  132. <div class="flex items-center">
  133. <div class="flex-shrink-0">
  134. <div class="w-8 h-8 bg-yellow-500 rounded-md flex items-center justify-center">
  135. <svg class="w-5 h-5 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
  136. <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 7h8m0 0v8m0-8l-8 8-4-4-6 6"></path>
  137. </svg>
  138. </div>
  139. </div>
  140. <div class="ml-5 w-0 flex-1">
  141. <dl>
  142. <dt class="text-sm font-medium text-gray-500 truncate">预期提分</dt>
  143. <dd class="flex items-baseline">
  144. <div class="text-2xl font-semibold text-gray-900">
  145. +{{ $dashboardData['prediction']['quick']['quick_prediction']['improvement_potential'] ?? 0 }}
  146. </div>
  147. <span class="ml-2 text-sm text-gray-600">分</span>
  148. </dd>
  149. </dl>
  150. </div>
  151. </div>
  152. </div>
  153. <div class="bg-gray-50 px-5 py-3">
  154. <div class="text-sm">
  155. <div class="flex justify-between text-xs text-gray-600">
  156. <span>预计学习: {{ $dashboardData['prediction']['quick']['quick_prediction']['estimated_study_hours'] ?? 0 }}小时</span>
  157. <span>置信度: {{ number_format(($dashboardData['prediction']['quick']['quick_prediction']['confidence_level'] ?? 0) * 100, 0) }}%</span>
  158. </div>
  159. </div>
  160. </div>
  161. </div>
  162. @endif
  163. {{-- 学习路径 --}}
  164. @if (isset($dashboardData['learning_path']['analytics']))
  165. <div class="bg-white overflow-hidden shadow rounded-lg">
  166. <div class="p-5">
  167. <div class="flex items-center">
  168. <div class="flex-shrink-0">
  169. <div class="w-8 h-8 bg-purple-500 rounded-md flex items-center justify-center">
  170. <svg class="w-5 h-5 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
  171. <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 20l-5.447-2.724A1 1 0 013 16.382V5.618a1 1 0 011.447-.894L9 7m0 13l6-3m-6 3V7m6 10l4.553 2.276A1 1 0 0021 18.382V7.618a1 1 0 00-.553-.894L15 4m0 13V4m0 0L9 7"></path>
  172. </svg>
  173. </div>
  174. </div>
  175. <div class="ml-5 w-0 flex-1">
  176. <dl>
  177. <dt class="text-sm font-medium text-gray-500 truncate">活跃路径</dt>
  178. <dd class="flex items-baseline">
  179. <div class="text-2xl font-semibold text-gray-900">
  180. {{ $dashboardData['learning_path']['analytics']['active_paths'] ?? 0 }}
  181. </div>
  182. </dd>
  183. </dl>
  184. </div>
  185. </div>
  186. </div>
  187. <div class="bg-gray-50 px-5 py-3">
  188. <div class="text-sm">
  189. <div class="flex justify-between text-xs text-gray-600">
  190. <span>已完成: {{ $dashboardData['learning_path']['analytics']['completed_paths'] ?? 0 }}</span>
  191. <span>效率: {{ number_format(($dashboardData['learning_path']['analytics']['average_efficiency_score'] ?? 0) * 100, 0) }}%</span>
  192. </div>
  193. </div>
  194. </div>
  195. </div>
  196. @endif
  197. </div>
  198. {{-- 主要内容区域 --}}
  199. <div class="grid grid-cols-1 gap-6 lg:grid-cols-2">
  200. {{-- 掌握度分析 --}}
  201. <div class="bg-white shadow rounded-lg">
  202. <div class="px-6 py-4 border-b border-gray-200 flex items-center justify-between">
  203. <h3 class="text-lg font-medium text-gray-900">知识点掌握度</h3>
  204. <button
  205. wire:click="batchUpdateSkills"
  206. class="inline-flex items-center px-3 py-1 border border-transparent text-xs font-medium rounded text-indigo-700 bg-indigo-100 hover:bg-indigo-200"
  207. >
  208. 刷新数据
  209. </button>
  210. </div>
  211. <div class="p-6">
  212. @if (isset($dashboardData['mastery']['overview']['total_knowledge_points']))
  213. <div class="space-y-4">
  214. <div class="flex items-center justify-between text-sm">
  215. <span class="text-gray-600">总知识点数</span>
  216. <span class="font-medium text-gray-900">{{ $dashboardData['mastery']['overview']['total_knowledge_points'] }}</span>
  217. </div>
  218. <div class="w-full bg-gray-200 rounded-full h-2">
  219. <div class="bg-indigo-600 h-2 rounded-full" style="width: {{ $dashboardData['mastery']['overview']['average_mastery_level'] * 100 }}%"></div>
  220. </div>
  221. <div class="grid grid-cols-3 gap-4 mt-4">
  222. <div class="text-center">
  223. <div class="text-2xl font-semibold text-green-600">{{ $dashboardData['mastery']['overview']['mastered_knowledge_points'] }}</div>
  224. <div class="text-xs text-gray-500">已掌握 (≥85%)</div>
  225. </div>
  226. <div class="text-center">
  227. <div class="text-2xl font-semibold text-blue-600">{{ $dashboardData['mastery']['overview']['good_knowledge_points'] }}</div>
  228. <div class="text-xs text-gray-500">良好 (70-85%)</div>
  229. </div>
  230. <div class="text-center">
  231. <div class="text-2xl font-semibold text-red-600">{{ $dashboardData['mastery']['overview']['weak_knowledge_points'] }}</div>
  232. <div class="text-xs text-gray-500">薄弱 (<50%)</div>
  233. </div>
  234. </div>
  235. @if (!empty($dashboardData['mastery']['overview']['weak_knowledge_points']))
  236. <div class="mt-6">
  237. <h4 class="text-sm font-medium text-gray-900 mb-3">薄弱知识点</h4>
  238. <div class="space-y-2">
  239. @foreach (array_slice($dashboardData['mastery']['overview']['weak_knowledge_points'], 0, 5) as $weak)
  240. <div class="flex items-center justify-between p-3 bg-red-50 rounded-lg">
  241. <div class="flex items-center">
  242. <div class="w-2 h-2 bg-red-500 rounded-full mr-3"></div>
  243. <span class="text-sm font-medium text-gray-900">{{ $weak['kp_code'] }}</span>
  244. </div>
  245. <div class="flex items-center space-x-3">
  246. <span class="text-sm text-gray-600">{{ number_format($weak['mastery_level'] * 100, 1) }}%</span>
  247. <button
  248. wire:click="recalculateMastery('{{ $weak['kp_code'] }}')"
  249. class="text-xs text-indigo-600 hover:text-indigo-800"
  250. >
  251. 重新计算
  252. </button>
  253. </div>
  254. </div>
  255. @endforeach
  256. </div>
  257. </div>
  258. @endif
  259. </div>
  260. @else
  261. <div class="text-center py-8 text-gray-500">
  262. 暂无掌握度数据
  263. </div>
  264. @endif
  265. </div>
  266. </div>
  267. {{-- 技能熟练度 --}}
  268. <div class="bg-white shadow rounded-lg">
  269. <div class="px-6 py-4 border-b border-gray-200 flex items-center justify-between">
  270. <h3 class="text-lg font-medium text-gray-900">技能熟练度</h3>
  271. <button
  272. wire:click="batchUpdateSkills"
  273. class="inline-flex items-center px-3 py-1 border border-transparent text-xs font-medium rounded text-green-700 bg-green-100 hover:bg-green-200"
  274. >
  275. 批量更新
  276. </button>
  277. </div>
  278. <div class="p-6">
  279. @if (isset($dashboardData['skill']['proficiency']['data']))
  280. <div class="space-y-4">
  281. @foreach (array_slice($dashboardData['skill']['proficiency']['data'], 0, 5) as $skill)
  282. <div class="flex items-center justify-between">
  283. <div class="flex items-center flex-1">
  284. <div class="w-2 h-2 bg-green-500 rounded-full mr-3"></div>
  285. <div class="flex-1">
  286. <div class="text-sm font-medium text-gray-900">{{ $skill['skill_name'] }}</div>
  287. <div class="w-full bg-gray-200 rounded-full h-1.5 mt-1">
  288. <div class="bg-green-500 h-1.5 rounded-full" style="width: {{ $skill['proficiency_level'] * 100 }}%"></div>
  289. </div>
  290. </div>
  291. </div>
  292. <div class="ml-4 text-right">
  293. <div class="text-sm font-semibold text-gray-900">{{ number_format($skill['proficiency_level'] * 100, 1) }}%</div>
  294. <div class="text-xs text-gray-500">{{ $skill['total_questions_attempted'] }}题</div>
  295. </div>
  296. </div>
  297. @endforeach
  298. </div>
  299. @else
  300. <div class="text-center py-8 text-gray-500">
  301. 暂无技能数据
  302. </div>
  303. @endif
  304. </div>
  305. </div>
  306. </div>
  307. {{-- 技能熟练度雷达图 --}}
  308. <div class="bg-white shadow rounded-lg">
  309. <div class="px-6 py-4 border-b border-gray-200">
  310. <h3 class="text-lg font-medium text-gray-900">技能熟练度雷达图</h3>
  311. </div>
  312. <div class="p-6">
  313. <livewire:skill-proficiency-radar :student-id="$studentId" />
  314. </div>
  315. </div>
  316. {{-- 提分预测和学习路径 --}}
  317. <div class="grid grid-cols-1 gap-6 lg:grid-cols-2">
  318. {{-- 提分预测 --}}
  319. <div class="bg-white shadow rounded-lg">
  320. <div class="px-6 py-4 border-b border-gray-200 flex items-center justify-between">
  321. <h3 class="text-lg font-medium text-gray-900">提分预测</h3>
  322. <button
  323. wire:click="generateQuickPrediction"
  324. class="inline-flex items-center px-3 py-1 border border-transparent text-xs font-medium rounded text-yellow-700 bg-yellow-100 hover:bg-yellow-200"
  325. >
  326. 快速预测
  327. </button>
  328. </div>
  329. <div class="p-6">
  330. @if (isset($dashboardData['prediction']['list']['predictions']))
  331. <div class="space-y-4">
  332. @foreach (array_slice($dashboardData['prediction']['list']['predictions'], 0, 3) as $prediction)
  333. <div class="p-4 border border-gray-200 rounded-lg">
  334. <div class="flex items-center justify-between mb-2">
  335. <div class="text-sm font-medium text-gray-900">{{ $prediction['target_entity'] }}</div>
  336. <span class="text-xs text-gray-500">{{ date('m-d', strtotime($prediction['prediction_date'])) }}</span>
  337. </div>
  338. <div class="flex items-center justify-between">
  339. <div class="text-sm text-gray-600">
  340. 当前: {{ $prediction['current_score'] }}分 →
  341. <span class="font-semibold text-gray-900">{{ $prediction['predicted_score'] }}分</span>
  342. </div>
  343. <div class="text-sm font-semibold text-green-600">
  344. +{{ number_format($prediction['predicted_score'] - $prediction['current_score'], 1) }}分
  345. </div>
  346. </div>
  347. </div>
  348. @endforeach
  349. </div>
  350. @else
  351. <div class="text-center py-8 text-gray-500">
  352. 暂无预测数据
  353. </div>
  354. @endif
  355. </div>
  356. </div>
  357. {{-- 学习路径 --}}
  358. <div class="bg-white shadow rounded-lg">
  359. <div class="px-6 py-4 border-b border-gray-200">
  360. <h3 class="text-lg font-medium text-gray-900">学习路径</h3>
  361. </div>
  362. <div class="p-6">
  363. @if (isset($dashboardData['learning_path']['list']['paths']))
  364. <div class="space-y-4">
  365. @foreach (array_slice($dashboardData['learning_path']['list']['paths'], 0, 3) as $path)
  366. <div class="p-4 border border-gray-200 rounded-lg">
  367. <div class="flex items-center justify-between mb-2">
  368. <div class="text-sm font-medium text-gray-900">{{ $path['target_kp_code'] }}</div>
  369. <span class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium
  370. {{ $path['status'] === 'active' ? 'bg-green-100 text-green-800' : '' }}
  371. {{ $path['status'] === 'completed' ? 'bg-blue-100 text-blue-800' : '' }}
  372. {{ $path['status'] === 'abandoned' ? 'bg-gray-100 text-gray-800' : '' }}">
  373. {{ $path['status'] }}
  374. </span>
  375. </div>
  376. <div class="w-full bg-gray-200 rounded-full h-2 mb-2">
  377. <div class="bg-purple-600 h-2 rounded-full" style="width: {{ $path['completion_percentage'] }}%"></div>
  378. </div>
  379. <div class="flex items-center justify-between text-xs text-gray-600">
  380. <span>完成度: {{ number_format($path['completion_percentage'], 0) }}%</span>
  381. <span>预计: {{ $path['estimated_hours'] }}小时</span>
  382. </div>
  383. </div>
  384. @endforeach
  385. </div>
  386. @else
  387. <div class="text-center py-8 text-gray-500">
  388. 暂无学习路径数据
  389. </div>
  390. @endif
  391. </div>
  392. </div>
  393. </div>
  394. {{-- 掌握度热力图 --}}
  395. <div class="bg-white shadow rounded-lg">
  396. <div class="px-6 py-4 border-b border-gray-200">
  397. <h3 class="text-lg font-medium text-gray-900">知识点掌握度热力图</h3>
  398. </div>
  399. <div class="p-6">
  400. <livewire:mastery-heatmap :student-id="$studentId" />
  401. </div>
  402. </div>
  403. {{-- 知识点依赖关系图 --}}
  404. <div class="bg-white shadow rounded-lg">
  405. <div class="px-6 py-4 border-b border-gray-200">
  406. <h3 class="text-lg font-medium text-gray-900">知识点依赖关系图</h3>
  407. </div>
  408. <div class="p-6">
  409. <livewire:knowledge-dependency-graph :student-id="$studentId" />
  410. </div>
  411. </div>
  412. {{-- 推荐学习路径 --}}
  413. @if (isset($dashboardData['learning_path']['recommendations']['recommendations']))
  414. <div class="bg-white shadow rounded-lg">
  415. <div class="px-6 py-4 border-b border-gray-200">
  416. <h3 class="text-lg font-medium text-gray-900">推荐学习路径</h3>
  417. </div>
  418. <div class="p-6">
  419. <div class="grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3">
  420. @foreach ($dashboardData['learning_path']['recommendations']['recommendations'] as $recommendation)
  421. <div class="p-4 border border-gray-200 rounded-lg hover:border-indigo-300 transition-colors">
  422. <div class="flex items-center mb-3">
  423. <div class="w-8 h-8 bg-indigo-100 rounded-full flex items-center justify-center mr-3">
  424. <svg class="w-4 h-4 text-indigo-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
  425. <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 10V3L4 14h7v7l9-11h-7z"></path>
  426. </svg>
  427. </div>
  428. <div class="flex-1">
  429. <div class="text-sm font-medium text-gray-900">{{ $recommendation['target_kp_name'] }}</div>
  430. <div class="text-xs text-gray-500">{{ $recommendation['target_kp_code'] }}</div>
  431. </div>
  432. </div>
  433. <div class="mb-3">
  434. <div class="flex items-center justify-between text-xs text-gray-600 mb-1">
  435. <span>当前掌握度</span>
  436. <span class="font-medium">{{ number_format($recommendation['current_mastery'] * 100, 1) }}%</span>
  437. </div>
  438. <div class="w-full bg-gray-200 rounded-full h-1.5">
  439. <div class="bg-indigo-500 h-1.5 rounded-full" style="width: {{ $recommendation['current_mastery'] * 100 }}%"></div>
  440. </div>
  441. </div>
  442. <p class="text-xs text-gray-600 mb-3">{{ $recommendation['reason'] }}</p>
  443. <button class="w-full text-xs bg-indigo-600 text-white py-2 px-3 rounded-md hover:bg-indigo-700 transition-colors">
  444. 生成学习路径
  445. </button>
  446. </div>
  447. @endforeach
  448. </div>
  449. </div>
  450. </div>
  451. @endif
  452. @endif
  453. {{-- 通知脚本 --}}
  454. <script>
  455. document.addEventListener('notify', (event) => {
  456. const message = event.detail.message;
  457. const type = event.detail.type || 'info';
  458. // 这里可以使用您喜欢的通知库,如toast、notyf等
  459. if (window.Alpine && window.Alpine.store) {
  460. Alpine.store('notifications').add(message, type);
  461. } else {
  462. alert(message);
  463. }
  464. });
  465. </script>
  466. </div>