From 5cebaec98af8b4d36b5bf7d24e6d8592dd5cf724 Mon Sep 17 00:00:00 2001
From: 罗广辉 <guanghui.luo@foxmail.com>
Date: Thu, 12 Feb 2026 00:22:41 +0800
Subject: [PATCH] feat: 语音

---
 uniapps/work-app/src/manifest.json                                  |    4 +
 applications/mobile-web-view/src/appPages/voiceCallDetail/index.vue |   59 +++++++++++++------
 uniapps/work-app/src/subPackages/voiceCallDetail/index.vue          |   59 ++++++++++---------
 uniapps/work-app/src/App.vue                                        |   12 +--
 4 files changed, 79 insertions(+), 55 deletions(-)

diff --git a/applications/mobile-web-view/src/appPages/voiceCallDetail/index.vue b/applications/mobile-web-view/src/appPages/voiceCallDetail/index.vue
index 6d7ee04..79c9fb0 100644
--- a/applications/mobile-web-view/src/appPages/voiceCallDetail/index.vue
+++ b/applications/mobile-web-view/src/appPages/voiceCallDetail/index.vue
@@ -8,7 +8,7 @@
 			<div class="contactAvatar">
 				<img :src="defaultAvatar" alt=""></img>
 			</div>
-			
+
 			<!--  联系人名称-->
 			<div class="contactName">
 				{{ contactName }}
@@ -39,6 +39,7 @@
 <script setup>
 import { useRoute } from 'vue-router'
 import defaultAvatar from '@/appDataSource/appwork/defaultAvatar.svg'
+import { showToast } from 'vant'
 const route = useRoute()
 /** ✅ 你的 WS 地址前缀(后面拼 userId) */
 // const WS_BASE = 'ws://218.202.104.82:38201/ws/chat?userId='
@@ -229,21 +230,43 @@
 	}
 
 	try {
-		localStream = await navigator.mediaDevices.getUserMedia({ 
-			audio: {
-				echoCancellation: true,
-				noiseSuppression: true,
-				autoGainControl: true,
-				// 移动端特定配置
-				channelCount: 1,
-				sampleRate: 16000,
-				sampleSize: 16
-			}
-	  	})
-		log('✅ 已获取本地麦克风')
+		// 尝试使用完整约束
+		try {
+			localStream = await navigator.mediaDevices.getUserMedia({
+				audio: {
+					echoCancellation: true,
+					noiseSuppression: true,
+					autoGainControl: true,
+					channelCount: 1,
+					sampleRate: 16000,
+					sampleSize: 16
+				}
+			})
+			log('✅ 已获取本地麦克风(标准约束)')
+		} catch (strictError) {
+			// 如果标准约束失败,尝试最简单的约束
+			log('⚠️ 标准约束失败,尝试简化约束:', String(strictError))
+			localStream = await navigator.mediaDevices.getUserMedia({
+				audio: true
+			})
+			log('✅ 已获取本地麦克风(简化约束)')
+		}
 		return localStream
 	} catch (e) {
 		log('❌ 获取麦克风失败:', String(e))
+
+		// 提供更详细的错误信息
+		if (e.name === 'NotReadableError') {
+			log('❌ NotReadableError: 麦克风可能被其他应用占用,或权限未正确配置')
+			showToast('麦克风被占用或权限未授予')
+		} else if (e.name === 'NotAllowedError') {
+			log('❌ NotAllowedError: 用户拒绝了麦克风权限')
+			showToast('请授予麦克风权限')
+		} else if (e.name === 'NotFoundError') {
+			log('❌ NotFoundError: 未找到麦克风设备')
+			showToast('未找到麦克风设备')
+		}
+
 		throw e
 	}
 }
@@ -530,13 +553,13 @@
 	lastOfferSdp = null
 
 	state.value = 'idle'
-	
+
 	// 清除之前的定时器
 	if (endTimer.value) {
 		clearTimeout(endTimer.value)
 		endTimer.value = null
 	}
-	
+
 	// 根据是谁发起的挂断决定不同的行为
 	if (sendToPeer) {
 		// 自己主动挂断:不提示,不延迟,直接跳转
@@ -552,7 +575,7 @@
 		// 对方挂断:显示提示,延迟10秒跳转
 		callEnded.value = true
 		endMessage.value = '对方已挂断'
-		
+
 		// 延迟5秒后跳转页面
 		endTimer.value = setTimeout(() => {
 			// 返回上一页(uni-app的语音通话列表页面)
@@ -580,7 +603,7 @@
 			if (params.access_token) {
 				accessToken.value = params.access_token;
 				log('🔗 从params获取access_token:', accessToken.value);
-			}	
+			}
 		} catch (e) {
 			log('❌ 解析params参数失败:', String(e));
 		}
@@ -882,4 +905,4 @@
 	height: 220px;
 	margin-bottom: 60px;
 }
