ExamHistory.php 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248
  1. <?php
  2. namespace App\Filament\Pages;
  3. use App\Services\QuestionBankService;
  4. use BackedEnum;
  5. use Filament\Notifications\Notification;
  6. use Filament\Pages\Page;
  7. use UnitEnum;
  8. use Livewire\Attributes\Computed;
  9. class ExamHistory extends Page
  10. {
  11. protected static ?string $title = '卷子历史记录';
  12. protected static string|BackedEnum|null $navigationIcon = 'heroicon-o-document-duplicate';
  13. protected static ?string $navigationLabel = '卷子历史';
  14. protected static string|UnitEnum|null $navigationGroup = '管理';
  15. protected static ?int $navigationSort = 14;
  16. protected string $view = 'filament.pages.exam-history-simple';
  17. // 分页
  18. public int $currentPage = 1;
  19. public int $perPage = 20;
  20. // 筛选
  21. public ?string $search = null;
  22. public ?string $statusFilter = null;
  23. public ?string $difficultyFilter = null;
  24. // 详情
  25. public ?string $selectedExamId = null;
  26. public array $selectedExamDetail = [];
  27. #[Computed(cache: false)]
  28. public function exams(): array
  29. {
  30. try {
  31. // 从本地数据库读取试卷列表 - 使用 papers 表
  32. // 只显示有对应题目数据的试卷
  33. $query = \App\Models\Paper::whereHas('questions');
  34. // 应用搜索过滤
  35. if ($this->search) {
  36. $query->where('paper_name', 'like', '%' . $this->search . '%');
  37. }
  38. // 应用状态过滤
  39. if ($this->statusFilter) {
  40. $query->where('status', $this->statusFilter);
  41. }
  42. // 应用难度过滤
  43. if ($this->difficultyFilter) {
  44. $query->where('difficulty_category', $this->difficultyFilter);
  45. }
  46. // 分页
  47. $total = $query->count();
  48. $papers = $query->withCount('questions')
  49. ->orderBy('created_at', 'desc')
  50. ->skip(($this->currentPage - 1) * $this->perPage)
  51. ->take($this->perPage)
  52. ->get()
  53. ->map(function ($paper) {
  54. return [
  55. 'id' => $paper->paper_id,
  56. 'paper_name' => $paper->paper_name,
  57. 'question_count' => $paper->questions_count, // 使用实际的题目数量
  58. 'total_score' => $paper->total_score,
  59. 'difficulty_category' => $paper->difficulty_category,
  60. 'status' => $paper->status,
  61. 'created_at' => $paper->created_at,
  62. ];
  63. })
  64. ->toArray();
  65. return [
  66. 'data' => $papers,
  67. 'meta' => [
  68. 'page' => $this->currentPage,
  69. 'per_page' => $this->perPage,
  70. 'total' => $total,
  71. 'total_pages' => ceil($total / $this->perPage),
  72. ]
  73. ];
  74. } catch (\Exception $e) {
  75. \Illuminate\Support\Facades\Log::error('获取试卷列表失败', ['error' => $e->getMessage()]);
  76. return [
  77. 'data' => [],
  78. 'meta' => ['page' => 1, 'per_page' => 20, 'total' => 0, 'total_pages' => 0]
  79. ];
  80. }
  81. }
  82. #[Computed(cache: false)]
  83. public function meta(): array
  84. {
  85. $examsData = $this->exams();
  86. return $examsData['meta'] ?? ['page' => 1, 'per_page' => 20, 'total' => 0, 'total_pages' => 0];
  87. }
  88. public function updatedCurrentPage()
  89. {
  90. $this->reset('selectedExamId', 'selectedExamDetail');
  91. }
  92. public function viewExamDetail(string $examId)
  93. {
  94. $this->selectedExamId = $examId;
  95. $this->loadExamDetail();
  96. }
  97. protected function loadExamDetail()
  98. {
  99. if (!$this->selectedExamId) {
  100. return;
  101. }
  102. // 从本地数据库获取试卷详情,而不是外部API
  103. $paper = \App\Models\Paper::with(['questions' => function($query) {
  104. $query->orderBy('question_number');
  105. }])->find($this->selectedExamId);
  106. if ($paper) {
  107. $this->selectedExamDetail = [
  108. 'paper_id' => $paper->paper_id,
  109. 'paper_name' => $paper->paper_name,
  110. 'question_count' => $paper->questions->count(), // 使用实际题目数量
  111. 'total_score' => $paper->total_score,
  112. 'difficulty_category' => $paper->difficulty_category,
  113. 'status' => $paper->status,
  114. 'created_at' => $paper->created_at,
  115. 'updated_at' => $paper->updated_at,
  116. 'questions' => $paper->questions->map(function($question) {
  117. return [
  118. 'id' => $question->id,
  119. 'question_number' => $question->question_number,
  120. 'question_bank_id' => $question->question_bank_id,
  121. 'question_type' => $question->question_type,
  122. 'score' => $question->score,
  123. 'knowledge_point' => $question->knowledge_point,
  124. 'difficulty' => $question->difficulty,
  125. ];
  126. })->toArray(),
  127. ];
  128. } else {
  129. $this->selectedExamDetail = [];
  130. }
  131. }
  132. public function exportPdf(string $examId)
  133. {
  134. $questionBankService = app(QuestionBankService::class);
  135. $pdfUrl = $questionBankService->exportExamToPdf($examId);
  136. if ($pdfUrl) {
  137. // TODO: 实际下载PDF
  138. Notification::make()
  139. ->title('PDF导出成功')
  140. ->body('试卷已导出为PDF格式')
  141. ->success()
  142. ->send();
  143. } else {
  144. Notification::make()
  145. ->title('PDF导出失败')
  146. ->body('无法导出试卷,请稍后重试')
  147. ->danger()
  148. ->send();
  149. }
  150. }
  151. public function duplicateExam(array $examData)
  152. {
  153. // 复制试卷配置,用于快速生成类似试卷
  154. $learningService = app(\App\Services\LearningAnalyticsService::class);
  155. // 提取试卷配置
  156. $examConfig = [
  157. 'paper_name' => $examData['paper_name'] . ' (副本)',
  158. 'total_questions' => $examData['question_count'],
  159. 'difficulty_category' => $examData['difficulty_category'] ?? '基础',
  160. 'question_type_ratio' => [
  161. '选择题' => 40,
  162. '填空题' => 30,
  163. '解答题' => 30,
  164. ],
  165. 'difficulty_ratio' => [
  166. '基础' => 50,
  167. '中等' => 35,
  168. '拔高' => 15,
  169. ],
  170. ];
  171. // TODO: 跳转到智能出卷页面并预填充配置
  172. // 这里可以通过session传递配置,或者使用URL参数
  173. Notification::make()
  174. ->title('试卷配置已复制')
  175. ->body('请前往智能出卷页面查看并使用该配置')
  176. ->success()
  177. ->send();
  178. }
  179. public function deleteExam(string $examId)
  180. {
  181. // TODO: 实现删除试卷功能
  182. // 需要在QuestionBankService中添加deleteExam方法
  183. Notification::make()
  184. ->title('删除成功')
  185. ->body('试卷记录已删除')
  186. ->success()
  187. ->send();
  188. $this->reset('selectedExamId', 'selectedExamDetail');
  189. }
  190. public function getStatusColor(string $status): string
  191. {
  192. return match($status) {
  193. 'draft' => 'ghost',
  194. 'completed' => 'success',
  195. 'graded' => 'primary',
  196. default => 'ghost',
  197. };
  198. }
  199. public function getStatusLabel(string $status): string
  200. {
  201. return match($status) {
  202. 'draft' => '草稿',
  203. 'completed' => '已完成',
  204. 'graded' => '已评分',
  205. default => '未知',
  206. };
  207. }
  208. public function getDifficultyColor(string $difficulty): string
  209. {
  210. return match($difficulty) {
  211. '基础' => 'success',
  212. '进阶' => 'warning',
  213. '竞赛' => 'error',
  214. default => 'ghost',
  215. };
  216. }
  217. }