feat: 🎸 新增了几处加载

This commit is contained in:
2025-08-26 11:27:09 +08:00
parent f4aac0e51e
commit 4d92976911
7 changed files with 181 additions and 192 deletions

View File

@@ -23,6 +23,11 @@
color: #262626; color: #262626;
} }
.module-class-rank-spin,
.empty-data {
margin: auto;
}
.module-class-rank-podium { .module-class-rank-podium {
width: 288px; width: 288px;
height: 120px; height: 120px;

View File

@@ -1,29 +1,11 @@
import { Avatar, Skeleton } from "@arco-design/web-react"; import { Avatar, Spin, Empty } from "@arco-design/web-react";
import "./index.css"; import "./index.css";
const positions = ["item2", "item1", "item3"]; const positions = ["item2", "item1", "item3"];
const icons = ["icon2", "icon1", "icon3"]; const icons = ["icon2", "icon1", "icon3"];
const Rank = ({ className, data = null, loading = false }) => { const Rank = ({ className, data, loading }) => {
if (loading) { const rankings = data?.rankings?.slice(0, 6) || [];
return (
<div className={`module-class-rank ${className}`}>
<p className="module-class-rank-title">班级排名</p>
<Skeleton loading={true} />
</div>
);
}
if (!data || !data?.rankings || data?.rankings?.length === 0) {
return (
<div className={`module-class-rank ${className}`}>
<p className="module-class-rank-title">班级排名</p>
<div className="no-data">暂无排名数据</div>
</div>
);
}
const rankings = data?.rankings?.slice(0, 6);
// 安全处理领奖台学生确保至少有3个位置 // 安全处理领奖台学生确保至少有3个位置
const podiumStudents = [ const podiumStudents = [
@@ -37,47 +19,57 @@ const Rank = ({ className, data = null, loading = false }) => {
return ( return (
<div className={`module-class-rank ${className}`}> <div className={`module-class-rank ${className}`}>
<p className="module-class-rank-title">班级排名</p> <p className="module-class-rank-title">班级排名</p>
{loading ? (
<ul className="module-class-rank-podium"> <Spin size={40} className="module-class-rank-spin" />
{podiumStudents.map((student, index) => { ) : !data || !data?.rankings || data?.rankings?.length === 0 ? (
return student ? ( <Empty description="暂无排名数据" className="empty-data" />
<li ) : (
key={student.studentId} <>
className={`module-class-rank-podium-${positions[index]}`} <ul className="module-class-rank-podium">
> {podiumStudents.map((student, index) => {
<Avatar className="module-class-rank-podium-avatar"> return student ? (
{student.avatar ? ( <li
<img alt="avatar" src={student.avatar} /> key={student.studentId}
) : ( className={`module-class-rank-podium-${positions[index]}`}
student.studentName?.charAt(0) || "?" >
)} <Avatar className="module-class-rank-podium-avatar">
</Avatar> {student.avatar ? (
<span className="module-class-rank-podium-name"> <img alt="avatar" src={student.avatar} />
{student.studentName || "未知"} ) : (
</span> student.studentName?.charAt(0) || "?"
<i className={`module-class-rank-podium-${icons[index]}`}></i> )}
</li> </Avatar>
) : ( <span className="module-class-rank-podium-name">
<li {student.studentName || "未知"}
key={`empty-${index}`} </span>
className={`module-class-rank-podium-${positions[index]} empty`} <i className={`module-class-rank-podium-${icons[index]}`}></i>
> </li>
<div className="module-class-rank-podium-placeholder"> ) : (
<span>-</span> <li
</div> key={`empty-${index}`}
</li> className={`module-class-rank-podium-${positions[index]} empty`}
); >
})} <div className="module-class-rank-podium-placeholder">
</ul> <span>-</span>
<ul className="module-class-rank-list"> </div>
{listStudents.map((student, index) => ( </li>
<li key={student.studentId} className="module-class-rank-list-item"> );
<i className={`module-class-rank-list-item-icon${index + 4}`} /> })}
<p>{student.studentName || "未知"}</p> </ul>
<span>{student.score}</span> <ul className="module-class-rank-list">
</li> {listStudents.map((student, index) => (
))} <li
</ul> key={student.studentId}
className="module-class-rank-list-item"
>
<i className={`module-class-rank-list-item-icon${index + 4}`} />
<p>{student.studentName || "未知"}</p>
<span>{student.score}</span>
</li>
))}
</ul>
</>
)}
</div> </div>
); );
}; };

View File

@@ -19,6 +19,9 @@
color: #262626; color: #262626;
margin-bottom: 20px; margin-bottom: 20px;
} }
.study-studes-card-spin {
margin: auto;
}
.study-studes-card-list { .study-studes-card-list {
width: 100%; width: 100%;

View File

@@ -1,27 +1,14 @@
import * as echarts from "echarts"; import * as echarts from "echarts";
import { useSelector } from "react-redux"; import { useSelector } from "react-redux";
import { Spin } from "@arco-design/web-react";
import StudyProgress from "../StudyProgress"; import StudyProgress from "../StudyProgress";
import ScoreRingChart from "../ScoreRingChart"; import ScoreRingChart from "../ScoreRingChart";
import AttendanceRingChart from "../AttendanceRingChart"; import AttendanceRingChart from "../AttendanceRingChart";
import "./index.css"; import "./index.css";
const StudyStudes = ({ const StudyStudes = ({ studyStatistics, learningProgress, loading }) => {
studyStatistics,
learningProgress,
loading = false,
}) => {
const studentInfo = useSelector((state) => state.student.studentInfo); const studentInfo = useSelector((state) => state.student.studentInfo);
// 如果数据还在加载中,显示加载状态
if (loading) {
return (
<div className="study-studes-card-wrapper">
<p className="study-studes-card-title">学习情况</p>
<div>加载中...</div>
</div>
);
}
// 使用传入的数据只在undefined时使用默认值0也是有效值 // 使用传入的数据只在undefined时使用默认值0也是有效值
const personalStudyHours = studyStatistics?.studyTime?.personal ?? 0; const personalStudyHours = studyStatistics?.studyTime?.personal ?? 0;
const classAverageStudyHours = studyStatistics?.studyTime?.classAverage ?? 0; const classAverageStudyHours = studyStatistics?.studyTime?.classAverage ?? 0;
@@ -113,114 +100,118 @@ const StudyStudes = ({
return ( return (
<div className="study-studes-card-wrapper"> <div className="study-studes-card-wrapper">
<p className="study-studes-card-title">学习情况</p> <p className="study-studes-card-title">学习情况</p>
<ul className="study-studes-card-list"> {loading ? (
{/* 时长 */} <Spin size={40} className="study-studes-card-spin" />
<li className="study-studes-card-study-info"> ) : (
<div className="study-studes-card-time-wrapper"> <ul className="study-studes-card-list">
<div className="study-studes-card-study-time-wrapper"> {/* 时长 */}
<p className="study-studes-card-study-info-title"> <li className="study-studes-card-study-info">
个人学习时长h <div className="study-studes-card-time-wrapper">
</p> <div className="study-studes-card-study-time-wrapper">
<p className="study-studes-card-study-time-line"> <p className="study-studes-card-study-info-title">
<i 个人学习时长h
style={{ </p>
width: `${Math.min( <p className="study-studes-card-study-time-line">
(personalStudyHours / classAverageStudyHours) * 70, <i
100 style={{
)}%`, width: `${Math.min(
}} (personalStudyHours / classAverageStudyHours) * 70,
> 100
<span>{personalStudyHours}</span> )}%`,
</i> }}
</p> >
</div> <span>{personalStudyHours}</span>
<div className="study-studes-card-study-time-wrapper"> </i>
<p className="study-studes-card-study-info-title"> </p>
班级平均学习时长h
</p>
<p className="study-studes-card-study-time-line study-studes-card-study-time-line-class">
<i style={{ width: "80%" }}>
<span>{classAverageStudyHours}</span>
</i>
</p>
</div>
</div>
</li>
{/* 进度 */}
<li className="study-studes-card-study-progress">
<StudyProgress
value={overallProgress}
className="study-studes-card-study-progress-chart"
/>
<p className="study-studes-card-study-progress-title">
整体课程完成进度
</p>
</li>
{/* 课程整体完成情况 */}
<li className="study-studes-card-curriculum">
<ScoreRingChart
title={`整体课程\n完成情况`}
ringData={courseRingData}
className="study-studes-card-curriculum-chart"
/>
<div className="study-studes-card-curriculum-info">
<p>班级平均进度</p>
<span className="study-studes-card-curriculum-info-span1">
{classAverageCourseProgress}%
</span>
</div>
<div className="study-studes-card-curriculum-info">
<p>我的进度</p>
<span className="study-studes-card-curriculum-info-span2">
{personalCourseProgress}%
</span>
</div>
</li>
{/* 课后作业完成情况 */}
<li className="study-studes-card-curriculum-homework study-studes-card-curriculum">
<ScoreRingChart
title={`课后作业\n完成情况`}
ringData={homeworkRingData}
className="study-studes-card-curriculum-chart"
/>
<div className="study-studes-card-curriculum-info">
<p>班级平均进度</p>
<span className="study-studes-card-curriculum-info-span1">
{classAverageHomeworkProgress}%
</span>
</div>
<div className="study-studes-card-curriculum-info">
<p>我的进度</p>
<span className="study-studes-card-curriculum-info-span2">
{personalHomeworkProgress}%
</span>
</div>
</li>
{/* 考勤情况 */}
<li className="study-studes-card-curriculum">
<AttendanceRingChart
data={attendanceData}
centerContent={
<div className="study-studes-card-curriculum-chart-center-content">
考勤情况
</div> </div>
} <div className="study-studes-card-study-time-wrapper">
/> <p className="study-studes-card-study-info-title">
班级平均学习时长h
</p>
<p className="study-studes-card-study-time-line study-studes-card-study-time-line-class">
<i style={{ width: "80%" }}>
<span>{classAverageStudyHours}</span>
</i>
</p>
</div>
</div>
</li>
{/* 进度 */}
<li className="study-studes-card-study-progress">
<StudyProgress
value={overallProgress}
className="study-studes-card-study-progress-chart"
/>
<p className="study-studes-card-study-progress-title">
整体课程完成进度
</p>
</li>
{/* 课程整体完成情况 */}
<li className="study-studes-card-curriculum">
<ScoreRingChart
title={`整体课程\n完成情况`}
ringData={courseRingData}
className="study-studes-card-curriculum-chart"
/>
<div className="study-studes-card-curriculum-info">
<p>班级平均进度</p>
<span className="study-studes-card-curriculum-info-span1">
{classAverageCourseProgress}%
</span>
</div>
<div className="study-studes-card-curriculum-info">
<p>我的进度</p>
<span className="study-studes-card-curriculum-info-span2">
{personalCourseProgress}%
</span>
</div>
</li>
{/* 课后作业完成情况 */}
<li className="study-studes-card-curriculum-homework study-studes-card-curriculum">
<ScoreRingChart
title={`课后作业\n完成情况`}
ringData={homeworkRingData}
className="study-studes-card-curriculum-chart"
/>
<div className="study-studes-card-curriculum-info">
<p>班级平均进度</p>
<span className="study-studes-card-curriculum-info-span1">
{classAverageHomeworkProgress}%
</span>
</div>
<div className="study-studes-card-curriculum-info">
<p>我的进度</p>
<span className="study-studes-card-curriculum-info-span2">
{personalHomeworkProgress}%
</span>
</div>
</li>
{/* 考勤情况 */}
<li className="study-studes-card-curriculum">
<AttendanceRingChart
data={attendanceData}
centerContent={
<div className="study-studes-card-curriculum-chart-center-content">
考勤情况
</div>
}
/>
<div className="study-studes-card-curriculum-info"> <div className="study-studes-card-curriculum-info">
<p>出勤率</p> <p>出勤率</p>
<span className="study-studes-card-curriculum-info-span1"> <span className="study-studes-card-curriculum-info-span1">
{attendanceRate}% {attendanceRate}%
</span> </span>
</div> </div>
<div className="study-studes-card-curriculum-info"> <div className="study-studes-card-curriculum-info">
<p>缺勤率</p> <p>缺勤率</p>
<span className="study-studes-card-curriculum-info-span3"> <span className="study-studes-card-curriculum-info-span3">
{absenceRate}% {absenceRate}%
</span> </span>
</div> </div>
</li> </li>
</ul> </ul>
)}
</div> </div>
); );
}; };

View File

@@ -1,4 +1,4 @@
import { useState, useEffect, useCallback } from "react"; import { useState, useEffect } from "react";
import { useDispatch } from "react-redux"; import { useDispatch } from "react-redux";
import ProfileCard from "./components/ProfileCard"; import ProfileCard from "./components/ProfileCard";
import Rank from "@/components/Rank"; import Rank from "@/components/Rank";
@@ -11,13 +11,15 @@ import "./index.css";
const PersonalProfile = () => { const PersonalProfile = () => {
const dispatch = useDispatch(); const dispatch = useDispatch();
const [profileData, setProfileData] = useState(null); // 个人档案完整数据 const [profileData, setProfileData] = useState(null); // 个人档案完整数据
const [loading, setLoading] = useState(true); const [loading, setLoading] = useState(false);
// 获取个人档案完整数据 // 获取个人档案完整数据
const queryProfileOverview = useCallback(async () => { const queryProfileOverview = async () => {
try { try {
if (loading) return;
setLoading(true); setLoading(true);
const res = await getProfileOverview(); const res = await getProfileOverview();
setLoading(false);
if (res.success) { if (res.success) {
const data = res.data; const data = res.data;
@@ -36,11 +38,11 @@ const PersonalProfile = () => {
} finally { } finally {
setLoading(false); setLoading(false);
} }
}, [dispatch]); };
useEffect(() => { useEffect(() => {
queryProfileOverview(); queryProfileOverview();
}, [queryProfileOverview]); }, []);
return ( return (
<div className="personal-profile"> <div className="personal-profile">

View File

@@ -5,7 +5,6 @@ export async function getStudyRecordsProgress() {
return request({ return request({
url: `/api/study-records/progress`, url: `/api/study-records/progress`,
method: "GET", method: "GET",
namespace: "dashboardLoading",
}); });
} }
@@ -15,7 +14,6 @@ export async function getMyTasks(params = {}) {
url: `/api/tasks/my-tasks`, url: `/api/tasks/my-tasks`,
method: "GET", method: "GET",
params: params, params: params,
namespace: "dashboardLoading",
}); });
} }
@@ -25,6 +23,5 @@ export async function getClassRanking(params = { limit: 6 }) {
url: `/api/rankings/class`, url: `/api/rankings/class`,
method: "GET", method: "GET",
params: params, params: params,
namespace: "dashboardLoading",
}); });
} }

View File

@@ -5,6 +5,5 @@ export async function getLoginStudentInfo() {
return request({ return request({
url: `/api/students/me`, url: `/api/students/me`,
method: "GET", method: "GET",
namespace: "globalLoading",
}); });
} }