feat: 添加统计首页, 基础数据,课堂报告页面
This commit is contained in:
parent
39020bd14c
commit
a0cb27afe4
@ -20,11 +20,13 @@
|
|||||||
"axios": "^1.11.0",
|
"axios": "^1.11.0",
|
||||||
"ckplayer": "^3.1.2",
|
"ckplayer": "^3.1.2",
|
||||||
"dplayer": "^1.27.1",
|
"dplayer": "^1.27.1",
|
||||||
|
"echarts": "5.6.0",
|
||||||
"naive-ui": "^2.42.0",
|
"naive-ui": "^2.42.0",
|
||||||
"naive-ui-editor": "^1.0.6",
|
"naive-ui-editor": "^1.0.6",
|
||||||
"pinia": "^3.0.3",
|
"pinia": "^3.0.3",
|
||||||
"quill": "^2.0.3",
|
"quill": "^2.0.3",
|
||||||
"vue": "^3.5.17",
|
"vue": "^3.5.17",
|
||||||
|
"vue-echarts": "7.0.3",
|
||||||
"vue-i18n": "^9.14.5",
|
"vue-i18n": "^9.14.5",
|
||||||
"vue-quill-editor": "^3.0.6",
|
"vue-quill-editor": "^3.0.6",
|
||||||
"vue-router": "^4.5.1"
|
"vue-router": "^4.5.1"
|
||||||
|
61
pnpm-lock.yaml
generated
61
pnpm-lock.yaml
generated
@ -29,6 +29,9 @@ importers:
|
|||||||
dplayer:
|
dplayer:
|
||||||
specifier: ^1.27.1
|
specifier: ^1.27.1
|
||||||
version: 1.27.1
|
version: 1.27.1
|
||||||
|
echarts:
|
||||||
|
specifier: 5.6.0
|
||||||
|
version: 5.6.0
|
||||||
naive-ui:
|
naive-ui:
|
||||||
specifier: ^2.42.0
|
specifier: ^2.42.0
|
||||||
version: 2.42.0(vue@3.5.18(typescript@5.9.2))
|
version: 2.42.0(vue@3.5.18(typescript@5.9.2))
|
||||||
@ -44,6 +47,9 @@ importers:
|
|||||||
vue:
|
vue:
|
||||||
specifier: ^3.5.17
|
specifier: ^3.5.17
|
||||||
version: 3.5.18(typescript@5.9.2)
|
version: 3.5.18(typescript@5.9.2)
|
||||||
|
vue-echarts:
|
||||||
|
specifier: 7.0.3
|
||||||
|
version: 7.0.3(@vue/runtime-core@3.5.18)(echarts@5.6.0)(vue@3.5.18(typescript@5.9.2))
|
||||||
vue-i18n:
|
vue-i18n:
|
||||||
specifier: ^9.14.5
|
specifier: ^9.14.5
|
||||||
version: 9.14.5(vue@3.5.18(typescript@5.9.2))
|
version: 9.14.5(vue@3.5.18(typescript@5.9.2))
|
||||||
@ -930,6 +936,9 @@ packages:
|
|||||||
resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==}
|
resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==}
|
||||||
engines: {node: '>= 0.4'}
|
engines: {node: '>= 0.4'}
|
||||||
|
|
||||||
|
echarts@5.6.0:
|
||||||
|
resolution: {integrity: sha512-oTbVTsXfKuEhxftHqL5xprgLoc0k7uScAwtryCgWF6hPYFLRwOUHiFmHGCBKP5NPFNkDVopOieyUqYGH8Fa3kA==}
|
||||||
|
|
||||||
electron-to-chromium@1.5.199:
|
electron-to-chromium@1.5.199:
|
||||||
resolution: {integrity: sha512-3gl0S7zQd88kCAZRO/DnxtBKuhMO4h0EaQIN3YgZfV6+pW+5+bf2AdQeHNESCoaQqo/gjGVYEf2YM4O5HJQqpQ==}
|
resolution: {integrity: sha512-3gl0S7zQd88kCAZRO/DnxtBKuhMO4h0EaQIN3YgZfV6+pW+5+bf2AdQeHNESCoaQqo/gjGVYEf2YM4O5HJQqpQ==}
|
||||||
|
|
||||||
@ -1478,6 +1487,9 @@ packages:
|
|||||||
treemate@0.3.11:
|
treemate@0.3.11:
|
||||||
resolution: {integrity: sha512-M8RGFoKtZ8dF+iwJfAJTOH/SM4KluKOKRJpjCMhI8bG3qB74zrFoArKZ62ll0Fr3mqkMJiQOmWYkdYgDeITYQg==}
|
resolution: {integrity: sha512-M8RGFoKtZ8dF+iwJfAJTOH/SM4KluKOKRJpjCMhI8bG3qB74zrFoArKZ62ll0Fr3mqkMJiQOmWYkdYgDeITYQg==}
|
||||||
|
|
||||||
|
tslib@2.3.0:
|
||||||
|
resolution: {integrity: sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==}
|
||||||
|
|
||||||
type@2.7.3:
|
type@2.7.3:
|
||||||
resolution: {integrity: sha512-8j+1QmAbPvLZow5Qpi6NCaN8FB60p/6x8/vfNqOk/hC+HuvFZhL4+WfekuhQLiqFZXOgQdrs3B+XxEmCc6b3FQ==}
|
resolution: {integrity: sha512-8j+1QmAbPvLZow5Qpi6NCaN8FB60p/6x8/vfNqOk/hC+HuvFZhL4+WfekuhQLiqFZXOgQdrs3B+XxEmCc6b3FQ==}
|
||||||
|
|
||||||
@ -1582,6 +1594,27 @@ packages:
|
|||||||
vscode-uri@3.1.0:
|
vscode-uri@3.1.0:
|
||||||
resolution: {integrity: sha512-/BpdSx+yCQGnCvecbyXdxHDkuk55/G3xwnC0GqY4gmQ3j+A+g8kzzgB4Nk/SINjqn6+waqw3EgbVF2QKExkRxQ==}
|
resolution: {integrity: sha512-/BpdSx+yCQGnCvecbyXdxHDkuk55/G3xwnC0GqY4gmQ3j+A+g8kzzgB4Nk/SINjqn6+waqw3EgbVF2QKExkRxQ==}
|
||||||
|
|
||||||
|
vue-demi@0.13.11:
|
||||||
|
resolution: {integrity: sha512-IR8HoEEGM65YY3ZJYAjMlKygDQn25D5ajNFNoKh9RSDMQtlzCxtfQjdQgv9jjK+m3377SsJXY8ysq8kLCZL25A==}
|
||||||
|
engines: {node: '>=12'}
|
||||||
|
hasBin: true
|
||||||
|
peerDependencies:
|
||||||
|
'@vue/composition-api': ^1.0.0-rc.1
|
||||||
|
vue: ^3.0.0-0 || ^2.6.0
|
||||||
|
peerDependenciesMeta:
|
||||||
|
'@vue/composition-api':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
vue-echarts@7.0.3:
|
||||||
|
resolution: {integrity: sha512-/jSxNwOsw5+dYAUcwSfkLwKPuzTQ0Cepz1LxCOpj2QcHrrmUa/Ql0eQqMmc1rTPQVrh2JQ29n2dhq75ZcHvRDw==}
|
||||||
|
peerDependencies:
|
||||||
|
'@vue/runtime-core': ^3.0.0
|
||||||
|
echarts: ^5.5.1
|
||||||
|
vue: ^2.7.0 || ^3.1.1
|
||||||
|
peerDependenciesMeta:
|
||||||
|
'@vue/runtime-core':
|
||||||
|
optional: true
|
||||||
|
|
||||||
vue-i18n@9.14.5:
|
vue-i18n@9.14.5:
|
||||||
resolution: {integrity: sha512-0jQ9Em3ymWngyiIkj0+c/k7WgaPO+TNzjKSNq9BvBQaKJECqn9cd9fL4tkDhB5G1QBskGl9YxxbDAhgbFtpe2g==}
|
resolution: {integrity: sha512-0jQ9Em3ymWngyiIkj0+c/k7WgaPO+TNzjKSNq9BvBQaKJECqn9cd9fL4tkDhB5G1QBskGl9YxxbDAhgbFtpe2g==}
|
||||||
engines: {node: '>= 16'}
|
engines: {node: '>= 16'}
|
||||||
@ -1635,6 +1668,9 @@ packages:
|
|||||||
resolution: {integrity: sha512-GQHQqAopRhwU8Kt1DDM8NjibDXHC8eoh1erhGAJPEyveY9qqVeXvVikNKrDz69sHowPMorbPUrH/mx8c50eiBQ==}
|
resolution: {integrity: sha512-GQHQqAopRhwU8Kt1DDM8NjibDXHC8eoh1erhGAJPEyveY9qqVeXvVikNKrDz69sHowPMorbPUrH/mx8c50eiBQ==}
|
||||||
engines: {node: '>=18'}
|
engines: {node: '>=18'}
|
||||||
|
|
||||||
|
zrender@5.6.1:
|
||||||
|
resolution: {integrity: sha512-OFXkDJKcrlx5su2XbzJvj/34Q3m6PvyCZkVPHGYpcCJ52ek4U/ymZyfuV1nKE23AyBJ51E/6Yr0mhZ7xGTO4ag==}
|
||||||
|
|
||||||
snapshots:
|
snapshots:
|
||||||
|
|
||||||
'@ampproject/remapping@2.3.0':
|
'@ampproject/remapping@2.3.0':
|
||||||
@ -2504,6 +2540,11 @@ snapshots:
|
|||||||
es-errors: 1.3.0
|
es-errors: 1.3.0
|
||||||
gopd: 1.2.0
|
gopd: 1.2.0
|
||||||
|
|
||||||
|
echarts@5.6.0:
|
||||||
|
dependencies:
|
||||||
|
tslib: 2.3.0
|
||||||
|
zrender: 5.6.1
|
||||||
|
|
||||||
electron-to-chromium@1.5.199: {}
|
electron-to-chromium@1.5.199: {}
|
||||||
|
|
||||||
entities@4.5.0: {}
|
entities@4.5.0: {}
|
||||||
@ -3061,6 +3102,8 @@ snapshots:
|
|||||||
|
|
||||||
treemate@0.3.11: {}
|
treemate@0.3.11: {}
|
||||||
|
|
||||||
|
tslib@2.3.0: {}
|
||||||
|
|
||||||
type@2.7.3: {}
|
type@2.7.3: {}
|
||||||
|
|
||||||
typescript@5.9.2: {}
|
typescript@5.9.2: {}
|
||||||
@ -3152,6 +3195,20 @@ snapshots:
|
|||||||
|
|
||||||
vscode-uri@3.1.0: {}
|
vscode-uri@3.1.0: {}
|
||||||
|
|
||||||
|
vue-demi@0.13.11(vue@3.5.18(typescript@5.9.2)):
|
||||||
|
dependencies:
|
||||||
|
vue: 3.5.18(typescript@5.9.2)
|
||||||
|
|
||||||
|
vue-echarts@7.0.3(@vue/runtime-core@3.5.18)(echarts@5.6.0)(vue@3.5.18(typescript@5.9.2)):
|
||||||
|
dependencies:
|
||||||
|
echarts: 5.6.0
|
||||||
|
vue: 3.5.18(typescript@5.9.2)
|
||||||
|
vue-demi: 0.13.11(vue@3.5.18(typescript@5.9.2))
|
||||||
|
optionalDependencies:
|
||||||
|
'@vue/runtime-core': 3.5.18
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- '@vue/composition-api'
|
||||||
|
|
||||||
vue-i18n@9.14.5(vue@3.5.18(typescript@5.9.2)):
|
vue-i18n@9.14.5(vue@3.5.18(typescript@5.9.2)):
|
||||||
dependencies:
|
dependencies:
|
||||||
'@intlify/core-base': 9.14.5
|
'@intlify/core-base': 9.14.5
|
||||||
@ -3209,3 +3266,7 @@ snapshots:
|
|||||||
yallist@3.1.1: {}
|
yallist@3.1.1: {}
|
||||||
|
|
||||||
yoctocolors@2.1.1: {}
|
yoctocolors@2.1.1: {}
|
||||||
|
|
||||||
|
zrender@5.6.1:
|
||||||
|
dependencies:
|
||||||
|
tslib: 2.3.0
|
||||||
|
32
src/main.ts
32
src/main.ts
@ -14,6 +14,35 @@ import '@/assets/fonts/庞门正道标题体3.0.ttf'
|
|||||||
import '@/assets/fonts/DouyinSansBold.otf'
|
import '@/assets/fonts/DouyinSansBold.otf'
|
||||||
import '@/assets/fonts/Alibaba_PuHuiTi_2.0_55_Regular_85_Bold.ttf'
|
import '@/assets/fonts/Alibaba_PuHuiTi_2.0_55_Regular_85_Bold.ttf'
|
||||||
|
|
||||||
|
// ECharts 全局注册
|
||||||
|
import VChart from 'vue-echarts'
|
||||||
|
import { use } from 'echarts/core'
|
||||||
|
import { CanvasRenderer } from 'echarts/renderers'
|
||||||
|
import { LineChart, BarChart, PieChart, ScatterChart } from 'echarts/charts'
|
||||||
|
import {
|
||||||
|
TitleComponent,
|
||||||
|
TooltipComponent,
|
||||||
|
LegendComponent,
|
||||||
|
GridComponent,
|
||||||
|
DataZoomComponent,
|
||||||
|
GraphicComponent
|
||||||
|
} from 'echarts/components'
|
||||||
|
|
||||||
|
// 注册 ECharts 组件
|
||||||
|
use([
|
||||||
|
CanvasRenderer,
|
||||||
|
LineChart,
|
||||||
|
BarChart,
|
||||||
|
PieChart,
|
||||||
|
ScatterChart,
|
||||||
|
TitleComponent,
|
||||||
|
TooltipComponent,
|
||||||
|
LegendComponent,
|
||||||
|
GridComponent,
|
||||||
|
DataZoomComponent,
|
||||||
|
GraphicComponent
|
||||||
|
])
|
||||||
|
|
||||||
// Naive UI
|
// Naive UI
|
||||||
import {
|
import {
|
||||||
create,
|
create,
|
||||||
@ -173,6 +202,9 @@ app.use(router)
|
|||||||
app.use(i18n)
|
app.use(i18n)
|
||||||
app.use(naive)
|
app.use(naive)
|
||||||
|
|
||||||
|
// 全局注册 VChart 组件
|
||||||
|
app.component('v-chart', VChart)
|
||||||
|
|
||||||
// 初始化用户认证状态
|
// 初始化用户认证状态
|
||||||
const userStore = useUserStore()
|
const userStore = useUserStore()
|
||||||
|
|
||||||
|
@ -52,7 +52,7 @@ import FileViewer from '@/views/teacher/course/FileViewer.vue'
|
|||||||
import FolderBrowser from '@/views/teacher/course/FolderBrowser.vue'
|
import FolderBrowser from '@/views/teacher/course/FolderBrowser.vue'
|
||||||
import CertificateManagement from '@/views/teacher/certificate/CertificateManagement.vue'
|
import CertificateManagement from '@/views/teacher/certificate/CertificateManagement.vue'
|
||||||
import DiscussionManagement from '@/views/teacher/course/DiscussionManagement.vue'
|
import DiscussionManagement from '@/views/teacher/course/DiscussionManagement.vue'
|
||||||
import StatisticsManagement from '@/views/teacher/course/StatisticsManagement.vue'
|
import StatisticsManagement from '@/views/teacher/statistics/StatisticsManagement.vue'
|
||||||
import NotificationManagement from '@/views/teacher/course/NotificationManagement.vue'
|
import NotificationManagement from '@/views/teacher/course/NotificationManagement.vue'
|
||||||
import GeneralManagement from '@/views/teacher/course/GeneralManagement.vue'
|
import GeneralManagement from '@/views/teacher/course/GeneralManagement.vue'
|
||||||
|
|
||||||
|
@ -1,36 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="statistics-management">
|
|
||||||
<div class="content-placeholder">
|
|
||||||
<h2>统计管理</h2>
|
|
||||||
<p>统计管理功能正在开发中...</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup lang="ts">
|
|
||||||
// 统计管理逻辑
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
.statistics-management {
|
|
||||||
padding: 20px;
|
|
||||||
background: #fff;
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.content-placeholder {
|
|
||||||
text-align: center;
|
|
||||||
padding: 60px 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.content-placeholder h2 {
|
|
||||||
font-size: 24px;
|
|
||||||
color: #333;
|
|
||||||
margin-bottom: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.content-placeholder p {
|
|
||||||
font-size: 16px;
|
|
||||||
color: #666;
|
|
||||||
}
|
|
||||||
</style>
|
|
432
src/views/teacher/statistics/StatisticsManagement.vue
Normal file
432
src/views/teacher/statistics/StatisticsManagement.vue
Normal file
@ -0,0 +1,432 @@
|
|||||||
|
<template>
|
||||||
|
<div class="statistics-management">
|
||||||
|
<!-- 顶部区域:课程信息和图表 -->
|
||||||
|
<div class="top-section">
|
||||||
|
<div class="statistics-container">
|
||||||
|
<!-- 左侧课程信息面板 -->
|
||||||
|
<div class="course-info-panel">
|
||||||
|
<div class="course-thumbnail">
|
||||||
|
<img src="/images/teacher/fj.png" alt="课程封面" class="thumbnail-image" />
|
||||||
|
</div>
|
||||||
|
<div class="course-details">
|
||||||
|
<h3 class="course-name">课程名称课程名称课</h3>
|
||||||
|
<div class="course-meta">
|
||||||
|
<p class="instructor">课程讲师: 王建国</p>
|
||||||
|
<p class="activity">学生活跃度: 74</p>
|
||||||
|
</div>
|
||||||
|
<button class="course-details-btn" @click="goToCourseDetails">课程详情</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 右侧活动图表面板 -->
|
||||||
|
<div class="activity-chart-panel">
|
||||||
|
<h3 class="chart-title">近7日课程活跃数: 20</h3>
|
||||||
|
<div class="chart-container">
|
||||||
|
<v-chart :option="chartOption" autoresize style="height: 100%;" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 中间区域:标签页 -->
|
||||||
|
<div class="middle-section">
|
||||||
|
<div class="tab-container">
|
||||||
|
<div class="tab-bar">
|
||||||
|
<div
|
||||||
|
v-for="(tab, index) in tabs"
|
||||||
|
:key="index"
|
||||||
|
class="tab-item"
|
||||||
|
:class="{ active: activeTab === index }"
|
||||||
|
@click="switchTab(index)"
|
||||||
|
>
|
||||||
|
<span>{{ tab.name }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Tab内容区域 -->
|
||||||
|
<div class="tab-content">
|
||||||
|
<transition name="tab-fade" mode="out-in">
|
||||||
|
<component :is="currentTabComponent" :key="activeTab" />
|
||||||
|
</transition>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ref, computed } from 'vue'
|
||||||
|
import { useRouter, useRoute } from 'vue-router'
|
||||||
|
import type { EChartsOption } from 'echarts'
|
||||||
|
|
||||||
|
// 导入tab组件
|
||||||
|
import BasicData from './tab/BasicData.vue'
|
||||||
|
import ClassroomReport from './tab/ClassroomReport.vue'
|
||||||
|
import LearningStatistics from './tab/LearningStatistics.vue'
|
||||||
|
import StudentGrades from './tab/StudentGrades.vue'
|
||||||
|
import LearningMonitor from './tab/LearningMonitor.vue'
|
||||||
|
|
||||||
|
// 路由相关
|
||||||
|
const router = useRouter()
|
||||||
|
const route = useRoute()
|
||||||
|
|
||||||
|
// Tab相关状态
|
||||||
|
const activeTab = ref(0)
|
||||||
|
|
||||||
|
// Tab配置
|
||||||
|
const tabs = [
|
||||||
|
{ name: '基础数据', component: BasicData },
|
||||||
|
{ name: '课堂报告', component: ClassroomReport },
|
||||||
|
{ name: '学情统计', component: LearningStatistics },
|
||||||
|
{ name: '学生成绩', component: StudentGrades },
|
||||||
|
{ name: '学习监控', component: LearningMonitor }
|
||||||
|
]
|
||||||
|
|
||||||
|
// 当前显示的tab组件
|
||||||
|
const currentTabComponent = computed(() => {
|
||||||
|
return tabs[activeTab.value].component
|
||||||
|
})
|
||||||
|
|
||||||
|
// 切换tab
|
||||||
|
const switchTab = (index: number) => {
|
||||||
|
activeTab.value = index
|
||||||
|
}
|
||||||
|
|
||||||
|
// 跳转到教师端课程详情页面
|
||||||
|
const goToCourseDetails = () => {
|
||||||
|
// 获取当前课程ID(从路由参数中获取)
|
||||||
|
const courseId = route.params.id || '1' // 如果没有ID参数,使用默认值
|
||||||
|
router.push(`/teacher/course-detail/${courseId}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ECharts配置
|
||||||
|
const chartOption = ref<EChartsOption>({
|
||||||
|
tooltip: {
|
||||||
|
trigger: 'axis',
|
||||||
|
axisPointer: {
|
||||||
|
type: 'line',
|
||||||
|
lineStyle: {
|
||||||
|
type: 'dashed',
|
||||||
|
color: '#ccc'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
formatter: function(params: any) {
|
||||||
|
return `<div style="padding: 8px;">
|
||||||
|
<div style="font-size: 12px; color: #999; margin-bottom: 4px;">活跃数</div>
|
||||||
|
<div style="font-size: 16px; font-weight: bold; color: #333;">${params[0].value}</div>
|
||||||
|
</div>`
|
||||||
|
}
|
||||||
|
},
|
||||||
|
grid: {
|
||||||
|
left: '2%',
|
||||||
|
right: '6%',
|
||||||
|
bottom: '15%',
|
||||||
|
top: '15%',
|
||||||
|
containLabel: true
|
||||||
|
},
|
||||||
|
xAxis: {
|
||||||
|
type: 'category',
|
||||||
|
boundaryGap: false,
|
||||||
|
data: ['2022-04-02', '2022-04-05', '2022-04-08', '2022-04-11', '2022-04-14', '2022-04-17'],
|
||||||
|
axisLine: {
|
||||||
|
show: false
|
||||||
|
},
|
||||||
|
axisTick: {
|
||||||
|
show: false
|
||||||
|
},
|
||||||
|
axisLabel: {
|
||||||
|
color: '#666',
|
||||||
|
fontSize: 9,
|
||||||
|
margin: 8
|
||||||
|
},
|
||||||
|
splitLine: {
|
||||||
|
show: true,
|
||||||
|
lineStyle: {
|
||||||
|
type: 'dashed',
|
||||||
|
color: '#f0f0f0'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
yAxis: {
|
||||||
|
type: 'value',
|
||||||
|
min: 0,
|
||||||
|
max: 14000,
|
||||||
|
interval: 2000,
|
||||||
|
axisLine: {
|
||||||
|
show: false
|
||||||
|
},
|
||||||
|
axisTick: {
|
||||||
|
show: false
|
||||||
|
},
|
||||||
|
axisLabel: {
|
||||||
|
color: '#666',
|
||||||
|
fontSize: 9,
|
||||||
|
margin: 8
|
||||||
|
},
|
||||||
|
splitLine: {
|
||||||
|
show: true,
|
||||||
|
lineStyle: {
|
||||||
|
type: 'dashed',
|
||||||
|
color: '#f0f0f0'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
series: [
|
||||||
|
{
|
||||||
|
name: '活跃数',
|
||||||
|
type: 'line',
|
||||||
|
smooth: false,
|
||||||
|
symbol: 'none',
|
||||||
|
lineStyle: {
|
||||||
|
color: '#2196F3',
|
||||||
|
width: 1.5
|
||||||
|
},
|
||||||
|
areaStyle: {
|
||||||
|
color: {
|
||||||
|
type: 'linear',
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
x2: 0,
|
||||||
|
y2: 1,
|
||||||
|
colorStops: [
|
||||||
|
{
|
||||||
|
offset: 0,
|
||||||
|
color: 'rgba(33, 150, 243, 0.25)'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
offset: 1,
|
||||||
|
color: 'rgba(33, 150, 243, 0.02)'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data: [2000, 10500, 12000, 9800, 11000, 7500]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
})
|
||||||
|
|
||||||
|
console.log('CourseStatistics component loaded with ECharts and Tab functionality')
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.statistics-management {
|
||||||
|
background: #fff;
|
||||||
|
min-height: 100vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 顶部区域 */
|
||||||
|
.top-section {
|
||||||
|
margin-bottom: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.statistics-container {
|
||||||
|
padding: 20px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: 20px;
|
||||||
|
margin: 0 auto;
|
||||||
|
border-bottom: 1.5px solid #F1F3F4;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 调整左右面板比例:左侧75%,右侧25% */
|
||||||
|
.course-info-panel {
|
||||||
|
flex: 0 0 60%;
|
||||||
|
background: white;
|
||||||
|
display: flex;
|
||||||
|
gap: 12px;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.activity-chart-panel {
|
||||||
|
flex: 0 0 30%;
|
||||||
|
background: white;
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 8px;
|
||||||
|
/* 从12px减少到8px */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 左侧课程信息面板样式 */
|
||||||
|
|
||||||
|
.course-thumbnail {
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.thumbnail-image {
|
||||||
|
width: 194px;
|
||||||
|
height: 136px;
|
||||||
|
object-fit: cover;
|
||||||
|
background: linear-gradient(135deg, #4CAF50, #8BC34A, #FFEB3B);
|
||||||
|
}
|
||||||
|
|
||||||
|
.course-details {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.course-name {
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: 500;
|
||||||
|
color: #333;
|
||||||
|
margin: 0;
|
||||||
|
line-height: 1.3;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.course-meta {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.instructor,
|
||||||
|
.activity {
|
||||||
|
font-size: 14px;
|
||||||
|
color: #333;
|
||||||
|
line-height: 1.3;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.course-details-btn {
|
||||||
|
background-color: #fff;
|
||||||
|
align-self: flex-start;
|
||||||
|
border: 1.5px solid #0C99DA;
|
||||||
|
color: #0C99DA;
|
||||||
|
padding: 6px 12px;
|
||||||
|
border-radius: 2px;
|
||||||
|
font-size: 12px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
margin-top: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.course-details-btn:hover {
|
||||||
|
background: #BBDEFB;
|
||||||
|
border-color: #1976D2;
|
||||||
|
color: #1976D2;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 右侧活动图表面板样式 */
|
||||||
|
|
||||||
|
.chart-title {
|
||||||
|
padding-left: 40px;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 500;
|
||||||
|
color: #666;
|
||||||
|
margin: 0 0 16px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chart-container {
|
||||||
|
position: relative;
|
||||||
|
height: 120px;
|
||||||
|
/* 从160px减少到120px */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ECharts容器样式 */
|
||||||
|
.chart-container :deep(.echarts) {
|
||||||
|
width: 100% !important;
|
||||||
|
height: 100% !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 中间区域:标签页 */
|
||||||
|
.middle-section {
|
||||||
|
margin-bottom: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-container {
|
||||||
|
display: flex;
|
||||||
|
border-bottom: 1.5px solid #F1F3F4;
|
||||||
|
padding-bottom: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-bar {
|
||||||
|
display: flex;
|
||||||
|
background: white;
|
||||||
|
border: 1.5px solid #0C99DA;
|
||||||
|
border-radius: 4px;
|
||||||
|
overflow: hidden;
|
||||||
|
margin-left: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-item {
|
||||||
|
min-width: 151px;
|
||||||
|
flex: 1;
|
||||||
|
padding: 5px 10px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
text-align: center;
|
||||||
|
background: white;
|
||||||
|
color: #2196F3;
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 500;
|
||||||
|
border-right: 1.5px solid #0C99DA;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-item:last-child {
|
||||||
|
border-right: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-item.active {
|
||||||
|
background: #0C99DA;
|
||||||
|
color: white;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-item:hover:not(.active) {
|
||||||
|
background: #F5F9FF;
|
||||||
|
color: #1976D2;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Tab内容区域 */
|
||||||
|
.tab-content {
|
||||||
|
background: white;
|
||||||
|
min-height: 200px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Tab切换过渡动画 */
|
||||||
|
.tab-fade-enter-active,
|
||||||
|
.tab-fade-leave-active {
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-fade-enter-from {
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateX(20px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-fade-leave-to {
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateX(-20px);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* 响应式设计 */
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.statistics-container {
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.course-info-panel {
|
||||||
|
flex-direction: column;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.course-details-btn {
|
||||||
|
align-self: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-bar {
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-item {
|
||||||
|
flex: none;
|
||||||
|
min-width: 120px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
295
src/views/teacher/statistics/tab/BasicData.vue
Normal file
295
src/views/teacher/statistics/tab/BasicData.vue
Normal file
@ -0,0 +1,295 @@
|
|||||||
|
<template>
|
||||||
|
<div class="basic-data">
|
||||||
|
<!-- 顶部统计卡片区域 -->
|
||||||
|
<div class="stats-cards">
|
||||||
|
<div class="stats-row">
|
||||||
|
<div class="stat-card">
|
||||||
|
<div class="stat-label">学生人数</div>
|
||||||
|
<div class="stat-value">
|
||||||
|
<span class="stat-number">0</span>
|
||||||
|
<span class="stat-unit">人</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="stat-card">
|
||||||
|
<div class="stat-label">班级</div>
|
||||||
|
<div class="stat-value">
|
||||||
|
<span class="stat-number">0</span>
|
||||||
|
<span class="stat-unit">个</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="stat-card">
|
||||||
|
<div class="stat-label">作业</div>
|
||||||
|
<div class="stat-value">
|
||||||
|
<span class="stat-number">0</span>
|
||||||
|
<span class="stat-unit">次</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="stat-card">
|
||||||
|
<div class="stat-label">考试</div>
|
||||||
|
<div class="stat-value">
|
||||||
|
<span class="stat-number">0</span>
|
||||||
|
<span class="stat-unit">次</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="stat-card">
|
||||||
|
<div class="stat-label">练习</div>
|
||||||
|
<div class="stat-value">
|
||||||
|
<span class="stat-number">0</span>
|
||||||
|
<span class="stat-unit">次</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="stat-card">
|
||||||
|
<div class="stat-label">讨论</div>
|
||||||
|
<div class="stat-value">
|
||||||
|
<span class="stat-number">0</span>
|
||||||
|
<span class="stat-unit">次</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="stat-card">
|
||||||
|
<div class="stat-label">证书</div>
|
||||||
|
<div class="stat-value">
|
||||||
|
<span class="stat-number">0</span>
|
||||||
|
<span class="stat-unit">个</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 教学建设区域 -->
|
||||||
|
<div class="teaching-construction">
|
||||||
|
<h3 class="section-title">教学建设</h3>
|
||||||
|
<div class="construction-grid">
|
||||||
|
<!-- 课件/视频 -->
|
||||||
|
<div class="construction-card blue" style="background-image: url('/images/teacher/teaching-construction1.png');">
|
||||||
|
<div class="card-icon">
|
||||||
|
<img src="/images/teacher/teaching-construction1.png" alt="课件/视频" />
|
||||||
|
</div>
|
||||||
|
<div class="card-content">
|
||||||
|
<div class="card-number">20</div>
|
||||||
|
<div class="card-label">课件/视频</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 资料/文档 -->
|
||||||
|
<div class="construction-card orange" style="background-image: url('/images/teacher/teaching-construction2.png');">
|
||||||
|
<div class="card-icon">
|
||||||
|
<img src="/images/teacher/teaching-construction2.png" alt="资料/文档" />
|
||||||
|
</div>
|
||||||
|
<div class="card-content">
|
||||||
|
<div class="card-number">139</div>
|
||||||
|
<div class="card-label">资料/文档</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 题库总数 -->
|
||||||
|
<div class="construction-card blue-gray" style="background-image: url('/images/teacher/teaching-construction3.png');">
|
||||||
|
<div class="card-icon">
|
||||||
|
<img src="/images/teacher/teaching-construction3.png" alt="题库总数" />
|
||||||
|
</div>
|
||||||
|
<div class="card-content">
|
||||||
|
<div class="card-number">862</div>
|
||||||
|
<div class="card-label">题库总数</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 试卷总数 -->
|
||||||
|
<div class="construction-card yellow" style="background-image: url('/images/teacher/teaching-construction4.png');">
|
||||||
|
<div class="card-icon">
|
||||||
|
<img src="/images/teacher/teaching-construction4.png" alt="试卷总数" />
|
||||||
|
</div>
|
||||||
|
<div class="card-content">
|
||||||
|
<div class="card-number">10</div>
|
||||||
|
<div class="card-label">试卷总数</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
console.log('BasicData component loaded')
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.basic-data {
|
||||||
|
padding: 0 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 顶部统计卡片区域 */
|
||||||
|
.stats-cards {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stats-row {
|
||||||
|
display: flex;
|
||||||
|
gap: 16px;
|
||||||
|
justify-content: space-between;
|
||||||
|
border: 1px solid #F1F3F4;
|
||||||
|
background-color: #FCFCFC;
|
||||||
|
border-radius: 2px;
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-card {
|
||||||
|
flex: 1;
|
||||||
|
text-align: center;
|
||||||
|
min-height: 80px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-label {
|
||||||
|
font-size: 12px;
|
||||||
|
color: #999999;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
line-height: 1.2;
|
||||||
|
white-space: nowrap;
|
||||||
|
width: 50px;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-value {
|
||||||
|
display: flex;
|
||||||
|
align-items: baseline;
|
||||||
|
gap: 2px;
|
||||||
|
line-height: 1.2;
|
||||||
|
white-space: nowrap;
|
||||||
|
width: 50px;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-number {
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: 500;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-unit {
|
||||||
|
font-size: 10px;
|
||||||
|
color: #666;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 教学建设区域 */
|
||||||
|
.teaching-construction {
|
||||||
|
margin-top: 26px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section-title {
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 500;
|
||||||
|
color: #333;
|
||||||
|
margin: 0 0 12px 0;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.construction-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(4, 1fr);
|
||||||
|
gap: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.construction-card {
|
||||||
|
background: white;
|
||||||
|
background-size: contain;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-position: center;
|
||||||
|
border-radius: 2px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 24px;
|
||||||
|
transition: transform 0.3s ease;
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
min-height: 95px;
|
||||||
|
padding: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.construction-card:hover {
|
||||||
|
transform: translateY(-2px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-icon {
|
||||||
|
width: 48px;
|
||||||
|
height: 48px;
|
||||||
|
border-radius: 8px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
flex-shrink: 0;
|
||||||
|
position: relative;
|
||||||
|
z-index: 2;
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-icon img {
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
filter: brightness(0) invert(1); /* 使图标变为白色 */
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-content {
|
||||||
|
text-align: left;
|
||||||
|
position: relative;
|
||||||
|
z-index: 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-number {
|
||||||
|
font-size: 22px;
|
||||||
|
font-weight: 500;
|
||||||
|
color: #646464;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
line-height: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-label {
|
||||||
|
font-size: 14px;
|
||||||
|
color: #646464;
|
||||||
|
line-height: 1.2;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* 卡片颜色主题 */
|
||||||
|
.construction-card.blue .card-icon {
|
||||||
|
background: #2196F3;
|
||||||
|
}
|
||||||
|
|
||||||
|
.construction-card.orange .card-icon {
|
||||||
|
background: #FF9800;
|
||||||
|
}
|
||||||
|
|
||||||
|
.construction-card.blue-gray .card-icon {
|
||||||
|
background: #607D8B;
|
||||||
|
}
|
||||||
|
|
||||||
|
.construction-card.yellow .card-icon {
|
||||||
|
background: #FFC107;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 响应式设计 */
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.stats-row {
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-card {
|
||||||
|
flex: 1 1 calc(50% - 6px);
|
||||||
|
min-width: 120px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.construction-grid {
|
||||||
|
grid-template-columns: repeat(2, 1fr);
|
||||||
|
max-width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 480px) {
|
||||||
|
.stat-card {
|
||||||
|
flex: 1 1 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
429
src/views/teacher/statistics/tab/ClassroomReport.vue
Normal file
429
src/views/teacher/statistics/tab/ClassroomReport.vue
Normal file
@ -0,0 +1,429 @@
|
|||||||
|
<template>
|
||||||
|
<div class="classroom-report">
|
||||||
|
<!-- 课程访问量图表 -->
|
||||||
|
<div class="chart-section">
|
||||||
|
<div class="chart-header">
|
||||||
|
<h3 class="chart-title">课程访问量</h3>
|
||||||
|
<div class="time-selector">
|
||||||
|
<span
|
||||||
|
class="time-option"
|
||||||
|
:class="{ active: visitVolumeTimeRange === 'week' }"
|
||||||
|
@click="switchVisitVolumeTimeRange('week')"
|
||||||
|
>
|
||||||
|
本周
|
||||||
|
</span>
|
||||||
|
<span
|
||||||
|
class="time-option"
|
||||||
|
:class="{ active: visitVolumeTimeRange === 'month' }"
|
||||||
|
@click="switchVisitVolumeTimeRange('month')"
|
||||||
|
>
|
||||||
|
本月
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="chart-container">
|
||||||
|
<v-chart :option="visitVolumeOption" style="height: 100%;" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 课程互动图表 -->
|
||||||
|
<div class="chart-section">
|
||||||
|
<div class="chart-header chart-header2">
|
||||||
|
<h3 class="chart-title">课程互动</h3>
|
||||||
|
<div class="time-selector">
|
||||||
|
<span
|
||||||
|
class="time-option"
|
||||||
|
:class="{ active: interactionTimeRange === 'week' }"
|
||||||
|
@click="switchInteractionTimeRange('week')"
|
||||||
|
>
|
||||||
|
本周
|
||||||
|
</span>
|
||||||
|
<span
|
||||||
|
class="time-option"
|
||||||
|
:class="{ active: interactionTimeRange === 'month' }"
|
||||||
|
@click="switchInteractionTimeRange('month')"
|
||||||
|
>
|
||||||
|
本月
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="chart-wrapper">
|
||||||
|
<div class="chart-legend">
|
||||||
|
<div class="legend-item">
|
||||||
|
<div class="legend-color" style="background-color: #81A2FF;"></div>
|
||||||
|
<span>评论</span>
|
||||||
|
</div>
|
||||||
|
<div class="legend-item">
|
||||||
|
<div class="legend-color" style="background-color: #3DC8F4;"></div>
|
||||||
|
<span>弹幕</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="chart-container">
|
||||||
|
<v-chart :option="interactionOption" style="height: 100%;" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ref, computed } from 'vue'
|
||||||
|
import type { EChartsOption } from 'echarts'
|
||||||
|
|
||||||
|
// 时间范围选择
|
||||||
|
const visitVolumeTimeRange = ref<'week' | 'month'>('week')
|
||||||
|
const interactionTimeRange = ref<'week' | 'month'>('week')
|
||||||
|
|
||||||
|
const switchVisitVolumeTimeRange = (range: 'week' | 'month') => {
|
||||||
|
visitVolumeTimeRange.value = range
|
||||||
|
}
|
||||||
|
|
||||||
|
const switchInteractionTimeRange = (range: 'week' | 'month') => {
|
||||||
|
interactionTimeRange.value = range
|
||||||
|
}
|
||||||
|
|
||||||
|
// 模拟数据
|
||||||
|
const weekData = {
|
||||||
|
dates: ['7.11', '7.12', '7.13', '7.14', '7.15', '7.16', '7.18'],
|
||||||
|
visitVolume: [280, 390, 180, 300, 130, 380, 250],
|
||||||
|
comments: [240, 420, 260, 390, 240, 370, 220],
|
||||||
|
bulletComments: [160, 300, 120, 200, 180, 280, 180]
|
||||||
|
}
|
||||||
|
|
||||||
|
const monthData = {
|
||||||
|
dates: ['7.1', '7.5', '7.10', '7.15', '7.20', '7.25', '7.30'],
|
||||||
|
visitVolume: [200, 350, 280, 400, 320, 380, 300],
|
||||||
|
comments: [180, 320, 250, 380, 300, 350, 280],
|
||||||
|
bulletComments: [120, 250, 180, 280, 220, 300, 200]
|
||||||
|
}
|
||||||
|
|
||||||
|
// 当前数据
|
||||||
|
const visitVolumeData = computed(() => {
|
||||||
|
return visitVolumeTimeRange.value === 'week' ? weekData : monthData
|
||||||
|
})
|
||||||
|
|
||||||
|
const interactionData = computed(() => {
|
||||||
|
return interactionTimeRange.value === 'week' ? weekData : monthData
|
||||||
|
})
|
||||||
|
|
||||||
|
// 课程访问量图表配置(柱状图)
|
||||||
|
const visitVolumeOption = computed<EChartsOption>(() => ({
|
||||||
|
tooltip: {
|
||||||
|
trigger: 'axis',
|
||||||
|
axisPointer: {
|
||||||
|
type: 'shadow'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
grid: {
|
||||||
|
left: '2%',
|
||||||
|
right: '1%',
|
||||||
|
top: '14%',
|
||||||
|
bottom: '6%',
|
||||||
|
containLabel: true
|
||||||
|
},
|
||||||
|
xAxis: {
|
||||||
|
type: 'category',
|
||||||
|
data: visitVolumeData.value.dates,
|
||||||
|
axisLine: {
|
||||||
|
lineStyle: {
|
||||||
|
color: '#C0CEE7'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
axisTick: {
|
||||||
|
show: false
|
||||||
|
},
|
||||||
|
axisLabel: {
|
||||||
|
color: '#BDC6D8',
|
||||||
|
fontSize: 12
|
||||||
|
}
|
||||||
|
},
|
||||||
|
yAxis: {
|
||||||
|
type: 'value',
|
||||||
|
max: 400,
|
||||||
|
interval: 100,
|
||||||
|
axisLine: {
|
||||||
|
show: false
|
||||||
|
},
|
||||||
|
axisTick: {
|
||||||
|
show: false
|
||||||
|
},
|
||||||
|
axisLabel: {
|
||||||
|
color: '#BDC6D8',
|
||||||
|
fontSize: 12
|
||||||
|
},
|
||||||
|
splitLine: {
|
||||||
|
lineStyle: {
|
||||||
|
color: '#F5F5F5',
|
||||||
|
type: 'dashed'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
series: [
|
||||||
|
{
|
||||||
|
name: '背景',
|
||||||
|
type: 'bar',
|
||||||
|
data: [400, 400, 400, 400, 400, 400, 400],
|
||||||
|
itemStyle: {
|
||||||
|
color: '#F8F8FC'
|
||||||
|
},
|
||||||
|
barWidth: '8px',
|
||||||
|
barGap: '-100%',
|
||||||
|
silent: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '访问量',
|
||||||
|
type: 'bar',
|
||||||
|
data: visitVolumeData.value.visitVolume,
|
||||||
|
itemStyle: {
|
||||||
|
color: {
|
||||||
|
type: 'linear',
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
x2: 0,
|
||||||
|
y2: 1,
|
||||||
|
colorStops: [
|
||||||
|
{ offset: 0, color: '#39E7E8' },
|
||||||
|
{ offset: 1, color: '#0288D1' }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
barWidth: '8px',
|
||||||
|
emphasis: {
|
||||||
|
itemStyle: {
|
||||||
|
color: {
|
||||||
|
type: 'linear',
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
x2: 0,
|
||||||
|
y2: 1,
|
||||||
|
colorStops: [
|
||||||
|
{ offset: 0, color: '#4FE8E9' },
|
||||||
|
{ offset: 1, color: '#039BE5' }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}))
|
||||||
|
|
||||||
|
// 课程互动图表配置(面积图)
|
||||||
|
const interactionOption = computed<EChartsOption>(() => ({
|
||||||
|
tooltip: {
|
||||||
|
trigger: 'axis',
|
||||||
|
axisPointer: {
|
||||||
|
type: 'line'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
grid: {
|
||||||
|
left: '2%',
|
||||||
|
right: '1%',
|
||||||
|
bottom: '8%',
|
||||||
|
top: '12%',
|
||||||
|
containLabel: true
|
||||||
|
},
|
||||||
|
xAxis: {
|
||||||
|
type: 'category',
|
||||||
|
data: interactionData.value.dates,
|
||||||
|
axisLine: {
|
||||||
|
lineStyle: {
|
||||||
|
color: '#C0CEE7'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
axisTick: {
|
||||||
|
show: false
|
||||||
|
},
|
||||||
|
axisLabel: {
|
||||||
|
color: '#BDC6D8',
|
||||||
|
fontSize: 12
|
||||||
|
}
|
||||||
|
},
|
||||||
|
yAxis: {
|
||||||
|
type: 'value',
|
||||||
|
max: 500,
|
||||||
|
interval: 100,
|
||||||
|
axisLine: {
|
||||||
|
show: false
|
||||||
|
},
|
||||||
|
axisTick: {
|
||||||
|
show: false
|
||||||
|
},
|
||||||
|
axisLabel: {
|
||||||
|
color: '#BDC6D8',
|
||||||
|
fontSize: 12
|
||||||
|
},
|
||||||
|
splitLine: {
|
||||||
|
lineStyle: {
|
||||||
|
color: '#F5F5F5',
|
||||||
|
type: 'dashed'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
series: [
|
||||||
|
{
|
||||||
|
name: '评论',
|
||||||
|
type: 'line',
|
||||||
|
smooth: true,
|
||||||
|
data: interactionData.value.comments,
|
||||||
|
lineStyle: {
|
||||||
|
color: '#81A2FF',
|
||||||
|
width: 2
|
||||||
|
},
|
||||||
|
areaStyle: {
|
||||||
|
color: {
|
||||||
|
type: 'linear',
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
x2: 0,
|
||||||
|
y2: 1,
|
||||||
|
colorStops: [
|
||||||
|
{ offset: 0, color: 'rgba(129, 162, 255, 0.3)' },
|
||||||
|
{ offset: 1, color: 'rgba(129, 162, 255, 0.05)' }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
itemStyle: {
|
||||||
|
color: '#81A2FF'
|
||||||
|
},
|
||||||
|
symbol: 'none'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '弹幕',
|
||||||
|
type: 'line',
|
||||||
|
smooth: true,
|
||||||
|
data: interactionData.value.bulletComments,
|
||||||
|
lineStyle: {
|
||||||
|
color: '#3DC8F4',
|
||||||
|
width: 2
|
||||||
|
},
|
||||||
|
areaStyle: {
|
||||||
|
color: {
|
||||||
|
type: 'linear',
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
x2: 0,
|
||||||
|
y2: 1,
|
||||||
|
colorStops: [
|
||||||
|
{ offset: 0, color: 'rgba(61, 200, 244, 0.2)' },
|
||||||
|
{ offset: 1, color: 'rgba(61, 200, 244, 0.02)' }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
itemStyle: {
|
||||||
|
color: '#3DC8F4'
|
||||||
|
},
|
||||||
|
symbol: 'none'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}))
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.classroom-report {
|
||||||
|
padding: 0 30px 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chart-section {
|
||||||
|
margin-bottom: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chart-header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chart-header2 {
|
||||||
|
margin-bottom: 35px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chart-title {
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 500;
|
||||||
|
color: #333;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.time-selector {
|
||||||
|
display: flex;
|
||||||
|
padding: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.time-option {
|
||||||
|
padding: 6px 16px;
|
||||||
|
font-size: 16px;
|
||||||
|
color: #333333;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.time-option.active {
|
||||||
|
color: #0C99DA;
|
||||||
|
}
|
||||||
|
|
||||||
|
.time-option:hover:not(.active) {
|
||||||
|
background: #E3F2FD;
|
||||||
|
color: #1976D2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chart-wrapper {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chart-legend {
|
||||||
|
position: absolute;
|
||||||
|
top: -30px;
|
||||||
|
left: 0;
|
||||||
|
display: flex;
|
||||||
|
gap: 16px;
|
||||||
|
z-index: 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
.legend-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 6px;
|
||||||
|
font-size: 12px;
|
||||||
|
color: #666;
|
||||||
|
}
|
||||||
|
|
||||||
|
.legend-color {
|
||||||
|
width: 12px;
|
||||||
|
height: 12px;
|
||||||
|
border-radius: 50%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chart-container {
|
||||||
|
width: 100%;
|
||||||
|
height: 210px;
|
||||||
|
border: 1px solid #E6E6E6;
|
||||||
|
border-radius: 4px;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* 响应式设计 */
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.classroom-report {
|
||||||
|
padding: 0 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chart-section {
|
||||||
|
padding: 16px;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chart-header {
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: flex-start;
|
||||||
|
gap: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.time-selector {
|
||||||
|
align-self: flex-end;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
34
src/views/teacher/statistics/tab/LearningMonitor.vue
Normal file
34
src/views/teacher/statistics/tab/LearningMonitor.vue
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
<template>
|
||||||
|
<div class="learning-monitor">
|
||||||
|
<div class="content-placeholder">
|
||||||
|
<h3>学习监控</h3>
|
||||||
|
<p>学习监控功能正在开发中...</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
console.log('LearningMonitor component loaded')
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.learning-monitor {
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content-placeholder {
|
||||||
|
text-align: center;
|
||||||
|
padding: 40px 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content-placeholder h3 {
|
||||||
|
font-size: 18px;
|
||||||
|
color: #333;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content-placeholder p {
|
||||||
|
font-size: 14px;
|
||||||
|
color: #666;
|
||||||
|
}
|
||||||
|
</style>
|
34
src/views/teacher/statistics/tab/LearningStatistics.vue
Normal file
34
src/views/teacher/statistics/tab/LearningStatistics.vue
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
<template>
|
||||||
|
<div class="learning-statistics">
|
||||||
|
<div class="content-placeholder">
|
||||||
|
<h3>学情统计</h3>
|
||||||
|
<p>学情统计功能正在开发中...</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
console.log('LearningStatistics component loaded')
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.learning-statistics {
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content-placeholder {
|
||||||
|
text-align: center;
|
||||||
|
padding: 40px 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content-placeholder h3 {
|
||||||
|
font-size: 18px;
|
||||||
|
color: #333;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content-placeholder p {
|
||||||
|
font-size: 14px;
|
||||||
|
color: #666;
|
||||||
|
}
|
||||||
|
</style>
|
34
src/views/teacher/statistics/tab/StudentGrades.vue
Normal file
34
src/views/teacher/statistics/tab/StudentGrades.vue
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
<template>
|
||||||
|
<div class="student-grades">
|
||||||
|
<div class="content-placeholder">
|
||||||
|
<h3>学生成绩</h3>
|
||||||
|
<p>学生成绩功能正在开发中...</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
console.log('StudentGrades component loaded')
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.student-grades {
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content-placeholder {
|
||||||
|
text-align: center;
|
||||||
|
padding: 40px 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content-placeholder h3 {
|
||||||
|
font-size: 18px;
|
||||||
|
color: #333;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content-placeholder p {
|
||||||
|
font-size: 14px;
|
||||||
|
color: #666;
|
||||||
|
}
|
||||||
|
</style>
|
Loading…
x
Reference in New Issue
Block a user