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 11:59:54 +08:00
parent aa09a1ffe1
commit 21a63661cd
No known key found for this signature in database
GPG Key ID: A6A54CF66F56BB64
4 changed files with 207 additions and 9 deletions

View File

@ -95,6 +95,122 @@
@clear-date-reports="onClearDateReports"
@clear-all-reports="onClearAllReports"
/>
<!-- 麦克风权限引导弹框 -->
<v-dialog
v-model="showMicPermissionDialog"
max-width="480"
persistent
>
<v-card rounded="xl">
<div class="text-center pt-8 pb-2">
<v-avatar
color="primary"
size="72"
>
<v-icon
icon="mdi-microphone-outline"
size="36"
/>
</v-avatar>
</div>
<v-card-title class="text-center text-h6 pt-4">
开启环境噪音监测
</v-card-title>
<v-card-text class="text-body-2 text-medium-emphasis px-6">
<p class="mb-3">
该功能可以实时监测教室环境噪音帮助营造安静的学习氛围
</p>
<div class="d-flex align-start mb-2">
<v-icon
icon="mdi-chart-line"
size="18"
color="primary"
class="mr-2 mt-1 flex-shrink-0"
/>
<span>实时显示环境分贝数与噪音等级评估</span>
</div>
<div class="d-flex align-start mb-2">
<v-icon
icon="mdi-clock-check-outline"
size="18"
color="primary"
class="mr-2 mt-1 flex-shrink-0"
/>
<span>在晚自习时段自动记录并生成统计报告</span>
</div>
<div class="d-flex align-start mb-4">
<v-icon
icon="mdi-shield-check-outline"
size="18"
color="primary"
class="mr-2 mt-1 flex-shrink-0"
/>
<span>音频数据仅在本地处理不会上传或存储录音</span>
</div>
<v-alert
type="info"
variant="tonal"
density="compact"
class="mb-2"
>
需要授予麦克风权限才能使用此功能浏览器将弹出权限请求请点击允许
</v-alert>
</v-card-text>
<v-divider />
<v-card-text class="px-6 py-3">
<v-list
density="compact"
class="pa-0"
>
<v-list-item class="px-0">
<template #prepend>
<v-icon
icon="mdi-microphone"
class="mr-3"
/>
</template>
<v-list-item-title>启用噪音监测</v-list-item-title>
<v-list-item-subtitle class="text-caption">
关闭后将不再提醒
</v-list-item-subtitle>
<template #append>
<v-switch
:model-value="noiseEnabled"
hide-details
density="comfortable"
@update:model-value="onPermissionDialogToggle"
/>
</template>
</v-list-item>
</v-list>
</v-card-text>
<v-card-actions class="px-6 pb-5">
<v-btn
variant="text"
@click="dismissMicPermission"
>
暂不开启
</v-btn>
<v-spacer />
<v-btn
color="primary"
variant="elevated"
prepend-icon="mdi-microphone"
:disabled="!noiseEnabled"
@click="grantMicPermission"
>
授权并开始
</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
</v-card>
<!-- 全屏时间弹框 -->
@ -634,6 +750,8 @@ export default {
noiseReportMeta: {}, // { dates: { '2026-03-07': { count, avgScore, sessions } } }
noiseSelectedDate: '', // YYYY-MM-DD
noiseCurrentDateReports: [], //
//
showMicPermissionDialog: false,
}
},
computed: {
@ -798,24 +916,33 @@ export default {
}
},
},
mounted() {
async mounted() {
this.loadSettings()
this.startTimer()
this.unwatch = watchSettings(() => {
this.loadSettings()
})
// : &
// : &
this.noiseEnabled = getSetting('noiseMonitor.enabled')
if (this.noiseEnabled) {
this.noiseHistory = noiseService.getHistory()
//
this.loadNoiseSessionConfig().then(() => {
this.startSessionCheck()
// autoStart:
if (getSetting('noiseMonitor.autoStart') && !this.noiseMonitoring) {
this.startNoise()
//
const micState = await this.checkMicPermission()
if (micState === 'granted') {
//
this.loadNoiseSessionConfig().then(() => {
this.startSessionCheck()
if (getSetting('noiseMonitor.autoStart') && !this.noiseMonitoring) {
this.startNoise()
}
})
} else if (micState === 'prompt') {
//
if (!getSetting('noiseMonitor.permissionDismissed')) {
this.showMicPermissionDialog = true
}
})
}
// denied
}
},
beforeUnmount() {
@ -997,6 +1124,38 @@ export default {
const centis = Math.floor((ms % 1000) / 10)
return `${String(m).padStart(2, '0')}:${String(s).padStart(2, '0')}.${String(centis).padStart(2, '0')}`
},
// ===== =====
async checkMicPermission() {
try {
if (!navigator.permissions || !navigator.permissions.query) return 'granted' // Permissions API
const result = await navigator.permissions.query({ name: 'microphone' })
return result.state // 'granted' | 'prompt' | 'denied'
} catch {
// Firefox query microphone prompt
return 'prompt'
}
},
async grantMicPermission() {
this.showMicPermissionDialog = false
//
await this.loadNoiseSessionConfig()
this.startSessionCheck()
// startNoise
await this.startNoise()
},
dismissMicPermission() {
this.showMicPermissionDialog = false
setSetting('noiseMonitor.permissionDismissed', true)
},
onPermissionDialogToggle(val) {
this.noiseEnabled = val
setSetting('noiseMonitor.enabled', val)
if (!val) {
//
this.showMicPermissionDialog = false
setSetting('noiseMonitor.permissionDismissed', true)
}
},
// ===== methods =====
async startNoise() {
try {

View File

@ -0,0 +1,27 @@
<template>
<settings-card
border
icon="mdi-microphone"
title="噪音监测"
>
<v-list>
<setting-item :setting-key="'noiseMonitor.enabled'" />
<v-divider class="my-2" />
<setting-item :setting-key="'noiseMonitor.autoStart'" />
<v-divider class="my-2" />
<setting-item :setting-key="'noiseMonitor.permissionDismissed'" />
</v-list>
</settings-card>
</template>
<script>
import SettingsCard from '@/components/SettingsCard.vue';
import SettingItem from '@/components/settings/SettingItem.vue';
export default {
name: 'NoiseSettingsCard',
components: { SettingsCard, SettingItem },
};
</script>

View File

@ -172,6 +172,10 @@
border
@saved="onSettingsSaved"
/>
<noise-settings-card
border
class="mt-4"
/>
</v-tabs-window-item>
<v-tabs-window-item value="notification">
@ -275,6 +279,7 @@ import SubjectManagementCard from "@/components/settings/cards/SubjectManagement
import KvDatabaseCard from "@/components/settings/cards/KvDatabaseCard.vue";
import HitokotoSettings from "@/components/HitokotoSettings.vue";
import NotificationSoundSettings from "@/components/settings/NotificationSoundSettings.vue";
import NoiseSettingsCard from "@/components/settings/cards/NoiseSettingsCard.vue";
export default {
name: "Settings",
@ -298,6 +303,7 @@ export default {
KvDatabaseCard,
HitokotoSettings,
NotificationSoundSettings,
NoiseSettingsCard,
},
setup() {
const {mobile} = useDisplay();

View File

@ -113,6 +113,12 @@ const settingsDefinitions = {
description: "打开页面时自动开始监测",
icon: "mdi-play-circle-outline",
},
"noiseMonitor.permissionDismissed": {
type: "boolean",
default: false,
description: "已跳过麦克风权限引导(不再弹出介绍弹框)",
icon: "mdi-microphone-off",
},
// 时间卡片设置
"timeCard.enabled": {