作业批阅

This commit is contained in:
白了个白 2024-09-09 07:04:21 +08:00
parent 78ac6e5ca6
commit 04ac5dc8b5
9 changed files with 2905 additions and 0 deletions

View File

@ -0,0 +1,237 @@
// 查询evaluation列表
import request from '@/utils/request'
// 查询反馈列表
// 查询classworkdata列表 班级作业列表
export function listClassworkdata(query) {
return request({
url: '/education/classworkdata/list',
method: 'get',
params: query
})
}
// 查询entpcoursework列表 课程作业列表
export function listEntpcoursework(query) {
return request({
url: '/education/entpcoursework/list',
method: 'get',
params: query
})
}
// 查询classworkeval列表 课堂作业列表
export function listClassworkeval(query) {
return request({
url: '/education/classworkeval/list',
method: 'get',
params: query
})
}
// 修改classworkeval
export function updateClassworkeval(data) {
return request({
url: '/education/classworkeval',
method: 'put',
data: data
})
}
// 0------------------------
// 查询班级列表
export function listClassmain(query) {
return request({
url: '/education/classmain/list',
method: 'get',
params: query
})
}
// 查询学生列表
export function listClassuser(query) {
return request({
url: '/education/classuser/list',
method: 'get',
params: query
})
}
// 新增班级
export function addClassmain(data) {
return request({
url: '/education/classmain',
method: 'post',
data: data
})
}
// 查询所有学科的列表
export function listEvaluation(query) {
return request({
url: '/education/evaluation/list',
method: 'get',
params: query
})
}
// 新增小组
export function addClassgroup(data) {
return request({
url: '/education/classgroup',
method: 'post',
data: data
})
}
//班级详情
export function getClassmain(id) {
return request({
url: '/education/classmain/' + id,
method: 'get'
})
}
// 获取小组列表
export function listClassgroup(query) {
return request({
url: '/education/classgroup/new/list',
method: 'get',
params: query
})
}
//删除小组
export function delClassgroup(id) {
return request({
url: '/education/classgroup/' + id,
method: 'delete'
})
}
//查询小组信息
export function getClassgroup(id) {
return request({
url: '/education/classgroup/' + id,
method: 'get'
})
}
//修改小组信息
export function updateClassgroup(data) {
return request({
url: '/education/classgroup',
method: 'put',
data: data
})
}
//新增学生
export function addStudentmain(data) {
return request({
url: '/education/studentmain',
method: 'post',
data: data
})
}
//修改学生信息
export function updateStudentmain(data) {
return request({
url: '/education/studentmain',
method: 'put',
data: data
})
}
//获取学生信息
export function getStudentmain(id) {
return request({
url: '/education/studentmain/' + id,
method: 'get'
})
}
//删除学生
export function leaveClass(data) {
return request({
url: '/education/classuser/leaveClass',
method: 'post',
data: data
})
}
//删除学生所有数据
export function removeStudentDataAll(id) {
return request({
url: '/education/studentmain/removeStudent/' + id,
method: 'post'
})
}
//删除教室
export function delClassroom(id) {
return request({
url: '/education/classroom/' + id,
method: 'delete'
})
}
//导入学生
export function addStudentmainByNameArray(data) {
return request({
url: '/education/studentmain/addByNameArray',
method: 'post',
data: data
})
}
//新增课程预约
export function addSmartClassReserv(data) {
return request({
url: '/smarttalk/classReserv/addSmartClassReserv',
method: 'post',
data: data
})
}
//修改课程预约
export function updateSmartClassReserv(data) {
return request({
url: '/smarttalk/classReserv/updateSmartClassReserv',
method: 'post',
data: data
})
}
//查询课程预约
export function getSelfReserv() {
return request({
url: '/smarttalk/classReserv/getSelfReserv',
method: 'get'
})
}
export function deleteSmartReserv(id) {
return request({
url: '/smarttalk/classReserv/' + id,
method: 'delete'
})
}
export function startClass(id, ex3) {
const params = {id}
!!ex3 && (params.ex3 = ex3)
return request({
url: '/smarttalk/classReserv/startClass',
method: 'get',
params
})
}
export function endClass(id) {
return request({
url: '/smarttalk/classReserv/endClass',
method: 'get',
params: {id}
})
}
/**
* @description 获取课堂信息
* @param {*} id
* @returns
*/
export function getClassInfo(id) {
return request({
url: '/smarttalk/classReserv/selectById',
method: 'get',
params: {id}
})
}

View File

