This commit is contained in:
2025-08-15 16:16:41 +08:00
commit 182abccc60
171 changed files with 26833 additions and 0 deletions

View File

@@ -0,0 +1,98 @@
import { useEffect, useRef, useState } from "react";
import * as echarts from "echarts";
import "echarts/lib/chart/pie";
import "echarts/lib/component/tooltip";
const AttendanceRingChart = ({
data = [],
centerContent = null, // 新增参数,用于传入中间的自定义内容
centerContentStyle = {}, // 新增参数,用于自定义中间内容的样式
}) => {
const chartRef = useRef(null);
const [chartInstance, setChartInstance] = useState(null);
const containerRef = useRef(null); // 新增容器ref
// 初始化/更新图表
useEffect(() => {
if (!chartRef.current) return;
// 销毁旧实例
if (chartInstance) {
chartInstance.dispose();
}
// 创建新实例
const newChart = echarts.init(chartRef.current);
setChartInstance(newChart);
// 配置图表选项
const option = {
tooltip: {
trigger: "item",
},
series: [
{
type: "pie",
radius: ["90%", "80%"],
avoidLabelOverlap: false,
padAngle: 5,
itemStyle: {
borderRadius: 30,
},
label: {
show: false,
},
emphasis: {
label: { show: false },
},
labelLine: {
show: false,
},
data,
},
],
};
newChart.setOption(option);
// 监听窗口变化自动调整大小
window.addEventListener("resize", () => newChart.resize());
return () => {
window.removeEventListener("resize", () => newChart.resize());
newChart.dispose();
};
}, [data]);
return (
<div
ref={containerRef}
style={{
position: "relative",
width: "100%",
height: "100%",
}}
>
<div
ref={chartRef}
style={{
width: "100%",
height: "100%",
}}
/>
{centerContent && (
<div
style={{
position: "absolute",
top: "50%",
left: "50%",
transform: "translate(-50%, -50%)",
textAlign: "center",
...centerContentStyle,
}}
>
{centerContent}
</div>
)}
</div>
);
};
export default AttendanceRingChart;

View File

@@ -0,0 +1,153 @@
.profile-card-wrapper {
width: 100%;
height: 374px;
border-radius: 8px;
background-image: url("@/assets/images/PersonalProfile/personal_profile_bg.png");
background-size: 100% 100%;
display: flex;
justify-content: flex-start;
align-items: center;
flex-direction: column;
box-sizing: border-box;
padding: 16px;
.profile-card-user-info {
width: 100%;
height: 60px;
display: flex;
justify-content: flex-start;
align-items: center;
margin-bottom: 10px;
.profile-card-user-avatar {
width: 60px;
height: 60px;
margin-right: 10px;
}
.profile-card-user-name {
width: 200px;
height: 100%;
display: flex;
justify-content: flex-start;
align-items: flex-start;
flex-direction: column;
.profile-card-user-name-text {
font-size: 20px;
font-weight: 600;
color: #1d2129;
margin-bottom: 5px;
position: relative;
&::after {
content: "";
position: absolute;
right: -25px;
top: 50%;
transform: translateY(-50%);
width: 18px;
height: 18px;
background-image: url("@/assets/images/PersonalProfile/male_icon.png");
background-size: 100% 100%;
}
}
.profile-card-user-name-student-id {
font-size: 12px;
font-weight: 400;
color: #4e5969;
}
}
}
.profile-card-achievement-info {
width: 328px;
height: 47px;
display: flex;
justify-content: space-around;
align-items: center;
margin-bottom: 10px;
.profile-card-achievement-info-item {
width: 80px;
height: 100%;
display: flex;
justify-content: space-between;
align-items: center;
flex-direction: column;
.profile-card-achievement-info-item-title {
font-size: 14px;
font-weight: 400;
color: #4e5969;
}
.profile-card-achievement-info-item-text {
font-size: 16px;
font-weight: 600;
color: #1d2129;
}
}
}
.profile-card-class-info {
width: 328px;
display: flex;
justify-content: flex-start;
align-items: center;
flex-direction: column;
box-sizing: border-box;
padding: 10px;
border-radius: 8px;
border: 1px solid #ffffff;
background-color: rgba(255, 255, 255, 0.8);
/* 投影box-shadow */
box-shadow: 2px 2px 16.4px 0 rgba(103, 162, 247, 0.25);
.profile-card-class-info-item:last-child {
margin-bottom: 0;
}
.profile-card-class-info-item {
width: 100%;
height: 32px;
position: relative;
display: flex;
align-items: center;
margin-bottom: 10px;
.profile-card-class-info-item-icon {
position: absolute;
left: 0;
width: 32px;
height: 32px;
background-size: 100% 100%;
}
.icon-school {
background-image: url("@/assets/images/PersonalProfile/school_icon.png");
}
.icon-major {
background-image: url("@/assets/images/PersonalProfile/major_icon.png");
}
.icon-location {
background-image: url("@/assets/images/PersonalProfile/location_icon.png");
}
.icon-course {
background-image: url("@/assets/images/PersonalProfile/course_icon.png");
}
.profile-card-class-info-item-title {
font-size: 14px;
font-weight: 400;
color: #4e5969;
position: absolute;
left: 40px;
}
.profile-card-class-info-item-text {
font-size: 16px;
font-weight: 400;
color: #1d2129;
position: absolute;
right: 0px;
}
}
}
}

