【灶台导航】烹饪导航页:步骤、定时器与语音播报

本文是灶台导航项目专栏的第六篇,分享烹饪导航页的核心功能实现。


一、功能概述

烹饪导航页是产品的差异化亮点------像地图导航一样,分步骤引导用户完成烹饪。主要功能:

  • 步骤展示:图文结合(图片资料展示没有导入,目前只实现文字指导),清晰指导
  • 定时器:每步可设置计时提醒
  • 语音播报:解放双手
  • 多菜品切换:支持同时烹饪多道菜(时间统筹算法-----初步版本并不实现)

二、页面结构

如果是ai生成的菜谱在顶部提示用户。

html 复制代码
<!-- pages/cook/cook.wxml -->
<page-header title="烹饪导航" showBack="{{true}}" statusBarHeight="{{statusBarHeight}}">
  <view slot="right">
    <text class="timer-display">{{timerDisplay}}</text>
  </view>
</page-header>

<view class="cook-page">
  <!-- 菜品切换 -->
  <view class="recipe-tabs" wx:if="{{recipes.length > 1}}">
    <view wx:for="{{recipes}}" wx:key="_id" 
          class="tab-item {{currentRecipeIndex === index ? 'active' : ''}}"
          bindtap="switchRecipe" data-index="{{index}}">
      {{item.name}}
    </view>
  </view>
  
  <!-- 步骤进度 -->
  <view class="progress-bar">
    <view class="progress-fill" style="width: {{progress}}%"></view>
    <text class="progress-text">步骤 {{currentStepIndex + 1}} / {{totalSteps}}</text>
  </view>
  
  <!-- 当前步骤 -->
  <view class="step-content">
    <view class="step-title">{{currentStep.description}}</view>
    
    <!-- 步骤图片 -->
    <image wx:if="{{currentStep.image}}" src="{{currentStep.image}}" class="step-image" />
    
    <!-- 小贴士 -->
    <view wx:if="{{currentStep.tips}}" class="step-tips">
      <text class="tips-icon">💡</text>
      <text class="tips-text">{{currentStep.tips}}</text>
    </view>
    
    <!-- 预计时间 -->
    <view wx:if="{{currentStep.duration}}" class="step-duration">
      预计耗时:{{currentStep.duration}}秒
    </view>
  </view>
  
  <!-- 定时器控制 -->
  <view class="timer-control" wx:if="{{currentStep.duration > 0}}">
    <view class="timer-circle">
      <text class="timer-seconds">{{remainingSeconds}}</text>
      <text class="timer-label">秒</text>
    </view>
    <view class="timer-btns">
      <button wx:if="{{!timerRunning}}" class="timer-btn start" bindtap="startTimer">开始计时</button>
      <button wx:if="{{timerRunning}}" class="timer-btn pause" bindtap="pauseTimer">暂停</button>
      <button wx:if="{{timerRunning}}" class="timer-btn resume" bindtap="resumeTimer">继续</button>
    </view>
  </view>
  
  <!-- 操作按钮 -->
  <view class="action-btns safe-area-bottom">
    <button class="btn-prev" bindtap="prevStep" disabled="{{currentStepIndex === 0}}">上一步</button>
    <button class="btn-next" bindtap="nextStep">
      {{currentStepIndex === totalSteps - 1 ? '完成烹饪' : '下一步'}}
    </button>
  </view>
</view>

页面效果如图:


三、核心逻辑实现

3.1 页面初始化

