1
0
mirror of https://github.com/ZeroCatDev/Classworks.git synced 2026-03-21 09:13:10 +00:00

feat: 添加麦克风权限状态提示,优化噪音监测体验

This commit is contained in:
Sunwuyuan 2026-03-07 14:07:05 +08:00
parent 787c95ca18
commit 41dc2c5251
No known key found for this signature in database
GPG Key ID: A6A54CF66F56BB64
2 changed files with 113 additions and 24 deletions

View File

@ -83,6 +83,48 @@
<v-tabs-window v-model="activeTab"> <v-tabs-window v-model="activeTab">
<!-- ==================== 实时监测 ==================== --> <!-- ==================== 实时监测 ==================== -->
<v-tabs-window-item value="realtime"> <v-tabs-window-item value="realtime">
<!-- 麦克风不可用提示 -->
<v-alert
v-if="micPermissionState === 'denied'"
type="error"
variant="tonal"
class="ma-4 mb-0"
prominent
>
<template #prepend>
<v-icon
icon="mdi-microphone-off"
size="28"
/>
</template>
<div class="text-subtitle-2 font-weight-bold mb-1">
麦克风权限被拒绝
</div>
<div class="text-body-2">
浏览器已拒绝麦克风访问无法进行噪音监测请在浏览器地址栏左侧的锁图标中重新授予麦克风权限然后刷新页面
</div>
</v-alert>
<v-alert
v-else-if="micPermissionState === 'unavailable'"
type="warning"
variant="tonal"
class="ma-4 mb-0"
prominent
>
<template #prepend>
<v-icon
icon="mdi-microphone-question"
size="28"
/>
</template>
<div class="text-subtitle-2 font-weight-bold mb-1">
未检测到麦克风
</div>
<div class="text-body-2">
当前设备未检测到麦克风硬件无法进行噪音监测请连接麦克风后刷新页面重试
</div>
</v-alert>
<!-- 分贝仪表区 --> <!-- 分贝仪表区 -->
<div class="noise-dashboard pa-5"> <div class="noise-dashboard pa-5">
<div class="d-flex align-center justify-center"> <div class="d-flex align-center justify-center">
@ -344,6 +386,7 @@
prepend-icon="mdi-play" prepend-icon="mdi-play"
size="large" size="large"
class="px-6" class="px-6"
:disabled="micPermissionState === 'denied' || micPermissionState === 'unavailable'"
@click="$emit('start')" @click="$emit('start')"
> >
开始监测 开始监测
@ -1006,6 +1049,7 @@ export default {
lastSlice: { type: Object, default: null }, lastSlice: { type: Object, default: null },
history: { type: Array, default: () => [] }, history: { type: Array, default: () => [] },
isMonitoring: { type: Boolean, default: false }, isMonitoring: { type: Boolean, default: false },
micPermissionState: { type: String, default: '' },
sessionActive: { type: Boolean, default: false }, sessionActive: { type: Boolean, default: false },
sessionData: { type: Object, default: null }, sessionData: { type: Object, default: null },
reportMeta: { type: Object, default: () => ({ dates: {} }) }, reportMeta: { type: Object, default: () => ({ dates: {} }) },

View File

@ -39,7 +39,36 @@
style="min-width: 80px;" style="min-width: 80px;"
@click.stop="onNoiseClick" @click.stop="onNoiseClick"
> >
<!-- 无麦克风权限提示 -->
<template v-if="micPermissionState === 'denied'">
<v-icon
color="error"
size="24"
>
mdi-microphone-off
</v-icon>
<div
class="text-caption text-error mt-1"
style="white-space: nowrap; font-size: 10px;"
>
权限被拒绝
</div>
</template>
<template v-else-if="micPermissionState === 'unavailable'">
<v-icon
color="warning"
size="24"
>
mdi-microphone-question
</v-icon>
<div
class="text-caption text-warning mt-1"
style="white-space: nowrap; font-size: 10px;"
>
无麦克风
</div>
</template>
<template v-else>
<div <div
class="noise-side-db font-weight-bold" class="noise-side-db font-weight-bold"
:class="`text-${noiseDbColor}`" :class="`text-${noiseDbColor}`"
@ -61,6 +90,7 @@
> >
点击开启 点击开启
</div> </div>
</template>
</div> </div>
</div> </div>
</v-card-text> </v-card-text>
@ -80,6 +110,7 @@
:last-slice="noiseLastSlice" :last-slice="noiseLastSlice"
:history="noiseHistory" :history="noiseHistory"
:is-monitoring="noiseMonitoring" :is-monitoring="noiseMonitoring"
:mic-permission-state="micPermissionState"
:session-active="noiseSessionActive" :session-active="noiseSessionActive"
:session-data="noiseSessionData" :session-data="noiseSessionData"
:report-meta="noiseReportMeta" :report-meta="noiseReportMeta"
@ -749,6 +780,7 @@ export default {
noiseCurrentDateReports: [], // noiseCurrentDateReports: [], //
// //
showMicPermissionDialog: false, showMicPermissionDialog: false,
micPermissionState: '', // 'granted' | 'prompt' | 'denied' | 'unavailable'
} }
}, },
computed: { computed: {
@ -925,6 +957,7 @@ export default {
this.noiseHistory = noiseService.getHistory() this.noiseHistory = noiseService.getHistory()
// //
const micState = await this.checkMicPermission() const micState = await this.checkMicPermission()
this.micPermissionState = micState
if (micState === 'granted') { if (micState === 'granted') {
// //
this.loadNoiseSessionConfig().then(() => { this.loadNoiseSessionConfig().then(() => {
@ -939,7 +972,7 @@ export default {
this.showMicPermissionDialog = true this.showMicPermissionDialog = true
} }
} }
// denied // denied / unavailable micPermissionState
} }
}, },
beforeUnmount() { beforeUnmount() {
@ -1124,7 +1157,13 @@ export default {
// ===== ===== // ===== =====
async checkMicPermission() { async checkMicPermission() {
try { 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' }) const result = await navigator.permissions.query({ name: 'microphone' })
return result.state // 'granted' | 'prompt' | 'denied' return result.state // 'granted' | 'prompt' | 'denied'
} catch { } catch {
@ -1139,6 +1178,8 @@ export default {
this.startSessionCheck() this.startSessionCheck()
// startNoise // startNoise
await this.startNoise() await this.startNoise()
//
this.micPermissionState = await this.checkMicPermission()
}, },
dismissMicPermission() { dismissMicPermission() {
this.showMicPermissionDialog = false this.showMicPermissionDialog = false
@ -1202,6 +1243,10 @@ export default {
this.noiseHistory = [] this.noiseHistory = []
}, },
onNoiseClick() { onNoiseClick() {
if (this.micPermissionState === 'denied' || this.micPermissionState === 'unavailable') {
this.showNoiseDetail = true
return
}
if (!this.noiseMonitoring) { if (!this.noiseMonitoring) {
this.startNoise() this.startNoise()
} else { } else {