| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046 |
- <template>
- <div class="learning-behavior-analysis">
- <!-- 页面头部 -->
- <div class="header">
- <h1>📊 学习行为分析</h1>
- <p>全面分析学院教学情况和学员学习行为</p>
- </div>
- <!-- 导航标签 -->
- <div class="nav-tabs">
- <div class="nav-tab" :class="{ active: activeTab === 'college' }" @click="switchTab('college')">
- 🏫 学院维度分析
- </div>
- <div class="nav-tab" :class="{ active: activeTab === 'student' }" @click="switchTab('student')">
- 👤 学员维度分析
- </div>
- </div>
- <!-- 学院维度分析 -->
- <div v-show="activeTab === 'college'" class="tab-content">
- <!-- 筛选条件 -->
- <div class="filter-section">
- <h3>🔍 数据筛选</h3>
- <div class="filter-controls">
- <div class="filter-group">
- <label>选择学院</label>
- <a-select v-model:value="collegeFilters.collegeId" placeholder="全部学院" @change="updateCollegeData">
- <a-select-option v-for="college in collegeList" :key="college.id" :value="college.id">
- {{ college.name }}
- </a-select-option>
- </a-select>
- </div>
- <div class="filter-group">
- <label>时间范围</label>
- <a-select v-model:value="collegeFilters.timeRange" @change="updateCollegeData">
- <a-select-option :value="7">最近7天</a-select-option>
- <a-select-option :value="30">最近30天</a-select-option>
- <a-select-option :value="90">最近90天</a-select-option>
- <a-select-option :value="365">最近一年</a-select-option>
- </a-select>
- </div>
- <div class="filter-group">
- <a-button type="primary" @click="updateCollegeData">查询</a-button>
- </div>
- </div>
- </div>
- <!-- 学院整体统计 -->
- <div class="stats-grid">
- <div class="stat-card">
- <h3>📚 课程访问统计</h3>
- <div class="stat-number">{{ collegeStats.allCourseCount }}</div>
- <div class="stat-label">总课程数</div>
- <div class="stat-number">{{ collegeStats.activeCourseCount }}</div>
- <div class="stat-label">活跃课程数</div>
- <div class="stat-number">{{ collegeStats.courseVisitCount }}%</div>
- <div class="stat-label">课程访问率</div>
- </div>
- <div class="stat-card">
- <h3>👥 用户登录统计</h3>
- <div class="stat-number">{{ collegeStats.allLoginCount.toLocaleString() }}</div>
- <div class="stat-label">总登录人次</div>
- <div class="stat-number">{{ collegeStats.userCount.toLocaleString() }}</div>
- <div class="stat-label">独立用户数</div>
- <div class="stat-number">{{ collegeStats.avgLoginCount }}</div>
- <div class="stat-label">人均登录次数</div>
- </div>
- <div class="stat-card">
- <h3>⏰ 观看时长统计</h3>
- <div class="stat-number">{{ collegeStats.userAllStayTime }}</div>
- <div class="stat-label">总观看时长</div>
- <div class="stat-number">{{ collegeStats.avgStayTime }}</div>
- <div class="stat-label">平均时长</div>
- <div class="stat-number">{{ collegeStats.peakWatchUserCount ? peakWatchUserCount.toLocaleString() : '0' }}</div>
- <div class="stat-label">峰值观看人数</div>
- </div>
- </div>
- <!-- 登录时段分析图表 -->
- <div class="chart-container">
- <h3>⏰ 用户登录时段分布</h3>
- <div ref="loginTimeChart" class="chart"></div>
- </div>
- <!-- 课程访问热度图表 -->
- <div class="chart-container">
- <h3>🔥 课程访问热度排行</h3>
- <div ref="courseHeatChart" class="chart"></div>
- </div>
- <!-- 学院课程详细统计 -->
- <div class="data-table">
- <h3>📊 学院课程详细统计</h3>
- <a-table
- :columns="collegeColumns"
- :data-source="collegeTableData"
- :pagination="{ pageSize: 10 }"
- row-key="id"
- />
- </div>
- </div>
- <!-- 学员维度分析 -->
- <div v-show="activeTab === 'student'" class="tab-content">
- <!-- 学员搜索 -->
- <div class="filter-section">
- <h3>🔍 学员搜索</h3>
- <div class="filter-controls">
- <div class="filter-group">
- <label>学员姓名/学号</label>
- <a-input v-model:value="studentSearch" placeholder="输入学员姓名或学号" @pressEnter="searchStudent" />
- </div>
- <div class="filter-group">
- <a-button type="primary" @click="searchStudent">搜索</a-button>
- </div>
- <div class="filter-group">
- <a-button @click="clearSearch">清除</a-button>
- </div>
- </div>
- </div>
- <!-- 学员学习行为卡片 -->
- <div class="student-cards">
- <div
- v-for="student in filteredStudents"
- :key="student.userId"
- class="student-card"
- :class="{ highlighted: student.highlighted }"
- >
- <div class="student-header">
- <div class="student-info">
- <h4>{{ student.userIdName }} ({{ student.userId }})</h4>
- <div class="student-meta">{{ student.college }} | {{ student.majorIdName }} | {{ student.grade }}</div>
- </div>
- <div class="student-stats">
- <div class="stat-item">
- <div class="stat-value">{{ (student.finishRate)* 100 }}%</div>
- <div class="stat-label">总体进度</div>
- </div>
- </div>
- </div>
- <div class="course-progress">
- <div v-for="course in student.courseList" :key="course.id" class="course-item">
- <div class="course-name">{{ course.courseName }}</div>
- <div class="course-stats">
- <div class="stat-item">
- <div class="stat-value">{{ (course.finishRate)* 100 }}%</div>
- <div class="stat-label">学习进度</div>
- </div>
- <div class="stat-item">
- <div class="stat-value">{{ course.workRate }}</div>
- <div class="stat-label">作业完成</div>
- </div>
- <div class="stat-item">
- <div class="stat-value">{{ course.answerCount }}</div>
- <div class="stat-label">讨论参与</div>
- </div>
- <div class="stat-item">
- <div class="stat-value">{{ course.postCount }}</div>
- <div class="stat-label">提问次数</div>
- </div>
- </div>
- </div>
- </div>
- </div>
- </div>
- </div>
- </div>
- </template>
- <script setup>
- import { ref, reactive, computed, onMounted, nextTick, onUnmounted } from 'vue'
- import * as echarts from 'echarts'
- import {
- getCollegeList,
- getCollegeStats,
- getLoginTimeDistribution,
- getCourseHeat,
- getCollegeCourseDetails,
- searchStudents
- } from '@/api/statisticalAnalysis/analysisLearningBehaviors'
- // 当前活动标签
- const activeTab = ref('college')
- // 学院维度筛选条件
- const collegeFilters = reactive({
- collegeId: '',
- timeRange: 30
- })
- // 学院列表
- const collegeList = ref([])
- // 学院统计数据
- const collegeStats = ref({
- totalCourses: 156,
- activeCourses: 142,
- courseAccessRate: 91.0,
- totalLogins: 12456,
- uniqueUsers: 3234,
- avgLoginPerUser: 3.85,
- totalOnlineTime: 2456,
- avgSessionTime: 45.2,
- peakOnlineUsers: 234,
- activeCourseCount : 0,
- allCourseCount : 0,
- courseVisitCount : 0,
- allLoginCount : 0,
- avgLoginCount: 0,
- userCount :0,
- avgStayTime:'',
- peakWatchUserCount : 0,
- userAllStayTime : ''
- })
- // 图表引用
- const loginTimeChart = ref(null)
- const courseHeatChart = ref(null)
- let loginTimeChartInstance = null
- let courseHeatChartInstance = null
- // 学院表格列定义
- const collegeColumns = [
- {
- title: '学院名称',
- dataIndex: 'collegeIdName',
- key: 'collegeIdName'
- },
- {
- title: '课程数量',
- dataIndex: 'courseCount',
- key: 'courseCount'
- },
- {
- title: '总访问量',
- dataIndex: 'watchCount',
- key: 'watchCount'
- },
- {
- title: '平均完成率',
- dataIndex: 'completeRate',
- key: 'completeRate',
- customRender: ({ text }) => `${text}%`
- },
- {
- title: '作业提交率',
- dataIndex: 'homeworkFinishRate',
- key: 'homeworkFinishRate',
- customRender: ({ text }) => `${text}%`
- },
- // {
- // title: '退课率',
- // dataIndex: 'homeworkFinishRate',
- // key: 'homeworkFinishRate',
- // customRender: ({ text }) => `${text}%`
- // }
- ]
- // 学院表格数据
- const collegeTableData = ref([
- {
- collegeId: 1,
- collegeIdName: '计算机学院',
- courseCount: 45,
- totalVisits: 5234,
- completeRate: 78.5,
- assignmentSubmissionRate: 85.2,
- dropoutRate: 10.2
- },
- {
- id: 2,
- name: '商学院',
- courseCount: 38,
- totalVisits: 4567,
- avgCompletionRate: 72.3,
- assignmentSubmissionRate: 79.8,
- dropoutRate: 12.5
- },
- {
- id: 3,
- name: '艺术学院',
- courseCount: 28,
- totalVisits: 3123,
- avgCompletionRate: 68.9,
- assignmentSubmissionRate: 76.4,
- dropoutRate: 15.2
- },
- {
- id: 4,
- name: '理学院',
- courseCount: 35,
- totalVisits: 3890,
- avgCompletionRate: 75.2,
- assignmentSubmissionRate: 82.1,
- dropoutRate: 13.8
- }
- ])
- // 学员搜索
- const studentSearch = ref('')
- // 学员数据
- const studentsData = ref([
- // {
- // id: 1,
- // name: '张三',
- // studentId: '2021001',
- // college: '计算机学院',
- // major: '计算机科学与技术',
- // grade: '大三',
- // overallProgress: 85.6,
- // highlighted: false,
- // courses: [
- // {
- // id: 1,
- // name: 'JavaScript程序设计',
- // progress: 92,
- // completedAssignments: 8,
- // totalAssignments: 10,
- // discussionParticipation: 15,
- // questionCount: 5
- // },
- // {
- // id: 2,
- // name: '数据结构与算法',
- // progress: 78,
- // completedAssignments: 6,
- // totalAssignments: 8,
- // discussionParticipation: 12,
- // questionCount: 3
- // }
- // ]
- // },
- // {
- // id: 2,
- // name: '李四',
- // studentId: '2021002',
- // college: '商学院',
- // major: '工商管理',
- // grade: '大二',
- // overallProgress: 72.3,
- // highlighted: false,
- // courses: [
- // {
- // id: 3,
- // name: '管理学原理',
- // progress: 85,
- // completedAssignments: 7,
- // totalAssignments: 9,
- // discussionParticipation: 8,
- // questionCount: 2
- // },
- // {
- // id: 4,
- // name: '市场营销学',
- // progress: 65,
- // completedAssignments: 4,
- // totalAssignments: 7,
- // discussionParticipation: 6,
- // questionCount: 1
- // }
- // ]
- // },
- // {
- // id: 3,
- // name: '王五',
- // studentId: '2021003',
- // college: '艺术学院',
- // major: '视觉传达设计',
- // grade: '大一',
- // overallProgress: 91.2,
- // highlighted: false,
- // courses: [
- // {
- // id: 5,
- // name: '设计基础',
- // progress: 95,
- // completedAssignments: 10,
- // totalAssignments: 10,
- // discussionParticipation: 20,
- // questionCount: 8
- // },
- // {
- // id: 6,
- // name: '色彩构成',
- // progress: 88,
- // completedAssignments: 8,
- // totalAssignments: 9,
- // discussionParticipation: 15,
- // questionCount: 6
- // }
- // ]
- // }
- ])
- // 过滤后的学员数据
- const filteredStudents = computed(() => {
- if (!studentSearch.value.trim()) {
- return studentsData.value.map((student) => ({ ...student, highlighted: false }))
- }
- const searchTerm = studentSearch.value.trim().toLowerCase()
- return studentsData.value
- .filter((student) => {
- const matchName = student.userIdName.toLowerCase().includes(searchTerm)
- const matchId = student.userId.includes(searchTerm)
- return matchName || matchId
- })
- .map((student) => ({ ...student, highlighted: true }))
- })
- // 切换标签
- const switchTab = (tabName) => {
- activeTab.value = tabName
- if (tabName === 'college') {
- nextTick(async () => {
- await initCollegeCharts()
- })
- }
- }
- const formatDateWithDays = (days = 0) => {
- const date = new Date();
- date.setDate(date.getDate() + days); // 设置天数偏移
- const year = date.getFullYear();
- const month = String(date.getMonth() + 1).padStart(2, '0');
- const day = String(date.getDate()).padStart(2, '0');
- return `${year}-${month}-${day}`;
- }
- // 更新学院数据
- const updateCollegeData = async () => {
- try {
- // 获取学院统计数据 船锚
- const statsResponse = await getCollegeStats({
- orgId: collegeFilters.collegeId,
- startTime : formatDateWithDays(0),
- endTime : formatDateWithDays(collegeFilters.timeRange)
- })
- console.log("asdasd",statsResponse.data)
- // Object.assign(collegeStats, statsResponse.data)
- collegeStats.value = {...collegeStats,...statsResponse.data}
- // 获取课程详细统计
- const detailsResponse = await getCollegeCourseDetails({
- orgId: collegeFilters.collegeId,
- startTime : formatDateWithDays(0),
- endTime : formatDateWithDays(collegeFilters.timeRange)
- })
- collegeTableData.value = detailsResponse.data
- // 重新初始化图表
- await initCollegeCharts()
- } catch (error) {
- console.error('获取学院数据失败:', error)
- }
- }
- // 搜索学员
- const searchStudent = async () => {
- try {
- const response = await searchStudents({
- name: studentSearch.value.trim(),
- current: 1,
- size: 99
- })
- studentsData.value = response.data.map((student) => ({
- ...student,
- highlighted: !!studentSearch.value.trim()
- }))
- console.log('看看呢',studentsData.value)
- if (response.length === 0 && studentSearch.value.trim()) {
- alert(`未找到包含"${studentSearch.value}"的学员`)
- }
- } catch (error) {
- console.error('搜索学员失败:', error)
- }
- }
- // 清除搜索
- const clearSearch = async () => {
- studentSearch.value = ''
- await searchStudent()
- }
- // 初始化学院维度图表
- const initCollegeCharts = async () => {
- await initLoginTimeChart()
- await initCourseHeatChart()
- }
- // 登录时段分布图表
- const initLoginTimeChart = async () => {
- if (!loginTimeChart.value) return
- if (loginTimeChartInstance) {
- loginTimeChartInstance.dispose()
- }
- loginTimeChartInstance = echarts.init(loginTimeChart.value)
- try {
- const response = await getLoginTimeDistribution({
- orgId: collegeFilters.collegeId,
- startTime : formatDateWithDays(0),
- endTime : formatDateWithDays(1),
- current : 1,
- size : 24
- })
- console.log('结果呢',response)
- const { hours, loginCounts } = response
- const option = {
- title: {
- text: '24小时登录时段分布',
- left: 'center',
- textStyle: {
- color: '#2c3e50',
- fontSize: 16,
- fontWeight: 'bold'
- }
- },
- tooltip: {
- trigger: 'axis',
- backgroundColor: 'rgba(255, 255, 255, 0.95)',
- borderColor: '#3498db',
- borderWidth: 1,
- textStyle: {
- color: '#333'
- }
- },
- xAxis: {
- type: 'category',
- data: hours,
- axisLine: {
- lineStyle: {
- color: '#bdc3c7'
- }
- },
- axisLabel: {
- color: '#2c3e50'
- }
- },
- yAxis: {
- type: 'value',
- name: '登录人次',
- nameTextStyle: {
- color: '#2c3e50'
- },
- axisLine: {
- lineStyle: {
- color: '#bdc3c7'
- }
- },
- axisLabel: {
- color: '#2c3e50'
- },
- splitLine: {
- lineStyle: {
- color: '#ecf0f1'
- }
- }
- },
- series: [
- {
- name: '登录人次',
- type: 'bar',
- data: loginCounts,
- itemStyle: {
- color: {
- type: 'linear',
- x: 0,
- y: 0,
- x2: 0,
- y2: 1,
- colorStops: [
- {
- offset: 0,
- color: '#3498db'
- },
- {
- offset: 1,
- color: '#2980b9'
- }
- ]
- }
- },
- emphasis: {
- itemStyle: {
- color: '#e74c3c'
- }
- }
- }
- ]
- }
- loginTimeChartInstance.setOption(option)
- } catch (error) {
- console.error('获取登录时段数据失败:', error)
- }
- }
- // 课程访问热度图表
- const initCourseHeatChart = async () => {
- if (!courseHeatChart.value) return
- if (courseHeatChartInstance) {
- courseHeatChartInstance.dispose()
- }
- courseHeatChartInstance = echarts.init(courseHeatChart.value)
- try {
- const response = await getCourseHeat({
- orgId: collegeFilters.collegeId,
- startTime : formatDateWithDays(0),
- endTime : formatDateWithDays(1),
- current : 1,
- size : 8
- })
- const { courses, visits } = response
- const option = {
- title: {
- text: '课程访问热度TOP8',
- left: 'center',
- textStyle: {
- color: '#2c3e50',
- fontSize: 16,
- fontWeight: 'bold'
- }
- },
- tooltip: {
- trigger: 'axis',
- backgroundColor: 'rgba(255, 255, 255, 0.95)',
- borderColor: '#3498db',
- borderWidth: 1,
- textStyle: {
- color: '#333'
- }
- },
- xAxis: {
- type: 'value',
- name: '访问量',
- nameTextStyle: {
- color: '#2c3e50'
- },
- axisLine: {
- lineStyle: {
- color: '#bdc3c7'
- }
- },
- axisLabel: {
- color: '#2c3e50'
- },
- splitLine: {
- lineStyle: {
- color: '#ecf0f1'
- }
- }
- },
- yAxis: {
- type: 'category',
- data: courses,
- axisLine: {
- lineStyle: {
- color: '#bdc3c7'
- }
- },
- axisLabel: {
- color: '#2c3e50'
- }
- },
- series: [
- {
- name: '访问量',
- type: 'bar',
- data: visits,
- itemStyle: {
- color: {
- type: 'linear',
- x: 0,
- y: 0,
- x2: 1,
- y2: 0,
- colorStops: [
- {
- offset: 0,
- color: '#e74c3c'
- },
- {
- offset: 1,
- color: '#f39c12'
- }
- ]
- }
- },
- emphasis: {
- itemStyle: {
- color: '#3498db'
- }
- }
- }
- ]
- }
- courseHeatChartInstance.setOption(option)
- } catch (error) {
- console.error('获取课程热度数据失败:', error)
- }
- }
- // 响应式处理
- const handleResize = () => {
- loginTimeChartInstance?.resize()
- courseHeatChartInstance?.resize()
- }
- // 初始化数据
- const initData = async () => {
- try {
- // 获取学院列表
- const colleges = await getCollegeList()
- collegeList.value = colleges
- collegeFilters.collegeId = colleges.length > 0 ? colleges[0].id : undefined
- // 获取初始学员数据
- // await searchStudent()
- // 获取学院数据
- await updateCollegeData()
- } catch (error) {
- console.error('初始化数据失败:', error)
- }
- }
- // 组件挂载后初始化
- onMounted(() => {
- nextTick(async () => {
- await initData()
- window.addEventListener('resize', handleResize)
- })
- })
- // 组件卸载时清理
- onUnmounted(() => {
- window.removeEventListener('resize', handleResize)
- loginTimeChartInstance?.dispose()
- courseHeatChartInstance?.dispose()
- })
- </script>
- <style scoped>
- .learning-behavior-analysis {
- padding: 20px;
- }
- .header {
- background: rgba(255, 255, 255, 0.95);
- backdrop-filter: blur(10px);
- border-radius: 15px;
- padding: 30px;
- margin-bottom: 30px;
- box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1);
- text-align: center;
- }
- .header h1 {
- color: #2c3e50;
- font-size: 2.5em;
- margin-bottom: 10px;
- }
- .header p {
- color: #7f8c8d;
- font-size: 1.1em;
- }
- .nav-tabs {
- display: flex;
- background: rgba(255, 255, 255, 0.95);
- backdrop-filter: blur(10px);
- border-radius: 15px;
- padding: 5px;
- margin-bottom: 30px;
- box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1);
- }
- .nav-tab {
- flex: 1;
- padding: 15px 20px;
- text-align: center;
- cursor: pointer;
- border-radius: 10px;
- transition: all 0.3s ease;
- font-weight: 600;
- color: #7f8c8d;
- }
- .nav-tab.active {
- background: linear-gradient(135deg, #3498db, #2980b9);
- color: white;
- box-shadow: 0 4px 15px rgba(52, 152, 219, 0.4);
- }
- .nav-tab:hover:not(.active) {
- background: rgba(52, 152, 219, 0.1);
- color: #3498db;
- }
- .tab-content {
- display: block;
- }
- .filter-section {
- background: rgba(255, 255, 255, 0.95);
- backdrop-filter: blur(10px);
- border-radius: 15px;
- padding: 25px;
- margin-bottom: 30px;
- box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1);
- }
- .filter-section h3 {
- color: #2c3e50;
- margin-bottom: 20px;
- font-size: 1.5em;
- }
- .filter-controls {
- display: grid;
- grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
- gap: 15px;
- align-items: end;
- }
- .filter-group {
- display: flex;
- flex-direction: column;
- }
- .filter-group label {
- margin-bottom: 8px;
- color: #2c3e50;
- font-weight: 600;
- }
- .stats-grid {
- display: grid;
- grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
- gap: 25px;
- margin-bottom: 30px;
- }
- .stat-card {
- background: rgba(255, 255, 255, 0.95);
- backdrop-filter: blur(10px);
- border-radius: 15px;
- padding: 25px;
- box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1);
- transition: transform 0.3s ease, box-shadow 0.3s ease;
- }
- .stat-card:hover {
- transform: translateY(-5px);
- box-shadow: 0 12px 40px rgba(0, 0, 0, 0.15);
- }
- .stat-card h3 {
- color: #2c3e50;
- margin-bottom: 15px;
- font-size: 1.2em;
- border-bottom: 2px solid #3498db;
- padding-bottom: 10px;
- }
- .stat-number {
- font-size: 2.2em;
- font-weight: bold;
- color: #3498db;
- margin-bottom: 8px;
- }
- .stat-label {
- color: #7f8c8d;
- font-size: 0.9em;
- }
- .chart-container {
- background: rgba(255, 255, 255, 0.95);
- backdrop-filter: blur(10px);
- border-radius: 15px;
- padding: 25px;
- margin-bottom: 30px;
- box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1);
- }
- .chart-container h3 {
- color: #2c3e50;
- margin-bottom: 20px;
- font-size: 1.5em;
- }
- .chart {
- height: 400px;
- border-radius: 10px;
- }
- .data-table {
- background: rgba(255, 255, 255, 0.95);
- backdrop-filter: blur(10px);
- border-radius: 15px;
- padding: 25px;
- margin-bottom: 30px;
- box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1);
- }
- .data-table h3 {
- color: #2c3e50;
- margin-bottom: 20px;
- font-size: 1.5em;
- }
- .student-cards {
- display: flex;
- flex-direction: column;
- gap: 20px;
- }
- .student-card {
- background: rgba(255, 255, 255, 0.95);
- backdrop-filter: blur(10px);
- border-radius: 15px;
- padding: 20px;
- box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1);
- transition: transform 0.3s ease;
- }
- .student-card:hover {
- transform: translateY(-3px);
- }
- .student-card.highlighted {
- border: 2px solid #3498db;
- box-shadow: 0 8px 32px rgba(52, 152, 219, 0.2);
- }
- .student-header {
- display: flex;
- justify-content: space-between;
- align-items: center;
- margin-bottom: 15px;
- padding-bottom: 10px;
- border-bottom: 2px solid #ecf0f1;
- }
- .student-info h4 {
- color: #2c3e50;
- font-size: 1.3em;
- margin-bottom: 5px;
- }
- .student-meta {
- color: #7f8c8d;
- font-size: 0.9em;
- }
- .course-progress {
- margin-bottom: 15px;
- }
- .course-item {
- display: flex;
- justify-content: space-between;
- align-items: center;
- padding: 10px 0;
- border-bottom: 1px solid #ecf0f1;
- }
- .course-name {
- font-weight: 600;
- color: #2c3e50;
- }
- .course-stats {
- display: flex;
- gap: 20px;
- font-size: 0.9em;
- }
- .stat-item {
- text-align: center;
- }
- .stat-value {
- font-weight: bold;
- color: #3498db;
- }
- .stat-label {
- color: #7f8c8d;
- font-size: 0.8em;
- }
- :deep(.completion-rate) {
- color: #27ae60;
- font-weight: bold;
- }
- :deep(.low-engagement) {
- color: #e74c3c;
- font-weight: bold;
- }
- @media (max-width: 768px) {
- .stats-grid {
- grid-template-columns: 1fr;
- }
- .filter-controls {
- grid-template-columns: 1fr;
- }
- .header h1 {
- font-size: 2em;
- }
- .nav-tabs {
- flex-direction: column;
- }
- .student-header {
- flex-direction: column;
- align-items: flex-start;
- }
- .course-stats {
- flex-direction: column;
- gap: 10px;
- }
- }
- </style>
|