// 摄像头拍照功能 window.takePhoto = function(fieldName) { // 检查权限 if (!window.hasUploadPermission) { showMessage('请先登录后再使用拍照功能', 'error'); return; } // 创建拍照模态框 const cameraModal = document.createElement('div'); cameraModal.className = 'fixed inset-0 bg-black bg-opacity-75 flex items-center justify-center z-50'; cameraModal.innerHTML = `

摄像头拍照

高拍仪用户提示:
• 文档摄像头:适合拍摄身份证、证件等文档
• 人像摄像头:适合拍摄头像照片
`; let stream = null; let capturedBlob = null; let availableCameras = []; let currentCameraIndex = 0; // 获取页面元素 const cameraSelection = cameraModal.querySelector('#cameraSelection'); const cameraPreview = cameraModal.querySelector('#cameraPreview'); const photoPreview = cameraModal.querySelector('#photoPreview'); const cameraSelect = cameraModal.querySelector('#cameraSelect'); const detectBtn = cameraModal.querySelector('#detectCameras'); const startSelectedBtn = cameraModal.querySelector('#startSelectedCamera'); const switchBtn = cameraModal.querySelector('#switchCamera'); const backBtn = cameraModal.querySelector('#backToSelection'); const captureBtn = cameraModal.querySelector('#capturePhoto'); const video = cameraModal.querySelector('#cameraVideo'); const canvas = cameraModal.querySelector('#cameraCanvas'); const cameraInfo = cameraModal.querySelector('#cameraInfo'); const previewImg = cameraModal.querySelector('#previewImage'); const retakeBtn = cameraModal.querySelector('#retakePhoto'); const uploadBtn = cameraModal.querySelector('#uploadCapturedPhoto'); // 检测可用摄像头 async function detectCameras() { try { // 先请求摄像头权限,这样才能获取设备标签 const tempStream = await navigator.mediaDevices.getUserMedia({ video: true }); tempStream.getTracks().forEach(track => track.stop()); // 立即停止临时流 // 获取所有媒体设备 const devices = await navigator.mediaDevices.enumerateDevices(); availableCameras = devices.filter(device => device.kind === 'videoinput'); console.log('检测到的摄像头:', availableCameras); // 更新选择列表 cameraSelect.innerHTML = ''; if (availableCameras.length === 0) { cameraSelect.innerHTML = ''; showMessage('未检测到可用的摄像头设备', 'error'); return; } availableCameras.forEach((camera, index) => { const option = document.createElement('option'); option.value = camera.deviceId; // 构建摄像头名称,特别识别高拍仪的摄像头类型 let cameraName = camera.label || `摄像头 ${index + 1}`; let cameraType = ''; // 高拍仪摄像头识别 if (camera.label) { const lowerLabel = camera.label.toLowerCase(); if (lowerLabel.includes('document') || lowerLabel.includes('doc') || lowerLabel.includes('overhead') || lowerLabel.includes('scanning')) { cameraType = ' 📄 (文档摄像头)'; } else if (lowerLabel.includes('face') || lowerLabel.includes('front') || lowerLabel.includes('portrait') || lowerLabel.includes('people')) { cameraType = ' 👤 (人像摄像头)'; } else if (lowerLabel.includes('usb')) { // 如果是USB摄像头但没有特殊标识,根据索引推测 cameraType = index === 0 ? ' 📄 (可能是文档)' : ' 👤 (可能是人像)'; } } option.textContent = cameraName + cameraType; cameraSelect.appendChild(option); }); // 智能选择默认摄像头 let defaultIndex = 0; // 如果是拍摄头像,优先选择人像摄像头 if (fieldName === 'thumb') { const faceCamera = availableCameras.findIndex(cam => cam.label && (cam.label.toLowerCase().includes('face') || cam.label.toLowerCase().includes('front') || cam.label.toLowerCase().includes('portrait')) ); if (faceCamera !== -1) defaultIndex = faceCamera; } else { // 其他情况优先选择文档摄像头 const docCamera = availableCameras.findIndex(cam => cam.label && (cam.label.toLowerCase().includes('document') || cam.label.toLowerCase().includes('doc') || cam.label.toLowerCase().includes('overhead')) ); if (docCamera !== -1) defaultIndex = docCamera; } cameraSelect.selectedIndex = defaultIndex; startSelectedBtn.disabled = false; const message = availableCameras.length === 1 ? '检测到 1 个摄像头设备' : `检测到 ${availableCameras.length} 个摄像头设备${availableCameras.length > 1 ? ',可以切换使用' : ''}`; showMessage(message, 'success'); } catch (error) { console.error('检测摄像头失败:', error); showMessage('检测摄像头失败,请检查设备权限', 'error'); } } // 启动选中的摄像头 async function startSelectedCamera() { const selectedDeviceId = cameraSelect.value; if (!selectedDeviceId) { showMessage('请先选择一个摄像头设备', 'error'); return; } try { // 停止当前流 if (stream) { stream.getTracks().forEach(track => track.stop()); } // 启动选中的摄像头 stream = await navigator.mediaDevices.getUserMedia({ video: { deviceId: { exact: selectedDeviceId }, width: { ideal: 640 }, height: { ideal: 480 } } }); video.srcObject = stream; // 更新当前摄像头信息 const selectedCamera = availableCameras.find(cam => cam.deviceId === selectedDeviceId); const cameraLabel = selectedCamera.label || '摄像头'; cameraInfo.textContent = cameraLabel; currentCameraIndex = availableCameras.findIndex(cam => cam.deviceId === selectedDeviceId); // 切换界面 cameraSelection.classList.add('hidden'); cameraPreview.classList.remove('hidden'); showMessage('摄像头启动成功', 'success'); } catch (error) { console.error('启动摄像头失败:', error); showMessage('启动摄像头失败: ' + error.message, 'error'); } } // 切换到下一个摄像头 async function switchToNextCamera() { if (availableCameras.length <= 1) { showMessage('只有一个摄像头设备', 'info'); return; } currentCameraIndex = (currentCameraIndex + 1) % availableCameras.length; const nextCamera = availableCameras[currentCameraIndex]; try { // 停止当前流 if (stream) { stream.getTracks().forEach(track => track.stop()); } // 启动下一个摄像头 stream = await navigator.mediaDevices.getUserMedia({ video: { deviceId: { exact: nextCamera.deviceId }, width: { ideal: 640 }, height: { ideal: 480 } } }); video.srcObject = stream; cameraInfo.textContent = nextCamera.label || `摄像头 ${currentCameraIndex + 1}`; const cameraType = nextCamera.label && nextCamera.label.toLowerCase().includes('document') ? '文档摄像头' : '人像摄像头'; showMessage(`已切换到: ${nextCamera.label || '摄像头'} (${cameraType})`, 'success'); } catch (error) { console.error('切换摄像头失败:', error); showMessage('切换摄像头失败: ' + error.message, 'error'); } } // 返回摄像头选择 function backToSelection() { if (stream) { stream.getTracks().forEach(track => track.stop()); stream = null; } cameraPreview.classList.add('hidden'); photoPreview.classList.add('hidden'); cameraSelection.classList.remove('hidden'); } // 事件绑定 detectBtn.onclick = detectCameras; startSelectedBtn.onclick = startSelectedCamera; switchBtn.onclick = switchToNextCamera; backBtn.onclick = backToSelection; // 拍照 captureBtn.onclick = function() { const context = canvas.getContext('2d'); canvas.width = video.videoWidth; canvas.height = video.videoHeight; context.drawImage(video, 0, 0); canvas.toBlob(function(blob) { capturedBlob = blob; const url = URL.createObjectURL(blob); previewImg.src = url; photoPreview.classList.remove('hidden'); cameraPreview.classList.add('hidden'); }, 'image/jpeg', 0.8); }; // 重拍 retakeBtn.onclick = function() { photoPreview.classList.add('hidden'); cameraPreview.classList.remove('hidden'); if (capturedBlob) { URL.revokeObjectURL(previewImg.src); capturedBlob = null; } }; // 自动检测摄像头 detectCameras(); // 上传拍摄的照片 uploadBtn.onclick = function() { if (!capturedBlob) { showMessage('没有拍摄的照片', 'error'); return; } // 获取字段配置信息 const categoryInfo = fieldMapping[fieldName]; if (!categoryInfo) { showMessage('未找到字段配置信息: ' + fieldName, 'error'); return; } const formData = new FormData(); const fileName = `camera_photo_${Date.now()}.jpg`; formData.append('file_data', capturedBlob, fileName); // 显示上传进度 showUploadProgress('正在上传拍摄的照片...'); // 获取正确的上传URL const uploadUrl = getUploadUrl(categoryInfo.fid); fetch(uploadUrl, { method: 'POST', body: formData }) .then(response => { if (!response.ok) { throw new Error(`HTTP错误! 状态: ${response.status}`); } return response.text(); }) .then(text => { try { const data = JSON.parse(text); if (data.code === 1) { // 文件上传成功,现在需要保存到字段中 // 根据字段类型决定保存格式 if (categoryInfo.field === 'thumb') { // 单文件字段(头像):直接保存文件ID const newValue = String(data.id); // 直接更新字段值 updateFieldValue('{$id}', categoryInfo.field, newValue) .then(updateResult => { hideUploadProgress(); if (updateResult && updateResult.success) { showMessage('照片拍摄并保存成功!', 'success'); setTimeout(() => { window.location.reload(); }, 1000); } else { showMessage(updateResult ? updateResult.msg || '保存失败' : '保存失败', 'error'); } }) .catch(error => { hideUploadProgress(); showMessage('保存头像时出现错误: ' + error.message, 'error'); }); } else { // 多文件字段:获取当前字段值并更新 getCurrentFieldValue('{$id}', categoryInfo.field) .then(currentResult => { if (!currentResult.success) { hideUploadProgress(); showMessage(currentResult.error || '获取字段值失败', 'error'); return; } // 构建新的值 let newValue; if (currentResult.data && Array.isArray(currentResult.data)) { newValue = currentResult.data.filter(item => item !== null && item !== undefined && item !== '') .map(item => String(item)); newValue.push(String(data.id)); } else { newValue = [String(data.id)]; } return updateFieldValue('{$id}', categoryInfo.field, newValue); }) .then(updateResult => { hideUploadProgress(); if (updateResult && updateResult.success) { showMessage('照片拍摄并保存成功!', 'success'); setTimeout(() => { window.location.reload(); }, 1000); } else { showMessage(updateResult ? updateResult.msg || '保存失败' : '保存失败', 'error'); } }) .catch(error => { hideUploadProgress(); showMessage('保存照片信息时出现错误: ' + error.message, 'error'); }); } } else { hideUploadProgress(); showMessage(data.msg || '上传失败: ' + text, 'error'); } } catch (e) { hideUploadProgress(); showMessage('服务器响应格式错误: ' + text.substring(0, 200), 'error'); } }) .catch(error => { hideUploadProgress(); showMessage('上传过程中出现错误: ' + error.message, 'error'); }); closeModal(); }; // 关闭模态框 function closeModal() { if (stream) { stream.getTracks().forEach(track => track.stop()); stream = null; } if (capturedBlob) { URL.revokeObjectURL(previewImg.src); capturedBlob = null; } cameraModal.remove(); } cameraModal.querySelector('#closeCameraModal').onclick = closeModal; // 点击背景关闭 cameraModal.onclick = function(e) { if (e.target === cameraModal) { closeModal(); } }; document.body.appendChild(cameraModal); };