|
|
@@ -1,226 +1,194 @@
|
|
|
-<div>
|
|
|
- <div class="space-y-6">
|
|
|
- <!-- 页面标题 -->
|
|
|
- <div class="flex justify-between items-center">
|
|
|
- <div>
|
|
|
- <h2 class="text-2xl font-bold text-gray-900">卷子历史记录</h2>
|
|
|
- <p class="mt-1 text-sm text-gray-500">
|
|
|
- 查看所有历史生成的试卷,支持导出、复制和删除操作
|
|
|
- </p>
|
|
|
+<div class="ui-page">
|
|
|
+ <div class="mx-auto flex max-w-7xl flex-col gap-6 px-4 py-8">
|
|
|
+ @include('filament.partials.page-header', [
|
|
|
+ 'kicker' => '卷子管理',
|
|
|
+ 'title' => '卷子历史记录',
|
|
|
+ 'subtitle' => '查看生成卷子、导出与编辑配置',
|
|
|
+ 'actions' => new \Illuminate\Support\HtmlString(
|
|
|
+ '<a class="btn btn-primary" href="' . url('/admin/intelligent-exam-generation') . '">新建卷子</a>'
|
|
|
+ . view('filament.partials.density-toggle')->render()
|
|
|
+ ),
|
|
|
+ ])
|
|
|
+
|
|
|
+ @php
|
|
|
+ $total = \App\Models\Paper::count();
|
|
|
+ $draft = \App\Models\Paper::where('status', 'draft')->count();
|
|
|
+ $completed = \App\Models\Paper::where('status', 'completed')->count();
|
|
|
+ $graded = \App\Models\Paper::where('status', 'graded')->count();
|
|
|
+ @endphp
|
|
|
+
|
|
|
+ <div class="grid grid-cols-1 gap-4 md:grid-cols-4">
|
|
|
+ <div class="ui-stat">
|
|
|
+ <div class="ui-stat-label">总卷数</div>
|
|
|
+ <div class="ui-stat-value">{{ $total }}</div>
|
|
|
+ </div>
|
|
|
+ <div class="ui-stat">
|
|
|
+ <div class="ui-stat-label">草稿</div>
|
|
|
+ <div class="ui-stat-value text-amber-600">{{ $draft }}</div>
|
|
|
+ </div>
|
|
|
+ <div class="ui-stat">
|
|
|
+ <div class="ui-stat-label">已完成</div>
|
|
|
+ <div class="ui-stat-value text-emerald-600">{{ $completed }}</div>
|
|
|
+ </div>
|
|
|
+ <div class="ui-stat">
|
|
|
+ <div class="ui-stat-label">已评分</div>
|
|
|
+ <div class="ui-stat-value text-blue-600">{{ $graded }}</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
|
|
|
- <!-- 筛选器 - 使用 DaisyUI -->
|
|
|
- <div class="card bg-base-100 shadow-xl">
|
|
|
- <div class="card-body">
|
|
|
- <div class="grid grid-cols-1 md:grid-cols-4 gap-4">
|
|
|
- <input
|
|
|
- type="text"
|
|
|
- wire:model.live="search"
|
|
|
- placeholder="搜索试卷名称..."
|
|
|
- class="input input-bordered input-primary w-full"
|
|
|
- />
|
|
|
+ <div class="ui-filter-bar">
|
|
|
+ <div class="grid grid-cols-1 gap-4 md:grid-cols-4">
|
|
|
+ <input
|
|
|
+ type="text"
|
|
|
+ wire:model.live="search"
|
|
|
+ placeholder="搜索试卷名称..."
|
|
|
+ class="input input-bordered w-full"
|
|
|
+ />
|
|
|
|
|
|
- <select wire:model.live="statusFilter" class="select select-bordered select-primary w-full">
|
|
|
- <option value="">-- 全部状态 --</option>
|
|
|
- <option value="draft">草稿</option>
|
|
|
- <option value="completed">已完成</option>
|
|
|
- <option value="graded">已评分</option>
|
|
|
- </select>
|
|
|
+ <select wire:model.live="statusFilter" class="select select-bordered w-full">
|
|
|
+ <option value="">-- 全部状态 --</option>
|
|
|
+ <option value="draft">草稿</option>
|
|
|
+ <option value="completed">已完成</option>
|
|
|
+ <option value="graded">已评分</option>
|
|
|
+ </select>
|
|
|
|
|
|
- <select wire:model.live="difficultyFilter" class="select select-bordered select-primary w-full">
|
|
|
- <option value="">-- 全部难度 --</option>
|
|
|
- <option value="基础">基础</option>
|
|
|
- <option value="进阶">进阶</option>
|
|
|
- <option value="竞赛">竞赛</option>
|
|
|
- </select>
|
|
|
+ <select wire:model.live="difficultyFilter" class="select select-bordered w-full">
|
|
|
+ <option value="">-- 全部难度 --</option>
|
|
|
+ <option value="基础">基础</option>
|
|
|
+ <option value="进阶">进阶</option>
|
|
|
+ <option value="竞赛">竞赛</option>
|
|
|
+ </select>
|
|
|
|
|
|
- <button
|
|
|
- wire:click="$refresh"
|
|
|
- type="button"
|
|
|
- class="btn btn-outline btn-secondary">
|
|
|
- 重置
|
|
|
- </button>
|
|
|
- </div>
|
|
|
+ <button wire:click="$refresh" type="button" class="btn btn-secondary">重置</button>
|
|
|
</div>
|
|
|
</div>
|
|
|
|
|
|
- <!-- 试卷列表 - 全宽表格视图 -->
|
|
|
- <div class="w-full">
|
|
|
- <div class="card bg-base-100 shadow-xl overflow-hidden">
|
|
|
- <div class="overflow-x-auto">
|
|
|
- <table class="table table-zebra w-full">
|
|
|
- <thead>
|
|
|
+ <div class="ui-card">
|
|
|
+ <div class="ui-card-header">
|
|
|
+ <div>
|
|
|
+ <div class="ui-section-title">卷子列表</div>
|
|
|
+ <div class="ui-subtitle">卷名、状态、题量与操作入口</div>
|
|
|
+ </div>
|
|
|
+ <div class="ui-badge-muted">支持批量查看</div>
|
|
|
+ </div>
|
|
|
+ <div class="ui-card-body overflow-x-auto">
|
|
|
+ <table class="table w-full">
|
|
|
+ <thead class="ui-table-head">
|
|
|
+ <tr>
|
|
|
+ <th>试卷名称</th>
|
|
|
+ <th>状态</th>
|
|
|
+ <th>难度</th>
|
|
|
+ <th>题目/总分</th>
|
|
|
+ <th>创建时间</th>
|
|
|
+ <th class="text-right">操作</th>
|
|
|
+ </tr>
|
|
|
+ </thead>
|
|
|
+ <tbody>
|
|
|
+ @forelse($this->exams()['data'] as $exam)
|
|
|
+ <tr class="hover">
|
|
|
+ <td>
|
|
|
+ <div class="font-semibold text-slate-900">{{ $exam['paper_name'] }}</div>
|
|
|
+ <div class="text-xs text-slate-400">{{ $exam['id'] }}</div>
|
|
|
+ </td>
|
|
|
+ <td>
|
|
|
+ <span class="badge badge-{{ $this->getStatusColor($exam['status']) }} badge-sm">
|
|
|
+ {{ $this->getStatusLabel($exam['status']) }}
|
|
|
+ </span>
|
|
|
+ </td>
|
|
|
+ <td>
|
|
|
+ <span class="badge badge-{{ $this->getDifficultyColor($exam['difficulty_category']) }} badge-sm">
|
|
|
+ {{ $exam['difficulty_category'] }}
|
|
|
+ </span>
|
|
|
+ </td>
|
|
|
+ <td>
|
|
|
+ <div class="text-sm">{{ $exam['question_count'] }} 题</div>
|
|
|
+ <div class="text-xs text-slate-400">{{ $exam['total_score'] }} 分</div>
|
|
|
+ </td>
|
|
|
+ <td class="text-sm">
|
|
|
+ {{ \Carbon\Carbon::parse($exam['created_at'])->format('Y-m-d H:i') }}
|
|
|
+ </td>
|
|
|
+ <td class="text-right">
|
|
|
+ <div class="inline-flex items-center gap-2">
|
|
|
+ <a href="{{ url('/admin/exam-detail?paperId=' . $exam['id']) }}" class="btn btn-ghost btn-xs">查看</a>
|
|
|
+ <button wire:click.stop="exportPdf('{{ $exam['id'] }}')" class="btn btn-outline btn-xs">导出</button>
|
|
|
+ <div class="dropdown dropdown-end">
|
|
|
+ <label tabindex="0" class="btn btn-ghost btn-xs">更多</label>
|
|
|
+ <ul tabindex="0" class="dropdown-content z-[1] menu rounded-box w-32 bg-base-100 p-2 shadow">
|
|
|
+ <li><button wire:click.stop="duplicateExam({{ json_encode($exam) }})">复制配置</button></li>
|
|
|
+ <li><button wire:click.stop="startEditExam('{{ $exam['id'] }}')">编辑</button></li>
|
|
|
+ <li><button class="text-error" wire:click.stop="deleteExam('{{ $exam['id'] }}')" wire:confirm="确定要删除这份试卷吗?此操作不可恢复!">删除</button></li>
|
|
|
+ </ul>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </td>
|
|
|
+ </tr>
|
|
|
+ @empty
|
|
|
<tr>
|
|
|
- <th>试卷名称</th>
|
|
|
- <th>状态</th>
|
|
|
- <th>难度</th>
|
|
|
- <th>题目/总分</th>
|
|
|
- <th>创建时间</th>
|
|
|
- <th>操作</th>
|
|
|
+ <td colspan="6" class="py-10">
|
|
|
+ @include('filament.partials.empty-state', [
|
|
|
+ 'title' => '暂无试卷记录',
|
|
|
+ 'description' => '请先生成卷子以便管理。',
|
|
|
+ 'action' => new \Illuminate\Support\HtmlString('<a class="btn btn-primary btn-sm" href="' . url('/admin/intelligent-exam-generation') . '">去出卷</a>'),
|
|
|
+ ])
|
|
|
+ </td>
|
|
|
</tr>
|
|
|
- </thead>
|
|
|
- <tbody>
|
|
|
- @forelse($this->exams()['data'] as $exam)
|
|
|
- <tr class="hover">
|
|
|
- <td>
|
|
|
- <div class="font-bold">{{ $exam['paper_name'] }}</div>
|
|
|
- <div class="text-xs opacity-50">{{ $exam['id'] }}</div>
|
|
|
- </td>
|
|
|
- <td>
|
|
|
- <span class="badge badge-{{ $this->getStatusColor($exam['status']) }} badge-sm">
|
|
|
- {{ $this->getStatusLabel($exam['status']) }}
|
|
|
- </span>
|
|
|
- </td>
|
|
|
- <td>
|
|
|
- <span class="badge badge-{{ $this->getDifficultyColor($exam['difficulty_category']) }} badge-sm">
|
|
|
- {{ $exam['difficulty_category'] }}
|
|
|
- </span>
|
|
|
- </td>
|
|
|
- <td>
|
|
|
- <div class="text-sm">{{ $exam['question_count'] }} 题</div>
|
|
|
- <div class="text-xs opacity-50">{{ $exam['total_score'] }} 分</div>
|
|
|
- </td>
|
|
|
- <td class="text-sm">
|
|
|
- {{ \Carbon\Carbon::parse($exam['created_at'])->format('Y-m-d H:i') }}
|
|
|
- </td>
|
|
|
- <td>
|
|
|
- <div class="flex gap-2">
|
|
|
- <a href="{{ url('/admin/exam-detail?paperId=' . $exam['id']) }}"
|
|
|
- class="btn btn-ghost btn-xs tooltip"
|
|
|
- data-tip="查看详情">
|
|
|
- <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
|
- <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"></path>
|
|
|
- <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z"></path>
|
|
|
- </svg>
|
|
|
- </a>
|
|
|
- <button
|
|
|
- wire:click.stop="exportPdf('{{ $exam['id'] }}')"
|
|
|
- class="btn btn-ghost btn-xs tooltip"
|
|
|
- data-tip="导出PDF">
|
|
|
- <svg class="w-4 h-4" 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.stop="duplicateExam({{ json_encode($exam) }})"
|
|
|
- class="btn btn-ghost btn-xs tooltip"
|
|
|
- data-tip="复制配置">
|
|
|
- <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z"></path></svg>
|
|
|
- </button>
|
|
|
- <button
|
|
|
- wire:click.stop="startEditExam('{{ $exam['id'] }}')"
|
|
|
- class="btn btn-ghost btn-xs tooltip"
|
|
|
- data-tip="编辑试卷">
|
|
|
- <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z"></path></svg>
|
|
|
- </button>
|
|
|
- <button
|
|
|
- wire:click.stop="deleteExam('{{ $exam['id'] }}')"
|
|
|
- wire:confirm="确定要删除这份试卷吗?此操作不可恢复!"
|
|
|
- class="btn btn-ghost btn-xs tooltip text-error"
|
|
|
- data-tip="删除试卷">
|
|
|
- <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"></path></svg>
|
|
|
- </button>
|
|
|
- </div>
|
|
|
- </td>
|
|
|
- </tr>
|
|
|
- @empty
|
|
|
- <tr>
|
|
|
- <td colspan="6" class="text-center py-8">
|
|
|
- <div class="flex flex-col items-center justify-center text-gray-500">
|
|
|
- <svg class="w-12 h-12 mb-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
|
- <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"></path>
|
|
|
- </svg>
|
|
|
- <p>暂无试卷记录</p>
|
|
|
- <a href="{{ url('/admin/intelligent-exam-generation') }}" class="btn btn-primary btn-sm mt-2">
|
|
|
- 去出卷
|
|
|
- </a>
|
|
|
- </div>
|
|
|
- </td>
|
|
|
- </tr>
|
|
|
- @endforelse
|
|
|
- </tbody>
|
|
|
- </table>
|
|
|
- </div>
|
|
|
-
|
|
|
- <!-- 分页 -->
|
|
|
- <div class="p-4 border-t">
|
|
|
- <div class="flex justify-between items-center">
|
|
|
- <div class="text-sm text-gray-500">
|
|
|
- 共 {{ $this->meta()['total'] }} 条记录
|
|
|
- </div>
|
|
|
- <div class="join">
|
|
|
- <button class="join-item btn btn-sm" wire:click="$set('currentPage', {{ max(1, $this->currentPage - 1) }})" {{ $this->currentPage <= 1 ? 'disabled' : '' }}>«</button>
|
|
|
- <button class="join-item btn btn-sm">第 {{ $this->currentPage }} 页</button>
|
|
|
- <button class="join-item btn btn-sm" wire:click="$set('currentPage', {{ $this->currentPage + 1 }})" {{ $this->currentPage >= $this->meta()['total_pages'] ? 'disabled' : '' }}>»</button>
|
|
|
- </div>
|
|
|
+ @endforelse
|
|
|
+ </tbody>
|
|
|
+ </table>
|
|
|
+ </div>
|
|
|
+ <div class="border-t border-slate-200 px-4 py-3">
|
|
|
+ <div class="flex items-center justify-between">
|
|
|
+ <div class="text-sm text-slate-500">共 {{ $this->meta()['total'] }} 条记录</div>
|
|
|
+ <div class="join">
|
|
|
+ <button class="join-item btn btn-sm" wire:click="$set('currentPage', {{ max(1, $this->currentPage - 1) }})" {{ $this->currentPage <= 1 ? 'disabled' : '' }}>«</button>
|
|
|
+ <button class="join-item btn btn-sm">第 {{ $this->currentPage }} 页</button>
|
|
|
+ <button class="join-item btn btn-sm" wire:click="$set('currentPage', {{ $this->currentPage + 1 }})" {{ $this->currentPage >= $this->meta()['total_pages'] ? 'disabled' : '' }}>»</button>
|
|
|
</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
|
|
|
- {{-- 编辑试卷模态框 --}}
|
|
|
@if($editingExamId)
|
|
|
- <div class="modal modal-open">
|
|
|
- <div class="modal-box">
|
|
|
- <h3 class="font-bold text-lg mb-4">编辑试卷</h3>
|
|
|
-
|
|
|
- <div class="space-y-4">
|
|
|
- <div class="form-control">
|
|
|
- <label class="label">
|
|
|
- <span class="label-text">试卷名称</span>
|
|
|
- </label>
|
|
|
- <input type="text" wire:model="editForm.paper_name"
|
|
|
- class="input input-bordered input-primary"
|
|
|
- placeholder="请输入试卷名称" />
|
|
|
- @error('editForm.paper_name')
|
|
|
+ <div class="modal modal-open">
|
|
|
+ <div class="modal-box">
|
|
|
+ <h3 class="text-lg font-semibold text-slate-900">编辑试卷</h3>
|
|
|
+ <div class="mt-4 space-y-4">
|
|
|
+ <div class="form-control">
|
|
|
<label class="label">
|
|
|
- <span class="label-text-alt text-error">{{ $message }}</span>
|
|
|
+ <span class="label-text">试卷名称</span>
|
|
|
</label>
|
|
|
- @enderror
|
|
|
- </div>
|
|
|
-
|
|
|
- <div class="form-control">
|
|
|
- <label class="label">
|
|
|
- <span class="label-text">难度分类</span>
|
|
|
- </label>
|
|
|
- <select wire:model="editForm.difficulty_category"
|
|
|
- class="select select-bordered select-primary">
|
|
|
- <option value="">-- 请选择难度 --</option>
|
|
|
- <option value="基础">基础</option>
|
|
|
- <option value="进阶">进阶</option>
|
|
|
- <option value="竞赛">竞赛</option>
|
|
|
- </select>
|
|
|
- @error('editForm.difficulty_category')
|
|
|
+ <input type="text" wire:model="editForm.paper_name" class="input input-bordered" placeholder="请输入试卷名称" />
|
|
|
+ </div>
|
|
|
+ <div class="form-control">
|
|
|
<label class="label">
|
|
|
- <span class="label-text-alt text-error">{{ $message }}</span>
|
|
|
+ <span class="label-text">难度分类</span>
|
|
|
</label>
|
|
|
- @enderror
|
|
|
- </div>
|
|
|
-
|
|
|
- <div class="form-control">
|
|
|
- <label class="label">
|
|
|
- <span class="label-text">状态</span>
|
|
|
- </label>
|
|
|
- <select wire:model="editForm.status"
|
|
|
- class="select select-bordered select-primary">
|
|
|
- <option value="">-- 请选择状态 --</option>
|
|
|
- <option value="draft">草稿</option>
|
|
|
- <option value="completed">已完成</option>
|
|
|
- <option value="graded">已评分</option>
|
|
|
- </select>
|
|
|
- @error('editForm.status')
|
|
|
+ <select wire:model="editForm.difficulty_category" class="select select-bordered">
|
|
|
+ <option value="">-- 请选择难度 --</option>
|
|
|
+ <option value="基础">基础</option>
|
|
|
+ <option value="进阶">进阶</option>
|
|
|
+ <option value="竞赛">竞赛</option>
|
|
|
+ </select>
|
|
|
+ </div>
|
|
|
+ <div class="form-control">
|
|
|
<label class="label">
|
|
|
- <span class="label-text-alt text-error">{{ $message }}</span>
|
|
|
+ <span class="label-text">状态</span>
|
|
|
</label>
|
|
|
- @enderror
|
|
|
+ <select wire:model="editForm.status" class="select select-bordered">
|
|
|
+ <option value="">-- 请选择状态 --</option>
|
|
|
+ <option value="draft">草稿</option>
|
|
|
+ <option value="completed">已完成</option>
|
|
|
+ <option value="graded">已评分</option>
|
|
|
+ </select>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div class="modal-action">
|
|
|
+ <button wire:click="cancelEdit" class="btn btn-ghost">取消</button>
|
|
|
+ <button wire:click="saveExamEdit" class="btn btn-primary">保存</button>
|
|
|
</div>
|
|
|
- </div>
|
|
|
-
|
|
|
- <div class="modal-action">
|
|
|
- <button wire:click="cancelEdit" class="btn btn-ghost">取消</button>
|
|
|
- <button wire:click="saveExamEdit" class="btn btn-primary">保存</button>
|
|
|
</div>
|
|
|
</div>
|
|
|
- </div>
|
|
|
@endif
|
|
|
+ @include('filament.partials.loading-overlay')
|
|
|
</div>
|