View File

@@ -0,0 +1,82 @@
import { Avatar } from "@arco-design/web-react";
import { mockData } from "@/data/mockData";
import "./index.css";
const ProfileCard = () => {
const { profile } = mockData;
return (
<div className="profile-card-wrapper">
<div className="profile-card-user-info">
<Avatar className="profile-card-user-avatar">
<img
alt="avatar"
src="//p1-arco.byteimg.com/tos-cn-i-uwbnlip3yd/3ee5f13fb09879ecb5185e440cef6eb9.png~tplv-uwbnlip3yd-webp.webp"
/>
</Avatar>
<div className="profile-card-user-name">
<span className="profile-card-user-name-text">{profile.name}</span>
<p className="profile-card-user-name-student-id">
学号 {profile.studentId}
</p>
</div>
</div>
<ul className="profile-card-achievement-info">
<li className="profile-card-achievement-info-item">
<span className="profile-card-achievement-info-item-title">学分</span>
<span className="profile-card-achievement-info-item-text">
{profile?.badges?.credits}
</span>
</li>
<li className="profile-card-achievement-info-item">
<span className="profile-card-achievement-info-item-title">
班级排名
</span>
<span className="profile-card-achievement-info-item-text">
{profile?.badges?.classRank}
</span>
</li>
<li className="profile-card-achievement-info-item">
<span className="profile-card-achievement-info-item-title">MBTI</span>
<span className="profile-card-achievement-info-item-text">
{profile?.badges?.mbti}
</span>
</li>
</ul>
<ul className="profile-card-class-info">
<li className="profile-card-class-info-item">
<i className="profile-card-class-info-item-icon icon-school" />
<span className="profile-card-class-info-item-title">学校</span>
<span className="profile-card-class-info-item-text">
{profile?.school}
</span>
</li>
<li className="profile-card-class-info-item">
<i className="profile-card-class-info-item-icon icon-major" />
<span className="profile-card-class-info-item-title">专业</span>
<span className="profile-card-class-info-item-text">
{profile?.major}
</span>
</li>
<li className="profile-card-class-info-item">
<i className="profile-card-class-info-item-icon icon-location" />
<span className="profile-card-class-info-item-title">
就业管家课程
</span>
<span className="profile-card-class-info-item-text">
{profile?.course}
</span>
</li>
<li className="profile-card-class-info-item">
<i className="profile-card-class-info-item-icon icon-course" />
<span className="profile-card-class-info-item-title">垂直方向</span>
<span className="profile-card-class-info-item-text">
{profile?.course}
</span>
</li>
</ul>
</div>
);
};
export default ProfileCard;

View File

