diff --git a/app/Filament/Resources/GuideResource.php b/app/Filament/Resources/GuideResource.php index 378b0ed..f7ca69b 100644 --- a/app/Filament/Resources/GuideResource.php +++ b/app/Filament/Resources/GuideResource.php @@ -193,6 +193,43 @@ class GuideResource extends Resource ]) ->actions([ Tables\Actions\EditAction::make()->label('编辑'), + Tables\Actions\Action::make('duplicate') + ->label('复制') + ->icon('heroicon-o-document-duplicate') + ->color('info') + ->requiresConfirmation() + ->action(function (Guide $record) { + $newGuide = $record->replicate(['pages_count', 'stations_count']); + $newGuide->name = $record->name . ' (副本)'; + $newGuide->created_by = auth()->id(); + $newGuide->published_at = null; + $newGuide->save(); + + // 复制关联的线站 + if ($record->stations()->count() > 0) { + $newGuide->stations()->sync($record->stations()->pluck('stations.id')); + } + + // 复制页面 + $pageIdMap = []; + foreach ($record->pages as $page) { + $newPage = $page->replicate(); + $newPage->guide_id = $newGuide->id; + $newPage->save(); + $pageIdMap[$page->id] = $newPage->id; + } + + // 复制边(edges),并更新页面 ID 映射 + foreach ($record->edges as $edge) { + $newEdge = $edge->replicate(); + $newEdge->guide_id = $newGuide->id; + $newEdge->from_page_id = $pageIdMap[$edge->from_page_id] ?? $edge->from_page_id; + $newEdge->to_page_id = $pageIdMap[$edge->to_page_id] ?? $edge->to_page_id; + $newEdge->save(); + } + + return redirect()->to(route('filament.admin.resources.guides.edit', ['record' => $newGuide])); + }), Tables\Actions\DeleteAction::make()->label('删除'), ]) ->bulkActions([ diff --git a/app/Filament/Resources/GuideResource/Pages/ManageGuidePages.php b/app/Filament/Resources/GuideResource/Pages/ManageGuidePages.php index 947b5e8..dcce43e 100644 --- a/app/Filament/Resources/GuideResource/Pages/ManageGuidePages.php +++ b/app/Filament/Resources/GuideResource/Pages/ManageGuidePages.php @@ -91,20 +91,21 @@ class ManageGuidePages extends Page } ksort($levelGroups); - // Compute positions: center each level horizontally, stack vertically - $nodeWidth = 240; - $gapX = 40; - $gapY = 150; + // Compute positions: center each level vertically, stack horizontally (left-to-right) + $nodeWidth = 180; // matches CSS max-width + $nodeHeight = 80; // compact node height + $gapX = 110; // horizontal gap between levels + $gapY = 30; // vertical gap within same level $positions = []; foreach ($levelGroups as $level => $ids) { $count = count($ids); - $totalWidth = $count * $nodeWidth + ($count - 1) * $gapX; - $startX = max(20, (800 - $totalWidth) / 2); + $totalHeight = $count * $nodeHeight + ($count - 1) * $gapY; + $startY = max(20, (600 - $totalHeight) / 2); foreach ($ids as $i => $id) { $positions[$id] = [ - 'x' => (int) ($startX + $i * ($nodeWidth + $gapX)), - 'y' => 40 + $level * $gapY, + 'x' => 40 + $level * ($nodeWidth + $gapX), + 'y' => (int) ($startY + $i * ($nodeHeight + $gapY)), ]; } } @@ -129,6 +130,11 @@ class ManageGuidePages extends Page // -- Livewire methods called by Drawflow events -- + private function dispatchGraphUpdated(): void + { + $this->dispatch('graphUpdated', nodes: $this->nodes, edges: $this->edges); + } + public function addEdge(int $fromPageId, int $toPageId, string $outputClass = 'output_1'): void { $guide = $this->getRecord(); @@ -170,7 +176,7 @@ class ManageGuidePages extends Page ]); $this->loadGraph(); - $this->dispatch('graphUpdated'); + $this->dispatchGraphUpdated(); } public function removeEdge(int $fromPageId, int $toPageId): void @@ -181,7 +187,7 @@ class ManageGuidePages extends Page ->delete(); $this->loadGraph(); - $this->dispatch('graphUpdated'); + $this->dispatchGraphUpdated(); } // -- Filament Actions -- @@ -196,7 +202,7 @@ class ManageGuidePages extends Page $this->getRecord()->pages()->create($data); $this->loadGraph(); - $this->dispatch('graphUpdated'); + $this->dispatchGraphUpdated(); }); } @@ -219,7 +225,7 @@ class ManageGuidePages extends Page $page->update($data); $this->loadGraph(); - $this->dispatch('graphUpdated'); + $this->dispatchGraphUpdated(); }); } @@ -235,7 +241,7 @@ class ManageGuidePages extends Page $page->delete(); $this->loadGraph(); - $this->dispatch('graphUpdated'); + $this->dispatchGraphUpdated(); }); } @@ -251,7 +257,7 @@ class ManageGuidePages extends Page $edge->delete(); $this->loadGraph(); - $this->dispatch('graphUpdated'); + $this->dispatchGraphUpdated(); }); } diff --git a/resources/views/filament/resources/guide/manage-pages.blade.php b/resources/views/filament/resources/guide/manage-pages.blade.php index 704520f..74b8445 100644 --- a/resources/views/filament/resources/guide/manage-pages.blade.php +++ b/resources/views/filament/resources/guide/manage-pages.blade.php @@ -17,13 +17,8 @@ diff --git a/resources/views/guides/page.blade.php b/resources/views/guides/page.blade.php index 9be0f1c..6a5dc95 100644 --- a/resources/views/guides/page.blade.php +++ b/resources/views/guides/page.blade.php @@ -26,6 +26,15 @@ article ul, article ol { padding-left: 28px; margin: 12px 0; } article li { margin: 4px 0; } article img { max-width: 100%; height: auto; border-radius: 8px; margin: 12px 0; } + article figure { margin: 12px 0; } + article figure img { margin: 0; } + article figcaption { display: none; } + /* 隐藏 Trix 富文本编辑器生成的图片附件说明文字 */ + article figure figcaption, + article .attachment__caption, + article .attachment__name, + article .attachment__size, + article action-text-attachment figcaption { display: none; } article a { color: #2563eb; text-decoration: underline; } article blockquote { border-left: 4px solid #cbd5e1;