接入学生学习数据接口
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<view class="page-container">
|
||||
<view class="header-panel"><view class="header-back" @tap="goHome"><UniIcons type="back" class="header-back-icon"></UniIcons></view><view class="header-copy"><text class="header-title">成长激励</text><text class="header-subtitle">{{ growth.studentName }} · {{ growth.studentGrade }}·{{ growth.studentClass }}</text></view><view class="header-action"><UniIcons type="person-filled" class="header-action-icon"></UniIcons></view></view>
|
||||
<view class="header-panel"><view class="header-back" @tap="goHome"><UniIcons type="back" class="header-back-icon"></UniIcons></view><view class="header-copy"><text class="header-title">成长激励</text><text class="header-subtitle">{{ homeStudentProfile?.name ?? growth.studentName }} · {{ homeStudentProfile?.school ?? '' }} {{ homeStudentProfile?.className ?? growth.studentClass }}</text></view><view class="header-action"><UniIcons type="person-filled" class="header-action-icon"></UniIcons></view></view>
|
||||
<scroll-view class="content-scroll" direction="vertical" scroll-y="true"><view v-if="isLoading" class="status-panel"><text class="status-text">成长数据加载中</text></view><view v-else class="content-panel"><view class="points-card"><view class="points-heading"><view class="points-copy"><text class="points-label">积分余额</text><text class="points-value">{{ growth.pointsBalance }}</text></view><view class="level-badge"><text class="level-label">等级</text><text class="level-value">Lv.{{ growth.level }}</text></view></view><view class="points-stats"><view class="points-stat"><text class="points-stat-value">{{ growth.rank }}</text><text class="points-stat-label">班级排名</text></view><view class="points-stat points-stat-middle"><text class="points-stat-value">{{ growth.streakDays }}</text><text class="points-stat-label">未能连续</text></view><view class="points-stat"><text class="points-stat-value">{{ growth.totalPoints }}</text><text class="points-stat-label">获得总量</text></view></view></view><view class="flow-card"><text class="section-title">积分流水</text><text class="section-description">记录孩子每天做了哪些正向行为</text><view class="flow-list"><view v-for="record in growth.records" :key="record.id" class="flow-item"><view class="flow-icon" :class="'flow-icon-' + record.icon"><UniIcons :type="record.icon === 'calendar' ? 'calendar' : record.icon === 'assignment' ? 'checkbox' : record.icon === 'streak' ? 'fire' : record.icon === 'accuracy' ? 'circle-filled' : record.icon === 'mastery' ? 'star-filled' : 'compose'" class="flow-icon-glyph"></UniIcons></view><view class="flow-copy"><text class="flow-title">{{ record.title }}</text><text class="flow-time">{{ record.timeText }}</text></view><text class="flow-points">+{{ record.points }}</text></view></view></view></view></scroll-view>
|
||||
</view>
|
||||
</template>
|
||||
@@ -8,7 +8,7 @@
|
||||
<script setup lang="ts">
|
||||
import UniIcons from '../../uni_modules/uni-icons-x/components/uni-icons/uni-icons.uvue'
|
||||
import { apiGetStudentGrowth, apiGetStudentHomeProfile, type StudentGrowth } from '../../api/student'
|
||||
import { homeStudentProfile, setHomeStudentProfile } from '../../store/home'
|
||||
import { homeStudentProfile, setHomeStudentProfile } from '../../store/home'
|
||||
|
||||
const growth = ref<StudentGrowth>({ studentId: '', studentName: '', studentGrade: '', studentClass: '', pointsBalance: 0, level: 0, rank: 0, streakDays: 0, totalPoints: 0, records: [] })
|
||||
const isLoading = ref(true)
|
||||
|
||||
+42
-21
@@ -3,7 +3,7 @@
|
||||
<!-- 固定顶部信息区 -->
|
||||
<view class="header-panel">
|
||||
<view class="header-copy">
|
||||
<text class="header-greeting">下午好,王女士</text>
|
||||
<text class="header-greeting">下午好,{{ parentName }}</text>
|
||||
<text class="header-title">孩子今天的学习情况</text>
|
||||
<text class="header-update">数据更新时间 {{ metricsUpdateText }} · 系统定时预计暂停,非实时</text>
|
||||
</view>
|
||||
@@ -25,7 +25,7 @@
|
||||
<view class="student-identity">
|
||||
<view class="student-name-row">
|
||||
<text class="student-name">{{ student.name }}</text>
|
||||
<text class="student-grade">{{ student.grade }}</text>
|
||||
<text class="student-grade">{{ student.className }}</text>
|
||||
</view>
|
||||
<view class="student-level-row">
|
||||
<view class="level-medal"></view>
|
||||
@@ -58,7 +58,7 @@
|
||||
</view>
|
||||
<view class="metric-item">
|
||||
<text class="metric-label">本周学习天数</text>
|
||||
<text class="metric-value metric-blue">{{ metrics.learningDaysThisWeek }}/7 天</text>
|
||||
<text class="metric-value metric-blue">{{ metrics.learningDaysThisWeek }}/{{ metrics.shouldStudyDaysThisWeek }} 天</text>
|
||||
</view>
|
||||
<view class="metric-item">
|
||||
<text class="metric-label">待完成作业</text>
|
||||
@@ -102,7 +102,7 @@
|
||||
<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 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"><image v-if="item.avatar" class="student-picker-avatar-image" :src="item.avatar" mode="aspectFill"></image><text v-else class="student-picker-avatar-text">{{ item.name.substring(0, 1) }}</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-grade">{{ item.grade }}</text></view><text class="student-picker-detail">账号:{{ item.account }}</text><text class="student-picker-detail">学校:{{ item.school }}</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-grade">{{ item.school }} {{ item.className }}</text></view><text class="student-picker-detail">账号:{{ item.account }}</text></view>
|
||||
<view v-if="selectedStudentId === item.id" class="student-picker-check"><text class="student-picker-check-text">已选</text></view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
@@ -115,8 +115,9 @@
|
||||
<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, apiGetStudentAlerts, apiGetStudentHomeMetrics, apiGetStudentHomeProfile, type BoundStudentSummary, type StudentAlert, type StudentAlertLevel, type StudentHomeMetrics, type StudentHomeProfile } from '../../api/student'
|
||||
import { homeStudentAlerts, homeStudentMetrics, homeStudentProfile, setHomeStudentAlerts, setHomeStudentMetrics, setHomeStudentProfile } from '../../store/home'
|
||||
import { apiGetBoundStudents, apiGetStudentHomeMetrics, apiGetStudentLearningWarnings, type BoundStudentSummary, type StudentAlert, type StudentAlertLevel, type StudentHomeMetrics, type StudentHomeProfile } from '../../api/student'
|
||||
import { applyBoundStudent, getSavedSelectedStudent, homeStudentAlerts, homeStudentMetrics, homeStudentProfile, saveSelectedStudent, setHomeStudentAlerts, setHomeStudentMetrics } from '../../store/home'
|
||||
import type { UserInfo } from '../../api/auth'
|
||||
|
||||
const loadingStudent = ref<StudentHomeProfile>({
|
||||
id: '',
|
||||
@@ -124,6 +125,7 @@
|
||||
account: '',
|
||||
grade: '',
|
||||
school: '',
|
||||
className: '',
|
||||
avatar: '',
|
||||
comprehensiveLevel: 'qualified'
|
||||
})
|
||||
@@ -135,9 +137,13 @@
|
||||
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, pendingHomeworkCount: 0, updatedAt: ''
|
||||
accuracyToday: null, learningDaysThisWeek: 0, shouldStudyDaysThisWeek: 0, pendingHomeworkCount: 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) : '学'
|
||||
@@ -165,18 +171,9 @@
|
||||
})
|
||||
|
||||
onLoad(() => {
|
||||
if (!homeStudentProfile.value || !homeStudentMetrics.value || homeStudentAlerts.value === null) {
|
||||
loadHomeData()
|
||||
}
|
||||
loadHomeData()
|
||||
})
|
||||
|
||||
async function loadStudentHomeProfile(studentId?: string, showLoading: boolean = true) {
|
||||
const res = await apiGetStudentHomeProfile(studentId, showLoading)
|
||||
if (res && res.data) {
|
||||
setHomeStudentProfile(res.data)
|
||||
}
|
||||
}
|
||||
|
||||
async function loadStudentHomeMetrics(studentId: string, showLoading: boolean = true) {
|
||||
const res = await apiGetStudentHomeMetrics(studentId, showLoading)
|
||||
if (res && res.data) {
|
||||
@@ -185,18 +182,37 @@
|
||||
}
|
||||
|
||||
async function loadStudentAlerts(studentId: string, showLoading: boolean = true) {
|
||||
const res = await apiGetStudentAlerts(studentId, 1, 20, 3, showLoading)
|
||||
const res = await apiGetStudentLearningWarnings(studentId, showLoading)
|
||||
if (res && res.data) {
|
||||
setHomeStudentAlerts(res.data.list)
|
||||
}
|
||||
}
|
||||
|
||||
async function loadHomeData(studentId?: string, showLoading: boolean = true) {
|
||||
await loadStudentHomeProfile(studentId, showLoading)
|
||||
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(student.value.id, showLoading),
|
||||
loadStudentAlerts(student.value.id, showLoading)
|
||||
loadStudentHomeMetrics(selectedId || student.value.id, showLoading),
|
||||
loadStudentAlerts(selectedId || student.value.id, showLoading)
|
||||
])
|
||||
}
|
||||
}
|
||||
@@ -261,6 +277,11 @@
|
||||
return
|
||||
}
|
||||
closeStudentPicker()
|
||||
const selectedStudent = boundStudents.value.find((item) => item.id === selectedStudentId.value)
|
||||
if (selectedStudent) {
|
||||
saveSelectedStudent(selectedStudent)
|
||||
applyBoundStudent(selectedStudent)
|
||||
}
|
||||
if (selectedStudentId.value !== student.value.id) {
|
||||
await loadHomeData(selectedStudentId.value)
|
||||
}
|
||||
|
||||
@@ -66,7 +66,9 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
import { apiLogin } from '../../api/auth'
|
||||
import { apiGetBoundStudents, type BoundStudentSummary } from '../../api/student'
|
||||
import { setUserInfo, saveRememberInfo, getRememberInfo } from '../../store/user'
|
||||
import { applyBoundStudent, saveSelectedStudent } from '../../store/home'
|
||||
|
||||
type LoginForm = {
|
||||
phone: string
|
||||
@@ -111,6 +113,7 @@
|
||||
if (res && res.data) {
|
||||
// 保存用户信息
|
||||
setUserInfo(res.data)
|
||||
await restoreSelectedStudent(res.data.userId)
|
||||
|
||||
// 保存记住密码信息
|
||||
saveRememberInfo(form.phone, form.password, form.remember)
|
||||
@@ -129,6 +132,22 @@
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 登录后加载家长绑定的学生,并恢复或初始化当前学生。
|
||||
*/
|
||||
async function restoreSelectedStudent(userId: string) {
|
||||
const res = await apiGetBoundStudents()
|
||||
if (!res || !res.data || res.data.students.length === 0) return
|
||||
|
||||
const savedStudent = uni.getStorageSync(`selectedStudent_${userId}`) as BoundStudentSummary | null
|
||||
const selectedStudent = savedStudent && res.data.students.some((item) => item.id === savedStudent.id)
|
||||
? res.data.students.find((item) => item.id === savedStudent.id)!
|
||||
: res.data.students[0]
|
||||
|
||||
saveSelectedStudent(selectedStudent)
|
||||
applyBoundStudent(selectedStudent)
|
||||
}
|
||||
|
||||
/**
|
||||
* 忘记密码
|
||||
*/
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
<text class="child-role">{{ index === 0 ? '儿子' : index === 1 ? '侄子' : '女儿' }}</text>
|
||||
<text class="child-status">{{ index === 1 ? '中上' : '优秀' }}</text>
|
||||
</view>
|
||||
<text class="child-school">{{ student.school }} · {{ student.grade }}({{ index + 1 }})班</text>
|
||||
<text class="child-school">{{ student.school }} · {{ student.className }}</text>
|
||||
</view>
|
||||
<text class="child-action" :class="selectedStudentId === student.id ? 'child-action-selected' : ''">{{ selectedStudentId === student.id ? '当前查看' : '切换' }}</text>
|
||||
</view>
|
||||
@@ -62,7 +62,7 @@
|
||||
<script setup lang="ts">
|
||||
import MainTabBar from '../../components/main-tab-bar.uvue'
|
||||
import { apiGetBoundStudents, type BoundStudentSummary } from '../../api/student'
|
||||
import { homeStudentProfile, setHomeStudentProfile } from '../../store/home'
|
||||
import { applyBoundStudent, homeStudentProfile, saveSelectedStudent } from '../../store/home'
|
||||
import type { UserInfo } from '../../api/auth'
|
||||
import { clearUserInfo } from '../../store/user'
|
||||
|
||||
@@ -91,7 +91,10 @@
|
||||
function selectStudent(studentId: string) {
|
||||
selectedStudentId.value = studentId
|
||||
const student = students.value.find((item) => item.id === studentId)
|
||||
if (student) setHomeStudentProfile({ id: student.id, name: student.name, account: student.account, grade: student.grade, school: student.school, avatar: student.avatar, comprehensiveLevel: 'good' })
|
||||
if (student) {
|
||||
saveSelectedStudent(student)
|
||||
applyBoundStudent(student)
|
||||
}
|
||||
}
|
||||
|
||||
function openVerification(mode: string) { uni.navigateTo({ url: '/pages/profile/verify-student?mode=' + mode }) }
|
||||
|
||||
@@ -79,7 +79,7 @@
|
||||
<image class="student-avatar" :src="student.avatar || '/static/avatar-default.png'" mode="aspectFill"></image>
|
||||
<view class="student-info">
|
||||
<text class="student-name">{{ student.name }}</text>
|
||||
<text class="student-detail">{{ student.grade }} {{ student.class }} | {{ student.account }}</text>
|
||||
<text class="student-detail">{{ student.schoolName }} {{ student.className }} | {{ student.account }}</text>
|
||||
</view>
|
||||
<button class="remove-btn" @tap="handleRemoveStudent(index)">移除</button>
|
||||
</view>
|
||||
|
||||
@@ -113,6 +113,7 @@
|
||||
<script setup lang="ts">
|
||||
import { apiRegister } from '../../api/auth'
|
||||
import { registrationStudents } from '../../store/registration'
|
||||
import { md5 } from '../../utils/md5'
|
||||
|
||||
type ParentAccountForm = {
|
||||
parentName: string
|
||||
@@ -190,7 +191,7 @@
|
||||
parentName: form.parentName,
|
||||
phone: form.phone,
|
||||
relationship: form.relationship,
|
||||
password: form.password,
|
||||
password: md5(form.password),
|
||||
students: studentIds
|
||||
})
|
||||
} catch (error) {
|
||||
|
||||
@@ -58,7 +58,7 @@
|
||||
<image class="student-avatar" :src="student.avatar || '/static/avatar-default.png'" mode="aspectFill"></image>
|
||||
<view class="student-info">
|
||||
<text class="student-name">{{ student.name }}</text>
|
||||
<text class="student-detail">{{ student.grade }} {{ student.class }} | {{ student.account }}</text>
|
||||
<text class="student-detail">{{ student.schoolName }} {{ student.className }} | {{ student.account }}</text>
|
||||
</view>
|
||||
<button class="remove-btn" @tap="handleRemoveStudent(index)">移除</button>
|
||||
</view>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<view class="page-container">
|
||||
<view class="header-panel">
|
||||
<view class="header-copy"><text class="header-title">学情报告</text><text class="header-subtitle">{{ report.studentName }} · {{ report.studentGrade }} · {{ report.studentClass }}</text></view>
|
||||
<view class="header-copy"><text class="header-title">学情报告</text><text class="header-subtitle">{{ homeStudentProfile?.name ?? report.studentName }} · {{ homeStudentProfile?.school ?? '' }} {{ homeStudentProfile?.className ?? report.studentClass }}</text></view>
|
||||
<view class="header-action"><UniIcons type="person-filled" class="header-action-icon"></UniIcons></view>
|
||||
</view>
|
||||
<view class="report-meta"><text class="report-meta-text">{{ activeReport.rangeText }} · 数据更新到 {{ report.updatedAtText }}</text></view>
|
||||
|
||||
+18
-3
@@ -5,15 +5,19 @@
|
||||
<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 { apiGetStudentHomeProfile, apiGetStudentLearningTasks, type LearningTaskItem, type StudentLearningTasks } from '../../api/student'
|
||||
import { apiGetParentStudyPacks, apiGetStudentHomeProfile, type LearningTaskItem, type ParentStudyPackStatus, type StudentLearningTasks } from '../../api/student'
|
||||
import { homeStudentProfile, setHomeStudentProfile } from '../../store/home'
|
||||
|
||||
const filters = [{ id: 'all', name: '全部' }, { id: 'pending', name: '待完成' }, { id: 'completed', name: '已完成' }, { id: 'failed', name: '未通过' }]
|
||||
const activeFilter = ref('all')
|
||||
const activeStatus = computed((): ParentStudyPackStatus => activeFilter.value === 'pending' ? 1 : activeFilter.value === 'completed' ? 2 : activeFilter.value === 'failed' ? 3 : 0)
|
||||
const isLoading = ref(true)
|
||||
const isRefreshing = ref(false)
|
||||
const tasks = ref<StudentLearningTasks>({ studentId: '', studentName: '', studentGrade: '', studentClass: '', pendingCount: 0, completedCount: 0, failedCount: 0, groups: [] })
|
||||
const visibleGroups = computed(() => tasks.value.groups.filter((group) => group.tasks.length === 0 || group.tasks.some((task) => matchesFilter(task))))
|
||||
const headerStudentName = computed((): string => homeStudentProfile.value?.name ?? tasks.value.studentName)
|
||||
const headerStudentGrade = computed((): string => homeStudentProfile.value?.grade ?? tasks.value.studentGrade)
|
||||
const headerStudentClass = computed((): string => homeStudentProfile.value?.className ?? tasks.value.studentClass)
|
||||
|
||||
onLoad(() => { loadTasks() })
|
||||
|
||||
@@ -23,7 +27,15 @@
|
||||
const profileRes = await apiGetStudentHomeProfile(undefined, false)
|
||||
if (profileRes && profileRes.data) { setHomeStudentProfile(profileRes.data); studentId = profileRes.data.id }
|
||||
}
|
||||
if (studentId) { const res = await apiGetStudentLearningTasks(studentId); if (res && res.data) tasks.value = res.data }
|
||||
if (studentId) {
|
||||
const res = await apiGetParentStudyPacks(studentId, activeStatus.value)
|
||||
if (res && res.data) {
|
||||
tasks.value = {
|
||||
...res.data,
|
||||
studentName: `${homeStudentProfile.value?.name ?? res.data.studentName} · ${homeStudentProfile.value?.school ?? ''} ${homeStudentProfile.value?.className ?? res.data.studentClass}`
|
||||
}
|
||||
}
|
||||
}
|
||||
isLoading.value = false
|
||||
}
|
||||
|
||||
@@ -33,7 +45,10 @@
|
||||
isRefreshing.value = false
|
||||
}
|
||||
|
||||
function setFilter(filter: string) { activeFilter.value = filter }
|
||||
async function setFilter(filter: string) {
|
||||
activeFilter.value = filter
|
||||
await loadTasks()
|
||||
}
|
||||
function matchesFilter(task: LearningTaskItem): boolean {
|
||||
if (activeFilter.value === 'all') return true
|
||||
if (activeFilter.value === 'pending') return task.status === 'not_started' || task.status === 'in_progress'
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<view class="page-container">
|
||||
<view class="header-panel">
|
||||
<view class="header-back" @tap="goBack"><UniIcons type="back" class="header-back-icon"></UniIcons></view>
|
||||
<view class="header-copy"><text class="header-title">今日学习</text><text class="header-subtitle">{{ detail.dateText }} {{ detail.studentName }}</text></view>
|
||||
<view class="header-copy"><text class="header-title">今日学习</text><text class="header-subtitle">{{ detail.dateText }} {{ homeStudentProfile?.name ?? detail.studentName }} · {{ homeStudentProfile?.school ?? '' }} {{ homeStudentProfile?.className ?? '' }}</text></view>
|
||||
<view class="header-action" @tap="goBack"><UniIcons type="home-filled" class="header-action-icon"></UniIcons></view>
|
||||
</view>
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<view class="page-container">
|
||||
<view class="header-panel"><view class="header-back" @tap="goHome"><UniIcons type="back" class="header-back-icon"></UniIcons></view><view class="header-copy"><text class="header-title">英语词汇</text><text class="header-subtitle">{{ vocabulary.studentName }} · {{ vocabulary.studentGrade }}·{{ vocabulary.studentClass }}</text></view><view class="header-action"><UniIcons type="person-filled" class="header-action-icon"></UniIcons></view></view>
|
||||
<scroll-view class="content-scroll" direction="vertical" scroll-y="true"><view v-if="isLoading" class="status-panel"><text class="status-text">词汇书加载中</text></view><view v-else class="content-panel"><view class="list-heading"><text class="list-title">全部词书</text><text class="book-count">{{ vocabulary.bookCount }} 本</text></view><text class="list-description">每本词书的总词量与已学进度</text><view v-for="book in vocabulary.books" :key="book.id" class="book-card" :class="'book-card-' + book.color"><view class="book-heading"><view class="book-icon" :class="'book-icon-' + book.color"><UniIcons type="font" class="book-icon-glyph" :class="'book-icon-glyph-' + book.color"></UniIcons></view><view class="book-copy"><text class="book-name">{{ book.name }}</text><view class="book-meta"><text class="book-total">{{ book.subtitle }}</text><text class="book-level">{{ book.levelText }}</text></view></view><text class="book-status" :class="'book-status-' + book.color">{{ book.statusText }}</text></view><view class="progress-heading"><text class="progress-label">学习进度</text><text class="progress-value">{{ book.learnedWords > 0 ? '已学 ' + book.learnedWords + ' / ' + book.totalWords + ' 词 · ' + progressPercent(book) + '%' : '未开始' }}</text></view><view class="progress-track"><view class="progress-fill" :class="'progress-fill-' + book.color" :style="{ width: progressPercent(book) + '%' }"></view></view></view></view></scroll-view>
|
||||
<view class="header-panel"><view class="header-back" @tap="goHome"><UniIcons type="back" class="header-back-icon"></UniIcons></view><view class="header-copy"><text class="header-title">英语词汇</text><text class="header-subtitle">{{ homeStudentProfile?.name ?? vocabulary.studentName }} · {{ homeStudentProfile?.school ?? '' }} {{ homeStudentProfile?.className ?? vocabulary.studentClass }}</text></view><view class="header-action"><UniIcons type="person-filled" class="header-action-icon"></UniIcons></view></view>
|
||||
<scroll-view class="content-scroll" direction="vertical" scroll-y="true"><view v-if="isLoading" class="status-panel"><text class="status-text">词汇书加载中</text></view><view v-else class="content-panel"><view class="list-heading"><text class="list-title">全部词书</text><text class="book-count">{{ vocabulary.bookCount }} 本</text></view><text class="list-description">每本词书的总词量与已学进度</text><view v-for="book in vocabulary.books" :key="book.id" class="book-card" :class="'book-card-' + book.color"><view class="book-heading"><view class="book-icon" :class="'book-icon-' + book.color"><image v-if="book.cover" class="book-cover" :src="book.cover" mode="aspectFill"></image><UniIcons v-else type="font" class="book-icon-glyph" :class="'book-icon-glyph-' + book.color"></UniIcons></view><view class="book-copy"><text class="book-name">{{ book.name }}</text><view class="book-meta"><text class="book-total">{{ book.subtitle }}</text><text class="book-level">{{ book.levelText }}</text></view></view><text class="book-status" :class="'book-status-' + book.color">{{ book.statusText }}</text></view><view class="progress-heading"><text class="progress-label">学习进度</text><text class="progress-value">{{ book.learnedWords > 0 ? '已学 ' + book.learnedWords + ' / ' + book.totalWords + ' 词 · ' + progressPercent(book) + '%' : '未开始' }}</text></view><view class="progress-track"><view class="progress-fill" :class="'progress-fill-' + book.color" :style="{ width: progressPercent(book) + '%' }"></view></view></view></view></scroll-view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
@@ -26,7 +26,14 @@
|
||||
}
|
||||
if (studentId) {
|
||||
const res = await apiGetStudentVocabulary(studentId)
|
||||
if (res && res.data) vocabulary.value = res.data
|
||||
if (res && res.data) {
|
||||
vocabulary.value = {
|
||||
...res.data,
|
||||
studentName: homeStudentProfile.value?.name ?? '',
|
||||
studentGrade: homeStudentProfile.value?.grade ?? '',
|
||||
studentClass: homeStudentProfile.value?.className ?? ''
|
||||
}
|
||||
}
|
||||
}
|
||||
isLoading.value = false
|
||||
}
|
||||
@@ -63,7 +70,8 @@
|
||||
.book-card-blue { border-color: #9cd7ff; background-color: #eff8ff; }
|
||||
.book-card-gray { border-color: #dbe2eb; background-color: #f2f6fa; }
|
||||
.book-heading { display: flex; flex-direction: row; align-items: flex-start; }
|
||||
.book-icon { display: flex; flex-direction: column; align-items: center; justify-content: center; width: 40px; height: 40px; border-radius: 11px; }
|
||||
.book-icon { display: flex; flex-direction: column; align-items: center; justify-content: center; width: 64px; height: 64px; flex-shrink: 0; border-radius: 11px; }
|
||||
.book-cover { width: 64px; height: 64px; border-radius: 11px; }
|
||||
.book-icon-green { background-color: #16bd88; }
|
||||
.book-icon-yellow { background-color: #fff0c5; }
|
||||
.book-icon-blue { background-color: #d9f0ff; }
|
||||
|
||||
Reference in New Issue
Block a user