@@ -0,0 +1,130 @@
import React, { useRef, useEffect } from "react";
import * as echarts from "echarts";
const screenWidth = window.screen.width; // 物理屏幕宽度(单位:像素)
const ScoreRingChart = ({
className = "",
ringData = [], // 数据
title = "",
bgColor = "#F7F8FA", // 未填充部分背景色
}) => {
// 创建图表容器引用
const chartRef = useRef(null);
// 图表实例引用
const chartInstance = useRef(null);
// 初始化图表
const initChart = () => {
if (!chartRef.current) return;
// 销毁已存在的图表实例
if (chartInstance.current) {
chartInstance.current.dispose();
}
// 创建新图表实例
chartInstance.current = echarts.init(chartRef.current);
// 配置图表
const option = {
series: [
{
type: "gauge",
startAngle: 90,
endAngle: -270,
radius: (80 / 1440) * screenWidth,
pointer: {
show: false,
},
progress: {
show: true,
overlap: false,
roundCap: true,
clip: false,
},
axisLine: {
lineStyle: {
width: 20,
color: [[1, bgColor]],
},
},
splitLine: {
show: false,
},
axisTick: {
show: false,
},
axisLabel: {
show: false,
},
data: [ringData[0]],
title: {
fontSize: 14,
},
detail: {
fontSize: 16,
color: "#1D2129",
formatter: title,
lineHeight: 20,
height: 40,
offsetCenter: [0, 0],
},
},
{
type: "gauge",
startAngle: 90,
endAngle: -270,
radius: (55 / 1440) * screenWidth,
pointer: {
show: false,
},
progress: {
show: true,
overlap: false,
roundCap: true,
clip: false,
},
axisLine: {
lineStyle: {
width: 20,
color: [[1, bgColor]],
},
},
splitLine: {
show: false,
},
axisTick: {
show: false,
},
axisLabel: {
show: false,
},
data: [ringData[1]],
title: {
fontSize: 14,
},
detail: { show: false },
},
],
};
// 设置图表配置
chartInstance.current.setOption(option);
};
// 组件挂载和更新时初始化图表
useEffect(() => {
initChart();
return () => {
if (chartInstance.current) {
chartInstance.current.dispose();
chartInstance.current = null;
}
};
}, [ringData, title, bgColor]);
return <div ref={chartRef} className={className} />;
};
export default ScoreRingChart;

View File