-</style>
\ No newline at end of file
+</style>
diff --git a/uniapps/work-app/src/App.vue b/uniapps/work-app/src/App.vue
index cfaebb6..deec9d2 100644
--- a/uniapps/work-app/src/App.vue
+++ b/uniapps/work-app/src/App.vue
@@ -4,7 +4,7 @@
 import { useGlobalWS } from '@/hooks/useGlobalWS.js'
 // #ifdef APP-PLUS
 import { keepAliveStart, requestIgnoreBatteryOptimization } from '@/uni_modules/keep-app'
-import { openDialog, allowFloat,getBatteryCapacity } from '@/uni_modules/lgh-dialog'
+import { openDialog, allowFloat, getBatteryCapacity } from '@/uni_modules/lgh-dialog'
 // #endif
 
 const appStore = useAppStore()
@@ -24,11 +24,8 @@
 // testKeep()
 
 // 开启悬浮窗
-allowFloat()
+// allowFloat()
 // 假设五秒后来电
-setTimeout(() => {
-	openDialog()
-}, 5000)
 // #endif
 
 useGlobalWS()
@@ -57,9 +54,9 @@
 	// 初始化系统信息
 	appStore.initSystemInfo()
 	// #ifdef APP-PLUS
-	keepAliveStart()
+	// keepAliveStart()
 	// 请求用户允许忽略电池优化(重要:用于保活)
-	setTimeout(requestIgnoreBatteryOptimization, 2000)
+	// setTimeout(requestIgnoreBatteryOptimization, 2000)
 	// #endif
 	if (!userStore.userInfo) {
 		//不存在则跳转至登录页
@@ -106,4 +103,3 @@
 	background: transparent !important;
 }
 </style>
-
diff --git a/uniapps/work-app/src/manifest.json b/uniapps/work-app/src/manifest.json
index e47c752..7d1d57e 100644
--- a/uniapps/work-app/src/manifest.json
+++ b/uniapps/work-app/src/manifest.json
@@ -53,12 +53,14 @@
                     "<uses-feature android:name=\"android.hardware.camera\"/>",
                     "<uses-permission android:name=\"android.permission.WRITE_SETTINGS\"/>",
                     "<uses-permission android:name=\"android.permission.RECORD_AUDIO\" />",
+                    "<uses-permission android:name=\"android.permission.MODIFY_AUDIO_SETTINGS\" />",
                     "<uses-permission android:name=\"android.permission.ACCESS_FINE_LOCATION\" />",
                     "<uses-permission android:name=\"android.permission.FOREGROUND_SERVICE\" />",
                     "<uses-permission android:name=\"android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS\" />",
                     "<uses-permission android:name=\"android.permission.SYSTEM_ALERT_WINDOW\" />",
                     "<uses-permission android:name=\"android.permission.USE_FULL_SCREEN_INTENT\" />"
-                ]
+                ],
+                "minSdkVersion" : 21
             },
             /* ios打包配置 */
             "ios" : {
diff --git a/uniapps/work-app/src/subPackages/voiceCallDetail/index.vue b/uniapps/work-app/src/subPackages/voiceCallDetail/index.vue
index ffe9134..6798ebd 100644
--- a/uniapps/work-app/src/subPackages/voiceCallDetail/index.vue
+++ b/uniapps/work-app/src/subPackages/voiceCallDetail/index.vue
@@ -23,38 +23,41 @@
  */
 async function requestAndroidMicPermission() {
   try {
- // 获取麦克风权限
-// #ifdef APP-PLUS
-plus.android.requestPermissions(
-	['android.permission.RECORD_AUDIO'],
-	(result) => {
-		if (result.granted.length > 0) {
-			console.log('麦克风权限已获取')
-		}
-		if (result.deniedAlways.length > 0) {
-			uni.showModal({
-				title: '提示',
-				content: '需要麦克风权限才能正常使用语音功能,请前往系统设置开启',
-				success(res) {
-					if (res.confirm) {
-						plus.runtime.openURL('app-settings:')
-					}
-				},
-			})
-		}
-	},
-	(error) => {
-		console.error('请求麦克风权限失败:', error)
-	}
-)
-// #endif
-    
-   
+    // 获取麦克风权限
+    return await new Promise((resolve) => {
+      plus.android.requestPermissions(
+        ['android.permission.RECORD_AUDIO'],
+        (result) => {
+          if (result.granted.length > 0) {
+            console.log('麦克风权限已获取')
+            resolve(true)
+            return
+          }
+          if (result.deniedAlways.length > 0) {
+            uni.showModal({
+              title: '提示',
+              content: '需要麦克风权限才能正常使用语音功能,请前往系统设置开启',
+              success(res) {
+                if (res.confirm) {
+                  plus.runtime.openURL('app-settings:')
+                }
+              },
+            })
+          }
+          resolve(false)
+        },
+        (error) => {
+          console.error('请求麦克风权限失败', error)
+          resolve(false)
+        }
+      )
+    })
   } catch (error) {
-    console.error('❌ 请求麦克风权限失败:', error)
+    console.error('❌ 请求麦克风权限失败', error)
     return false
   }
 }
+
 // #endif
 
 onLoad(async (options) => {

--
Gitblit v1.9.3