feat: 全自动演出——开场自动进入/回复自动继续/终局台词演绎/已播行折叠(v2.1)

This commit is contained in:
2026-08-13 17:35:20 +08:00
parent 83fbf61e8a
commit 0f8a371120
3 changed files with 36 additions and 12 deletions
+2 -7
View File
@@ -43,10 +43,6 @@
</view>
</view>
</view>
<view class="theater-actions">
<view class="btn-primary theater-go" @click="finish">开始 </view>
</view>
</view>
</template>
@@ -84,7 +80,8 @@ export default {
mounted() {
this.speaking = true
speak(this.content, this.audio)
setTimeout(() => { this.speaking = false }, Math.min(3000, this.content.length * 300))
// v2.1:朗读结束自动进入决策幕(「跳过」仍可手动跳过)
setTimeout(() => { this.speaking = false; this.$emit('done') }, Math.min(3000, this.content.length * 300))
},
beforeUnmount() {
stopSpeak()
@@ -126,6 +123,4 @@ export default {
@keyframes t-word { 0% { opacity: 0; transform: translateY(12rpx); } 100% { opacity: 1; transform: translateY(0); } }
.theater-py { font-size: 20rpx; color: #ff6b35; line-height: 1.3; height: 28rpx; }
.theater-ch { font-size: 34rpx; line-height: 1.5; color: #5b4636; }
.theater-actions { display: flex; justify-content: center; margin-top: 32rpx; position: relative; z-index: 2; }
.theater-go { font-size: 34rpx; min-width: 240rpx; animation: t-pop 0.5s ease 1.2s backwards; }
</style>
+2 -2
View File
@@ -9,7 +9,7 @@
</view>
<view class="story-lines">
<view v-for="(line, i) in script" :key="i" class="story-line" :class="{ active: i === lineIndex, done: i < lineIndex }">
<view v-for="(line, i) in script" :key="i" v-show="i === lineIndex || i === lineIndex - 1" class="story-line" :class="{ active: i === lineIndex, fading: i === lineIndex - 1 }">
<view v-if="line.speaker === '旁白'" class="bubble nar-bubble">
<view class="bubble-name">📖 旁白</view>
<RubyText :text="line.text" :pinyin="line.pinyin || ''" :show="showPinyin" :highlight="i === lineIndex ? charIndex : -1" />
@@ -136,7 +136,7 @@ export default {
.story-lines { display: flex; flex-direction: column; gap: 24rpx; padding-bottom: 24rpx; }
.story-line { opacity: 0; transition: opacity 0.4s; }
.story-line.active { opacity: 1; }
.story-line.done { opacity: 0.55; }
.story-line.fading { opacity: 0.35; }
.bubble { border-radius: 24rpx; padding: 24rpx 28rpx; box-shadow: 0 4rpx 16rpx rgba(91, 70, 54, 0.08); }
.bubble-name { font-size: 24rpx; font-weight: 700; color: #ff6b35; margin-bottom: 8rpx; }
.nar-bubble { background: #fff3d6; margin-left: 40rpx; margin-right: 40rpx; }
+32 -3
View File
@@ -45,6 +45,11 @@
<SpeakButton class="scene-speak" :text="detail.scene_content" :file="detail.scene_audio" label="读一读情境" />
</view>
<!-- v2.1 终局演绎结局台词播完自动跳结算 -->
<view v-if="playingFinal && finalNode" class="node story-node final-story">
<StoryPlayer :key="'final-' + finalNode.node_id" :script="finalScript" :character="finalNode.character" :show-pinyin="showPinyin" @done="gotoResult" />
</view>
<!-- 节点剧情流script 非空 v1 卡片回退 -->
<view v-if="curNode && isStoryNode" class="node story-node">
<StoryPlayer :key="curNode.node_id" :script="storyScript" :character="curNode.character" :show-pinyin="showPinyin" @done="showOptions = true" />
@@ -170,7 +175,10 @@ export default {
soundOn: isSoundEnabled(),
showIntro: false,
sceneFx: '',
branchTimer: null
branchTimer: null,
replyTimer: null,
playingFinal: false,
finalNode: null
}
},
onLoad(options) {
@@ -179,6 +187,7 @@ export default {
},
onUnload() {
if (this.branchTimer) clearTimeout(this.branchTimer)
if (this.replyTimer) clearTimeout(this.replyTimer)
},
computed: {
scene() {
@@ -207,6 +216,9 @@ export default {
},
introProps() {
return this.detail ? entryProps(this.detail.entry) : []
},
finalScript() {
return this.finalNode ? parseScript(this.finalNode.script) : null
}
},
methods: {
@@ -266,6 +278,8 @@ export default {
if (this.isStoryNode) {
this.reply = fb
this.showOptions = false // 提交后隐藏选项区,防重复点击
// v2.1:回应气泡约 3 秒自动继续(点击「继续」立即继续)
this.replyTimer = setTimeout(() => this.autoContinue(), 3000)
} else {
this.feedback = fb
}
@@ -279,8 +293,15 @@ export default {
route_steps: this.routeSteps,
final: data.final
})
playSound('finish')
uni.redirectTo({ url: '/pages/result/result' })
this.choosing = false
// v2.1:终局节点有剧本 → 先演绎结局台词再跳结算;无剧本直接跳(回退)
if (data.final.final_node && parseScript(data.final.final_node.script)) {
this.finalNode = data.final.final_node
this.playingFinal = true
} else {
playSound('finish')
uni.redirectTo({ url: '/pages/result/result' })
}
} else {
this.choosing = false
}
@@ -288,7 +309,15 @@ export default {
this.choosing = false
}
},
autoContinue() {
if (this.reply && !this.choosing) this.continuePlay()
},
gotoResult() {
playSound('finish')
uni.redirectTo({ url: '/pages/result/result' })
},
continuePlay() {
if (this.replyTimer) { clearTimeout(this.replyTimer); this.replyTimer = null }
if (this.isStoryNode) {
this.curNode = this.reply.next
this.reply = null