@ -0,0 +1,514 @@
export const isJson = (str) => {
if (typeof str == 'string') {
try {
let obj = JSON.parse(str)
if (typeof obj == 'object' && obj) {
return true
} else {
return false
}
} catch (e) {
return false
}
}
}
/**
* @description processList 格式化试题
* @param {*} row
*/
export const processList = (row) => {
for (var i = 0; i < row.length; i++) {
if (isJson(row[i].workanalysis)) {
//1、先默认格式化 格式化各项内容(待优化, 后续界面显示的为format的值)
row[i].titleFormat = row[i].title // 题目
row[i].examdateFormat = row[i].examdate // ?考试日期 eg 2024-07-11 14:39:27"
row[i].workdescFormat = row[i].workdesc // 题目选项
row[i].workanswerFormat = row[i].workanswer // 题目正确答案
if (row[i].workanswerFormat == null || row[i].workanswerFormat == '') {
row[i].workanswerFormat = '见试题解答内容'
}
// workanalysis 解析内容analyse解答 method分析 discuss点评
var jjj = JSON.parse(row[i].workanalysis)
row[i].analyse = jjj.analyse
row[i].method = jjj.method
row[i].discuss = jjj.discuss
//row[i].discusscollapse = false;
if (row[i].examdate !== null && row[i].examdate !== undefined) {
row[i].examdate = row[i].examdate.substring(0, 10)
}
// 具体题型数据结构处理
if (row[i].worktype == '复合题') {
// 旧类型
if (row[i].title.indexOf('!@#$%') !== -1) {
// 1.选项解析替换
const options = JSON.parse(row[i].workdesc)
// 题目(背景材料+复合题目)
const bjTitle = row[i].title.split('!@#$%')[0]
const tmTitles = row[i].title.split('!@#$%').filter((it, ix) => ix > 0)
// console.log(bjTitle,'背景标题');
// console.log(tmTitles,'复合题目');
let titls = []
options.forEach((element, index1) => {
const workDescArr = element.split('#&')
let tmp = ''
let j = 0
for (; j < workDescArr.length; j++) {
if (j % 2 == 0) {
tmp += `<div style='width:80%;display:flex;'>`
}
const char = String.fromCharCode(65 + j)
tmp += `<div style='display:flex;margin-left:2%;width:35%;overflow:hidden;text-overflow:ellipsis;font-size:0.9em;'>${char}.${workDescArr[j]}</div>`
if (j % 2 == 1) {
tmp += '</div>'
}
}
// j此刻已自增1, 故当选项为单数时, 需要补充结束标签
if (j % 2 == 1) {
tmp += '</div>'
}
// workDescArr为 [''] 表示为 判断题或者填空题,这里不需要选项
if (workDescArr[0] != '') {
titls.splice(index1, 1, tmp)
} else {
titls.splice(index1, 1, '')
}
})
const s = []
tmTitles.map((it, ix) => {
s.push(it)
titls.map((it2, ix2) => {
if (ix == ix2) {
s.push(it2)
}
})
})
// console.log(s,'?????????????????')
row[i].titleFormat = bjTitle + s.join('')
row[i].workdescFormat = ''
//2.答案 - 数字转为ABCD
const answerArr = JSON.parse(row[i].workanswer)
let indexLabel = 1
let arr = []
answerArr.forEach((item) => {
const arrTmp = item.answer.split('#&')
let value = `(${indexLabel})`
arrTmp.forEach((element, i) => {
if (item.type == '单选题' || item.type == '多选题') {
value += `${String.fromCharCode(65 + Number(element))}`
}
if (item.type == '判断题' || item.type == '填空题') {
// 去除下 html标签
value += `${element.replace(/<[^>]+>/g, '')}` + (i == arrTmp.length - 1 ? '' : '、')
}
if (item.type == '主观题') {
if (element) {
console.log(element, 'element')
value += item.answer
} else {
value += '答案不唯一,请参考分析解答点评!'
}
}
})
arr.push(value)
indexLabel++
})
const answer = arr.join('<br />')
row[i].workanswerFormat = answer
} else {
// 处理[题干显示] - 不再需要处理
// row[i].titleFormat = row[i].title; // 仅占位提示
/**
* 处理[选项显示] - 特殊结构
* [
* {type: '单选题', title: '题目1', options: ['ABC123','ABC123']},
* {type: '多选题', title: '题目1', options: ['ABC123','ABC123']},
* {type: '填空题', title: '题目1', options: []},
* {type: '判断题', title: '题目1', options: []},
* {type: '主观题', title: '题目1', options: []},
* ]
*/
let workDescArr = JSON.parse(row[i].workdesc)
let workDescHtml = `<div style='width:80%;display:flex;>`
workDescArr.map((item, index) => {
if (item.type == '单选题' || item.type == '多选题') {
workDescHtml += `<div style='width:80%;display:flex;'>${index + 1}. ${item.title}</div>`
let tmp = ''
let j = 0
let optionsArr = item.options
for (; j < optionsArr.length; j++) {
if (j % 2 == 0) {
tmp += `<div style='width:80%;display:flex;'>`
}
const char = String.fromCharCode(65 + j)
tmp += `<div style='display:flex;margin-left: 2%; width: 36%'>${char}.${optionsArr[j]}</div>`
if (j % 2 == 1) {
tmp += '</div>'
}
}
// j此刻已自增1, 故当选项为单数时, 需要补充结束标签
if (j % 2 == 1) {
tmp += '</div>'
}
workDescHtml += tmp
} else if (item.type == '填空题' || item.type == '判断题' || item.type == '主观题') {
workDescHtml += `<div style='width:80%;display:flex;'>${index + 1}. ${item.title}</div>`
}
})
workDescHtml += '</div>'
row[i].workdescFormat = workDescHtml
/**
* 处理[答案显示] - 特殊结构
* [
* {type: '单选题', answer: ['0']},
* {type: '多选题', answer: ['0','1']},
* {type: '填空题', answer: ['填空1','填空2']},
* {type: '判断题', answer: ['0'/'1']},
* {type: '主观题', answer: [xxxx]},
* ]
*/
let workAnswerArr = JSON.parse(row[i].workanswer)
let workAnswerHtml = ``
workAnswerArr.map((item, index) => {
const answerArr = item.answer //JSON.parse(item.answer);
if (item.type == '单选题' || item.type == '多选题') {
const answer = answerArr
.map((item) => {
return String.fromCharCode(65 + Number(item))
})
.join('')
workAnswerHtml += `<div style='display:flex;'>${index + 1}. ${answer}</div>`
} else if (item.type == '填空题') {
const answer = answerArr.join('、')
workAnswerHtml += `<div style='display:flex;'>${index + 1}. ${answer}</div>`
} else if (item.type == '判断题') {
const answer = answerArr
.map((item) => {
return item === '1' ? '正确' : '错误'
})
.join('、')
workAnswerHtml += `<div style='display:flex;'>${index + 1}. ${answer}</div>`
} else if (item.type == '主观题') {
// 复合题里面的主观题只有一个答案,或没填
const answer = answerArr.join('、')
if (answerArr[0]) {
workAnswerHtml += `<div style='display:flex;'>${index + 1}. ${answer}</div>`
} else {
workAnswerHtml += `<div style='display:flex;'>${index + 1}. ${answer}答案不唯一,请参考分析解答点评!</div>`
}
}
})
row[i].workanswerFormat = workAnswerHtml
}
} else if (
row[i].worktype == '主观题' ||
(row[i].worktype !== '单选题' &&
row[i].worktype !== '多选题' &&
row[i].worktype !== '填空题' &&
row[i].worktype !== '判断题')
) {
// 处理[选项显示] - 主观题中无选项, 故置空
row[i].workdescFormat = ''
row[i].workanswerFormat = ''
// 答案处理- eg: "\"不唯一的答案,参考\""
if (row[i].workanswer && row[i].workanswer != '') {
row[i].workanswerFormat = JSON.parse(row[i].workanswer)
}
} else {
// 单选题|多选题|填空题|判断题|主观题?(待确认是否归在这里)
// 通用选项结构 ['ABC123','ABC123'] | ['ABC123','ABC123'] | [](填空题无选项) | [](判断题无选项)
let workDescArr = []
if (
row[i].workdesc.charAt(0) === '[' &&
row[i].workdesc.charAt(row[i].workdesc.length - 1) === ']'
) {
//123会直接被转换, 且不是数组对象, 故手动判断是否有[和]两个字符
workDescArr = JSON.parse(row[i].workdesc)
} else if (row[i].workdesc.indexOf('#&') !== -1) {
workDescArr = row[i].workdesc.split('#&')
} else if (row[i].workdesc.indexOf(',') !== -1) {
workDescArr = row[i].workdesc.split(',')
} else {
// 单字符串直接添加至空数组(待考虑确认)
workDescArr.push(row[i].workdesc)
}
// 单选题|多选题|填空题|判断题|主观题?(待确认是否归在这里)
// 通用答案结构 ['0'] | ['0','1'] | ['填空1','填空2'] | ['0'/'1']
let workAnswerArr = []
if (
row[i].workanswer.charAt(0) === '[' &&
row[i].workanswer.charAt(row[i].workanswer.length - 1) === ']'
) {
// 123会直接被转换, 且不是数组对象, 故手动判断是否有[和]两个字符
workAnswerArr = JSON.parse(row[i].workanswer)
} else if (row[i].workanswer.indexOf('#&') !== -1) {
workAnswerArr = row[i].workanswer.split('#&')
} else if (row[i].workanswer.indexOf(',') !== -1) {
workAnswerArr = row[i].workanswer.split(',')
} else {
// 单字符串直接添加至空数组(待考虑确认)
workAnswerArr.push(row[i].workanswer)
}
// 具体题型处理
if (row[i].worktype == '单选题' || row[i].worktype == '多选题') {
// 处理[选项显示] - 拼接ABCD首序号
let tmp = ''
let j = 0
for (; j < workDescArr.length; j++) {
if (j % 2 == 0) {
tmp += `<div style='width:80%;display:flex;'>`
}
const char = String.fromCharCode(65 + j)
tmp += `<div style='display:flex;margin-left: 2%; width: 36%'>${char}.${workDescArr[j]}</div>`
if (j % 2 == 1) {
tmp += '</div>'
}
}
if (j % 2 == 0) {
tmp += '</div>'
}
row[i].workdescFormat = tmp
// 处理[答案显示] - 转换ABCD
let arr2Char = workAnswerArr
.map((item) => {
return String.fromCharCode(65 + Number(item))
})
.join('')
row[i].workanswerFormat = arr2Char
} else if (row[i].worktype == '填空题') {
// 处理[选项显示] - 填空题中无选项, 故置空
row[i].workdescFormat = ''
// 处理[答案显示] - 逗号连接
row[i].workanswerFormat = workAnswerArr.join('、')
} else if (row[i].worktype == '判断题') {
// 处理[选项显示] - 判断题中无选项, 故置空
row[i].workdescFormat = ''
// 处理[答案显示] - 1-正常 0-错误
const answer = workAnswerArr
.map((item) => {
return item === '1' ? '正确' : '错误'
})
.join('、')
row[i].workanswerFormat = answer
}
}
/*
//2、处理单选题
if(row[i].worktype == '单选题' || row[i].worktype == '多选题' ){
//1.选项前增加ABCD workdesc: "①②#&①③#&②④#&③④" || "<div>为了活着</div>#&<div>为了填报肚子</div>#&<div>为了吃饭而吃饭</div>"
let workDescArr = [];
if(row[i].workdesc.indexOf('[')!==-1 && row[i].workdesc.indexOf(']')!==-1) {
//123会直接被转换, 且不是数组对象, 故手动判断是否有[和]两个字符
workDescArr = JSON.parse(row[i].workdesc);
}
else if(row[i].workdesc.indexOf('#&')) {
workDescArr = row[i].workdesc.split('#&');
}
else if(row[i].workdesc.indexOf(',')){
workDescArr = row[i].workdesc.split(',');
}
else {
// 待考虑
workDescArr.push(item.workdesc)
}
//2.答案 - 数字转为ABCD
if(row[i].worktype == '单选题') {
const str2Char = String.fromCharCode(65+Number(row[i].workanswer));
row[i].workanswerFormat = str2Char;
} else if (row[i].worktype == '多选题') {
const answerArr = row[i].workanswer.split('#&');
let arr2Char = '';
for(let k=0; k<answerArr.length; k++){
arr2Char += String.fromCharCode(65+Number(answerArr[k]));
}
row[i].workanswerFormat = arr2Char;
}
}
else if(row[i].worktype == '填空题') {
// console.log(row[i].workanswer.replace(/<[^>]*>/g, "").split('#&'),'????')
// 填空题答案
row[i].workanswerFormat = row[i].workanswer.replace(/#&/g," ");
// 填空选项不需要展示,
row[i].workdescFormat = '';
}
else if(row[i].worktype == '判断题'){
// console.log(row[i].workanswer.replace(/<[^>]*>/g, "").split('#&'),'????')
// 判断题答案
row[i].workanswerFormat = row[i].workanswer.replace(/#&/g," ");
// 判断选项不需要展示,
row[i].workdescFormat = '';
}
else if(row[i].worktype == '复合题') {
// 1.选项解析替换
const options = JSON.parse(row[i].workdesc);
// 题目(背景材料+复合题目)
const bjTitle = row[i].title.split('!@#$%')[0];
const tmTitles = row[i].title.split('!@#$%').filter((it,ix)=>ix>0);
// console.log(bjTitle,'背景标题');
// console.log(tmTitles,'复合题目');
let titls = [];
options.forEach((element,index1) => {
const workDescArr = element.split('#&');
let tmp = '';
let j=0;
for(; j<jsonArr.length; j++){
if(j%2 == 0){
tmp += `<div style='width:80%;display:flex;'>`;
}
const char = String.fromCharCode(65+j);
tmp += `<div style='display:flex;margin-left: 2%; width: 36%'>${char}.${jsonArr[j]}</div>`;
if(j%2 == 1){
tmp += '</div>';
}
}
if(j%2== 0){
tmp += '</div>';
}
workdesc = tmp;
}
row[i].workdescFormat = workdesc; // 题目选项
// 答案处理
let workanswer = '';
if(row[i].workanswer && row[i].workanswer != '') {
// 因答案内容存在多种格式: 1.["123","1234"] 2.123#&1234 3.123
if(row[i].workanswer.indexOf('[')!==-1 && row[i].workanswer.indexOf(']')!==-1) {
//123会直接被转换, 且不是数组对象, 故手动判断是否有[和]两个字符
let json = JSON.parse(row[i].workanswer);
// 单选、多选 需要 数字转为ABCD
if(row[i].worktype == '单选题') {
const str2Char = String.fromCharCode(65+Number(json[0]));
workanswer = str2Char;
} else if (row[i].worktype == '多选题') {
// const answerArr = row[i].workanswer.split('#&');
let arr2Char = '';
for(let k=0; k<json.length; k++){
arr2Char += String.fromCharCode(65+Number(json[k]));
}
workanswer = arr2Char;
} else if(row[i].worktype == '主观题' ) {
let arr2Char = '';
for(let k=0; k<json.length; k++){
const itemArr = json[k];
arr2Char += '('+ (parseInt(k) + 1) +')'+ itemArr.join('、')+ '<br />';
}
workanswer = arr2Char;
row[i].titleFormat = row[i].titleFormat.replace(/!@#\$%/g, '');
} else {
workanswer = json.join('、');
}
} else if(row[i].workanswer.indexOf('#&')) {
// 意味着多个答案或者填空内容
let workanswerList = row[i].workanswer.split('#&');
if(row[i].worktype == '多选题') {
// 数字转为ABCD
let arr2Char = '';
for(let k=0; k<workanswerList.length; k++){
arr2Char += String.fromCharCode(65+Number(workanswerList[k]));
}
workanswer = arr2Char;
}else{
workanswer = workanswerList.join('、');
}
} else if(row[i].workanswer.indexOf(',')){
// 意味这同样多个答案或者填空内容
let workanswerList = row[i].workanswer.split(',');
if(row[i].worktype == '多选题') {
// 数字转为ABCD
let arr2Char = '';
for(let k=0; k<workanswerList.length; k++){
arr2Char += String.fromCharCode(65+Number(workanswerList[k]));
}
workanswer = arr2Char;
}else{
workanswer = workanswerList.join('、');
}
} else {
// 待考虑
workanswer = row[i].workanswer;
}
}
row[i].workanswerFormat = workanswer; // 题目正确答案
//2.答案 - 数字转为ABCD
const answerArr = JSON.parse(row[i].workanswer);
let indexLabel = 1;
let arr = [];
answerArr.forEach(item => {
const arrTmp = item.answer.split('#&');
let value = `(${indexLabel})`;
arrTmp.forEach((element,i) => {
if(item.type == '单选题' || item.type == '多选题'){
value += `${String.fromCharCode(65+Number(element))}`;
}
if(item.type == '判断题' || item.type == '填空题'){
// 去除下 html标签
value += `${element.replace(/<[^>]+>/g, '')}`+ (i==arrTmp.length-1?'':'、');
}
})
arr.push(value);
indexLabel++;
})
const answer = arr.join('<br />');
row[i].workanswerFormat = answer;
}
else if(row[i].worktype == '主观题') {
// 1.选项解析替换---主观题没选项
// 题目(背景材料+主观题目)
const bjTitle = row[i].title.split('!@#$%')[0];
const tmTitles = row[i].title.split('!@#$%').filter((it,ix)=>ix>0);
// console.log(bjTitle,'背景标题');
// console.log(tmTitles,'主观题目');
let titls = [];
const s = [];
tmTitles.map((it,ix)=>{
s.push(it);
})
// console.log(s,'?????????????????')
row[i].titleFormat = bjTitle + s.join('');
// 填空选项不需要展示,
row[i].workdescFormat = '';
//2.答案
// 填空题答案
const workanswerList = JSON.parse(row[i].workanswer);
let tmp='';
workanswerList&&workanswerList.map((item,index)=>{
tmp += '<div>'+(index+1)+'.'+item.replace(/#&/g, '')+'</div>';
})
row[i].workanswerFormat = tmp;
}
else {
//处理答案
row[i].workanswerFormat = '见试题解答内容';
}
*/
}
}
}

