|
|
@@ -0,0 +1,373 @@
|
|
|
+<template>
|
|
|
+ <div class="app-container">
|
|
|
+ <el-row :gutter="20">
|
|
|
+ <splitpanes :horizontal="appStore.device === 'mobile'" class="default-theme">
|
|
|
+ <pane size="100">
|
|
|
+ <el-col>
|
|
|
+ <el-form :model="queryParams" ref="queryRef" :inline="true" v-show="showSearch" label-width="100px">
|
|
|
+ <el-form-item label="注册学校" prop="schoolId">
|
|
|
+ <IeSelect
|
|
|
+ v-model="selectedSchool"
|
|
|
+ :options="schoolList"
|
|
|
+ label-key="name"
|
|
|
+ value-key="id"
|
|
|
+ filterable
|
|
|
+ clearable
|
|
|
+ placeholder="请选择注册学校"
|
|
|
+ style="width: 240px"
|
|
|
+ @change="handleSchoolChange"
|
|
|
+ />
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="学校班级" prop="classId">
|
|
|
+ <IeSelect
|
|
|
+ v-model="selectedClass"
|
|
|
+ :options="classList"
|
|
|
+ label-key="name"
|
|
|
+ value-key="classId"
|
|
|
+ filterable
|
|
|
+ clearable
|
|
|
+ placeholder="请选择学校班级"
|
|
|
+ style="width: 240px"
|
|
|
+ />
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="培训校区" prop="campusId">
|
|
|
+ <IeSelect
|
|
|
+ v-model="selectedCampus"
|
|
|
+ :options="campusList"
|
|
|
+ label-key="name"
|
|
|
+ value-key="id"
|
|
|
+ filterable
|
|
|
+ clearable
|
|
|
+ placeholder="请选择培训校区"
|
|
|
+ style="width: 240px"
|
|
|
+ @change="handleCampusChange"
|
|
|
+ />
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="校区班级" prop="campusClassId">
|
|
|
+ <IeSelect
|
|
|
+ v-model="selectedCampusClass"
|
|
|
+ :options="campusClassList"
|
|
|
+ label-key="name"
|
|
|
+ value-key="classId"
|
|
|
+ filterable
|
|
|
+ clearable
|
|
|
+ placeholder="请选择校区班级"
|
|
|
+ style="width: 240px"
|
|
|
+ />
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="代理商" prop="agentId">
|
|
|
+ <IeAgentSelect
|
|
|
+ v-model="queryParams.agentId"
|
|
|
+ placeholder="请选择代理商"
|
|
|
+ clearable
|
|
|
+ filterable
|
|
|
+ style="width: 240px"
|
|
|
+ />
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="姓名" prop="nickName">
|
|
|
+ <el-input
|
|
|
+ v-model="queryParams.nickName"
|
|
|
+ placeholder="请输入姓名"
|
|
|
+ clearable
|
|
|
+ @keyup.enter="handleQuery"
|
|
|
+ style="width: 240px"
|
|
|
+ />
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="卡号" prop="cardNo">
|
|
|
+ <el-input
|
|
|
+ v-model="queryParams.cardNo"
|
|
|
+ placeholder="请输入卡号"
|
|
|
+ clearable
|
|
|
+ @keyup.enter="handleQuery"
|
|
|
+ style="width: 240px"
|
|
|
+ />
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item>
|
|
|
+ <el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
|
|
|
+ <el-button icon="Refresh" @click="resetQuery">重置</el-button>
|
|
|
+ </el-form-item>
|
|
|
+ </el-form>
|
|
|
+
|
|
|
+ <el-row :gutter="10" class="mb8">
|
|
|
+ <right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
|
|
|
+ </el-row>
|
|
|
+
|
|
|
+ <el-table
|
|
|
+ v-loading="loading"
|
|
|
+ :data="statisticsList"
|
|
|
+ >
|
|
|
+ <el-table-column label="注册学校" align="center" prop="schoolName" min-width="120">
|
|
|
+ <template #default="scope">
|
|
|
+ <span>{{ scope.row.schoolName || '-' }}</span>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column label="学校班级" align="center" prop="schoolClassName" min-width="120">
|
|
|
+ <template #default="scope">
|
|
|
+ <span>{{ scope.row.schoolClassName || '-' }}</span>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column label="培训校区" align="center" prop="campusName" min-width="120">
|
|
|
+ <template #default="scope">
|
|
|
+ <span>{{ scope.row.campusName || '-' }}</span>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column label="校区班级" align="center" prop="campusClassName" min-width="120">
|
|
|
+ <template #default="scope">
|
|
|
+ <span>{{ scope.row.campusClassName || '-' }}</span>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column label="代理商" align="center" prop="agentId" min-width="120">
|
|
|
+ <template #default="scope">
|
|
|
+ <span>{{ getAgentName(scope.row.agentId) }}</span>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column label="姓名" align="center" prop="nickName" min-width="100" />
|
|
|
+ <el-table-column label="卡号" align="center" prop="cardNo" min-width="120" />
|
|
|
+ <el-table-column label="刷题总数" align="center" prop="num" min-width="100" />
|
|
|
+ <el-table-column label="刷题正确率" align="center" prop="rate" min-width="120">
|
|
|
+ <template #default="scope">
|
|
|
+ <span>{{ formatRate(scope.row.rate) }}%</span>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column label="视频总数" align="center" prop="videoTotalTitles" min-width="100" />
|
|
|
+ <el-table-column label="累计观看时长" align="center" prop="videoDurationValue" min-width="140">
|
|
|
+ <template #default="scope">
|
|
|
+ <span>{{ formatDuration(scope.row.videoDurationValue) }}</span>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column label="试卷套数" align="center" prop="paperCount" min-width="100" />
|
|
|
+ <el-table-column label="平均正确率" align="center" prop="rate" min-width="120">
|
|
|
+ <template #default="scope">
|
|
|
+ <span>{{ formatRate(scope.row.rate) }}%</span>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ </el-table>
|
|
|
+
|
|
|
+ <pagination
|
|
|
+ v-show="total > 0"
|
|
|
+ :total="total"
|
|
|
+ v-model:page="queryParams.pageNum"
|
|
|
+ v-model:limit="queryParams.pageSize"
|
|
|
+ @pagination="getList"
|
|
|
+ />
|
|
|
+ </el-col>
|
|
|
+ </pane>
|
|
|
+ </splitpanes>
|
|
|
+ </el-row>
|
|
|
+ </div>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script setup name="StudyRecordStatistics">
|
|
|
+import { statisticStudyRecord } from "@/api/learn/student"
|
|
|
+import IeSelect from '@/components/IeSelect/index.vue'
|
|
|
+import IeAgentSelect from '@/components/IeAgentSelect/index.vue'
|
|
|
+import useAppStore from '@/store/modules/app'
|
|
|
+import useSchool from '@/hooks/useSchool'
|
|
|
+import { Splitpanes, Pane } from "splitpanes"
|
|
|
+import "splitpanes/dist/splitpanes.css"
|
|
|
+import Pagination from '@/components/Pagination/index.vue'
|
|
|
+import { getAgentList } from '@/api/dz/cards'
|
|
|
+import { getCurrentInstance, onMounted, ref, reactive, nextTick } from 'vue'
|
|
|
+
|
|
|
+const { proxy } = getCurrentInstance()
|
|
|
+const appStore = useAppStore()
|
|
|
+
|
|
|
+const {
|
|
|
+ reset,
|
|
|
+ schoolList,
|
|
|
+ selectedSchool,
|
|
|
+ classList,
|
|
|
+ selectedClass,
|
|
|
+ campusList,
|
|
|
+ selectedCampus,
|
|
|
+ campusClassList,
|
|
|
+ selectedCampusClass,
|
|
|
+} = useSchool({ loadCampus: true, loadClass: true })
|
|
|
+
|
|
|
+const statisticsList = ref([])
|
|
|
+const loading = ref(false)
|
|
|
+const showSearch = ref(true)
|
|
|
+const total = ref(0)
|
|
|
+const agentList = ref([])
|
|
|
+
|
|
|
+const queryParams = reactive({
|
|
|
+ pageNum: 1,
|
|
|
+ pageSize: 10,
|
|
|
+ schoolId: null,
|
|
|
+ classId: null,
|
|
|
+ campusId: null,
|
|
|
+ campusClassId: null,
|
|
|
+ agentId: null,
|
|
|
+ nickName: null,
|
|
|
+ cardNo: null
|
|
|
+})
|
|
|
+
|
|
|
+/** 查询统计列表 */
|
|
|
+function getList() {
|
|
|
+ loading.value = true
|
|
|
+ const params = {
|
|
|
+ pageNum: queryParams.pageNum,
|
|
|
+ pageSize: queryParams.pageSize
|
|
|
+ }
|
|
|
+
|
|
|
+ if (selectedSchool.value) {
|
|
|
+ params.schoolId = selectedSchool.value
|
|
|
+ }
|
|
|
+ if (selectedClass.value) {
|
|
|
+ params.classId = selectedClass.value
|
|
|
+ }
|
|
|
+ if (selectedCampus.value) {
|
|
|
+ params.campusId = selectedCampus.value
|
|
|
+ }
|
|
|
+ if (selectedCampusClass.value) {
|
|
|
+ params.campusClassId = selectedCampusClass.value
|
|
|
+ }
|
|
|
+ if (queryParams.agentId) {
|
|
|
+ params.agentId = queryParams.agentId
|
|
|
+ }
|
|
|
+ if (queryParams.nickName) {
|
|
|
+ params.nickName = queryParams.nickName
|
|
|
+ }
|
|
|
+ if (queryParams.cardNo) {
|
|
|
+ params.cardNo = queryParams.cardNo
|
|
|
+ }
|
|
|
+
|
|
|
+ statisticStudyRecord(params).then(response => {
|
|
|
+ console.log('统计接口响应:', response)
|
|
|
+ statisticsList.value = response.rows || []
|
|
|
+ total.value = response.total || 0
|
|
|
+ loading.value = false
|
|
|
+ }).catch(error => {
|
|
|
+ console.error('获取统计数据失败:', error)
|
|
|
+ proxy.$modal.msgError('获取统计数据失败')
|
|
|
+ loading.value = false
|
|
|
+ })
|
|
|
+}
|
|
|
+
|
|
|
+/** 搜索按钮操作 */
|
|
|
+function handleQuery() {
|
|
|
+ queryParams.pageNum = 1
|
|
|
+ getList()
|
|
|
+}
|
|
|
+
|
|
|
+/** 重置按钮操作 */
|
|
|
+function resetQuery() {
|
|
|
+ proxy.resetForm("queryRef")
|
|
|
+ reset()
|
|
|
+ queryParams.agentId = null
|
|
|
+ queryParams.nickName = null
|
|
|
+ queryParams.cardNo = null
|
|
|
+ handleQuery()
|
|
|
+}
|
|
|
+
|
|
|
+/** 学校变化处理 */
|
|
|
+function handleSchoolChange() {
|
|
|
+ selectedClass.value = null
|
|
|
+ getList()
|
|
|
+}
|
|
|
+
|
|
|
+/** 校区变化处理 */
|
|
|
+function handleCampusChange() {
|
|
|
+ selectedCampusClass.value = null
|
|
|
+ getList()
|
|
|
+}
|
|
|
+
|
|
|
+/** 获取学校名称 */
|
|
|
+function getSchoolName(schoolId) {
|
|
|
+ if (!schoolId || !schoolList.value || schoolList.value.length === 0) {
|
|
|
+ return '-'
|
|
|
+ }
|
|
|
+ const school = schoolList.value.find(s => s.id === schoolId)
|
|
|
+ return school ? school.name : '-'
|
|
|
+}
|
|
|
+
|
|
|
+/** 获取班级名称 */
|
|
|
+function getClassName(classId) {
|
|
|
+ if (!classId || !classList.value || classList.value.length === 0) {
|
|
|
+ return '-'
|
|
|
+ }
|
|
|
+ const cls = classList.value.find(c => c.classId === classId)
|
|
|
+ return cls ? cls.name : '-'
|
|
|
+}
|
|
|
+
|
|
|
+/** 获取校区名称 */
|
|
|
+function getCampusName(campusId) {
|
|
|
+ if (!campusId || !campusList.value || campusList.value.length === 0) {
|
|
|
+ return '-'
|
|
|
+ }
|
|
|
+ const campus = campusList.value.find(c => c.id === campusId)
|
|
|
+ return campus ? campus.name : '-'
|
|
|
+}
|
|
|
+
|
|
|
+/** 获取校区班级名称 */
|
|
|
+function getCampusClassName(classId) {
|
|
|
+ if (!classId || !campusClassList.value || campusClassList.value.length === 0) {
|
|
|
+ return '-'
|
|
|
+ }
|
|
|
+ const cls = campusClassList.value.find(c => c.classId === classId)
|
|
|
+ return cls ? cls.name : '-'
|
|
|
+}
|
|
|
+
|
|
|
+/** 获取代理商名称 */
|
|
|
+function getAgentName(agentId) {
|
|
|
+ if (!agentId || !agentList.value || agentList.value.length === 0) {
|
|
|
+ return '-'
|
|
|
+ }
|
|
|
+ const agent = agentList.value.find(a => a.agentId === agentId)
|
|
|
+ return agent ? agent.name : '-'
|
|
|
+}
|
|
|
+
|
|
|
+/** 格式化正确率 */
|
|
|
+function formatRate(rate) {
|
|
|
+ if (rate == null || rate === undefined) {
|
|
|
+ return '0.0'
|
|
|
+ }
|
|
|
+ return rate.toFixed(1)
|
|
|
+}
|
|
|
+
|
|
|
+/** 格式化时长(秒转小时分钟) */
|
|
|
+function formatDuration(seconds) {
|
|
|
+ if (!seconds || seconds === 0) {
|
|
|
+ return '0分钟'
|
|
|
+ }
|
|
|
+ const hours = Math.floor(seconds / 3600)
|
|
|
+ const minutes = Math.floor((seconds % 3600) / 60)
|
|
|
+ if (hours > 0) {
|
|
|
+ return `${hours}小时${minutes}分钟`
|
|
|
+ }
|
|
|
+ return `${minutes}分钟`
|
|
|
+}
|
|
|
+
|
|
|
+/** 加载代理商列表 */
|
|
|
+function loadAgentList() {
|
|
|
+ getAgentList({}).then(response => {
|
|
|
+ agentList.value = response.data || []
|
|
|
+ })
|
|
|
+}
|
|
|
+
|
|
|
+onMounted(() => {
|
|
|
+ console.log('页面初始化,开始加载数据')
|
|
|
+ loadAgentList()
|
|
|
+ // 直接调用,onMounted 已经确保组件挂载完成
|
|
|
+ getList()
|
|
|
+})
|
|
|
+</script>
|
|
|
+
|
|
|
+<style scoped>
|
|
|
+.app-container {
|
|
|
+ padding: 20px;
|
|
|
+}
|
|
|
+
|
|
|
+/* 确保 splitpanes 的 pane 可以滚动 */
|
|
|
+:deep(.splitpanes__pane) {
|
|
|
+ overflow: auto !important;
|
|
|
+ height: 100%;
|
|
|
+}
|
|
|
+
|
|
|
+/* 确保 el-col 内容正常显示,不被遮挡 */
|
|
|
+:deep(.splitpanes__pane .el-col) {
|
|
|
+ padding: 0;
|
|
|
+ overflow: visible;
|
|
|
+}
|
|
|
+</style>
|
|
|
+
|