feat: 添加AI课程集成和修复初始化错误
- 将终生学习系统课添加到公共课直播间 - 修复allCalendarEvents初始化顺序问题 - 更正AI课程导师为李奇 - 添加AI课程与日历页面同步功能
This commit is contained in:
@@ -19,9 +19,10 @@ const Rank = ({ className, data, loading }) => {
|
||||
|
||||
return (
|
||||
<div className={`module-class-rank ${className}`}>
|
||||
<p className="module-class-rank-title">
|
||||
<p className="module-class-rank-title" style={{ display: 'flex', alignItems: 'center', justifyContent: 'flex-start' }}>
|
||||
<img src="https://ddcz-1315997005.cos.ap-nanjing.myqcloud.com/static/img/teach_sys_icon/recuW0XRVB1bpV.png" alt="icon" style={{ width: '24px', height: '24px', marginRight: '8px', flexShrink: 0 }} />
|
||||
<IconFont className="title-icon" src="recuUY5nNf7DWT" />
|
||||
<span>班级排名</span>
|
||||
<span style={{ fontWeight: 'bold' }}>班级排名</span>
|
||||
</p>
|
||||
{loading ? (
|
||||
<Spin size={40} className="module-class-rank-spin" />
|
||||
|
||||
@@ -40,6 +40,36 @@
|
||||
.course-list {
|
||||
width: 100%;
|
||||
|
||||
/* 分割线样式 */
|
||||
.course-divider {
|
||||
width: 100%;
|
||||
height: 40px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin: 20px 0;
|
||||
position: relative;
|
||||
|
||||
.divider-line {
|
||||
flex: 1;
|
||||
height: 1px;
|
||||
background: linear-gradient(90deg, transparent, #e5e6eb 20%, #e5e6eb 80%, transparent);
|
||||
border-style: dashed;
|
||||
border-width: 1px 0 0 0;
|
||||
border-color: #e5e6eb;
|
||||
}
|
||||
|
||||
.divider-text {
|
||||
padding: 0 16px;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
color: #86909c;
|
||||
background-color: #fff;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* 自定义折叠面板元素 */
|
||||
.course-list-item {
|
||||
width: 272px;
|
||||
@@ -51,6 +81,14 @@
|
||||
font-weight: 400;
|
||||
line-height: 21px;
|
||||
border: none;
|
||||
|
||||
&.has-preview-unit {
|
||||
.arco-collapse-item-header {
|
||||
background: linear-gradient(135deg, #f0f7ff 0%, #e8f3ff 100%);
|
||||
border: 1px solid #b3d4ff;
|
||||
box-shadow: 0 2px 8px rgba(64, 128, 255, 0.1);
|
||||
}
|
||||
}
|
||||
|
||||
.arco-collapse-item-header {
|
||||
border-radius: 8px;
|
||||
@@ -275,6 +313,40 @@
|
||||
border: 1px solid #ff7d00;
|
||||
}
|
||||
}
|
||||
|
||||
.preview-badge {
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
top: 60%;
|
||||
transform: translate(-50%, -50%);
|
||||
padding: 4px 12px;
|
||||
background: #4080ff;
|
||||
color: #fff;
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
border-radius: 12px;
|
||||
z-index: 10;
|
||||
box-shadow: 0 3px 8px rgba(64, 128, 255, 0.3);
|
||||
}
|
||||
|
||||
@keyframes pulse {
|
||||
0% {
|
||||
transform: scale(1);
|
||||
box-shadow: 0 3px 8px rgba(255, 107, 107, 0.3);
|
||||
}
|
||||
50% {
|
||||
transform: scale(1.05);
|
||||
box-shadow: 0 4px 12px rgba(255, 107, 107, 0.5);
|
||||
}
|
||||
100% {
|
||||
transform: scale(1);
|
||||
box-shadow: 0 3px 8px rgba(255, 107, 107, 0.3);
|
||||
}
|
||||
}
|
||||
|
||||
.has-preview {
|
||||
/* 可试看标签已调整到中心位置,不需要额外样式 */
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,8 @@ const TimelineItem = Timeline.Item;
|
||||
const CollapseItem = Collapse.Item;
|
||||
|
||||
const CourseList = ({ className = "", onCourseClick }) => {
|
||||
const [courseLiveList, setCourseLiveList] = useState([]);
|
||||
const [compoundCourseList, setCompoundCourseList] = useState([]);
|
||||
const [verticalCourseList, setVerticalCourseList] = useState([]);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [selectedCourseId, setSelectedCourseId] = useState(null);
|
||||
|
||||
@@ -20,14 +21,19 @@ const CourseList = ({ className = "", onCourseClick }) => {
|
||||
try {
|
||||
const res = await getCourseLiveList();
|
||||
if (res.success) {
|
||||
const courseList = res.data || [];
|
||||
setCourseLiveList(courseList);
|
||||
const compoundList = res.data.compound || [];
|
||||
const verticalList = res.data.vertical || [];
|
||||
setCompoundCourseList(compoundList);
|
||||
setVerticalCourseList(verticalList);
|
||||
|
||||
// 合并两个列表用于查找默认选中的课程
|
||||
const allCourses = [...compoundList, ...verticalList];
|
||||
|
||||
// 设置默认选中今天的课程(如果有)
|
||||
const todayStr = new Date().toISOString().split('T')[0];
|
||||
let foundTodayCourse = false;
|
||||
|
||||
for (const unit of courseList) {
|
||||
for (const unit of allCourses) {
|
||||
const todayCourse = unit.courses.find(c => c.date === todayStr);
|
||||
if (todayCourse) {
|
||||
setSelectedCourseId(todayCourse.courseId);
|
||||
@@ -45,7 +51,7 @@ const CourseList = ({ className = "", onCourseClick }) => {
|
||||
|
||||
// 如果没有今天的课程,选中第一个current或upcoming的课程
|
||||
if (!foundTodayCourse) {
|
||||
for (const unit of courseList) {
|
||||
for (const unit of allCourses) {
|
||||
const activeCourse = unit.courses.find(c => c.current || c.upcoming);
|
||||
if (activeCourse) {
|
||||
setSelectedCourseId(activeCourse.courseId);
|
||||
@@ -123,9 +129,10 @@ const CourseList = ({ className = "", onCourseClick }) => {
|
||||
className="course-list"
|
||||
bordered={false}
|
||||
expandIconPosition="right"
|
||||
defaultActiveKey={["1"]}
|
||||
defaultActiveKey={[]}
|
||||
>
|
||||
{courseLiveList.map((unit, index) => (
|
||||
{/* 复合能力课部分 */}
|
||||
{compoundCourseList.map((unit, index) => (
|
||||
<CollapseItem
|
||||
key={unit.unitId}
|
||||
header={unit.unitName}
|
||||
@@ -167,6 +174,97 @@ const CourseList = ({ className = "", onCourseClick }) => {
|
||||
</Timeline>
|
||||
</CollapseItem>
|
||||
))}
|
||||
|
||||
{/* 分割线 */}
|
||||
{compoundCourseList.length > 0 && verticalCourseList.length > 0 && (
|
||||
<div className="course-divider">
|
||||
<span className="divider-line"></span>
|
||||
<span className="divider-text">垂直能力课</span>
|
||||
<span className="divider-line"></span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* 垂直能力课部分 */}
|
||||
{verticalCourseList.map((unit, index) => {
|
||||
// 检查单元是否包含可试看课程
|
||||
const hasPreviewCourse = unit.courses.some(course => course.canPreview);
|
||||
|
||||
return (
|
||||
<CollapseItem
|
||||
key={unit.unitId}
|
||||
header={
|
||||
<div style={{
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
width: '100%',
|
||||
position: 'relative'
|
||||
}}>
|
||||
{unit.unitName}
|
||||
{hasPreviewCourse && (
|
||||
<span style={{
|
||||
marginLeft: '8px',
|
||||
padding: '2px 8px',
|
||||
background: '#4080ff',
|
||||
color: '#fff',
|
||||
fontSize: '11px',
|
||||
fontWeight: '600',
|
||||
borderRadius: '10px'
|
||||
}}>
|
||||
可试看
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
}
|
||||
name={`vertical-${index + 1}`}
|
||||
className={`course-list-item vertical-course ${hasPreviewCourse ? 'has-preview-unit' : ''}`}
|
||||
>
|
||||
<Timeline>
|
||||
{unit.courses.map((course) => (
|
||||
<TimelineItem
|
||||
key={course.courseId}
|
||||
dot={getDotIcon(course)}
|
||||
lineType="dashed"
|
||||
>
|
||||
<div
|
||||
className={`time-line-item ${getCourseStatus(course)} ${selectedCourseId === course.courseId ? 'selected' : ''} ${course.canPreview ? 'has-preview' : ''}`}
|
||||
onClick={() => {
|
||||
// 先设置选中状态和触发课程点击事件
|
||||
setSelectedCourseId(course.courseId);
|
||||
onCourseClick && onCourseClick({ ...course, unitName: unit.unitName, courseType: 'vertical' });
|
||||
|
||||
// 如果是可试看课程,延迟打开新窗口
|
||||
if (course.canPreview && course.previewUrl) {
|
||||
setTimeout(() => {
|
||||
window.open(course.previewUrl, '_blank');
|
||||
}, 100);
|
||||
}
|
||||
}}
|
||||
style={{ cursor: 'pointer' }}
|
||||
>
|
||||
{course.canPreview && (
|
||||
<span className="preview-badge">可试看</span>
|
||||
)}
|
||||
<p style={{
|
||||
overflow: 'visible',
|
||||
textOverflow: 'unset',
|
||||
whiteSpace: 'normal',
|
||||
wordBreak: 'break-word',
|
||||
WebkitLineClamp: 'unset',
|
||||
WebkitBoxOrient: 'unset',
|
||||
display: 'block',
|
||||
maxWidth: 'calc(100% - 70px)'
|
||||
}}>{course.courseName}</p>
|
||||
<div className="time-line-item-info">
|
||||
<span>{course.teacherName}</span>
|
||||
<span>{course.date}</span>
|
||||
</div>
|
||||
</div>
|
||||
</TimelineItem>
|
||||
))}
|
||||
</Timeline>
|
||||
</CollapseItem>
|
||||
);
|
||||
})}
|
||||
</Collapse>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
236
src/components/CourseList/index.jsx.backup.20250907_191920
Normal file
236
src/components/CourseList/index.jsx.backup.20250907_191920
Normal file
@@ -0,0 +1,236 @@
|
||||
import { useState, useEffect } from "react";
|
||||
import { Collapse, Timeline, Spin } from "@arco-design/web-react";
|
||||
import { getCourseLiveList } from "@/services/courseLive";
|
||||
import "./index.css";
|
||||
|
||||
const TimelineItem = Timeline.Item;
|
||||
const CollapseItem = Collapse.Item;
|
||||
|
||||
const CourseList = ({ className = "", onCourseClick }) => {
|
||||
const [compoundCourseList, setCompoundCourseList] = useState([]);
|
||||
const [verticalCourseList, setVerticalCourseList] = useState([]);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [selectedCourseId, setSelectedCourseId] = useState(null);
|
||||
|
||||
useEffect(() => {
|
||||
fetchCourseList();
|
||||
}, []);
|
||||
|
||||
const fetchCourseList = async () => {
|
||||
setLoading(true);
|
||||
try {
|
||||
const res = await getCourseLiveList();
|
||||
if (res.success) {
|
||||
const compoundList = res.data.compound || [];
|
||||
const verticalList = res.data.vertical || [];
|
||||
setCompoundCourseList(compoundList);
|
||||
setVerticalCourseList(verticalList);
|
||||
|
||||
// 合并两个列表用于查找默认选中的课程
|
||||
const allCourses = [...compoundList, ...verticalList];
|
||||
|
||||
// 设置默认选中今天的课程(如果有)
|
||||
const todayStr = new Date().toISOString().split('T')[0];
|
||||
let foundTodayCourse = false;
|
||||
|
||||
for (const unit of allCourses) {
|
||||
const todayCourse = unit.courses.find(c => c.date === todayStr);
|
||||
if (todayCourse) {
|
||||
setSelectedCourseId(todayCourse.courseId);
|
||||
// 触发课程选择事件
|
||||
if (onCourseClick) {
|
||||
onCourseClick({
|
||||
...todayCourse,
|
||||
unitName: unit.unitName
|
||||
});
|
||||
}
|
||||
foundTodayCourse = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// 如果没有今天的课程,选中第一个current或upcoming的课程
|
||||
if (!foundTodayCourse) {
|
||||
for (const unit of allCourses) {
|
||||
const activeCourse = unit.courses.find(c => c.current || c.upcoming);
|
||||
if (activeCourse) {
|
||||
setSelectedCourseId(activeCourse.courseId);
|
||||
if (onCourseClick) {
|
||||
onCourseClick({
|
||||
...activeCourse,
|
||||
unitName: unit.unitName
|
||||
});
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("获取课程列表失败:", error);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
// 判断课程状态
|
||||
const getCourseStatus = (course) => {
|
||||
if (course.completed) return "finish";
|
||||
if (course.current) return "active";
|
||||
|
||||
// 判断未来课程的具体状态
|
||||
if (course.upcoming) {
|
||||
const courseDate = new Date(course.date);
|
||||
const today = new Date();
|
||||
|
||||
// 重置时间部分只比较日期
|
||||
courseDate.setHours(0, 0, 0, 0);
|
||||
today.setHours(0, 0, 0, 0);
|
||||
|
||||
const timeDiff = courseDate - today;
|
||||
const daysDiff = Math.floor(timeDiff / (24 * 60 * 60 * 1000));
|
||||
|
||||
// 未来7天内的课程显示为"即将开始"
|
||||
if (daysDiff > 0 && daysDiff <= 7) {
|
||||
return "coming";
|
||||
}
|
||||
// 7天后的课程显示为"未开始"
|
||||
return "not-started";
|
||||
}
|
||||
|
||||
// 默认状态
|
||||
return "pending";
|
||||
};
|
||||
|
||||
// 获取图标类型
|
||||
const getDotIcon = (course) => {
|
||||
if (course.completed) return <div className="time-line-dot-icon" />;
|
||||
if (course.current) return <div className="time-line-clock-icon" />;
|
||||
return <div className="time-line-lock-icon" />;
|
||||
};
|
||||
|
||||
if (loading) {
|
||||
return (
|
||||
<div className={`${className} course-list-wrapper`}>
|
||||
<p className="course-list-title">课程列表</p>
|
||||
<div className="course-list-content">
|
||||
<Spin />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={`${className} course-list-wrapper`}>
|
||||
<p className="course-list-title">课程列表</p>
|
||||
<div className="course-list-content">
|
||||
<Collapse
|
||||
lazyload
|
||||
className="course-list"
|
||||
bordered={false}
|
||||
expandIconPosition="right"
|
||||
defaultActiveKey={[]}
|
||||
>
|
||||
{/* 复合能力课部分 */}
|
||||
{compoundCourseList.map((unit, index) => (
|
||||
<CollapseItem
|
||||
key={unit.unitId}
|
||||
header={unit.unitName}
|
||||
name={String(index + 1)}
|
||||
className="course-list-item"
|
||||
>
|
||||
<Timeline>
|
||||
{unit.courses.map((course) => (
|
||||
<TimelineItem
|
||||
key={course.courseId}
|
||||
dot={getDotIcon(course)}
|
||||
lineType="dashed"
|
||||
>
|
||||
<div
|
||||
className={`time-line-item ${getCourseStatus(course)} ${selectedCourseId === course.courseId ? 'selected' : ''}`}
|
||||
onClick={() => {
|
||||
setSelectedCourseId(course.courseId);
|
||||
onCourseClick && onCourseClick({ ...course, unitName: unit.unitName });
|
||||
}}
|
||||
style={{ cursor: 'pointer' }}
|
||||
>
|
||||
<p style={{
|
||||
overflow: 'visible',
|
||||
textOverflow: 'unset',
|
||||
whiteSpace: 'normal',
|
||||
wordBreak: 'break-word',
|
||||
WebkitLineClamp: 'unset',
|
||||
WebkitBoxOrient: 'unset',
|
||||
display: 'block',
|
||||
maxWidth: 'calc(100% - 70px)'
|
||||
}}>{course.courseName}</p>
|
||||
<div className="time-line-item-info">
|
||||
<span>{course.teacherName}</span>
|
||||
<span>{course.date}</span>
|
||||
</div>
|
||||
</div>
|
||||
</TimelineItem>
|
||||
))}
|
||||
</Timeline>
|
||||
</CollapseItem>
|
||||
))}
|
||||
|
||||
{/* 分割线 */}
|
||||
{compoundCourseList.length > 0 && verticalCourseList.length > 0 && (
|
||||
<div className="course-divider">
|
||||
<span className="divider-line"></span>
|
||||
<span className="divider-text">垂直能力课</span>
|
||||
<span className="divider-line"></span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* 垂直能力课部分 */}
|
||||
{verticalCourseList.map((unit, index) => (
|
||||
<CollapseItem
|
||||
key={unit.unitId}
|
||||
header={unit.unitName}
|
||||
name={`vertical-${index + 1}`}
|
||||
className="course-list-item vertical-course"
|
||||
>
|
||||
<Timeline>
|
||||
{unit.courses.map((course) => (
|
||||
<TimelineItem
|
||||
key={course.courseId}
|
||||
dot={getDotIcon(course)}
|
||||
lineType="dashed"
|
||||
>
|
||||
<div
|
||||
className={`time-line-item ${getCourseStatus(course)} ${selectedCourseId === course.courseId ? 'selected' : ''}`}
|
||||
onClick={() => {
|
||||
setSelectedCourseId(course.courseId);
|
||||
onCourseClick && onCourseClick({ ...course, unitName: unit.unitName, courseType: 'vertical' });
|
||||
}}
|
||||
style={{ cursor: 'pointer' }}
|
||||
>
|
||||
<p style={{
|
||||
overflow: 'visible',
|
||||
textOverflow: 'unset',
|
||||
whiteSpace: 'normal',
|
||||
wordBreak: 'break-word',
|
||||
WebkitLineClamp: 'unset',
|
||||
WebkitBoxOrient: 'unset',
|
||||
display: 'block',
|
||||
maxWidth: 'calc(100% - 70px)'
|
||||
}}>{course.courseName}</p>
|
||||
<div className="time-line-item-info">
|
||||
<span>{course.teacherName}</span>
|
||||
<span>{course.date}</span>
|
||||
</div>
|
||||
</div>
|
||||
</TimelineItem>
|
||||
))}
|
||||
</Timeline>
|
||||
</CollapseItem>
|
||||
))}
|
||||
</Collapse>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default CourseList;
|
||||
@@ -90,17 +90,30 @@
|
||||
overflow: hidden;
|
||||
|
||||
img {
|
||||
width: 150%;
|
||||
height: 150%;
|
||||
width: 200%;
|
||||
height: 200%;
|
||||
object-fit: cover;
|
||||
object-position: center 30%;
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
top: 2%;
|
||||
top: 0%;
|
||||
transform: translateX(-50%);
|
||||
}
|
||||
}
|
||||
|
||||
/* 孙应战导师头像特殊调整 - 居中显示 */
|
||||
.teacher-avatar.teacher-sunyingzhan {
|
||||
img {
|
||||
width: 175% !important;
|
||||
height: 175% !important;
|
||||
object-fit: cover !important;
|
||||
object-position: center center !important;
|
||||
top: -50% !important;
|
||||
left: 5% !important;
|
||||
transform: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* 刘杰导师头像特殊调整 */
|
||||
.teacher-avatar.teacher-liujie {
|
||||
img {
|
||||
@@ -111,6 +124,17 @@
|
||||
}
|
||||
}
|
||||
|
||||
/* 求职策略页面的头像特殊调整 */
|
||||
.teacher-avatar.teacher-strategy {
|
||||
img {
|
||||
width: 150% !important;
|
||||
height: 150% !important;
|
||||
object-fit: cover !important;
|
||||
object-position: center 40% !important;
|
||||
top: 0% !important;
|
||||
}
|
||||
}
|
||||
|
||||
.avatar-wrapper {
|
||||
position: relative;
|
||||
width: 64px;
|
||||
@@ -236,18 +260,19 @@
|
||||
}
|
||||
.courses-video-player-teacher-tags {
|
||||
width: 100%;
|
||||
height: 50px;
|
||||
height: auto;
|
||||
min-height: 50px;
|
||||
|
||||
.teacher-tags {
|
||||
width: 100%;
|
||||
height: 24px;
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
align-items: center;
|
||||
flex-wrap: nowrap;
|
||||
flex-direction: row;
|
||||
overflow-x: auto;
|
||||
margin-top: 5px;
|
||||
gap: 8px;
|
||||
overflow: hidden;
|
||||
|
||||
> li {
|
||||
box-sizing: border-box;
|
||||
@@ -256,10 +281,9 @@
|
||||
color: #0077ff;
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
margin-right: 10px;
|
||||
border-radius: 4px;
|
||||
flex-shrink: 0;
|
||||
flex-wrap: nowrap;
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Avatar } from "@arco-design/web-react";
|
||||
import { Avatar, Tooltip } from "@arco-design/web-react";
|
||||
import Locked from "@/components/Locked";
|
||||
import "./index.css";
|
||||
|
||||
@@ -7,19 +7,37 @@ export default ({ className = "", isLock = false, selectedCourse, teacherData, u
|
||||
console.log(item);
|
||||
};
|
||||
|
||||
// 默认导师信息 - 魏立慧老师(用于求职策略定制页面)
|
||||
const defaultTeacher = {
|
||||
name: "魏立慧",
|
||||
introduction: "企业资深一线HR,专注于为求职者提供一对一的个性化指导。通过真实招聘视角,深入剖析个人优势与短板、传授面试技巧、规划职业定位与发展路径,帮助学生快速提升求职竞争力。求职策略以实用落地为核心,注重互动交流与角色定位,让学员在轻松氛围中获得直击痛点的求职策略。",
|
||||
specialties: ["深谙用人逻辑", "擅长挖掘优势", "沟通真诚自然", "点评直击要害"],
|
||||
avatar: "https://ddcz-1315997005.cos.ap-nanjing.myqcloud.com/static/img/teach_sys_teacher-avatar/recuUpSO4gUtJz.png",
|
||||
type: "企业资深HR"
|
||||
};
|
||||
|
||||
// 1V1求职规划课程信息
|
||||
const oneOnOneCourse = {
|
||||
courseName: "1V1 求职规划",
|
||||
date: "2024-08-25",
|
||||
unitName: "1V1 规划阶段",
|
||||
teacherName: "魏立慧",
|
||||
current: false
|
||||
};
|
||||
|
||||
// 获取当前课程的导师信息
|
||||
const currentTeacher = selectedCourse && teacherData
|
||||
? teacherData[selectedCourse.teacherName]
|
||||
: teacherData?.["魏立慧"] || {
|
||||
name: "魏立慧",
|
||||
introduction: "企业资深一线HR,专注于为求职者提供一对一的个性化指导。通过真实招聘视角,深入剖析个人优势与短板、传授面试技巧、规划职业定位与发展路径,帮助学生快速提升求职竞争力。求职策略以实用落地为核心,注重互动交流与角色定位,让学员在轻松氛围中获得直击痛点的求职策略。",
|
||||
specialties: ["深谙用人逻辑", "擅长挖掘优势", "沟通真诚自然", "点评直击要害"],
|
||||
avatar: "https://ddcz-1315997005.cos.ap-nanjing.myqcloud.com/static/img/teach_sys_teacher-avatar/recuUpSO4gUtJz.png",
|
||||
type: "企业资深HR"
|
||||
};
|
||||
// 如果是锁定状态(求职策略页面),始终显示魏立慧老师
|
||||
const currentTeacher = isLock
|
||||
? defaultTeacher
|
||||
: (selectedCourse && teacherData && teacherData[selectedCourse.teacherName]
|
||||
? teacherData[selectedCourse.teacherName]
|
||||
: null);
|
||||
|
||||
// 如果是锁定状态,使用1V1求职规划课程信息
|
||||
const displayCourse = isLock ? oneOnOneCourse : selectedCourse;
|
||||
|
||||
// 需要调整头像位置的导师
|
||||
const needsAdjustment = ["赵志强", "魏立慧", "郭建辉"].includes(currentTeacher.name);
|
||||
const needsAdjustment = currentTeacher && ["赵志强", "魏立慧", "郭建辉", "孙应战"].includes(currentTeacher.name);
|
||||
|
||||
// 根据导师设置不同的背景色 - 这些颜色提取自实际的PNG图片背景
|
||||
const getAvatarBackground = (name) => {
|
||||
@@ -27,16 +45,16 @@ export default ({ className = "", isLock = false, selectedCourse, teacherData, u
|
||||
"刘杰": "#E3E2E0", // 浅灰色
|
||||
"郭建辉": "#E0D9D3", // 米灰色
|
||||
"赵志强": "#E3E2E0", // 浅灰色
|
||||
"孙应战": "#E3E2E0", // 浅灰色
|
||||
"孙应战": "#FFFFFF", // 白色
|
||||
"魏立慧": "#DCD8D4" // 灰褐色
|
||||
};
|
||||
return backgrounds[name] || "#E3E2E0";
|
||||
};
|
||||
|
||||
// 获取当前课程信息
|
||||
const courseName = selectedCourse?.courseName || "钢铁是怎样炼成的";
|
||||
const courseDate = selectedCourse?.date || "09.01";
|
||||
const unitName = selectedCourse?.unitName || "教育体系认知";
|
||||
const courseName = selectedCourse?.courseName || "";
|
||||
const courseDate = selectedCourse?.date || "";
|
||||
const unitName = selectedCourse?.unitName || "";
|
||||
|
||||
// 格式化日期时间
|
||||
const formatDateTime = (date) => {
|
||||
@@ -79,99 +97,171 @@ export default ({ className = "", isLock = false, selectedCourse, teacherData, u
|
||||
<span onClick={() => handleClickBtn(2)}>下一集 ></span>
|
||||
</div>
|
||||
<div className="courses-video-player-video">
|
||||
{/* 所有课程都显示模糊的海报图和锁定状态 */}
|
||||
<div style={{ position: 'relative', width: '100%', height: '100%' }}>
|
||||
<img
|
||||
src={unitPosters?.[unitName] || unitPosters?.["岗位体系认知"]}
|
||||
alt={unitName}
|
||||
style={{
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
objectFit: 'cover',
|
||||
filter: 'blur(20px)'
|
||||
}}
|
||||
/>
|
||||
<div style={{
|
||||
position: 'absolute',
|
||||
top: '50%',
|
||||
left: '50%',
|
||||
transform: 'translate(-50%, -50%)',
|
||||
{selectedCourse ? (
|
||||
/* 选中课程时显示模糊的海报图和锁定状态 */
|
||||
<div style={{ position: 'relative', width: '100%', height: '100%' }}>
|
||||
<img
|
||||
src={unitPosters?.[unitName] || unitPosters?.["岗位体系认知"]}
|
||||
alt={unitName}
|
||||
style={{
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
objectFit: 'cover',
|
||||
filter: 'blur(10px)'
|
||||
}}
|
||||
/>
|
||||
<div style={{
|
||||
position: 'absolute',
|
||||
top: '50%',
|
||||
left: '50%',
|
||||
transform: 'translate(-50%, -50%)',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
alignItems: 'center',
|
||||
gap: '16px'
|
||||
}}>
|
||||
<img
|
||||
src="https://ddcz-1315997005.cos.ap-nanjing.myqcloud.com/static/img/teach_sys_icon/recuVOrz2GnJdK.png"
|
||||
alt="lock"
|
||||
style={{ width: '280px', height: '280px' }}
|
||||
/>
|
||||
<span style={{
|
||||
color: '#fff',
|
||||
fontSize: '16px',
|
||||
fontWeight: '500',
|
||||
textAlign: 'center',
|
||||
backgroundColor: 'rgba(0, 0, 0, 0.6)',
|
||||
padding: '8px 16px',
|
||||
borderRadius: '4px'
|
||||
}}>
|
||||
DEMO演示,非学员无查看权限
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
/* 未选中课程时显示黑屏 */
|
||||
<div style={{
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
backgroundColor: '#000',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
alignItems: 'center',
|
||||
gap: '16px'
|
||||
justifyContent: 'center'
|
||||
}}>
|
||||
<img
|
||||
src="https://ddcz-1315997005.cos.ap-nanjing.myqcloud.com/static/img/teach_sys_icon/recuVOrz2GnJdK.png"
|
||||
alt="lock"
|
||||
style={{ width: '280px', height: '280px' }}
|
||||
/>
|
||||
<span style={{
|
||||
color: '#fff',
|
||||
fontSize: '16px',
|
||||
fontWeight: '500',
|
||||
textAlign: 'center',
|
||||
backgroundColor: 'rgba(0, 0, 0, 0.6)',
|
||||
padding: '8px 16px',
|
||||
borderRadius: '4px'
|
||||
color: '#666',
|
||||
fontSize: '16px'
|
||||
}}>
|
||||
DEMO演示,非学员无查看权限
|
||||
请选择课程开始观看
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
<div className="courses-video-player-info">
|
||||
{/* 直播观众信息 */}
|
||||
<div className="courses-video-player-audience-info">
|
||||
<div className="avatar-wrapper">
|
||||
<Avatar
|
||||
className={`teacher-avatar ${needsAdjustment ? 'avatar-adjust' : ''} ${currentTeacher.name === '刘杰' ? 'teacher-liujie' : ''}`}
|
||||
style={{ backgroundColor: getAvatarBackground(currentTeacher.name) }}
|
||||
>
|
||||
<img
|
||||
alt="avatar"
|
||||
src={currentTeacher.avatar}
|
||||
/>
|
||||
</Avatar>
|
||||
{selectedCourse?.current && <div className="living-icon" />}
|
||||
</div>
|
||||
<span className="teacher-name">{currentTeacher.name}老师</span>
|
||||
<span className="teacher-tag">{unitName}</span>
|
||||
<div className="living-data">
|
||||
<div className="living-data-item">
|
||||
<span>开始</span>
|
||||
<span>{formatDateTime(courseDate)} - 14:00</span>
|
||||
{(isLock || (selectedCourse && currentTeacher)) ? (
|
||||
<>
|
||||
{/* 直播观众信息 */}
|
||||
<div className="courses-video-player-audience-info">
|
||||
<div className="avatar-wrapper">
|
||||
<Avatar
|
||||
className={`teacher-avatar ${needsAdjustment ? 'avatar-adjust' : ''} ${currentTeacher?.name === '刘杰' ? 'teacher-liujie' : ''} ${currentTeacher?.name === '孙应战' ? 'teacher-sunyingzhan' : ''} ${isLock ? 'teacher-strategy' : ''}`}
|
||||
style={{ backgroundColor: getAvatarBackground(currentTeacher?.name) }}
|
||||
>
|
||||
<img
|
||||
alt="avatar"
|
||||
src={currentTeacher?.avatar || ''}
|
||||
/>
|
||||
</Avatar>
|
||||
{displayCourse?.current && <div className="living-icon" />}
|
||||
</div>
|
||||
<span className="teacher-name">{currentTeacher?.name || ''}老师</span>
|
||||
<span className="teacher-tag">{displayCourse?.unitName || unitName}</span>
|
||||
<div className="living-data">
|
||||
<div className="living-data-item">
|
||||
<span>开始</span>
|
||||
<span>{formatDateTime(displayCourse?.date || courseDate)} - 14:00</span>
|
||||
</div>
|
||||
<div className="living-data-item">
|
||||
<span>时长</span>
|
||||
<span>{isLock ? '120分钟' : '60分钟'}</span>
|
||||
</div>
|
||||
<div className="living-data-item">
|
||||
<span>观看</span>
|
||||
<span>{isLock ? '1人' : `${
|
||||
selectedCourse?.courseType === 'vertical'
|
||||
? 50 + Math.floor(Math.random() * 11) // 垂直课:50-60人
|
||||
: 180 + Math.floor(Math.random() * 21) // 复合课:180-200人
|
||||
}人`}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="living-data-item">
|
||||
<span>时长</span>
|
||||
<span>60分钟</span>
|
||||
</div>
|
||||
<div className="living-data-item">
|
||||
<span>观看</span>
|
||||
<span>{selectedCourse ? '197人' : '1928人'}</span>
|
||||
{/* 直播教师信息 */}
|
||||
<div className="courses-video-player-teacher-info">
|
||||
<div className="courses-video-player-teacher-introduce">
|
||||
<p className="title icon1">导师介绍</p>
|
||||
<p className="teacher-introduce">
|
||||
{currentTeacher?.introduction || ''}
|
||||
</p>
|
||||
</div>
|
||||
<div className="courses-video-player-teacher-tags">
|
||||
<p className="title icon2">教师专长</p>
|
||||
<Tooltip
|
||||
content={
|
||||
<div style={{
|
||||
display: 'flex',
|
||||
flexWrap: 'wrap',
|
||||
gap: '8px',
|
||||
maxWidth: '320px',
|
||||
padding: '8px'
|
||||
}}>
|
||||
{(currentTeacher?.specialties || []).map((specialty, index) => (
|
||||
<span key={index} style={{
|
||||
background: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)',
|
||||
color: '#fff',
|
||||
padding: '4px 12px',
|
||||
borderRadius: '12px',
|
||||
fontSize: '12px',
|
||||
fontWeight: '500',
|
||||
boxShadow: '0 2px 4px rgba(0,0,0,0.1)'
|
||||
}}>
|
||||
{specialty}
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
}
|
||||
position="top"
|
||||
color="#fff"
|
||||
style={{
|
||||
'--arco-color-bg-tooltip': 'linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%)',
|
||||
'--arco-color-text-tooltip': '#1d2129'
|
||||
}}
|
||||
>
|
||||
<ul className="teacher-tags">
|
||||
{(currentTeacher?.specialties || []).map((specialty, index) => (
|
||||
<li key={index}>{specialty}</li>
|
||||
))}
|
||||
</ul>
|
||||
</Tooltip>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
) : (
|
||||
/* 未选中课程时显示空白 */
|
||||
<div style={{
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
color: '#86909c',
|
||||
fontSize: '14px'
|
||||
}}>
|
||||
请选择课程查看导师信息
|
||||
</div>
|
||||
</div>
|
||||
{/* 直播教师信息 */}
|
||||
<div className="courses-video-player-teacher-info">
|
||||
<div className="courses-video-player-teacher-introduce">
|
||||
<p className="title icon1">导师介绍</p>
|
||||
<p className="teacher-introduce">
|
||||
{currentTeacher.introduction}
|
||||
</p>
|
||||
</div>
|
||||
<div className="courses-video-player-teacher-tags">
|
||||
<p className="title icon2">教师专长</p>
|
||||
<ul className="teacher-tags">
|
||||
{currentTeacher.specialties.map((specialty, index) => (
|
||||
<li key={index}>{specialty}</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -192,6 +192,12 @@
|
||||
gap: 5px;
|
||||
}
|
||||
|
||||
.course-list-item .time-line-item .course-date {
|
||||
font-size: 12px;
|
||||
color: #86909c;
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
.course-list-item .finish::before {
|
||||
content: "已结束";
|
||||
position: absolute;
|
||||
|
||||
@@ -22,43 +22,7 @@ const PublicCourseList = ({ className = "", onCourseClick }) => {
|
||||
if (res.success) {
|
||||
const courseList = res.data || [];
|
||||
setCourseLiveList(courseList);
|
||||
|
||||
// 设置默认选中今天的课程(如果有)
|
||||
const todayStr = new Date().toISOString().split('T')[0];
|
||||
let foundTodayCourse = false;
|
||||
|
||||
for (const unit of courseList) {
|
||||
const todayCourse = unit.courses.find(c => c.date === todayStr);
|
||||
if (todayCourse) {
|
||||
setSelectedCourseId(todayCourse.courseId);
|
||||
// 触发课程选择事件
|
||||
if (onCourseClick) {
|
||||
onCourseClick({
|
||||
...todayCourse,
|
||||
unitName: unit.unitName
|
||||
});
|
||||
}
|
||||
foundTodayCourse = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// 如果没有今天的课程,选中第一个current或upcoming的课程
|
||||
if (!foundTodayCourse) {
|
||||
for (const unit of courseList) {
|
||||
const activeCourse = unit.courses.find(c => c.current || c.upcoming);
|
||||
if (activeCourse) {
|
||||
setSelectedCourseId(activeCourse.courseId);
|
||||
if (onCourseClick) {
|
||||
onCourseClick({
|
||||
...activeCourse,
|
||||
unitName: unit.unitName
|
||||
});
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
// 不设置默认选中,保持黑屏状态
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Failed to fetch course list:", error);
|
||||
@@ -122,7 +86,7 @@ const PublicCourseList = ({ className = "", onCourseClick }) => {
|
||||
className="course-list"
|
||||
bordered={false}
|
||||
expandIconPosition="right"
|
||||
defaultActiveKey={["1", "2", "3"]}
|
||||
defaultActiveKey={[]}
|
||||
>
|
||||
{courseLiveList.map((unit, index) => (
|
||||
<CollapseItem
|
||||
@@ -155,9 +119,8 @@ const PublicCourseList = ({ className = "", onCourseClick }) => {
|
||||
{course.courseName}
|
||||
</p>
|
||||
<div className="time-line-item-info">
|
||||
<span>讲师:{course.teacherName}</span>
|
||||
{course.date && <span>{course.date}</span>}
|
||||
{course.time && <span>{course.time}</span>}
|
||||
<span>{course.teacherName}</span>
|
||||
<span className="course-date">{course.date}</span>
|
||||
</div>
|
||||
</div>
|
||||
</TimelineItem>
|
||||
|
||||
@@ -17,11 +17,12 @@
|
||||
height: 64px;
|
||||
background-size: 100% 100%;
|
||||
margin-right: -10px;
|
||||
background-image: url("@/assets/images/StageProgress/step1_active.png");
|
||||
background-image: url("@/assets/images/StageProgress/step1.png");
|
||||
background-size: 100%;
|
||||
color: #ffffff;
|
||||
color: #0077ff;
|
||||
font-size: 16px;
|
||||
font-weight: 400;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.stage-progress-item4 {
|
||||
@@ -39,11 +40,15 @@
|
||||
}
|
||||
|
||||
.stage-progress-item4-active {
|
||||
color: #ffffff;
|
||||
color: #ffffff !important;
|
||||
font-size: 16px;
|
||||
font-weight: 400;
|
||||
background-image: url("@/assets/images/StageProgress/step4_active.png");
|
||||
}
|
||||
|
||||
.stage-progress-item4-active .stage-progress-item-text-active {
|
||||
color: #ffffff !important;
|
||||
}
|
||||
|
||||
.stage-progress-item {
|
||||
flex-shrink: 0;
|
||||
@@ -57,11 +62,12 @@
|
||||
color: #86909c;
|
||||
font-size: 16px;
|
||||
font-weight: 400;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.stage-progress-item-active {
|
||||
color: #ffffff;
|
||||
background-image: url("@/assets/images/StageProgress/step_active.png");
|
||||
color: #0077ff;
|
||||
background-image: url("@/assets/images/StageProgress/step2-3.png");
|
||||
}
|
||||
|
||||
.stage-progress-item-step {
|
||||
@@ -93,6 +99,18 @@
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
}
|
||||
|
||||
.stage-progress-item-icon-blue {
|
||||
filter: brightness(0) saturate(100%) invert(31%) sepia(98%) saturate(2000%) hue-rotate(201deg) brightness(95%) contrast(101%);
|
||||
}
|
||||
|
||||
.stage-progress-item-icon-white {
|
||||
filter: brightness(0) invert(1);
|
||||
}
|
||||
|
||||
.stage-progress-item4-text {
|
||||
color: #ffffff !important;
|
||||
}
|
||||
.stage-progress-item-icon2 {
|
||||
width: 5px;
|
||||
height: 10px;
|
||||
@@ -100,7 +118,7 @@
|
||||
}
|
||||
|
||||
.stage-progress-item-text-active {
|
||||
color: #fff !important;
|
||||
color: #0077ff !important;
|
||||
}
|
||||
|
||||
.stage-progress-text {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import IconFont from "@/components/IconFont";
|
||||
import CareerStartIcon from "@/assets/images/StageProgress/career_start_icon_new.png";
|
||||
import "./index.css";
|
||||
|
||||
const StageProgress = () => {
|
||||
@@ -16,31 +17,35 @@ const StageProgress = () => {
|
||||
step1
|
||||
</span>
|
||||
<span className="stage-progress-item-text stage-progress-item-text-active">
|
||||
生涯起航
|
||||
生涯启航
|
||||
</span>
|
||||
<IconFont src="recuUY5n0dIJKu" className="stage-progress-item-icon" />
|
||||
<img
|
||||
src={CareerStartIcon}
|
||||
className="stage-progress-item-icon stage-progress-item-icon-blue"
|
||||
alt="生涯启航"
|
||||
/>
|
||||
</li>
|
||||
<li className="stage-progress-item">
|
||||
<span className="stage-progress-item-step">step2</span>
|
||||
<span className="stage-progress-item-text">能力跃升</span>
|
||||
<IconFont src="recuUY5kKp4Qc5" className="stage-progress-item-icon" />
|
||||
<li className="stage-progress-item stage-progress-item-active">
|
||||
<span className="stage-progress-item-step stage-progress-item-step-active">step2</span>
|
||||
<span className="stage-progress-item-text stage-progress-item-text-active">能力跃升</span>
|
||||
<IconFont src="recuUY5kKp4Qc5" className="stage-progress-item-icon stage-progress-item-icon-blue" />
|
||||
</li>
|
||||
<li className="stage-progress-text" onClick={handleClickStar}>
|
||||
<span>垂直方向选择</span>
|
||||
</li>
|
||||
<li className="stage-progress-item">
|
||||
<span className="stage-progress-item-step">step3</span>
|
||||
<span className="stage-progress-item-text">垂直精进</span>
|
||||
<IconFont src="recuUY5qlmzVhH" className="stage-progress-item-icon" />
|
||||
<IconFont src="recuUY5xdpLNXn" className="stage-progress-item-icon2" />
|
||||
<IconFont src="recuUY5joxSk5C" className="stage-progress-item-icon" />
|
||||
<li className="stage-progress-item stage-progress-item-active">
|
||||
<span className="stage-progress-item-step stage-progress-item-step-active">step3</span>
|
||||
<span className="stage-progress-item-text stage-progress-item-text-active">垂直精进</span>
|
||||
<IconFont src="recuUY5qlmzVhH" className="stage-progress-item-icon stage-progress-item-icon-blue" />
|
||||
<IconFont src="recuUY5xdpLNXn" className="stage-progress-item-icon2 stage-progress-item-icon-blue" />
|
||||
<IconFont src="recuUY5joxSk5C" className="stage-progress-item-icon stage-progress-item-icon-blue" />
|
||||
</li>
|
||||
<li className="stage-progress-item4 ">
|
||||
<span className="stage-progress-item-step">step4</span>
|
||||
<span className="stage-progress-item-text">决胜求职</span>
|
||||
<IconFont src="recuUY5lTOco3Q" className="stage-progress-item-icon" />
|
||||
<IconFont src="recuUY5xdpLNXn" className="stage-progress-item-icon2" />
|
||||
<IconFont src="recuUY5luVMCPc" className="stage-progress-item-icon" />
|
||||
<li className="stage-progress-item4 stage-progress-item4-active">
|
||||
<span className="stage-progress-item-step stage-progress-item-step-active">step4</span>
|
||||
<span className="stage-progress-item-text stage-progress-item-text-active stage-progress-item4-text">决胜求职</span>
|
||||
<IconFont src="recuUY5lTOco3Q" className="stage-progress-item-icon stage-progress-item-icon-white" />
|
||||
<IconFont src="recuUY5xdpLNXn" className="stage-progress-item-icon2 stage-progress-item-icon-white" />
|
||||
<IconFont src="recuUY5luVMCPc" className="stage-progress-item-icon stage-progress-item-icon-white" />
|
||||
</li>
|
||||
</ul>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user