fix: hoist prompt toast to App.vue so it survives ChoicePanel unmount after selection

This commit is contained in:
2026-06-09 16:55:57 +08:00
parent bf4b85f727
commit bca137535b
2 changed files with 37 additions and 31 deletions

View File

@@ -26,6 +26,8 @@ const showChapterSelect = ref(false)
const hasAutoSave = ref(false) const hasAutoSave = ref(false)
const currentSpeed = ref(1) const currentSpeed = ref(1)
const canSkip = ref(false) const canSkip = ref(false)
const promptToast = ref('')
const showPromptToast = ref(false)
const { loadGame, start, resumeAutoSave, makeChoice, clickHotspot, startChapter, const { loadGame, start, resumeAutoSave, makeChoice, clickHotspot, startChapter,
skipScene, setSpeed, getSpeed, isSceneWatched, skipScene, setSpeed, getSpeed, isSceneWatched,
@@ -57,6 +59,12 @@ function onChoose(index: number) {
makeChoice(index) makeChoice(index)
} }
function onPrompt(text: string) {
promptToast.value = text
showPromptToast.value = true
setTimeout(() => { showPromptToast.value = false }, 2500)
}
function toggleMenu() { function toggleMenu() {
showMenu.value = !showMenu.value showMenu.value = !showMenu.value
if (showMenu.value) { if (showMenu.value) {
@@ -167,7 +175,11 @@ init()
:timer-total="store.timerTotal" :timer-total="store.timerTotal"
:timer-remaining="store.timerRemaining" :timer-remaining="store.timerRemaining"
@choose="onChoose" @choose="onChoose"
@prompt="onPrompt"
/> />
<Transition name="prompt-toast">
<div v-if="showPromptToast" class="prompt-toast">{{ promptToast }}</div>
</Transition>
<div v-if="started && !store.gameEnded" class="top-bar"> <div v-if="started && !store.gameEnded" class="top-bar">
<LangSwitch /> <LangSwitch />
<PlaybackBar <PlaybackBar
@@ -356,4 +368,27 @@ html, body {
.end-btn:hover { .end-btn:hover {
background: rgba(255, 200, 100, 0.15); background: rgba(255, 200, 100, 0.15);
} }
.prompt-toast {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
padding: 16px 36px;
font-size: 20px;
color: #ffc107;
background: rgba(0, 0, 0, 0.85);
border: 1px solid rgba(255, 193, 7, 0.4);
border-radius: 6px;
letter-spacing: 3px;
text-align: center;
white-space: nowrap;
pointer-events: none;
z-index: 50;
}
.prompt-toast-enter-active { transition: opacity 0.3s ease; }
.prompt-toast-leave-active { transition: opacity 0.8s ease; }
.prompt-toast-enter-from,
.prompt-toast-leave-to { opacity: 0; }
</style> </style>

View File

@@ -11,13 +11,12 @@ const props = defineProps<{
const emit = defineEmits<{ const emit = defineEmits<{
choose: [index: number] choose: [index: number]
prompt: [text: string]
}>() }>()
const { t } = useI18n() const { t } = useI18n()
const focusIndex = ref(0) const focusIndex = ref(0)
const btnRefs = ref<(HTMLButtonElement | null)[]>([]) const btnRefs = ref<(HTMLButtonElement | null)[]>([])
const toastText = ref('')
const toastVisible = ref(false)
function timerPercent(): number { function timerPercent(): number {
if (props.timerTotal <= 0) return 0 if (props.timerTotal <= 0) return 0
@@ -58,9 +57,7 @@ function onKeydown(e: KeyboardEvent, index: number) {
function handleChoose(index: number) { function handleChoose(index: number) {
const choice = props.choices[index] const choice = props.choices[index]
if (choice?.prompt) { if (choice?.prompt) {
toastText.value = choice.prompt emit('prompt', choice.prompt)
toastVisible.value = true
setTimeout(() => { toastVisible.value = false }, 2200)
} }
emit('choose', index) emit('choose', index)
} }
@@ -90,10 +87,6 @@ function handleChoose(index: number) {
{{ t(choice.textKey || choice.text) }} {{ t(choice.textKey || choice.text) }}
</button> </button>
</div> </div>
<Transition name="toast-fade">
<div class="prompt-toast" v-if="toastVisible">{{ toastText }}</div>
</Transition>
</div> </div>
</template> </template>
@@ -191,26 +184,4 @@ function handleChoose(index: number) {
border-color: rgba(255, 193, 7, 0.7); border-color: rgba(255, 193, 7, 0.7);
box-shadow: 0 0 12px rgba(255, 193, 7, 0.3); box-shadow: 0 0 12px rgba(255, 193, 7, 0.3);
} }
.prompt-toast {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
padding: 14px 32px;
font-size: 18px;
color: #ffc107;
background: rgba(0, 0, 0, 0.85);
border: 1px solid rgba(255, 193, 7, 0.4);
border-radius: 6px;
letter-spacing: 2px;
text-align: center;
white-space: nowrap;
pointer-events: none;
}
.toast-fade-enter-active { transition: opacity 0.3s ease; }
.toast-fade-leave-active { transition: opacity 0.6s ease; }
.toast-fade-enter-from,
.toast-fade-leave-to { opacity: 0; }
</style> </style>