PromptManagement.php 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402
  1. <?php
  2. namespace App\Filament\Pages;
  3. use App\Services\PromptService;
  4. use BackedEnum;
  5. use Filament\Actions;
  6. use Filament\Actions\Action;
  7. use Filament\Notifications\Notification;
  8. use Filament\Pages\Page;
  9. use UnitEnum;
  10. use Livewire\Attributes\Computed;
  11. use Livewire\Attributes\On;
  12. class PromptManagement extends Page
  13. {
  14. protected static string|BackedEnum|null $navigationIcon = 'heroicon-o-chat-bubble-left-right';
  15. protected static string|UnitEnum|null $navigationGroup = '题库管理';
  16. protected static ?string $navigationLabel = '提示词管理';
  17. protected static ?int $navigationSort = 6;
  18. protected ?string $heading = '提示词管理';
  19. protected string $view = 'filament.pages.prompt-management';
  20. public ?string $selectedType = null;
  21. public ?string $search = null;
  22. public int $currentPage = 1;
  23. public int $perPage = 10;
  24. public array $selectedPrompts = [];
  25. // 表单状态
  26. public bool $showPromptModal = false;
  27. public bool $isEditing = false;
  28. public ?string $editingName = null;
  29. public array $form = [
  30. 'template_name' => '',
  31. 'template_type' => 'question_generation',
  32. 'template_content' => '',
  33. 'variables' => '[]',
  34. 'description' => '',
  35. 'tags' => '',
  36. 'is_active' => 'yes',
  37. ];
  38. /**
  39. * 获取提示词列表
  40. */
  41. public function getPrompts(): array
  42. {
  43. $service = app(PromptService::class);
  44. try {
  45. // 获取所有提示词
  46. $prompts = $service->listPrompts();
  47. // 筛选
  48. if ($this->selectedType) {
  49. $prompts = array_filter($prompts, fn($prompt) =>
  50. $prompt['template_type'] === $this->selectedType
  51. );
  52. }
  53. if ($this->search) {
  54. $searchTerm = strtolower($this->search);
  55. $prompts = array_filter($prompts, fn($prompt) =>
  56. str_contains(strtolower($prompt['template_name']), $searchTerm) ||
  57. str_contains(strtolower($prompt['description']), $searchTerm)
  58. );
  59. }
  60. // 分页
  61. $total = count($prompts);
  62. $offset = ($this->currentPage - 1) * $this->perPage;
  63. $paginated = array_slice($prompts, $offset, $this->perPage);
  64. return [
  65. 'data' => $paginated,
  66. 'meta' => [
  67. 'page' => $this->currentPage,
  68. 'per_page' => $this->perPage,
  69. 'total' => $total,
  70. 'total_pages' => (int) ceil($total / $this->perPage),
  71. ]
  72. ];
  73. } catch (\Exception $e) {
  74. \Log::error('Failed to fetch prompts: ' . $e->getMessage());
  75. return ['data' => [], 'meta' => ['total' => 0]];
  76. }
  77. }
  78. /**
  79. * 获取提示词类型统计
  80. */
  81. public function getTypeStats(): array
  82. {
  83. $service = app(PromptService::class);
  84. try {
  85. $prompts = $service->listPrompts();
  86. $stats = [];
  87. foreach ($prompts as $prompt) {
  88. $type = $prompt['template_type'];
  89. if (!isset($stats[$type])) {
  90. $stats[$type] = 0;
  91. }
  92. $stats[$type]++;
  93. }
  94. return $stats;
  95. } catch (\Exception $e) {
  96. \Log::error('Failed to fetch prompt stats: ' . $e->getMessage());
  97. return [];
  98. }
  99. }
  100. /**
  101. * 获取所有类型选项
  102. */
  103. public function getTypeOptions(): array
  104. {
  105. return [
  106. '题目生成' => '题目生成',
  107. '掌握度评估' => '掌握度评估',
  108. '技能熟练度' => '技能熟练度',
  109. '质量审核' => '质量审核',
  110. ];
  111. }
  112. /**
  113. * 筛选更新
  114. */
  115. public function updatedSelectedType(): void
  116. {
  117. $this->currentPage = 1;
  118. }
  119. public function updatedSearch(): void
  120. {
  121. $this->currentPage = 1;
  122. }
  123. public function updatedPerPage(): void
  124. {
  125. $this->currentPage = 1;
  126. }
  127. /**
  128. * 跳转到指定页
  129. */
  130. public function gotoPage(int $page): void
  131. {
  132. $this->currentPage = $page;
  133. }
  134. /**
  135. * 上一页
  136. */
  137. public function previousPage(): void
  138. {
  139. if ($this->currentPage > 1) {
  140. $this->currentPage--;
  141. }
  142. }
  143. /**
  144. * 下一页
  145. */
  146. public function nextPage(): void
  147. {
  148. $totalPages = $this->prompts['meta']['total_pages'] ?? 1;
  149. if ($this->currentPage < $totalPages) {
  150. $this->currentPage++;
  151. }
  152. }
  153. /**
  154. * 创建新提示词
  155. */
  156. #[On('create-prompt')]
  157. public function createPrompt(): void
  158. {
  159. $this->resetPromptForm();
  160. $this->isEditing = false;
  161. $this->editingName = null;
  162. $this->showPromptModal = true;
  163. }
  164. /**
  165. * 编辑提示词
  166. */
  167. #[On('edit-prompt')]
  168. public function editPrompt(array $prompt): void
  169. {
  170. $this->form = [
  171. 'template_name' => $prompt['template_name'] ?? '',
  172. 'template_type' => $prompt['template_type'] ?? 'question_generation',
  173. 'template_content' => $this->fetchPromptContent($prompt['template_name'] ?? '') ?? '',
  174. 'variables' => $prompt['variables'] ?? '[]',
  175. 'description' => $prompt['description'] ?? '',
  176. 'tags' => $prompt['tags'] ?? '',
  177. 'is_active' => ($prompt['is_active'] === 'yes' || $prompt['is_active'] === true) ? 'yes' : 'no',
  178. ];
  179. $this->isEditing = true;
  180. $this->editingName = $prompt['template_name'] ?? null;
  181. $this->showPromptModal = true;
  182. }
  183. /**
  184. * 删除提示词
  185. */
  186. #[On('delete-prompt')]
  187. public function deletePrompt(string $promptName): void
  188. {
  189. try {
  190. app(PromptService::class)->deletePrompt($promptName);
  191. Notification::make()
  192. ->title('删除成功')
  193. ->body("提示词 {$promptName} 已删除")
  194. ->success()
  195. ->send();
  196. } catch (\Exception $e) {
  197. Notification::make()
  198. ->title('删除失败')
  199. ->body($e->getMessage())
  200. ->danger()
  201. ->send();
  202. }
  203. }
  204. /**
  205. * 启用/禁用提示词
  206. */
  207. #[On('toggle-prompt')]
  208. public function togglePrompt(?string $promptName = null, ?bool $isActive = null): void
  209. {
  210. if (!$promptName || $isActive === null) {
  211. return;
  212. }
  213. try {
  214. $current = app(PromptService::class)->getPrompt($promptName);
  215. if ($current) {
  216. $current['is_active'] = $isActive ? 'no' : 'yes';
  217. app(PromptService::class)->savePrompt($current);
  218. }
  219. Notification::make()
  220. ->title(($isActive ? '已禁用 ' : '已启用 ') . $promptName)
  221. ->success()
  222. ->send();
  223. } catch (\Exception $e) {
  224. Notification::make()
  225. ->title('更新状态失败')
  226. ->body($e->getMessage())
  227. ->danger()
  228. ->send();
  229. }
  230. }
  231. /**
  232. * 复制提示词
  233. */
  234. #[On('duplicate-prompt')]
  235. public function duplicatePrompt(array $prompt): void
  236. {
  237. $newName = ($prompt['template_name'] ?? 'template') . '_copy';
  238. try {
  239. app(PromptService::class)->savePrompt([
  240. 'template_name' => $newName,
  241. 'template_type' => $prompt['template_type'] ?? 'question_generation',
  242. 'template_content' => $this->fetchPromptContent($prompt['template_name'] ?? '') ?? '',
  243. 'variables' => $prompt['variables'] ?? '[]',
  244. 'description' => $prompt['description'] ?? '',
  245. 'tags' => $prompt['tags'] ?? '',
  246. ]);
  247. Notification::make()
  248. ->title('复制成功')
  249. ->body("已创建副本:{$newName}")
  250. ->success()
  251. ->send();
  252. } catch (\Exception $e) {
  253. Notification::make()
  254. ->title('复制失败')
  255. ->body($e->getMessage())
  256. ->danger()
  257. ->send();
  258. }
  259. }
  260. /**
  261. * 刷新数据
  262. */
  263. #[On('refresh-prompts')]
  264. public function refreshPrompts(): void
  265. {
  266. Notification::make()
  267. ->title('数据已刷新')
  268. ->success()
  269. ->send();
  270. }
  271. /**
  272. * 头部操作
  273. */
  274. protected function getHeaderActions(): array
  275. {
  276. return [
  277. Actions\Action::make('create')
  278. ->label('新建提示词')
  279. ->icon('heroicon-m-plus')
  280. ->color('success')
  281. ->action('createPrompt'),
  282. Actions\Action::make('refresh')
  283. ->label('刷新')
  284. ->icon('heroicon-m-arrow-path')
  285. ->color('warning')
  286. ->action('refreshPrompts'),
  287. ];
  288. }
  289. public function savePrompt(): void
  290. {
  291. $data = $this->validate([
  292. 'form.template_name' => $this->isEditing ? 'nullable|string' : 'required|string',
  293. 'form.template_type' => 'required|string',
  294. 'form.template_content' => 'required|string',
  295. 'form.variables' => 'nullable|string',
  296. 'form.description' => 'nullable|string',
  297. 'form.tags' => 'nullable|string',
  298. 'form.is_active' => 'nullable|string',
  299. ])['form'];
  300. try {
  301. $payload = [
  302. 'template_name' => $this->isEditing && $this->editingName
  303. ? $this->editingName
  304. : $data['template_name'],
  305. 'template_content' => $data['template_content'],
  306. 'template_type' => $data['template_type'],
  307. 'variables' => $data['variables'] ?? '[]',
  308. 'description' => $data['description'] ?? '',
  309. 'tags' => $data['tags'] ?? '',
  310. 'is_active' => $data['is_active'] ?? 'yes',
  311. ];
  312. if ($this->isEditing && $this->editingName) {
  313. app(PromptService::class)->savePrompt($payload);
  314. } else {
  315. app(PromptService::class)->savePrompt($payload);
  316. }
  317. $this->showPromptModal = false;
  318. $this->refreshPrompts();
  319. Notification::make()
  320. ->title('保存成功')
  321. ->success()
  322. ->send();
  323. } catch (\Exception $e) {
  324. Notification::make()
  325. ->title('保存失败')
  326. ->body($e->getMessage())
  327. ->danger()
  328. ->send();
  329. }
  330. }
  331. protected function fetchPromptContent(string $templateName): ?string
  332. {
  333. if (!$templateName) {
  334. return null;
  335. }
  336. try {
  337. return app(PromptService::class)->getPromptContent($templateName);
  338. } catch (\Exception $e) {
  339. \Log::warning('获取提示词内容失败: ' . $e->getMessage());
  340. }
  341. return null;
  342. }
  343. protected function resetPromptForm(): void
  344. {
  345. $this->form = [
  346. 'template_name' => '',
  347. 'template_type' => 'question_generation',
  348. 'template_content' => '',
  349. 'variables' => '[]',
  350. 'description' => '',
  351. 'tags' => '',
  352. 'is_active' => 'yes',
  353. ];
  354. }
  355. }