javascript 复制代码
// pages/cook/cook.js
Page({
  data: {
    recipes: [],
    currentRecipeIndex: 0,
    currentStepIndex: 0,
    timerRunning: false,
    remainingSeconds: 0,
    timerInterval: null,
    statusBarHeight: 0
  },

  onLoad(options) {
    const { recipeIds } = options
    this.loadRecipes(recipeIds.split(','))
    
    // 获取状态栏高度
    const app = getApp()
    this.setData({ statusBarHeight: app.globalData.statusBarHeight })
  },

  async loadRecipes(recipeIds) {
    const recipes = []
    for (const id of recipeIds) {
      const recipe = await callFunction('getRecipeDetail', { recipeId: id })
      recipes.push(recipe)
    }
    this.setData({ 
      recipes,
      totalSteps: recipes[0].steps.length,
      currentStep: recipes[0].steps[0]
    })
  },

  onUnload() {
    // 清理定时器
    if (this.data.timerInterval) {
      clearInterval(this.data.timerInterval)
    }
  }
})

3.2 步骤切换

javascript 复制代码
// 上一步
prevStep() {
  if (this.data.currentStepIndex > 0) {
    this.goToStep(this.data.currentStepIndex - 1)
  }
},

// 下一步
nextStep() {
  const { currentStepIndex, totalSteps } = this.data
  if (currentStepIndex < totalSteps - 1) {
    this.goToStep(currentStepIndex + 1)
  } else {
    this.completeCooking()
  }
},

// 跳转到指定步骤
goToStep(index) {
  this.pauseTimer() // 切换步骤时暂停定时器
  
  const recipe = this.data.recipes[this.data.currentRecipeIndex]
  const step = recipe.steps[index]
  
  this.setData({
    currentStepIndex: index,
    currentStep: step,
    remainingSeconds: step.duration || 0,
    progress: ((index + 1) / recipe.steps.length) * 100
  })
  
  // 语音播报当前步骤
  this.speakStep(step.description)
}

3.3 定时器实现

javascript 复制代码
// 开始计时
startTimer() {
  const duration = this.data.currentStep.duration
  this.setData({ 
    timerRunning: true, 
    remainingSeconds: duration 
  })
  
  this.data.timerInterval = setInterval(() => {
    const seconds = this.data.remainingSeconds - 1
    this.setData({ remainingSeconds: seconds })
    
    if (seconds <= 0) {
      this.timerComplete()
    }
  }, 1000)
},

// 暂停计时
pauseTimer() {
  if (this.data.timerInterval) {
    clearInterval(this.data.timerInterval)
    this.setData({ timerRunning: false })
  }
},

// 继续计时
resumeTimer() {
  if (this.data.remainingSeconds > 0) {
    this.setData({ timerRunning: true })
    this.data.timerInterval = setInterval(() => {
      const seconds = this.data.remainingSeconds - 1
      this.setData({ remainingSeconds: seconds })
      
      if (seconds <= 0) {
        this.timerComplete()
      }
    }, 1000)
  }
},

// 计时完成
timerComplete() {
  clearInterval(this.data.timerInterval)
  this.setData({ timerRunning: false })
  
  // 震动提醒
  wx.vibrateLong()
  
  // 弹窗提示
  wx.showModal({
    title: '时间到!',
    content: '当前步骤已完成,可以进行下一步了',
    showCancel: false
  })
}

四、语音播报

4.1 文字转语音(TTS)

使用微信同声传译插件:

javascript 复制代码
const plugin = requirePlugin('WechatSI')
const ttsManager = plugin.textToSpeech

// 播报步骤
speakStep(text) {
  ttsManager speak({
    content: text,
    success: () => console.log('播报成功'),
    fail: (err) => console.error('播报失败:', err)
  })
}

4.2 后台播放

使用微信内置播放器,支持后台播放:

javascript 复制代码
const innerAudioContext = wx.createInnerAudioContext()

// 播放语音
speakStep(text) {
  const url = this.getTTSUrl(text)
  innerAudioContext.src = url
  innerAudioContext.play()
}

// 获取TTS音频URL
getTTSUrl(text) {
  return `https://tts.tencentcloudapi.com/speak?text=${encodeURIComponent(text)}`
}

五、多菜品切换

javascript 复制代码
// 切换菜品
switchRecipe(e) {
  const index = e.currentTarget.dataset.index
  this.setData({
    currentRecipeIndex: index,
    currentStepIndex: 0,
    currentStep: this.data.recipes[index].steps[0],
    progress: 0
  })
}