View File

@ -37,6 +37,7 @@
<el-dropdown-item @click="changePage('/profile')">个人中心</el-dropdown-item>
<el-dropdown-item @click="changePage('/classReserv')">课程预约</el-dropdown-item>
<el-dropdown-item @click="changePage('/class')">班级中心</el-dropdown-item>
<el-dropdown-item @click="changePage('/classTask')">作业批改</el-dropdown-item>
<el-dropdown-item divided command="logout">
<span>退出登录</span>
</el-dropdown-item>

View File

@ -1,3 +1,8 @@
/*
* @Author: 苦逼程序猿
* @Date: 2024-09-06 16:15:32
* @Warning: 千行代码Bug露锋芒
*/
import { createRouter, createWebHashHistory } from 'vue-router'
import Layout from '../layout/index.vue'
@ -57,6 +62,12 @@ export const constantRoutes = [
name: 'class',
meta: {title: '班级中心'},
},
{
path: '/classTask',
component: () => import('@/views/classTask/classTask.vue'),
name: 'class',
meta: {title: '作业批改'},
},
]
},
...toolRouters

View File

@ -108,4 +108,29 @@ export const getAfterMinutes = (m) => {
let minutes = afterMinutes.getMinutes();
minutes = minutes < 10 ? ('0' + minutes) : minutes
return `${hours}:${minutes}`;
}
/**
* 表格时间格式化
*/
export function formatDate(cellValue) {
if (cellValue == null || cellValue == "") return "";
var date = new Date(cellValue)
var year = date.getFullYear()
var month = date.getMonth() + 1 < 10 ? '0' + (date.getMonth() + 1) : date.getMonth() + 1
var day = date.getDate() < 10 ? '0' + date.getDate() : date.getDate()
var hours = date.getHours() < 10 ? '0' + date.getHours() : date.getHours()
var minutes = date.getMinutes() < 10 ? '0' + date.getMinutes() : date.getMinutes()
var seconds = date.getSeconds() < 10 ? '0' + date.getSeconds() : date.getSeconds()
return year + '-' + month + '-' + day + ' ' + hours + ':' + minutes + ':' + seconds
}
export function getTimeDate() {
var date = new Date()
var year = date.getFullYear()
var month = date.getMonth() + 1 < 10 ? '0' + (date.getMonth() + 1) : date.getMonth() + 1
var day = date.getDate() < 10 ? '0' + date.getDate() : date.getDate()
var hours = date.getHours() < 10 ? '0' + date.getHours() : date.getHours()
var minutes = date.getMinutes() < 10 ? '0' + date.getMinutes() : date.getMinutes()
var seconds = date.getSeconds() < 10 ? '0' + date.getSeconds() : date.getSeconds()
return year + '-' + month + '-' + day + ' ' + hours + ':' + minutes + ':' + seconds
}

View File

@ -0,0 +1,394 @@
<!--
* @Author: 苦逼程序猿
* @Date: 2024-09-06 16:58:59
* @Warning: 千行代码Bug露锋芒
-->
<template>
<el-container class="class-reserv-wrap">
<div class="class-reserv-tabs">
<el-segmented v-model="tabActive" block :options="tabOptions" size="large" />
</div>
<div class="class-reserv-body">
<task-item
v-for="(item, index) in activeDataList"
v-show="tabActive === '进行中'"
:key="index"
:item="item"
:tabactive="tabActive"
@click="itemDialogRef.openDialog(item)"
@delete-reserv="deleteReserv(item)"
></task-item>
<task-item
v-for="(item, index) in doneDataList"
v-show="tabActive === '已结束'"
:key="index"
:item="item"
:tabactive="tabActive"
@click="itemDialogRef.openDialog(item)"
@delete-reserv="deleteReserv(item)"
></task-item>
</div>
<item-dialog ref="itemDialogRef"></item-dialog>
</el-container>
</template>
<script setup>
import { ref, onMounted, onUnmounted, computed, watch } from 'vue'
import { getSelfReserv } from '@/api/classManage'
import { listClassmain } from '@/api/classManage/index'
import { listClassworkdata } from '@/api/classTask'
import { homeworklist } from '@/api/teaching/classwork'
import TaskItem from '@/views/classTask/container/task-item.vue'
import ItemDialog from '@/views/classTask/container/item-dialog.vue'
import { useToolState } from '@/store/modules/tool'
import { sessionStore } from '@/utils/tool'
import useUserStore from '@/store/modules/user'
const userStore = useUserStore().user
const itemDialogRef = ref(null)
const tabOptions = ref(['进行中', '已结束'])
const tabActive = ref('进行中')
const dataList = ref([])
//
const classList = ref([])
const classListIds = ref([])
//
const classWorkList = ref([])
const total = ref(0)
const loading = ref(false)
const activeDataList = computed(() => {
// return classWorkList.value
// []
return classWorkList.value && classWorkList.value.filter((item) => getDateTime > item.deaddate)
})
const deleteReserv = (item) => {
console.log('删除待开发', item)
// dataList.value = dataList.value.filter((is) => {
// return is.id !== item.id
// })
}
const doneDataList = computed(() => {
// return classWorkList.value
return classWorkList.value && classWorkList.value.filter((item) => getDateTime < item.deaddate)
})
//
const getData = () => {
//
listClassmain({ classuserid: userStore.userId, pageSize: 100, status: 'open' }).then((res) => {
var clslist = []
for (var i = 0; i < res.rows.length; i++) {
if (res.rows[i].classstudentlist != '') {
var array = JSON.parse('[' + res.rows[i].classstudentlist + ']')
res.rows[i].classstudents = array
}
classListIds.value.push(res.rows[i].id)
clslist.push(res.rows[i])
}
classList.value = clslist
//
homeworklist({
classidarray: classListIds.value.join(','),
entpcourseid: '', // id
orderby: 'uniquekey DESC',
pageSize: 100
}).then((response) => {
for (var i = 0; i < response.rows.length; i++) {
//
response.rows[i].workdatalist = []
response.rows[i].workdatacount = 0 //
response.rows[i].workdatalistVisible = false
response.rows[i].workdatafeedbackcount = 0
response.rows[i].feedtimelength = 0
response.rows[i].rightAnswerCount = 0
response.rows[i].scoingRate = 0 + '%' //
response.rows[i].averagetime = 0 //
// ----------------------------------------------
// UI
if (response.rows[i].worktype == '学习目标定位') {
response.rows[i].workclass = 'success'
response.rows[i].workcodesList = JSON.parse(response.rows[i].workcodes)
} else if (response.rows[i].worktype == '教材研读') {
response.rows[i].workclass = 'primary'
} else if (response.rows[i].worktype == '框架梳理') {
response.rows[i].workclass = 'warning'
} else if (response.rows[i].worktype == '学科定位') {
response.rows[i].workclass = 'info'
} else if (response.rows[i].worktype == '习题训练') {
response.rows[i].workclass = 'danger'
} else {
response.rows[i].workclass = ''
}
//
if (response.rows[i].entpcourseworklist != '') {
response.rows[i].entpcourseworklistarray = JSON.parse(
'[' + response.rows[i].entpcourseworklist + ']'
)
} else {
response.rows[i].entpcourseworklistarray = []
}
// classworkdatastudentids
if (
response.rows[i].classworkdatastudentids != '' &&
response.rows[i].classworkdatastudentids != null &&
response.rows[i].classworkdatastudentids != 'null'
) {
const stuList = JSON.parse('[' + response.rows[i].classworkdatastudentids + ']')
response.rows[i].workdatacount = stuList.length
}
}
// >0
if (response.rows && response.rows.length > 0) {
classWorkList.value =
response.rows && response.rows.filter((item) => item.workdatacount > 0)
//TODO total
total.value = response.total
}
loading.value = false
//
getStudentClassWorkData()
})
})
//--------------
// getSelfReserv().then((res) => {
// const list = res.data || []
// list.sort((a, b) => {
// if (a.status == '') return -1
// else return 0
// })
// dataList.value = list
// })
}
const toolStore = useToolState()
//
const escapeHtmlQuotes = (str) => {
// replace,
return str
//
// return str.replace(/(<[^>]+>)/g, function (match) {
// return match.replace(/"/g, '\\"')
// })
}
const pollingST = ref(null) //
onMounted(() => {
getData() //
//
pollingST.value = setInterval(() => {
getStudentVisible()
}, 1000 * 60)
})
onUnmounted(() => {
clearInterval(pollingST.value)
//clearInterval(this.classWorkActiveData.timerId) //
})
const getDateTime = () => {
//
const now = new Date()
const year = now.getFullYear()
const month = String(now.getMonth() + 1).padStart(2, '0')
const day = String(now.getDate()).padStart(2, '0')
const hh = String(now.getHours()).padStart(2, '0')
const mm = String(now.getMinutes()).padStart(2, '0')
return `${year}-${month}-${day} ${hh}:${mm}`
}
// [] -
const getStudentVisible = async () => {
if (classListIds.value.length <= 0) {
return
}
//
const response = await homeworklist({
classidarray: classListIds.value.join(','),
entpcourseid: '', //
orderby: 'uniquekey DESC',
pageSize: 100
})
const curWorkList = response.rows
/**
* warn: 这里仅更新了finishpercent(进度条), 且当前作业布置推送新任务时, curWorkList中会查到新的任务与当前页面中this.classWorkList长度不一致,
* 故这里需循环this.classWorkList且只更新当前页面中的存在的任务进度
*/
for (let t = 0; t < classWorkList.value.length; t++) {
// []
// if( getDateTime > classWorkList.value[t].deaddate ){
// continue;
// }
// (index)
let curWork = curWorkList.find((work) => work.id === classWorkList.value[t].id)
// workdataresultcount workdatacount0
if (curWork && curWork.workdataresultcount > 0 && classWorkList.value[t].workdatacount > 0) {
classWorkList.value[t].workdataresultcount = curWork.workdataresultcount
//
classWorkList.value[t].finishpercent = parseInt(
(classWorkList.value[t].workdataresultcount / classWorkList.value[t].workdatacount) * 100
)
//
console.log('平均用时??---', classWorkList.value[t].workdatafeedbackcount)
if (classWorkList.value[t].workdatafeedbackcount > 0) {
classWorkList.value[t].averagetime = (
classWorkList.value[t].feedtimelength / classWorkList.value[t].workdatafeedbackcount
).toFixed(0)
console.log('平均用时---', classWorkList.value[t].averagetime)
} else {
classWorkList.value[t].averagetime = 0
}
} else {
classWorkList.value[t].finishpercent = 0
}
}
return 1
}
//
const getStudentClassWorkData = () => {
//
console.log('======????????""""""""""')
listClassworkdata({
classids: classListIds.value.join(','),
entpcourseid: '', // id
pageSize: 1000
}).then((res) => {
console.log('多个班级里,每个学生的作业数据', res.rows)
for (var t = 0; t < classWorkList.value.length; t++) {
for (var i = 0; i < res.rows.length; i++) {
if (res.rows[i].classworkid == classWorkList.value[t].id) {
// if (res.rows[i].classworkid == classWorkList.value[t].id && res.rows[i].resultcount > 0) {
console.log('==================')
// /
// resultcount0
classWorkList.value[t].workdatafeedbackcount++
//
classWorkList.value[t].feedtimelength += parseInt(res.rows[i].finishtimelength)
//
if (
res.rows[i].classworkevallist != '' &&
res.rows[i].classworkevallist != null &&
res.rows[i].classworkevallist != 'null'
) {
let replacedString = res.rows[i].classworkevallist.replace(/""/g, '"')
// , : "{\"id\":172907, \"rating\":0, \"teacherRating\":0, \"entpcourseworkid\":358520, \"feedcontent\":\"\", \"score\":4, \"rightanswer\":\"\"},{\"id\":172908, \"rating\":0, \"teacherRating\":0, \"entpcourseworkid\":358521, \"feedcontent\":\"\", \"score\":4, \"rightanswer\":\"\"},{\"id\":172909, \"rating\":0, \"teacherRating\":0, \"entpcourseworkid\":363096, \"feedcontent\":\"\", \"score\":4, \"rightanswer\":\"\"},{\"id\":172910, \"rating\":0, \"teacherRating\":0, \"entpcourseworkid\":363098, \"feedcontent\":\"<bdo class=\"mathjye-underpoint2\"></bdo>\", \"score\":4, \"rightanswer\":\"<bdo class=\"mathjye-underpoint2\"></bdo>\"},{\"id\":172911, \"rating\":0, \"teacherRating\":0, \"entpcourseworkid\":363100, \"feedcontent\":\"<bdo class=\"mathjye-underpoint2\"></bdo>\", \"score\":4, \"rightanswer\":\"<bdo class=\"mathjye-underpoint2\"></bdo>\"}"
replacedString = escapeHtmlQuotes(res.rows[i].classworkevallist).replace(
/"(\[.*\])"/g,
'$1'
)
replacedString = escapeHtmlQuotes(res.rows[i].classworkevallist)
var evalarray
try {
evalarray = JSON.parse('[' + res.rows[i].classworkevallist + ']')
} catch {
evalarray = JSON.parse('[' + replacedString + ']')
}
for (var e = 0; e < evalarray.length; e++) {
if (res.rows[i].worktype == '常规作业') {
evalarray[e].feedcontent = escapeHtmlQuotes(evalarray[e].feedcontent).replace(
/"(\[.*\])"/g,
'$1'
)
evalarray[e].feedcontent = escapeHtmlQuotes(evalarray[e].feedcontent)
}
if (evalarray[e].feedcontent == evalarray[e].rightanswer) {
//
classWorkList.value[t].rightAnswerCount++
}
}
}
}
// workdatacount
if (res.rows[i].classworkid == classWorkList.value[t].id) {
classWorkList.value[t].workdatalist.push(res.rows[i])
}
}
// workdatacount0
if (
classWorkList.value[t].workdataresultcount > 0 &&
classWorkList.value[t].workdatacount > 0
) {
classWorkList.value[t].finishpercent = parseInt(
(classWorkList.value[t].workdataresultcount / classWorkList.value[t].workdatacount) * 100
)
} else {
classWorkList.value[t].finishpercent = 0
}
//
// 2024-04-12by jackyshen
//
if (classWorkList.value[t].workdatafeedbackcount > 0) {
classWorkList.value[t].averagetime =
(
classWorkList.value[t].feedtimelength / classWorkList.value[t].workdatafeedbackcount
).toFixed(0)
} else {
classWorkList.value[t].averagetime = 0
}
//
//
// /**100
if (
classWorkList.value[t].entpcourseworklistarray &&
classWorkList.value[t].entpcourseworklistarray.length > 0
) {
var dd =
(classWorkList.value[t].rightAnswerCount /
(classWorkList.value[t].entpcourseworklistarray.length *
classWorkList.value[t].workdatacount)) *
100
classWorkList.value[t].scoingRate = dd.toFixed(0) + '%'
} else {
classWorkList.value[t].scoingRate = '0%'
}
//
//
}
})
}
watch(
() => [dataList, toolStore.isToolWin],
() => {
console.log('====', toolStore)
setTimeout(() => {
getData() //
}, 300)
}
)
</script>
<style scoped lang="scss">
.class-reserv-wrap {
height: 100%;
display: flex;
flex-direction: column;
padding: 15px 30px;
.class-reserv-tabs {
width: 30%;
text-align: left;
}
.class-reserv-body {
height: 100%;
flex: 1;
overflow: auto;
padding: 10px 0;
}
}
</style>

