This commit is contained in:
Harry
2026-04-25 10:09:16 +08:00
committed by GitHub
commit bf215c4b9f
15 changed files with 2482 additions and 0 deletions

228
index.js Normal file
View File

@@ -0,0 +1,228 @@
// 用户点名JavaScript - 支持服务器和本地双模式
// 状态管理
let currentList = null;
let stream = null;
// DOM元素
const inputSection = document.getElementById('inputSection');
const rollCallSection = document.getElementById('rollCallSection');
const enterForm = document.getElementById('enterForm');
const codeInput = document.getElementById('codeInput');
const scanQRBtn = document.getElementById('scanQRBtn');
const completedCount = document.getElementById('completedCount');
const totalCount = document.getElementById('totalCount');
const progressFill = document.getElementById('progressFill');
const memberListDisplay = document.getElementById('memberListDisplay');
const video = document.getElementById('video');
const canvas = document.getElementById('canvas');
const startCameraBtn = document.getElementById('startCameraBtn');
const captureBtn = document.getElementById('captureBtn');
const stopCameraBtn = document.getElementById('stopCameraBtn');
const capturedPhoto = document.getElementById('capturedPhoto');
const photoPreview = document.getElementById('photoPreview');
const confirmPhotoBtn = document.getElementById('confirmPhotoBtn');
const retakeBtn = document.getElementById('retakeBtn');
const manualSelect = document.getElementById('manualSelect');
const manualRollCallBtn = document.getElementById('manualRollCallBtn');
// 初始化
document.addEventListener('DOMContentLoaded', () => {
// 显示运行模式
const modeText = dataStorage.isServerMode ? '服务器模式' : '本地模式';
const modeIndicator = document.getElementById('modeIndicator');
if (modeIndicator) {
modeIndicator.textContent = `当前运行模式: ${modeText}`;
if (!dataStorage.isServerMode) {
modeIndicator.textContent += ' (数据保存在浏览器本地)';
}
}
console.log(`点名系统运行在: ${modeText}`);
// 检查URL参数
const urlParams = new URLSearchParams(window.location.search);
const codeFromUrl = urlParams.get('code');
if (codeFromUrl) {
codeInput.value = codeFromUrl;
enterRollCall(codeFromUrl);
}
});
// 进入点名
enterForm.addEventListener('submit', (e) => {
e.preventDefault();
const code = codeInput.value.trim();
enterRollCall(code);
});
async function enterRollCall(code) {
try {
currentList = await dataStorage.getList(code);
inputSection.style.display = 'none';
rollCallSection.style.display = 'block';
updateProgress();
renderMemberList();
populateManualSelect();
} catch (error) {
alert('进入点名失败:' + error.message);
}
}
// 扫描二维码(简化版)
scanQRBtn.addEventListener('click', () => {
alert('扫码功能需要集成摄像头扫码库,当前版本请手动输入编号');
});
// 更新进度
function updateProgress() {
if (!currentList) return;
const completed = currentList.members.filter(m => m.completed).length;
const total = currentList.members.length;
const percentage = total > 0 ? (completed / total * 100) : 0;
completedCount.textContent = completed;
totalCount.textContent = total;
progressFill.style.width = percentage + '%';
progressFill.textContent = Math.round(percentage) + '%';
}
// 渲染成员列表
function renderMemberList() {
if (!currentList) return;
memberListDisplay.innerHTML = currentList.members.map((member, index) => `
<div class="member-item ${member.completed ? 'completed' : ''}">
<span class="member-name">${member.name}</span>
<span class="member-status ${member.completed ? 'status-completed' : 'status-pending'}">
${member.completed ? '已点名' : '未点名'}
</span>
</div>
`).join('');
}
// 填充手动选择下拉框
function populateManualSelect() {
if (!currentList) return;
const uncompletedMembers = currentList.members.filter(m => !m.completed);
manualSelect.innerHTML = '<option value="">请选择姓名</option>' +
uncompletedMembers.map(member => `<option value="${member.name}">${member.name}</option>`).join('');
}
// 开启摄像头
startCameraBtn.addEventListener('click', async () => {
try {
stream = await navigator.mediaDevices.getUserMedia({ video: true });
video.srcObject = stream;
video.style.display = 'block';
startCameraBtn.style.display = 'none';
captureBtn.style.display = 'inline-block';
stopCameraBtn.style.display = 'inline-block';
} catch (err) {
alert('无法访问摄像头,请检查权限设置');
console.error(err);
}
});
// 拍照
captureBtn.addEventListener('click', () => {
const context = canvas.getContext('2d');
context.drawImage(video, 0, 0, canvas.width, canvas.height);
const dataURL = canvas.toDataURL('image/jpeg');
photoPreview.src = dataURL;
capturedPhoto.style.display = 'block';
video.style.display = 'none';
captureBtn.style.display = 'none';
});
// 关闭摄像头
stopCameraBtn.addEventListener('click', () => {
if (stream) {
stream.getTracks().forEach(track => track.stop());
stream = null;
}
video.style.display = 'block';
capturedPhoto.style.display = 'none';
startCameraBtn.style.display = 'inline-block';
captureBtn.style.display = 'none';
stopCameraBtn.style.display = 'none';
});
// 确认照片
confirmPhotoBtn.addEventListener('click', () => {
// 模拟人脸识别
const recognizedName = simulateFaceRecognition();
if (recognizedName) {
markAsCompleted(recognizedName);
alert(`识别成功:${recognizedName}`);
} else {
alert('未能识别,请手动选择姓名');
}
// 重置拍照区域
capturedPhoto.style.display = 'none';
video.style.display = 'block';
captureBtn.style.display = 'inline-block';
});
// 重新拍摄
retakeBtn.addEventListener('click', () => {
capturedPhoto.style.display = 'none';
video.style.display = 'block';
captureBtn.style.display = 'inline-block';
});
// 模拟人脸识别
function simulateFaceRecognition() {
// 随机返回一个未点名的成员(模拟识别)
const uncompletedMembers = currentList.members.filter(m => !m.completed);
if (uncompletedMembers.length === 0) return null;
const randomIndex = Math.floor(Math.random() * uncompletedMembers.length);
return uncompletedMembers[randomIndex].name;
}
// 手动点名
manualRollCallBtn.addEventListener('click', () => {
const selectedName = manualSelect.value;
if (!selectedName) {
alert('请选择姓名');
return;
}
markAsCompleted(selectedName);
});
// 标记为已完成
async function markAsCompleted(name) {
try {
await dataStorage.rollCall(currentList.code, name);
// 更新本地数据
const member = currentList.members.find(m => m.name === name);
if (member) {
member.completed = true;
member.timestamp = new Date().toLocaleString();
}
// 更新UI
updateProgress();
renderMemberList();
populateManualSelect();
// 检查是否全部完成
const allCompleted = currentList.members.every(m => m.completed);
if (allCompleted) {
alert('点名完成!所有成员已点名');
}
} catch (error) {
alert('点名失败:' + error.message);
}
}