// 计算总进度
getTotalProgress() {
  const { recipes, currentRecipeIndex, currentStepIndex } = this.data
  const completedSteps = recipes.slice(0, currentRecipeIndex)
    .reduce((sum, r) => sum + r.steps.length, 0)
  const totalSteps = recipes.reduce((sum, r) => sum + r.steps.length, 0)
  return ((completedSteps + currentStepIndex + 1) / totalSteps) * 100
}

六、踩坑记录

问题1:定时器暂停后秒数重置

现象 :暂停定时器后,remainingSeconds变成0
解决:分离暂停状态和秒数状态

javascript 复制代码
// 错误写法
this.setData({ timerRunning: false, remainingSeconds: 0 })

// 正确写法
this.setData({ timerRunning: false })
// remainingSeconds保持不变

问题2:页面卸载后定时器仍在运行

现象 :退出页面后,定时器继续运行
解决:在onUnload中清理

javascript 复制代码
onUnload() {
  if (this.data.timerInterval) {
    clearInterval(this.data.timerInterval)
  }
  innerAudioContext.stop()
}

问题3:双导航栏问题

现象 :自定义导航栏和系统导航栏同时出现
解决 :在cook.json中设置"navigationStyle": "custom"

json 复制代码
// pages/cook/cook.json
{
  "navigationStyle": "custom",
  "usingComponents": {}
}

七、完成烹饪

javascript 复制代码
completeCooking() {
  wx.showModal({
    title: '烹饪完成!',
    content: '恭喜你完成本次烹饪,是否保存到历史记录?',
    confirmText: '保存',
    cancelText: '不保存',
    success: (res) => {
      if (res.confirm) {
        this.saveHistory()
      }
      wx.navigateBack()
    }
  })
}

async saveHistory() {
  const recipeIds = this.data.recipes.map(r => r._id).join(',')
  await callFunction('userProfile', {
    action: 'saveHistory',
    data: { recipeIds }
  })
}

完成烹饪这里也遇到了一点小问题,主要是忘记设置完成烹饪后页面操作,一开始只是提示用户给菜谱评分。但是从用户体验角度来看应该将数据清空并回到首页,标识这个菜已经做完了。

只需要新增 _finishAllCooking() 方法:

停止所有计时器和语音

重置全部页面数据(菜谱列表、步骤、定时器、统筹方案、评分等)

显示"烹饪完成,辛苦了!"提示

1.5秒后 switchTab 回到首页


项目地址 :Gitee/ZaoTaiNavigation
团队名称 :倒灶了队
更新时间:2026年5月

相关推荐
前端 贾公子1 小时前
响应式系统基础:依赖追踪的基础 —— 发布订阅模式(前端应用最广的设计模式)上
javascript·vue.js
索西引擎1 小时前
【理论】TypeScript 函数重载:从 Vue 3 defineEmits 说起的类型安全实践
前端·typescript
女生也可以敲代码2 小时前
2026前端面试题精选:大厂高频考点与标准答案
前端
Jinuss2 小时前
代码质量管理工具-SonarQube
前端·代码规范
gCode Teacher 格码致知2 小时前
Javascript提高:使用canvas绘制一个绚丽的按钮-由Deepseek产生
javascript·css·css3
ZFSS2 小时前
WebExtrator 网页渲染与内容提取 API 使用指南
前端·人工智能·ai·ai编程
M ? A2 小时前
VuReact:Vue转React的增量编译利器
前端·vue.js·后端·react.js·面试·开源·vureact
px不是xp2 小时前
Docker部署Qdrant向量数据库,初始化向量数据库,重构RAG逻辑
数据库·docker·微信小程序·重构·qdrant
小四的小六2 小时前
WebView安全防护实战:从XSS到中间人攻击,我的踩坑与防御总结
javascript·webview
csj502 小时前
前端基础之《React(9)—React组件》
前端·react.js