baigl #180

Merged
baigl merged 13 commits from baigl into main 2024-09-10 13:51:04 +08:00
10 changed files with 688 additions and 4 deletions
Showing only changes of commit 0b6b0c318d - Show all commits

View File

@ -25,6 +25,7 @@ export default defineConfig({
proxy: { proxy: {
'/dev-api': { '/dev-api': {
target: 'http://27.128.240.72:7865', target: 'http://27.128.240.72:7865',
// target: 'http://36.134.181.164:7863',
// target: 'http://192.168.2.52:7863', // target: 'http://192.168.2.52:7863',
changeOrigin: true, changeOrigin: true,
rewrite: (p) => p.replace(/^\/dev-api/, '') rewrite: (p) => p.replace(/^\/dev-api/, '')

View File

@ -0,0 +1,18 @@
import { defineStore } from 'pinia'
const overviewStore = defineStore(
'overview',
{
state: () => {
return {
tableList:[]
}
},
actions: {
getTableList(data){
this.tableList = [...data]
}
}
})
export default overviewStore

View File

@ -0,0 +1,68 @@
<template>
<div class="common-layout" style="width: 100%; height: 73vh;">
<el-container>
<el-container>
<el-header style="height: auto">
<!--学情分布-->
<el-card>
<template #header>
<div style="font-size: 20px;font-weight: bold">
学情分布
</div>
</template>
<Distribution></Distribution>
</el-card>
</el-header>
<el-main>
<!-- 时长分析-->
<el-card>
<template #header>
<div style="font-size: 20px;font-weight: bold">
时长分析
</div>
</template>
<TimeAnalyse></TimeAnalyse>
</el-card>
</el-main>
<el-footer style="height: auto;margin-bottom: 20px;">
<!--知识点概况-->
<el-card>
<template #header>
<div style="font-size: 20px;font-weight: bold">
知识点概览
</div>
</template>
<Konwledge></Konwledge>
</el-card>
</el-footer>
</el-container>
</el-container>
</div>
</template>
<script setup>
import Distribution from '@/views/classTask/container/classOverview/distribution.vue'
import Konwledge from '@/views/classTask/container/classOverview/knowledge.vue'
import TimeAnalyse from '@/views/classTask/container/classOverview/timeAnalyse.vue'
import {defineProps,watch} from 'vue'
import overviewStore from "@/store/modules/overview";
// import {getBindlist} from "@/api/education/knowledgePoint";
const useOverview = overviewStore()
const props = defineProps({
tableList: {
type: Array,
default: () => {
return []
}
},
// evalId:{
// type: Number,
// default: 0
// }
})
watch(() => props.tableList,() => {
useOverview.getTableList(props.tableList)
},{deep:true})
</script>

View File

@ -0,0 +1,23 @@
<template>
<div class="common-layout">
<el-container>
<el-aside width="400px">
<!-- 柱状图学情分布-->
<Echarts></Echarts>
</el-aside>
<el-main>
<!-- 列表分布的人员-->
<StuList></StuList>
</el-main>
</el-container>
</div>
</template>
<script setup>
import Echarts from '@/views/classTask/container/classOverview/distribution/echarts.vue'
import StuList from "@/views/classTask/container/classOverview/distribution/stuList.vue";
</script>
<style scoped>
</style>

View File

@ -0,0 +1,109 @@
<template>
<div className="chart-container">
<div ref="chartRef" className="chart"></div>
</div>
</template>
<script setup>
import {ref,nextTick,watch} from 'vue';
import * as echarts from 'echarts';
import overviewStore from '@/store/modules/overview'
const useOverview = overviewStore()
//
const chartRef = ref(null);
//
const dataList = ref([
{name: '优', value: 0,rating:1},
{name: '优-', value: 0,rating:2},
{name: '良', value: 0,rating:3},
{name: '良-', value: 0,rating:4},
{name: '差', value: 0,rating:5},
]);
//
function getColor(index) {
//
const colors = ['#d14a61','#675bba', '#e89110','#008c8c','#5793f3'];
return colors[index];
}
//
function initChart() {
const myChart = echarts.init(chartRef.value);
const options = {
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'shadow'
}
},
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: [{
name: '数据',
type: 'bar',
barWidth: '30%',
data: dataList.value.map(item => item.value),
itemStyle: {
color: function (params) {
//
return getColor(params.dataIndex);
}
},
//
label: {
show: true,
position: 'top',
formatter: '{c}人',
color: '#333',
fontSize: 12
}
}]
};
myChart.setOption(options);
}
//
const showEcharts =() => {
useOverview.tableList.forEach(item => {
const index = dataList.value.findIndex(item1 => item1.rating === item.rating)
if(index !== -1)
dataList.value[index].value ++
})
}
watch(() => useOverview.tableList,() => {
showEcharts()
nextTick(() => {
initChart();
})
})
</script>
<style scoped>
.chart-container {
width: 100%;
height: 400px;
}
.chart {
width: 100%;
height: 100%;
}
</style>

View File

@ -0,0 +1,115 @@
<template>
<el-tabs :tab-position="tabPosition" style="height: 100%" class="demo-tabs" @tabChange="handelChange">
<template v-for="(item,index) in leftList" :key="index">
<el-tab-pane :label="item.label" style="text-align:left">
<template v-if="item.stuList.length > 0">
<template v-for="(stuItem,stuIndex) in item.stuList" :key="stuIndex">
<el-tag style="margin:5px 10px 0 0" type="primary">{{stuItem.studentname}}</el-tag>
</template>
</template>
<template v-else>
<el-empty description="该分段暂时没有学生" />
</template>
</el-tab-pane>
</template>
</el-tabs>
</template>
<script setup>
import {nextTick, ref, watch} from 'vue'
import overviewStore from '@/store/modules/overview'
const useOverview = overviewStore()
const tabPosition = ref('left')
const leftList = ref([
{
label:'优',
stuList:[],
rating:1
},
{
label:'优-',
stuList:[],
rating:2
},
{
label:'良',
stuList:[],
rating:3
},
{
label:'良-',
stuList:[],
rating:4
},
{
label:'差',
stuList:[],
rating:5
},
])
//
const handelChange = (item) => {
showStudents(item)
}
//
const showStudents = (index) => {
console.log(useOverview.tableList,'lef')
leftList.value[index].stuList = useOverview.tableList.filter(item => {
if(item.rating == leftList.value[index].rating) return item
})
}
watch(() => useOverview.tableList,() => {
showStudents(0)
})
</script>
<style scoped>
:deep(.el-tabs__item) {
position: relative; /* 使 ::before 相对于自身定位 */
padding-left: 24px; /* 增加左边距以留出圆圈的位置 */
}
/* 圆圈样式 */
:deep(.el-tabs__item::before) {
content: '';
position: absolute;
left: 0;
top: 50%;
transform: translateY(-50%);
width: 10px;
height: 10px;
border-radius: 50%;
border: 2px solid #409eff; /* 创建空心圆圈 */
background-color: transparent; /* 设置背景颜色为透明 */
margin-left: 5px;
}
/* 根据索引设置不同的颜色 */
:deep(.el-tabs__item:nth-child(1)::before) {
border-color: #5793f3;
}
:deep(.el-tabs__item:nth-child(2)::before) {
border-color: #d14a61;
}
:deep(.el-tabs__item:nth-child(3)::before) {
border-color: #675bba;
}
:deep(.el-tabs__item:nth-child(4)::before) {
border-color: #e89110;
}
:deep(.el-tabs__item:nth-child(5)::before) {
border-color: #008c8c;
}
/* 选中状态下的样式 */
:deep(.el-tabs__item.is-active::before) {
background-color: transparent; /* 改变选中状态下的圆圈颜色 */
}
:deep(.el-tabs__item.is-active){
background-color: rgb(238, 241, 246);
}
:deep(.el-tabs--left .el-tabs__item.is-left){
text-align: left;
justify-content: flex-start;
}
</style>

View File

@ -0,0 +1,104 @@
<template>
<el-table
:data="tableData"
style="width: 100%; margin-bottom: 20px;min-height: 300px;"
row-key="id"
border
default-expand-all
>
<el-table-column prop="title" label="知识点"/>
<el-table-column prop="allPoint" label="分值" sortable/>
<el-table-column prop="point" label="平均分" sortable/>
<el-table-column prop="scoingRate" label="得分率" sortable>
<template #default="scope">
<div>{{scope.row.scoingRate + '%'}}</div>
</template>
</el-table-column>
</el-table>
</template>
<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 getKonwledge = () => {
useOverview.tableList.forEach(item => {
if(item.knowledgePoint){
konwledge.value.push({...JSON.parse(item.knowledgePoint),...{scoingRate:Number(item.scoingRate),point:item.point,allPoint:allScore.value}})
}
})
tableData.value = getTableList(konwledge.value)
tableData.value = tableData.value.map(item => {
return{
...item,
allPoint: allScore.value
}
})
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()
}
}
//tableList
const getTableList = (data) => {
const result = [];
data.forEach(item => {
const existingItem = result.find(i => i.id === item.id);
if (existingItem) {
// pointscoingRate
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()
})
</script>
<style scoped>
</style>

View File

@ -0,0 +1,184 @@
<template>
<div class="chart-container">
<div ref="chartRef" class="chart"></div>
</div>
</template>
<script setup>
import * as echarts from 'echarts';
import {ref, nextTick, watch} from 'vue'
import overviewStore from '@/store/modules/overview'
const useOverview = overviewStore()
//
const chartRef = ref(null);
const estimateTime = ref([]);
const avaterTime = ref([]);
// x
const xAxisData = ref([]);
// y
const getyAxisData = () => {
estimateTime.value = [];
avaterTime.value = [];
useOverview.tableList.forEach(item => {
if (item.rating !== 0) {
estimateTime.value.push({
name: item.scoingRate ? item.scoingRate + '%' : 0 + '%',
value: Number(item.timelength)
});
avaterTime.value.push({
name: item.scoingRate ? item.scoingRate + '%' : 0 + '%',
value: Number(item.finishtimelength)
});
}
});
// x
xAxisData.value.sort((a, b) => {
const aPercentage = parseInt(a.replace('%', ''));
const bPercentage = parseInt(b.replace('%', ''));
return aPercentage - bPercentage;
});
// x
generateXAxisData();
};
// x
function generateXAxisData() {
// 8x
if(estimateTime.value.length > 8){
const minScoreRate = 0;
const maxScoreRate = 100;
const numPoints = 6; // x
const step = (maxScoreRate - minScoreRate) / (numPoints - 1);
xAxisData.value = [];
for (let i = 0; i < numPoints; i++) {
const scoreRate = minScoreRate + i * step;
xAxisData.value.push(scoreRate + '%');
}
}else{
let uniqueXAxisData = new Set();
estimateTime.value.forEach(item => {
// Set
uniqueXAxisData.add(item.name);
});
// Set
xAxisData.value = Array.from(uniqueXAxisData);
//
xAxisData.value.sort((a, b) => {
const aPercentage = parseInt(a.replace('%', ''));
const bPercentage = parseInt(b.replace('%', ''));
return aPercentage - bPercentage;
});
}
}
//
function initChart() {
const myChart = echarts.init(chartRef.value);
const options = {
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'cross'
}
},
legend: {
data: ['预估时长', '平均用时']
},
grid: {
left: '3%',
right: '4%',
bottom: '3%',
top: '10%',
containLabel: true
},
xAxis: {
type: 'category',
boundaryGap: false,
name: '得分率',
nameTextStyle: {
color: '#999',
fontSize: 12,
padding: [0, 0, 10, 0]
},
data: xAxisData.value
},
yAxis: {
type: 'value',
name: '作业时长',
nameTextStyle: {
color: '#999',
fontSize: 12,
padding: [0, 0, 10, 0]
},
},
series: [
{
name: `预估时长`,
type: 'line',
smooth: true,
symbol: 'circle',
symbolSize: 10,
lineStyle: {
color: '#5793f3'
},
data: estimateTime.value.map(item => ({
name: item.name,
value: item.value
}))
},
{
name: `平均用时`,
type: 'line',
smooth: true,
symbol: 'circle',
symbolSize: 10,
lineStyle: {
color: '#d14a61'
},
data: avaterTime.value.map(item => ({
name: item.name,
value: item.value
}))
}
]
};
myChart.setOption(options);
}
//
const getAvaterTime = () => {
return useOverview.tableList.reduce((acc, cur) => acc + cur.finishtimelength, 0) / useOverview.tableList.length;
}
const getEstimateTime = () => {
return useOverview.tableList.reduce((acc, cur) => acc + cur.timelength, 0) / useOverview.tableList.length;
}
watch(() => useOverview.tableList,() => {
getyAxisData()
nextTick(() => {
initChart();
})
})
</script>
<style scoped>
.chart-container {
width: 100%;
height: 400px;
}
.chart {
width: 100%;
height: 100%;
}
</style>

View File

@ -3,7 +3,7 @@
v-model="classWorkAnalysis.open" v-model="classWorkAnalysis.open"
:modal-append-to-body="false" :modal-append-to-body="false"
class="clwk_dialog" class="clwk_dialog"
style="width: 90%; height: 85%" style="width: 90%; height: 85vh"
:show-close="false" :show-close="false"
top="8vh" top="8vh"
append-to-body append-to-body
@ -63,7 +63,7 @@
<div class="view_table"> <div class="view_table">
<el-radio-group <el-radio-group
v-model="tableRadio.value" v-model="tableRadio.value"
style="margin-bottom: 10px" style="margin-bottom: 1px"
@change="tableRadioChange" @change="tableRadioChange"
> >
<el-radio-button :value="1" :label="'已交' + '' + tableRadio.num1 + ''" /> <el-radio-button :value="1" :label="'已交' + '' + tableRadio.num1 + ''" />
@ -143,6 +143,7 @@
<!-- 作业报告--> <!-- 作业报告-->
<div v-else-if="classWorkAnalysis.view == 'report'" style="overflow-y: scroll"> <div v-else-if="classWorkAnalysis.view == 'report'" style="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"></ClassOverview>
</div> </div>
<!-- <template #footer> <!-- <template #footer>
@ -163,6 +164,7 @@ import { processList } from '@/hooks/useProcessList'
import ItemDialogScore from '@/views/classTask/container/item-dialog-score.vue' import ItemDialogScore from '@/views/classTask/container/item-dialog-score.vue'
// zdg: // zdg:
import quizStats from '@/views/classTask/container/quizStats.vue' import quizStats from '@/views/classTask/container/quizStats.vue'
import ClassOverview from '@/views/classTask/container/classOverview.vue'
const { proxy } = getCurrentInstance() const { proxy } = getCurrentInstance()
const emit = defineEmits(['addSuccess']) const emit = defineEmits(['addSuccess'])
@ -199,6 +201,9 @@ const classWorkAnalysisScore = reactive({
quizlist: [] // list quizlist: [] // list
}) })
//
const overviewData = ref([])
// watch( // watch(
// // () => props.currentNode, // // () => props.currentNode,
// (newValue, oldValue) => { // (newValue, oldValue) => {
@ -551,9 +556,66 @@ const getWorkFeedList = async() =>{
res.rows.forEach(o => { o.studentid = getStudentid(o.workdataid) }) res.rows.forEach(o => { o.studentid = getStudentid(o.workdataid) })
classWorkActiveData.workFeedList = res.rows classWorkActiveData.workFeedList = res.rows
} }
//#endregion
//#regin
/*
author: yangws
time: 2024-8-06 16:35:33
function:作业报告的处理
*/
const handleClassOverviewOpen = (type) =>{
// ui
isopen_dtwk_table.value = false;
classWorkAnalysis.view = type
const data = classWorkAnalysis.row
//
listClassworkdata({classworkid: data.id, pageSize: 100}).then((response) => {
if(response.code === 200){
response.rows.forEach(item => {
let rightAnswer = 0
let answers = 0
if(!item.classworkevallist) return
// 使
let replacedString = item.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(item.classworkevallist);
let allTopic
try{
allTopic = JSON.parse(`[${item.classworkevallist}]`)
}catch{
allTopic = JSON.parse(`[${replacedString}]`)
}
if(item.classworkevallist != ''){
allTopic.forEach(itemTopic => {
if(itemTopic.feedcontent != ''){
answers ++
//
if(itemTopic.feedcontent === itemTopic.rightanswer){
rightAnswer ++
}
}
})
rightAnswer > 0?item.scoingRate = (rightAnswer/answers * 100).toFixed(0):item.scoingRate = ''
}else{
item.scoingRate = ''
}
//
const point = allTopic.reduce((acc, cur) => {
if(cur.rating !== 0){
return acc + cur.teacherRating;
}
},0)
// item.chapter = this.courseObj.evalid
item.point = point || 0
item.rating = allTopic[0].rating
})
overviewData.value = [...response.rows]
}
})
}
//#endregion //#endregion

View File

@ -210,7 +210,7 @@ watchEffect(() => { initData() })
// //
.c-warp{ .c-warp{
background: #F2F3F5; background: #F2F3F5;
height: 75vh; height: 73vh;
margin: 0 !important; margin: 0 !important;
.left{padding-left: 0 !important;} .left{padding-left: 0 !important;}
.right{padding-right: 0 !important;} .right{padding-right: 0 !important;}
@ -219,7 +219,7 @@ watchEffect(() => { initData() })
background: #fff; background: #fff;
border: none; border: none;
overflow-y: auto; overflow-y: auto;
height: 75vh; height: 73vh;
} }
.collapse-item{ .collapse-item{
.item-title-o{ .item-title-o{