const storage = require('../../utils/storage'); const app = getApp(); const DEFAULT_STAPLES = ['油', '盐', '酱油', '醋', '料酒', '生抽', '蚝油', '葱', '姜', '蒜']; Page({ data: { inputValue: '', ingredients: [], selected: {}, staples: [], voiceRecording: false, showStapleEditor: false, stapleEditValue: '' }, onLoad() { const saved = storage.get('custom_staples', null); this.setData({ staples: saved || [...DEFAULT_STAPLES] }); }, onInput(e) { this.setData({ inputValue: e.detail.value }); }, addIngredient() { const name = this.data.inputValue.trim(); if (!name) return; this.add(name); this.setData({ inputValue: '' }); }, onToggle(e) { const { name, selected } = e.detail; if (selected) { this.add(name); } else { this.remove(name); } }, add(name) { if (this.data.ingredients.includes(name)) return; const ingredients = [...this.data.ingredients, name]; const selected = { ...this.data.selected, [name]: true }; this.setData({ ingredients, selected }); }, remove(name) { const ingredients = this.data.ingredients.filter(i => i !== name); const selected = { ...this.data.selected, [name]: false }; this.setData({ ingredients, selected }); }, /* ── 语音输入 ── */ voiceInput() { const recorder = wx.getRecorderManager(); if (this.data.voiceRecording) { // 松手停止 recorder.stop(); this.setData({ voiceRecording: false }); wx.hideLoading(); return; } this.setData({ voiceRecording: true }); wx.showLoading({ title: '正在听…', mask: true }); recorder.start({ duration: 10000, sampleRate: 16000, numberOfChannels: 1, encodeBitRate: 48000, format: 'mp3' }); recorder.onStop((res) => { this.setData({ voiceRecording: false }); wx.hideLoading(); // 上传录音到后端识别 wx.uploadFile({ url: app.globalData.baseUrl + '/api/voice/recognize', filePath: res.tempFilePath, name: 'audio', header: { 'Content-Type': 'multipart/form-data' }, success: (uploadRes) => { try { const data = JSON.parse(uploadRes.data); if (data.code === 200 && data.data && data.data.text) { const text = data.data.text.trim(); if (text) { // 按逗号/空格/顿号分割多个食材 const names = text.split(/[,,、\s]+/); names.forEach(n => this.add(n)); wx.showToast({ title: '已识别 ' + names.length + ' 种食材', icon: 'success' }); return; } } wx.showToast({ title: '未识别到食材,请手动输入', icon: 'none' }); } catch (e) { wx.showToast({ title: '识别失败,请手动输入', icon: 'none' }); } }, fail: () => { wx.showToast({ title: '语音服务暂不可用', icon: 'none' }); } }); }); recorder.onError(() => { this.setData({ voiceRecording: false }); wx.hideLoading(); wx.showToast({ title: '录音失败,请重试', icon: 'none' }); }); }, /* ── 自定义常备调料 ── */ editStaples() { this.setData({ showStapleEditor: true, stapleEditValue: '' }); }, onStapleInput(e) { this.setData({ stapleEditValue: e.detail.value }); }, addStaple() { const name = this.data.stapleEditValue.trim(); if (!name) return; if (this.data.staples.includes(name)) { wx.showToast({ title: '已存在', icon: 'none' }); return; } const staples = [...this.data.staples, name]; this.setData({ staples, stapleEditValue: '' }); storage.set('custom_staples', staples); }, removeStaple(e) { const name = e.currentTarget.dataset.name; const staples = this.data.staples.filter(s => s !== name); this.setData({ staples }); storage.set('custom_staples', staples); }, closeStapleEditor() { this.setData({ showStapleEditor: false }); }, /* ── 开盒 ── */ openBox() { if (this.data.ingredients.length === 0) { wx.showToast({ title: '请先输入食材', icon: 'none' }); return; } // 将用户自定义的常备调料一并传入后端 const payload = { ingredients: this.data.ingredients, staples: this.data.staples }; const params = encodeURIComponent(JSON.stringify(payload)); wx.navigateTo({ url: '/pages/recipe-list/recipe-list?payload=' + params }); } });