baigl #180
|
@ -245,6 +245,8 @@ const getStudentVisible = async () => {
|
|||
} else {
|
||||
classWorkList.value[t].averagetime = 0
|
||||
}
|
||||
// 更新批阅数
|
||||
classWorkList.value[t].teacherrationgcount = curWork.teacherrationgcount
|
||||
} else {
|
||||
classWorkList.value[t].finishpercent = 0
|
||||
}
|
||||
|
@ -256,8 +258,6 @@ const getStudentVisible = async () => {
|
|||
// 获取多个班级学生作业数据
|
||||
const getStudentClassWorkData = () => {
|
||||
// 再查找多个班级里,每个学生的作业数据
|
||||
console.log('======????????""""""""""')
|
||||
//TODO 这里id变动,看后续用什么判断
|
||||
listClassworkdata({
|
||||
classids: classListIds.value.join(','),
|
||||
//entpcourseid: '', // 章节id? 这里要全课程的作业 不分章节? 根据学段学科查询所有的作业
|
||||
|
@ -268,8 +268,8 @@ const getStudentClassWorkData = () => {
|
|||
}).then((res) => {
|
||||
for (var t = 0; t < classWorkList.value.length; t++) {
|
||||
for (var i = 0; i < res.rows.length; i++) {
|
||||
if (res.rows[i].uniquekey == classWorkList.value[t].uniquekey) {
|
||||
// if (res.rows[i].classworkid == classWorkList.value[t].id && res.rows[i].resultcount > 0) {
|
||||
//if (res.rows[i].uniquekey == classWorkList.value[t].uniquekey) {
|
||||
if (res.rows[i].classworkid == classWorkList.value[t].id && res.rows[i].resultcount > 0) {
|
||||
console.log('==================')
|
||||
// 有几个学生完成/正在完成学习任务
|
||||
// 至少resultcount不是0
|
||||
|
|
|
@ -98,7 +98,8 @@
|
|||
<!-- <span>学生答案:{{ stuItem.feedcontent }}</span> -->
|
||||
<span
|
||||
>学生答案:
|
||||
<span v-if="quItem.workdesc == ''">
|
||||
<span v-if="quItem.workdesc == ''|| quItem.workdesc == '[]' ">
|
||||
<!-- quItem.workdesc 没值,说明是非选择题 -->
|
||||
<span
|
||||
v-if="stuItem.feedcontent != ''"
|
||||
style="
|
||||
|
@ -112,6 +113,7 @@
|
|||
</span>
|
||||
</span>
|
||||
<span v-else>
|
||||
<!-- 选择题类型:学生答题转换为 ABCD格式 -->
|
||||
<span
|
||||
v-if="stuItem.feedcontent != ''"
|
||||
style="
|
||||
|
|
|
@ -137,7 +137,7 @@
|
|||
|
||||
<!-- 作业概况 -->
|
||||
<div v-else-if="classWorkAnalysis.view == 'quizStats'">
|
||||
<!-- <quiz-stats :active-data="classWorkActiveData" /> -->
|
||||
<quiz-stats :active-data="classWorkActiveData" />
|
||||
</div>
|
||||
|
||||
<!-- 作业报告-->
|
||||
|
|
|
@ -0,0 +1,278 @@
|
|||
<template>
|
||||
<el-row class="c-warp" :gutter="10">
|
||||
<el-col class="left" :span="16">
|
||||
<el-collapse class="c-item" v-model="activeTopic" accordion>
|
||||
<template v-for="(item, index) in dataList">
|
||||
<el-collapse-item class="collapse-item" :name="index+1" :id="'collapse-'+(index+1)">
|
||||
<template #title>
|
||||
<el-popover :width="500" placement="right">
|
||||
<p>{{item.def?.titletext}}</p>
|
||||
<template #reference>
|
||||
<el-button type="primary" size="small" round>{{index+1}}</el-button>
|
||||
</template>
|
||||
</el-popover>
|
||||
<span class="item-title-o">{{item.type}} {{getRatioTxt(item)}} </span>
|
||||
<el-tag type="success" size="small">{{item.points}}%</el-tag>
|
||||
</template>
|
||||
<div class="respond">
|
||||
<div class="c-label">
|
||||
<b t1>作答情况</b>
|
||||
<span>(已经完成 <el-text type="danger">{{item.accSum}}</el-text> 人)</span>
|
||||
</div>
|
||||
<div class="c-childen">
|
||||
<template v-for="(it, ind) in item.children">
|
||||
<el-collapse v-model="item.active">
|
||||
<el-collapse-item class="collapse-item" :name="ind+1">
|
||||
<template #title>
|
||||
<div class="t-left">
|
||||
<el-tooltip placement="right" :content="it.def">
|
||||
<el-tag size="small" style="vertical-align: 2px;" v-html="it.code"></el-tag>
|
||||
</el-tooltip>
|
||||
<el-text t1>{{it.studentIds.length}} 人/占 {{ratio_1(it, item.accSum)}}%</el-text>
|
||||
</div>
|
||||
<div style="flex: 1;">
|
||||
<el-progress :status="getStatus(it)" :stroke-width="10" :percentage="ratio_1(it, item.accSum)" />
|
||||
</div>
|
||||
</template>
|
||||
<div class="c-respond">
|
||||
<template v-for="(sid, indStu) in it.studentIds">
|
||||
<el-tag>{{getStudentName(sid)}}</el-tag>
|
||||
</template>
|
||||
</div>
|
||||
</el-collapse-item>
|
||||
</el-collapse>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
</el-collapse-item>
|
||||
</template>
|
||||
</el-collapse>
|
||||
</el-col>
|
||||
<el-col class="right" :span="8">
|
||||
<div class="c-item">
|
||||
<div class="title">答题情况</div>
|
||||
<div class="respond">
|
||||
<el-space wrap>
|
||||
<!-- <template v-for="it in 11"> -->
|
||||
<template v-for="(item, index) in dataList">
|
||||
<el-card shadow="hover" class="card-warp">
|
||||
<div class="card-body">
|
||||
<el-progress type="dashboard" :color="colorArr" :width="80" :percentage="ratio_2(item)" />
|
||||
<el-button type="primary" :plain="getActive(index+1)" size="small" round
|
||||
@click="clickInfo(index+1)">{{index+1}}</el-button>
|
||||
</div>
|
||||
</el-card>
|
||||
</template>
|
||||
</el-space>
|
||||
</div>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</template>
|
||||
<script setup>
|
||||
// 功能说明:习题-概况|作业统计(使用:公屏-习题练习|作业管理-作业布置)
|
||||
// === 引入和参数初始化 ===
|
||||
// import { nextTick } from 'vue'
|
||||
|
||||
// import * as elementPlus from 'element-plus' // ElMessage ElMessageBox
|
||||
let colorArr = [] // 进度颜色值 -- 静态数据
|
||||
const attrs = useAttrs() // props中未定义属性
|
||||
const activeTopic = ref(0) // 展开的题
|
||||
let dataList = ref([]) // 左侧数据
|
||||
let studentList = ref([]) // 学生数据
|
||||
const props = defineProps({ // 参数 defineProps
|
||||
activeData: { // 数据
|
||||
type: Object,
|
||||
// required: true, // 必传
|
||||
default: () => ({
|
||||
quizlist: [], // 当前习题列表
|
||||
studentList: [], // 当前课程-所有学生
|
||||
workFeedList: [] // 当前课程-所有学生反馈数据
|
||||
})
|
||||
},
|
||||
})
|
||||
// 左侧数据
|
||||
// dataList.value = [
|
||||
// { id: 1, type: '单选题', points: '47.5', accSum: 18, active: [], children: [
|
||||
// { code: 'A', isOk: false, studentIds: [55056, 55057, 55058]},
|
||||
// ]
|
||||
// },
|
||||
// ]
|
||||
// 颜色值
|
||||
colorArr = [
|
||||
{ color: '#f56c6c', percentage: 20 },
|
||||
{ color: '#e6a23c', percentage: 50 },
|
||||
{ color: '#1989fa', percentage: 80 },
|
||||
{ color: '#5cb87a', percentage: 100 },
|
||||
]
|
||||
|
||||
// === 初始加载完 ===
|
||||
onMounted(() => {})
|
||||
|
||||
// === 方法(methods) ===
|
||||
// 初始-数据处理
|
||||
const initData = () => {
|
||||
// console.log('xxx', props)
|
||||
// window.test = activeCourse
|
||||
studentList.value = props.activeData.studentList || []
|
||||
const activeWorkFeedList = props.activeData.workFeedList || []
|
||||
const quizlist = props.activeData.quizlist || []
|
||||
// 习题特殊处理
|
||||
let data = quizlist.map(o => {
|
||||
// 解析题选项
|
||||
const workdesc = o.workdesc || ''
|
||||
let accSum = 0 // 该题总人数
|
||||
let activeIds = [] // 已做答学生
|
||||
const quizFeedList = activeWorkFeedList.filter(f => f.entpcourseworkid == o.id) // 做该题的列表
|
||||
let children = []
|
||||
if (['单选题','多选题'].includes(o.worktype)) { // '单选题','多选题'
|
||||
const list = workdesc.includes('#&') ? workdesc.split('#&') : isJson(workdesc)?JSON.parse(workdesc):[]
|
||||
children = list.map((v,i) => {
|
||||
const isOne = o.worktype == '单选题'
|
||||
const code = toCode(i) // 转换 A-Z
|
||||
// const isOk = isOne ? i == o.workanswer : o.workanswer.includes(i) // 是否(包含)正确答案
|
||||
const isOk = (isJson(workdesc)?JSON.parse(o.workanswer):o.workanswer||'').includes(i+'') // 是否(包含)正确答案
|
||||
// 改选项的学生id
|
||||
const studentIds = quizFeedList.filter(f => isOne ? f.feedcontent==v : f.feedcontent.includes(i)).map(f => f.studentid)||[]
|
||||
accSum += studentIds.length
|
||||
if(isOk) isOne ? activeIds.push(...studentIds) : activeIds=[...new Set(activeIds.concat(studentIds))] // 多选去重
|
||||
return { def: v, code, isOk, studentIds }
|
||||
})
|
||||
} else if (o.worktype == '填空题') { // 填空题
|
||||
const regex = /<!--BA-->(.*?)<!--EA-->/g // 定义正则表达式,匹配 <!--BA-->xxx<!--EA--> 格式的内容
|
||||
children = (o.title||'').match(regex).map((v,i) => {
|
||||
const def = `填空项 ${i+1}`
|
||||
const code = '( )', txt=v
|
||||
// 改选项的学生id
|
||||
const studentIds = quizFeedList.filter(f => !!(f.feedcontent||'').replace(/#$/,'').split('#')[i]).map(f => f.studentid)||[]
|
||||
activeIds=[...new Set(activeIds.concat(studentIds))] // 多选去重
|
||||
accSum = activeIds.length
|
||||
return { def, code, txt, isOk:true, studentIds }
|
||||
})
|
||||
} else if (o.worktype == '论述题') { // 论述题
|
||||
const code = '( )', def = '论述内容'
|
||||
const studentIds = quizFeedList.filter(f => !!(f.feedcontent||'').replace(/#$/,'')).map(f => f.studentid)||[]
|
||||
activeIds=[...new Set(activeIds.concat(studentIds))] // 多选去重
|
||||
accSum = activeIds.length
|
||||
children = [{ def, code, isOk:true, studentIds }]
|
||||
}
|
||||
const studentSum = studentList.value.length || 0 // 当前推送答题人数
|
||||
const points = percent((activeIds.length / (studentSum||1)).toFixed(2)) // 计算得分率
|
||||
// def: 原始题数据 type 类型 active: 选中 points: 得分率, accSum 题解答人数
|
||||
return { def: o, id: o.id, type: o.worktype, active: [], points, accSum, children }
|
||||
})
|
||||
console.log('获取数据: ', data)
|
||||
dataList.value = data
|
||||
}
|
||||
// 获取-该题各选项-完成进度
|
||||
const ratio_1 = (row, sum = 1) => percent(((row.studentIds.length||0) / (sum||1)).toFixed(2))
|
||||
// 获取-该题-完成进度
|
||||
const ratio_2 = row => percent(((row.accSum||0) / (studentList.value.length||1)).toFixed(2))
|
||||
// 获取-该题进度-txt
|
||||
const getRatioTxt = row => row.type.includes('选题') ? '得分率' : '完成度'
|
||||
// 获取-进度条状态-左侧
|
||||
const getStatus = row => row.isOk ? 'success' : 'exception'
|
||||
// 获取-学生姓名(id)
|
||||
const getStudentName = id => studentList.value.length && (studentList.value.find(o => o.studentid == id)||{})?.name || id
|
||||
// 获取-选中题
|
||||
const getActive = ind => activeTopic.value != ind
|
||||
|
||||
// 答题情况-点击题
|
||||
const clickInfo = async ind => {
|
||||
activeTopic.value = activeTopic.value != ind ? ind : 0
|
||||
setTimeout(() => {scrollToElement('collapse-' + ind)}, 300);
|
||||
// elementPlus.ElMessage.warning('功能未开放!')
|
||||
}
|
||||
// === 通用工具 ===
|
||||
// 滚动到指定位置
|
||||
const scrollToElement = id => {
|
||||
const el = document.getElementById(id)
|
||||
!!el && el.scrollIntoView({ behavior: 'smooth', block: 'center',inline:'center' })
|
||||
}
|
||||
// 百分比现在 0-100
|
||||
const percent = v => v > 1 ? 1 : v < 0 ? 0 : Math.round(v * 100)
|
||||
// Unicode 转 字符 差值65
|
||||
const toCode = (v, b) => b ? v.charCodeAt() - 65 : String.fromCharCode(v + 65)
|
||||
// 判断是否为json字符串
|
||||
const isJson = str => {if(typeof str == 'string'){
|
||||
try {
|
||||
const res = JSON.parse(str)
|
||||
if(typeof res == 'object' && res) return true
|
||||
} catch (error) {}}return false
|
||||
}
|
||||
|
||||
// === 监听器 ===
|
||||
watchEffect(() => { initData() })
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
// 弹窗容器
|
||||
.c-warp{
|
||||
background: #F2F3F5;
|
||||
height: 100vh;
|
||||
margin: 0 !important;
|
||||
.left{padding-left: 0 !important;}
|
||||
.right{padding-right: 0 !important;}
|
||||
.c-item{
|
||||
padding: 10px;
|
||||
background: #fff;
|
||||
border: none;
|
||||
overflow-y: auto;
|
||||
height: 100vh;
|
||||
}
|
||||
.collapse-item{
|
||||
.item-title-o{
|
||||
margin: 0 10px;
|
||||
font-size: 18px;
|
||||
font-weight: bold;
|
||||
}
|
||||
&:last-child{
|
||||
color: red;
|
||||
:deep(.el-collapse-item__header){
|
||||
border-bottom: none;
|
||||
}
|
||||
}
|
||||
.respond{
|
||||
.c-label{
|
||||
b[t1]{margin-right: 10px;}
|
||||
}
|
||||
.c-childen{
|
||||
padding: 15px;
|
||||
border-radius: 4px;
|
||||
border: 1px solid #CDD0D6;
|
||||
.el-text[t1]{
|
||||
margin-left: 10px;
|
||||
}
|
||||
.t-left{width: 160px;text-align: left;}
|
||||
.c-respond{
|
||||
.el-tag{margin: 0 5px;}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.right{
|
||||
.title{
|
||||
font-size: 18px;
|
||||
font-weight: bold;
|
||||
padding-bottom: 10px;
|
||||
border-bottom: 1px solid #CDD0D6;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.respond{
|
||||
height: calc(100vh - 65px);
|
||||
overflow: auto;
|
||||
.el-space{padding: 5px;}
|
||||
.card-warp{
|
||||
border: none;
|
||||
:deep(.el-card__body){
|
||||
padding: 10px !important;
|
||||
}
|
||||
}
|
||||
.card-body{
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -23,11 +23,24 @@
|
|||
<span>已交</span>
|
||||
</div>
|
||||
<div class="class-reserv-item-tool">
|
||||
<span style="color: #ff7f00; font-weight: 900; font-size: 15px">{{ item.teacherRationgCount?item.teacherRationgCount:0 }}</span>
|
||||
<!-- 总人数-已批阅人数 -->
|
||||
<span style="color: #ff7f00; font-weight: 900; font-size: 15px">{{ item.teacherrationgcount?item.workdatacount - item.teacherrationgcount:item.workdatacount }}</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>
|
||||
<!-- {{ item.averagetime?item.averagetime:0 }} -->
|
||||
<span v-if=" item.averagetime<60 ">
|
||||
<span style="color: #007fff; font-weight: 900; font-size: 15px">{{ item.averagetime }}</span>分钟
|
||||
</span>
|
||||
<span v-if=" item.averagetime==60 ">
|
||||
<span style="color: #007fff; font-weight: 900; font-size: 15px">1</span>小时
|
||||
</span>
|
||||
<span v-if=" item.averagetime>60 ">
|
||||
<span style="color: #007fff; font-weight: 900; font-size: 15px">{{ Math.floor(item.averagetime / 60)}}</span>小时
|
||||
<span style="color: #007fff; font-weight: 900; font-size: 15px">{{ Math.floor(item.averagetime % 60)}}</span>分钟
|
||||
</span>
|
||||
</span>
|
||||
<span>平均用时</span>
|
||||
</div>
|
||||
<div class="class-reserv-item-tool">
|
||||
|
|
Loading…
Reference in New Issue