714 lines
22 KiB
Plaintext
714 lines
22 KiB
Plaintext
<template>
|
|
<view class="page-container">
|
|
<!-- 固定顶部信息区 -->
|
|
<view class="header-panel">
|
|
<view class="header-copy">
|
|
<text class="header-greeting">下午好,{{ parentName }}</text>
|
|
<text class="header-title">孩子今天的学习情况</text>
|
|
<text class="header-update">数据更新时间 {{ metricsUpdateText }} · 系统定时预计暂停,非实时</text>
|
|
</view>
|
|
<view class="notification-button" @tap="goAlerts">
|
|
<view class="notification-bell"></view>
|
|
<view class="notification-clapper"></view>
|
|
<view class="notification-dot"></view>
|
|
</view>
|
|
</view>
|
|
|
|
<!-- 仅此区域滚动 -->
|
|
<scroll-view class="content-scroll" direction="vertical" scroll-y="true" :refresher-enabled="true" :refresher-triggered="isRefreshing" @refresherrefresh="handleRefresh">
|
|
<view class="student-panel">
|
|
<view class="student-row">
|
|
<view class="student-avatar">
|
|
<image v-if="student.avatar" class="student-avatar-image" :src="student.avatar" mode="aspectFill"></image>
|
|
<text v-else class="avatar-face">{{ studentInitial }}</text>
|
|
</view>
|
|
<view class="student-identity">
|
|
<view class="student-name-row">
|
|
<text class="student-name">{{ student.name }}</text>
|
|
<text class="student-grade">{{ student.className }}</text>
|
|
</view>
|
|
<view class="student-level-row">
|
|
<view class="level-medal"></view>
|
|
<text class="student-level-label">综合等级</text>
|
|
<text class="student-level-value">{{ levelText }}</text>
|
|
</view>
|
|
</view>
|
|
<view class="switch-button" @tap="openStudentPicker">
|
|
<view class="switch-icon"></view>
|
|
<text class="switch-text">切换</text>
|
|
</view>
|
|
</view>
|
|
|
|
<view class="metrics-grid">
|
|
<view class="metric-item">
|
|
<text class="metric-label">今日上线</text>
|
|
<text class="metric-value" :class="metrics.onlineToday ? 'metric-green' : ''">{{ metrics.onlineToday ? '已上线' : '未上线' }}</text>
|
|
</view>
|
|
<view class="metric-item">
|
|
<text class="metric-label">今日打卡</text>
|
|
<text class="metric-value" :class="metrics.checkedInToday ? 'metric-green' : ''">{{ metrics.checkedInToday ? '已打卡' : '未打卡' }}</text>
|
|
</view>
|
|
<view class="metric-item">
|
|
<text class="metric-label">今日学习时长</text>
|
|
<text class="metric-value">{{ learningDurationText }}</text>
|
|
</view>
|
|
<view class="metric-item">
|
|
<text class="metric-label">今日正确率</text>
|
|
<text class="metric-value">{{ accuracyText }}</text>
|
|
</view>
|
|
<view class="metric-item">
|
|
<text class="metric-label">本周学习天数</text>
|
|
<text class="metric-value metric-blue">{{ metrics.learningDaysThisWeek }}/{{ metrics.shouldStudyDaysThisWeek }} 天</text>
|
|
</view>
|
|
<view class="metric-item">
|
|
<text class="metric-label">待完成学习包</text>
|
|
<text class="metric-value metric-orange">{{ metrics.pendingPackCount }} 个</text>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
|
|
<view class="reminder-section">
|
|
<view class="section-heading">
|
|
<text class="section-title">预警提醒</text>
|
|
<text class="section-link" @tap="goAlerts">全部预警 ></text>
|
|
</view>
|
|
<view class="reminder-list">
|
|
<view v-if="alerts.length === 0" class="reminder-empty"><text class="reminder-empty-text">暂无预警信息</text></view>
|
|
<view v-else v-for="alert in alerts" :key="alert.id" class="reminder-item"><text class="reminder-tag" :class="alertLevelClass(alert.level)">{{ alert.title }}</text><text class="reminder-text">{{ alert.content }}</text></view>
|
|
</view>
|
|
</view>
|
|
|
|
<view class="entry-section">
|
|
<text class="section-title">常用入口</text>
|
|
<view class="entry-grid">
|
|
<view class="entry-card" @tap="goTodayLearning"><UniIcons type="calendar" class="entry-icon"></UniIcons><text class="entry-title">今日学习</text></view>
|
|
<view class="entry-card" @tap="goStudyReport"><UniIcons type="calendar-filled" class="entry-icon"></UniIcons><text class="entry-title">周报月报</text></view>
|
|
<view class="entry-card" @tap="goVocabulary"><UniIcons type="font" class="entry-icon"></UniIcons><text class="entry-title">英语词汇</text></view>
|
|
<view class="entry-card" @tap="goGrowth"><UniIcons type="gift-filled" class="entry-icon"></UniIcons><text class="entry-title">成长激励</text></view>
|
|
</view>
|
|
</view>
|
|
</scroll-view>
|
|
|
|
<MainTabBar active-tab="home"></MainTabBar>
|
|
|
|
<page-container :show="showStudentPicker" position="bottom" :round="true" :overlay="true" :close-on-slide-down="true" @beforeleave="closeStudentPicker">
|
|
<view class="student-picker-panel">
|
|
<view class="student-picker-header">
|
|
<text class="student-picker-title">选择孩子</text>
|
|
<text class="student-picker-subtitle">选择后将更新首页学习信息</text>
|
|
</view>
|
|
<scroll-view class="student-picker-list" direction="vertical" scroll-y="true">
|
|
<view v-if="isStudentsLoading" class="student-picker-empty"><text class="student-picker-empty-text">学生信息加载中</text></view>
|
|
<view v-else-if="boundStudents.length === 0" class="student-picker-empty"><text class="student-picker-empty-text">暂无已绑定学生</text></view>
|
|
<view v-else v-for="(item, index) in boundStudents" :key="item.id" class="student-picker-item" :class="selectedStudentId === item.id ? 'student-picker-item-selected' : ''" @tap="selectStudent(item.id)">
|
|
<view class="student-picker-avatar" :class="studentPickerAvatarClass(index)"><image v-if="item.avatar" class="student-picker-avatar-image" :src="item.avatar" mode="aspectFill"></image><text v-else class="student-picker-avatar-text">{{ studentPickerInitial(item.name) }}</text></view>
|
|
<view class="student-picker-info"><view class="student-picker-name-row"><text class="student-picker-name">{{ item.name }}</text><text class="student-picker-role">{{ studentPickerRole(index) }}</text><text class="student-picker-status">{{ studentPickerStatus(index) }}</text></view><text class="student-picker-detail">{{ item.school }} · {{ item.className }}</text></view>
|
|
<text class="student-picker-action" :class="selectedStudentId === item.id ? 'student-picker-action-selected' : ''">{{ selectedStudentId === item.id ? '当前查看' : '切换' }}</text>
|
|
</view>
|
|
</scroll-view>
|
|
<view class="student-picker-footer"><view class="student-picker-cancel" @tap="closeStudentPicker"><text class="student-picker-cancel-text">取消</text></view><view class="student-picker-confirm" @tap="confirmStudent"><text class="student-picker-confirm-text">确定</text></view></view>
|
|
</view>
|
|
</page-container>
|
|
</view>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import MainTabBar from '../../components/main-tab-bar.uvue'
|
|
import UniIcons from '../../uni_modules/uni-icons-x/components/uni-icons/uni-icons.uvue'
|
|
import { apiGetBoundStudents, apiGetStudentHomeMetrics, apiGetStudentLearningWarnings, type BoundStudentSummary, type StudentAlert, type StudentAlertLevel, type StudentHomeMetrics, type StudentHomeProfile } from '../../api/student'
|
|
import { applyBoundStudent, getSavedSelectedStudent, homeStudentAlerts, homeStudentMetrics, homeStudentProfile, saveSelectedStudent, selectedStudentChangeVersion, setHomeStudentAlerts, setHomeStudentMetrics, switchBoundStudent } from '../../store/home'
|
|
import type { UserInfo } from '../../api/auth'
|
|
|
|
const loadingStudent = ref<StudentHomeProfile>({
|
|
id: '',
|
|
name: '加载中',
|
|
account: '',
|
|
grade: '',
|
|
school: '',
|
|
className: '',
|
|
avatar: '',
|
|
comprehensiveLevel: 'qualified'
|
|
})
|
|
const isRefreshing = ref(false)
|
|
const showStudentPicker = ref(false)
|
|
const isStudentsLoading = ref(false)
|
|
const boundStudents = ref<BoundStudentSummary[]>([])
|
|
const selectedStudentId = ref('')
|
|
const observedStudentVersion = ref(selectedStudentChangeVersion.value)
|
|
const student = computed((): StudentHomeProfile => homeStudentProfile.value ?? loadingStudent.value)
|
|
const metrics = computed((): StudentHomeMetrics => homeStudentMetrics.value ?? {
|
|
studentId: '', onlineToday: false, checkedInToday: false, learningMinutesToday: 0,
|
|
accuracyToday: null, learningDaysThisWeek: 0, shouldStudyDaysThisWeek: 0, pendingPackCount: 0, updatedAt: ''
|
|
})
|
|
const alerts = computed((): StudentAlert[] => homeStudentAlerts.value ?? [])
|
|
const parentName = computed((): string => {
|
|
const userInfo = uni.getStorageSync('userInfo') as UserInfo | null
|
|
return userInfo?.nickname || '家长'
|
|
})
|
|
|
|
const studentInitial = computed((): string => {
|
|
return student.value.name.length > 0 ? student.value.name.substring(0, 1) : '学'
|
|
})
|
|
|
|
const levelText = computed((): string => {
|
|
if (student.value.comprehensiveLevel === 'excellent') return '优秀'
|
|
if (student.value.comprehensiveLevel === 'good') return '良好'
|
|
if (student.value.comprehensiveLevel === 'qualified') return '合格'
|
|
return '待提升'
|
|
})
|
|
|
|
const learningDurationText = computed((): string => {
|
|
const hours = Math.floor(metrics.value.learningMinutesToday / 60)
|
|
const minutes = metrics.value.learningMinutesToday % 60
|
|
return hours > 0 ? `${hours}h${minutes}m` : `${minutes}m`
|
|
})
|
|
|
|
const accuracyText = computed((): string => {
|
|
return metrics.value.accuracyToday === null ? '--' : `${metrics.value.accuracyToday}%`
|
|
})
|
|
|
|
const metricsUpdateText = computed((): string => {
|
|
return metrics.value.updatedAt.length > 0 ? metrics.value.updatedAt.substring(5, 16).replace('T', ' ') : '--'
|
|
})
|
|
|
|
onLoad(() => {
|
|
loadHomeData()
|
|
})
|
|
|
|
onShow(() => {
|
|
syncSelectedStudentFromStorage()
|
|
})
|
|
|
|
async function syncSelectedStudentFromStorage() {
|
|
const savedStudent = getSavedSelectedStudent()
|
|
if (savedStudent && savedStudent.id !== homeStudentProfile.value?.id) {
|
|
applyBoundStudent(savedStudent)
|
|
observedStudentVersion.value = selectedStudentChangeVersion.value
|
|
await loadHomeData(savedStudent.id, false)
|
|
return
|
|
}
|
|
if (observedStudentVersion.value !== selectedStudentChangeVersion.value) {
|
|
observedStudentVersion.value = selectedStudentChangeVersion.value
|
|
await loadHomeData(homeStudentProfile.value?.id ?? '', false)
|
|
}
|
|
}
|
|
|
|
async function loadStudentHomeMetrics(studentId: string, showLoading: boolean = true) {
|
|
const res = await apiGetStudentHomeMetrics(studentId, showLoading)
|
|
if (res && res.data) {
|
|
setHomeStudentMetrics(res.data)
|
|
}
|
|
}
|
|
|
|
async function loadStudentAlerts(studentId: string, showLoading: boolean = true) {
|
|
const res = await apiGetStudentLearningWarnings(studentId, showLoading, 3)
|
|
if (res && res.data) {
|
|
setHomeStudentAlerts(res.data.list)
|
|
}
|
|
}
|
|
|
|
async function loadHomeData(studentId?: string, showLoading: boolean = true) {
|
|
let selectedId = studentId || homeStudentProfile.value?.id || ''
|
|
if (selectedId && (!homeStudentProfile.value || homeStudentProfile.value.id !== selectedId)) {
|
|
const savedStudent = getSavedSelectedStudent()
|
|
if (savedStudent?.id === selectedId) applyBoundStudent(savedStudent)
|
|
}
|
|
if (!selectedId) {
|
|
const savedStudent = getSavedSelectedStudent()
|
|
if (savedStudent) {
|
|
selectedId = savedStudent.id
|
|
applyBoundStudent(savedStudent)
|
|
} else {
|
|
const res = await apiGetBoundStudents()
|
|
const firstStudent = res?.data?.students[0]
|
|
if (firstStudent) {
|
|
saveSelectedStudent(firstStudent)
|
|
applyBoundStudent(firstStudent)
|
|
selectedId = firstStudent.id
|
|
}
|
|
}
|
|
}
|
|
if (student.value.id) {
|
|
await Promise.all([
|
|
loadStudentHomeMetrics(selectedId || student.value.id, showLoading),
|
|
loadStudentAlerts(selectedId || student.value.id, showLoading)
|
|
])
|
|
}
|
|
}
|
|
|
|
async function handleRefresh() {
|
|
if (isRefreshing.value) return
|
|
isRefreshing.value = true
|
|
await loadHomeData(student.value.id, false)
|
|
isRefreshing.value = false
|
|
}
|
|
|
|
async function openStudentPicker() {
|
|
showStudentPicker.value = true
|
|
selectedStudentId.value = student.value.id
|
|
isStudentsLoading.value = true
|
|
const res = await apiGetBoundStudents()
|
|
if (res && res.data) {
|
|
boundStudents.value = res.data.students
|
|
if (!selectedStudentId.value && res.data.currentStudentId) {
|
|
selectedStudentId.value = res.data.currentStudentId
|
|
}
|
|
}
|
|
isStudentsLoading.value = false
|
|
}
|
|
|
|
function selectStudent(studentId: string) {
|
|
selectedStudentId.value = studentId
|
|
}
|
|
|
|
function studentPickerInitial(name: string): string {
|
|
return name.length > 1 ? name.substring(1, 2) : name.substring(0, 1)
|
|
}
|
|
|
|
function studentPickerAvatarClass(index: number): string {
|
|
if (index === 0) return 'student-picker-avatar-0'
|
|
if (index === 1) return 'student-picker-avatar-1'
|
|
return 'student-picker-avatar-2'
|
|
}
|
|
|
|
function studentPickerRole(index: number): string {
|
|
if (index === 0) return '儿子'
|
|
if (index === 1) return '侄子'
|
|
return '女儿'
|
|
}
|
|
|
|
function studentPickerStatus(index: number): string {
|
|
return index === 1 ? '中上' : '优秀'
|
|
}
|
|
|
|
function alertLevelClass(level: StudentAlertLevel): string {
|
|
return level === 'low' ? 'reminder-blue' : level === 'medium' ? 'reminder-yellow' : 'reminder-red'
|
|
}
|
|
|
|
function goAlerts() {
|
|
uni.navigateTo({ url: '/pages/alerts/index' })
|
|
}
|
|
|
|
function goTodayLearning() {
|
|
uni.navigateTo({ url: '/pages/today-learning/index' })
|
|
}
|
|
|
|
function goStudyReport() {
|
|
uni.navigateTo({ url: '/pages/study/index' })
|
|
}
|
|
|
|
function goVocabulary() {
|
|
uni.navigateTo({ url: '/pages/vocabulary/index' })
|
|
}
|
|
|
|
function goGrowth() {
|
|
uni.navigateTo({ url: '/pages/growth/index' })
|
|
}
|
|
|
|
function closeStudentPicker() {
|
|
showStudentPicker.value = false
|
|
}
|
|
|
|
async function confirmStudent() {
|
|
if (!selectedStudentId.value) {
|
|
uni.showToast({ title: '请选择学生', icon: 'none' })
|
|
return
|
|
}
|
|
closeStudentPicker()
|
|
const selectedStudent = boundStudents.value.find((item) => item.id === selectedStudentId.value)
|
|
if (selectedStudent) {
|
|
switchBoundStudent(selectedStudent)
|
|
observedStudentVersion.value = selectedStudentChangeVersion.value
|
|
await loadHomeData(selectedStudent.id)
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<style>
|
|
.page-container {
|
|
display: flex;
|
|
flex-direction: column;
|
|
flex: 1;
|
|
background-color: #f5f7fb;
|
|
}
|
|
|
|
.header-panel {
|
|
display: flex;
|
|
flex-direction: row;
|
|
align-items: flex-start;
|
|
justify-content: space-between;
|
|
padding: 15px 20px 17px;
|
|
background: linear-gradient(135deg, #5d3de8 0%, #8236e9 100%);
|
|
border-radius: 0 0 0 0;
|
|
}
|
|
|
|
.header-copy {
|
|
display: flex;
|
|
flex-direction: column;
|
|
flex: 1;
|
|
}
|
|
|
|
.header-greeting {
|
|
font-size: 13px;
|
|
color: #e5ddff;
|
|
margin-bottom: 5px;
|
|
}
|
|
|
|
.header-title {
|
|
font-size: 20px;
|
|
font-weight: 700;
|
|
color: #ffffff;
|
|
margin-bottom: 8px;
|
|
}
|
|
|
|
.header-update {
|
|
font-size: 10px;
|
|
color: #e5ddff;
|
|
line-height: 15px;
|
|
}
|
|
|
|
.notification-button {
|
|
position: relative;
|
|
width: 42px;
|
|
height: 42px;
|
|
margin-top: 2px;
|
|
margin-left: 12px;
|
|
border-radius: 22px;
|
|
background-color: rgba(255, 255, 255, 0.18);
|
|
}
|
|
|
|
.notification-bell {
|
|
position: absolute;
|
|
left: 14px;
|
|
top: 13px;
|
|
width: 14px;
|
|
height: 14px;
|
|
border-radius: 8px 8px 3px 3px;
|
|
background-color: #ffffff;
|
|
}
|
|
|
|
.notification-clapper {
|
|
position: absolute;
|
|
left: 12px;
|
|
bottom: 11px;
|
|
width: 18px;
|
|
height: 3px;
|
|
border-radius: 2px;
|
|
background-color: #ffffff;
|
|
}
|
|
|
|
.notification-dot {
|
|
position: absolute;
|
|
right: 9px;
|
|
top: 9px;
|
|
width: 7px;
|
|
height: 7px;
|
|
border-radius: 4px;
|
|
background-color: #ffd64a;
|
|
}
|
|
|
|
.content-scroll {
|
|
flex: 1;
|
|
padding: 12px 20px 18px;
|
|
}
|
|
|
|
.student-panel,
|
|
.reminder-section {
|
|
padding: 16px;
|
|
background-color: #ffffff;
|
|
border-radius: 16px;
|
|
margin-bottom: 12px;
|
|
}
|
|
|
|
.student-row {
|
|
display: flex;
|
|
flex-direction: row;
|
|
align-items: center;
|
|
}
|
|
|
|
.student-avatar {
|
|
position: relative;
|
|
width: 48px;
|
|
height: 48px;
|
|
border-radius: 15px;
|
|
background-color: #7161ed;
|
|
margin-right: 12px;
|
|
}
|
|
|
|
.student-avatar-image {
|
|
width: 48px;
|
|
height: 48px;
|
|
border-radius: 15px;
|
|
}
|
|
|
|
.student-picker-panel { display: flex; flex-direction: column; background-color: #ffffff; }
|
|
.student-picker-header { display: flex; flex-direction: column; padding: 18px 20px 12px; }
|
|
.student-picker-title { font-size: 18px; font-weight: 700; color: #242b3d; }
|
|
.student-picker-subtitle { margin-top: 5px; font-size: 12px; color: #8992a5; }
|
|
.student-picker-list { max-height: 340px; padding: 0 20px; }
|
|
.student-picker-empty { display: flex; flex-direction: column; align-items: center; justify-content: center; height: 150px; }
|
|
.student-picker-empty-text { font-size: 13px; color: #8992a5; }
|
|
.student-picker-item { display: flex; flex-direction: row; align-items: center; padding: 10px; margin-bottom: 10px; border: 1px solid #edf0f5; border-radius: 15px; background-color: #ffffff; }
|
|
.student-picker-item-selected { background-color: #f0efff; }
|
|
.student-picker-avatar { display: flex; flex-direction: column; align-items: center; justify-content: center; width: 46px; height: 46px; border-radius: 14px; background-color: #7161ed; }
|
|
.student-picker-avatar-0 { background-color: #6558e9; }
|
|
.student-picker-avatar-1 { background-color: #12a9df; }
|
|
.student-picker-avatar-2 { background-color: #e63a9c; }
|
|
.student-picker-avatar-image { width: 46px; height: 46px; border-radius: 14px; }
|
|
.student-picker-avatar-text { font-size: 18px; font-weight: 700; color: #ffffff; }
|
|
.student-picker-info { display: flex; flex-direction: column; flex: 1; margin-left: 11px; }
|
|
.student-picker-name-row { display: flex; flex-direction: row; align-items: center; margin-bottom: 5px; }
|
|
.student-picker-name { margin-right: 8px; font-size: 15px; font-weight: 700; color: #2a3042; }
|
|
.student-picker-grade { padding: 2px 6px; border-radius: 7px; font-size: 10px; color: #554be9; background-color: #e8e7ff; }
|
|
.student-picker-role { margin-left: 7px; padding: 2px 6px; border-radius: 8px; font-size: 9px; color: #7c8ba1; background-color: #f1f4f8; }
|
|
.student-picker-status { margin-left: 7px; padding: 2px 6px; border-radius: 8px; font-size: 9px; color: #16a979; background-color: #e7faf2; }
|
|
.student-picker-detail { margin-top: 2px; font-size: 11px; color: #7e8799; }
|
|
.student-picker-check { padding: 4px 7px; border-radius: 9px; background-color: #554be9; }
|
|
.student-picker-check-text { font-size: 10px; color: #ffffff; }
|
|
.student-picker-action { flex-shrink: 0; padding: 5px 8px; border-radius: 10px; font-size: 9px; color: #6e7c91; background-color: #f1f4f8; }
|
|
.student-picker-action-selected { color: #ffffff; background-color: #554be9; }
|
|
.student-picker-footer { display: flex; flex-direction: row; padding: 12px 20px calc(12px + var(--uni-safe-area-inset-bottom)); border-top: 1px solid #edf0f5; }
|
|
.student-picker-cancel, .student-picker-confirm { display: flex; flex-direction: column; align-items: center; justify-content: center; flex: 1; height: 44px; border-radius: 11px; }
|
|
.student-picker-cancel { margin-right: 10px; background-color: #f0f2f7; }
|
|
.student-picker-confirm { background-color: #554be9; }
|
|
.student-picker-cancel-text { font-size: 15px; color: #596275; }
|
|
.student-picker-confirm-text { font-size: 15px; color: #ffffff; }
|
|
|
|
.avatar-face {
|
|
position: absolute;
|
|
left: 8px;
|
|
top: 12px;
|
|
font-size: 20px;
|
|
font-weight: 700;
|
|
color: #ffd69c;
|
|
}
|
|
|
|
.avatar-cap {
|
|
position: absolute;
|
|
right: 4px;
|
|
top: 6px;
|
|
font-size: 15px;
|
|
font-weight: 700;
|
|
color: #35394d;
|
|
}
|
|
|
|
.student-identity {
|
|
display: flex;
|
|
flex-direction: column;
|
|
flex: 1;
|
|
}
|
|
|
|
.student-name-row,
|
|
.student-level-row {
|
|
display: flex;
|
|
flex-direction: row;
|
|
align-items: center;
|
|
}
|
|
|
|
.student-name-row {
|
|
margin-bottom: 8px;
|
|
}
|
|
|
|
.student-name {
|
|
font-size: 17px;
|
|
font-weight: 700;
|
|
color: #22283a;
|
|
margin-right: 8px;
|
|
}
|
|
|
|
.student-grade {
|
|
padding: 2px 7px;
|
|
border-radius: 8px;
|
|
font-size: 11px;
|
|
color: #635ce8;
|
|
background-color: #eef0ff;
|
|
}
|
|
|
|
.level-medal {
|
|
width: 13px;
|
|
height: 13px;
|
|
border-radius: 7px;
|
|
background-color: #f4a400;
|
|
margin-right: 7px;
|
|
}
|
|
|
|
.student-level-label,
|
|
.metric-label {
|
|
font-size: 11px;
|
|
color: #8992a5;
|
|
}
|
|
|
|
.student-level-label {
|
|
margin-right: 9px;
|
|
}
|
|
|
|
.student-level-value {
|
|
font-size: 12px;
|
|
font-weight: 600;
|
|
color: #18a56b;
|
|
}
|
|
|
|
.switch-button {
|
|
display: flex;
|
|
flex-direction: row;
|
|
align-items: center;
|
|
padding: 8px 10px;
|
|
border-radius: 17px;
|
|
background-color: #eef1ff;
|
|
}
|
|
|
|
.switch-icon {
|
|
width: 13px;
|
|
height: 10px;
|
|
border-radius: 7px 7px 3px 3px;
|
|
background-color: #5b58e9;
|
|
margin-right: 5px;
|
|
}
|
|
|
|
.switch-text {
|
|
font-size: 12px;
|
|
color: #5b58e9;
|
|
}
|
|
|
|
.metrics-grid {
|
|
display: flex;
|
|
flex-direction: row;
|
|
flex-wrap: wrap;
|
|
justify-content: space-between;
|
|
margin-top: 13px;
|
|
}
|
|
|
|
.metric-item {
|
|
display: flex;
|
|
flex-direction: column;
|
|
width: 31.5%;
|
|
padding: 10px 8px;
|
|
margin-bottom: 7px;
|
|
border-radius: 12px;
|
|
background-color: #f7f9fc;
|
|
}
|
|
|
|
.metric-label {
|
|
margin-bottom: 7px;
|
|
}
|
|
|
|
.metric-value {
|
|
font-size: 15px;
|
|
font-weight: 700;
|
|
color: #20283a;
|
|
}
|
|
|
|
.metric-green {
|
|
color: #079b69;
|
|
}
|
|
|
|
.metric-blue {
|
|
color: #4b62d8;
|
|
}
|
|
|
|
.metric-orange {
|
|
color: #ee8900;
|
|
}
|
|
|
|
.section-heading {
|
|
display: flex;
|
|
flex-direction: row;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
margin-bottom: 12px;
|
|
}
|
|
|
|
.section-title {
|
|
font-size: 16px;
|
|
font-weight: 700;
|
|
color: #242b3d;
|
|
}
|
|
|
|
.section-link {
|
|
font-size: 12px;
|
|
color: #514ce8;
|
|
}
|
|
|
|
.reminder-list {
|
|
display: flex;
|
|
flex-direction: column;
|
|
}
|
|
|
|
.reminder-item {
|
|
display: flex;
|
|
flex-direction: row;
|
|
align-items: flex-start;
|
|
padding: 11px 10px;
|
|
margin-bottom: 8px;
|
|
border-radius: 12px;
|
|
background-color: #f7f9fc;
|
|
}
|
|
|
|
.reminder-item-last {
|
|
margin-bottom: 0;
|
|
}
|
|
|
|
.reminder-tag {
|
|
flex-shrink: 0;
|
|
padding: 3px 7px;
|
|
margin-right: 9px;
|
|
border-radius: 9px;
|
|
font-size: 10px;
|
|
}
|
|
|
|
.reminder-yellow {
|
|
color: #e69300;
|
|
background-color: #fff5d9;
|
|
}
|
|
|
|
.reminder-red {
|
|
color: #ed4a5c;
|
|
background-color: #fff0f2;
|
|
}
|
|
|
|
.reminder-text {
|
|
flex: 1;
|
|
font-size: 12px;
|
|
line-height: 20px;
|
|
color: #596275;
|
|
}
|
|
.reminder-empty { display: flex; flex-direction: column; align-items: center; justify-content: center; min-height: 72px; }
|
|
.reminder-empty-text { font-size: 13px; color: #8992a5; }
|
|
.reminder-blue { color: #3c79cf; background-color: #e8f1ff; }
|
|
|
|
.entry-section {
|
|
padding-bottom: 10px;
|
|
}
|
|
|
|
.entry-grid {
|
|
display: flex;
|
|
flex-direction: row;
|
|
justify-content: space-between;
|
|
margin-top: 12px;
|
|
}
|
|
|
|
.entry-card {
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
width: 23%;
|
|
padding: 8px 2px;
|
|
border-radius: 8px;
|
|
background-color: #ffffff;
|
|
}
|
|
|
|
.entry-icon {
|
|
margin-bottom: 9px;
|
|
font-size: 24px;
|
|
line-height: 24px;
|
|
color: #6558e9;
|
|
}
|
|
|
|
.entry-title {
|
|
font-size: 13px;
|
|
font-weight: 600;
|
|
color: #2a3042;
|
|
}
|
|
|
|
</style>
|