yangws #344

Merged
yangws merged 3 commits from yangws into main 2024-10-20 01:36:44 +08:00
6 changed files with 295 additions and 94 deletions

View File

@ -1,5 +1,5 @@
<template> <template>
<div class="common-layout" style="width: 100%; height: 73vh;"> <div class="common-layout" style="width: 100%">
<el-container> <el-container>
<el-container> <el-container>
<el-header style="height: auto"> <el-header style="height: auto">
@ -7,7 +7,7 @@
<el-card> <el-card>
<template #header> <template #header>
<div style="font-size: 20px;font-weight: bold"> <div style="font-size: 20px;font-weight: bold">
学情分布 等级分布
</div> </div>
</template> </template>
<Distribution></Distribution> <Distribution></Distribution>
@ -18,7 +18,7 @@
<el-card> <el-card>
<template #header> <template #header>
<div style="font-size: 20px;font-weight: bold"> <div style="font-size: 20px;font-weight: bold">
分析 时分析
</div> </div>
</template> </template>
<TimeAnalyse></TimeAnalyse> <TimeAnalyse></TimeAnalyse>
@ -29,7 +29,7 @@
<el-card> <el-card>
<template #header> <template #header>
<div style="font-size: 20px;font-weight: bold"> <div style="font-size: 20px;font-weight: bold">
知识点概览 价值透析
</div> </div>
</template> </template>
<Konwledge></Konwledge> <Konwledge></Konwledge>
@ -43,12 +43,12 @@
</template> </template>
<script setup> <script setup>
import Distribution from '@/views/classTask/container/classOverview/distribution.vue' import {ref,watchEffect,provide } from 'vue'
import Konwledge from '@/views/classTask/container/classOverview/knowledge.vue' import Distribution from './classOverview/distribution.vue'
import TimeAnalyse from '@/views/classTask/container/classOverview/timeAnalyse.vue' import Konwledge from './classOverview/knowledge.vue'
import TimeAnalyse from './classOverview/timeAnalyse.vue'
import {defineProps,watch} from 'vue' import {defineProps,watch} from 'vue'
import overviewStore from "@/store/modules/overview"; import overviewStore from "@/store/modules/overview";
// import {getBindlist} from "@/api/education/knowledgePoint";
const useOverview = overviewStore() const useOverview = overviewStore()
const props = defineProps({ const props = defineProps({
tableList: { tableList: {
@ -57,12 +57,185 @@ const props = defineProps({
return [] return []
} }
}, },
// evalId:{ evalId:{
// type: Number, type: Number,
// default: 0 default: 0
// } },
activeData: { //
type: Object,
// required: true, //
default: () => ({
quizlist: [], //
studentList: [], // -
workFeedList: [] // -
})
},
}) })
let studentList = ref([]) //
const stuHasAnswers = 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 || []
//
let data = quizlist.map(o => {
//
const workdesc = o.workdesc || ''
let accSum = 0 //
let activeIds = [] //
let rightIds = [] //
let hasAnswers= [] //
let timeAnalyse = [] //
const quizFeedList = activeWorkFeedList.filter(f => f.entpcourseworkid == o.id) //
let children = []
const allStudents = [];
if (o.worktype == '单选题') { // '',''
const list = workdesc.includes('#&') ? workdesc.split('#&') : isJson(workdesc)?JSON.parse(workdesc):[];
children = list.map((v,i) => {
const code = toCode(i) // A-Z
const isOk = (isJson(workdesc)?JSON.parse(o.workanswer):o.workanswer||'').includes(i+'') // ()
// id
const studentIds = quizFeedList.filter(f => f.feedcontent==v&&f.finishtimelength!='0').map(f => f.studentid)||[];
accSum += studentIds.length;
if (isOk) {
activeIds.push(...studentIds)
}
hasAnswers.push(...studentIds)
return { def: v, code, isOk, studentIds }
})
}
else if (o.worktype == '多选题') {
//
rightIds = quizFeedList.filter(f => {
const workanswer = (isJson(o.workanswer)) ? JSON.parse(o.workanswer) : o.workanswer || [];
const res = isSame((f.feedcontent||'').split(','), workanswer);
return f.entpcourseworkid == o.id && f.finishtimelength!='0' && res;
});
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 => f.feedcontent.includes(i)&&f.finishtimelength!='0').map(f => f.studentid)||[];
accSum += studentIds.length;
if (studentIds.length>0) {
allStudents.push(...studentIds);
}
if(isOk) {
activeIds=[...new Set(activeIds.concat(studentIds))] //
}
hasAnswers=[...new Set(hasAnswers.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 = '(&emsp;)'
const code = '(略)', txt=v
// id
const studentIds = quizFeedList.filter(f => !!(f.feedcontent||'').replace(/#$/,'').split('#')[i] && f.finishtimelength!='0').map(f => f.studentid)||[]
activeIds=[...new Set(activeIds.concat(studentIds))] //
hasAnswers=[...new Set(hasAnswers.concat(studentIds))]
accSum = activeIds.length
return { def, code, txt, isOk:true, studentIds }
})
} else if (o.worktype == '判断题') { //
const list = ['正确', '错误'];
children = list.map((v,i) => {
const workanswer = o.workanswer
.replace('×', '0')
.replace('√', '1')
.replace('错误', '0')
.replace('正确', '1')
.replace('正确。', '1')
.replace('F', '0')
.replace('T', '1')
.replace('错', '0')
.replace('对', '1');
const workanswerFormat = isJson(workanswer) ? JSON.parse(workanswer) : workanswer||''
const code = v=='正确' ? '1' : '0'
let isOk = (workanswerFormat).includes(code)
// warn:
// if (workanswerFormat == '0') {
// isOk = !isOk;
// }
// id
const studentIds = quizFeedList.filter(f => {
const feedcontent = f.feedcontent
.replace('×', '0')
.replace('√', '1')
.replace('错误', '0')
.replace('正确', '1')
.replace('正确。', '1')
.replace('F', '0')
.replace('T', '1')
.replace('错', '0')
.replace('对', '1');
if(feedcontent == code&&f.finishtimelength!='0'){
return f
}
}).map(f => f.studentid)||[];
accSum += studentIds.length;
if(isOk) activeIds.push(...studentIds)
hasAnswers.push(...studentIds)
return { def: v, code: v, isOk, studentIds }
})
} else { //
// code = '(&emsp;)'
const code = '(略)', def = '解答内容'
const studentIds = quizFeedList.filter(f => !!(f.feedcontent||'').replace(/#$/,'')&&f.finishtimelength!='0').map(f => f.studentid)||[]
activeIds=[...new Set(activeIds.concat(studentIds))] //
hasAnswers=[...new Set(hasAnswers.concat(studentIds))]
accSum = activeIds.length
children = [{ def, code, isOk:true, studentIds }]
}
const studentSum = studentList.value.length || 0 //
let points = percent((activeIds.length / (studentSum||1)).toFixed(2)) //
let rightSum = activeIds.length; //
//
if (o.worktype == '多选题') {
//
const uniqueTmpStuents = [...new Set(allStudents)];
accSum = uniqueTmpStuents.length;
//
points = percent((rightIds.length / (studentSum||1)).toFixed(2)) //
//
rightSum = rightIds.length;
}
// def: type active: points: , accSum
return { def: o, id: o.id, type: o.worktype, active: [], points, accSum, rightSum, children,hasAnswers }
})
console.log('获取数据: ', data)
stuHasAnswers.value = [...data[0].hasAnswers]
provide('hasAnswer', stuHasAnswers.value)
}
// 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
}
watch(() => props.tableList,() => { watch(() => props.tableList,() => {
useOverview.getTableList(props.tableList) useOverview.getTableList(props.tableList)
},{deep:true}) },{deep:true})
// === ===
watchEffect(() => { initData() })
</script> </script>

View File

@ -1,32 +1,40 @@
<template> <template>
<div className="chart-container"> <div class="chart-container">
<div ref="chartRef" className="chart"></div> <div ref="chartRef" class="chart"></div>
</div> </div>
</template> </template>
<script setup> <script setup>
import {ref,nextTick,watch} from 'vue'; import { ref, nextTick, watch, inject,watchEffect } from 'vue';
import * as echarts from 'echarts'; import * as echarts from 'echarts';
import overviewStore from '@/store/modules/overview' import overviewStore from '@/store/modules/overview';
const useOverview = overviewStore() const useOverview = overviewStore();
// //
const chartRef = ref(null); const chartRef = ref(null);
// //
const dataList = ref([ const dataList = ref([
{name: '完美', value: 0,rating:1,max:100,min:100,}, { name: '完美', value: 0, rating: 1, max: 100, min: 100 },
{name: '优秀', value: 0,rating:2,max:99,min:80,}, { name: '优秀', value: 0, rating: 2, max: 99, min: 80 },
{name: '良好', value: 0,rating:3,max:79,min:70,}, { name: '良好', value: 0, rating: 3, max: 79, min: 70 },
{name: '及格', value: 0,rating:4,max:69,min:60,}, { name: '及格', value: 0, rating: 4, max: 69, min: 60 },
{name: '不及格', value: 0,rating:5,max:59,min:0,}, { name: '不及格', value: 0, rating: 5, max: 59, min: 0 },
]); ]);
//
const hasAnswersData = ref([]);
const hasStudents = ref([])
// //
function getColor(index) { function getColor(name) {
// const colorMap = {
const colors = ['#d14a61','#675bba', '#e89110','#008c8c','#5793f3']; '完美': '#d14a61',
return colors[index]; '优秀': '#675bba',
'良好': '#e89110',
'及格': '#008c8c',
'不及格': '#5793f3'
};
return colorMap[name];
} }
// //
@ -34,75 +42,81 @@ function initChart() {
const myChart = echarts.init(chartRef.value); const myChart = echarts.init(chartRef.value);
const total = dataList.value.reduce((acc, cur) => acc + cur.value, 0); // const total = dataList.value.reduce((acc, cur) => acc + cur.value, 0); //
// 0
const filteredData = dataList.value.filter(item => item.value > 0);
const options = { const options = {
tooltip: { tooltip: {
trigger: 'axis', trigger: 'item',
axisPointer: { formatter: params => {
type: 'shadow' const value = params.value;
const percentage = value ? ((value / total) * 100).toFixed(2) : 0; //
return `${params.name}: ${value}人 (${percentage}%)`; //
} }
}, },
grid: {
left: '3%',
right: '4%',
bottom: '3%',
containLabel: true
},
xAxis: {
type: 'category',
data: dataList.value.map(item => item.name),
axisTick: {
alignWithLabel: true
}
},
yAxis: {
type: 'value'
},
series: [{ series: [{
name: '数据', name: '数据',
type: 'bar', type: 'pie',
barWidth: '30%', radius: '50%', //
data: dataList.value.map(item => item.value), data: filteredData.map(item => ({
itemStyle: { name: item.name,
color: function (params) { value: item.value,
// itemStyle: {
return getColor(params.dataIndex); color: getColor(item.name)
} }
}, })),
//
label: { label: {
show: true, show: true,
position: 'top', position: 'inside', //
formatter: params => { formatter: params => {
const value = dataList.value[params.dataIndex].value; const value = params.value;
const percentage = value ? ((value / total) * 100).toFixed() : 0; // const percentage = value ? ((value / total) * 100).toFixed(2) : 0; //
return `${value}${percentage}%`; // return `${params.name}\n${value}(${percentage}%)`; //
}, },
color: '#333', color: '#333',
fontSize: 12 fontSize: 12
},
labelLine: {
show: false // 线
} }
}] }]
}; };
myChart.setOption(options); myChart.setOption(options);
} }
// //
const showEcharts =() => { const showEcharts = () => {
useOverview.tableList.forEach((item,index) => { hasStudents.value.forEach((item, index) => {
if(item.rating === 0) return // if (item.rating === 0) {
dataList.value.forEach((item1,index1) => { dataList.value.forEach((item1, index1) => {
if(item1.min <= Number(item.scoingRate) && Number(item.scoingRate) <= item1.max ){ if (item1.min <= Number(item.scoingRate) && Number(item.scoingRate) <= item1.max) {
item1.value ++ item1.value++;
} }
}) });
}) } else {
dataList.value.forEach((item1, index1) => {
if (item1.rating === item.rating) {
item1.value++;
}
});
}
});
} }
watch(() => useOverview.tableList,() => { //
showEcharts() watch(() => useOverview.tableList, () => {
hasStudents.value = useOverview.tableList.filter(item => hasAnswersData.value.includes(item.studentid)).map(item => item);
showEcharts();
nextTick(() => { nextTick(() => {
initChart(); initChart();
}) });
}) },{deep: true})
//
watchEffect(() => {
const stus = inject('hasAnswer');
if (!stus) return;
hasAnswersData.value = [...stus]
});
</script> </script>
<style scoped> <style scoped>
@ -115,4 +129,4 @@ watch(() => useOverview.tableList,() => {
width: 100%; width: 100%;
height: 100%; height: 100%;
} }
</style> </style>

