feat: full-screen StoryGallery with flow layout, startAtScene engine method, clickable flow nodes

This commit is contained in:
2026-06-12 11:37:14 +08:00
parent 6417a9de43
commit 0379548a29
5 changed files with 327 additions and 389 deletions

View File

@@ -7,8 +7,13 @@ const props = defineProps<{
node: PlayerTreeNode | null
}>()
const emit = defineEmits<{
selectScene: [sceneId: string]
}>()
interface FlowNode {
id: string
sceneId: string
label: string
visited: boolean
isMystery: boolean
@@ -33,7 +38,7 @@ const containerW = ref(800)
const containerH = ref(400)
function buildFlow(root: PlayerTreeNode) {
const dagreNodes: { id: string; parent: string | null; label: string; visited: boolean; isMystery: boolean; locked: boolean; lockHint?: string }[] = []
const dagreNodes: { id: string; sceneId: string; parent: string | null; label: string; visited: boolean; isMystery: boolean; locked: boolean; lockHint?: string }[] = []
const dagreEdges: { from: string; to: string; visited: boolean }[] = []
function walk(node: PlayerTreeNode, parentId: string | null) {
@@ -41,6 +46,7 @@ function buildFlow(root: PlayerTreeNode) {
const dagreId = parentId ? `${parentId}/${node.sceneId}` : node.sceneId
dagreNodes.push({
id: dagreId,
sceneId: node.sceneId,
parent: parentId,
label: node.label,
visited: true,
@@ -65,6 +71,7 @@ function buildFlow(root: PlayerTreeNode) {
const mysteryId = `${dagreId}/__mystery`
dagreNodes.push({
id: mysteryId,
sceneId: '',
parent: dagreId,
label: '? ?',
visited: false,
@@ -100,6 +107,7 @@ function buildFlow(root: PlayerTreeNode) {
const pos = g.node(n.id)
return {
id: n.id,
sceneId: n.sceneId,
label: n.label,
visited: n.visited,
isMystery: n.isMystery,
@@ -221,9 +229,10 @@ const svgH = computed(() => containerH.value)
v-for="n in nodes"
:key="n.id"
class="flow-node"
:class="{ visited: n.visited, mystery: n.isMystery, locked: n.locked }"
:class="{ visited: n.visited, mystery: n.isMystery, locked: n.locked, clickable: n.visited && n.sceneId }"
:style="{ left: n.x + 'px', top: n.y + 'px', width: n.w + 'px', height: n.h + 'px' }"
:title="n.lockHint || ''"
@click="n.visited && n.sceneId && emit('selectScene', n.sceneId)"
>
<span class="node-icon">{{ n.visited ? '✦' : n.isMystery ? '?' : '⬜' }}</span>
<span class="node-label">{{ n.label }}</span>
@@ -264,6 +273,15 @@ const svgH = computed(() => containerH.value)
overflow: hidden;
}
.flow-node.clickable {
cursor: pointer;
}
.flow-node.clickable:hover {
background: rgba(201, 168, 76, 0.22);
border-color: rgba(201, 168, 76, 0.5);
}
.flow-node.visited {
background: rgba(201, 168, 76, 0.12);
border: 1px solid rgba(201, 168, 76, 0.3);