Просмотр исходного кода

加入摄像头后切换前后摄的按钮

liyanbo 3 месяцев назад
Родитель
Сommit
d575d0af7c
2 измененных файлов с 116 добавлено и 10 удалено
  1. 115 9
      src/components/ImageUpload/index.vue
  2. 1 1
      src/components/ai/text/TextToText.vue

+ 115 - 9
src/components/ImageUpload/index.vue

@@ -63,6 +63,13 @@
       <div class="camera-container">
       <div class="camera-container">
         <div v-if="!isCapturing" class="camera-view">
         <div v-if="!isCapturing" class="camera-view">
           <video ref="videoRef" autoplay muted playsinline></video>
           <video ref="videoRef" autoplay muted playsinline></video>
+          <el-button
+            class="switch-camera-btn"
+            @click="switchCamera"
+            type="text"
+            :icon="Camera"
+            circle
+          ></el-button>
         </div>
         </div>
         <div v-else class="captured-image-view">
         <div v-else class="captured-image-view">
           <img :src="capturedImage" alt="已拍摄照片" />
           <img :src="capturedImage" alt="已拍摄照片" />
@@ -112,6 +119,7 @@ const capturedImage = ref(''); // 拍摄的照片
 const isCapturing = ref(false); // 是否正在拍摄中
 const isCapturing = ref(false); // 是否正在拍摄中
 const videoRef = ref(null); // 视频元素引用
 const videoRef = ref(null); // 视频元素引用
 const isSmallScreen = ref(false); // 是否为小屏设备
 const isSmallScreen = ref(false); // 是否为小屏设备
