|
|
|
<template>
|
|
|
|
<el-row :gutter="20">
|
|
|
|
<el-col :span="6">
|
|
|
|
<el-card>
|
|
|
|
<template #header>
|
|
|
|
<div>
|
|
|
|
<span>考生信息</span>
|
|
|
|
</div>
|
|
|
|
</template>
|
|
|
|
<p v-for="(userItem, userIndex) in userInfo" :key="userIndex">
|
|
|
|
<template v-if="userItem.type === 'datetimerange'">
|
|
|
|
<div>
|
|
|
|
<span>{{ userItem.lable }}:</span>
|
|
|
|
<span>{{ _.join(userData[userItem.prop], "——") }}</span>
|
|
|
|
</div>
|
|
|
|
</template>
|
|
|
|
<template v-else-if="userItem.type === 'text'">
|
|
|
|
<div>
|
|
|
|
<span>{{ userItem.lable }}:</span>
|
|
|
|
<span>{{ userData[userItem.prop] }}</span>
|
|
|
|
</div>
|
|
|
|
</template>
|
|
|
|
</p>
|
|
|
|
<template #footer>
|
|
|
|
<el-button type="primary" @click="connectTeacher">联系老师</el-button>
|
|
|
|
|
|
|
|
</template>
|
|
|
|
</el-card>
|
|
|
|
</el-col>
|
|
|
|
<el-col :span="6">
|
|
|
|
<el-card>
|
|
|
|
<template #header>
|
|
|
|
<div>
|
|
|
|
<span>实时作弊信息</span>
|
|
|
|
</div>
|
|
|
|
</template>
|
|
|
|
<p>
|
|
|
|
<span>
|
|
|
|
实时作弊信息
|
|
|
|
</span>
|
|
|
|
<el-button type="primary" @click="startExam">开始考试</el-button>
|
|
|
|
</p>
|
|
|
|
</el-card>
|
|
|
|
</el-col>
|
|
|
|
<el-col :span="12">
|
|
|
|
<video ref="videoEL" class="canvasClass" playsinline></video>
|
|
|
|
</el-col>
|
|
|
|
</el-row>
|
|
|
|
</template>
|
|
|
|
<script>
|
|
|
|
import _ from "lodash";
|
|
|
|
import {
|
|
|
|
getUser, //获取user信息
|
|
|
|
} from "@/api/student";
|
|
|
|
import { ElMessage, ElMessageBox } from 'element-plus'
|
|
|
|
export default {
|
|
|
|
name: "student",
|
|
|
|
components: {},
|
|
|
|
data() {
|
|
|
|
return {
|
|
|
|
_: _,
|
|
|
|
userId: "",
|
|
|
|
// 学生姓名、学号,考试类型、考试科目、考试时间段
|
|
|
|
userInfo: [
|
|
|
|
{
|
|
|
|
lable: "学校名称",
|
|
|
|
prop: "xuexiaomingcheng",
|
|
|
|
type: "text"
|
|
|
|
},
|
|
|
|
{
|
|
|
|
lable: "学校代号",
|
|
|
|
prop: "xuexiaodaihao",
|
|
|
|
type: "text"
|
|
|
|
},
|
|
|
|
{
|
|
|
|
lable: "专业名称",
|
|
|
|
prop: "zhuanyemingcheng",
|
|
|
|
type: "text"
|
|
|
|
},
|
|
|
|
{
|
|
|
|
lable: "专业代号",
|
|
|
|
prop: "zhuanyedaihao",
|
|
|
|
type: "text"
|
|
|
|
},
|
|
|
|
{
|
|
|
|
lable: "年级",
|
|
|
|
prop: "nianji",
|
|
|
|
type: "text"
|
|
|
|
},
|
|
|
|
{
|
|
|
|
lable: "班级",
|
|
|
|
prop: "banji",
|
|
|
|
type: "text"
|
|
|
|
},
|
|
|
|
{
|
|
|
|
lable: "学生姓名",
|
|
|
|
prop: "xueshengxingming",
|
|
|
|
type: "text"
|
|
|
|
},
|
|
|
|
{
|
|
|
|
lable: "学号",
|
|
|
|
prop: "xuehao",
|
|
|
|
type: "text"
|
|
|
|
},
|
|
|
|
{
|
|
|
|
lable: "考试类型",
|
|
|
|
prop: "kaoshileixing",
|
|
|
|
type: "text"
|
|
|
|
},
|
|
|
|
{
|
|
|
|
lable: "考试科目",
|
|
|
|
prop: "kaoshikemu",
|
|
|
|
type: "text"
|
|
|
|
},
|
|
|
|
{
|
|
|
|
lable: "考试时间段",
|
|
|
|
prop: "kaoshishijianduan",
|
|
|
|
type: "datetimerange"
|
|
|
|
},
|
|
|
|
],
|
|
|
|
userData: {},
|
|
|
|
ws: null,
|
|
|
|
videoStream: null,
|
|
|
|
};
|
|
|
|
},
|
|
|
|
watch: {},
|
|
|
|
computed: {},
|
|
|
|
async mounted() {
|
|
|
|
this.userId = _.get(this.$route, ["params", "id"], "")
|
|
|
|
await this.getUser()
|
|
|
|
await this.initWebSocket();
|
|
|
|
},
|
|
|
|
methods: {
|
|
|
|
async getUser() {
|
|
|
|
let res = await getUser({
|
|
|
|
id: this.userId
|
|
|
|
})
|
|
|
|
this.userData = res.list[0]
|
|
|
|
},
|
|
|
|
async initWebSocket() {
|
|
|
|
this.ws = new WebSocket('ws://127.0.0.1:5000/ws/video');
|
|
|
|
this.ws.binaryType = 'arraybuffer';
|
|
|
|
this.ws.onopen = () => {
|
|
|
|
console.log(1, 'WebSocket connected');
|
|
|
|
};
|
|
|
|
this.ws.onerror = (err) => {
|
|
|
|
console.error(2, 'WebSocket error:', err);
|
|
|
|
};
|
|
|
|
this.ws.onclose = () => {
|
|
|
|
console.log(3, 'WebSocket disconnected');
|
|
|
|
};
|
|
|
|
},
|
|
|
|
async connectTeacher() {
|
|
|
|
console.log(78744, this.socketMsg)
|
|
|
|
this.socketMsg.emit('sendMsg', { sid: this.socketMsg.id, id: this.userId, msg: "我有问题" });
|
|
|
|
},
|
|
|
|
async startExam() {
|
|
|
|
try {
|
|
|
|
let device = {}
|
|
|
|
let devices = await navigator.mediaDevices.enumerateDevices()
|
|
|
|
for (let key in devices) {
|
|
|
|
if (devices[key].kind === 'videoinput') {
|
|
|
|
device = devices[key]
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
let stream = await navigator.mediaDevices.getUserMedia({
|
|
|
|
audio: false,
|
|
|
|
video: {
|
|
|
|
sourceId: device.deviceId, // 把对应的 摄像头ID 放到这里
|
|
|
|
width: 600,
|
|
|
|
height: 600,
|
|
|
|
}
|
|
|
|
})
|
|
|
|
// 摄像头开启成功
|
|
|
|
this.$refs['videoEL'].srcObject = stream
|
|
|
|
this.$refs['videoEL'].play()
|
|
|
|
this.sendVideoFrames();
|
|
|
|
} catch (error) {
|
|
|
|
ElMessage.error(`摄像头开启失败,请检查摄像头是否可用!${error}`)
|
|
|
|
}
|
|
|
|
},
|
|
|
|
sendVideoFrames() {
|
|
|
|
let that = this
|
|
|
|
let video = this.$refs['videoEL'];
|
|
|
|
let canvas = document.createElement('canvas');
|
|
|
|
let ctx = canvas.getContext('2d');
|
|
|
|
canvas.width = video.videoWidth;
|
|
|
|
canvas.height = video.videoHeight;
|
|
|
|
let sendFrame = () => {
|
|
|
|
ctx.drawImage(video, 0, 0, canvas.width, canvas.height);
|
|
|
|
canvas.toBlob((blob) => {
|
|
|
|
let arrayBufferView = new Uint8Array(blob);
|
|
|
|
that.ws.send(arrayBufferView.buffer);
|
|
|
|
}, 'image/jpeg');
|
|
|
|
};
|
|
|
|
setInterval(sendFrame, 1000 / 30); // 30 FPS
|
|
|
|
},
|
|
|
|
stopVideoStream() {
|
|
|
|
if (this.videoStream) {
|
|
|
|
this.videoStream.getTracks().forEach(track => track.stop());
|
|
|
|
}
|
|
|
|
},
|
|
|
|
closeWebSocket() {
|
|
|
|
if (this.ws && this.ws.readyState === WebSocket.OPEN) {
|
|
|
|
this.ws.close();
|
|
|
|
}
|
|
|
|
},
|
|
|
|
},
|
|
|
|
beforeUnmount() {
|
|
|
|
this.stopVideoStream();
|
|
|
|
this.closeWebSocket();
|
|
|
|
},
|
|
|
|
};
|
|
|
|
</script>
|
|
|
|
<style scoped>
|
|
|
|
.canvasClass {
|
|
|
|
position: relative;
|
|
|
|
width: 600px;
|
|
|
|
height: 600px;
|
|
|
|
}
|
|
|
|
</style>
|