Quasar + TypeScript 企业级语音识别组件开发指南
Quasar + TypeScript 企业级语音识别组件开发指南
一、组件概述
本文档介绍基于Quasar 框架和Vue 3 组合式 API开发的企业级语音识别组件SpeechRecognitionComponent,集成浏览器原生语音识别 API,支持实时语音转文本、状态管理、错误处理及语音指令扩展。组件遵循 TypeScript 类型安全规范,确保代码健壮性和可维护性,适用于智能客服、语音输入、会议记录等企业级场景。
二、核心功能
|
功能 |
描述 |
|
实时语音监听 |
支持开始/停止/暂停语音监听,实时反馈识别状态(监听中/已暂停/未启动) |
|
结果分层显示 |
区分最终识别结果(稳定文本)和临时结果(实时草稿),提升用户体验 |
|
全类型安全 |
定义完整 TypeScript 接口(如SpeechRecognitionEvent),避免any类型 |
|
错误处理与兼容性 |
检测浏览器支持性,处理识别错误(如无权限、网络异常)并显示友好提示 |
|
语音指令扩展 |
支持自定义语音指令(如“清空内容”“停止监听”),实现语音交互逻辑 |
三、完整组件实现
3.1 模板(Template)
<template>
<q-card class="speech-recognition-card q-pa-md">
<!-- 标题区域 -->
<q-card-section class="q-pa-none">
<div class="text-h6">语音识别模块</div>
</q-card-section>
<!-- 状态与控制区域 -->
<q-card-section class="q-pa-none q-mt-md">
<!-- 状态指示器 -->
<div class="row items-center q-mb-md">
<q-icon
name="record_voice_over"
size="sm"
:color="isListening ? 'red' : 'grey'"
class="q-mr-sm"
/>
<span class="text-caption">状态: {{ recognitionState }}</span>
</div>
<!-- 控制按钮组 -->
<div class="row q-gutter-sm">
<q-btn
color="primary"
icon="keyboard_voice"
label="开始监听"
:disable="isListening"
@click="startRecognition"
/>
<q-btn
color="negative"
icon="stop"
label="停止监听"
:disable="!isListening"
@click="stopRecognition"
/>
<q-btn
color="warning"
icon="pause"
label="暂停监听"
:disable="!isListening || isPaused"
@click="pauseRecognition"
/>
</div>
<!-- 识别结果显示 -->
<q-input
v-model="transcript"
class="q-mt-md"
type="textarea"
filled
autogrow
label="识别结果"
readonly
rows="4"
/>
<!-- 临时结果提示 -->
<div v-if="interimTranscript" class="q-mt-xs text-caption text-italic text-grey-6">
正在输入: {{ interimTranscript }}
</div>
</q-card-section>
<!-- 错误提示区域 -->
<q-card-section v-if="errorMessage" class="q-pa-none q-mt-md">
<q-banner dense class="bg-negative text-white">
{{ errorMessage }}
</q-banner>
</q-card-section>
</q-card>
</template>
3.2 脚本(Script)
<script setup lang="ts">
import { ref, computed, onMounted, onUnmounted } from 'vue';
// ============================ 类型定义 ============================
/** 语音识别结果事件接口 */
interface SpeechRecognitionEvent extends Event {
resultIndex: number; // 结果起始索引
results: SpeechRecognitionResultList; // 识别结果列表
}
/** 语音识别错误事件接口 */
interface SpeechRecognitionErrorEvent extends Event {
error: string; // 错误类型(如 "not-allowed" "no-speech")
message?: string; // 错误描述(可选)
}
/** 语音识别核心接口(封装浏览器原生 API) */
interface SpeechRecognition extends EventTarget {
continuous: boolean; // 是否持续监听(true: 多轮识别,false: 单句识别)
interimResults: boolean; // 是否返回临时结果(true: 实时草稿,false: 仅最终结果)
lang: string; // 识别语言(如 "zh-CN" "en-US")
start(): void; // 开始监听
stop(): void; // 停止监听
abort(): void; // 终止监听(不返回结果)
onresult: ((event: SpeechRecognitionEvent) => void) | null; // 结果回调
onerror: ((event: SpeechRecognitionErrorEvent) => void) | null; // 错误回调
onstart: (() => void) | null; // 开始监听回调
onend: (() => void) | null; // 结束监听回调
onnomatch: (() => void) | null; // 无匹配结果回调
}
// 扩展 Window 接口,适配浏览器前缀(如 webkitSpeechRecognition)
declare global {
interface Window {
SpeechRecognition: new () => SpeechRecognition;
webkitSpeechRecognition: new () => SpeechRecognition;
}
}
// ============================ 响应式状态 ============================
const transcript = ref<string>(''); // 最终识别结果(稳定文本)
const interimTranscript = ref<string>(''); // 临时识别结果(实时草稿)
const isListening = ref<boolean>(false); // 是否正在监听
const isPaused = ref<boolean>(false); // 是否暂停监听
const errorMessage = ref<string>(''); // 错误信息
const recognition = ref<SpeechRecognition | null>(null); // 语音识别实例
// ============================ 计算属性 ============================
/** 识别状态文本(监听中/已暂停/未启动) */
const recognitionState = computed<string>(() => {
if (!isListening.value) return '未启动';
if (isPaused.value) return '已暂停';
return '监听中';
});
// ============================ 核心逻辑 ============================
/** 初始化语音识别实例 */
const initSpeechRecognition = (): void => {
// 检测浏览器支持性(兼容 Chrome/WebKit 内核)
const SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition;
if (!SpeechRecognition) {
errorMessage.value = '您的浏览器不支持语音识别 API(推荐使用 Chrome 或 Edge)';
return;
}
// 创建识别实例并配置参数
recognition.value = new SpeechRecognition();
recognition.value.continuous = true; // 持续监听(多轮识别)
recognition.value.interimResults = true; // 返回临时结果(实时反馈)
recognition.value.lang = 'zh-CN'; // 识别语言:中文(可扩展为多语言配置)
// 绑定事件回调
recognition.value.onstart = () => {
isListening.value = true;
isPaused.value = false;
errorMessage.value = '';
};
recognition.value.onresult = (event: SpeechRecognitionEvent) => {
let finalTranscript = '';
interimTranscript.value = '';
// 遍历结果列表,区分临时/最终结果
for (let i = event.resultIndex; i < event.results.length; i++) {
const transcriptPart = event.results[i][0].transcript;
if (event.results[i].isFinal) {
finalTranscript += transcriptPart; // 最终结果:追加到稳定文本
} else {
interimTranscript.value += transcriptPart; // 临时结果:实时显示草稿
}
}
if (finalTranscript) transcript.value += finalTranscript;
};
recognition.value.onerror = (event: SpeechRecognitionErrorEvent) => {
console.error('语音识别错误:', event.error);
errorMessage.value = `识别失败: ${getErrorText(event.error)}`;
stopRecognition(); // 错误时自动停止监听
};
recognition.value.onend = () => {
isListening.value = false;
isPaused.value = false;
};
};
/** 错误类型转文本(用户友好提示) */
const getErrorText = (errorType: string): string => {
const errorMap: Record<string, string> = {
'not-allowed': '无麦克风权限,请在浏览器设置中启用',
'no-speech': '未检测到语音,请重试',
'audio-capture': '麦克风访问失败,请检查设备',
'network': '网络错误,无法连接识别服务'
};
return errorMap[errorType] || errorType;
};
// ============================ 控制方法 ============================
/** 开始语音监听 */
const startRecognition = (): void => {
if (!recognition.value) initSpeechRecognition(); // 延迟初始化(首次使用时)
try {
recognition.value?.start();
transcript.value = ''; // 清空历史结果
interimTranscript.value = ''; // 清空临时结果
} catch (error) {
errorMessage.value = `启动失败: ${error instanceof Error ? error.message : String(error)}`;
}
};
/** 停止语音监听 */
const stopRecognition = (): void => {
try {
recognition.value?.stop(); // 停止监听并返回最终结果
} catch (error) {
console.error('停止监听失败:', error);
} finally {
isListening.value = false;
isPaused.value = false;
}
};
/** 暂停语音监听(暂停后可恢复) */
const pauseRecognition = (): void => {
if (recognition.value && isListening.value) {
recognition.value.stop(); // 调用 stop() 触发 onend,标记暂停状态
isPaused.value = true;
}
};
// ============================ 生命周期 ============================
onMounted(() => {
initSpeechRecognition(); // 组件挂载时初始化(预检测浏览器支持性)
});
onUnmounted(() => {
if (isListening.value) stopRecognition(); // 组件卸载时停止监听,释放资源
});
</script>
3.3 样式(Style)
<style scoped lang="scss">
.speech-recognition-card {
min-width: 300px;
max-width: 600px;
margin: 0 auto; // 居中布局
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); // 轻微阴影提升层次感
}
/* 临时结果样式优化 */
.text-italic {
font-style: italic;
color: #666; // 灰色区分临时结果与最终结果
}
</style>
四、企业级最佳实践
4.1 类型安全与代码健壮性
- 严格类型定义:通过SpeechRecognitionSpeechRecognitionEvent等接口,避免any类型,确保编译时类型校验。
- 空值处理:所有状态变量(如recognition)初始化为null,操作前通过?.安全调用(如recognition.value?.start())。
- 错误分级处理:将原生错误类型(如not-allowed)映射为用户友好提示,避免技术术语暴露。
- 延迟初始化:语音识别实例在首次调用startRecognition时初始化,减少组件挂载时的资源占用。
- 组件卸载清理:onUnmounted钩子中停止监听,避免内存泄漏和无效资源占用。
- 状态联动:通过isListeningisPaused精确控制按钮禁用状态,防止重复操作(如监听中无法再次开始)。
- 实时反馈:通过图标颜色(红色=监听中/灰色=未启动)和状态文本,直观展示组件运行状态。
- 分层结果显示:临时结果(灰色斜体)与最终结果(黑色正常文本)区分,降低用户等待焦虑。
- 错误可视化:使用 Quasarq-banner组件展示错误信息,样式醒目且符合 Quasar 设计规范。
4.2 性能与资源管理
4.3 用户体验优化
五、扩展功能:语音指令系统
基于核心组件扩展语音指令功能,支持通过语音触发特定操作(如“清空内容”“停止监听”),适用于无接触交互场景。
5.1 指令类型与注册
// 扩展脚本部分(添加到 <script setup lang="ts"> 中)
// 语音指令类型定义
interface VoiceCommand {
command: string; // 指令关键词(如“清空内容”)
callback: () => void; // 指令触发的回调函数
description: string; // 指令描述(用于用户提示)
}
// 注册指令列表(支持动态增删)
const voiceCommands = ref<VoiceCommand[]>([
{
command: "清空内容",
callback: () => { transcript.value = ''; },
description: "清空当前识别结果"
},
{
command: "停止监听",
callback: () => { stopRecognition(); },
description: "停止语音识别"
}
]);
const activeCommand = ref<string>(''); // 当前执行的指令(用于UI反馈)
5.2 指令匹配与执行
修改recognition.value.onresult回调,添加指令匹配逻辑:
// 替换原 onresult 回调
recognition.value.onresult = (event: SpeechRecognitionEvent) => {
let finalTranscript = '';
interimTranscript.value = '';
// 提取结果(原有逻辑不变)
for (let i = event.resultIndex; i < event.results.length; i++) {
const transcriptPart = event.results[i][0].transcript;
if (event.results[i].isFinal) finalTranscript += transcriptPart;
else interimTranscript.value += transcriptPart;
}
// 新增:指令匹配逻辑
if (finalTranscript) {
transcript.value += finalTranscript;
const matchedCmd = voiceCommands.value.find(cmd =>
finalTranscript.trim().toLowerCase().includes(cmd.command.toLowerCase())
);
if (matchedCmd) {
activeCommand.value = matchedCmd.command; // 显示指令执行状态
setTimeout(() => {
matchedCmd.callback(); // 执行指令回调
activeCommand.value = ''; // 300ms后清除状态
}, 300);
}
}
};
5.3 指令UI反馈
在模板中添加指令执行状态显示:
<!-- 模板中新增(放在识别结果下方) -->
<q-card-section v-if="activeCommand" class="q-pa-none q-mt-xs">
<q-banner dense class="bg-positive text-white">
<q-icon name="check_circle" class="q-mr-sm" />
已执行指令: {{ activeCommand }}
</q-banner>
</q-card-section>
<!-- 指令列表提示(非监听状态显示) -->
<q-card-section v-if="!isListening" class="q-pa-none q-mt-xs">
<div class="text-caption text-grey-6">
<div class="text-weight-medium">语音指令:</div>
<div v-for="(cmd, idx) in voiceCommands" :key="idx">
• "{{ cmd.command }}" - {{ cmd.description }}
</div>
</div>
</q-card-section>
六、总结
本组件通过TypeScript 类型安全、响应式状态管理、错误友好处理和可扩展指令系统,实现企业级语音识别功能。核心优势包括:
- 低侵入性:基于浏览器原生 API,无需额外依赖,轻量化集成。
- 高可定制:支持语言切换、指令扩展、样式定制,适配不同业务场景。
- 企业级标准:符合代码健壮性、性能优化和用户体验规范,可直接用于生产环境。
可进一步扩展方向:多语言支持、后端 ASR 服务集成(如阿里云语音识别)、噪声抑制等高级特性。# Quasar + TypeScript 企业级语音识别组件开发指南
一、组件概述
本文档介绍基于Quasar 框架和Vue 3 组合式 API开发的企业级语音识别组件SpeechRecognitionComponent。该组件集成浏览器原生语音识别 API,支持实时语音转文本、状态管理、错误处理及语音指令扩展,适用于智能客服、语音输入、会议记录等场景。组件遵循 TypeScript 类型安全规范,确保代码健壮性和可维护性,满足企业级应用对稳定性、性能和用户体验的要求。
二、核心功能
|
功能 |
描述 |
|
实时语音监听 |
支持开始/停止/暂停语音监听,实时反馈识别状态(监听中/已暂停/未启动) |
|
分层结果展示 |
区分最终识别结果(稳定文本)和临时结果(实时草稿),提升用户体验 |
|
全类型安全 |
定义完整 TypeScript 接口(如SpeechRecognitionEvent),避免any类型 |
|
错误处理与兼容性 |
检测浏览器支持性,处理识别错误(如无权限、网络异常)并显示友好提示 |
|
语音指令扩展 |
支持自定义语音指令(如“清空内容”“停止监听”),实现语音交互逻辑 |
三、完整组件实现
3.1 模板实现(Template)
<template>
<q-card class="speech-recognition-card q-pa-md">
<!-- 标题区域 -->
<q-card-section class="q-pa-none">
<div class="text-h6">语音识别模块</div>
</q-card-section>
<!-- 状态与控制区域 -->
<q-card-section class="q-pa-none q-mt-md">
<!-- 状态指示器 -->
<div class="row items-center q-mb-md">
<q-icon
name="record_voice_over"
size="sm"
:color="isListening ? 'red' : 'grey'"
class="q-mr-sm"
/>
<span class="text-caption">状态: {{ recognitionState }}</span>
</div>
<!-- 控制按钮组 -->
<div class="row q-gutter-sm">
<q-btn
color="primary"
icon="keyboard_voice"
label="开始监听"
:disable="isListening"
@click="startRecognition"
/>
<q-btn
color="negative"
icon="stop"
label="停止监听"
:disable="!isListening"
@click="stopRecognition"
/>
<q-btn
color="warning"
icon="pause"
label="暂停监听"
:disable="!isListening || isPaused"
@click="pauseRecognition"
/>
</div>
<!-- 识别结果显示 -->
<q-input
v-model="transcript"
class="q-mt-md"
type="textarea"
filled
autogrow
label="识别结果"
readonly
rows="4"
/>
<!-- 临时结果提示 -->
<div v-if="interimTranscript" class="q-mt-xs text-caption text-italic text-grey-6">
正在输入: {{ interimTranscript }}
</div>
</q-card-section>
<!-- 错误提示区域 -->
<q-card-section v-if="errorMessage" class="q-pa-none q-mt-md">
<q-banner dense class="bg-negative text-white">
{{ errorMessage }}
</q-banner>
</q-card-section>
</q-card>
</template>
3.2 脚本实现(Script)
<script setup lang="ts">
import { ref, computed, onMounted, onUnmounted } from 'vue';
// ============================ 类型定义 ============================
/** 语音识别结果事件接口:扩展原生 Event,包含识别结果 */
interface SpeechRecognitionEvent extends Event {
resultIndex: number; // 结果起始索引
results: SpeechRecognitionResultList; // 识别结果列表(原生接口,包含临时/最终结果)
}
/** 语音识别错误事件接口:扩展原生 Event,包含错误信息 */
interface SpeechRecognitionErrorEvent extends Event {
error: string; // 错误类型(如 "not-allowed" "no-speech")
message?: string; // 错误描述(可选,部分浏览器支持)
}
/** 语音识别核心接口:封装浏览器原生 SpeechRecognition API */
interface SpeechRecognition extends EventTarget {
continuous: boolean; // 是否持续监听(true:多轮识别,false:单句识别)
interimResults: boolean; // 是否返回临时结果(true:实时草稿,false:仅最终结果)
lang: string; // 识别语言(如 "zh-CN" "en-US")
start(): void; // 开始监听
stop(): void; // 停止监听(返回最终结果)
abort(): void; // 终止监听(不返回结果)
onresult: ((event: SpeechRecognitionEvent) => void) | null; // 结果回调
onerror: ((event: SpeechRecognitionErrorEvent) => void) | null; // 错误回调
onstart: (() => void) | null; // 开始监听回调
onend: (() => void) | null; // 结束监听回调
onnomatch: (() => void) | null; // 无匹配结果回调
}
// 扩展 Window 接口,适配浏览器前缀(如 Chrome 的 webkitSpeechRecognition)
declare global {
interface Window {
SpeechRecognition: new () => SpeechRecognition;
webkitSpeechRecognition: new () => SpeechRecognition;
}
}
// ============================ 响应式状态 ============================
const transcript = ref<string>(''); // 最终识别结果(稳定文本,用户可见)
const interimTranscript = ref<string>(''); // 临时识别结果(实时草稿,灰色斜体显示)
const isListening = ref<boolean>(false); // 是否正在监听(控制按钮状态)
const isPaused = ref<boolean>(false); // 是否暂停监听(区分停止与暂停状态)
const errorMessage = ref<string>(''); // 错误信息(用户友好提示)
const recognition = ref<SpeechRecognition | null>(null); // 语音识别实例(浏览器原生 API 封装)
// ============================ 计算属性 ============================
/** 识别状态文本:根据 isListening 和 isPaused 动态生成用户友好状态 */
const recognitionState = computed<string>(() => {
if (!isListening.value) return '未启动';
if (isPaused.value) return '已暂停';
return '监听中';
});
// ============================ 核心逻辑 ============================
/** 初始化语音识别实例:检测浏览器支持性并配置参数 */
const initSpeechRecognition = (): void => {
// 检测浏览器支持性(兼容 Chrome/WebKit 内核浏览器)
const SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition;
if (!SpeechRecognition) {
errorMessage.value = '您的浏览器不支持语音识别 API(推荐使用 Chrome 或 Edge)';
return;
}
// 创建识别实例并配置核心参数
recognition.value = new SpeechRecognition();
recognition.value.continuous = true; // 持续监听(支持多轮语音输入)
recognition.value.interimResults = true; // 返回临时结果(实时反馈)
recognition.value.lang = 'zh-CN'; // 识别语言:中文(可扩展为多语言配置)
// 绑定事件回调:处理识别结果
recognition.value.onresult = (event: SpeechRecognitionEvent) => {
let finalTranscript = ''; // 最终结果(稳定文本)
interimTranscript.value = ''; // 临时结果(实时草稿,初始化为空)
// 遍历结果列表,区分临时/最终结果
for (let i = event.resultIndex; i < event.results.length; i++) {
const transcriptPart = event.results[i][0].transcript; // 当前片段文本
if (event.results[i].isFinal) {
finalTranscript += transcriptPart; // 最终结果:追加到稳定文本
} else {
interimTranscript.value += transcriptPart; // 临时结果:实时更新草稿
}
}
// 最终结果非空时,更新识别结果
if (finalTranscript) transcript.value += finalTranscript;
};
// 绑定事件回调:处理错误
recognition.value.onerror = (event: SpeechRecognitionErrorEvent) => {
console.error('语音识别错误:', event.error);
errorMessage.value = `识别失败: ${getErrorText(event.error)}`; // 错误信息用户友好化
stopRecognition(); // 错误时自动停止监听
};
// 绑定事件回调:监听结束(如超时、手动停止)
recognition.value.onend = () => {
isListening.value = false;
isPaused.value = false;
};
};
/** 错误类型映射:将原生错误类型转换为用户友好提示 */
const getErrorText = (errorType: string): string => {
const errorMap: Record<string, string> = {
'not-allowed': '无麦克风权限,请在浏览器设置中启用',
'no-speech': '未检测到语音,请重试',
'audio-capture': '麦克风访问失败,请检查设备',
'network': '网络错误,无法连接识别服务'
};
return errorMap[errorType] || errorType; // 未知错误直接显示类型
};
// ============================ 控制方法 ============================
/** 开始语音监听:初始化实例(首次调用时)并启动监听 */
const startRecognition = (): void => {
if (!recognition.value) initSpeechRecognition(); // 延迟初始化,减少组件挂载时资源占用
try {
recognition.value?.start(); // 调用原生 API 启动监听
transcript.value = ''; // 清空历史结果
interimTranscript.value = ''; // 清空临时结果
} catch (error) {
errorMessage.value = `启动失败: ${error instanceof Error ? error.message : String(error)}`;
}
};
/** 停止语音监听:调用原生 API 停止并重置状态 */
const stopRecognition = (): void => {
try {
recognition.value?.stop(); // 调用原生 API 停止监听(返回最终结果)
} catch (error) {
console.error('停止监听失败:', error); // 非关键错误仅控制台输出,不影响用户
} finally {
isListening.value = false; // 强制重置状态,避免 API 异常导致状态不一致
isPaused.value = false;
}
};
/** 暂停语音监听:停止监听但标记为暂停状态(可恢复) */
const pauseRecognition = (): void => {
if (recognition.value && isListening.value) {
recognition.value.stop(); // 调用原生 API 停止监听
isPaused.value = true; // 标记为暂停状态,区别于完全停止
}
};
// ============================ 生命周期 ============================
/** 组件挂载时初始化:预检测浏览器支持性,提前发现环境问题 */
onMounted(() => {
initSpeechRecognition();
});
/** 组件卸载时清理:停止监听并释放资源,避免内存泄漏 */
onUnmounted(() => {
if (isListening.value) stopRecognition();
});
</script>
3.3 样式实现(Style)
<style scoped>
/* 卡片容器样式:限制宽度范围,适配不同屏幕 */
.speech-recognition-card {
min-width: 300px; /* 最小宽度,避免移动端过窄 */
max-width: 600px; /* 最大宽度,避免桌面端过宽 */
margin: 0 auto; /* 居中布局,提升视觉体验 */
}
/* 临时结果样式:灰色斜体,区分于最终结果 */
.text-italic {
font-style: italic;
color: #666; /* 灰色文本,弱化视觉权重 */
}
</style>
四、企业级最佳实践
4.1 类型安全与代码健壮性
- 严格类型定义:通过SpeechRecognitionSpeechRecognitionEvent等接口,明确原生 API 的参数和返回值类型,避免any类型导致的运行时错误。例如,SpeechRecognitionEvent接口定义了resultIndex和results属性,确保事件处理时类型校验通过。
- 空值安全处理:所有状态变量(如recognition)初始化为null,操作前通过可选链(?.)安全调用(如recognition.value?.start()),避免空指针异常。
- 错误分级处理:将原生错误类型(如not-allowed)映射为用户友好提示(如“无麦克风权限”),隐藏技术细节,提升用户体验。
- 延迟初始化:语音识别实例在首次调用startRecognition时初始化,而非组件挂载时,减少初始加载资源占用。
- 组件卸载清理:onUnmounted钩子中停止监听,释放麦克风资源,避免组件销毁后仍占用设备权限。
- 状态联动控制:通过isListening和isPaused精确控制按钮禁用状态(如监听中禁用“开始”按钮),防止重复操作导致的 API 异常。
- 实时状态反馈:通过图标颜色(红色=监听中/灰色=未启动)和状态文本(“监听中”“已暂停”),直观展示组件运行状态,降低用户等待焦虑。
- 分层结果展示:临时结果(灰色斜体)与最终结果(黑色正常文本)区分,实时反馈语音输入进度,提升交互流畅性。
- 错误可视化:使用 Quasarq-banner组件展示错误信息,样式醒目且符合 Quasar 设计规范,确保用户不会忽略关键提示。
4.2 性能与资源管理
4.3 用户体验优化
五、扩展功能:语音指令系统
基于核心组件扩展语音指令功能,支持通过语音触发特定操作(如“清空内容”“停止监听”),适用于无接触交互场景(如医疗、工业控制)。
5.1 指令类型与注册
// 扩展脚本部分(添加到 <script setup lang="ts"> 中)
/** 语音指令类型:定义指令关键词、回调函数和描述 */
interface VoiceCommand {
command: string; // 指令关键词(如“清空内容”)
callback: () => void; // 指令触发的回调函数(如清空识别结果)
description: string; // 指令描述(用于用户提示,如“清空当前识别结果”)
}
/** 注册语音指令列表:支持动态增删,适配不同业务场景 */
const voiceCommands = ref<VoiceCommand[]>([
{
command: "清空内容",
callback: () => { transcript.value = ''; },
description: "清空当前识别结果"
},
{
command: "停止监听",
callback: () => { stopRecognition(); },
description: "停止语音识别"
}
]);
const activeCommand = ref<string>(''); // 当前执行的指令(用于 UI 反馈)
5.2 指令匹配与执行
修改onresult事件处理逻辑,添加指令匹配:
// 修改 recognition.value.onresult 回调,加入指令判断
recognition.value.onresult = (event: SpeechRecognitionEvent) => {
let finalTranscript = '';
interimTranscript.value = '';
// 遍历结果列表,提取临时/最终结果(原有逻辑不变)
for (let i = event.resultIndex; i < event.results.length; i++) {
const transcriptPart = event.results[i][0].transcript;
if (event.results[i].isFinal) finalTranscript += transcriptPart;
else interimTranscript.value += transcriptPart;
}
// 最终结果非空时,检查是否匹配语音指令
if (finalTranscript) {
transcript.value += finalTranscript;
// 匹配指令:忽略大小写,判断结果是否包含指令关键词
const matchedCommand = voiceCommands.value.find(cmd =>
finalTranscript.trim().toLowerCase().includes(cmd.command.toLowerCase())
);
if (matchedCommand) {
activeCommand.value = matchedCommand.command; // 显示指令执行状态
setTimeout(() => {
matchedCommand.callback(); // 执行指令回调
activeCommand.value = ''; // 300ms 后清除状态
}, 300);
}
}
};
5.3 指令 UI 反馈
在模板中添加指令执行状态和可用指令列表:
<!-- 扩展模板部分(添加到 <template> 中) -->
<!-- 指令执行状态提示 -->
<q-card-section v-if="activeCommand" class="q-pa-none q-mt-xs">
<q-banner dense class="bg-positive text-white">
<q-icon name="check_circle" class="q-mr-sm" />
已执行指令: {{ activeCommand }}
</q-banner>
</q-card-section>
<!-- 可用指令列表(非监听状态显示) -->
<q-card-section v-if="!isListening" class="q-pa-none q-mt-xs">
<div class="text-caption text-grey-6">
<div class="text-weight-medium">语音指令:</div>
<div v-for="(cmd, idx) in voiceCommands" :key="idx">
• "{{ cmd.command }}" - {{ cmd.description }}
</div>
</div>
</q-card-section>
六、总结
本组件通过TypeScript 类型安全、响应式状态管理、错误友好处理和可扩展指令系统,实现了企业级语音识别功能。核心优势包括:
- 低侵入性:基于浏览器原生 API,无需额外依赖,轻量化集成到 Quasar 应用。
- 高可定制:支持语言切换、指令扩展、样式定制,适配智能客服、会议记录等多场景。
- 企业级标准:符合代码健壮性、性能优化和用户体验规范,可直接用于生产环境。
可进一步扩展方向:多语言支持(动态切换lang属性)、后端 ASR 服务集成(如阿里云语音识别)、噪声抑制(结合 Web Audio API)等高级特性。
浙公网安备 33010602011771号