| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605 |
- <?php
- namespace App\Filament\Pages;
- use App\Services\QuestionServiceApi;
- use App\Services\KnowledgeGraphService;
- use App\Services\QuestionBankService;
- use App\Services\PromptService;
- use BackedEnum;
- use Filament\Actions;
- use Filament\Notifications\Notification;
- use Filament\Pages\Page;
- use UnitEnum;
- use Livewire\Attributes\Computed;
- use Livewire\Attributes\On;
- class QuestionManagement extends Page
- {
- protected static ?string $title = '题库管理';
- protected static string|BackedEnum|null $navigationIcon = 'heroicon-o-rectangle-stack';
- protected static string|UnitEnum|null $navigationGroup = '题库系统';
- protected static ?string $navigationLabel = '题库管理';
- protected static ?int $navigationSort = 2;
- protected string $view = 'filament.pages.question-management';
- public ?string $search = null;
- public ?string $selectedKpCode = null;
- public ?string $selectedDifficulty = null;
- public int $currentPage = 1;
- public int $perPage = 25;
- // 生成题目相关属性
- public ?string $generateKpCode = null;
- public array $selectedSkills = [];
- public int $questionCount = 100;
- public ?string $promptTemplate = null;
- public bool $showGenerateModal = false;
- public bool $showPromptModal = false;
- // 异步任务相关属性
- public ?string $currentTaskId = null;
- public ?string $currentTaskStatus = null;
- public int $currentTaskProgress = 0;
- public ?string $currentTaskMessage = null;
- public bool $isGenerating = false;
- /**
- * 计算属性:从 API 获取题目列表
- */
- #[Computed]
- public function questions(): array
- {
- $service = app(QuestionServiceApi::class);
- $filters = array_filter([
- 'kp_code' => $this->selectedKpCode,
- 'difficulty' => $this->selectedDifficulty,
- 'search' => $this->search,
- ], fn ($value) => filled($value));
- $response = $service->listQuestions($this->currentPage, $this->perPage, $filters);
- return $response['data'] ?? [];
- }
- /**
- * 计算属性:分页信息
- */
- #[Computed]
- public function meta(): array
- {
- $service = app(QuestionServiceApi::class);
- $filters = array_filter([
- 'kp_code' => $this->selectedKpCode,
- 'difficulty' => $this->selectedDifficulty,
- 'search' => $this->search,
- ], fn ($value) => filled($value));
- $response = $service->listQuestions($this->currentPage, $this->perPage, $filters);
- return $response['meta'] ?? [
- 'page' => 1,
- 'per_page' => 25,
- 'total' => 0,
- 'total_pages' => 0,
- ];
- }
- /**
- * 计算属性:统计数据
- */
- #[Computed]
- public function statistics(): array
- {
- $service = app(QuestionServiceApi::class);
- return $service->getStatistics();
- }
- /**
- * 计算属性:知识点选项
- */
- #[Computed]
- public function knowledgePointOptions(): array
- {
- $service = app(QuestionServiceApi::class);
- return $service->getKnowledgePointOptions();
- }
- /**
- * 计算属性:根据知识点获取技能列表
- */
- #[Computed]
- public function skillsOptions(): array
- {
- if (!$this->generateKpCode) {
- return [];
- }
- $service = app(KnowledgeGraphService::class);
- return $service->getSkillsByKnowledgePoint($this->generateKpCode);
- }
- /**
- * 计算属性:提示词模板
- */
- #[Computed]
- public function promptTemplateData(): array
- {
- $service = app(PromptService::class);
- try {
- $response = $service->listPrompts();
- if (!empty($response)) {
- return $response;
- }
- } catch (\Exception $e) {
- // 使用默认提示词
- }
- // 返回默认提示词模板
- return [[
- 'id' => 'default',
- 'template_name' => 'AI题目生成_增强版',
- 'template_content' => $service->getDefaultPromptTemplate(),
- 'version' => 2,
- 'is_active' => true,
- 'description' => '增强版AI题目生成模板,支持精确的难度和题型分布控制',
- 'tags' => 'AI生成,增强版,智能分布'
- ]];
- }
- /**
- * 打开生成题目模态框
- */
- public function openGenerateModal(): void
- {
- $this->showGenerateModal = true;
- }
- /**
- * 关闭生成题目模态框
- */
- public function closeGenerateModal(): void
- {
- $this->showGenerateModal = false;
- $this->reset(['generateKpCode', 'selectedSkills', 'questionCount']);
- }
- /**
- * 打开提示词编辑模态框
- */
- public function openPromptModal(): void
- {
- $templates = $this->promptTemplateData;
- if (!empty($templates)) {
- $this->promptTemplate = $templates[0]['template_content'] ?? null;
- }
- $this->showPromptModal = true;
- }
- /**
- * 关闭提示词编辑模态框
- */
- public function closePromptModal(): void
- {
- $this->showPromptModal = false;
- $this->reset('promptTemplate');
- }
- /**
- * 全选/取消全选技能
- */
- public function toggleAllSkills(): void
- {
- $skills = $this->skillsOptions;
- if (count($this->selectedSkills) === count($skills)) {
- $this->selectedSkills = [];
- } else {
- $this->selectedSkills = array_column($skills, 'code');
- }
- }
- /**
- * 监听知识点选择变化
- */
- public function updatedGenerateKpCode(): void
- {
- // 选择新知识点时重置技能选择
- $this->selectedSkills = [];
- // 重新计算skillsOptions会自动触发(通过#[Computed])
- }
- /**
- * 监听技能选择变化
- */
- public function updatedSelectedSkills(): void
- {
- // 可选:在这里添加其他逻辑
- }
- /**
- * 搜索更新处理
- */
- public function updatedSearch(): void
- {
- $this->currentPage = 1;
- }
- /**
- * 知识点筛选更新处理
- */
- public function updatedSelectedKpCode(): void
- {
- $this->currentPage = 1;
- }
- /**
- * 难度筛选更新处理
- */
- public function updatedSelectedDifficulty(): void
- {
- $this->currentPage = 1;
- }
- /**
- * 每页数量更新处理
- */
- public function updatedPerPage(): void
- {
- $this->currentPage = 1;
- }
- /**
- * 刷新数据
- */
- #[On('refresh-data')]
- public function refreshData(): void
- {
- $this->resetCache();
- Notification::make()
- ->title('数据已刷新')
- ->success()
- ->send();
- }
- /**
- * AI 生成题目
- */
- #[On('ai-generate')]
- public function aiGenerate(): void
- {
- // 打开生成模态框
- $this->openGenerateModal();
- }
- /**
- * 执行生成题目(异步模式)
- */
- #[On('execute-generate')]
- public function executeGenerate(): void
- {
- if (!$this->generateKpCode) {
- Notification::make()
- ->title('请选择知识点')
- ->danger()
- ->send();
- return;
- }
- if (empty($this->selectedSkills)) {
- Notification::make()
- ->title('请至少选择一个技能')
- ->danger()
- ->send();
- return;
- }
- try {
- $service = app(QuestionBankService::class);
- // 构建回调 URL
- $callbackUrl = route('api.questions.callback');
- $result = $service->generateIntelligentQuestions([
- 'kp_code' => $this->generateKpCode,
- 'skills' => $this->selectedSkills,
- 'count' => $this->questionCount,
- 'prompt_template' => $this->promptTemplate
- ], $callbackUrl);
- if ($result['success'] ?? false) {
- // 获取task_id
- $this->currentTaskId = $result['task_id'] ?? null;
- // 关闭模态框
- $this->showGenerateModal = false;
- // 显示提示消息
- Notification::make()
- ->title('任务已创建')
- ->body("任务 ID: {$this->currentTaskId}\n题目生成完成后将自动刷新列表")
- ->info()
- ->send();
- // 不再轮询,等待回调
- } else {
- $error = $result['message'] ?? '未知错误';
- Notification::make()
- ->title('创建任务失败')
- ->body($error)
- ->danger()
- ->send();
- }
- } catch (\Exception $e) {
- Notification::make()
- ->title('生成异常')
- ->body($e->getMessage())
- ->danger()
- ->send();
- }
- }
- /**
- * 轮询任务状态
- */
- public function pollTaskStatus(): void
- {
- if (!$this->currentTaskId) {
- return;
- }
- try {
- $service = app(QuestionBankService::class);
- $taskStatus = $service->getTaskStatus($this->currentTaskId);
- if ($taskStatus) {
- $this->currentTaskStatus = $taskStatus['status'] ?? 'unknown';
- $this->currentTaskProgress = $taskStatus['progress'] ?? 0;
- $this->currentTaskMessage = $this->getTaskStatusMessage($taskStatus);
- // 如果任务完成或失败,停止轮询
- if (in_array($this->currentTaskStatus, ['completed', 'failed'])) {
- $this->isGenerating = false;
- if ($this->currentTaskStatus === 'completed') {
- $result = $taskStatus['result'] ?? null;
- $total = $result['total'] ?? $this->questionCount;
- Notification::make()
- ->title('生成完成')
- ->body("已为知识点 {$this->generateKpCode} 成功生成 {$total} 道题目")
- ->success()
- ->send();
- // 刷新数据
- $this->refreshData();
- } else {
- $error = $taskStatus['error_message'] ?? '未知错误';
- Notification::make()
- ->title('生成失败')
- ->body($error)
- ->danger()
- ->send();
- }
- // 重置任务状态
- $this->currentTaskId = null;
- $this->currentTaskStatus = null;
- $this->currentTaskProgress = 0;
- $this->currentTaskMessage = null;
- } else {
- // 继续轮询
- $this->dispatch('$refresh');
- $this->dispatch('poll-task');
- }
- }
- } catch (\Exception $e) {
- $this->isGenerating = false;
- Notification::make()
- ->title('查询任务状态失败')
- ->body($e->getMessage())
- ->danger()
- ->send();
- }
- }
- /**
- * 获取任务状态消息
- */
- private function getTaskStatusMessage(array $taskStatus): string
- {
- $status = $taskStatus['status'] ?? 'unknown';
- $progress = $taskStatus['progress'] ?? 0;
- return match ($status) {
- 'pending' => '任务已创建,等待开始...',
- 'processing' => "正在生成题目... {$progress}%",
- 'completed' => '生成完成',
- 'failed' => '生成失败',
- default => '未知状态',
- };
- }
- /**
- * 取消生成任务
- */
- #[On('cancel-generate')]
- public function cancelGenerate(): void
- {
- $this->isGenerating = false;
- $this->currentTaskId = null;
- $this->currentTaskStatus = null;
- $this->currentTaskProgress = 0;
- $this->currentTaskMessage = null;
- Notification::make()
- ->title('已取消生成任务')
- ->warning()
- ->send();
- }
- /**
- * 保存提示词
- */
- #[On('save-prompt')]
- public function savePrompt(): void
- {
- if (!$this->promptTemplate) {
- Notification::make()
- ->title('提示词不能为空')
- ->danger()
- ->send();
- return;
- }
- try {
- $service = app(PromptService::class);
- $result = $service->savePrompt([
- 'template_name' => 'AI题目生成_增强版',
- 'template_type' => '题目生成',
- 'template_content' => $this->promptTemplate,
- 'version' => 2,
- 'is_active' => true,
- 'description' => '增强版AI题目生成模板,支持精确的难度和题型分布控制',
- 'tags' => 'AI生成,增强版,智能分布'
- ]);
- if ($result['success'] ?? false) {
- Notification::make()
- ->title('提示词保存成功')
- ->success()
- ->send();
- } else {
- Notification::make()
- ->title('保存失败')
- ->body($result['message'] ?? '未知错误')
- ->danger()
- ->send();
- }
- $this->closePromptModal();
- } catch (\Exception $e) {
- Notification::make()
- ->title('保存异常')
- ->body($e->getMessage())
- ->danger()
- ->send();
- }
- }
- /**
- * 删除题目
- */
- public function deleteQuestion(string $questionCode): void
- {
- try {
- $service = app(QuestionBankService::class);
- $result = $service->deleteQuestion($questionCode);
- if ($result) {
- // 显示成功消息
- Notification::make()
- ->title('删除成功')
- ->body("题目 {$questionCode} 已删除")
- ->success()
- ->send();
- // 延迟1秒后刷新页面,确保用户体验好
- $this->dispatch('show-message', [
- 'type' => 'success',
- 'message' => "题目 {$questionCode} 已删除"
- ]);
- // 强制刷新页面
- $this->dispatch('refresh-page');
- } else {
- Notification::make()
- ->title('删除失败')
- ->body("题目 {$questionCode} 不存在或已被删除")
- ->warning()
- ->send();
- }
- } catch (\Exception $e) {
- Notification::make()
- ->title('删除异常')
- ->body($e->getMessage())
- ->danger()
- ->send();
- }
- }
- /**
- * 智能搜索
- */
- #[On('smart-search')]
- public function smartSearch(): void
- {
- Notification::make()
- ->title('智能搜索功能')
- ->body('请使用搜索框输入关键词')
- ->info()
- ->send();
- }
- /**
- * 跳转到指定页
- */
- public function gotoPage(int $page): void
- {
- $this->currentPage = $page;
- }
- /**
- * 上一页
- */
- public function previousPage(): void
- {
- if ($this->currentPage > 1) {
- $this->currentPage--;
- }
- }
- /**
- * 下一页
- */
- public function nextPage(): void
- {
- if ($this->currentPage < ($this->meta['total_pages'] ?? 1)) {
- $this->currentPage++;
- }
- }
- /**
- * 获取页码数组(用于分页器)
- */
- public function getPages(): array
- {
- $totalPages = $this->meta['total_pages'] ?? 1;
- $currentPage = $this->currentPage;
- $pages = [];
- $start = max(1, $currentPage - 2);
- $end = min($totalPages, $currentPage + 2);
- for ($i = $start; $i <= $end; $i++) {
- $pages[] = $i;
- }
- return $pages;
- }
- /**
- * 头部操作按钮已在视图中直接添加
- */
- }
|