// 摄像头拍照功能
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);
};