Compare commits
16 Commits
906559134e
...
06cb96c7bc
Author | SHA1 | Date |
---|---|---|
yangws | 06cb96c7bc | |
小杨 | 8784c0cfe3 | |
yangws | 9e13e3dfb5 | |
小杨 | c8aae6f408 | |
baigl | 13f7398d79 | |
白了个白 | 6ef29f2b5a | |
白了个白 | 5cf7d737d6 | |
baigl | 346b07e47a | |
白了个白 | 743d8e05d3 | |
yangws | 7c9b2755f2 | |
小杨 | 25acd398ff | |
yangws | fe078e7ce5 | |
小杨 | eaaba2b5e8 | |
zhengdegang | 4816a4565b | |
zdg | 8b55ebffa5 | |
zdg | 9674c68d11 |
|
@ -75,12 +75,12 @@ let studentList = ref([]) // 学生数据
|
|||
|
||||
// 初始-数据处理
|
||||
const initData = () => {
|
||||
console.log('xxx', props)
|
||||
// window.test = activeCourse
|
||||
studentList.value = props.activeData.studentList || []
|
||||
const activeWorkFeedList = props.activeData.workFeedList || []
|
||||
const quizlist = props.activeData.quizlist || []
|
||||
const timeArr = groupByField(props.activeData.workFeedList,'entpcourseworkid')
|
||||
console.log(quizlist,'quizlist');
|
||||
|
||||
// 习题特殊处理
|
||||
let data = quizlist.map(o => {
|
||||
// 解析题选项
|
||||
|
@ -90,14 +90,9 @@ const initData = () => {
|
|||
let rightIds = [] // 正确学生
|
||||
let hasAnswers= [] // 答过题的学生
|
||||
let timeAnalyse = [] // 平均时长和编号
|
||||
// let subjectCourese = [] // 题目编号
|
||||
const quizFeedList = activeWorkFeedList.filter(f => f.entpcourseworkid == o.id) // 做该题的列表
|
||||
// 获取所有的做题时间
|
||||
timeArr.forEach((item,index) => {
|
||||
const arr = item.reduce((acc, cur) => {
|
||||
return acc + (cur.timelength ? Number(cur.timelength) : 0);
|
||||
},0)
|
||||
timeAnalyse.push(arr)
|
||||
})
|
||||
|
||||
let children = []
|
||||
const allStudents = [];
|
||||
if (o.worktype == '单选题') { // '单选题','多选题'
|
||||
|
@ -108,6 +103,11 @@ const initData = () => {
|
|||
// 改选项的学生id
|
||||
const studentIds = quizFeedList.filter(f => f.feedcontent==v&&f.finishtimelength!='0').map(f => f.studentid)||[];
|
||||
accSum += studentIds.length;
|
||||
// 该到题的用时时间
|
||||
timeAnalyse = quizFeedList.reduce((acc, cur) => {
|
||||
return acc + (cur.timelength ? Number(cur.timelength) : 0);
|
||||
},0)
|
||||
|
||||
if (isOk) {
|
||||
activeIds.push(...studentIds)
|
||||
}
|
||||
|
@ -135,6 +135,10 @@ const initData = () => {
|
|||
if (studentIds.length>0) {
|
||||
allStudents.push(...studentIds);
|
||||
}
|
||||
// 该到题的用时时间
|
||||
timeAnalyse = quizFeedList.reduce((acc, cur) => {
|
||||
return acc + (cur.timelength ? Number(cur.timelength) : 0);
|
||||
},0)
|
||||
if(isOk) {
|
||||
activeIds=[...new Set(activeIds.concat(studentIds))] // 多选去重
|
||||
}
|
||||
|
@ -153,6 +157,10 @@ const initData = () => {
|
|||
activeIds=[...new Set(activeIds.concat(studentIds))] // 多选去重
|
||||
hasAnswers=[...new Set(hasAnswers.concat(studentIds))]
|
||||
accSum = activeIds.length
|
||||
// 该到题的用时时间
|
||||
timeAnalyse = quizFeedList.reduce((acc, cur) => {
|
||||
return acc + (cur.timelength ? Number(cur.timelength) : 0);
|
||||
},0)
|
||||
return { def, code, txt, isOk:true, studentIds }
|
||||
})
|
||||
} else if (o.worktype == '判断题') { // 判断题
|
||||
|
@ -194,6 +202,10 @@ const initData = () => {
|
|||
accSum += studentIds.length;
|
||||
if(isOk) activeIds.push(...studentIds)
|
||||
hasAnswers.push(...studentIds)
|
||||
// 该到题的用时时间
|
||||
timeAnalyse = quizFeedList.reduce((acc, cur) => {
|
||||
return acc + (cur.timelength ? Number(cur.timelength) : 0);
|
||||
},0)
|
||||
return { def: v, code: v, isOk, studentIds }
|
||||
})
|
||||
} else { // 论述题
|
||||
|
@ -203,6 +215,10 @@ const initData = () => {
|
|||
activeIds=[...new Set(activeIds.concat(studentIds))] // 多选去重
|
||||
hasAnswers=[...new Set(hasAnswers.concat(studentIds))]
|
||||
accSum = activeIds.length
|
||||
// 该到题的用时时间
|
||||
timeAnalyse = quizFeedList.reduce((acc, cur) => {
|
||||
return acc + (cur.timelength ? Number(cur.timelength) : 0);
|
||||
},0)
|
||||
children = [{ def, code, isOk:true, studentIds }]
|
||||
}
|
||||
|
||||
|
@ -222,7 +238,7 @@ const initData = () => {
|
|||
}
|
||||
|
||||
// def: 原始题数据 type 类型 active: 选中 points: 得分率, accSum 题解答人数
|
||||
return { def: o, id: o.id, type: o.worktype, active: [], points, accSum, rightSum, children,hasAnswers,timeAnalyse }
|
||||
return { def: o, id: o.id, type: o.worktype, active: [], points, accSum, rightSum, children,hasAnswers,timeAnalyse,score:o.score }
|
||||
})
|
||||
if (data.length === 0) return
|
||||
useOverview.getAllData([...data])
|
||||
|
|
|
@ -24,6 +24,7 @@ const dataList = ref([
|
|||
// 答过题的学生才进行统计
|
||||
const hasStudents = ref([])
|
||||
|
||||
|
||||
// 根据数据生成不同的颜色
|
||||
function getColor(name) {
|
||||
const colorMap = {
|
||||
|
@ -104,7 +105,18 @@ const showEcharts = () => {
|
|||
}
|
||||
//执行
|
||||
watch(() => useOverview.tableList, () => {
|
||||
// 判断是不是客观题
|
||||
const subType = useOverview.allData.map(item => item.type)
|
||||
const objectiveQuestion = ['单选题','多选题','判断题']
|
||||
if( !subType.every(item => objectiveQuestion.includes(item)) ){
|
||||
hasStudents.value = useOverview.tableList.filter(item => {
|
||||
if(item.rating > 0 && useOverview.allData[0].hasAnswers.includes(item.studentid)){
|
||||
return item
|
||||
}
|
||||
})
|
||||
}else{
|
||||
hasStudents.value = useOverview.tableList.filter(item => useOverview.allData[0].hasAnswers.includes(item.studentid)).map(item => item);
|
||||
}
|
||||
showEcharts();
|
||||
nextTick(() => {
|
||||
initChart();
|
||||
|
|
|
@ -81,7 +81,18 @@ const showStudents = (index) => {
|
|||
})
|
||||
}
|
||||
watch(() => useOverview.tableList, () => {
|
||||
// 判断是不是客观题
|
||||
const subType = useOverview.allData.map(item => item.type)
|
||||
const objectiveQuestion = ['单选题','多选题','判断题']
|
||||
if( !subType.every(item => objectiveQuestion.includes(item)) ){
|
||||
hasStudents.value = useOverview.tableList.filter(item => {
|
||||
if(item.rating > 0 && useOverview.allData[0].hasAnswers.includes(item.studentid)){
|
||||
return item
|
||||
}
|
||||
})
|
||||
}else{
|
||||
hasStudents.value = useOverview.tableList.filter(item => useOverview.allData[0].hasAnswers.includes(item.studentid)).map(item => item);
|
||||
}
|
||||
showStudents(0)
|
||||
},{deep: true})
|
||||
</script>
|
||||
|
|
|
@ -20,82 +20,80 @@
|
|||
<script setup>
|
||||
import {ref, watch} from 'vue'
|
||||
import overviewStore from '@/store/modules/overview'
|
||||
import {listEntpcoursework} from '@/api/education/entpCourseWork'
|
||||
|
||||
const useOverview = overviewStore()
|
||||
const tableData = ref([])
|
||||
//获取题目id
|
||||
const ids = ref('')
|
||||
//总分
|
||||
const allScore = ref(0)
|
||||
//用来获取所有知识点
|
||||
const konwledge = ref([])
|
||||
const hasStudents = ref([])
|
||||
// 获取的所有得分
|
||||
const allScore = ref([])
|
||||
//所有题目的知识点
|
||||
const getKonwledge = () => {
|
||||
useOverview.tableList.forEach(item => {
|
||||
const getScoreRate = []
|
||||
// 获取知识点的种数
|
||||
const ledges = []
|
||||
hasStudents.value.forEach((item,index) => {
|
||||
//判断是否存在知识点
|
||||
if(item.knowledgePoint){
|
||||
konwledge.value.push({...JSON.parse(item.knowledgePoint),...{scoingRate:Number(item.scoingRate),point:item.point,allPoint:allScore.value}})
|
||||
const title = JSON.parse(item.knowledgePoint)
|
||||
//判断知识点是否重复
|
||||
if(!ledges.includes(title.id)){
|
||||
ledges.push(title.id)
|
||||
// 假如分数是0 或者 得分率为空
|
||||
konwledge.value.push({title:title.title,allPoint:allScore.value,id:title.id})
|
||||
}
|
||||
// 判断学生是否答过题
|
||||
if(useOverview.allData[0].hasAnswers.includes(item.studentid))
|
||||
getScoreRate.push({rate:item.scoingRate,id:title.id})
|
||||
}
|
||||
})
|
||||
tableData.value = getTableList(konwledge.value)
|
||||
tableData.value = tableData.value.map(item => {
|
||||
return{
|
||||
|
||||
// 看看有几个知识点
|
||||
konwledge.value.forEach(item => {
|
||||
let sunRate = 0
|
||||
let num = 0
|
||||
if(getScoreRate.length === 0) return
|
||||
|
||||
getScoreRate.forEach(item2 => {
|
||||
if(item.id === item2.id){
|
||||
sunRate += extractedNumber(item2.rate)
|
||||
num ++
|
||||
}
|
||||
})
|
||||
const scoreRate = sunRate / num
|
||||
tableData.value.push({
|
||||
scoingRate:scoreRate.toFixed(2),
|
||||
...item,
|
||||
allPoint: allScore.value
|
||||
}
|
||||
point:(item.allPoint * scoreRate / 100).toFixed(2)
|
||||
})
|
||||
})
|
||||
console.log(tableData.value,'tableData.value')
|
||||
}
|
||||
//获取总分
|
||||
const getScore = async () => {
|
||||
const scoreId = useOverview.tableList[0].entpcourseworklist
|
||||
const fixedJsonString = `[${scoreId}]`;
|
||||
const objects = JSON.parse(fixedJsonString);
|
||||
const id = objects.map(obj => obj.id);
|
||||
ids.value = id.join(',')
|
||||
const res = await listEntpcoursework({ids: ids.value, pageSize: 500})
|
||||
if(res.code === 200){
|
||||
allScore.value = res.rows.reduce((acc, cur) => acc + cur.workScore, 0);
|
||||
getKonwledge()
|
||||
}
|
||||
// 获取百分比的数字
|
||||
const extractedNumber = (score) => {
|
||||
const match = score.match(/\d+/);
|
||||
return match ? parseInt(match[0], 10) : null;
|
||||
}
|
||||
//组装tableList表格
|
||||
const getTableList = (data) => {
|
||||
const result = [];
|
||||
data.forEach(item => {
|
||||
const existingItem = result.find(i => i.id === item.id);
|
||||
if (existingItem) {
|
||||
// 累加point和scoingRate
|
||||
existingItem.pointTotal += parseInt(item.point);
|
||||
existingItem.scoingRateTotal += parseFloat(item.scoingRate);
|
||||
existingItem.count++;
|
||||
} else {
|
||||
// 新的对象
|
||||
result.push({
|
||||
id: item.id,
|
||||
title: item.title,
|
||||
pointTotal: item.point,
|
||||
scoingRateTotal: parseFloat(item.scoingRate),
|
||||
count: 1
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// 计算平均值
|
||||
result.forEach(item => {
|
||||
item.point = Math.round(item.pointTotal / item.count);
|
||||
// item.scoingRate = Math.round((item.scoingRateTotal / item.count) * 100) / 100;
|
||||
item.scoingRate = Math.round((item.point / allScore.value) * 100);
|
||||
delete item.pointTotal;
|
||||
delete item.scoingRateTotal;
|
||||
delete item.count;
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
watch(() => useOverview.tableList,() => {
|
||||
console.log(useOverview.tableList,'useOverview.tableList')
|
||||
getScore()
|
||||
// 判断是不是客观题
|
||||
const subType = useOverview.allData.map(item => item.type)
|
||||
const objectiveQuestion = ['单选题','多选题','判断题']
|
||||
if( !subType.every(item => objectiveQuestion.includes(item)) ){
|
||||
hasStudents.value = useOverview.tableList.filter(item => {
|
||||
if(item.rating > 0 && useOverview.allData[0].hasAnswers.includes(item.studentid)){
|
||||
return item
|
||||
}
|
||||
})
|
||||
}else{
|
||||
hasStudents.value = useOverview.tableList.filter(item => useOverview.allData[0].hasAnswers.includes(item.studentid)).map(item => item);
|
||||
}
|
||||
// 这里获取所有知识点的总分
|
||||
allScore.value = useOverview.allData.reduce((acc, cur) => {
|
||||
return acc + Number(cur.score)
|
||||
},0)
|
||||
getKonwledge()
|
||||
})
|
||||
</script>
|
||||
|
||||
|
|
|
@ -9,14 +9,17 @@ import * as echarts from 'echarts';
|
|||
import {ref, nextTick, watch} from 'vue'
|
||||
import overviewStore from '@/store/modules/overview'
|
||||
|
||||
const useOverview = overviewStore()
|
||||
const useOverview = overviewStore();
|
||||
|
||||
// 获取图表容器的引用
|
||||
const chartRef = ref(null);
|
||||
// 预计用时
|
||||
const expectedDuration = ref([]);
|
||||
|
||||
// 初始化图表
|
||||
function initChart() {
|
||||
const myChart = echarts.init(chartRef.value);
|
||||
|
||||
// 指定图表的配置项和数据
|
||||
let option = {
|
||||
tooltip: {
|
||||
|
@ -25,15 +28,16 @@ function initChart() {
|
|||
type: "shadow", // 默认为直线,可选为:'line' | 'shadow'
|
||||
},
|
||||
formatter: function (parms) {
|
||||
let str =
|
||||
parms[0].axisValue +
|
||||
"</br>" +
|
||||
parms[0].marker +
|
||||
"平均用时:" +
|
||||
parms[0].value + 's'
|
||||
let str = "";
|
||||
parms.forEach(param => {
|
||||
if (param.seriesType === 'bar') {
|
||||
str += param.axisValue + "</br>" + param.marker + "平均用时:" + param.value + 's' + "</br>";
|
||||
} else if (param.seriesType === 'line') {
|
||||
str += param.marker + "预计用时:" + param.value + 's';
|
||||
}
|
||||
});
|
||||
return str;
|
||||
},
|
||||
|
||||
},
|
||||
textStyle: {
|
||||
color: "#333",
|
||||
|
@ -84,7 +88,8 @@ function initChart() {
|
|||
},
|
||||
axisLabel: {},
|
||||
},
|
||||
series: [{
|
||||
series: [
|
||||
{
|
||||
data: getYValue(),
|
||||
stack: "zs",
|
||||
type: "bar",
|
||||
|
@ -98,45 +103,61 @@ function initChart() {
|
|||
y2: 1,
|
||||
type: "linear",
|
||||
global: false,
|
||||
colorStops: [{
|
||||
offset: 0,
|
||||
color: "#5EA1FF",
|
||||
},
|
||||
{
|
||||
offset: 1,
|
||||
color: "#90BEFF",
|
||||
},
|
||||
colorStops: [
|
||||
{ offset: 0, color: "#5EA1FF" },
|
||||
{ offset: 1, color: "#90BEFF" },
|
||||
],
|
||||
},
|
||||
},
|
||||
label: {
|
||||
show: true,
|
||||
position: 'top',
|
||||
formatter: '{c}s',
|
||||
color: '#333',
|
||||
},
|
||||
},
|
||||
//折线的加入
|
||||
{
|
||||
data: expectedDuration.value,
|
||||
type: "line",
|
||||
smooth: true,
|
||||
symbol: 'circle',
|
||||
symbolSize: 8,
|
||||
lineStyle: {
|
||||
color: '#FF7F50',
|
||||
width: 2,
|
||||
},
|
||||
itemStyle: {
|
||||
color: '#FF7F50',
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
myChart.setOption(option);
|
||||
}
|
||||
|
||||
// 获取平均时长纵坐标
|
||||
const getYValue = () => {
|
||||
const arr = [...useOverview.allData[0].timeAnalyse]
|
||||
const num = useOverview.allData[0].hasAnswers.length
|
||||
const arr = useOverview.allData.map(item => item.timeAnalyse)
|
||||
const num = useOverview.allData[0].hasAnswers.length;
|
||||
if (arr.length === 0) return [];
|
||||
return arr.map(item => (item ? (item / num).toFixed(2) : 0));
|
||||
};
|
||||
|
||||
return arr.map(item => {
|
||||
return item ? (item / num).toFixed(2) : 0
|
||||
})
|
||||
}
|
||||
// 获取横纵轴坐标数据
|
||||
const getXValue = () => {
|
||||
return useOverview.allData.map(item => item.id)
|
||||
}
|
||||
|
||||
|
||||
return useOverview.allData.map((item, index) => `第${index + 1}题`);
|
||||
};
|
||||
|
||||
watch(() => useOverview.tableList, () => {
|
||||
expectedDuration.value = useOverview.tableList.map(item => (Number(item.timelength) * 60 / useOverview.allData.length).toFixed(2));
|
||||
|
||||
// 获取横纵坐标
|
||||
nextTick(() => {
|
||||
initChart();
|
||||
})
|
||||
})
|
||||
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
|
|
@ -90,7 +90,7 @@
|
|||
<span>学生答案:
|
||||
<span
|
||||
v-if="stuItem.feedcontent !=''"
|
||||
style="background-color: red; color: white; padding: 0 5px; border-radius: 5px;"
|
||||
:style="{backgroundColor: `${formatWorkAnswer(quItem) == formatFeedContent(stuItem, quItem)? '#0ed116' : 'red'}`,color: 'white', padding: '0 5px', borderRadius: '5px'}"
|
||||
v-html="formatFeedContent(stuItem, quItem)"
|
||||
>
|
||||
</span>
|
||||
|
|
|
@ -390,17 +390,29 @@ const getClassWorkStudentList = (rowId) => {
|
|||
tableRadio.value = '1'
|
||||
tableRadio.num0 = classWorkAnalysis.classworkdata.length - tableRadio.list.length
|
||||
tableRadio.num1 = tableRadio.list.length
|
||||
tableRadio.list = tableRadio.list.map((item) => {
|
||||
return {
|
||||
...item,
|
||||
teacherRating : checkWorkType(item)
|
||||
}
|
||||
})
|
||||
// 自动批阅判断
|
||||
teacherCriticism();
|
||||
})
|
||||
.catch(() => {
|
||||
loading_dt_table.value = false
|
||||
})
|
||||
}
|
||||
/**
|
||||
* 自动批阅判断:
|
||||
* 已交 并 作业类型为习题训练
|
||||
*/
|
||||
const teacherCriticism = ()=>{
|
||||
// 已交的list才自动批阅判断
|
||||
if(tableRadio.value == '1'&& classWorkAnalysis.worktype == '习题训练'){
|
||||
// 只有习题训练才会自动批阅
|
||||
tableRadio.list = tableRadio.list.map((item) => {
|
||||
return {
|
||||
...item,
|
||||
teacherRating : item.teacherRating || checkWorkType(item)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
const checkWorkType = (item) => {
|
||||
//这里判断题目类型
|
||||
const subType = classWorkActiveData.quizlist.map(item => item.worktype)
|
||||
|
@ -408,7 +420,7 @@ const checkWorkType = (item) => {
|
|||
let rating = 0
|
||||
//判断题目是不是客观题
|
||||
if(subType.every(item => objectiveQuestion.includes(item))){
|
||||
// 获取学生答题列表
|
||||
// 获取学生答题列表 scoingRate 得分率
|
||||
const score = extractedNumber(item.scoingRate)
|
||||
if(0<=score && score<=59){
|
||||
rating = 5
|
||||
|
@ -551,6 +563,8 @@ const tableRadioChange = (e) => {
|
|||
tableRadio.value = '1';
|
||||
tableRadio.num0 = classWorkAnalysis.classworkdata.length - tableRadio.list.length;
|
||||
tableRadio.num1 = tableRadio.list.length;
|
||||
// 自动批阅判断
|
||||
teacherCriticism();
|
||||
}else if(e=='0'){
|
||||
tableRadio.list = classWorkAnalysis.classworkdata.filter(item => item.finishtimelength == '0')
|
||||
tableRadio.value = '0';
|
||||
|
|
|
@ -73,9 +73,8 @@ const list = computed(() => props.data.map((o,i) => {
|
|||
}))
|
||||
onMounted(() => {
|
||||
posBtnAll = btnRef.value.getBoundingClientRect()
|
||||
hPost.value = posBtnAll.height
|
||||
|
||||
curNode = sessionStore.get('subject.curNode')
|
||||
hPost.value = Math.round(posBtnAll.height)
|
||||
curNode = sessionStore?.get?.('subject.curNode')
|
||||
})
|
||||
// === 方法 ===
|
||||
// 获取颜色索引
|
||||
|
@ -97,7 +96,7 @@ const clickHandel = (o, e) => {
|
|||
isVisible.value = !isColse // 相同的按钮且打开状态,点击关闭
|
||||
activeObj.value = o
|
||||
const nodeH = parseInt(node.height / 2) // 高度的一半
|
||||
topPos.value = parseInt(node.top) - posBtnAll.top + nodeH
|
||||
topPos.value = Math.round(parseInt(node.top) - posBtnAll.top + nodeH)
|
||||
}
|
||||
emit('change', o)
|
||||
}
|
||||
|
@ -149,8 +148,8 @@ const closeActive = () =>{
|
|||
--top: 30px;
|
||||
--height: 40vh;
|
||||
position: fixed;
|
||||
inset: 50% 75px auto auto;
|
||||
transform: translateY(-50%);
|
||||
inset: 0 75px auto auto;
|
||||
// transform: translateY(-50%);
|
||||
background-color: #121212;
|
||||
padding: 10px;
|
||||
border-radius: 4px;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<template>
|
||||
<div v-if="props.test">
|
||||
<el-button type="primary" @click="trigger" v-tap:trigger="">点赞</el-button>
|
||||
<el-button type="primary" @click="trigger(1)" v-tap:trigger="[1,'']">点赞</el-button>
|
||||
<el-button type="primary" @click="trigger(2, '学生A')" v-tap:trigger="[2,'学生A']">疑惑</el-button>
|
||||
</div>
|
||||
<!-- 温度计-模式 -->
|
||||
|
@ -258,11 +258,11 @@ defineExpose({ trigger })
|
|||
min-width: 15px;
|
||||
// height: 500px;
|
||||
&.like{
|
||||
background-image: linear-gradient(to top, #fef0f0, #f56c6c);
|
||||
background-image: linear-gradient(to top, #d2f0cb, #2f9e44);
|
||||
// animation: striped-flow 5s linear infinite;
|
||||
}
|
||||
&.doubt{
|
||||
background-image: linear-gradient(to top, #fdf6ec, #e6a23c);
|
||||
background-image: linear-gradient(to top, #ebc6c6, #ff0000);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue