完善预警和积分接口接入

This commit is contained in:
tanxin
2026-11-06 15:07:51 +08:00
parent af73453035
commit 267ef98616
5 changed files with 172 additions and 388 deletions
+7 -5
View File
@@ -4,8 +4,8 @@
<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-if="alerts.length === 0" class="status-panel"><text class="status-text">暂无预警信息</text></view>
<view v-else v-for="alert in alerts" :key="alert.id" class="alert-card">
<view class="alert-card-top"><view class="alert-level-dot" :class="levelDotClass(alert.level)"></view><text class="alert-title">{{ alert.title }}</text><text v-if="!alert.isRead" class="unread-mark">未读</text></view>
<view v-else v-for="alert in alerts" :key="alert.id" class="alert-card">
<view class="alert-card-top"><view class="alert-level-dot" :class="levelDotClass(alert.level)"></view><text class="alert-title">{{ alert.title }}</text><text class="alert-status" :class="alert.isRead ? 'alert-status-normal' : 'alert-status-triggered'">{{ alert.statusName }}</text></view>
<text class="alert-content">{{ alert.content }}</text>
<text class="alert-time">{{ formatTime(alert.occurredAt) }}</text>
</view>
@@ -14,7 +14,7 @@
</template>
<script setup lang="ts">
import { apiGetStudentAlerts, type StudentAlert, type StudentAlertLevel } from '../../api/student'
import { apiGetStudentLearningWarnings, type StudentAlert, type StudentAlertLevel } from '../../api/student'
import { homeStudentProfile } from '../../store/home'
const alerts = ref<StudentAlert[]>([])
@@ -31,7 +31,7 @@
isLoading.value = false
return
}
const res = await apiGetStudentAlerts(studentId, 1, 20)
const res = await apiGetStudentLearningWarnings(studentId, true, false)
if (res && res.data) {
alerts.value = res.data.list
}
@@ -62,7 +62,9 @@
.alert-level-medium { background-color: #e8a129; }
.alert-level-low { background-color: #4d87cf; }
.alert-title { flex: 1; font-size: 15px; font-weight: 700; color: #30384a; }
.unread-mark { padding: 3px 7px; border-radius: 8px; font-size: 10px; color: #554be9; background-color: #eeefff; }
.alert-status { padding: 3px 7px; border-radius: 8px; font-size: 10px; }
.alert-status-triggered { color: #e25a5a; background-color: #fff0f1; }
.alert-status-normal { color: #16a979; background-color: #e8fbf3; }
.alert-content { margin-top: 10px; font-size: 13px; line-height: 20px; color: #626c7e; }
.alert-time { margin-top: 10px; font-size: 11px; color: #9aa2b1; }
</style>
+43 -3
View File
@@ -1,17 +1,23 @@
<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">{{ 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>
<scroll-view class="content-scroll" direction="vertical" scroll-y="true" refresher-enabled="true" :refresher-triggered="isRefreshing" @refresherrefresh="refreshGrowth" @scrolltolower="loadMoreRecords"><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 v-if="growth.records.length === 0" class="flow-empty"><text class="flow-empty-text">暂无积分流水</text></view><view v-else 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 >= 0 ? '+' : '' }}{{ record.points }}</text></view></view><view v-if="isLoadingMore" class="records-footer"><text class="records-footer-text">加载更多...</text></view><view v-else-if="!hasMoreRecords && growth.records.length > 0" class="records-footer"><text class="records-footer-text">没有更多记录</text></view></view></view></scroll-view>
</view>
</template>
<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 { apiGetStudentGrowth, apiGetStudentHomeProfile, apiGetStudentPointRecords, type StudentGrowth } from '../../api/student'
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)
const isRefreshing = ref(false)
const isLoadingMore = ref(false)
const hasMoreRecords = ref(true)
const recordsPageIndex = ref(1)
const recordsLastId = ref<number | null>(null)
const currentStudentId = ref('')
onLoad(() => { loadGrowth() })
@@ -25,12 +31,44 @@
}
}
if (studentId) {
currentStudentId.value = studentId
const res = await apiGetStudentGrowth(studentId)
if (res && res.data) growth.value = res.data
await loadPointRecords(false)
}
isLoading.value = false
}
async function loadPointRecords(append: boolean) {
if (!currentStudentId.value || isLoadingMore.value) return
if (append && !hasMoreRecords.value) return
if (append) isLoadingMore.value = true
const pageIndex = append ? recordsPageIndex.value + 1 : 1
const res = await apiGetStudentPointRecords(currentStudentId.value, pageIndex, 20, append ? recordsLastId.value ?? undefined : undefined, !append)
if (res && res.data) {
const records = append ? growth.value.records.concat(res.data.records) : res.data.records
growth.value = { ...growth.value, records }
recordsPageIndex.value = res.data.pageIndex
recordsLastId.value = res.data.lastId
hasMoreRecords.value = records.length < res.data.total && res.data.records.length > 0
}
isLoadingMore.value = false
}
async function loadMoreRecords() {
await loadPointRecords(true)
}
async function refreshGrowth() {
if (isRefreshing.value) return
isRefreshing.value = true
hasMoreRecords.value = true
recordsPageIndex.value = 1
recordsLastId.value = null
await loadGrowth()
isRefreshing.value = false
}
function goHome() { uni.reLaunch({ url: '/pages/index/index' }) }
</script>
@@ -72,4 +110,6 @@
.flow-title { font-size: 13px; color: #242b3d; }
.flow-time { margin-top: 3px; font-size: 10px; color: #94a0b1; }
.flow-points { font-size: 13px; font-weight: 700; color: #f29900; }
.flow-empty, .records-footer { display: flex; flex-direction: column; align-items: center; padding: 14px 0 4px; }
.flow-empty-text, .records-footer-text { font-size: 11px; color: #94a0b1; }
</style>
+3 -3
View File
@@ -209,10 +209,10 @@
}
}
}
if (student.value.id) {
if (selectedId) {
await Promise.all([
loadStudentHomeMetrics(selectedId || student.value.id, showLoading),
loadStudentAlerts(selectedId || student.value.id, showLoading)
loadStudentHomeMetrics(selectedId, showLoading),
loadStudentAlerts(selectedId, showLoading)
])
}
}