feat(plan): 添加学生学案历史查询及展示功能
- 新增FindStudentPlanHistoryReqVO和FindStudentPlanHistoryListRspVO数据对象 - 将LessonPlansDO及StudentLessonPlansDO中日期类型由Date改为LocalDateTime - LessonPlansServiceImpl中创建时间使用LocalDateTime.now() - StudentLessonPlansService及实现类添加按学生ID查询学案历史接口 - StudentLessonPlansDOMapper及XML添加按学生ID查询学案历史SQL映射 - StudentLessonPlansController新增/history接口返回学生学案历史列表 - 前端student.vue新增学案历史图表PlanHistoryChart组件展示学案历史数据 - 新增PlanHistoryChart.vue组件,实现基于echarts的学案历史折线图 - studentLessonPlans.js新增findStudentPlanHistory接口调用后端学案历史数据接口
This commit is contained in:
@@ -3,10 +3,7 @@ package com.yinlihupo.enlish.service.controller;
|
||||
import com.yinlihupo.enlish.service.domain.dataobject.LessonPlansDO;
|
||||
import com.yinlihupo.enlish.service.domain.dataobject.StudentLessonPlansDO;
|
||||
import com.yinlihupo.enlish.service.model.bo.StudentDetail;
|
||||
import com.yinlihupo.enlish.service.model.vo.plan.FindStudentPlansReqVO;
|
||||
import com.yinlihupo.enlish.service.model.vo.plan.FindStudentPlansRspVO;
|
||||
import com.yinlihupo.enlish.service.model.vo.plan.FinishStudentPlanReqVO;
|
||||
import com.yinlihupo.enlish.service.model.vo.plan.LessonPlanItem;
|
||||
import com.yinlihupo.enlish.service.model.vo.plan.*;
|
||||
import com.yinlihupo.enlish.service.service.LessonPlansService;
|
||||
import com.yinlihupo.enlish.service.service.StudentLessonPlansService;
|
||||
import com.yinlihupo.enlish.service.service.StudentService;
|
||||
@@ -77,7 +74,7 @@ public class StudentLessonPlansController {
|
||||
return PageResponse.success(findStudentPlansRspVOList, page, studentLessonPlanTotal, size);
|
||||
}
|
||||
|
||||
@PostMapping("/finish")
|
||||
@PostMapping("finish")
|
||||
@ApiOperationLog(description = "完成学案")
|
||||
public Response<String> finishStudentPlan(@RequestBody FinishStudentPlanReqVO finishStudentPlanReqVO) {
|
||||
Integer studentId = finishStudentPlanReqVO.getStudentId();
|
||||
@@ -89,4 +86,22 @@ public class StudentLessonPlansController {
|
||||
}
|
||||
return Response.fail("完成学案失败");
|
||||
}
|
||||
|
||||
@PostMapping("history")
|
||||
@ApiOperationLog(description = "查询学案历史")
|
||||
public Response<List<FindStudentPlanHistoryListRspVO>> findStudentPlanHistory(@RequestBody FindStudentPlanHistoryReqVO findStudentPlanHistoryReqVO) {
|
||||
Integer studentId = findStudentPlanHistoryReqVO.getStudentId();
|
||||
List<FindStudentPlanHistoryListRspVO> list = studentLessonPlansService.findStudentLessonPlansByStudentId(studentId)
|
||||
.stream().map(studentLessonPlansDO -> FindStudentPlanHistoryListRspVO.builder()
|
||||
.id(studentLessonPlansDO.getId())
|
||||
.studentId(studentLessonPlansDO.getStudentId())
|
||||
.planId(studentLessonPlansDO.getPlanId())
|
||||
.startTime(studentLessonPlansDO.getStartTime())
|
||||
.masteryRat(studentLessonPlansDO.getMasteryRat())
|
||||
.totalCount(studentLessonPlansDO.getTotalCount())
|
||||
.build()
|
||||
).toList();
|
||||
|
||||
return Response.success(list);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Date;
|
||||
|
||||
@AllArgsConstructor
|
||||
@@ -20,7 +21,7 @@ public class LessonPlansDO {
|
||||
|
||||
private Integer unitId;
|
||||
|
||||
private Date createdAt;
|
||||
private LocalDateTime createdAt;
|
||||
|
||||
private String contentDetails;
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Date;
|
||||
|
||||
@AllArgsConstructor
|
||||
@@ -19,7 +20,7 @@ public class StudentLessonPlansDO {
|
||||
|
||||
private Integer planId;
|
||||
|
||||
private Date startTime;
|
||||
private LocalDateTime startTime;
|
||||
|
||||
private BigDecimal masteryRat;
|
||||
|
||||
|
||||
@@ -14,4 +14,6 @@ public interface StudentLessonPlansDOMapper {
|
||||
Integer selectStudentPlanTotal();
|
||||
|
||||
Integer finfishStudentPlan(@Param("studentId") Integer studentId, @Param("planId") Integer planId, @Param("count") Integer count, @Param("mastery") double mastery);
|
||||
|
||||
List<StudentLessonPlansDO> selectStudentLessonPlanListByStudentId(@Param("studentId") Integer studentId);
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package com.yinlihupo.enlish.service.model.vo.plan;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Date;
|
||||
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@Data
|
||||
@Builder
|
||||
public class FindStudentPlanHistoryListRspVO {
|
||||
|
||||
private Integer id;
|
||||
|
||||
private Integer studentId;
|
||||
|
||||
private Integer planId;
|
||||
|
||||
private LocalDateTime startTime;
|
||||
|
||||
private BigDecimal masteryRat;
|
||||
|
||||
private Integer totalCount;
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package com.yinlihupo.enlish.service.model.vo.plan;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@Data
|
||||
@Builder
|
||||
public class FindStudentPlanHistoryReqVO {
|
||||
|
||||
private Integer studentId;
|
||||
}
|
||||
@@ -11,4 +11,6 @@ public interface StudentLessonPlansService {
|
||||
Integer findStudentLessonPlanTotal();
|
||||
|
||||
int finishStudentLessonPlan(Integer studentId, Integer planId);
|
||||
|
||||
List<StudentLessonPlansDO> findStudentLessonPlansByStudentId(Integer studentId);
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.*;
|
||||
|
||||
@Service
|
||||
@@ -104,7 +105,7 @@ public class LessonPlansServiceImpl implements LessonPlansService {
|
||||
.title(lessonPlanMap.get("title").toString())
|
||||
.gradeId(gradeDO.getId().toString())
|
||||
.unitId(unitDO.getId())
|
||||
.createdAt(new Date())
|
||||
.createdAt(LocalDateTime.now())
|
||||
.contentDetails(JsonUtils.toJsonString(lessonPlanMap))
|
||||
.build();
|
||||
lessonPlansDOMapper.insert(lessonPlansDO);
|
||||
@@ -132,7 +133,7 @@ public class LessonPlansServiceImpl implements LessonPlansService {
|
||||
.title(map.get("title").toString())
|
||||
.gradeId(gradeDO.getId().toString())
|
||||
.unitId(unitDO.getId())
|
||||
.createdAt(new Date())
|
||||
.createdAt(LocalDateTime.now())
|
||||
.contentDetails(JsonUtils.toJsonString(map))
|
||||
.build();
|
||||
lessonPlansDOMapper.insert(lessonPlansDO);
|
||||
|
||||
@@ -44,4 +44,9 @@ public class StudentLessonPlansServiceImpl implements StudentLessonPlansService
|
||||
|
||||
return studentLessonPlansDOMapper.finfishStudentPlan(studentId, planId, wordStrengthCount, (double) wordStrengthCount / wordTotal);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<StudentLessonPlansDO> findStudentLessonPlansByStudentId(Integer studentId) {
|
||||
return studentLessonPlansDOMapper.selectStudentLessonPlanListByStudentId(studentId);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,6 +45,12 @@
|
||||
SELECT count(DISTINCT student_id) AS total
|
||||
FROM student_lesson_plans;
|
||||
</select>
|
||||
<select id="selectStudentLessonPlanListByStudentId" resultMap="BaseResultMap">
|
||||
SELECT *
|
||||
FROM student_lesson_plans
|
||||
WHERE student_id = #{studentId}
|
||||
limit 500
|
||||
</select>
|
||||
|
||||
<update id="finfishStudentPlan">
|
||||
update student_lesson_plans
|
||||
|
||||
@@ -14,3 +14,9 @@ export function finishLessonPlan(studentId, planId) {
|
||||
planId: planId
|
||||
})
|
||||
}
|
||||
|
||||
export function findStudentPlanHistory(studentId) {
|
||||
return axios.post('/studentLessonPlans/history', {
|
||||
studentId: studentId
|
||||
})
|
||||
}
|
||||
|
||||
131
enlish-vue/src/layouts/components/student/PlanHistoryChart.vue
Normal file
131
enlish-vue/src/layouts/components/student/PlanHistoryChart.vue
Normal file
@@ -0,0 +1,131 @@
|
||||
<template>
|
||||
<div style="width: 100%; height: 260px;">
|
||||
<div ref="elRef" style="width: 100%; height: 100%;"></div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { defineProps, ref, onMounted, onUnmounted, watch } from 'vue'
|
||||
import { findStudentPlanHistory } from '@/api/studentLessonPlans'
|
||||
|
||||
const props = defineProps({
|
||||
studentId: { type: [Number, String], required: true }
|
||||
})
|
||||
|
||||
const elRef = ref(null)
|
||||
let chart = null
|
||||
let echartsLib = null
|
||||
const rows = ref([])
|
||||
|
||||
function sortData(arr) {
|
||||
return Array.isArray(arr)
|
||||
? arr.slice().sort((a, b) => new Date(a.startTime).getTime() - new Date(b.startTime).getTime())
|
||||
: []
|
||||
}
|
||||
|
||||
function toSource(arr) {
|
||||
return sortData(arr).map(it => ({
|
||||
startTime: it.startTime,
|
||||
totalCount: Number(it.totalCount) || 0,
|
||||
planId: it.planId ?? null,
|
||||
id: it.id ?? null
|
||||
}))
|
||||
}
|
||||
|
||||
function buildOption(source) {
|
||||
return {
|
||||
dataset: [
|
||||
{
|
||||
id: 'dataset_history',
|
||||
source: source
|
||||
}
|
||||
],
|
||||
tooltip: {
|
||||
trigger: 'axis'
|
||||
},
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
nameLocation: 'middle'
|
||||
},
|
||||
yAxis: {
|
||||
name: 'Total Count',
|
||||
min: 0
|
||||
},
|
||||
series: [
|
||||
{
|
||||
type: 'line',
|
||||
datasetId: 'dataset_history',
|
||||
showSymbol: false,
|
||||
encode: {
|
||||
x: 'startTime',
|
||||
y: 'totalCount',
|
||||
itemName: 'startTime',
|
||||
tooltip: ['totalCount']
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
function resize() {
|
||||
if (chart) chart.resize()
|
||||
}
|
||||
|
||||
async function ensureEcharts() {
|
||||
try {
|
||||
const mod = await import('echarts')
|
||||
echartsLib = mod
|
||||
} catch (e) {
|
||||
if (window.echarts) {
|
||||
echartsLib = window.echarts
|
||||
} else {
|
||||
await new Promise(resolve => {
|
||||
const s = document.createElement('script')
|
||||
s.src = 'https://cdn.jsdelivr.net/npm/echarts@5/dist/echarts.min.js'
|
||||
s.onload = resolve
|
||||
document.head.appendChild(s)
|
||||
})
|
||||
echartsLib = window.echarts
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function render() {
|
||||
if (!elRef.value) return
|
||||
await ensureEcharts()
|
||||
if (!echartsLib) return
|
||||
if (!chart) {
|
||||
chart = echartsLib.init(elRef.value)
|
||||
window.addEventListener('resize', resize)
|
||||
}
|
||||
const source = toSource(rows.value)
|
||||
const option = buildOption(source)
|
||||
chart.setOption(option)
|
||||
}
|
||||
|
||||
async function fetch() {
|
||||
if (props.studentId === undefined || props.studentId === null) return
|
||||
const res = await findStudentPlanHistory(Number(props.studentId))
|
||||
const d = res?.data?.data ?? []
|
||||
rows.value = Array.isArray(d) ? d : []
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
await fetch()
|
||||
await render()
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
window.removeEventListener('resize', resize)
|
||||
if (chart) {
|
||||
chart.dispose()
|
||||
chart = null
|
||||
}
|
||||
})
|
||||
|
||||
watch(() => props.studentId, async () => {
|
||||
await fetch()
|
||||
await render()
|
||||
})
|
||||
</script>
|
||||
|
||||
@@ -27,6 +27,10 @@
|
||||
<div class="text-md font-semibold mb-3">学生考试记录</div>
|
||||
<ExamHistoryChart :data="history" />
|
||||
</div>
|
||||
<div class="bg-white dark:bg-gray-800 rounded-lg shadow p-6">
|
||||
<div class="text-md font-semibold mb-3">学生学案记录</div>
|
||||
<PlanHistoryChart :student-id="route.params.id" />
|
||||
</div>
|
||||
</div>
|
||||
</el-main>
|
||||
|
||||
@@ -41,6 +45,7 @@ import { useRoute } from 'vue-router'
|
||||
import { getStudentDetail } from '@/api/student'
|
||||
import { getStudentExamHistory } from '@/api/exam'
|
||||
import ExamHistoryChart from '@/layouts/components/student/ExamHistoryChart.vue'
|
||||
import PlanHistoryChart from '@/layouts/components/student/PlanHistoryChart.vue'
|
||||
|
||||
const loading = ref(false)
|
||||
const detail = ref(null)
|
||||
|
||||
Reference in New Issue
Block a user