schema([ Forms\Components\Section::make('基本信息') ->schema([ Forms\Components\TextInput::make('name') ->label('终端名称') ->required() ->maxLength(255) ->placeholder('例如: 生产线A-工位1') ->helperText('终端的显示名称'), Forms\Components\TextInput::make('code') ->label('终端编码') ->required() ->unique(ignoreRecord: true) ->maxLength(100) ->placeholder('例如: TERM-0001') ->helperText('终端的唯一标识符') ->regex('/^[A-Z0-9\-]+$/') ->validationMessages([ 'regex' => '终端编码只能包含大写字母、数字和连字符', ]), Forms\Components\TextInput::make('ip_address') ->label('IP地址') ->ip() ->maxLength(45) ->placeholder('例如: 192.168.1.100') ->helperText('终端的IP地址'), Forms\Components\TextInput::make('station_id') ->label('线站ID') ->numeric() ->placeholder('请输入线站ID') ->helperText('关联的生产线站ID'), ]) ->columns(2), Forms\Components\Section::make('组态图配置') ->schema([ Forms\Components\TextInput::make('diagram_url') ->label('组态图URL') ->url() ->maxLength(500) ->placeholder('https://example.com/diagram.png') ->helperText('组态图的访问地址'), ]), Forms\Components\Section::make('显示配置') ->schema([ Forms\Components\KeyValue::make('display_config') ->label('显示参数') ->keyLabel('参数名称') ->valueLabel('参数值') ->addActionLabel('添加参数') ->helperText('配置终端的显示参数,如分辨率、刷新率等') ->default([ 'resolution' => '1920x1080', 'refresh_rate' => '60', 'orientation' => 'landscape', 'brightness' => '80', ]), ]), Forms\Components\Section::make('知识库关联') ->schema([ Forms\Components\Repeater::make('knowledgeBaseAssociations') ->label('关联知识库') ->relationship('knowledgeBases') ->schema([ Forms\Components\Select::make('id') ->label('知识库') ->options(\App\Models\KnowledgeBase::where('status', 'active')->pluck('name', 'id')) ->required() ->searchable() ->distinct() ->disableOptionsWhenSelectedInSiblingRepeaterItems() ->helperText('选择要关联的知识库'), Forms\Components\TextInput::make('priority') ->label('优先级') ->numeric() ->default(0) ->required() ->minValue(0) ->helperText('数字越小优先级越高,0为最高优先级'), ]) ->columns(2) ->reorderable() ->reorderableWithButtons() ->addActionLabel('添加知识库') ->reorderableWithDragAndDrop(false) ->itemLabel(fn (array $state): ?string => \App\Models\KnowledgeBase::find($state['id'])?->name ?? '未选择' ) ->collapsed() ->collapsible() ->helperText('可以关联多个知识库,并设置优先级。拖动或使用按钮调整顺序。'), ]) ->description('配置终端可以访问的知识库及其优先级'), Forms\Components\Section::make('AI提示词配置') ->schema([ Forms\Components\Grid::make(3) ->schema([ \AbdelhamidErrahmouni\FilamentMonacoEditor\MonacoEditor::make('prompt.prompt_template') ->label('提示词模板') ->language('markdown') ->fontSize('14px') ->helperText('编辑AI提示词模板,支持使用变量如 {user}, {station}, {time} 等') ->placeholderText('请输入AI提示词模板...') ->disablePreview() ->columnSpan(2), Forms\Components\Grid::make(1) ->schema([ Forms\Components\Placeholder::make('template_selector') ->label('模板库') ->content(fn () => view('filament.components.prompt-template-selector')), Forms\Components\Placeholder::make('variable_helper') ->label('变量参考') ->content(fn () => view('filament.components.prompt-variable-helper')), ]) ->columnSpan(1), ]), ]) ->description('配置终端的AI提示词模板,用于指导AI助手的行为') ->collapsible(), Forms\Components\Section::make('状态信息') ->schema([ Forms\Components\Toggle::make('is_online') ->label('在线状态') ->helperText('终端是否在线') ->default(false) ->disabled() ->dehydrated(false), Forms\Components\DateTimePicker::make('last_online_at') ->label('最后在线时间') ->disabled() ->dehydrated(false), ]) ->columns(2) ->visibleOn('edit'), ]); } public static function table(Table $table): Table { return $table ->columns([ Tables\Columns\TextColumn::make('name') ->label('终端名称') ->searchable() ->sortable() ->weight('bold'), Tables\Columns\TextColumn::make('code') ->label('终端编码') ->searchable() ->sortable() ->copyable() ->tooltip('点击复制'), Tables\Columns\TextColumn::make('ip_address') ->label('IP地址') ->searchable() ->copyable() ->placeholder('未设置'), Tables\Columns\TextColumn::make('station_id') ->label('线站ID') ->sortable() ->placeholder('未绑定'), Tables\Columns\IconColumn::make('is_online') ->label('在线状态') ->boolean() ->trueIcon('heroicon-o-check-circle') ->falseIcon('heroicon-o-x-circle') ->trueColor('success') ->falseColor('danger') ->sortable(), Tables\Columns\TextColumn::make('latestSyncLog.status') ->label('同步状态') ->badge() ->formatStateUsing(fn (string $state): string => match ($state) { 'pending' => '待同步', 'syncing' => '同步中', 'synced' => '已同步', 'failed' => '失败', default => '未知', }) ->color(fn (string $state): string => match ($state) { 'pending' => 'warning', 'syncing' => 'info', 'synced' => 'success', 'failed' => 'danger', default => 'gray', }) ->icon(fn (string $state): string => match ($state) { 'pending' => 'heroicon-o-clock', 'syncing' => 'heroicon-o-arrow-path', 'synced' => 'heroicon-o-check-circle', 'failed' => 'heroicon-o-x-circle', default => 'heroicon-o-question-mark-circle', }) ->placeholder('从未同步') ->sortable(), Tables\Columns\TextColumn::make('last_online_at') ->label('最后在线时间') ->dateTime('Y-m-d H:i:s') ->sortable() ->placeholder('从未在线') ->toggleable(), Tables\Columns\TextColumn::make('created_at') ->label('创建时间') ->dateTime('Y-m-d H:i:s') ->sortable() ->toggleable(isToggledHiddenByDefault: true), Tables\Columns\TextColumn::make('updated_at') ->label('更新时间') ->dateTime('Y-m-d H:i:s') ->sortable() ->toggleable(isToggledHiddenByDefault: true), ]) ->filters([ Tables\Filters\TernaryFilter::make('is_online') ->label('在线状态') ->placeholder('全部') ->trueLabel('在线') ->falseLabel('离线'), Tables\Filters\Filter::make('station_id') ->label('已绑定线站') ->query(fn (Builder $query): Builder => $query->whereNotNull('station_id')), Tables\Filters\Filter::make('has_diagram') ->label('已配置组态图') ->query(fn (Builder $query): Builder => $query->whereNotNull('diagram_url')), ]) ->actions([ SyncConfigAction::make(), Tables\Actions\ViewAction::make() ->label('查看'), Tables\Actions\EditAction::make() ->label('编辑'), Tables\Actions\DeleteAction::make() ->label('删除'), ]) ->bulkActions([ Tables\Actions\BulkActionGroup::make([ SyncConfigAction::makeBulk(), Tables\Actions\DeleteBulkAction::make() ->label('批量删除'), ]), ]) ->defaultSort('created_at', 'desc') ->groups([ Tables\Grouping\Group::make('station_id') ->label('按线站分组') ->collapsible(), Tables\Grouping\Group::make('is_online') ->label('按在线状态分组') ->getTitleFromRecordUsing(fn (Terminal $record): string => $record->is_online ? '在线' : '离线') ->collapsible(), ]); } public static function getPages(): array { return [ 'index' => Pages\ListTerminals::route('/'), 'create' => Pages\CreateTerminal::route('/create'), 'edit' => Pages\EditTerminal::route('/{record}/edit'), 'view' => Pages\ViewTerminal::route('/{record}'), ]; } }