View File

@ -0,0 +1,964 @@
<template>
<el-form ref="classWorkFormScoreRef" :model="classWorkFormScore">
<div class="teacher_content" :style="{ height: dialogProps.maxheight + 'px' }">
<div style="font-size: 18px; width: 100%; padding: 5px 10px" class="sticky">
{{ classWorkFormScore.name }} 答题详情
</div>
<div class="teacher_content_con">
<!-- 题目内容习题训练 -->
<div v-if="dialogProps.studentObj.worktype == '习题训练'">
<div v-for="(stuItem, sIndex) in dialogProps.studentQuizAllList" :key="stuItem.id">
<div v-for="quItem in dialogProps.quizlist" :key="quItem.id">
<div v-if="stuItem.entpcourseworkid == quItem.id">
<el-card style="max-width: 100%; margin-bottom: 10px">
<!-- 题型 分值 -->
<template #header>
<div class="card-header">
<span
>{{ sIndex + 1 }}{{ quItem.worktype }}
{{ stuItem.score ? stuItem.score : 0 }}</span
>
</div>
</template>
<!-- 习题训练 -->
<div v-if="dialogProps.studentObj.worktype == '习题训练'">
<el-row>
<el-col :span="24" style="padding: 10px">
<!-- 题源题目标题题目选项 -->
<span>{{ quItem.worktag }}</span>
<span style="margin-left: 4px" v-html="quItem.titleFormat"></span>
<div :span="24" style="padding: 10px" v-html="quItem.workdescFormat"></div>
<!-- 折叠 详情分析解答 -->
<div class="demo-collapse">
<el-collapse>
<el-collapse-item title="详情分析解答" name="1">
<el-row style="padding: 1% 4%; border: 2px dotted">
<template #default="scope">
<el-col :span="2" style="padding: 10px 0px"
><em>答案</em></el-col
>
<el-col
:span="21"
style="padding: 10px 0px"
v-html="quItem.workanswerFormat"
></el-col>
<el-col :span="2" style="padding: 10px 0px"
><em>分析</em></el-col
>
<el-col
:span="21"
style="padding: 10px 0px"
v-html="quItem.method"
></el-col>
<el-col :span="2" style="padding: 10px 0px"
><em>解答</em></el-col
>
<el-col
:span="21"
style="padding: 10px 0px"
v-html="quItem.analyse"
></el-col>
<el-col :span="2" style="padding: 10px 0px"
><em>点评</em></el-col
>
<el-col
:span="21"
style="padding: 10px 0px"
v-html="quItem.discuss"
></el-col>
</template>
</el-row>
</el-collapse-item>
</el-collapse>
</div>
</el-col>
</el-row>
</div>
<!-- 答案 -->
<template #footer>
<el-row>
<el-col :span="6" style="padding: 10px">
<span
>参考答案
<span v-if="quItem.workanswerFormat != ''">
<sapn
style="
background-color: #0ed116;
color: white;
padding: 0 5px;
border-radius: 5px;
"
>{{ quItem.workanswerFormat.replace(/<[^>]+>/g, '') }}</sapn
>
</span>
</span>
</el-col>
<el-col :span="6" style="padding: 10px">
<!-- <span>学生答案{{ stuItem.feedcontent }}</span> -->
<span
>学生答案
<span v-if="quItem.workdesc == ''">
<span
v-if="stuItem.feedcontent != ''"
style="
background-color: red;
color: white;
padding: 0 5px;
border-radius: 5px;
"
>
{{ stuItem.feedcontent.replace('#', '、') }}
</span>
</span>
<span v-else>
<span
v-if="stuItem.feedcontent != ''"
style="
background-color: red;
color: white;
padding: 0 5px;
border-radius: 5px;
"
>
{{
JSON.parse(quItem.workdesc)
.map((item, index) => {
if (item == stuItem.feedcontent) {
return String.fromCharCode(65 + Number(index))
}
})
.filter(Boolean)[0]
}}
</span>
</span>
</span>
</el-col>
<el-col :span="6" style="padding: 10px">
<div
v-for="(imageItem, index) in stuItem.imagefile"
v-if="stuItem.imagefile && stuItem.imagefile.length > 0"
:key="index"
>
<el-image
style="width: 30px; height: 30px"
:src="imageItem"
:zoom-rate="1.2"
:max-scale="7"
:min-scale="0.2"
:preview-src-list="stuItem.imagefile"
:initial-index="4"
/>
</div>
</el-col>
<el-col :span="6" style="padding: 10px">
<!-- 单选题 填空题 多选题 判断题 主观题 复合题 待完善
1目前只支持单选题多选题不用老师批改分数这里先禁止分值修改 -->
<el-input-number
v-model="classWorkFormScore.teacherRating[sIndex].score"
:min="0"
:max="classWorkFormScore.teacherRating[sIndex].maxScore"
size="small"
:disabled="
(quItem.worktype == '单选题' || quItem.worktype == '多选题') &&
stuItem.feedcontent == stuItem.rightanswer &&
stuItem.feedcontent != ''
? true
: false
"
></el-input-number>
</el-col>
</el-row>
</template>
</el-card>
</div>
</div>
</div>
</div>
<!-- 题目内容常规作业课堂展示 -->
<div
v-if="
dialogProps.studentObj.worktype == '常规作业' ||
dialogProps.studentObj.worktype == '课堂展示' ||
dialogProps.studentObj.worktype == '框架梳理'
"
>
<div v-for="(stuItem, sIndex) in dialogProps.studentQuizAllList" :key="stuItem.id">
<el-card style="max-width: 100%; margin-bottom: 10px">
<!-- 题型 分值 -->
<template #header>
<div class="card-header">
<span>{{ sIndex + 1 }}{{ stuItem.score ? stuItem.score : 0 }}</span>
</div>
</template>
<!-- 常规作业 -->
<div
v-if="
dialogProps.studentObj.worktype == '常规作业' ||
dialogProps.studentObj.worktype == '课堂展示' ||
dialogProps.studentObj.worktype == '框架梳理'
"
>
<!-- 文件内容格式mp3/mp4/doc/docx/excel/pdf/ppt/pptx/jpg/jpeg/gif/png/txt ->
<-- 老师附件展示 -->
<!-- 折叠 详情分析解答 -->
<div v-if="teacherFeedContentList.length > 0">
<div class="demo-collapse" style="border: 2px dotted">
<el-collapse>
<el-collapse-item title="老师布置详情" name="1">
<div class="image_list">
<div v-if="teachImageList.length > 0">
<div style="margin-bottom: 5px">
<span style="color: red">温馨提示点击图片可放大预览 </span>
</div>
<div v-for="(imageItem, index) in teachImageList" :key="index">
<el-image
style="width: 400px; height: 400px"
:src="imageItem.url"
:zoom-rate="1.2"
:max-scale="7"
:min-scale="0.2"
:preview-src-list="
teachImageList
.filter(
(item) =>
item.name.indexOf('jpg') > -1 ||
item.name.indexOf('jpeg') > -1 ||
item.name.indexOf('png') > -1
)
.map((item) => item.url)
"
:initial-index="4"
/>
</div>
</div>
<div v-if="teachFileList.length > 0">
<div style="margin: 10px 0">
<span style="color: red" @click="openFile"
>温馨提示点击此处 可预览其他类型附件
</span>
</div>
</div>
</div>
</el-collapse-item>
</el-collapse>
</div>
</div>
<!-- 学生答题展示 -->
<div v-if="feedContentList.length > 0">
<p>学生答题附件内容</p>
<div class="image_list">
<div v-if="imageList.length > 0">
<div style="margin-bottom: 5px">
<span style="color: red">温馨提示点击图片可放大预览 </span>
</div>
<div v-for="(imageItem, index) in imageList" :key="index">
<el-image
style="width: 500px; height: 500px"
:src="imageItem.url"
:zoom-rate="1.2"
:max-scale="7"
:min-scale="0.2"
:preview-src-list="
imageList
.filter(
(item) =>
item.name.indexOf('jpg') > -1 ||
item.name.indexOf('jpeg') > -1 ||
item.name.indexOf('png') > -1
)
.map((item) => item.url)
"
:initial-index="4"
/>
</div>
</div>
<div v-if="fileList.length > 0">
<div style="margin: 10px 0">
<span style="color: red" @click="openFile"
>温馨提示点击此处 可预览其他类型附件
</span>
</div>
</div>
</div>
</div>
<div v-else>
<el-empty
description="该学生还未作答"
style="width: 100%; height: 200px"
></el-empty>
</div>
</div>
<!-- 答案 -->
<!-- <template #footer>
<el-row>
<el-col :span="12" style="padding: 10px">
<el-input-number v-model="classWorkFormScore.teacherRating[sIndex].score"
:controls="false"
:min="0"
:max="classWorkFormScore.teacherRating[sIndex].maxScore"
size="small"
></el-input-number>
</el-col>
</el-row>
</template> -->
</el-card>
</div>
</div>
</div>
<!-- 批改评价与评语 -->
<div class="tacher_conten_foot">
<el-row style="padding: 1% 4%; border: 2px dotted">
<el-col :span="24" style="display: flex; flex-direction: column">
<el-row>
<el-col :span="14">
<div style="display: flex; margin: 10px auto">
<span style="display: flex; align-items: center">
<span v-if="dialogProps.studentObj.worktype == '习题训练'">
得分<span style="margin: 0; color: red">{{
classWorkFormScore.teacherRating.reduce((a, b) => a + b.score, 0).toFixed(2)
}}</span
>
</span>
<span v-else>
得分
<span v-if="classWorkFormScore.teacherRating.length > 0">
<el-input-number
v-model="classWorkFormScore.teacherRating[0].score"
:controls="false"
type="number"
:min="0"
:max="classWorkFormScore.teacherRating[0].maxScore"
size="small"
style="width: 60px"
@change="handleChange"
></el-input-number>
</span>
</span>
</span>
<div class="score-container">
<div
v-for="(score, index) in teacherRatingList"
:key="index"
:class="[
'score-circle',
{ active: classWorkFormScore.rating == score.ratingKey }
]"
@click="selectScore(score)"
>
{{ score.ratingValue }}
</div>
</div>
</div>
</el-col>
<el-col :span="10" style="display: flex; align-items: center">
<el-select
v-model="value"
placeholder="常用评语"
style="width: 240px"
@change="onSelectOption"
>
<el-option
v-for="item in cities"
:key="item.value"
:label="item.label"
:value="item.value"
/>
<template #footer>
<el-button v-if="!isAdding" text bg size="small" @click="onAddOption">
新增常用语
</el-button>
<template v-else>
<el-input
v-model="optionName"
class="option-input"
placeholder="输入新的常用语"
size="small"
/>
<el-button type="primary" size="small" @click="onConfirm"> 确定 </el-button>
<el-button size="small" @click="clear">取消</el-button>
</template>
</template>
</el-select>
</el-col>
</el-row>
</el-col>
<el-col :span="24" style="display: flex; flex-direction: column">
<el-form-item label="评语说明">
<el-col :span="15" style="padding: 0px">
<el-input
v-model="classWorkFormScore.teacherremark"
type="textarea"
rows="3"
placeholder="请输入评语说明"
/>
</el-col>
</el-form-item>
</el-col>
</el-row>
<div style="margin: 10px">
<el-button type="primary" @click="onClassWorkFormScoreSave">批阅确认</el-button>
</div>
</div>
</div>
<el-dialog
v-model="fileReadopen"
title="文件预览"
width="80%"
:style="{ height: dialogProps.maxheight + 'px' }"
append-to-body
>
<div class="file-read-dialog">
<div>
<!-- 老师附件 -->
<div v-if="teachFileList.length > 0">
<el-card style="max-width: 480px">
<template #header>
<div class="card-header">
<span>老师文件列表</span>
</div>
</template>
<div
v-for="item in teachFileList"
:key="item"
style="margin: 10px; display: flex; align-items: center"
>
<span style="margin-right: 10px">{{ item.name }}</span>
<el-button type="primary" @click="onFileRead(item)">预览</el-button>
</div>
</el-card>
</div>
<!-- 学生附件 -->
<div v-if="fileList.length > 0">
<el-card style="max-width: 480px">
<template #header>
<div class="card-header">
<span>学生文件列表</span>
</div>
</template>
<div
v-for="item in fileList"
:key="item"
style="margin: 10px; display: flex; align-items: center"
>
<span style="margin-right: 10px">{{ item.name }}</span>
<el-button type="primary" @click="onFileRead(item)">预览</el-button>
</div>
</el-card>
</div>
</div>
<div style="width: 100%" :style="{ height: dialogProps.maxheight + 'px' }">
<div style="margin-left: 10px">
预览展示区域<span style="color: red; margin-left: 10px">
温馨提示若预览失败<span style="margin-left: 10px">{{ props.name }}</span
>可点击此处<a
:href="fileitem.url ? fileitem.url : ''"
target="_blank"
style="color: blue"
>下载</a
></span
>
</div>
<!-- <ReFilePreview
:name="fileitem.name"
:type="fileitem.type"
:file-type="fileitem.type"
:file-path="fileitem.url"
:text-content="textContent"
/> -->
</div>
</div>
</el-dialog>
</el-form>
</template>
<script setup name="classWorkAnalysisScoreDialogRef">
import { useRouter, useRoute } from 'vue-router'
// import useAppStore from '@/store/modules/app'
import useUserStore from '@/store/modules/user'
import { ref, reactive } from 'vue'
// import { Plus } from '@element-plus/icons-vue'
import { ElMessageBox, ElMessage } from 'element-plus'
import { updateClassworkeval } from '@/api/classTask'
import { getTimeDate } from '@/utils/date'
// import ReFilePreview from '@/components/ReFilePreview/index.vue'
// import mammoth from 'mammoth'
// import * as XLSX from 'xlsx'
const userStore = useUserStore()
const router = useRouter()
const route = useRoute()
const emits = defineEmits(['class_work_score_submit'])
const props = defineProps({})
const fileReadopen = ref(false)
const analysisScoreOpen = ref(false)
const dialogProps = ref({
maxheight: 300,
studentObj: {}, //
studentQuizAllList: [], //list
quizlist: [] // list
})
//list
const feedContentList = ref([])
const imageList = ref([])
const fileList = ref([])
// list
const teacherFeedContentList = ref([])
const teachImageList = ref([])
const teachFileList = ref([])
//
const classWorkFormScore = reactive({
name: '', //
score: 0, //
teacherRating: [], // list
rating: 0, // 1- 2- 3- 4- 5-
teacherremark: '' //
})
const teacherRatingList = ref([
{ ratingKey: '1', ratingValue: '优' },
{ ratingKey: '2', ratingValue: '优-' },
{ ratingKey: '3', ratingValue: '良' },
{ ratingKey: '4', ratingValue: '良-' },
{ ratingKey: '5', ratingValue: '差' }
])
// 线
//#region
const fileitem = reactive({
name: '',
type: '',
url: ''
})
//
const onFileRead = (file) => {
textContent.value = '1'
//
fileitem.type = file.name.split('.').pop().toLowerCase()
fileitem.url = file.url
fileitem.name = file.name
// txt
if (fileitem.type == 'txt') {
loadFileTextContent(fileitem.url)
}
}
const openFile = () => {
console.log('打开文件弹窗!')
fileReadopen.value = true
}
// txt
const textContent = ref('')
const loadFileTextContent = async (url) => {
try {
const response = await fetch(url)
if (!response.ok) {
textContent.value = '文件读取失败,您可以点击上方链接跳到另外页面查看'
throw new Error('文件读取失败')
}
textContent.value = await response.text()
} catch (error) {
console.error('读取文件时出错:', error)
textContent.value = '文件读取失败,您可以点击上方链接跳到另外页面查看'
}
}
//#endregion
// e
// e valuenull
const handleChange = (value) => {
console.log(value, '=====')
console.log(typeof value, '===value==')
// if ((value+"").includes('e')) {
// classWorkFormScore.teacherRating[0].score = value.replace('e', '');
// }
if (typeof value === 'object') {
console.log('??????')
classWorkFormScore.teacherRating[0].score = 0
} else {
classWorkFormScore.teacherRating[0].score = value
}
}
//#region
const isAdding = ref(false)
const value = ref([])
const optionName = ref('')
const cities = ref([
{
value: '需要继续加油努力哟!棒棒哒!',
label: '需要继续加油努力哟!棒棒哒!'
},
{
value: '做得很好',
label: '做得很好'
},
{
value: '需要改进',
label: '需要改进'
},
{
value: '需要更多练习',
label: '需要更多练习'
},
{
value: '需要更仔细',
label: '需要更仔细'
},
{
value: '需要更清晰',
label: '需要更清晰'
},
{
value: '需要更准确',
label: '需要更准确'
},
{
value: '需要更详细',
label: '需要更详细'
},
{
value: '需要更简洁',
label: '需要更简洁'
},
{
value: '需要更有条理',
label: '需要更有条理'
},
{
value: '需要更有创意',
label: '需要更有创意'
}
])
const onSelectOption = (option) => {
classWorkFormScore.teacherremark = option
}
const onAddOption = () => {
isAdding.value = true
}
const onConfirm = () => {
if (optionName.value) {
cities.value.push({
label: optionName.value,
value: optionName.value
})
clear()
}
}
const clear = () => {
optionName.value = ''
isAdding.value = false
}
//#endregion
const selectScore = (score) => {
console.log(score, 'score----')
classWorkFormScore.rating = score.ratingKey
}
//
const acceptParams = (params) => {
console.log(params)
console.log(dialogProps, 'dialogProps')
//
//
classWorkFormScore.name = '' //
classWorkFormScore.score = 0 //
classWorkFormScore.teacherRating = [] //
classWorkFormScore.rating = 0 // 1- 2- 3- 4- 5-
classWorkFormScore.teacherremark = '' //
value.value = '' //
//
feedContentList.value = []
imageList.value = []
fileList.value = []
//
teacherFeedContentList.value = []
teachImageList.value = []
teachFileList.value = []
// -----------------
dialogProps.value = params
classWorkFormScore.name = params.studentObj.studentname
//
if (params.studentQuizAllList.length > 0) {
if (params.studentObj.worktype == '习题训练') {
params.studentQuizAllList.forEach((item) => {
params.quizlist.forEach((item2) => {
//
if (item.entpcourseworkid == item2.id) {
//
classWorkFormScore.teacherRating.push({
maxScore: item.score ? item.score : 100, // 100
score:
(item2.worktype == '单选题' || item2.worktype == '多选题') &&
item.feedcontent == item.rightanswer
? item.score
: item.teacherRating
? item.teacherRating
: 0, // :
id: item.id // id
})
classWorkFormScore.teacherremark = item.teacherremark //
// imagefile [null] "[\"https://wzyzoss.ee2f687.png\"]"
if (item.imagefile != '') {
// !
const filelist = JSON.parse(item.imagefile)
console.log(filelist, '学生习题附带图片fimagefile-------------')
if (filelist && filelist.length > 0 && filelist[0] != null) {
item.imagefile = filelist
} else {
item.imagefile = ''
}
}
}
})
})
} else {
//
if (params.studentObj.worktype == '常规作业') {
try {
// datacontent TODO
if (params.studentObj.datacontent != '') {
const teachWorkFileList = JSON.parse(params.studentObj.datacontent)
console.log(teachWorkFileList, '老师filelist-------------')
teachWorkFileList &&
teachWorkFileList.forEach((item) => {
if (
item.name.indexOf('jpg') > -1 ||
item.name.indexOf('jpeg') > -1 ||
item.name.indexOf('png') > -1
) {
teachImageList.value.push(item)
} else {
teachFileList.value.push(item)
}
})
teacherFeedContentList.value.push(teachWorkFileList)
}
dialogProps.value.studentObj.datacontent = dialogProps.value.studentObj.datacontent
} catch (error) {
console.error('Invalid JSON:', error)
}
params.studentQuizAllList.forEach((item) => {
classWorkFormScore.teacherRating.push({
maxScore: item.score ? item.score : 100, // 100
score: item.teacherRating, // :
id: item.id // id
})
classWorkFormScore.teacherremark = item.teacherremark //
if (item.feedcontent != '') {
const filelist = JSON.parse(item.feedcontent)
console.log(filelist, '学生filelist-------------')
// feedcontent
filelist &&
filelist.forEach((item) => {
if (
item.name.indexOf('jpg') > -1 ||
item.name.indexOf('jpeg') > -1 ||
item.name.indexOf('png') > -1
) {
imageList.value.push(item)
} else {
fileList.value.push(item)
}
})
feedContentList.value.push(filelist)
}
})
} else if (
params.studentObj.worktype == '课堂展示' ||
params.studentObj.worktype == '框架梳理'
) {
params.studentQuizAllList.forEach((item) => {
classWorkFormScore.teacherRating.push({
maxScore: item.score ? item.score : 100, // 100
score: item.teacherRating, // :
id: item.id // id
})
classWorkFormScore.teacherremark = item.teacherremark //
// rightanswer"https://wzyzoss.eos-chongqing-3.cmecloud.cn/2024/9/5/c5d8e00a93364dd3b975f669afa217f9.png"
//
console.log(item.rightanswer, '----------课堂展示学生答题------------------')
if (item.rightanswer != '' && item.rightanswer != null) {
if (
item.rightanswer.indexOf('jpg') > -1 ||
item.rightanswer.indexOf('jpeg') > -1 ||
item.rightanswer.indexOf('png') > -1
) {
const imgeobj = {
name: item.rightanswer,
url: item.rightanswer
}
// list
imageList.value.push(imgeobj)
//
//fileList.value.push(?);
feedContentList.value.push(imgeobj)
}
}
})
} else {
//
}
}
// 0
console.log(params.studentQuizAllList[0].rating, '----------------------------')
// null 0 0
classWorkFormScore.rating =
params.studentQuizAllList[0].rating == 0 ? 0 : params.studentQuizAllList[0].rating
}
analysisScoreOpen.value = true
}
//
const onClassWorkFormScoreSave = () => {
console.log(classWorkFormScore)
// rating 0
if (dialogProps.value.studentQuizAllList && dialogProps.value.studentQuizAllList[0].rating != 0) {
ElMessageBox.confirm(`该学生已批改,再次批改会覆盖之前的评分!`, '温馨提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
draggable: true
})
.then(() => {
onSubmit()
})
.catch(() => {})
} else {
onSubmit()
}
}
const onSubmit = () => {
if (classWorkFormScore.rating == 0) {
ElMessage({
type: 'error',
message: '请您给学生一个评价吧!'
})
return
}
classWorkFormScore.teacherRating &&
classWorkFormScore.teacherRating.map((item, index) => {
const queryParams = {
id: item.id,
teacherRating: item.score, //
rating: classWorkFormScore.rating, //
teacherremark: classWorkFormScore.teacherremark, //
timestamp: getTimeDate() //
}
console.log(queryParams)
updateClassworkeval(queryParams).then((res) => {
// if(res.code == 200){
//
// }
})
})
ElMessage({
type: 'success',
message: '提交成功!'
})
analysisScoreOpen.value = false
//
emits('class_work_score_submit')
}
//
// onMounted(() => {})
// ()
defineExpose({
acceptParams
})
</script>
<style scoped>
.teacher_content {
display: flex;
flex-direction: column;
justify-content: space-around;
align-items: center;
overflow: hidden;
}
.teacher_content_con {
flex: 1;
width: 100%;
height: 100%;
overflow-y: auto;
}
.tacher_conten_foot {
width: 100%;
}
.image_list {
display: flex;
flex-wrap: nowrap;
flex-direction: column;
/* justify-content: space-evenly; */
}
/* .el-dialog__body{
padding: 0!important;
height: 100%;
} */
.file-read-dialog {
width: 100%;
height: 100%;
display: flex;
flex-direction: row;
justify-content: flex-start;
align-items: flex-start;
}
.score-container {
display: flex;
justify-content: center;
align-items: center;
/* margin-bottom: 20px; */
}
.score-circle {
width: 30px;
height: 30px;
border-radius: 50%;
background-color: pink;
color: red;
display: flex;
justify-content: center;
align-items: center;
font-size: 13px;
margin: 0 10px;
cursor: pointer;
transition: background-color 0.3s;
}
.score-circle.active {
background-color: red;
color: white;
}
</style>