+const currentCamera = ref('environment'); // 当前使用的摄像头方向 (environment: 后摄, user: 前摄)
 
 
 // 监听窗口大小变化
 // 监听窗口大小变化
 const handleResize = () => {
 const handleResize = () => {
@@ -233,8 +241,8 @@ const download = (index) => {
   document.body.removeChild(link);
   document.body.removeChild(link);
 };
 };
 
 
-// 打开相机弹窗
-const openCamera = async () => {
+// 获取相机视频流的辅助函数
+const getCameraStream = async (facingMode) => {
   try {
   try {
     // 获取相机容器的尺寸
     // 获取相机容器的尺寸
     const containerWidth = window.innerWidth * (isSmallScreen.value ? 0.9 : 0.8);
     const containerWidth = window.innerWidth * (isSmallScreen.value ? 0.9 : 0.8);
@@ -247,24 +255,45 @@ const openCamera = async () => {
       frameRate: { ideal: 30, max: 60 }
       frameRate: { ideal: 30, max: 60 }
     };
     };
     
     
-    // 尝试优先使用后摄像头(不带exact,提高兼容性)
+    // 尝试获取指定方向的摄像头
+    return await navigator.mediaDevices.getUserMedia({
+      video: { ...videoConstraints, facingMode }
+    });
+  } catch (error) {
+    console.error(`获取${facingMode === 'environment' ? '后' : '前'}摄像头失败:`, error);
+    throw error;
+  }
+};
+
+// 打开相机弹窗
+const openCamera = async () => {
+  try {
+    // 尝试获取后摄像头(默认)
     try {
     try {
-      stream.value = await navigator.mediaDevices.getUserMedia({
-        video: { ...videoConstraints, facingMode: 'environment' }
-      });
+      stream.value = await getCameraStream('environment');
+      currentCamera.value = 'environment';
     } catch (backCameraError) {
     } catch (backCameraError) {
       console.log('后摄像头不可用,尝试使用前摄像头:', backCameraError);
       console.log('后摄像头不可用,尝试使用前摄像头:', backCameraError);
       // 后摄像头不可用,尝试使用前摄像头
       // 后摄像头不可用,尝试使用前摄像头
       try {
       try {
-        stream.value = await navigator.mediaDevices.getUserMedia({
-          video: { ...videoConstraints, facingMode: 'user' }
-        });
+        stream.value = await getCameraStream('user');
+        currentCamera.value = 'user';
       } catch (frontCameraError) {
       } catch (frontCameraError) {
         console.log('前摄像头也不可用,尝试默认设置:', frontCameraError);
         console.log('前摄像头也不可用,尝试默认设置:', frontCameraError);
         // 前摄像头也不可用,使用默认设置
         // 前摄像头也不可用,使用默认设置
+        const containerWidth = window.innerWidth * (isSmallScreen.value ? 0.9 : 0.8);
+        const containerHeight = window.innerHeight * (isSmallScreen.value ? 0.7 : 0.6);
+        
+        const videoConstraints = {
+          width: { ideal: containerWidth, max: containerWidth * 1.2 },
+          height: { ideal: containerHeight, max: containerHeight * 1.2 },
+          frameRate: { ideal: 30, max: 60 }
+        };
+        
         stream.value = await navigator.mediaDevices.getUserMedia({ 
         stream.value = await navigator.mediaDevices.getUserMedia({ 
           video: videoConstraints 
           video: videoConstraints 
         });
         });
+        currentCamera.value = 'default';
       }
       }
     }
     }
 
 
@@ -284,6 +313,50 @@ const openCamera = async () => {
   }
   }
 };
 };
 
 
+// 切换摄像头
+const switchCamera = async () => {
+  try {
+    // 停止当前的媒体流
+    if (stream.value) {
+      stream.value.getTracks().forEach(track => track.stop());
+      stream.value = null;
+    }
+
+    // 切换摄像头方向
+    const newCamera = currentCamera.value === 'environment' ? 'user' : 'environment';
+    
+    try {
+      // 尝试获取新的摄像头流
+      stream.value = await getCameraStream(newCamera);
+      currentCamera.value = newCamera;
+      
+      // 更新视频源
+      if (videoRef.value) {
+        videoRef.value.srcObject = stream.value;
+      }
+    } catch (error) {
+      // 如果获取新摄像头失败,尝试切换回原来的摄像头
+      console.error(`切换到${newCamera === 'environment' ? '后' : '前'}摄像头失败:`, error);
+      Message().error(`无法切换到${newCamera === 'environment' ? '后' : '前'}摄像头,请检查设备是否支持!`, true);
+      
+      // 尝试恢复原来的摄像头
+      try {
+        stream.value = await getCameraStream(currentCamera.value);
+        if (videoRef.value) {
+          videoRef.value.srcObject = stream.value;
+        }
+      } catch (revertError) {
+        console.error('恢复原来摄像头失败:', revertError);
+        Message().error('无法恢复原来的摄像头!', true);
+        closeCamera();
+      }
+    }
+  } catch (error) {
+    console.error('切换摄像头失败:', error);
+    Message().error('切换摄像头失败,请重试!', true);
+  }
+};
+
 // 关闭相机弹窗
 // 关闭相机弹窗
 const closeCamera = () => {
 const closeCamera = () => {
   if (stream.value) {
   if (stream.value) {
@@ -514,6 +587,16 @@ const confirmAndUpload = async () => {
   align-items: center;
   align-items: center;
 }
 }
 
 
+.camera-view {
+  width: 100%;
+  height: 100%;
+  overflow: hidden;
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  position: relative;
+}
+
 .camera-view video {
 .camera-view video {
   max-width: 100%;
   max-width: 100%;
   max-height: 100%;
   max-height: 100%;
@@ -522,6 +605,29 @@ const confirmAndUpload = async () => {
   border-radius: 5px;
   border-radius: 5px;
 }
 }
 
 
+.switch-camera-btn {
+  position: absolute;
+  bottom: 20px;
+  right: 20px;
+  background-color: rgba(0, 0, 0, 0.5);
+  color: white;
+  font-size: 20px;
+  width: 40px;
+  height: 40px;
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  border-radius: 50%;
+  cursor: pointer;
+  z-index: 10;
+  transition: background-color 0.3s;
+}
+
+.switch-camera-btn:hover {
+  background-color: rgba(0, 0, 0, 0.7);
+  color: white;
+}
+
 .captured-image-view {
 .captured-image-view {
   width: 100%;
   width: 100%;
   height: 100%;
   height: 100%;

+ 1 - 1
src/components/ai/text/TextToText.vue

@@ -435,7 +435,7 @@ const doSendMessageStream = async (userMessage) => {
       async (res) => {
       async (res) => {
         const { code, data, msg } = JSON.parse(res.data);
         const { code, data, msg } = JSON.parse(res.data);
         if (code !== 0) {
         if (code !== 0) {
-          console.log(`对话异常! ${msg}`);
+          console.error(`对话异常! ${msg}`);
           return;
           return;
         }
         }