Files
KnowledgeBase/app/Filament/Resources/TerminalResource.php
lizhuoran 6a6c59e3e4 feat(阶段三): 实现终端管理基础功能
- 创建 TerminalResource 及其所有页面(列表、创建、编辑、查看)
- 实现终端基本信息管理(名称、编码、IP、线站、组态图)
- 添加显示配置管理(KeyValue 组件)
- 实现在线状态显示和筛选
- 添加按线站分组功能
- 创建 TerminalPolicy 权限策略
- 支持搜索、排序、批量删除等功能
2026-03-09 10:59:29 +08:00

318 lines
14 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
namespace App\Filament\Resources;
use App\Filament\Resources\TerminalResource\Pages;
use App\Filament\Actions\SyncConfigAction;
use App\Models\Terminal;
use Filament\Forms;
use Filament\Forms\Form;
use Filament\Resources\Resource;
use Filament\Tables;
use Filament\Tables\Table;
use Illuminate\Database\Eloquent\Builder;
class TerminalResource extends Resource
{
protected static ?string $model = Terminal::class;
protected static ?string $navigationIcon = 'heroicon-o-computer-desktop';
protected static ?string $navigationLabel = '终端管理';
protected static ?string $modelLabel = '终端';
protected static ?string $pluralModelLabel = '终端';
protected static ?int $navigationSort = 3;
protected static ?string $navigationGroup = '大屏配置';
public static function form(Form $form): Form
{
return $form
->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}'),
];
}
}