View File

@ -0,0 +1,616 @@
<template>
<el-dialog
v-model="classWorkAnalysis.open"
:modal-append-to-body="false"
class="clwk_dialog"
style="width: 90%; height: 85%"
:show-close="false"
top="8vh"
append-to-body
destory-on-close
>
<template #title>
<div style="font-size: 18px; display: flex; flex-wrap: nowrap">
<div style="flex: 1">
{{ classWorkAnalysis.title }}答题情况
<el-tag :type="classWorkAnalysis.workclass" size="large" style="height: 25px">{{
classWorkAnalysis.worktype
}}</el-tag>
</div>
<!-- classWorkAnalysis.entpcourseworklistarray 当前学习任务所包含的试题ID -->
<el-row
v-if="classWorkAnalysis.entpcourseworklistarray.length > 0"
style="margin: 0 auto; flex: 1"
>
<el-button-group style="margin-bottom: 10px">
<el-button
:type="classWorkAnalysis.view == 'studentview' ? 'success' : ''"
@click="classWorkAnalysis.view = 'studentview'"
>作业批阅</el-button
>
<el-button
v-if="classWorkAnalysis.row.worktype == '习题训练'"
:type="classWorkAnalysis.view == 'quizStats' ? 'success' : ''"
@click="workHandle('quizStats')"
>作业概况</el-button
>
<el-button
v-if="classWorkAnalysis.row.worktype == '习题训练'"
:type="classWorkAnalysis.view == 'report' ? 'success' : ''"
@click="handleClassOverviewOpen('report')"
>作业报告</el-button
>
</el-button-group>
</el-row>
<div style="flex: 1">
<div
style="float: right; padding: 0 10px; cursor: pointer"
icon="el-icon-close"
@click="classWorkAnalysis.open = false"
>
x
</div>
</div>
</div>
</template>
<!-- 如果当前学习没有试题 -->
<div
v-if="classWorkAnalysis.view == 'studentview'"
style="width: 100%"
class="clwk_dialog_view"
:height="mainHeight"
>
<div class="view_table">
<el-radio-group
v-model="tableRadio.value"
style="margin-bottom: 10px"
@change="tableRadioChange"
>
<el-radio-button :value="1" :label="'已交' + '' + tableRadio.num1 + ''" />
<el-radio-button :value="0" :label="'未交' + '' + tableRadio.num0 + ''" />
</el-radio-group>
<!-- 学生列表classWorkAnalysis.classworkdata; 已交未交tableRadio.list -->
<el-table
v-loading="loading_dt_table"
:data="tableRadio.list"
row-key="id"
highlight-current-row
@row-click="getStudentClassWorkDataDetail"
>
<el-table-column type="index" label="序号" width="52" reserve-selection align="center" />
<el-table-column label="姓名" prop="studentname" width="100" align="center" />
<el-table-column label="提交时间" prop="updatedate" width="170" align="center" />
<el-table-column label="批阅状态" prop="teacherRating" align="center" width="120" sortable>
<template #default="scope">
<template v-if="scope.row.teacherRating == 0"
><span style="color: #2196f3">待批阅</span></template
>
<!-- 1- 2-优减 3- 4-良减 5- -->
<template v-if="scope.row.teacherRating == 1"
><el-tag type="danger"></el-tag></template
>
<template v-if="scope.row.teacherRating == 2"
><el-tag type="danger">-</el-tag></template
>
<template v-if="scope.row.teacherRating == 3"
><el-tag type="warning"></el-tag></template
>
<template v-if="scope.row.teacherRating == 4"
><el-tag type="info">-</el-tag></template
>
<template v-if="scope.row.teacherRating == 5"
><el-tag type="info"></el-tag></template
>
</template>
</el-table-column>
</el-table>
</div>
<div class="view_teachrting">
<div class="classwork-score">
<div v-if="classWorkAnalysis.activeStudentQuizlist.length == 0">
<el-empty
description="点击左侧表格学生信息可查看批阅详情"
style="width: 100%; height: 500px"
></el-empty>
</div>
<div v-else>
<div v-if="isopen_dtwk_table">
<div v-show="classWorkAnalysis.activeStudentQuizlist.length > 0">
<item-dialog-score
ref="classWorkAnalysisScoreDialogRef"
@class_work_score_submit="onClassWorkScoreSubmit"
/>
</div>
</div>
<div v-else>
<el-empty
description="点击左侧表格学生信息可查看批阅详情"
style="width: 100%; height: 500px"
></el-empty>
</div>
</div>
</div>
</div>
</div>
<!-- 作业概况 -->
<div v-else-if="classWorkAnalysis.view == 'quizStats'">
<!-- <quiz-stats :active-data="classWorkActiveData" /> -->
</div>
<!-- 作业报告-->
<div v-else-if="classWorkAnalysis.view == 'report'" style="overflow-y: scroll">
<!-- <ClassOverview :table-list="overviewData" :eval-id="courseObj.evalid"></ClassOverview> -->
</div>
<!-- <template #footer>
<div class="dialog-footer">
<el-button type="primary" @click="classWorkAnalysis.open=false"> </el-button>
</div>
</template> -->
</el-dialog>
</template>
<script setup>
import { ref, defineExpose, onMounted, reactive, computed, watch, nextTick, getCurrentInstance } from 'vue'
import { addSmartClassReserv, updateSmartClassReserv, listClassmain } from '@/api/classManage'
import { listClassworkdata, listEntpcoursework, listClassworkeval } from '@/api/classTask'
import useUserStore from '@/store/modules/user'
import { ElMessage } from 'element-plus'
import { getCurrentTime, getAfterMinutes } from '@/utils/date'
import { processList } from '@/hooks/useProcessList'
import ItemDialogScore from '@/views/classTask/container/item-dialog-score.vue'
const { proxy } = getCurrentInstance()
const emit = defineEmits(['addSuccess'])
const props = defineProps({
bookId: {
type: Number,
default: 0
}
})
const mainHeight = ref(document.documentElement.clientHeight - 110)
const classWorkAnalysis = reactive({
open: false
})
const tableRadio = reactive({
value: '1', //
list: [], // list
num1: 0, //
num0: 0 //
}) //
const loading_dt_table = ref(false)
const isopen_dtwk_table = ref(false)
// zdg:
const classWorkActiveData = reactive({
quizlist: [], //
studentList: [], // -
workFeedList: [], // -
timerId: 0 // id
})
//-
const classWorkAnalysisScore = reactive({
studentObj: {}, //
studentQuizAllList: [], // list
quizlist: [] // list
})
// watch(
// // () => props.currentNode,
// (newValue, oldValue) => {
// form.name = newValue.label
// }
// )
const openDialog = (data) => {
console.log(data, '点击的item答题情况')
classWorkAnalysis.title = data.uniquekey ? data.uniquekey + '--' : ''
classWorkAnalysis.worktype = data.worktype
classWorkAnalysis.workclass = data.workclass
//
tableRadio.list = []
tableRadio.value = '1'
tableRadio.num0 = 0
tableRadio.num1 = 0
classWorkAnalysis.open = true
//
classWorkAnalysis.view = 'studentview'
// ID
classWorkAnalysis.entpcourseworklistarray = data.entpcourseworklistarray
//
classWorkAnalysis.activeStudentQuizlist = []
//
classWorkAnalysis.activeQuizAnalysisData = []
classWorkAnalysis.row = data
window.test = this
// zdg:
const studentArr = data.classworkdatastudentids
? JSON.parse(`[${data.classworkdatastudentids}]`)
: []
classWorkActiveData.studentList = studentArr
/** 学生完成情况分析--获取作业学生list数据 */
getClassWorkStudentList(data.id)
// idlist
var ids = []
for (var i = 0; i < data.entpcourseworklistarray.length; i++) {
ids.push(data.entpcourseworklistarray[i].id)
}
//
listEntpcoursework({ ids: ids.join(','), pageSize: 500 }).then((idres) => {
for (var i = 0; i < idres.rows.length; i++) {
// // + .replace(/!@#\$%/g,'')
idres.rows[i].titletext = idres.rows[i].title.replace(/!@#\$%/g, '')
}
classWorkAnalysis.quizlist = idres.rows
classWorkActiveData.quizlist = idres.rows // zdg: 使
//
// + , pageSize: 100
listClassworkeval({ workid: data.id, pageSize: 1000 }).then((wevalres) => {
for (var i = 0; i < classWorkAnalysis.quizlist.length; i++) {
//
var scoingCount = 0
var feedcount = 0
//
var evalCount = 0
for (var w = 0; w < wevalres.rows.length; w++) {
if (wevalres.rows[w].entpcourseworkid == classWorkAnalysis.quizlist[i].id) {
evalCount++
//
if (wevalres.rows[w].feedcontent != '') {
//
feedcount++
//
if (wevalres.rows[w].feedcontent == wevalres.rows[w].rightanswer) {
wevalres.rows[w].scoingStatus = true
scoingCount++
// =
wevalres.rows[w].teacherRating = wevalres.rows[w].score
} else {
wevalres.rows[w].scoingStatus = false
}
}
}
}
classWorkAnalysis.quizlist[i].evalCount = evalCount
//
classWorkAnalysis.quizlist[i].feedcount = feedcount
// NaN% scoingRate
if (scoingCount == 0 && feedcount == 0) {
classWorkAnalysis.quizlist[i].scoingRate = '0%'
} else {
classWorkAnalysis.quizlist[i].scoingRate =
((scoingCount / feedcount) * 100).toFixed(0) + '%'
}
}
// zdg:
const getStudentid = (workdataid) => {
// id
const classworkdata = (classWorkAnalysis.classworkdata || []).find(
(o) => o.id === workdataid
)
return classworkdata ? classworkdata.studentid : ''
}
wevalres.rows.forEach((o) => {
o.studentid = getStudentid(o.workdataid)
})
classWorkActiveData.workFeedList = wevalres.rows
})
})
console.log(classWorkAnalysis, '点击进度后获得的数据')
}
//#region
/** 1、获取作业学生列表 */
const getClassWorkStudentList = (rowId) => {
// rowid使
localStorage.setItem('activeClassWorkRowId', rowId)
//
classWorkAnalysis.classworkdata = []
// _
loading_dt_table.value = true
// classworkdata
listClassworkdata({ classworkid: rowId, pageSize: 100 })
.then((response) => {
for (var i = 0; i < response.rows.length; i++) {
if (response.rows[i].entpcourseworklist != '') {
response.rows[i].entpcourseworkarray = JSON.parse(
'[' + response.rows[i].entpcourseworklist + ']'
)
} else {
response.rows[i].entpcourseworkarray = []
}
// 0
response.rows[i].teacherRating = 0
//
if (
response.rows[i].classworkevallist != '' &&
response.rows[i].classworkevallist != null &&
response.rows[i].classworkevallist != 'null'
) {
// , : "{\"id\":172910, \"feedcontent\":\"<bdo class=\"mathjye-underpoint2\"></bdo>\", \"score\":4, \"rightanswer\":\"<bdo class=\"mathjye-underpoint2\"></bdo>\"},{\"id\":172911, \"rating\":0, \"teacherRating\":0, \"entpcourseworkid\":363100, \"feedcontent\":\"<bdo class=\"mathjye-underpoint2\"></bdo>\", \"score\":4, \"rightanswer\":\"<bdo class=\"mathjye-underpoint2\"></bdo>\"}"
// .replace(/"(\[.*\])"/g, '$1'); eg: "feedcontent\":\"[{\"name\":\"Bliss.jpg\",\"url\":\"https://wzyzoss.3b8daa474.jpg\"}]\",
// json .replace(/""/g, '"') eg: """"
response.rows[i].classworkevallist = escapeHtmlQuotes(response.rows[i].classworkevallist)
console.log('学生完成情况分析classworkevallist', response.rows[i].classworkevallist)
const evalarray = JSON.parse('[' + response.rows[i].classworkevallist + ']')
var scoingCount = 0
var feedcount = 0
for (var e = 0; e < evalarray.length; e++) {
if (evalarray[e].feedcontent != '') {
feedcount++
//
if (evalarray[e].feedcontent == evalarray[e].rightanswer) {
scoingCount++
}
}
}
console.log(evalarray, 'evalarray------------------------------------')
if (feedcount > 0) {
// : /*100
response.rows[i].scoingRate = ((scoingCount / feedcount) * 100).toFixed(0) + '%'
} else {
response.rows[i].scoingRate = '0%'
}
// :
if (evalarray[0].rating != '') {
response.rows[i].teacherRating = evalarray[0].rating
}
} else {
response.rows[i].scoingRate = '0%'
}
}
classWorkAnalysis.classworkdata = response.rows
loading_dt_table.value = false
//
tableRadio.list =
classWorkAnalysis.classworkdata &&
classWorkAnalysis.classworkdata.filter((item) => item.resultcount > 0)
tableRadio.value = '1'
tableRadio.num0 = classWorkAnalysis.classworkdata.length - tableRadio.list.length
tableRadio.num1 = tableRadio.list.length
})
.catch(() => {
loading_dt_table.value = false
})
}
/** 2、查看某一个学生的学习任务完成详情*/
const getStudentClassWorkDataDetail = (row) => {
//
// this.classWorkAnalysis.quizlist
console.log(row, '点击了左侧学生')
//
classWorkAnalysisScore.studentObj = row
listClassworkeval({ workdataid: row.id, pageSize: 100 })
.then((wevalres) => {
for (var i = 0; i < classWorkAnalysis.quizlist.length; i++) {
//
for (var w = 0; w < wevalres.rows.length; w++) {
if (wevalres.rows[w].entpcourseworkid == classWorkAnalysis.quizlist[i].id) {
wevalres.rows[w].quiztitle = classWorkAnalysis.quizlist[i].title
wevalres.rows[w].quiztitletext = classWorkAnalysis.quizlist[i].title.replace(
/<[^>]*>/g,
''
)
wevalres.rows[w].score = wevalres.rows[w].score ? wevalres.rows[w].score : 0
// html
wevalres.rows[w].rightanswer =
wevalres.rows[w].rightanswer != '' && wevalres.rows[w].rightanswer != null
? wevalres.rows[w].rightanswer.replace(/<[^>]+>/g, '')
: wevalres.rows[w].rightanswer
// html
wevalres.rows[w].feedcontent =
wevalres.rows[w].feedcontent != '' && wevalres.rows[w].feedcontent != null
? wevalres.rows[w].feedcontent.replace(/<[^>]+>/g, '')
: wevalres.rows[w].feedcontent
if (classWorkAnalysis.row.worktype == '常规作业') {
wevalres.rows[w].feedcontent = JSON.parse(wevalres.rows[w].feedcontent)
}
if (wevalres.rows[w].feedcontent != '') {
if (wevalres.rows[w].feedcontent == wevalres.rows[w].rightanswer) {
wevalres.rows[w].scoingStatus = true
// =
wevalres.rows[w].teacherRating = wevalres.rows[w].score
} else {
wevalres.rows[w].scoingStatus = false
}
} else {
wevalres.rows[w].scoingStatus = ''
}
//
}
// "" prop="feedcontent" width="200" align="center"></el-table-column>
// <el-table-column label="" prop="rightanswer"
// +
wevalres.rows[w].worktitle = wevalres.rows[w].worktitle.replace(/!@#\$%/g, '')
}
}
classWorkAnalysis.activeStudentQuizlist = wevalres.rows
//
isopen_dtwk_table.value = true
//
if (wevalres.rows.length > 0) {
handleClassWorkAnalysissScoreOpen(row)
} else {
ElMessage({
type: 'warning',
message: '未获取到答题信息,请稍后再看,或者联系管理员查看情况!'
})
}
})
.catch(() => {
console.log('获取答题情况失败')
ElMessage({
type: 'warning',
message: '未获取到答题信息!'
})
})
}
/** 3、教师批改后返回的方法*/
const onClassWorkScoreSubmit = () => {
console.log('批改后返回的方法')
loading_dt_table.value = true
isopen_dtwk_table.value = false
// 1table- classWorkAnalysis.classworkdata- classWorkAnalysis.activeStudentQuizlist
// -
classWorkAnalysis.classworkdata = []
classWorkAnalysis.activeStudentQuizlist = []
// 2
const rowid = localStorage.getItem('activeClassWorkRowId')
getClassWorkStudentList(rowid)
}
// ()
const handleClassWorkAnalysissScoreOpen = (row) => {
console.log(row, '所选点击的信息')
// list
classWorkAnalysisScore.studentQuizAllList = classWorkAnalysis.activeStudentQuizlist
// list
classWorkAnalysisScore.quizlist = classWorkAnalysis.quizlist
//
processList(classWorkAnalysisScore.quizlist)
//
classWorkAnalysisScore.maxheight = mainHeight.value - 100
//
nextTick(() => {
proxy.$refs.classWorkAnalysisScoreDialogRef.acceptParams(classWorkAnalysisScore)
})
}
//#endregion
//
const escapeHtmlQuotes = (str) => {
// replace,
return str
//
// return str.replace(/(<[^>]+>)/g, function (match) {
// return match.replace(/"/g, '\\"')
// })
}
defineExpose({
openDialog
})
</script>
<style scoped lang="scss">
// :deep(.reserv-date-pick) {
// width: 140px;
// }
// :deep(.reserv-time-pick) {
// width: 240px;
// }
.clwk_dialog {
.clwk_dialog_view {
display: flex;
flex-direction: row;
justify-content: space-around;
// align-items: center;
overflow: hidden;
}
.view_table {
flex: 1;
height: 100%;
overflow-y: auto;
}
.view_teachrting {
flex: 2;
height: 100%;
overflow-y: auto;
}
}
.clwk_dialog {
display: flex;
justify-content: center;
overflow: hidden;
}
.clwk_dialog .el-dialog {
margin: 0 auto !important;
height: 85%!important;
overflow: hidden;
}
.clwk_dialog .el-dialog__header {
/* position: absolute;
top: 0;
left: 0; */
width: 100%!important;
}
.clwk_dialog .el-dialog__body {
position: absolute;
left: 0;
top: 15px;
bottom: 1px;
right:0;
padding:5px;
z-index:1;
display: flex;
flex-direction: column;
overflow: hidden;
/* overflow:hidden;
overflow-y: auto; */
}
.clwk_dialog .el-dialog__footer{
position: absolute;
bottom: 10px;
right: 10px;
}
.clwk_dialog .classwork-score{
overflow-y: auto;
}
</style>
<style scoped>
.clwk_dialog {
display: flex;
justify-content: center;
overflow: hidden;
}
.clwk_dialog .el-dialog {
margin: 0 auto !important;
height: 85%!important;
overflow: hidden;
}
.clwk_dialog .el-dialog__header {
/* position: absolute;
top: 0;
left: 0; */
width: 100%!important;
}
.clwk_dialog .el-dialog__body {
position: absolute;
left: 0;
top: 15px;
bottom: 1px;
right:0;
padding:5px;
z-index:1;
display: flex;
flex-direction: column;
overflow: hidden;
/* overflow:hidden;
overflow-y: auto; */
}
.clwk_dialog .el-dialog__footer{
position: absolute;
bottom: 10px;
right: 10px;
}
.clwk_dialog .classwork-score{
overflow-y: auto;
}
</style>

View File

@ -0,0 +1,143 @@
<template>
<div class="class-reserv-item">
<div class="class-reserv-item-body">
<div class="class-reserv-item-title1">
<el-tag style="margin-left: 5px" :type="item.workclass"> {{ item.worktype }}</el-tag>
<label style="margin-left: 10px">{{ item.uniquekey }}</label>
</div>
<div class="class-reserv-item-title3">
<!-- <span v-for="(tag, index) in item.classItemList" :key="index" style="margin-left: 5px">
{{ index === 0 ? tag.name : '、' + tag.name }}
</span> -->
<span>{{ item.classcaption }}</span>
&nbsp;|&nbsp; 截止时间{{ item.deaddate }} &nbsp;|&nbsp;{{ tabactive }}
</div>
</div>
<el-switch v-model="value1" active-text="云同步"> </el-switch>
<div class="class-reserv-item-tool">
<span><span style="color: #000fff; font-weight: 900; font-size: 15px">{{ item.workdataresultcount }}</span>/{{ item.workdatacount }}</span>
<span>已交</span>
</div>
<div class="class-reserv-item-tool">
<span style="color: #ff7f00; font-weight: 900; font-size: 15px">2</span>
<span>待批阅</span>
</div>
<div class="class-reserv-item-tool">
<span><span style="color: #007fff; font-weight: 900; font-size: 15px">{{ item.averagetime?item.averagetime:0 }}</span>分钟 </span>
<span>平均用时</span>
</div>
<div class="class-reserv-item-tool">
<span style="color: #ff6ec7; font-weight: 900; font-size: 15px">{{ item.scoingRate }}</span>
<span>得分率</span>
</div>
</div>
</template>
<script setup>
import { useToolState } from '@/store/modules/tool'
import useUserStore from '@/store/modules/user'
import { createWindow } from '@/utils/tool'
import { deleteSmartReserv, startClass, endClass } from '@/api/classManage'
import { ElMessage } from 'element-plus'
import { listEntpcourse } from '@/api/teaching/classwork'
const emit = defineEmits(['openEdit', 'deleteReserv'])
const props = defineProps({
item: {
type: Object,
default: () => {}
},
tabactive: {
type: String,
default: () => ''
}
})
import { ref, reactive } from 'vue'
const value1 = ref(true);
const basePath = import.meta.env.VITE_APP_BUILD_BASE_PATH
const toolStore = useToolState() // -tool
const openEdit = () => {
emit('openEdit', props.item)
}
const deleteReserv = () => {
deleteSmartReserv([props.item.id]).then((res) => {
if (res.data === true) {
ElMessage({
message: '删除成功',
type: 'success'
})
emit('deleteReserv', props.item)
}
})
}
const startClassR = (item) => {
// startClass(item.id).then((res) => {
// if (res.data === true) {
// item.status = ''
// openLesson()
// }
// })
item.status = '上课中'
openLesson()
}
// const toolStore = useToolState()
let wins = null;
// -
const openLesson = () => {
// startClass(props.item.id)
listEntpcourse({
evalid: props.item.ex2,
edituserid: useUserStore().user.userId,
pageSize: 500
}).then(async res=>{
if (res.rows[0].id) {
wins = await createWindow('tool-sphere', { url: '/tool/sphere?entpcourseid=' + res.rows[0].id + "&reservId=" + props.item.id })
}
})
}
const endClassR = (item) => {
endClass(item.id).then((res) => {
if (res.data === true) {
ElMessage({
message: '下课成功',
type: 'success'
})
item.status = '已结束'
}
})
}
</script>
<style scoped lang="scss">
.class-reserv-item {
display: flex;
background-color: white;
border-radius: 10px;
padding: 10px 5px;
margin-bottom: 10px;
.class-reserv-item-body {
flex: 1;
display: flex;
flex-direction: column;
text-align: left;
padding-left: 10px;
font-size: 14px;
.class-reserv-item-title1 {
flex: 1;
display: flex;
flex-direction: row;
align-items: center;
label {
font-size: 20px;
font-weight: bold;
}
}
}
.class-reserv-item-tool {
margin-left: 10px;
display: flex;
align-items: center;
flex-direction: column;
font-size: 14px;
}
}
</style>