diff --git a/app/Filament/Resources/TerminalResource.php b/app/Filament/Resources/TerminalResource.php index 58cc71f..62b536b 100644 --- a/app/Filament/Resources/TerminalResource.php +++ b/app/Filament/Resources/TerminalResource.php @@ -101,12 +101,27 @@ class TerminalResource extends Resource Forms\Components\Section::make('组态配置') ->schema([ - Forms\Components\TextInput::make('diagram_url') + Forms\Components\Repeater::make('diagram_urls') ->label('组态界面地址') - ->url() - ->maxLength(500) - ->placeholder('https://example.com/diagram.png') - ->helperText('组态界面的访问地址'), + ->schema([ + Forms\Components\TextInput::make('title') + ->label('标题') + ->required() + ->maxLength(100) + ->placeholder('例如: 主组态'), + Forms\Components\TextInput::make('url') + ->label('地址') + ->required() + ->url() + ->maxLength(500) + ->placeholder('https://example.com/diagram.html'), + ]) + ->columns(2) + ->defaultItems(0) + ->addActionLabel('添加组态地址') + ->itemLabel(fn (array $state): ?string => $state['title'] ?? null) + ->collapsible() + ->reorderable(), ]), Forms\Components\Section::make('网关配置') diff --git a/app/Filament/Resources/TerminalResource/Pages/ViewTerminal.php b/app/Filament/Resources/TerminalResource/Pages/ViewTerminal.php index 5d97f8d..d593a53 100644 --- a/app/Filament/Resources/TerminalResource/Pages/ViewTerminal.php +++ b/app/Filament/Resources/TerminalResource/Pages/ViewTerminal.php @@ -45,12 +45,19 @@ class ViewTerminal extends ViewRecord Infolists\Components\Section::make('组态配置') ->schema([ - Infolists\Components\TextEntry::make('diagram_url') + Infolists\Components\RepeatableEntry::make('diagram_urls') ->label('组态界面地址') - ->copyable() - ->placeholder('未设置') - ->url(fn($state) => $state) - ->openUrlInNewTab(), + ->schema([ + Infolists\Components\TextEntry::make('title') + ->label('标题'), + Infolists\Components\TextEntry::make('url') + ->label('地址') + ->copyable() + ->url(fn ($state) => $state) + ->openUrlInNewTab(), + ]) + ->columns(2) + ->placeholder('未设置'), ]), Infolists\Components\Section::make('AI提示词配置') diff --git a/app/Http/Controllers/Api/TerminalApiController.php b/app/Http/Controllers/Api/TerminalApiController.php index 97ff009..32661c6 100644 --- a/app/Http/Controllers/Api/TerminalApiController.php +++ b/app/Http/Controllers/Api/TerminalApiController.php @@ -37,7 +37,10 @@ class TerminalApiController extends Controller 'terminal_name' => $terminal->name, 'terminal_code' => $terminal->code, 'station_name' => $terminal->station?->name, - 'diagram_url' => $terminal->diagram_url, + 'diagram_urls' => collect($terminal->diagram_urls ?? [])->values()->map(fn ($item) => [ + 'title' => $item['title'] ?? '', + 'url' => $item['url'] ?? '', + ])->all(), 'scada_data_url' => $terminal->scada_data_url, 'scada_tags_url' => $terminal->scada_tags_url, 'voice_wakeup_enabled' => $terminal->voice_wakeup_enabled, diff --git a/app/Models/Terminal.php b/app/Models/Terminal.php index df763b8..22f8d0e 100644 --- a/app/Models/Terminal.php +++ b/app/Models/Terminal.php @@ -23,7 +23,7 @@ class Terminal extends Model 'ip_address', 'mac_address', 'station_id', - 'diagram_url', + 'diagram_urls', 'scada_data_url', 'scada_tags_url', 'voice_wakeup_enabled', @@ -40,6 +40,7 @@ class Terminal extends Model protected function casts(): array { return [ + 'diagram_urls' => 'array', 'voice_wakeup_enabled' => 'boolean', 'is_online' => 'boolean', 'last_online_at' => 'datetime', @@ -74,7 +75,7 @@ class Terminal extends Model public function getActivitylogOptions(): LogOptions { return LogOptions::defaults() - ->logOnly(['name', 'code', 'station_id', 'diagram_url']) + ->logOnly(['name', 'code', 'station_id', 'diagram_urls']) ->logOnlyDirty() ->setDescriptionForEvent(fn(string $eventName) => "终端已{$eventName}"); } diff --git a/database/factories/TerminalFactory.php b/database/factories/TerminalFactory.php index eaa5258..5b0c15f 100644 --- a/database/factories/TerminalFactory.php +++ b/database/factories/TerminalFactory.php @@ -29,7 +29,7 @@ class TerminalFactory extends Factory 'code' => 'TERM-' . fake()->unique()->numerify('####'), 'ip_address' => fake()->localIpv4(), 'station_id' => null, - 'diagram_url' => fake()->imageUrl(1920, 1080, 'diagram', true), + 'diagram_urls' => [['title' => '组态', 'url' => fake()->url()]], 'voice_wakeup_enabled' => false, 'voice_wakeup_word' => null, 'is_online' => fake()->boolean(70), // 70%概率在线 diff --git a/database/migrations/2026_04_06_082621_rename_diagram_url_to_diagram_urls_on_terminals_table.php b/database/migrations/2026_04_06_082621_rename_diagram_url_to_diagram_urls_on_terminals_table.php new file mode 100644 index 0000000..a1c5b84 --- /dev/null +++ b/database/migrations/2026_04_06_082621_rename_diagram_url_to_diagram_urls_on_terminals_table.php @@ -0,0 +1,53 @@ +renameColumn('diagram_url', 'diagram_urls'); + }); + + // Migrate existing string URLs to JSON array format + DB::table('terminals')->whereNotNull('diagram_urls')->orderBy('id')->each(function ($terminal) { + $decoded = json_decode($terminal->diagram_urls, true); + if (is_array($decoded)) { + return; // Already JSON, skip + } + + DB::table('terminals')->where('id', $terminal->id)->update([ + 'diagram_urls' => json_encode([['title' => '默认组态', 'url' => $terminal->diagram_urls]]), + ]); + }); + + Schema::table('terminals', function (Blueprint $table) { + $table->json('diagram_urls')->nullable()->comment('组态界面地址')->change(); + }); + } + + public function down(): void + { + // Extract first URL back to string + DB::table('terminals')->whereNotNull('diagram_urls')->orderBy('id')->each(function ($terminal) { + $decoded = json_decode($terminal->diagram_urls, true); + $url = is_array($decoded) ? ($decoded[0]['url'] ?? null) : $terminal->diagram_urls; + + DB::table('terminals')->where('id', $terminal->id)->update([ + 'diagram_urls' => $url, + ]); + }); + + Schema::table('terminals', function (Blueprint $table) { + $table->string('diagram_urls', 500)->nullable()->comment('组态界面地址')->change(); + }); + + Schema::table('terminals', function (Blueprint $table) { + $table->renameColumn('diagram_urls', 'diagram_url'); + }); + } +}; diff --git a/database/seeders/TerminalSeeder.php b/database/seeders/TerminalSeeder.php index 946fef7..5f8253b 100644 --- a/database/seeders/TerminalSeeder.php +++ b/database/seeders/TerminalSeeder.php @@ -53,7 +53,10 @@ PROMPT; 'code' => "SCREEN-{$beamline}", 'ip_address' => $ipAddress, 'station_id' => $station->id, - 'diagram_url' => 'https://ssrf.9z.work/scada/demo.html', + 'diagram_urls' => [ + ['title' => 'BL16U1', 'url' => 'https://ssrf.9z.work/scada/BL16U1.svg'], + ['title' => 'BL16U1前端布局图', 'url' => 'https://ssrf.9z.work/scada/BL16U1-1.svg'] + ], 'is_online' => in_array($beamline, ['BL02U1', 'BL07U', 'BL08U', 'BL13U', 'BL15U']), 'last_online_at' => in_array($beamline, ['BL02U1', 'BL07U', 'BL08U', 'BL13U', 'BL15U']) ? now() diff --git a/tests/Feature/TerminalResourceTest.php b/tests/Feature/TerminalResourceTest.php index b777322..2d61116 100644 --- a/tests/Feature/TerminalResourceTest.php +++ b/tests/Feature/TerminalResourceTest.php @@ -65,7 +65,7 @@ class TerminalResourceTest extends TestCase 'code' => 'TEST-0001', 'ip_address' => '192.168.1.100', 'station_id' => $station->id, - 'diagram_url' => 'https://example.com/diagram.html', + 'diagram_urls' => [['title' => '测试组态', 'url' => 'https://example.com/diagram.html']], ]; Livewire::test(CreateTerminal::class)