diff --git a/src/pages/CompanyJobsPage/index.jsx b/src/pages/CompanyJobsPage/index.jsx
index e66f380..6745420 100644
--- a/src/pages/CompanyJobsPage/index.jsx
+++ b/src/pages/CompanyJobsPage/index.jsx
@@ -1,11 +1,11 @@
-import { useState } from "react";
+import { useState, useEffect } 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 { getCompanyJobsPageData, getJobsList, getInterviewsList } from "@/services";
import "./index.css";
const PAGE_SIZE = 10;
@@ -19,10 +19,57 @@ const CompanyJobsPage = () => {
const [interviews, setInterviews] = useState([]);
const [interviewsPage, setInterviewsPage] = useState(1);
const [interviewsHasMore, setInterviewsHasMore] = useState(true);
+ const [initialDataLoaded, setInitialDataLoaded] = useState(false);
const navigate = useNavigate();
- // 获取面试信息
+ // 初始化页面数据 - 使用聚合接口
+ useEffect(() => {
+ const fetchInitialData = async () => {
+ try {
+ const res = await getCompanyJobsPageData({
+ studentId: studentInfo?.id,
+ });
+
+ if (res?.success) {
+ // 设置岗位数据
+ if (res.data?.jobs) {
+ const mappedJobs = mapJobList(res.data.jobs.list || []);
+ setJobs(mappedJobs);
+ setJoblistHasMore(res.data.jobs.hasMore);
+ if (mappedJobs.length > 0) {
+ setJobsListPage(2); // 下次从第2页开始
+ }
+ }
+
+ // 设置面试数据
+ if (res.data?.interviews && studentInfo?.id) {
+ const mappedInterviews = mapInterviewList(res.data.interviews.list || []);
+ setInterviews(mappedInterviews);
+ setInterviewsHasMore(res.data.interviews.hasMore);
+ if (mappedInterviews.length > 0) {
+ setInterviewsPage(2); // 下次从第2页开始
+ }
+ }
+
+ setInitialDataLoaded(true);
+ }
+ } catch (error) {
+ console.error('Failed to fetch initial page data:', error);
+ // 如果聚合接口失败,回退到原来的方式
+ setInitialDataLoaded(true);
+ }
+ };
+
+ fetchInitialData();
+ }, [studentInfo?.id]);
+
+ // 获取面试信息 - 用于分页加载更多
const fetchInterviewsData = async () => {
+ // 如果初始数据还没加载完成,或者是第一页且已有初始数据,则跳过
+ if (!initialDataLoaded || (interviewsPage === 1 && interviews.length > 0)) {
+ return;
+ }
+
if (studentInfo?.id) {
const res = await getInterviewsList({
page: interviewsPage,
@@ -42,14 +89,21 @@ const CompanyJobsPage = () => {
return newList;
});
} else {
- setInterviews([]);
+ if (interviewsPage === 1) {
+ setInterviews([]);
+ }
toast.error(res.message);
}
}
};
- // 获取企业内推岗位
+ // 获取企业内推岗位 - 用于分页加载更多
const fetchJobsList = async () => {
+ // 如果初始数据还没加载完成,或者是第一页且已有初始数据,则跳过
+ if (!initialDataLoaded || (jobsListPage === 1 && jobs.length > 0)) {
+ return;
+ }
+
try {
const res = await getJobsList({
page: jobsListPage,
@@ -71,7 +125,9 @@ const CompanyJobsPage = () => {
}
} catch (error) {
console.error("Failed to fetch data:", error);
- setJobs([]);
+ if (jobsListPage === 1) {
+ setJobs([]);
+ }
}
};
diff --git a/src/pages/Dashboard/components/CalendarTaskModule/index.css b/src/pages/Dashboard/components/CalendarTaskModule/index.css
index 55b1735..bb57347 100644
--- a/src/pages/Dashboard/components/CalendarTaskModule/index.css
+++ b/src/pages/Dashboard/components/CalendarTaskModule/index.css
@@ -33,5 +33,48 @@
}
.arco-calendar-cell {
height: 40px !important;
+ position: relative;
+ cursor: pointer;
+ }
+
+ .calendar-date-cell {
+ position: relative;
+ width: 100%;
+ height: 100%;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ cursor: pointer;
+
+ &.is-today {
+ .date-number {
+ background-color: rgb(0, 119, 255);
+ color: white;
+ width: 28px;
+ height: 28px;
+ border-radius: 50%;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ font-weight: 500;
+ }
+ }
+ }
+
+ .date-number {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ transition: all 0.2s;
+ }
+
+ .task-dot {
+ position: absolute;
+ bottom: 4px;
+ width: 4px;
+ height: 4px;
+ border-radius: 50%;
+ background-color: rgb(0, 119, 255);
}
}
diff --git a/src/pages/Dashboard/components/CalendarTaskModule/index.jsx b/src/pages/Dashboard/components/CalendarTaskModule/index.jsx
index 7c0e48f..4b047af 100644
--- a/src/pages/Dashboard/components/CalendarTaskModule/index.jsx
+++ b/src/pages/Dashboard/components/CalendarTaskModule/index.jsx
@@ -1,25 +1,68 @@
import "./index.css";
-import { Calendar } from "@arco-design/web-react";
+import { Calendar, Skeleton } from "@arco-design/web-react";
-// 获取当前日期
-const today = new Date();
-// 提取年、月、日
-const year = today.getFullYear();
-const month = String(today.getMonth() + 1).padStart(2, "0"); // 月份从0开始,需+1并补0
-const day = String(today.getDate()).padStart(2, "0"); // 日期补0
-// 格式化日期字符串
-const formattedDate = `${year}-${month}-${day}`;
+const CalendarTaskModule = ({ tasks = [], selectedDate, onDateChange, loading }) => {
+ if (loading) {
+ return (
+
+ );
+ }
+
+ // 格式化今天的日期
+ const today = new Date();
+ const formattedToday = `${today.getFullYear()}-${String(today.getMonth() + 1).padStart(2, "0")}-${String(today.getDate()).padStart(2, "0")}`;
+
+ // 获取有任务的日期集合
+ const datesWithTasks = new Set(tasks?.map(task => task.date) || []);
+
+ // 日历单元格渲染函数
+ const dateRender = (current) => {
+ const dateStr = current.format('YYYY-MM-DD');
+ const hasTasks = datesWithTasks.has(dateStr);
+ const isToday = dateStr === formattedToday;
+
+ return (
+
+
{current.date()}
+ {hasTasks &&
}
+
+ );
+ };
+
+ const handleDateChange = (date, dateString) => {
+ if (onDateChange) {
+ // Arco Calendar passes a dayjs object
+ if (date && date.format) {
+ // Convert dayjs to Date object
+ const dateStr = date.format('YYYY-MM-DD');
+ const dateObj = new Date(dateStr + 'T00:00:00');
+ if (!isNaN(dateObj.getTime())) {
+ onDateChange(dateObj);
+ }
+ } else if (dateString) {
+ // Fallback to dateString if available
+ const dateObj = new Date(dateString + 'T00:00:00');
+ if (!isNaN(dateObj.getTime())) {
+ onDateChange(dateObj);
+ }
+ }
+ }
+ };
-const CalendarTaskModule = () => {
return (
日历
console.log(a)}
+ onChange={handleDateChange}
+ dateRender={dateRender}
/>
);
diff --git a/src/pages/Dashboard/components/StartClass/index.jsx b/src/pages/Dashboard/components/StartClass/index.jsx
index 245605e..6823073 100644
--- a/src/pages/Dashboard/components/StartClass/index.jsx
+++ b/src/pages/Dashboard/components/StartClass/index.jsx
@@ -1,11 +1,35 @@
+import { Skeleton } from "@arco-design/web-react";
import "./index.css";
-const StartClass = () => {
- // 获取当前时间用于筛选
- const now = new Date();
- const today = new Date(now.getFullYear(), now.getMonth(), now.getDate());
- const yesterday = new Date(today.getTime() - 24 * 60 * 60 * 1000);
- const tomorrow = new Date(today.getTime() + 24 * 60 * 60 * 1000);
+const StartClass = ({ courses, tasks, loading }) => {
+ if (loading) {
+ return (
+
+ );
+ }
+
+ // 获取待办任务
+ const pendingTasks = tasks?.allTasks?.filter(task =>
+ task.status === 'PENDING' || task.status === 'IN_PROGRESS'
+ ).slice(0, 5) || [];
+
+ // 格式化日期显示
+ const formatCourseDate = (dateStr) => {
+ const date = new Date(dateStr);
+ const today = new Date();
+ const yesterday = new Date(today.getTime() - 24 * 60 * 60 * 1000);
+
+ if (date.toDateString() === today.toDateString()) {
+ return '今日已上';
+ } else if (date.toDateString() === yesterday.toDateString()) {
+ return '昨日已上';
+ } else {
+ return `${date.getMonth() + 1}/${date.getDate()}已上`;
+ }
+ };
return (
@@ -14,32 +38,53 @@ const StartClass = () => {
最近课程
- -
- 昨日已上《xxxxx计算机课》
-
- -
- 今日已上《xxxxxx高等数学课》
-
- -
- 今日计划《xxxxxx中国现代史》
-
+ {courses?.recentCourses?.length > 0 ? (
+ courses.recentCourses.slice(0, 3).map((course, index) => (
+ -
+ {formatCourseDate(course.date)}《{course.courseName}》
+
+ ))
+ ) : (
+ - 暂无最近课程记录
+ )}
+ {courses?.todaysCourses?.length > 0 && (
+ courses.todaysCourses.slice(0, 2).map((course, index) => (
+ -
+ 今日计划《{course.courseName}》
+
+ ))
+ )}
下次上课
- - 《xxxxxx中国现代史》
+ {courses?.nextCourse ? (
+ -
+ 《{courses.nextCourse.courseName}》
+ {courses.nextCourse.classroom && (
+ - {courses.nextCourse.classroom}
+ )}
+
+ ) : (
+ - 暂无安排课程
+ )}
- 待办事项(2)
+
+ 待办事项({pendingTasks.length})
+
- -
- 《计算机课》 Excel 数据处理
-
- -
- 《高等数学课》线性代数的矩阵运算题
-
+ {pendingTasks.length > 0 ? (
+ pendingTasks.map((task) => (
+ -
+ 《{task.courseName}》{task.title}
+
+ ))
+ ) : (
+ - 暂无待办事项
+ )}
diff --git a/src/pages/Dashboard/components/StudyStatus/index.jsx b/src/pages/Dashboard/components/StudyStatus/index.jsx
index 27048b7..6a94ca7 100644
--- a/src/pages/Dashboard/components/StudyStatus/index.jsx
+++ b/src/pages/Dashboard/components/StudyStatus/index.jsx
@@ -1,13 +1,25 @@
import EchartsProgress from "../EchartsProgress";
+import { Skeleton } from "@arco-design/web-react";
import "./index.css";
-const StudyStatus = () => {
+const StudyStatus = ({ progress = 0, loading }) => {
+ if (loading) {
+ return (
+
+ );
+ }
+
return (
学习情况
-
- 整体课程完成进度
+
+
+ 整体课程完成进度 ({progress || 0}%)
+
);
diff --git a/src/pages/Dashboard/components/TaskList/index.css b/src/pages/Dashboard/components/TaskList/index.css
index a72df60..e2b0f0a 100644
--- a/src/pages/Dashboard/components/TaskList/index.css
+++ b/src/pages/Dashboard/components/TaskList/index.css
@@ -18,6 +18,55 @@
width: 100%;
font-size: 20px;
font-weight: 500;
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+
+ .task-count {
+ font-size: 14px;
+ color: #86909c;
+ font-weight: normal;
+ }
+ }
+
+ .no-tasks {
+ flex: 1;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ }
+
+
+ .task-type {
+ font-weight: bold;
+ color: #1d2129;
+ }
+
+ .task-status {
+ font-size: 10px;
+ padding: 2px 6px;
+ border-radius: 4px;
+ margin-left: 8px;
+
+ &.status-pending {
+ background-color: #fff3cd;
+ color: #856404;
+ }
+
+ &.status-in_progress {
+ background-color: #d1ecf1;
+ color: #0c5460;
+ }
+
+ &.status-completed {
+ background-color: #d4edda;
+ color: #155724;
+ }
+ }
+
+ .classroom {
+ color: #86909c;
+ font-size: 12px;
line-height: 30px;
color: #262626;
}
diff --git a/src/pages/Dashboard/components/TaskList/index.jsx b/src/pages/Dashboard/components/TaskList/index.jsx
index 402fb45..a70bea1 100644
--- a/src/pages/Dashboard/components/TaskList/index.jsx
+++ b/src/pages/Dashboard/components/TaskList/index.jsx
@@ -1,45 +1,103 @@
-import { mockData } from "@/data/mockData";
-import { Avatar } from "@arco-design/web-react";
+import { Avatar, Skeleton, Empty } from "@arco-design/web-react";
import "./index.css";
-const TaskList = () => {
- const { tasks } = mockData;
+const TaskList = ({ tasks = [], selectedDate, loading }) => {
+ if (loading) {
+ return (
+
+ );
+ }
+
+ const formatDate = (date) => {
+ return date.toLocaleDateString('zh-CN', {
+ year: 'numeric',
+ month: 'long',
+ day: 'numeric'
+ });
+ };
+
+ const getTaskTypeText = (type) => {
+ const typeMap = {
+ 'HOMEWORK': '作业',
+ 'PROJECT': '项目',
+ 'REPORT': '报告',
+ 'INTERVIEW': '面试',
+ 'OTHER': '其他'
+ };
+ return typeMap[type] || type;
+ };
+
+ const getPriorityClass = (priority) => {
+ const classMap = {
+ 'URGENT': 'urgent',
+ 'HIGH': 'high',
+ 'MEDIUM': 'medium',
+ 'LOW': 'low'
+ };
+ return classMap[priority] || 'medium';
+ };
return (
@@ -96,7 +137,7 @@ const StudyStudes = () => {
{/* 进度 */}
@@ -107,32 +148,32 @@ const StudyStudes = () => {
班级平均进度
-
60%
+
{classAverageCourseProgress}%
我的进度
-
60%
+
{personalCourseProgress}%
{/* 课后作业完成情况 */}
班级平均进度
-
60%
+
{classAverageHomeworkProgress}%
我的进度
-
60%
+
{personalHomeworkProgress}%
{/* 考勤情况 */}
@@ -148,11 +189,11 @@ const StudyStudes = () => {
出勤率
-
60%
+
{attendanceRate}%
缺勤率
-
60%
+
{absenceRate}%
diff --git a/src/pages/PersonalProfile/index.jsx b/src/pages/PersonalProfile/index.jsx
index 3912a77..21e3ce9 100644
--- a/src/pages/PersonalProfile/index.jsx
+++ b/src/pages/PersonalProfile/index.jsx
@@ -5,34 +5,45 @@ 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 { getProfileOverview } from "@/services";
import "./index.css";
const PersonalProfile = () => {
const dispatch = useDispatch();
- const [rankData, setRankData] = useState([]); // 班级排名数据
+ const [profileData, setProfileData] = useState(null); // 个人档案完整数据
+ const [loading, setLoading] = useState(true);
- 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));
+ // 获取个人档案完整数据
+ const queryProfileOverview = async () => {
+ try {
+ setLoading(true);
+ console.log("Fetching profile overview...");
+ const res = await getProfileOverview();
+ console.log("Profile overview response:", res);
+
+ if (res.success) {
+ const data = res.data;
+
+ // 更新Redux中的学生信息
+ const studentInfo = {
+ ...data.studentInfo,
+ myRank: data.ranking.myRank,
+ classInfo: data.ranking.classInfo,
+ };
+
+ setProfileData(data);
+ dispatch(updateStudentInfo(studentInfo));
+ console.log("Profile data set:", data);
+ }
+ } catch (error) {
+ console.error("Failed to fetch profile overview:", error);
+ } finally {
+ setLoading(false);
}
};
useEffect(() => {
- queryRankData();
- queryLearningProgressSummary();
+ queryProfileOverview();
}, []);
return (
@@ -43,10 +54,22 @@ const PersonalProfile = () => {
diff --git a/src/pages/ResumeInterviewPage/index.jsx b/src/pages/ResumeInterviewPage/index.jsx
index 16dd006..a62906b 100644
--- a/src/pages/ResumeInterviewPage/index.jsx
+++ b/src/pages/ResumeInterviewPage/index.jsx
@@ -1,15 +1,15 @@
import { useRef, useState, useEffect } from "react";
import InterviewQuestionsModal from "./components/InterviewQuestionsModal";
-import { mockData } from "@/data/mockData";
+import { getPageData } from "@/services/resumeInterview";
import "./index.css";
-const { resumeInterview } = mockData;
-
const ResumeInterviewPage = () => {
const [activeIndustry, setActiveIndustry] = useState("frontend");
const [resumeModalVisible, setResumeModalVisible] = useState(false);
const [modalData, setModalData] = useState(undefined);
+ const [pageData, setPageData] = useState(null);
+ const [loading, setLoading] = useState(true);
const sectionsRef = useRef({});
// 导航到指定行业段落
@@ -32,12 +32,37 @@ const ResumeInterviewPage = () => {
setModalData(undefined);
};
+ // 获取页面数据
+ useEffect(() => {
+ const fetchPageData = async () => {
+ try {
+ setLoading(true);
+ const response = await getPageData();
+ if (response.success) {
+ setPageData(response.data);
+ // 设置默认选中第一个行业
+ if (response.data.industries && response.data.industries.length > 0) {
+ setActiveIndustry(response.data.industries[0].id);
+ }
+ }
+ } catch (error) {
+ console.error("Failed to fetch page data:", error);
+ } finally {
+ setLoading(false);
+ }
+ };
+
+ fetchPageData();
+ }, []);
+
// 监听滚动位置更新导航状态
useEffect(() => {
+ if (!pageData?.industries) return;
+
const handleScroll = () => {
const scrollPosition = window.scrollY + 200;
- resumeInterview.industries.forEach((industry) => {
+ pageData.industries.forEach((industry) => {
const section = sectionsRef.current[industry.id];
if (section) {
const sectionTop = section.offsetTop;
@@ -52,12 +77,28 @@ const ResumeInterviewPage = () => {
window.addEventListener("scroll", handleScroll);
return () => window.removeEventListener("scroll", handleScroll);
- }, [resumeInterview.industries]);
+ }, [pageData?.industries]);
+
+ if (loading) {
+ return (
+
+ );
+ }
+
+ if (!pageData) {
+ return (
+
+ );
+ }
return (
- {resumeInterview.industries.map((industry) => (
+ {pageData.industries.map((industry) => (
- {
))}
- {resumeInterview?.industries?.map((item) => (
+ {pageData.industries.map((item) => (
- {
简历与面试题
- {item.positions.map((i) => (
- -
- {i.level}
+ {item.positions.map((position) => (
+
-
+ {position.level}
-
{i.name}
+
{position.name}
详情 >
))}
- - handleHookQuestionClick(item)}
- >
-
面试题1122345
-
+ {item.questions.map((question) => (
+ - handleHookQuestionClick({ ...item, question })}
+ >
+
{question.question}
+
+ ))}
@@ -105,6 +149,7 @@ const ResumeInterviewPage = () => {
);
diff --git a/src/services/companyJobs.js b/src/services/companyJobs.js
index 3ea965e..064cf39 100644
--- a/src/services/companyJobs.js
+++ b/src/services/companyJobs.js
@@ -1,5 +1,14 @@
import request from "@/utils/request";
+// 获取企业内推岗位页面聚合数据
+export async function getCompanyJobsPageData(params) {
+ return request({
+ url: `/api/company-jobs/page-data`,
+ method: "GET",
+ params,
+ });
+}
+
// 获取企业内推岗位
export async function getJobsList(params) {
return request({
diff --git a/src/services/index.js b/src/services/index.js
index 77a7098..be382dc 100644
--- a/src/services/index.js
+++ b/src/services/index.js
@@ -6,6 +6,7 @@ import {
} from "./dashboard";
import { getProjectsList } from "./projectLibrary";
import {
+ getCompanyJobsPageData,
getJobsList,
getJobsDetail,
getInterviewsList,
@@ -18,6 +19,7 @@ import {
getLoginStudentProgress,
getClassRank,
getMyRanking,
+ getProfileOverview,
} from "./personalProfile";
import {} from "./resumeInterview";
@@ -35,11 +37,13 @@ export {
getLoginStudentInfo, // 获取当前登录学生基本信息
getLoginStudentProgress, // 获取当前学生学习进度
getClassRank, // 获取班级排名(别名)
+ getProfileOverview, // 获取个人档案完整数据(新接口)
// 项目和作品相关
getProjectsList, // 获取项目列表
// 求职相关
+ getCompanyJobsPageData, // 获取企业内推岗位页面聚合数据
getJobsList, // 获取岗位列表
getJobsDetail, // 岗位详情
getInterviewsList, // 获取面试列表
diff --git a/src/services/personalProfile.js b/src/services/personalProfile.js
index 69f438b..cc02b17 100644
--- a/src/services/personalProfile.js
+++ b/src/services/personalProfile.js
@@ -36,3 +36,12 @@ export async function getMyRanking() {
namespace: "profileLoading",
});
}
+
+// 获取个人档案完整数据 (新接口)
+export async function getProfileOverview() {
+ return request({
+ url: `/api/profile/overview`,
+ method: "GET",
+ namespace: "profileLoading",
+ });
+}
diff --git a/src/services/resumeInterview.js b/src/services/resumeInterview.js
index 075344e..f5d2ead 100644
--- a/src/services/resumeInterview.js
+++ b/src/services/resumeInterview.js
@@ -1 +1,33 @@
import request from "@/utils/request";
+
+// Get all page data for resume-interview page
+export const getPageData = () => {
+ return request({
+ url: "/api/resume-interview",
+ method: "GET"
+ });
+};
+
+// Export individual functions for future use if needed
+export const getResumes = (params) => {
+ return request({
+ url: "/api/resumes",
+ method: "GET",
+ params
+ });
+};
+
+export const getInterviews = (params) => {
+ return request({
+ url: "/api/interviews",
+ method: "GET",
+ params
+ });
+};
+
+export const getMyInterviews = () => {
+ return request({
+ url: "/api/interviews/my-interviews",
+ method: "GET"
+ });
+};