list-pre-question-candidates.blade.php 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134
  1. <div class="ui-page">
  2. <div class="mx-auto flex max-w-7xl flex-col gap-6 px-4 py-8">
  3. @include('filament.partials.page-header', [
  4. 'kicker' => 'Markdown 导入',
  5. 'title' => 'Step 2 · 结构识别与预览',
  6. 'subtitle' => '左侧查看卷子结构,右侧进行题目校对与批量入库。',
  7. 'actions' => view('filament.partials.density-toggle') . view('filament.partials.quick-links'),
  8. ])
  9. <div class="grid grid-cols-1 gap-4 md:grid-cols-3 xl:grid-cols-5">
  10. <div class="ui-stat">
  11. <div class="ui-stat-label">总题数</div>
  12. <div class="ui-stat-value">{{ $this->summaryStats['total'] ?? 0 }}</div>
  13. </div>
  14. <div class="ui-stat">
  15. <div class="ui-stat-label">待审核</div>
  16. <div class="ui-stat-value text-amber-600">{{ $this->summaryStats['pending'] ?? 0 }}</div>
  17. </div>
  18. <div class="ui-stat">
  19. <div class="ui-stat-label">已审核</div>
  20. <div class="ui-stat-value text-blue-600">{{ $this->summaryStats['reviewed'] ?? 0 }}</div>
  21. </div>
  22. <div class="ui-stat">
  23. <div class="ui-stat-label">已接受</div>
  24. <div class="ui-stat-value text-emerald-600">{{ $this->summaryStats['accepted'] ?? 0 }}</div>
  25. </div>
  26. <div class="ui-stat">
  27. <div class="ui-stat-label">已拒绝</div>
  28. <div class="ui-stat-value text-rose-600">{{ $this->summaryStats['rejected'] ?? 0 }}</div>
  29. </div>
  30. </div>
  31. <div class="grid grid-cols-1 gap-6 lg:grid-cols-12">
  32. <div class="lg:col-span-4 space-y-6">
  33. <div class="ui-card">
  34. <div class="ui-card-header">
  35. <div>
  36. <div class="ui-section-title">卷子结构</div>
  37. <div class="ui-subtitle">source_papers → paper_parts</div>
  38. </div>
  39. </div>
  40. <div class="ui-card-body space-y-4">
  41. @forelse($this->paperTree as $paper)
  42. <div class="rounded-xl border border-slate-200 bg-slate-50 px-4 py-3">
  43. <div class="text-sm font-semibold text-slate-900">{{ $paper['title'] }}</div>
  44. <div class="mt-2 space-y-2">
  45. @foreach($paper['parts'] ?? [] as $part)
  46. <div class="flex items-center justify-between rounded-lg border border-slate-200 bg-white px-3 py-2 text-sm">
  47. <span class="text-slate-700">{{ $part['title'] }}</span>
  48. <span class="ui-badge-muted">{{ $part['count'] }} 题</span>
  49. </div>
  50. @endforeach
  51. </div>
  52. </div>
  53. @empty
  54. @include('filament.partials.empty-state', [
  55. 'title' => '暂无解析结构',
  56. 'description' => '请先完成 Markdown 解析流程。',
  57. ])
  58. @endforelse
  59. </div>
  60. </div>
  61. <div class="ui-card">
  62. <div class="ui-card-header">
  63. <div>
  64. <div class="ui-section-title">Step 3 · 审核入库</div>
  65. <div class="ui-subtitle">使用批量操作提交入库</div>
  66. </div>
  67. </div>
  68. <div class="ui-card-body text-sm text-slate-500">
  69. 选中候选题后,使用批量操作「入库到筛选库」完成审核流程。
  70. </div>
  71. </div>
  72. </div>
  73. <div class="lg:col-span-8">
  74. <div class="ui-card">
  75. <div class="ui-card-header">
  76. <div>
  77. <div class="ui-section-title">题目预览与校对</div>
  78. <div class="ui-subtitle">右侧支持编辑与状态标记</div>
  79. </div>
  80. <div class="ui-badge-muted">支持批量操作</div>
  81. </div>
  82. <div class="ui-card-body">
  83. {{ $this->table }}
  84. </div>
  85. </div>
  86. </div>
  87. </div>
  88. </div>
  89. @include('filament.partials.loading-overlay')
  90. </div>
  91. @push('scripts')
  92. <script>
  93. document.addEventListener('DOMContentLoaded', () => {
  94. const params = new URLSearchParams(window.location.search);
  95. const importId = params.get('import_id') || '';
  96. const source = new EventSource(`/admin/markdown-imports/stream?type=pre-question-candidates&import_id=${importId}`);
  97. let refreshTimer = null;
  98. const refreshLivewire = () => {
  99. if (refreshTimer) return;
  100. refreshTimer = setTimeout(() => {
  101. refreshTimer = null;
  102. }, 800);
  103. if (!window.Livewire || !Livewire.find) {
  104. return;
  105. }
  106. document.querySelectorAll('[wire\\:id]').forEach((el) => {
  107. const id = el.getAttribute('wire:id');
  108. const component = id ? Livewire.find(id) : null;
  109. if (component && typeof component.$refresh === 'function') {
  110. component.$refresh();
  111. }
  112. });
  113. };
  114. source.addEventListener('update', (event) => {
  115. try {
  116. JSON.parse(event.data || '{}');
  117. } catch (e) {
  118. return;
  119. }
  120. refreshLivewire();
  121. });
  122. window.addEventListener('beforeunload', () => source.close());
  123. });
  124. </script>
  125. @endpush