|
|
|
<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'
|
|
|
|
import io from 'socket.io-client';
|
|
|
|
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: {},
|
|
|
|
socket: 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.socket = io('http://localhost:5000/ws/video', { query: { id: this.userId, isAdmin: "0" } }); // 替换为你的后端服务器地址
|
|
|
|
this.socket.on('connect', () => {
|
|
|
|
console.log('Connected to server');
|
|
|
|
});
|
|
|
|
this.socket.on('message', (data) => {
|
|
|
|
console.log(78454, data)
|
|
|
|
});
|
|
|
|
},
|
|
|
|
async connectTeacher() {
|
|
|
|
console.log(78744, this.socketMsg)
|
|
|
|
this.socket.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.offsetWidth;
|
|
|
|
canvas.height = video.offsetHeight;
|
|
|
|
let sendFrame = () => {
|
|
|
|
ctx.drawImage(video, 0, 0, canvas.width, canvas.height);
|
|
|
|
canvas.toBlob((blob) => {
|
|
|
|
let reader = new FileReader();
|
|
|
|
reader.onloadend = () => {
|
|
|
|
let arrayBuffer = reader.result;
|
|
|
|
that.socket.emit('video', arrayBuffer,);
|
|
|
|
};
|
|
|
|
reader.readAsArrayBuffer(blob);
|
|
|
|
// let arrayBufferView = new Uint8Array(blob);
|
|
|
|
// console.log(89444, video)
|
|
|
|
// that.socket.emit('video', arrayBufferView.buffer);
|
|
|
|
}, 'image/png');
|
|
|
|
};
|
|
|
|
setInterval(sendFrame, 1000 / 30); // 30 FPS
|
|
|
|
},
|
|
|
|
stopVideoStream() {
|
|
|
|
if (this.videoStream) {
|
|
|
|
this.videoStream.getTracks().forEach(track => track.stop());
|
|
|
|
}
|
|
|
|
},
|
|
|
|
closeWebSocket() {
|
|
|
|
if (this.socket && this.socket.connected) {
|
|
|
|
this.socket.disconnect();
|
|
|
|
}
|
|
|
|
},
|
|
|
|
},
|
|
|
|
beforeUnmount() {
|
|
|
|
this.stopVideoStream();
|
|
|
|
this.closeWebSocket();
|
|
|
|
},
|
|
|
|
};
|
|
|
|
</script>
|
|
|
|
<style scoped>
|
|
|
|
.canvasClass {
|
|
|
|
position: relative;
|
|
|
|
width: 600px;
|
|
|
|
height: 600px;
|
|
|
|
}
|
|
|
|
</style>
|