KnowledgePoints.php 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210
  1. <?php
  2. namespace App\Filament\Pages;
  3. use App\Services\KnowledgeServiceApi;
  4. use BackedEnum;
  5. use Filament\Actions\Action;
  6. use Filament\Pages\Page;
  7. use Illuminate\Http\Request;
  8. use Illuminate\Support\Collection;
  9. use Illuminate\Support\Str;
  10. use UnitEnum;
  11. class KnowledgePoints extends Page
  12. {
  13. protected static ?string $slug = 'knowledge-points-overview';
  14. protected static string|BackedEnum|null $navigationIcon = 'heroicon-o-map';
  15. protected static string|UnitEnum|null $navigationGroup = '知识图谱管理';
  16. protected static ?string $navigationLabel = '知识点总览';
  17. protected static ?int $navigationSort = 9;
  18. protected string $view = 'filament.pages.knowledge-points';
  19. public ?string $phaseFilter = null;
  20. public ?string $categoryFilter = null;
  21. public ?string $search = null;
  22. public int $page = 1;
  23. public int $perPage = 8;
  24. protected bool $loading = false;
  25. protected ?KnowledgeServiceApi $knowledgeService = null;
  26. protected ?string $selectedKpCode = null;
  27. // Private property to store loaded data
  28. private ?array $selectedPointData = null;
  29. public function mount(Request $request): void
  30. {
  31. // Only set properties that need to be available immediately
  32. // The search and filter properties will be set via getter
  33. $this->selectedKpCode = $request->query('selected');
  34. $this->page = (int) $request->query('page', 1);
  35. }
  36. public function getSelectedPointProperty(): ?array
  37. {
  38. if ($this->selectedPointData) {
  39. return $this->selectedPointData;
  40. }
  41. // If we have a specific kp code to load
  42. if ($this->selectedKpCode) {
  43. $this->selectedPointData = $this->getKnowledgeService()->getFullGraphData($this->selectedKpCode);
  44. return $this->selectedPointData;
  45. }
  46. // Get first point from paginated results
  47. $points = $this->paginatedPoints;
  48. if (!empty($points['data']) && count($points['data']) > 0) {
  49. $kpCode = $points['data'][0]['kp_code'];
  50. $this->selectedPointData = $this->getKnowledgeService()->getFullGraphData($kpCode);
  51. }
  52. return $this->selectedPointData;
  53. }
  54. public function getCurrentPhaseProperty(): ?string
  55. {
  56. return request()->query('phase');
  57. }
  58. protected function getKnowledgeService(): KnowledgeServiceApi
  59. {
  60. if (!$this->knowledgeService) {
  61. $this->knowledgeService = app(KnowledgeServiceApi::class);
  62. }
  63. return $this->knowledgeService;
  64. }
  65. public function selectPoint(string $kpCode): void
  66. {
  67. $this->selectedPoint = $this->getKnowledgeService()->getKnowledgePointDetail($kpCode);
  68. }
  69. public function getStatsProperty(): array
  70. {
  71. $all = $this->getKnowledgeService()->listKnowledgePoints();
  72. $phases = $all->groupBy('phase')->map->count()->sortDesc()->map(fn ($count, $phase) => "{$phase}·{$count}");
  73. $categories = $all->groupBy('category')->map->count()->sortDesc()->take(3)->map(fn ($count, $category) => "{$category}·{$count}");
  74. return [
  75. ['label' => '知识点数量', 'value' => number_format($all->count()), 'hint' => '当前版本节点'],
  76. ['label' => '学段覆盖', 'value' => $phases->implode(' | ') ?: '未配置', 'hint' => 'phase 字段'],
  77. ['label' => 'Top 类别', 'value' => $categories->implode(' | ') ?: '未配置', 'hint' => 'category 字段'],
  78. ['label' => '平均重要度', 'value' => number_format($all->avg('importance') ?? 0, 1), 'hint' => 'importance'],
  79. ];
  80. }
  81. public function getPhaseOptionsProperty(): array
  82. {
  83. return $this->getKnowledgeService()
  84. ->listKnowledgePoints()
  85. ->pluck('phase')
  86. ->filter()
  87. ->unique()
  88. ->sort()
  89. ->mapWithKeys(fn ($phase) => [$phase => $phase])
  90. ->all();
  91. }
  92. public function getCategoryOptionsProperty(): array
  93. {
  94. return $this->getKnowledgeService()
  95. ->listKnowledgePoints()
  96. ->pluck('category')
  97. ->filter()
  98. ->unique()
  99. ->sort()
  100. ->mapWithKeys(fn ($category) => [$category => $category])
  101. ->all();
  102. }
  103. public function getPaginatedPointsProperty(): array
  104. {
  105. // Get filters from URL
  106. $request = request();
  107. $phaseFilter = $request->query('phase');
  108. $categoryFilter = $request->query('category');
  109. $searchTerm = trim((string) $request->query('search'));
  110. // Get all points
  111. $filters = array_filter([
  112. 'phase' => $phaseFilter,
  113. 'category' => $categoryFilter,
  114. ]);
  115. $allPoints = $this->getKnowledgeService()->listKnowledgePoints(
  116. perPage: 200,
  117. filters: $filters
  118. );
  119. // Apply search filter on the client side
  120. if ($searchTerm !== '') {
  121. $allPoints = $allPoints->filter(function (array $record) use ($searchTerm): bool {
  122. return Str::contains(Str::lower($record['cn_name'] ?? ''), Str::lower($searchTerm))
  123. || Str::contains(Str::lower($record['kp_code'] ?? ''), Str::lower($searchTerm))
  124. || Str::contains(Str::lower($record['description'] ?? ''), Str::lower($searchTerm));
  125. });
  126. }
  127. // Paginate the results
  128. $total = $allPoints->count();
  129. $totalPages = (int) ceil($total / $this->perPage);
  130. $offset = ($this->page - 1) * $this->perPage;
  131. $records = $allPoints->slice($offset, $this->perPage)->values();
  132. return [
  133. 'data' => $records,
  134. 'total' => $total,
  135. 'page' => $this->page,
  136. 'total_pages' => $totalPages,
  137. 'has_prev' => $this->page > 1,
  138. 'has_next' => $this->page < $totalPages,
  139. ];
  140. }
  141. public function getSelectedSkillsProperty(): Collection
  142. {
  143. $selectedPoint = $this->selectedPointData;
  144. return collect($selectedPoint['skills'] ?? []);
  145. }
  146. public function getRelatedNodesProperty(): array
  147. {
  148. $point = $this->selectedPointData;
  149. if (! $point) {
  150. return ['parents' => [], 'children' => []];
  151. }
  152. $parents = collect($point['parents'] ?? [])
  153. ->map(fn ($name, $index) => [
  154. 'id' => "parent-{$index}",
  155. 'label' => $name,
  156. 'relation' => 'parent',
  157. ])->values()->all();
  158. $children = collect($point['children'] ?? [])
  159. ->map(fn ($name, $index) => [
  160. 'id' => "child-{$index}",
  161. 'label' => $name,
  162. 'relation' => 'child',
  163. ])->values()->all();
  164. return ['parents' => $parents, 'children' => $children];
  165. }
  166. }