解决合并冲突: 整合班级排名功能和学习进度功能

- 保留完整的班级排名显示逻辑(领奖台+列表)
- 整合学习进度查询功能
- 兼容不同的响应数据格式
- 添加加载状态和错误处理
This commit is contained in:
2025-08-25 14:32:11 +08:00
26 changed files with 951 additions and 236 deletions

View File

@@ -24,9 +24,14 @@
background-color: #fff;
}
}
.empty-data-wrapper {
width: 100%;
min-height: 555px;
display: flex;
}
.job-info-modal-user-resumes-list {
width: 100%;
min-height: 555px;
margin-top: 16px;
display: grid;
grid-template-columns: repeat(2, 1fr); /* 每行两列 */

View File

@@ -1,23 +1,48 @@
import { useState } from "react";
import { useSelector } from "react-redux";
import { Input } from "@arco-design/web-react";
import Modal from "@/components/Modal";
import { mockData } from "@/data/mockData";
import InfiniteScroll from "@/components/InfiniteScroll";
import ResumeInfoModal from "@/pages/CompanyJobsPage/components/ResumeInfoModal";
import FILEICON from "@/assets/images/CompanyJobsPage/file_icon.png";
import { getResumesList } from "@/services";
import "./index.css";
const InputSearch = Input.Search;
const { userResumes } = mockData;
const PAGE_SIZE = 10;
export default ({ visible, onClose, data }) => {
const studentInfo = useSelector((state) => state.student.studentInfo);
const [resumeModalShow, setResumeModalShow] = useState(false);
const [resumeInfoModalShow, setResumeInfoModalShow] = useState(false);
const [resumeList, setResumeList] = useState([]); // 简历列表
const [listPage, setListPage] = useState(1);
const [listHasMore, setListHasMore] = useState(true);
const handleCloseModal = () => {
setResumeModalShow(false);
onClose();
};
const queryResumeList = async () => {
const res = await getResumesList({
page: listPage,
pageSize: PAGE_SIZE,
studentId: studentInfo?.id,
});
if (res.success) {
setResumeList((prevList) => {
const newList = [...prevList, ...res.data];
if (res.total === newList?.length) {
setListHasMore(false);
} else {
setListPage((prevPage) => prevPage + 1);
}
return newList;
});
}
};
// 点击立即投递
const handleClickDeliverBtn = (e) => {
e.stopPropagation();
@@ -52,36 +77,46 @@ export default ({ visible, onClose, data }) => {
searchButton
placeholder="搜索简历"
/>
<ul className="job-info-modal-user-resumes-list">
{userResumes.map((item) => (
<li
key={item.id}
className="list-item"
onClick={() => userResumesClick(item)}
>
<div className="list-item-info">
<img src={FILEICON} className="file-icon" />
<div className="file-info">
<p className="file-info-targetPosition">
{item.targetPosition}
</p>
{item?.skills?.length > 0 && (
<p className="file-info-skills">
{item?.skills?.join("/")}
</p>
)}
</div>
</div>
<div
className="info-btn"
onClick={(e) => userResumesBtnClick(e, item)}
{
<InfiniteScroll
loadMore={queryResumeList}
hasMore={listHasMore}
empty={resumeList.length === 0}
className={`${
resumeList.length
? "job-info-modal-user-resumes-list"
: "empty-data-wrapper"
}`}
>
{resumeList.map((item) => (
<li
key={item.id}
className="list-item"
onClick={() => userResumesClick(item)}
>
简历详情
</div>
</li>
))}
</ul>
<div className="list-item-info">
<img src={FILEICON} className="file-icon" />
<div className="file-info">
<p className="file-info-targetPosition">
{item.title}
</p>
{item?.skills?.length > 0 && (
<p className="file-info-skills">
{item?.skills?.join("/")}
</p>
)}
</div>
</div>
<div
className="info-btn"
onClick={(e) => userResumesBtnClick(e, item)}
>
简历详情
</div>
</li>
))}
</InfiniteScroll>
}
</>
) : (
<>
@@ -90,7 +125,7 @@ export default ({ visible, onClose, data }) => {
{data?.position}
</span>
<span className="job-info-modal-content-position-info-num">
该岗位仅剩9
该岗位仅剩{data?.remainingPositions}
</span>
<span className="job-info-modal-content-position-info-salary">
{data?.salary}
@@ -105,15 +140,19 @@ export default ({ visible, onClose, data }) => {
))}
</ul>
)}
{data?.details?.description && (
{data?.description && (
<div className="job-info-modal-content-position-info-description">
<p className="description-title">岗位描述</p>
<p className="description-content">
{data?.details?.description}
</p>
<p className="description-content">{data?.description}</p>
</div>
)}
{data?.details?.requirements?.length > 0 && (
{data?.requirements && (
<div className="job-info-modal-content-position-info-description">
<p className="description-title">岗位要求</p>
<p className="description-content">{data?.requirements}</p>
</div>
)}
{/* {data?.details?.requirements?.length > 0 && (
<div className="job-info-modal-content-position-info-requirements">
<p className="requirements-title">岗位要求</p>
<ul className="requirements-content">
@@ -124,12 +163,12 @@ export default ({ visible, onClose, data }) => {
))}
</ul>
</div>
)}
{data?.details?.companyInfo && (
)} */}
{data?.company?.industry && (
<div className="job-info-modal-content-position-info-companyInfo">
<p className="companyInfo-title">公司介绍</p>
<p className="companyInfo-content">
{data?.details?.companyInfo}
{data?.company?.industry}
</p>
</div>
)}

View File

@@ -1,17 +1,23 @@
import { useState } from "react";
import { useNavigate } from "react-router-dom";
import toast from "@/components/Toast";
import JobInfoModal from "../JobInfoModal";
import { getJobsDetail } from "@/services";
import { mapJob } from "@/utils/dataMapper";
import "./index.css";
export default ({ className = "", data = [], backgroundColor = "#FFFFFF" }) => {
const navigate = useNavigate();
const [jobInfoData, setJobInfoData] = useState(undefined);
const [jobInfoModalVisible, setJobInfoModalVisible] = useState(false);
const handleJobClick = (e, item) => {
const handleJobClick = async (e, item) => {
e.stopPropagation();
setJobInfoModalVisible(true);
setJobInfoData(item);
const res = await getJobsDetail(item.id);
if (res.success) {
setJobInfoData(mapJob(res.data));
setJobInfoModalVisible(true);
} else {
toast.error(res.message);
}
};
const onClickJobInfoModalClose = () => {

View File

@@ -1,6 +1,5 @@
.company-jobs-page-wrapper {
width: 100%;
height: 100%;
box-sizing: border-box;
padding: 20px;
position: relative;

View File

@@ -2,9 +2,10 @@ import { useState } from "react";
import { useSelector } from "react-redux";
import { useNavigate } from "react-router-dom";
import { mapJobList, mapInterviewList } from "@/utils/dataMapper";
import InfiniteScroll from "@/components/InfiniteScroll";
import toast from "@/components/Toast";
import JobList from "./components/JobList";
import { getJobsList, getInterviewsList } from "@/services";
import InfiniteScroll from "@/components/InfiniteScroll";
import "./index.css";
const PAGE_SIZE = 10;
@@ -22,30 +23,28 @@ const CompanyJobsPage = () => {
// 获取面试信息
const fetchInterviewsData = async () => {
try {
if (studentInfo?.id) {
const res = await getInterviewsList({
page: interviewsPage,
pageSize: PAGE_SIZE,
studentId: studentInfo?.id,
status: "SCHEDULED",
if (studentInfo?.id) {
const res = await getInterviewsList({
page: interviewsPage,
pageSize: PAGE_SIZE,
studentId: studentInfo?.id,
status: "SCHEDULED",
});
if (res.success) {
const mappedInterviews = mapInterviewList(res.data || []);
setInterviews((prevList) => {
const newList = [...prevList, ...mappedInterviews];
if (res.total === newList?.length) {
setInterviewsHasMore(false);
} else {
setInterviewsPage((prevPage) => prevPage + 1);
}
return newList;
});
if (res.success) {
const mappedInterviews = mapInterviewList(res.data || []);
setInterviews((prevList) => {
const newList = [...prevList, ...mappedInterviews];
if (res.total === newList?.length) {
setInterviewsHasMore(false);
} else {
setInterviewsPage((prevPage) => prevPage + 1);
}
return newList;
});
}
} else {
setInterviews([]);
toast.error(res.message);
}
} catch (error) {
console.error("Failed to fetch data:", error);
setInterviews([]);
}
};

View File

@@ -1,14 +1,23 @@
import "./index.css";
import { useNavigate } from "react-router-dom";
import icon1 from "@/assets/images/Dashboard/QuickAccess/icon1.png";
import icon2 from "@/assets/images/Dashboard/QuickAccess/icon2.png";
import icon3 from "@/assets/images/Dashboard/QuickAccess/icon3.png";
import "./index.css";
const QuickAccess = () => {
const navigate = useNavigate();
const handleClick = (path) => {
navigate(path);
};
return (
<div className="module-quick-access-wrapper">
<p className="module-quick-access-title">快捷入口</p>
<ul className="module-quick-access-list">
<li className="module-quick-access-item">
<li
className="module-quick-access-item"
onClick={() => handleClick("/expert-support")}
>
<img
src={icon1}
alt="icon1"
@@ -16,7 +25,10 @@ const QuickAccess = () => {
/>
<p className="module-quick-access-item-text">专家支持中心</p>
</li>
<li className="module-quick-access-item">
<li
className="module-quick-access-item"
onClick={() => handleClick("/live")}
>
<img
src={icon2}
alt="icon1"
@@ -24,7 +36,10 @@ const QuickAccess = () => {
/>
<p className="module-quick-access-item-text">课程直播间</p>
</li>
<li className="module-quick-access-item">
<li
className="module-quick-access-item"
onClick={() => handleClick("/career-tree")}
>
<img
src={icon3}
alt="icon1"

View File

@@ -6,7 +6,7 @@ import StudyStatus from "./components/StudyStatus";
import Rank from "@/components/Rank";
import StageProgress from "@/components/StageProgress";
import TaskList from "./components/TaskList";
import { getClassRanking } from "@/services/dashboard";
import { getClassRanking, getStudyRecordsProgress } from "@/services";
import "./index.css";
const Dashboard = () => {
@@ -15,13 +15,18 @@ const Dashboard = () => {
useEffect(() => {
fetchRankingData();
fetchLearningProgressSummary();
}, []);
// 获取班级排名数据
const fetchRankingData = async () => {
try {
setRankingLoading(true);
const response = await getClassRanking();
if (response) {
if (response && response.success) {
setRankingData(response.data);
} else if (response) {
// 兼容直接返回数据的情况
setRankingData(response);
}
} catch (error) {
@@ -31,6 +36,16 @@ const Dashboard = () => {
}
};
// 获取整体学习进度
const fetchLearningProgressSummary = async () => {
try {
const res = await getStudyRecordsProgress();
console.log("learningProgressSummary", res);
} catch (error) {
console.error('Failed to fetch learning progress:', error);
}
};
return (
<div className="dashboard">
<StageProgress showBlockageAlert={true} />

View File

@@ -27,7 +27,7 @@ const ProfileCard = () => {
<li className="profile-card-achievement-info-item">
<span className="profile-card-achievement-info-item-title">学分</span>
<span className="profile-card-achievement-info-item-text">
{studentInfo?.totalCredits || 0}
{studentInfo?.myRank?.score || "-"}
</span>
</li>
<li className="profile-card-achievement-info-item">
@@ -35,7 +35,7 @@ const ProfileCard = () => {
班级排名
</span>
<span className="profile-card-achievement-info-item-text">
{studentInfo?.badges?.classRank || "-"}
{studentInfo?.myRank?.rank || "-"}
</span>
</li>
<li className="profile-card-achievement-info-item">

View File

@@ -1,11 +1,11 @@
import * as echarts from "echarts";
import { useState, useEffect } from "react";
import { useSelector } from "react-redux";
import StudyProgress from "../StudyProgress";
import ScoreRingChart from "../ScoreRingChart";
import AttendanceRingChart from "../AttendanceRingChart";
import { getLoginStudentProgress } from "@/services";
import { getDashboardStatistics } from "@/services";
import "./index.css";
import { useEffect } from "react";
const ringData = [
{
@@ -54,17 +54,14 @@ const attendanceData = [
const StudyStudes = () => {
const studentInfo = useSelector((state) => state.student.studentInfo);
const [progressData, setProgressData] = useState({});
const queryLoginStudentProgress = async () => {
const res = await getLoginStudentProgress();
if (res.success) {
setProgressData(res.data);
}
// 获取仪表盘数据
const queryDashboardStatistics = async () => {
const res = await getDashboardStatistics();
console.log(res);
};
useEffect(() => {
queryLoginStudentProgress();
queryDashboardStatistics();
}, []);
return (

View File

@@ -1,10 +1,40 @@
import { useState, useEffect } from "react";
import { useDispatch } from "react-redux";
import ProfileCard from "./components/ProfileCard";
import Rank from "@/components/Rank";
import StageProgress from "@/components/StageProgress";
import StudyStudes from "./components/StudyStudes";
import { updateStudentInfo } from "@/store/slices/studentSlice";
import { getClassRanking, getStudyRecordsProgress } from "@/services";
import "./index.css";
const PersonalProfile = () => {
const dispatch = useDispatch();
const [rankData, setRankData] = useState([]); // 班级排名数据
const queryLearningProgressSummary = async () => {
const res = await getStudyRecordsProgress();
console.log("learningProgressSummary", res);
};
// 获取班级排名
const queryRankData = async () => {
const res = await getClassRanking();
if (res.success) {
const studentInfo = {
myRank: res.data.myRank,
classInfo: res.data.classInfo,
};
setRankData(res.data);
dispatch(updateStudentInfo(studentInfo));
}
};
useEffect(() => {
queryRankData();
queryLearningProgressSummary();
}, []);
return (
<div className="personal-profile">
<StageProgress />
@@ -13,7 +43,7 @@ const PersonalProfile = () => {
<div className="unified-profile-layout">
<div className="unified-profile-left">
<ProfileCard />
<Rank className="unified-profile-rank" />
<Rank className="unified-profile-rank" data={rankData} />
</div>
<div className="unified-profile-right">
<StudyStudes />