| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207 |
- <?php
- namespace App\Filament\Pages;
- use App\Services\KnowledgeServiceApi;
- use BackedEnum;
- use Filament\Pages\Page;
- use Filament\Tables\Actions\Action;
- use Filament\Tables\Columns\BadgeColumn;
- use Filament\Tables\Columns\TextColumn;
- use Filament\Tables\Concerns\InteractsWithTable;
- use Filament\Tables\Contracts\HasTable;
- use Filament\Tables\Filters\SelectFilter;
- use Filament\Tables\Table;
- use Illuminate\Support\Collection;
- use Illuminate\Support\Str;
- use UnitEnum;
- class KnowledgePoints extends Page implements HasTable
- {
- use InteractsWithTable;
- protected static string|BackedEnum|null $navigationIcon = 'heroicon-o-map';
- protected static string|UnitEnum|null $navigationGroup = '知识图谱';
- protected static string|UnitEnum|null $navigationLabel = '知识点总览';
- protected static string $view = 'filament.pages.knowledge-points';
- protected KnowledgeServiceApi $knowledgeService;
- protected ?Collection $cachedPoints = null;
- public function mount(KnowledgeServiceApi $knowledgeService): void
- {
- $this->knowledgeService = $knowledgeService;
- }
- public function table(Table $table): Table
- {
- return $table
- ->columns([
- TextColumn::make('kp_code')
- ->label('编号')
- ->sortable()
- ->searchable(),
- TextColumn::make('cn_name')
- ->label('知识点')
- ->wrap()
- ->searchable(),
- BadgeColumn::make('phase')
- ->label('学段')
- ->colors([
- 'primary',
- 'success' => fn (?string $state): bool => $state === '高中',
- ]),
- TextColumn::make('grade')
- ->label('年级')
- ->sortable(),
- BadgeColumn::make('category')
- ->label('类别')
- ->color('info'),
- TextColumn::make('importance')
- ->label('重要度')
- ->sortable()
- ->formatStateUsing(fn ($state): string => number_format((float) $state, 1)),
- TextColumn::make('description')
- ->label('摘要')
- ->toggleable(isToggledHiddenByDefault: true)
- ->wrap()
- ->limit(80),
- ])
- ->filters([
- SelectFilter::make('phase')
- ->label('学段')
- ->options($this->getPhaseOptions()),
- SelectFilter::make('category')
- ->label('知识类别')
- ->options($this->getCategoryOptions()),
- ])
- ->records(fn (array $params) => $this->filterPoints($params))
- ->paginated(false)
- ->defaultSort('importance', 'desc')
- ->actions([
- Action::make('view')
- ->label('查看详情')
- ->icon('heroicon-m-eye')
- ->modalHeading(fn (array $record): string => "{$record['cn_name']}({$record['kp_code']})")
- ->modalContent(fn (array $record) => view('filament.pages.partials.kp-detail', [
- 'point' => $this->knowledgeService->getKnowledgePointDetail($record['kp_code']),
- ]))
- ->modalSubmitAction(false),
- ])
- ->deferLoading();
- }
- protected function filterPoints(array $params): Collection
- {
- $records = $this->getPoints();
- $getFilters = $params['filters'];
- if ($filters = $getFilters()) {
- if (! empty($filters['phase'])) {
- $records = $records->where('phase', $filters['phase']);
- }
- if (! empty($filters['category'])) {
- $records = $records->where('category', $filters['category']);
- }
- }
- $searchCallback = $params['search'];
- $search = Str::lower(trim((string) $searchCallback()));
- if ($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)
- || Str::contains(Str::lower($record['description'] ?? ''), $search);
- });
- }
- $sortColumn = $params['sortColumn']();
- $sortDirection = $params['sortDirection']() ?? 'asc';
- if ($sortColumn) {
- $records = $records->sortBy(fn ($record) => $record[$sortColumn] ?? null, SORT_REGULAR, $sortDirection === 'desc');
- }
- return $records->values();
- }
- protected function getPoints(): Collection
- {
- if ($this->cachedPoints) {
- return $this->cachedPoints;
- }
- return $this->cachedPoints = $this->knowledgeService
- ->listKnowledgePoints()
- ->map(fn (array $point) => [
- 'kp_code' => $point['kp_code'] ?? null,
- 'cn_name' => $point['cn_name'] ?? '-',
- 'phase' => $point['phase'] ?? null,
- 'grade' => $point['grade'] ?? null,
- 'category' => $point['category'] ?? null,
- 'importance' => $point['importance'] ?? null,
- 'description' => $point['description'] ?? '',
- 'group_path' => $point['group_path'] ?? null,
- 'parents' => $point['parents'] ?? [],
- 'created_at' => $point['created_at'] ?? null,
- 'updated_at' => $point['updated_at'] ?? null,
- ]);
- }
- protected function getPhaseOptions(): array
- {
- return $this->getPoints()
- ->pluck('phase')
- ->filter()
- ->unique()
- ->sort()
- ->mapWithKeys(fn ($phase) => [$phase => $phase])
- ->all();
- }
- protected function getCategoryOptions(): array
- {
- return $this->getPoints()
- ->pluck('category')
- ->filter()
- ->unique()
- ->sort()
- ->mapWithKeys(fn ($category) => [$category => $category])
- ->all();
- }
- /**
- * @return array<int, array<string, string>>
- */
- public function getStatsProperty(): array
- {
- $points = $this->getPoints();
- $byPhase = $points->groupBy('phase')->map->count();
- $byCategory = $points->groupBy('category')->map->count();
- return [
- [
- 'label' => '知识点数量',
- 'value' => number_format($points->count()),
- ],
- [
- 'label' => '学段覆盖',
- 'value' => $byPhase->count() ? $byPhase->map(fn ($count, $phase) => "{$phase}:{$count}")->implode(' | ') : '未设置',
- ],
- [
- 'label' => '类别分布 Top3',
- 'value' => $byCategory->sortDesc()->take(3)->map(fn ($count, $category) => "{$category}:{$count}")->implode(' | ') ?: '未设置',
- ],
- [
- 'label' => '最近同步',
- 'value' => now()->toDateTimeString(),
- ],
- ];
- }
- }
|