diff --git a/src/components/NoiseMonitorDetail.vue b/src/components/NoiseMonitorDetail.vue index 3e71251..6858e3d 100644 --- a/src/components/NoiseMonitorDetail.vue +++ b/src/components/NoiseMonitorDetail.vue @@ -83,6 +83,48 @@ + + + +
+ 麦克风权限被拒绝 +
+
+ 浏览器已拒绝麦克风访问,无法进行噪音监测。请在浏览器地址栏左侧的锁图标中重新授予麦克风权限,然后刷新页面。 +
+
+ + +
+ 未检测到麦克风 +
+
+ 当前设备未检测到麦克风硬件,无法进行噪音监测。请连接麦克风后刷新页面重试。 +
+
+
@@ -344,6 +386,7 @@ prepend-icon="mdi-play" size="large" class="px-6" + :disabled="micPermissionState === 'denied' || micPermissionState === 'unavailable'" @click="$emit('start')" > 开始监测 @@ -1006,6 +1049,7 @@ export default { lastSlice: { type: Object, default: null }, history: { type: Array, default: () => [] }, isMonitoring: { type: Boolean, default: false }, + micPermissionState: { type: String, default: '' }, sessionActive: { type: Boolean, default: false }, sessionData: { type: Object, default: null }, reportMeta: { type: Object, default: () => ({ dates: {} }) }, diff --git a/src/components/TimeCard.vue b/src/components/TimeCard.vue index 4243cda..4e875f1 100644 --- a/src/components/TimeCard.vue +++ b/src/components/TimeCard.vue @@ -39,28 +39,58 @@ style="min-width: 80px;" @click.stop="onNoiseClick" > - -
- {{ noiseDisplayDb }} -
-
- {{ noiseStatusText }} -
-
- 点击开启 -
+ + + +
@@ -80,6 +110,7 @@ :last-slice="noiseLastSlice" :history="noiseHistory" :is-monitoring="noiseMonitoring" + :mic-permission-state="micPermissionState" :session-active="noiseSessionActive" :session-data="noiseSessionData" :report-meta="noiseReportMeta" @@ -749,6 +780,7 @@ export default { noiseCurrentDateReports: [], // 当前日期的报告列表 // 麦克风权限引导 showMicPermissionDialog: false, + micPermissionState: '', // 'granted' | 'prompt' | 'denied' | 'unavailable' } }, computed: { @@ -925,6 +957,7 @@ export default { this.noiseHistory = noiseService.getHistory() // 检查麦克风权限状态 const micState = await this.checkMicPermission() + this.micPermissionState = micState if (micState === 'granted') { // 已授权 → 正常流程 this.loadNoiseSessionConfig().then(() => { @@ -939,7 +972,7 @@ export default { this.showMicPermissionDialog = true } } - // denied → 什么也不做 + // denied / unavailable → 不自动启动,micPermissionState 已记录 } }, beforeUnmount() { @@ -1124,7 +1157,13 @@ export default { // ===== 麦克风权限引导 ===== async checkMicPermission() { try { - if (!navigator.permissions || !navigator.permissions.query) return 'granted' // 不支持 Permissions API 时视为已授权 + // 先检查是否有麦克风硬件 + if (navigator.mediaDevices && navigator.mediaDevices.enumerateDevices) { + const devices = await navigator.mediaDevices.enumerateDevices() + const hasMic = devices.some(d => d.kind === 'audioinput') + if (!hasMic) return 'unavailable' + } + if (!navigator.permissions || !navigator.permissions.query) return 'granted' const result = await navigator.permissions.query({ name: 'microphone' }) return result.state // 'granted' | 'prompt' | 'denied' } catch { @@ -1139,6 +1178,8 @@ export default { this.startSessionCheck() // 调用 startNoise 触发浏览器权限弹框 await this.startNoise() + // 重新检查状态 + this.micPermissionState = await this.checkMicPermission() }, dismissMicPermission() { this.showMicPermissionDialog = false @@ -1202,6 +1243,10 @@ export default { this.noiseHistory = [] }, onNoiseClick() { + if (this.micPermissionState === 'denied' || this.micPermissionState === 'unavailable') { + this.showNoiseDetail = true + return + } if (!this.noiseMonitoring) { this.startNoise() } else {