|
|
|
|
<template>
|
|
|
|
|
<view>
|
|
|
|
|
<!-- 文件上传输入框,接受 Excel 文件 -->
|
|
|
|
|
<input type="file" accept=".xlsx, .xls" @change="handleFileUpload" class="file-input" />
|
|
|
|
|
<!-- 上传按钮 -->
|
|
|
|
|
<button @click="upload" class="button">上传名单</button>
|
|
|
|
|
|
|
|
|
|
<view>
|
|
|
|
|
<button @click="randomCall" class="button">随机点名</button>
|
|
|
|
|
</view>
|
|
|
|
|
|
|
|
|
|
<view class="list">
|
|
|
|
|
<view class="list-header">
|
|
|
|
|
<text class="list-left">签到学生名单</text>
|
|
|
|
|
<text class="list-mid">状态</text>
|
|
|
|
|
<text class="list-right">积分</text>
|
|
|
|
|
</view>
|
|
|
|
|
<view v-for="(student, index) in selectedStudents" :key="index" class="list-item">
|
|
|
|
|
<text class="item-left">{{ student.name }}</text>
|
|
|
|
|
<text class="item-mid">{{ student.signed ? '✔' : '' }}</text>
|
|
|
|
|
<text class="item-right">{{ student.points }}</text>
|
|
|
|
|
</view>
|
|
|
|
|
</view>
|
|
|
|
|
</view>
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
<script setup>
|
|
|
|
|
import { ref } from 'vue';
|
|
|
|
|
|
|
|
|
|
// 存储上传的学生数据和选中的学生
|
|
|
|
|
const rcallData = ref([]);
|
|
|
|
|
const selectedStudents = ref([]);
|
|
|
|
|
const file = ref(null);
|
|
|
|
|
|
|
|
|
|
// 处理文件上传
|
|
|
|
|
const handleFileUpload = (event) => {
|
|
|
|
|
file.value = event.target.files[0]; // 获取上传的文件
|
|
|
|
|
console.log("Selected file:", file.value); // 确认文件是否正确获取
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// 上传文件到服务器
|
|
|
|
|
const upload = async () => {
|
|
|
|
|
if (!file.value) {
|
|
|
|
|
console.warn("请选择一个文件进行上传");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const formData = new FormData();
|
|
|
|
|
formData.append('file', file.value);
|
|
|
|
|
console.log("FormData:", formData.get('file')); // 检查 FormData 中的文件
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
const response = await uni.request({
|
|
|
|
|
url: 'http://10.198.140.41:3000/api/upload',
|
|
|
|
|
method: 'POST',
|
|
|
|
|
header: {
|
|
|
|
|
'Content-Type': 'multipart/form-data'
|
|
|
|
|
},
|
|
|
|
|
data: formData
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
if (response.statusCode === 200) {
|
|
|
|
|
console.log('文件上传成功:', response.data);
|
|
|
|
|
rcallData.value = response.data; // 服务器返回学生数据
|
|
|
|
|
} else {
|
|
|
|
|
console.error('文件上传失败:', response.data);
|
|
|
|
|
}
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error('上传过程中发生错误:', error);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// 随机点名的函数
|
|
|
|
|
const randomCall = async () => {
|
|
|
|
|
if (rcallData.value.length === 0) {
|
|
|
|
|
console.warn("没有学生数据可供点名");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const studentCount = Math.floor(Math.random() * (30 - 20 + 1)) + 20;
|
|
|
|
|
const shuffledStudents = rcallData.value.sort(() => 0.5 - Math.random());
|
|
|
|
|
const selected = shuffledStudents.slice(0, Math.min(studentCount, rcallData.value.length));
|
|
|
|
|
|
|
|
|
|
selected.forEach(student => {
|
|
|
|
|
student.signed = true;
|
|
|
|
|
student.points += 1;
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
selectedStudents.value = selected;
|
|
|
|
|
|
|
|
|
|
await Promise.all(selected.map(student => sendAttendance(student)));
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const sendAttendance = async (student) => {
|
|
|
|
|
try {
|
|
|
|
|
const response = await uni.request({
|
|
|
|
|
url: "http://10.198.140.41:3000/api/upload",
|
|
|
|
|
method: 'PUT',
|
|
|
|
|
data: {
|
|
|
|
|
name: student.name,
|
|
|
|
|
points: student.points
|
|
|
|
|
},
|
|
|
|
|
header: {
|
|
|
|
|
'Content-Type': 'application/json'
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
if (response.statusCode === 200) {
|
|
|
|
|
console.log('Attendance updated successfully:', response.data);
|
|
|
|
|
} else {
|
|
|
|
|
console.error('Failed to update attendance:', response.data);
|
|
|
|
|
}
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error('Error occurred while updating attendance:', error);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
<style scoped>
|
|
|
|
|
.button {
|
|
|
|
|
display: block;
|
|
|
|
|
width: 80%;
|
|
|
|
|
height: 60px;
|
|
|
|
|
margin: 10px auto;
|
|
|
|
|
padding: 10px;
|
|
|
|
|
background-color: #1E90FF;
|
|
|
|
|
color: white;
|
|
|
|
|
text-align: center;
|
|
|
|
|
border-radius: 5px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.list {
|
|
|
|
|
margin: 20px;
|
|
|
|
|
background-color: #f9f9f9;
|
|
|
|
|
border-bottom: 1px solid #ccc;
|
|
|
|
|
padding: 10px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.list-header {
|
|
|
|
|
display: flex;
|
|
|
|
|
justify-content: space-between;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.list-left {
|
|
|
|
|
flex-grow: 2;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.list-mid {
|
|
|
|
|
text-align: center;
|
|
|
|
|
flex-grow: 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.list-right {
|
|
|
|
|
padding-left: 15px;
|
|
|
|
|
padding-right: 8px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.list-item {
|
|
|
|
|
display: flex;
|
|
|
|
|
justify-content: space-between;
|
|
|
|
|
padding: 5px 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.item-left {
|
|
|
|
|
font-size: 32rpx;
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
text-overflow: ellipsis;
|
|
|
|
|
white-space: nowrap;
|
|
|
|
|
text-align: left;
|
|
|
|
|
flex: 6;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.item-mid {
|
|
|
|
|
text-align: center;
|
|
|
|
|
padding-left: 60px;
|
|
|
|
|
padding-right: 45px;
|
|
|
|
|
flex: 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.item-right {
|
|
|
|
|
text-align: center;
|
|
|
|
|
padding-right: 10px;
|
|
|
|
|
flex: 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.button-container {
|
|
|
|
|
display: flex;
|
|
|
|
|
justify-content: center;
|
|
|
|
|
}
|
|
|
|
|
</style>
|