zuobijiancedaima
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

607 lines
18 KiB

<template>
<el-row>
<el-col :span="14">
<div style="margin-bottom: 4px;">
<el-button icon="Plus" type="primary" @click="addUserData">
add User
</el-button>
<el-button icon="Delete" type="danger" @click="delUserData">
delete User
</el-button>
<el-button icon="Refresh" type="info" @click="init({})" text>
refresh
</el-button>
</div>
<div>
<vueTable ref="userTableParent" :refName="refName" :tableHeader="tableHeader" :tableData="tableData"
@detailInfo="detailInfo" @editInfo="editInfo" @updateMima="updateMima">
</vueTable>
<el-card>
<template #header>
<div>
<span>Cheat Pie Chart</span>
<!-- <el-button type="primary" @click="getzuobiList">获取作弊信息</el-button> -->
</div>
</template>
<div id="teacherZuobiEcharts"></div>
</el-card>
</div>
</el-col>
<el-col :span="10">
<el-row>
<el-col :span="12" v-for="studentItem in tableData" :key="studentItem._id">
<!-- <img v-if="imageSrc" :src="imageSrc" class="canvasClass" alt="Video Frame" /> -->
<canvas :ref="`videoEL${studentItem._id}`" class="canvasClass"></canvas>
</el-col>
<!-- <el-col :span="12">
<canvas ref="videoEL2" class="canvasClass"></canvas>
</el-col>
<el-col :span="12">
<canvas ref="videoEL3" class="canvasClass"></canvas>
</el-col>
<el-col :span="12">
<canvas ref="videoEL4" class="canvasClass"></canvas>
</el-col> -->
</el-row>
</el-col>
</el-row>
<el-dialog v-model="addDialog.show" :title="addDialog.title" width="80%" draggable
:close-on-click-modal="formDisabled">
<changeItem :formData="formData" :formDisabled="formDisabled" :type="addDialog.type" :formHeader="formHeader">
</changeItem>
<template #footer v-if="!formDisabled">
<div class="dialog-footer">
<el-button @click="closeDialog(addDialog)" type="info" plain>
取消
</el-button>
<el-button type="primary" @click="submitAdd" plain> 提交 </el-button>
</div>
</template>
</el-dialog>
</template>
<script>
import vueTable from "../component/table.vue";
import changeItem from "../component/changeItem.vue";
import _ from "lodash";
import {
addUser, //新增user信息
delUser, //删除user信息
updateUser, //修改user信息
getUser, //获取user信息
updatePassword,//修改密码
getzuobi,//获取作弊信息
} from "@/api/teacher";
import io from "socket.io-client";
import { ElMessage, ElMessageBox } from 'element-plus'
import * as echarts from 'echarts';
import getip from "@/plugins/getip"
export default {
name: "teacher",
components: { vueTable, changeItem },
data() {
return {
_: _,
refName: "userTable",
localComponent: [
{
prop: "xuexiaomingcheng",
label: "School_name",
type: "text",
tableShow: false,
formShow: true
},
{
prop: "xuexiaodaihao",
label: "学校代号",
type: "text",
tableShow: false,
formShow: true
},
{
prop: "zhuanyemingcheng",
label: "专业名称",
type: "text",
tableShow: true,
formShow: true
},
{
prop: "zhuanyedaihao",
label: "专业代号",
type: "text",
tableShow: false,
formShow: true
},
{
prop: "nianji",
label: "年级",
type: "text",
tableShow: false,
formShow: true
},
{
prop: "banji",
label: "班级",
type: "text",
tableShow: true,
formShow: true
},
{
prop: "xueshengxingming",
label: "学生姓名",
type: "text",
tableShow: true,
formShow: true
},
{
prop: "xuehao",
label: "学号",
type: "text",
tableShow: true,
formShow: true
},
{
label: "照片",
prop: "kaoshengtupian",
type: "img",
tableShow: true,
formShow: true
},
{
prop: "mima",
label: "密码",
type: "text",
tableShow: false,
formShow: true
},
{
prop: "chengji",
label: "成绩",
type: "text",
tableShow: false,
formShow: true
},
{
prop: "zuobiqingkuang",
label: "作弊情况",
type: "text",
tableShow: false,
formShow: true
},
{
prop: "kaoshileixing",
label: "考试类型",
type: "text",
tableShow: true,
formShow: true
},
{
prop: "kaoshikemu",
label: "考试科目",
type: "text",
tableShow: true,
formShow: true
},
{
prop: "kaoshilianjie",
label: "考试链接",
type: "link",
tableShow: true,
formShow: true
},
{
prop: "kaoshishijianduan",
label: "考试时间段",
type: "datetimerange",
startplaceholder: "考试开始时间",
endplaceholder: "考试结束时间",
rangeseparator: "-",
tableShow: true,
formShow: true
},
{
prop: "chuangjianshijian",
label: "创建时间",
type: "text",
tableShow: false,
formShow: false
},
{
prop: "gengxinshijian",
label: "更新时间",
type: "text",
tableShow: false,
formShow: false
},
],
tableHeader: [],
tableData: [],
addDialog: {
type: "add",
show: false,
title: "新增用户",
},
formData: {},
formDataLocal: {},
formHeader: [],
formHeaderLocal: [],
formDisabled: false,
socket: null,
videoStream: null,
imageSrc: "",
canvasObj: {},
zuobiInterval: null,
teacherEcharts: null,
};
},
watch: {},
computed: {},
async mounted() {
if (localStorage.getItem("userId")) {
this.dealTableHeader()
this.dealFormHeader()
await this.init();
await this.initWebSocket()
await this.initCanvas()
let echartsDom = document.getElementById('teacherZuobiEcharts');
this.teacherEcharts = echarts.init(echartsDom)
this.zuobiInterval = setInterval(async () => {
await this.getzuobiList()
}, 1000 * 10);
// getip((ip) => { console.log(777, ip) })
}
},
methods: {
// 处理表格
dealTableHeader() {
this.tableHeader = _.filter(this.localComponent, o => {
o.minWidth = o.label.length * 17 + 5
return o.tableShow
})
},
// 处理表头
dealFormHeader() {
this.formHeaderLocal = []
this.formDataLocal = {}
for (let i = 0; i < this.localComponent.length; i++) {
let element = this.localComponent[i];
if (element.formShow) {
this.formHeaderLocal.push({ ...element })
if (["text"].indexOf(element.type) !== -1) {
this.formDataLocal[element.prop] = ""
} else if (["datetimerange"].indexOf(element.type) !== -1) {
this.formDataLocal[element.prop] = []
} else if (["img"].indexOf(element.type) !== -1) {
this.formDataLocal[element.prop] = ""
}
}
}
},
// 初始化表格
async init(params = {}) {
let res = await getUser({
...params,
});
this.tableData = _.filter(res.list, o => o.isAdmin === 0);
},
// 打开新增学生的模态框
async addUserData() {
this.addDialog = {
type: "add",
show: true,
title: "新增学生",
};
this.formData = { ...this.formDataLocal };
this.formHeader = _.cloneDeep(this.formHeaderLocal)
this.formDisabled = false;
},
// 关闭模态框
closeDialog(addDialog) {
addDialog.show = false;
},
// 新增学生和编辑学生的提交
async submitAdd() {
let subData = {};
for (let i = 0; i < this.formHeader.length; i++) {
let elei = this.formHeader[i];
if (elei.type !== "time") {
subData[elei.prop] = this.formData[elei.prop];
}
}
if (this.addDialog.type === "edit") {
subData["userid"] = this.formData._id;
}
let res = {}
try {
if (this.addDialog.type === "edit") {
res = await updateUser(subData);
} else {
res = await addUser(subData);
}
ElMessage({
message: res.msg,
type: "success",
});
} catch (error) {
} finally {
this.closeDialog(this.addDialog);
await this.init();
}
},
// 删除学生信息
async delUserData() {
let rowList =
this.$refs[`${this.refName}Parent`].$refs[
this.refName
].getSelectionRows();
let delData = [];
let delDispalyData = [];
for (let i = 0; i < rowList.length; i++) {
let elei = rowList[i];
delData.push(elei._id);
delDispalyData.push(`${elei.xueshengxingming}`);
}
ElMessageBox.confirm(`${delDispalyData.join("、")}`, "删除", {
confirmButtonText: "确认",
cancelButtonText: "取消",
type: "error",
}).then(async () => {
let res = await delUser({ ids: delData });
await this.init();
ElMessage({
message: res.msg,
type: "success",
});
});
},
// 查看学生
detailInfo(item) {
this.addDialog = {
type: "info",
show: true,
title: `查看${item.name || ""}`,
};
this.formDisabled = true;
this.formHeader = _.cloneDeep(_.filter(this.formHeaderLocal, o => o.prop !== "mima"))
this.formData = { ...item };
},
// 编辑学生
editInfo(item) {
this.addDialog = {
type: "edit",
show: true,
title: `编辑${item.name || ""}`,
};
this.formDisabled = false;
this.formHeader = _.cloneDeep(_.filter(this.formHeaderLocal, o => o.prop !== "mima"))
this.formData = { ...item };
},
//更新密码
updateMima(item) {
ElMessageBox.prompt("请输入新密码", "修改密码", {
confirmButtonText: "确认",
cancelButtonText: "取消",
}).then(async (value) => {
let res = await updatePassword({ userid: item._id, mima: value.value });
ElMessage({
message: res.msg,
type: "success",
});
})
},
// 初始化画布
async initCanvas() {
for (let i = 0; i < this.tableData.length; i++) {
let element = this.tableData[i];
if (this.$refs[`videoEL${element._id}`]) {
this.canvasObj[`context${element._id}`] = this.$refs[`videoEL${element._id}`][0].getContext('2d');
}
}
},
// 初始化实时传输信息
async initWebSocket() {
this.socket = io('http://127.0.0.1:5000',
{
secure: true,
rejectUnauthorized: false, // 由于自签名证书,可能需要设置此选项
query: { id: localStorage.getItem('userId'), isAdmin: "1" }
}
); // 替换为你的后端服务器地址
this.socket.on('connect', () => {
console.log('Connected to server');
console.log(879784, this.socket.id)
});
//监听视频帧并渲染
for (let index = 0; index < this.tableData.length; index++) {
let element = this.tableData[index];
this.socket.on(`teacherVideo${element._id}`, (data) => {
// 在这里处理接收到的视频帧
let blob = this.dataURLToBlob(data.data);
let urlCreator = window.URL || window.webkitURL;
let imageUrl = urlCreator.createObjectURL(blob);
let image = new Image();
image.onload = () => {
this.canvasObj[`context${data.userId}`].drawImage(image, 0, 0, image.width, image.height, 0, 0, 300, 150);
};
image.src = imageUrl;
});
}
//监听说话信息并提示
for (let index = 0; index < this.tableData.length; index++) {
let element = this.tableData[index];
this.socket.on(`teacherTalk${element._id}`, (data) => {
let userId = _.get(data, ['userId'], "")
let msg = _.get(data, ['data'], "")
let type = _.get(data, ['type'], "")
let stedentItem = _.find(this.tableData, o => o._id === userId)
if (type === "talk") {
ElMessage({
message: `${stedentItem.xueshengxingming}说:${msg}`,
type: "error",
});
} else {
if (type === "normal") {
ElMessage({
message: `${stedentItem.xueshengxingming}${type}`,
type: "success",
});
} else {
ElMessage({
message: `${stedentItem.xueshengxingming}正在:${type}`,
type: "error",
});
}
}
console.log(78744, stedentItem, msg)
// 在这里处理接收到的学生端谈话信息
});
}
//监听疑问信息并处理
for (let index = 0; index < this.tableData.length; index++) {
let element = this.tableData[index];
this.socket.on(`teacherMsg${element._id}`, (data) => {
let userId = _.get(data, ['userId'], "")
let msg = _.get(data, ['data'], "")
let stedentItem = _.find(this.tableData, o => o._id === userId)
ElMessageBox.prompt(`${stedentItem.xueshengxingming}问:${msg}`, `疑问`, {
confirmButtonText: 'OK',
cancelButtonText: 'Cancel'
})
.then(({ value }) => {
ElMessage({
type: 'success',
message: `已回答${value}`,
})
this.socket.emit('answerMsg', { userId: stedentItem._id, data: `${msg}@@@${value}` });
})
.catch(() => {
ElMessage({
type: 'info',
message: 'Input canceled',
})
this.socket.emit('answerMsg', { userId: stedentItem._id, data: `${msg}@@@ ` });
})
// 在这里处理接收到的学生端疑问信息
});
}
},
stopVideoStream() {
if (this.videoStream) {
this.videoStream.getTracks().forEach(track => track.stop());
}
},
closeWebSocket() {
if (this.socket && this.socket.connected) {
this.socket.disconnect();
}
},
dataURLToBlob(dataURL) {
let parts = dataURL.split(';base64,');
let contentType = parts[0].split(':')[1];
let byteCharacters = atob(parts[1]);
let byteArrays = [];
for (let offset = 0; offset < byteCharacters.length; offset += 1024) {
let slice = byteCharacters.slice(offset, offset + 1024);
let byteNumbers = new Array(slice.length);
for (let i = 0; i < slice.length; i++) {
byteNumbers[i] = slice.charCodeAt(i);
}
let byteArray = new Uint8Array(byteNumbers);
byteArrays.push(byteArray);
}
let blob = new Blob(byteArrays, { type: contentType });
return blob;
},
// 获取作弊信息
async getzuobiList() {
let res = await getzuobi()
let zuobiList = res.list
let zuobiObj = _.groupBy(zuobiList, 'userId')
let allZuobi = {}
let dataLocal = []
for (let key in zuobiObj) {
let value = zuobiObj[key]
let valueObj = _.groupBy(value, 'type')
let studentItem = _.find(this.tableData, o => o._id === key)
if (studentItem) {
allZuobi[key] = valueObj
dataLocal.push({
value: value.length,
name: studentItem.xueshengxingming,
_id: key
})
}
}
let option = {
tooltip: {
trigger: 'item',
formatter: function (params) {
let msg = ""
for (let key1 in allZuobi[params.data._id]) {
let value1 = allZuobi[params.data._id][key1]
msg += `${key1}:${value1.length}<br>`
}
return `${params.name}<br>${msg}`;
}
},
legend: {
top: '1%',
left: 'center'
},
series: [
{
name: '作弊次数',
type: 'pie',
radius: ['30%', '80%'],
avoidLabelOverlap: false,
itemStyle: {
borderRadius: 10,
borderColor: '#fff',
borderWidth: 2
},
label: {
show: false,
position: 'center'
},
emphasis: {
label: {
show: true,
fontSize: 40,
fontWeight: 'bold'
}
},
labelLine: {
show: false
},
data: dataLocal
}
]
};
this.teacherEcharts.setOption(option)
}
},
beforeUnmount() {
this.stopVideoStream();
this.closeWebSocket();
clearInterval(this.zuobiInterval)
},
};
</script>
<style scoped>
.canvasClass {
position: relative;
width: 270px;
height: 270px;
background-color: aliceblue
}
#teacherZuobiEcharts {
width: 300px;
height: 300px;
position: relative;
left: calc(50% - 200px);
}
</style>