# 方案总结
基于提供的 Git diff,我将生成以下 conventional commit message: ## 变更分析: 1. **核心功能**: - 新增睡眠监控服务(`services/sleepMonitor.ts`) - 新增睡眠通知服务(`services/sleepNotificationService.ts`) - iOS 原生端增加睡眠观察者方法 2. **应用启动优化**: - 重构 `app/_layout.tsx` 中的初始化流程,按优先级分阶段加载服务 3. **药品功能改进**: - 优化语音识别交互(实时预览、可取消) - Widget 增加 URL scheme 支持 4. **路由配置**: - 新增药品管理路由常量 ## 提交信息类型: - **主类型**:`feat` (新增睡眠监控功能) - **作用域**:`health` (健康相关功能) --- 请确认方案后,我将生成最终的 commit message。 --- **最终 Commit Message:** feat(health): 添加睡眠监控和通知服务,优化应用启动流程 - 新增睡眠监控服务,支持实时监听 HealthKit 睡眠数据更新 - 实现睡眠质量分析算法,计算睡眠评分和各阶段占比 - 新增睡眠通知服务,分析完成后自动推送质量评估和建议 - iOS 原生端实现睡眠数据观察者,支持后台数据传递 - 重构应用启动初始化流程,按优先级分阶段加载服务(关键/次要/后台/空闲) - 优化药品录入页面语音识别交互,支持实时预览和取消操作 - 药品 Widget 增加 deeplink 支持,点击跳转到应用 - 新增药品管理路由常量配置
This commit is contained in:
@@ -163,6 +163,10 @@ export default function AddMedicationScreen() {
|
||||
const [note, setNote] = useState('');
|
||||
const [dictationActive, setDictationActive] = useState(false);
|
||||
const [dictationLoading, setDictationLoading] = useState(false);
|
||||
// 临时存储当前语音识别的结果,用于实时预览
|
||||
const [currentDictationText, setCurrentDictationText] = useState('');
|
||||
// 记录语音识别开始前的文本,用于取消时恢复
|
||||
const [noteBeforeDictation, setNoteBeforeDictation] = useState('');
|
||||
const isDictationSupported = Platform.OS === 'ios';
|
||||
|
||||
useEffect(() => {
|
||||
@@ -181,20 +185,44 @@ export default function AddMedicationScreen() {
|
||||
});
|
||||
}, [timesPerDay]);
|
||||
|
||||
const appendDictationResult = useCallback(
|
||||
// 实时更新语音识别结果(替换式,不是追加)
|
||||
const updateDictationResult = useCallback(
|
||||
(text: string) => {
|
||||
const clean = text.trim();
|
||||
if (!clean) return;
|
||||
|
||||
// 实时更新:用识别的新文本替换当前识别文本
|
||||
setCurrentDictationText(clean);
|
||||
|
||||
// 同步更新到 note 中,以便用户能看到实时效果
|
||||
setNote((prev) => {
|
||||
if (!prev) {
|
||||
// 移除之前的语音识别文本,添加新的识别文本
|
||||
const baseText = noteBeforeDictation;
|
||||
if (!baseText) {
|
||||
return clean;
|
||||
}
|
||||
return `${prev}${prev.endsWith('\n') ? '' : '\n'}${clean}`;
|
||||
// 在原文本后追加,确保格式正确
|
||||
return `${baseText}${baseText.endsWith('\n') ? '' : '\n'}${clean}`;
|
||||
});
|
||||
},
|
||||
[setNote]
|
||||
[noteBeforeDictation]
|
||||
);
|
||||
|
||||
// 确认语音识别结果
|
||||
const confirmDictationResult = useCallback(() => {
|
||||
// 语音识别结束,确认当前文本
|
||||
setCurrentDictationText('');
|
||||
setNoteBeforeDictation('');
|
||||
}, []);
|
||||
|
||||
// 取消语音识别
|
||||
const cancelDictationResult = useCallback(() => {
|
||||
// 恢复到语音识别前的文本
|
||||
setNote(noteBeforeDictation);
|
||||
setCurrentDictationText('');
|
||||
setNoteBeforeDictation('');
|
||||
}, [noteBeforeDictation]);
|
||||
|
||||
const stepTitle = STEP_TITLES[currentStep] ?? STEP_TITLES[0];
|
||||
const stepDescription = STEP_DESCRIPTIONS[currentStep] ?? '';
|
||||
|
||||
@@ -224,19 +252,25 @@ export default function AddMedicationScreen() {
|
||||
};
|
||||
|
||||
Voice.onSpeechEnd = () => {
|
||||
// 语音识别结束,确认识别结果
|
||||
confirmDictationResult();
|
||||
setDictationActive(false);
|
||||
setDictationLoading(false);
|
||||
};
|
||||
|
||||
Voice.onSpeechResults = (event: any) => {
|
||||
// 获取最新的识别结果(这是累积的结果,包含之前说过的内容)
|
||||
const recognized = event?.value?.[0];
|
||||
if (recognized) {
|
||||
appendDictationResult(recognized);
|
||||
// 实时更新识别结果,替换式而非追加式
|
||||
updateDictationResult(recognized);
|
||||
}
|
||||
};
|
||||
|
||||
Voice.onSpeechError = (error: any) => {
|
||||
console.log('[MEDICATION] voice error', error);
|
||||
// 发生错误时取消识别
|
||||
cancelDictationResult();
|
||||
setDictationActive(false);
|
||||
setDictationLoading(false);
|
||||
Alert.alert('语音识别不可用', '无法使用语音输入,请检查权限设置后重试');
|
||||
@@ -249,7 +283,7 @@ export default function AddMedicationScreen() {
|
||||
})
|
||||
.catch(() => {});
|
||||
};
|
||||
}, [appendDictationResult, isDictationSupported]);
|
||||
}, [updateDictationResult, confirmDictationResult, cancelDictationResult, isDictationSupported]);
|
||||
|
||||
const handleNext = useCallback(async () => {
|
||||
if (!canProceed) return;
|
||||
@@ -359,25 +393,34 @@ export default function AddMedicationScreen() {
|
||||
|
||||
try {
|
||||
if (dictationActive) {
|
||||
// 停止录音
|
||||
setDictationLoading(true);
|
||||
await Voice.stop();
|
||||
// Voice.onSpeechEnd 会自动确认结果
|
||||
setDictationLoading(false);
|
||||
return;
|
||||
}
|
||||
|
||||
// 开始录音前,保存当前的文本内容
|
||||
setNoteBeforeDictation(note);
|
||||
setCurrentDictationText('');
|
||||
|
||||
setDictationLoading(true);
|
||||
try {
|
||||
// 确保之前的录音已停止
|
||||
await Voice.stop();
|
||||
} catch {
|
||||
// no-op: safe to ignore if not already recording
|
||||
// 忽略错误:如果之前没有录音,stop 会抛出异常
|
||||
}
|
||||
|
||||
// 开始语音识别
|
||||
await Voice.start('zh-CN');
|
||||
} catch (error) {
|
||||
console.log('[MEDICATION] unable to start dictation', error);
|
||||
setDictationLoading(false);
|
||||
Alert.alert('无法启动语音输入', '请检查麦克风与语音识别权限后重试');
|
||||
}
|
||||
}, [dictationActive, dictationLoading, isDictationSupported]);
|
||||
}, [dictationActive, dictationLoading, isDictationSupported, note]);
|
||||
|
||||
// 处理图片选择(拍照或相册)
|
||||
const handleSelectPhoto = useCallback(() => {
|
||||
|
||||
Reference in New Issue
Block a user