@@ -0,0 +1,235 @@
import { useRef, useEffect } from "react";
import * as echarts from "echarts";
const screenWidth = window.screen.width; // 物理屏幕宽度(单位:像素)
const StudyProgress = ({
value = 0, // 默认值78%
width = (180 / 1440) * screenWidth,
height = (180 / 1440) * screenWidth,
className = "",
}) => {
// 创建图表容器引用
const chartRef = useRef(null);
// 图表实例引用
const chartInstance = useRef(null);
// 图表配置常量
const ROOT_PATH = "https://echarts.apache.org/examples";
const _panelImageURL = ROOT_PATH + "/data/asset/img/custom-gauge-panel.png";
const _animationDuration = 1000;
const _animationDurationUpdate = 1000;
const _animationEasingUpdate = "quarticInOut";
const _valOnRadianMax = 200;
const _outerRadius = Math.min(width, height) / 2;
const _innerRadius = _outerRadius * 0.85;
const _pointerInnerRadius = _outerRadius * 0.2;
const _insidePanelRadius = _outerRadius * 0.7;
// 转换为极坐标点
const convertToPolarPoint = (renderItemParams, radius, radian) => {
return [
Math.cos(radian) * radius + renderItemParams.coordSys.cx,
-Math.sin(radian) * radius + renderItemParams.coordSys.cy,
];
};
// 创建指针点
const makePionterPoints = (renderItemParams, polarEndRadian) => {
return [
convertToPolarPoint(renderItemParams, _outerRadius, polarEndRadian),
convertToPolarPoint(
renderItemParams,
_outerRadius,
polarEndRadian + Math.PI * 0.03
),
convertToPolarPoint(
renderItemParams,
_pointerInnerRadius,
polarEndRadian
),
];
};
// 生成文本
const makeText = (valOnRadian) => {
if (valOnRadian < -10) {
console.error("非法值:", valOnRadian);
}
return ((valOnRadian / _valOnRadianMax) * 100).toFixed(0) + "%";
};
// 渲染项函数
const renderItem = (params, api) => {
// 将百分比转换为弧度值
const valOnRadian = (value / 100) * _valOnRadianMax;
const coords = api.coord([api.value(0), valOnRadian]);
const polarEndRadian = coords[3];
const imageStyle = {
image: _panelImageURL,
x: params.coordSys.cx - _outerRadius,
y: params.coordSys.cy - _outerRadius,
width: _outerRadius * 2,
height: _outerRadius * 2,
};
return {
type: "group",
children: [
{
type: "image",
style: imageStyle,
clipPath: {
type: "sector",
shape: {
cx: params.coordSys.cx,
cy: params.coordSys.cy,
r: _outerRadius,
r0: _innerRadius,
startAngle: 0,
endAngle: -polarEndRadian,
transition: "endAngle",
enterFrom: { endAngle: 0 },
},
},
},
{
type: "image",
style: imageStyle,
clipPath: {
type: "polygon",
shape: {
points: makePionterPoints(params, polarEndRadian),
},
extra: {
polarEndRadian: polarEndRadian,
transition: "polarEndRadian",
enterFrom: { polarEndRadian: 0 },
},
during: function (apiDuring) {
apiDuring.setShape(
"points",
makePionterPoints(params, apiDuring.getExtra("polarEndRadian"))
);
},
},
},
{
type: "circle",
shape: {
cx: params.coordSys.cx,
cy: params.coordSys.cy,
r: _insidePanelRadius,
},
style: {
fill: "#fff",
shadowBlur: 25,
shadowOffsetX: 0,
shadowOffsetY: 0,
shadowColor: "rgba(76,107,167,0.4)",
},
},
{
type: "text",
extra: {
valOnRadian: valOnRadian,
transition: "valOnRadian",
enterFrom: { valOnRadian: 0 },
},
style: {
text: makeText(valOnRadian),
fontSize: Math.min(width, height) / 8,
fontWeight: 700,
x: params.coordSys.cx,
y: params.coordSys.cy,
fill: "rgb(0,50,190)",
align: "center",
verticalAlign: "middle",
enterFrom: { opacity: 0 },
},
during: function (apiDuring) {
apiDuring.setStyle(
"text",
makeText(apiDuring.getExtra("valOnRadian"))
);
},
},
],
};
};
// 图表选项配置
const getChartOption = () => ({
animationEasing: _animationEasingUpdate,
animationDuration: _animationDuration,
animationDurationUpdate: _animationDurationUpdate,
animationEasingUpdate: _animationEasingUpdate,
dataset: {
source: [[1, (value / 100) * _valOnRadianMax]],
},
tooltip: {},
angleAxis: {
type: "value",
startAngle: 0,
show: false,
min: 0,
max: _valOnRadianMax,
},
radiusAxis: {
type: "value",
show: false,
},
polar: {},
series: [
{
type: "custom",
coordinateSystem: "polar",
renderItem: renderItem,
},
],
});
// 初始化和更新图表
useEffect(() => {
// 确保DOM已经渲染
if (chartRef.current && !chartInstance.current) {
chartInstance.current = echarts.init(chartRef.current);
chartInstance.current.setOption(getChartOption());
}
// 更新图表
if (chartInstance.current) {
chartInstance.current.setOption(getChartOption());
}
// 处理窗口大小变化
const handleResize = () => {
if (chartInstance.current) {
chartInstance.current.resize();
}
};
window.addEventListener("resize", handleResize);
// 组件卸载时清理
return () => {
window.removeEventListener("resize", handleResize);
if (chartInstance.current) {
chartInstance.current.dispose();
chartInstance.current = null;
}
};
}, [value, width, height]);
return (
<div
ref={chartRef}
className={className}
style={{
width: width,
height: height,
}}
/>
);
};
export default StudyProgress;

View File

