diff --git a/src/components/TimeCard.vue b/src/components/TimeCard.vue index 45a5dbf..02b04c5 100644 --- a/src/components/TimeCard.vue +++ b/src/components/TimeCard.vue @@ -95,6 +95,122 @@ @clear-date-reports="onClearDateReports" @clear-all-reports="onClearAllReports" /> + + + + + + + + + + + + 开启环境噪音监测 + + + + + 该功能可以实时监测教室环境噪音,帮助营造安静的学习氛围: + + + + 实时显示环境分贝数与噪音等级评估 + + + + 在晚自习时段自动记录并生成统计报告 + + + + 音频数据仅在本地处理,不会上传或存储录音 + + + + 需要授予麦克风权限才能使用此功能。浏览器将弹出权限请求,请点击「允许」。 + + + + + + + + + + + + 启用噪音监测 + + 关闭后将不再提醒 + + + + + + + + + + + 暂不开启 + + + + 授权并开始 + + + + @@ -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 { diff --git a/src/components/settings/cards/NoiseSettingsCard.vue b/src/components/settings/cards/NoiseSettingsCard.vue new file mode 100644 index 0000000..7bc37a9 --- /dev/null +++ b/src/components/settings/cards/NoiseSettingsCard.vue @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + diff --git a/src/pages/settings.vue b/src/pages/settings.vue index df697bd..14ffbb5 100644 --- a/src/pages/settings.vue +++ b/src/pages/settings.vue @@ -172,6 +172,10 @@ border @saved="onSettingsSaved" /> + @@ -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(); diff --git a/src/utils/settings.js b/src/utils/settings.js index 74e8a5e..c475762 100644 --- a/src/utils/settings.js +++ b/src/utils/settings.js @@ -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": {
+ 该功能可以实时监测教室环境噪音,帮助营造安静的学习氛围: +