fix: 优化指引页面复制与流程图布局
This commit is contained in:
@@ -51,6 +51,18 @@ class ManageGuidePages extends Page
|
||||
}
|
||||
}
|
||||
|
||||
$pageMap = $pages->keyBy('id');
|
||||
$incomingEdges = [];
|
||||
$outgoingEdges = [];
|
||||
foreach ($pages as $p) {
|
||||
$incomingEdges[$p->id] = [];
|
||||
$outgoingEdges[$p->id] = [];
|
||||
}
|
||||
foreach ($edgeModels as $e) {
|
||||
$incomingEdges[$e->to_page_id][] = $e;
|
||||
$outgoingEdges[$e->from_page_id][] = $e;
|
||||
}
|
||||
|
||||
// BFS from entry nodes (no incoming edges) to assign levels
|
||||
$hasIncoming = array_flip($edgeModels->pluck('to_page_id')->toArray());
|
||||
$levels = [];
|
||||
@@ -91,11 +103,80 @@ class ManageGuidePages extends Page
|
||||
}
|
||||
ksort($levelGroups);
|
||||
|
||||
$orders = [];
|
||||
foreach ($levelGroups as $ids) {
|
||||
foreach (array_values($ids) as $index => $id) {
|
||||
$orders[$id] = $index;
|
||||
}
|
||||
}
|
||||
|
||||
$edgeOffset = function (GuidePageEdge $edge) use ($pageMap): float {
|
||||
$page = $pageMap->get($edge->from_page_id);
|
||||
$options = $page?->options ?? [];
|
||||
$index = $edge->label === null ? false : array_search($edge->label, $options, true);
|
||||
|
||||
return $index === false ? 0 : (($index + 1) / (count($options) + 1)) * 0.4;
|
||||
};
|
||||
|
||||
$incomingScore = function (int $id) use (&$orders, $incomingEdges, $levels, $edgeOffset): ?float {
|
||||
$scores = [];
|
||||
foreach ($incomingEdges[$id] ?? [] as $edge) {
|
||||
if (($levels[$edge->from_page_id] ?? null) >= ($levels[$id] ?? null)) {
|
||||
continue;
|
||||
}
|
||||
$scores[] = ($orders[$edge->from_page_id] ?? 0) + $edgeOffset($edge);
|
||||
}
|
||||
|
||||
return empty($scores) ? null : array_sum($scores) / count($scores);
|
||||
};
|
||||
|
||||
$outgoingScore = function (int $id) use (&$orders, $outgoingEdges, $levels): ?float {
|
||||
$scores = [];
|
||||
foreach ($outgoingEdges[$id] ?? [] as $edge) {
|
||||
if (($levels[$edge->to_page_id] ?? null) <= ($levels[$id] ?? null)) {
|
||||
continue;
|
||||
}
|
||||
$scores[] = $orders[$edge->to_page_id] ?? 0;
|
||||
}
|
||||
|
||||
return empty($scores) ? null : array_sum($scores) / count($scores);
|
||||
};
|
||||
|
||||
$sortLevel = function (array &$ids, callable $scoreResolver) use (&$orders): void {
|
||||
usort($ids, function (int $a, int $b) use ($scoreResolver, $orders): int {
|
||||
$scoreA = $scoreResolver($a) ?? ($orders[$a] ?? 0);
|
||||
$scoreB = $scoreResolver($b) ?? ($orders[$b] ?? 0);
|
||||
|
||||
return $scoreA <=> $scoreB ?: ($orders[$a] ?? 0) <=> ($orders[$b] ?? 0) ?: $a <=> $b;
|
||||
});
|
||||
|
||||
foreach ($ids as $index => $id) {
|
||||
$orders[$id] = $index;
|
||||
}
|
||||
};
|
||||
|
||||
for ($i = 0; $i < 3; $i++) {
|
||||
foreach ($levelGroups as $level => &$ids) {
|
||||
if ($level === array_key_first($levelGroups)) {
|
||||
continue;
|
||||
}
|
||||
$sortLevel($ids, $incomingScore);
|
||||
}
|
||||
unset($ids);
|
||||
|
||||
foreach (array_reverse(array_keys($levelGroups)) as $level) {
|
||||
if ($level === array_key_last($levelGroups)) {
|
||||
continue;
|
||||
}
|
||||
$sortLevel($levelGroups[$level], $outgoingScore);
|
||||
}
|
||||
}
|
||||
|
||||
// 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
|
||||
$gapY = 60; // vertical gap within same level
|
||||
$positions = [];
|
||||
|
||||
foreach ($levelGroups as $level => $ids) {
|
||||
@@ -229,6 +310,29 @@ class ManageGuidePages extends Page
|
||||
});
|
||||
}
|
||||
|
||||
public function copyPageAction(): Action
|
||||
{
|
||||
return Action::make('copyPage')
|
||||
->label('复制页面')
|
||||
->icon('heroicon-o-document-duplicate')
|
||||
->requiresConfirmation()
|
||||
->modalHeading('复制页面')
|
||||
->modalDescription('确认复制该页面?复制后会生成一个独立的新页面,不会复制连线关系。')
|
||||
->modalSubmitActionLabel('确认复制')
|
||||
->action(function (array $arguments): void {
|
||||
$page = $this->getRecord()->pages()->findOrFail($arguments['id']);
|
||||
|
||||
$this->getRecord()->pages()->create([
|
||||
'title' => $page->title.' - 副本',
|
||||
'content' => $page->content,
|
||||
'options' => $page->options ?? [],
|
||||
]);
|
||||
|
||||
$this->loadGraph();
|
||||
$this->dispatchGraphUpdated();
|
||||
});
|
||||
}
|
||||
|
||||
public function deletePageAction(): Action
|
||||
{
|
||||
return Action::make('deletePage')
|
||||
|
||||
Reference in New Issue
Block a user