@@ -0,0 +1,196 @@
.study-studes-card-wrapper {
width: 100%;
height: 100%;
border-radius: 8px;
background-color: #fff;
display: flex;
justify-content: flex-start;
align-items: center;
flex-direction: column;
box-sizing: border-box;
padding: 20px;
.study-studes-card-title {
height: 30px;
width: 100%;
font-size: 20px;
font-weight: 500;
line-height: 30px;
color: #262626;
}
.study-studes-card-list {
width: 100%;
height: 100%;
display: flex;
justify-content: flex-start;
align-items: flex-start;
flex-wrap: wrap;
.study-studes-card-study-info {
flex-shrink: 0;
width: 338px;
height: 308px;
position: relative;
background-image: url("@/assets/images/PersonalProfile/study_study_bg.png");
background-size: 100% 100%;
margin-bottom: 37px;
.study-studes-card-time-wrapper {
position: absolute;
bottom: 10px;
left: 50%;
transform: translateX(-50%);
border-radius: 8px;
border: 1px solid #fff;
width: 314px;
height: 156px;
background-color: #fff;
box-shadow: 2px 2px 16.4px 0px rgba(103, 162, 247, 0.25);
box-sizing: border-box;
padding: 20px;
.study-studes-card-study-time-wrapper {
width: 100%;
height: 60px;
display: flex;
justify-content: flex-start;
align-items: flex-start;
flex-direction: column;
.study-studes-card-study-info-title {
font-size: 14px;
font-weight: 600;
color: #4e5969;
margin-bottom: 10px;
}
.study-studes-card-study-time-line-class {
> i {
background: linear-gradient(
to right,
#4564ff,
#ab2cff
) !important;
}
}
.study-studes-card-study-time-line {
width: 265px;
height: 10px;
border-radius: 12px;
background-color: #cedefa;
position: relative;
> i {
position: absolute;
top: 0;
left: 0;
height: 100%;
border-radius: 12px;
background: linear-gradient(to right, #2c7aff, #00aeff);
> span {
position: absolute;
display: block;
right: 0;
top: 30%;
transform: translate(50%, 30%);
font-style: normal;
width: 38px;
height: 21px;
line-height: 22px;
text-align: center;
font-size: 12px;
font-weight: 600;
color: #2358ed;
background-image: url("@/assets/images/PersonalProfile/line_icon.png");
background-size: 100% 100%;
}
}
}
}
}
}
.study-studes-card-study-progress {
display: flex;
justify-content: flex-start;
align-items: center;
flex-direction: column;
height: 308px;
width: 330px;
border-radius: 8px;
margin-left: 20px;
.study-studes-card-study-progress-chart {
width: 250px;
height: 250px;
margin-top: 50px;
margin-bottom: 20px;
}
.study-studes-card-study-progress-title {
color: #1d2129;
font-size: 16px;
font-weight: 600;
margin-top: 20px;
}
}
.study-studes-card-curriculum-homework {
margin-left: 40px;
margin-right: 40px;
}
.study-studes-card-curriculum {
width: 194px;
height: 350px;
display: flex;
justify-content: flex-start;
align-items: center;
flex-direction: column;
margin-top: 20px;
.study-studes-card-curriculum-chart {
width: 194px;
height: 200px;
}
.study-studes-card-curriculum-info {
width: 194px;
height: 69px;
border-radius: 8px;
background-color: #f7f8fa;
box-sizing: border-box;
padding: 10px;
margin-bottom: 20px;
> p {
font-size: 14px;
font-weight: 400;
color: #4e5969;
}
> span {
font-size: 20px;
font-weight: 600;
}
.study-studes-card-curriculum-info-span1 {
color: #0aa4ff;
}
.study-studes-card-curriculum-info-span2 {
color: #5e57ff;
}
.study-studes-card-curriculum-info-span3 {
color: #ff9d2c;
}
}
}
.study-studes-card-curriculum-chart-center-content {
width: 127px;
height: 127px;
border-radius: 50%;
line-height: 127px;
color: #1d2129;
font-size: 16px;
font-weight: 600;
background-color: #e8f3ff;
box-shadow: inset 0 0 15.6px 0 rgba(0, 172, 255, 0.2); /* #0AC3FF 转 rgba20% 透明度 */
}
}
}

View File

@@ -0,0 +1,148 @@
import * as echarts from "echarts";
import StudyProgress from "../StudyProgress";
import ScoreRingChart from "../ScoreRingChart";
import AttendanceRingChart from "../AttendanceRingChart";
import "./index.css";
const ringData = [
{
value: 80,
name: "我的",
title: {
offsetCenter: ["-30%", "-90%"],
},
itemStyle: {
color: new echarts.graphic.LinearGradient(0, 0, 1, 1, [
{ offset: 0, color: "#2C7AFF" },
{ offset: 1, color: "#00AEFF" },
]),
},
}, // 外环
{
value: 60,
name: "班级",
title: {
offsetCenter: ["-60%", "-90%"],
},
itemStyle: {
color: new echarts.graphic.LinearGradient(0, 0, 1, 1, [
{ offset: 0, color: "#4564FF" },
{ offset: 1, color: "#AB2CFF" },
]),
},
}, // 中环
];
const attendanceData = [
{
name: "出勤率",
value: 22,
itemStyle: {
color: new echarts.graphic.LinearGradient(0, 0, 1, 1, [
{ offset: 0, color: "#2C7AFF" },
{ offset: 1, color: "#00D5FF" },
]),
},
},
{ name: "缺勤率", value: 8, itemStyle: { color: "#FF9D2C" } },
// 可添加更多状态(如迟到、早退、缺勤等)
];
const StudyStudes = () => {
return (
<div className="study-studes-card-wrapper">
<p className="study-studes-card-title">学习情况</p>
<ul className="study-studes-card-list">
{/* 时长 */}
<li className="study-studes-card-study-info">
<div className="study-studes-card-time-wrapper">
<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">
<i style={{ width: "70%" }}>
<span>145</span>
</i>
</p>
</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>145</span>
</i>
</p>
</div>
</div>
</li>
{/* 进度 */}
<li className="study-studes-card-study-progress">
<StudyProgress
value={75}
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={ringData}
className="study-studes-card-curriculum-chart"
/>
<div className="study-studes-card-curriculum-info">
<p>班级平均进度</p>
<span className="study-studes-card-curriculum-info-span1">60%</span>
</div>
<div className="study-studes-card-curriculum-info">
<p>我的进度</p>
<span className="study-studes-card-curriculum-info-span2">60%</span>
</div>
</li>
{/* 课后作业完成情况 */}
<li className="study-studes-card-curriculum-homework study-studes-card-curriculum">
<ScoreRingChart
title={`课后作业\n完成情况`}
ringData={ringData}
className="study-studes-card-curriculum-chart"
/>
<div className="study-studes-card-curriculum-info">
<p>班级平均进度</p>
<span className="study-studes-card-curriculum-info-span1">60%</span>
</div>
<div className="study-studes-card-curriculum-info">
<p>我的进度</p>
<span className="study-studes-card-curriculum-info-span2">60%</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">
<p>出勤率</p>
<span className="study-studes-card-curriculum-info-span1">60%</span>
</div>
<div className="study-studes-card-curriculum-info">
<p>缺勤率</p>
<span className="study-studes-card-curriculum-info-span3">60%</span>
</div>
</li>
</ul>
</div>
);
};
export default StudyStudes;

View File

@@ -0,0 +1,41 @@
/* 个人档案页面样式 */
.personal-profile {
width: 100%;
height: 100%;
display: flex;
justify-content: flex-start;
align-items: center;
flex-direction: column;
}
/* 统一布局系统 */
.unified-profile-layout {
box-sizing: border-box;
padding: 20px;
width: 100%;
height: 781px;
display: flex;
justify-content: space-between;
align-items: center;
.unified-profile-left {
width: 360px;
height: 100%;
display: flex;
flex-direction: column;
justify-content: space-between;
align-items: center;
.unified-profile-rank {
height: 385px;
margin-right: 0;
}
}
.unified-profile-right {
width: 744px;
height: 100%;
border-radius: 8px;
background-color: #fff;
}
}

View File

@@ -0,0 +1,26 @@
import ProfileCard from "./components/ProfileCard";
import Rank from "@/components/Rank";
import StageProgress from "@/components/StageProgress";
import StudyStudes from "./components/StudyStudes";
import "./index.css";
const PersonalProfile = () => {
return (
<div className="personal-profile">
<StageProgress />
{/* 统一布局内容区域 */}
<div className="unified-profile-layout">
<div className="unified-profile-left">
<ProfileCard />
<Rank className="unified-profile-rank" />
</div>
<div className="unified-profile-right">
<StudyStudes />
</div>
</div>
</div>
);
};
export default PersonalProfile;