hydratePoints(); } protected function getKnowledgeService(): KnowledgeServiceApi { if (!$this->knowledgeService) { $this->knowledgeService = app(KnowledgeServiceApi::class); } return $this->knowledgeService; } public function updatedPhaseFilter(): void { $this->page = 1; $this->hydratePoints(); } public function updatedCategoryFilter(): void { $this->page = 1; $this->hydratePoints(); } public function updatedSearch(): void { $this->page = 1; $this->hydratePoints(); } public function changePage(int $page): void { $this->page = max($page, 1); $this->hydratePoints(); } public function selectPoint(string $kpCode): void { $this->selectedPoint = $this->getKnowledgeService()->getKnowledgePointDetail($kpCode); } public function getStatsProperty(): array { $all = $this->getKnowledgeService()->listKnowledgePoints(); $phases = $all->groupBy('phase')->map->count()->sortDesc()->map(fn ($count, $phase) => "{$phase}·{$count}"); $categories = $all->groupBy('category')->map->count()->sortDesc()->take(3)->map(fn ($count, $category) => "{$category}·{$count}"); return [ ['label' => '知识点数量', 'value' => number_format($all->count()), 'hint' => '当前版本节点'], ['label' => '学段覆盖', 'value' => $phases->implode(' | ') ?: '未配置', 'hint' => 'phase 字段'], ['label' => 'Top 类别', 'value' => $categories->implode(' | ') ?: '未配置', 'hint' => 'category 字段'], ['label' => '平均重要度', 'value' => number_format($all->avg('importance') ?? 0, 1), 'hint' => 'importance'], ]; } public function getPhaseOptionsProperty(): array { return $this->getKnowledgeService() ->listKnowledgePoints() ->pluck('phase') ->filter() ->unique() ->sort() ->mapWithKeys(fn ($phase) => [$phase => $phase]) ->all(); } public function getCategoryOptionsProperty(): array { return $this->getKnowledgeService() ->listKnowledgePoints() ->pluck('category') ->filter() ->unique() ->sort() ->mapWithKeys(fn ($category) => [$category => $category]) ->all(); } public function getPaginatedPointsProperty(): array { $filters = array_filter([ 'phase' => $this->phaseFilter, 'category' => $this->categoryFilter, ]); $response = $this->getKnowledgeService()->paginateKnowledgePoints( page: $this->page, perPage: $this->perPage, filters: $filters ); $records = collect($response['data'] ?? []); if (filled($this->search)) { $search = Str::lower($this->search); $records = $records->filter(function (array $record) use ($search): bool { return Str::contains(Str::lower($record['cn_name'] ?? ''), $search) || Str::contains(Str::lower($record['kp_code'] ?? ''), $search); }); } $meta = $response['meta'] ?? []; return [ 'data' => $records->values(), 'total' => $meta['total'] ?? $records->count(), 'page' => $meta['page'] ?? $this->page, 'total_pages' => $meta['total_pages'] ?? 1, 'has_prev' => $meta['has_prev'] ?? ($this->page > 1), 'has_next' => $meta['has_next'] ?? false, ]; } public function getSelectedSkillsProperty(): Collection { return collect($this->selectedPoint['skills'] ?? []); } public function getSelectedPointProperty(): ?array { return $this->selectedPoint; } public function getRelatedNodesProperty(): array { $point = $this->selectedPoint; if (! $point) { return ['parents' => [], 'children' => []]; } $parents = collect($point['parents'] ?? []) ->map(fn ($name, $index) => [ 'id' => "parent-{$index}", 'label' => $name, 'relation' => 'parent', ])->values()->all(); $children = collect($point['children'] ?? []) ->map(fn ($name, $index) => [ 'id' => "child-{$index}", 'label' => $name, 'relation' => 'child', ])->values()->all(); return ['parents' => $parents, 'children' => $children]; } protected function hydratePoints(): void { $this->loading = true; $this->selectedPoint = null; $points = $this->paginatedPoints['data']; if ($points->isNotEmpty()) { $this->selectPoint($points->first()['kp_code']); } $this->loading = false; } }