TextbookResource.php 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279
  1. <?php
  2. namespace App\Filament\Resources;
  3. use App\Filament\Resources\TextbookResource\Pages;
  4. use App\Filament\Resources\TextbookResource\Schemas\TextbookFormSchema;
  5. use App\Filament\Resources\TextbookResource\Tables\TextbookTable;
  6. use App\Filament\Resources\TextbookResource\Actions\DeleteTextbookAction;
  7. use App\Models\Textbook;
  8. use App\Services\TextbookApiService;
  9. use BackedEnum;
  10. use UnitEnum;
  11. use Filament\Resources\Resource;
  12. use Filament\Schemas\Schema;
  13. use Filament\Tables;
  14. use Illuminate\Database\Eloquent\Model;
  15. class TextbookResource extends Resource
  16. {
  17. protected static ?string $model = Textbook::class;
  18. protected static ?string $recordTitleAttribute = 'official_title';
  19. protected static string|BackedEnum|null $navigationIcon = 'heroicon-o-book-open';
  20. protected static ?string $navigationLabel = '教材管理';
  21. protected static UnitEnum|string|null $navigationGroup = '教材管理';
  22. protected static ?int $navigationSort = 2;
  23. protected static ?TextbookApiService $apiService = null;
  24. public static function boot()
  25. {
  26. parent::boot();
  27. static::$apiService = app(TextbookApiService::class);
  28. }
  29. protected static function getApiService(): TextbookApiService
  30. {
  31. if (!static::$apiService) {
  32. static::$apiService = app(TextbookApiService::class);
  33. }
  34. return static::$apiService;
  35. }
  36. public static function form(Schema $schema): Schema
  37. {
  38. return TextbookFormSchema::make($schema);
  39. }
  40. public static function table(Tables\Table $table): Tables\Table
  41. {
  42. return $table
  43. ->columns([
  44. \Filament\Tables\Columns\TextColumn::make('id')
  45. ->label('ID')
  46. ->sortable(),
  47. \Filament\Tables\Columns\TextColumn::make('series.name')
  48. ->label('系列')
  49. ->searchable(),
  50. \Filament\Tables\Columns\TextColumn::make('official_title')
  51. ->label('官方书名')
  52. ->searchable()
  53. ->wrap(),
  54. \Filament\Tables\Columns\TextColumn::make('stage')
  55. ->label('学段')
  56. ->formatStateUsing(function ($state): string {
  57. return match ($state) {
  58. 'primary' => '小学',
  59. 'junior' => '初中',
  60. 'senior' => '高中',
  61. default => $state,
  62. };
  63. })
  64. ->badge()
  65. ->color('info'),
  66. \Filament\Tables\Columns\TextColumn::make('grade')
  67. ->label('年级')
  68. ->formatStateUsing(function ($state): string {
  69. return $state ? "{$state}年级" : '-';
  70. }),
  71. \Filament\Tables\Columns\TextColumn::make('semester')
  72. ->label('学期')
  73. ->formatStateUsing(function ($state): string {
  74. return match ($state) {
  75. 1 => '上学期',
  76. 2 => '下学期',
  77. default => '-',
  78. };
  79. })
  80. ->badge()
  81. ->color('success'),
  82. \Filament\Tables\Columns\TextColumn::make('naming_scheme')
  83. ->label('体系')
  84. ->formatStateUsing(function ($state): string {
  85. return match ($state) {
  86. 'new' => '新体系',
  87. 'old' => '旧体系',
  88. default => $state,
  89. };
  90. })
  91. ->badge(),
  92. \Filament\Tables\Columns\TextColumn::make('status')
  93. ->label('状态')
  94. ->formatStateUsing(function ($state): string {
  95. return match ($state) {
  96. 'draft' => '草稿',
  97. 'published' => '已发布',
  98. 'archived' => '已归档',
  99. default => $state,
  100. };
  101. })
  102. ->badge()
  103. ->color(function ($state): string {
  104. return match ($state) {
  105. 'draft' => 'gray',
  106. 'published' => 'success',
  107. 'archived' => 'danger',
  108. default => 'gray',
  109. };
  110. }),
  111. \Filament\Tables\Columns\TextColumn::make('created_at')
  112. ->label('创建时间')
  113. ->dateTime()
  114. ->sortable()
  115. ->toggleable(isToggledHiddenByDefault: true),
  116. \Filament\Tables\Columns\TextColumn::make('updated_at')
  117. ->label('更新时间')
  118. ->dateTime()
  119. ->sortable()
  120. ->toggleable(isToggledHiddenByDefault: true),
  121. ])
  122. ->filters([
  123. \Filament\Tables\Filters\SelectFilter::make('stage')
  124. ->label('学段')
  125. ->options([
  126. 'primary' => '小学',
  127. 'junior' => '初中',
  128. 'senior' => '高中',
  129. ]),
  130. \Filament\Tables\Filters\SelectFilter::make('status')
  131. ->label('状态')
  132. ->options([
  133. 'draft' => '草稿',
  134. 'published' => '已发布',
  135. 'archived' => '已归档',
  136. ]),
  137. ])
  138. ->actions([
  139. \Filament\Actions\EditAction::make()
  140. ->label('编辑')
  141. ->url(fn($record): string =>
  142. route('filament.admin.resources.textbooks.edit', ['record' => $record->id])
  143. ),
  144. DeleteTextbookAction::make()
  145. ->label('删除'),
  146. \Filament\Actions\Action::make('view_catalog')
  147. ->label('查看目录')
  148. ->icon('heroicon-o-list-bullet')
  149. ->url(fn(Model $record): string =>
  150. route('filament.admin.resources.textbook-catalogs.index', ['tableFilters[textbook_id][value]' => $record->id])
  151. ),
  152. ])
  153. ->bulkActions([
  154. \Filament\Actions\BulkActionGroup::make([
  155. \Filament\Actions\DeleteBulkAction::make()
  156. ->label('批量删除'),
  157. ]),
  158. ])
  159. ->defaultSort('id')
  160. ->paginated([10, 25, 50, 100]);
  161. }
  162. public static function getEloquentQuery(): \Illuminate\Database\Eloquent\Builder
  163. {
  164. // 返回空查询,实际数据通过 API 获取
  165. return parent::getEloquentQuery()->whereRaw('1=0');
  166. }
  167. public static function getRecords(): array
  168. {
  169. // 从 API 获取教材数据
  170. $apiService = static::getApiService();
  171. $result = $apiService->getTextbooks();
  172. \Log::info('TextbookResource::getRecords called', [
  173. 'count' => count($result['data'] ?? []),
  174. 'has_data' => !empty($result['data'])
  175. ]);
  176. $records = [];
  177. foreach ($result['data'] ?? [] as $item) {
  178. $model = new \App\Models\Textbook($item);
  179. $model->exists = true;
  180. $model->id = $item['id'];
  181. $records[] = $model;
  182. }
  183. return $records;
  184. }
  185. public static function resolveRecordRouteBinding(int | string $key, ?\Closure $modifyQuery = null): ?\Illuminate\Database\Eloquent\Model
  186. {
  187. $record = static::getApiService()->getTextbook((int) $key);
  188. if (!$record) {
  189. return null;
  190. }
  191. $model = new \App\Models\Textbook($record);
  192. $model->exists = true;
  193. $model->id = $record['id'];
  194. return $model;
  195. }
  196. public static function getPages(): array
  197. {
  198. return [
  199. 'index' => Pages\ManageTextbooks::route('/'),
  200. 'create' => Pages\CreateTextbook::route('/create'),
  201. 'edit' => Pages\EditTextbook::route('/{record}/edit'),
  202. ];
  203. }
  204. public static function canViewAny(): bool
  205. {
  206. // 临时允许所有用户查看,等待权限系统完善
  207. return true;
  208. }
  209. public static function getHeaderActions(): array
  210. {
  211. return [
  212. \Filament\Actions\Action::make('import_excel')
  213. ->label('Excel导入')
  214. ->icon('heroicon-o-document-arrow-up')
  215. ->color('success')
  216. ->url(fn(): string =>
  217. route('filament.admin.pages.textbook-excel-import-page')
  218. ),
  219. ];
  220. }
  221. public static function canCreate(): bool
  222. {
  223. // 临时允许所有用户创建,等待权限系统完善
  224. return true;
  225. }
  226. public static function canEdit(Model $record): bool
  227. {
  228. // 临时允许所有用户编辑,等待权限系统完善
  229. return true;
  230. }
  231. public static function canDelete(Model $record): bool
  232. {
  233. // 临时允许所有用户删除,等待权限系统完善
  234. return true;
  235. }
  236. public static function canDeleteAny(): bool
  237. {
  238. // 临时允许所有用户批量删除,等待权限系统完善
  239. return true;
  240. }
  241. }