View File

@ -16,11 +16,14 @@
</template> </template>
<script setup> <script setup>
import {nextTick, ref, watch} from 'vue' import {nextTick, ref, watch,inject,watchEffect} from 'vue'
import overviewStore from '@/store/modules/overview' import overviewStore from '@/store/modules/overview'
const useOverview = overviewStore() const useOverview = overviewStore()
const tabPosition = ref('left') const tabPosition = ref('left')
//
const hasAnswersData = ref([])
const hasStudents = ref([])
const leftList = ref([ const leftList = ref([
{ {
label:'完美', label:'完美',
@ -65,17 +68,27 @@ const handelChange = (item) => {
} }
// //
const showStudents = (index) => { const showStudents = (index) => {
leftList.value[index].stuList = useOverview.tableList.filter(item => { leftList.value[index].stuList = hasStudents.value.filter(item => {
if(item.rating > 0){ if(item.rating === 0){
if(leftList.value[index].min <= Number(item.scoingRate || 0) && Number(item.scoingRate || 0) <= leftList.value[index].max ){ if(leftList.value[index].min <= Number(item.scoingRate || 0) && Number(item.scoingRate || 0) <= leftList.value[index].max ){
return item return item
} }
}else{
if(item.rating == leftList.value[index].rating){
return item
}
} }
}) })
} }
watch(() => useOverview.tableList,() => { watch(() => useOverview.tableList, () => {
hasStudents.value = useOverview.tableList.filter(item => hasAnswersData.value.includes(item.studentid)).map(item => item);
showStudents(0) showStudents(0)
}) },{deep: true})
watchEffect(() => {
const stus = inject('hasAnswer')
if(!stus) return
hasAnswersData.value = [...stus]
})
</script> </script>
<style scoped> <style scoped>

View File

@ -318,6 +318,7 @@
<div <div
v-for="(score, index) in teacherRatingList" v-for="(score, index) in teacherRatingList"
:key="index" :key="index"
style="white-space: nowrap;"
:class="[ :class="[
'score-circle', 'score-circle',
{ active: classWorkFormScore.rating == score.ratingKey } { active: classWorkFormScore.rating == score.ratingKey }
@ -501,11 +502,11 @@ const classWorkFormScore = reactive({
teacherremark: '' // teacherremark: '' //
}) })
const teacherRatingList = ref([ const teacherRatingList = ref([
{ ratingKey: '1', ratingValue: '' }, { ratingKey: '1', ratingValue: '完美' },
{ ratingKey: '2', ratingValue: '优-' }, { ratingKey: '2', ratingValue: '优' },
{ ratingKey: '3', ratingValue: '良' }, { ratingKey: '3', ratingValue: '良' },
{ ratingKey: '4', ratingValue: '良-' }, { ratingKey: '4', ratingValue: '及格' },
{ ratingKey: '5', ratingValue: '' } { ratingKey: '5', ratingValue: '不及格' }
]) ])
// 线 // 线
//#region //#region

View File

@ -94,19 +94,19 @@
</template> </template>
<!-- 1- 2-优减 3- 4-良减 5- --> <!-- 1- 2-优减 3- 4-良减 5- -->
<template v-if="scope.row.teacherRating == 1" <template v-if="scope.row.teacherRating == 1"
><el-tag type="danger"></el-tag></template ><el-tag type="danger">完美</el-tag></template
> >
<template v-if="scope.row.teacherRating == 2" <template v-if="scope.row.teacherRating == 2"
><el-tag type="danger">-</el-tag></template ><el-tag type="danger"></el-tag></template
> >
<template v-if="scope.row.teacherRating == 3" <template v-if="scope.row.teacherRating == 3"
><el-tag type="warning"></el-tag></template ><el-tag type="warning"></el-tag></template
> >
<template v-if="scope.row.teacherRating == 4" <template v-if="scope.row.teacherRating == 4"
><el-tag type="info">-</el-tag></template ><el-tag type="info">及格</el-tag></template
> >
<template v-if="scope.row.teacherRating == 5" <template v-if="scope.row.teacherRating == 5"
><el-tag type="info"></el-tag></template ><el-tag type="info">不及格</el-tag></template
> >
</template> </template>
</el-table-column> </el-table-column>

View File

@ -84,19 +84,19 @@
</template> </template>
<!-- 1- 2-优减 3- 4-良减 5- --> <!-- 1- 2-优减 3- 4-良减 5- -->
<template v-if="scope.row.teacherRating == 1" <template v-if="scope.row.teacherRating == 1"
><el-tag type="danger"></el-tag></template ><el-tag type="danger">完美</el-tag></template
> >
<template v-if="scope.row.teacherRating == 2" <template v-if="scope.row.teacherRating == 2"
><el-tag type="danger">-</el-tag></template ><el-tag type="danger"></el-tag></template
> >
<template v-if="scope.row.teacherRating == 3" <template v-if="scope.row.teacherRating == 3"
><el-tag type="warning"></el-tag></template ><el-tag type="warning"></el-tag></template
> >
<template v-if="scope.row.teacherRating == 4" <template v-if="scope.row.teacherRating == 4"
><el-tag type="info">-</el-tag></template ><el-tag type="info">及格</el-tag></template
> >
<template v-if="scope.row.teacherRating == 5" <template v-if="scope.row.teacherRating == 5"
><el-tag type="info"></el-tag></template ><el-tag type="info">不及格</el-tag></template
> >
</template> </template>
</el-table-column> </el-table-column>
@ -139,7 +139,7 @@
<!-- 训练报告--> <!-- 训练报告-->
<div v-else-if="classWorkAnalysis.view == 'report'" style="width: 100%;overflow-y: scroll"> <div v-else-if="classWorkAnalysis.view == 'report'" style="width: 100%;overflow-y: scroll">
<!-- <ClassOverview :table-list="overviewData" :eval-id="courseObj.evalid"></ClassOverview> --> <!-- <ClassOverview :table-list="overviewData" :eval-id="courseObj.evalid"></ClassOverview> -->
<ClassOverview :table-list="overviewData" style="width: 100%;"></ClassOverview> <ClassOverview :active-data="classWorkActiveData" :table-list="overviewData" style="width: 100%;"></ClassOverview>
</div> </div>
</div> </div>