feat: 作业页面单元分类导航和样式优化
- 为复合能力课和垂直能力课添加单元分组结构 - 实现单元导航栏和课程筛选功能 - 优化导航栏样式,采用胶囊式设计 - 调整页面布局和间距,提升视觉体验 - 修复营销能力课日历事件显示问题 - 修复1v1规划时间为14:00-16:00 - 修复作业页面iframe返回后滚动失效问题 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -177,6 +177,28 @@ const EventDetailModal = ({ isOpen, event, onClose }) => {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 企业高管公开课添加线下参与标签 */}
|
||||
{eventItem.type === 'public-course' && (
|
||||
<div className="event-info-row" style={{ marginTop: '8px' }}>
|
||||
<div style={{
|
||||
padding: '4px 12px',
|
||||
backgroundColor: '#f0f9ff',
|
||||
color: '#0ea5e9',
|
||||
borderRadius: '4px',
|
||||
fontSize: '12px',
|
||||
fontWeight: '500',
|
||||
display: 'inline-flex',
|
||||
alignItems: 'center',
|
||||
gap: '4px'
|
||||
}}>
|
||||
<svg width="14" height="14" viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M12 2C8.13 2 5 5.13 5 9c0 5.25 7 13 7 13s7-7.75 7-13c0-3.87-3.13-7-7-7zm0 9.5c-1.38 0-2.5-1.12-2.5-2.5s1.12-2.5 2.5-2.5 2.5 1.12 2.5 2.5-1.12 2.5-2.5 2.5z"/>
|
||||
</svg>
|
||||
可报名线下参与
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{eventItem.description && (
|
||||
<div className="event-description">
|
||||
{eventItem.description}
|
||||
|
||||
@@ -242,44 +242,37 @@
|
||||
|
||||
/* 复合技能课 */
|
||||
.event-item.compound-skill {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
background: #5AC6FF;
|
||||
}
|
||||
|
||||
/* 垂直技能课 */
|
||||
.event-item.vertical-skill {
|
||||
background: linear-gradient(135deg, #4ade80 0%, #22c55e 100%);
|
||||
background: #CB78E0;
|
||||
}
|
||||
|
||||
/* 公开课 */
|
||||
/* 企业高管公开课 */
|
||||
.event-item.public-course {
|
||||
background: linear-gradient(135deg, #f59e0b 0%, #fbbf24 100%);
|
||||
background: #D0A474;
|
||||
}
|
||||
|
||||
/* AI课程 */
|
||||
.event-item.ai-course {
|
||||
background: #F7A133;
|
||||
}
|
||||
|
||||
/* 营销课 */
|
||||
.event-item.marketing-course {
|
||||
background: #FF4277;
|
||||
}
|
||||
|
||||
/* 1v1规划 */
|
||||
.event-item.one-on-one {
|
||||
background: linear-gradient(135deg, #ec4899 0%, #f472b6 100%);
|
||||
background: #FFCC3F;
|
||||
}
|
||||
|
||||
/* 线下面试模拟 */
|
||||
.event-item.interview {
|
||||
background: linear-gradient(135deg, #06b6d4 0%, #3b82f6 100%);
|
||||
}
|
||||
|
||||
/* 默认类型保留 */
|
||||
.event-item.class {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
}
|
||||
|
||||
.event-item.meeting {
|
||||
background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
|
||||
}
|
||||
|
||||
.event-item.lab {
|
||||
background: linear-gradient(135deg, #fa709a 0%, #fee140 100%);
|
||||
}
|
||||
|
||||
.event-item.exam {
|
||||
background: linear-gradient(135deg, #ff6b6b 0%, #ff8e53 100%);
|
||||
background: #0743DA;
|
||||
}
|
||||
|
||||
.event-more {
|
||||
|
||||
@@ -16,26 +16,68 @@
|
||||
.homework-page-content-list {
|
||||
width: 1120px;
|
||||
min-height: 360px;
|
||||
border-radius: 8px;
|
||||
border-radius: 12px;
|
||||
background-color: #fff;
|
||||
margin-bottom: 20px;
|
||||
margin-bottom: 24px;
|
||||
box-sizing: border-box;
|
||||
padding: 20px 15px;
|
||||
padding: 24px 20px 20px;
|
||||
position: relative;
|
||||
overflow: hidden; /* 重要:隐藏溢出内容 */
|
||||
overflow: hidden;
|
||||
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.04);
|
||||
|
||||
.homework-page-content-list-title {
|
||||
font-size: 20px;
|
||||
font-weight: 600;
|
||||
color: #000;
|
||||
.homework-page-content-list-header {
|
||||
margin-bottom: 16px;
|
||||
|
||||
.homework-page-content-list-title {
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
color: #1d2129;
|
||||
margin: 0;
|
||||
line-height: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.homework-page-unit-nav {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
margin-bottom: 20px;
|
||||
padding-bottom: 16px;
|
||||
border-bottom: 1px solid #f2f3f5;
|
||||
|
||||
.unit-nav-item {
|
||||
padding: 5px 14px;
|
||||
font-size: 13px;
|
||||
color: #4e5969;
|
||||
background: #f7f8fa;
|
||||
border: 1px solid transparent;
|
||||
border-radius: 14px;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
white-space: nowrap;
|
||||
font-weight: 400;
|
||||
|
||||
&:hover {
|
||||
background: #e8f3ff;
|
||||
color: #2c7aff;
|
||||
}
|
||||
|
||||
&.active {
|
||||
background: #2c7aff;
|
||||
color: #fff;
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.homework-page-content-list-class {
|
||||
width: calc(100% + 30px); /* 扩展到padding外 */
|
||||
height: 300px;
|
||||
margin-top: 20px;
|
||||
margin-left: -15px;
|
||||
margin-right: -15px;
|
||||
padding: 0 15px;
|
||||
width: calc(100% + 40px); /* 扩展到padding外 */
|
||||
height: 280px;
|
||||
margin-top: 0;
|
||||
margin-left: -20px;
|
||||
margin-right: -20px;
|
||||
padding: 0 20px;
|
||||
padding-bottom: 10px;
|
||||
overflow-x: auto;
|
||||
overflow-y: hidden;
|
||||
|
||||
@@ -9,7 +9,12 @@ import "./index.css";
|
||||
const HomeworkPage = () => {
|
||||
const { homework } = mockData;
|
||||
const scrollContainerRef = useRef(null);
|
||||
const verticalScrollContainerRef = useRef(null);
|
||||
const [showIframe, setShowIframe] = useState(false);
|
||||
const [selectedUnits, setSelectedUnits] = useState({
|
||||
1: "全部", // 复合能力课的选中单元
|
||||
2: "全部" // 垂直能力课的选中单元
|
||||
});
|
||||
|
||||
// 调试:打印课程数量
|
||||
console.log('作业数据:', homework);
|
||||
@@ -19,67 +24,90 @@ const HomeworkPage = () => {
|
||||
|
||||
// 添加鼠标滚轮横向滚动支持(更丝滑的滚动)
|
||||
useEffect(() => {
|
||||
const container = scrollContainerRef.current;
|
||||
if (!container) return;
|
||||
// 如果显示iframe,不初始化滚动
|
||||
if (showIframe) return;
|
||||
|
||||
const containers = [scrollContainerRef.current, verticalScrollContainerRef.current].filter(Boolean);
|
||||
if (containers.length === 0) return;
|
||||
|
||||
let animationId = null;
|
||||
let targetScrollLeft = container.scrollLeft;
|
||||
const animationIds = new Map();
|
||||
const targetScrollLefts = new Map();
|
||||
const handlers = new Map();
|
||||
|
||||
// 平滑滚动动画
|
||||
const smoothScroll = () => {
|
||||
const currentScrollLeft = container.scrollLeft;
|
||||
const diff = targetScrollLeft - currentScrollLeft;
|
||||
containers.forEach(container => {
|
||||
targetScrollLefts.set(container, container.scrollLeft);
|
||||
|
||||
if (Math.abs(diff) > 0.5) {
|
||||
container.scrollLeft = currentScrollLeft + diff * 0.15; // 缓动系数
|
||||
animationId = requestAnimationFrame(smoothScroll);
|
||||
} else {
|
||||
container.scrollLeft = targetScrollLeft;
|
||||
if (animationId) {
|
||||
cancelAnimationFrame(animationId);
|
||||
animationId = null;
|
||||
// 平滑滚动动画
|
||||
const smoothScroll = () => {
|
||||
const currentScrollLeft = container.scrollLeft;
|
||||
const targetScrollLeft = targetScrollLefts.get(container);
|
||||
const diff = targetScrollLeft - currentScrollLeft;
|
||||
|
||||
if (Math.abs(diff) > 0.5) {
|
||||
container.scrollLeft = currentScrollLeft + diff * 0.15; // 缓动系数
|
||||
const animId = requestAnimationFrame(smoothScroll);
|
||||
animationIds.set(container, animId);
|
||||
} else {
|
||||
container.scrollLeft = targetScrollLeft;
|
||||
const animId = animationIds.get(container);
|
||||
if (animId) {
|
||||
cancelAnimationFrame(animId);
|
||||
animationIds.delete(container);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
// 鼠标滚轮横向滚动
|
||||
const handleWheel = (e) => {
|
||||
e.preventDefault();
|
||||
// 鼠标滚轮横向滚动
|
||||
const handleWheel = (e) => {
|
||||
e.preventDefault();
|
||||
|
||||
// 计算滚动距离,使用较小的值让滚动更平滑
|
||||
const scrollAmount = e.deltaY * 0.8;
|
||||
let newTargetScrollLeft = targetScrollLefts.get(container) + scrollAmount;
|
||||
|
||||
// 限制滚动范围
|
||||
newTargetScrollLeft = Math.max(0, Math.min(newTargetScrollLeft, container.scrollWidth - container.clientWidth));
|
||||
targetScrollLefts.set(container, newTargetScrollLeft);
|
||||
|
||||
// 如果没有正在进行的动画,启动平滑滚动
|
||||
if (!animationIds.has(container)) {
|
||||
const animId = requestAnimationFrame(smoothScroll);
|
||||
animationIds.set(container, animId);
|
||||
}
|
||||
};
|
||||
|
||||
// 计算滚动距离,使用较小的值让滚动更平滑
|
||||
const scrollAmount = e.deltaY * 0.8;
|
||||
targetScrollLeft = container.scrollLeft + scrollAmount;
|
||||
|
||||
// 限制滚动范围
|
||||
targetScrollLeft = Math.max(0, Math.min(targetScrollLeft, container.scrollWidth - container.clientWidth));
|
||||
|
||||
// 如果没有正在进行的动画,启动平滑滚动
|
||||
if (!animationId) {
|
||||
animationId = requestAnimationFrame(smoothScroll);
|
||||
}
|
||||
};
|
||||
|
||||
container.addEventListener('wheel', handleWheel, { passive: false });
|
||||
handlers.set(container, handleWheel);
|
||||
container.addEventListener('wheel', handleWheel, { passive: false });
|
||||
});
|
||||
|
||||
return () => {
|
||||
container.removeEventListener('wheel', handleWheel);
|
||||
if (animationId) {
|
||||
cancelAnimationFrame(animationId);
|
||||
}
|
||||
containers.forEach(container => {
|
||||
const handleWheel = handlers.get(container);
|
||||
if (handleWheel) {
|
||||
container.removeEventListener('wheel', handleWheel);
|
||||
}
|
||||
const animId = animationIds.get(container);
|
||||
if (animId) {
|
||||
cancelAnimationFrame(animId);
|
||||
}
|
||||
});
|
||||
};
|
||||
}, []);
|
||||
}, [showIframe]);
|
||||
|
||||
const handleClickBtn = (sectionId, itemId) => {
|
||||
// 只有复合能力培养的第72个项目(展会策划教学)可以点击
|
||||
if (sectionId === 1 && itemId === 72) {
|
||||
const handleClickBtn = (sectionId, item) => {
|
||||
// 垂直能力课中标记为isShowCase的课程可以点击
|
||||
if (sectionId === 2 && item.isShowCase) {
|
||||
setShowIframe(true);
|
||||
}
|
||||
};
|
||||
|
||||
// 判断是否应该显示灰色图片和禁用按钮
|
||||
const isDisabled = (sectionId, itemId) => {
|
||||
// 只有复合能力培养的展会策划教学(第72个)是启用状态
|
||||
return !(sectionId === 1 && itemId === 72);
|
||||
const isDisabled = (sectionId, item) => {
|
||||
// 垂直能力课中标记为isShowCase的课程是启用状态
|
||||
if (sectionId === 2 && item.isShowCase) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
// 如果显示iframe,渲染全页面的iframe
|
||||
@@ -105,18 +133,62 @@ const HomeworkPage = () => {
|
||||
);
|
||||
}
|
||||
|
||||
// 获取筛选后的课程列表
|
||||
const getFilteredCourses = (sectionId) => {
|
||||
const section = homework.find(h => h.id === sectionId);
|
||||
if (!section) return [];
|
||||
|
||||
const selectedUnit = selectedUnits[sectionId];
|
||||
|
||||
// 如果有units结构,使用新结构
|
||||
if (section.units) {
|
||||
if (selectedUnit === "全部") {
|
||||
// 将所有单元的课程合并
|
||||
return section.units.flatMap(unit => unit.courses);
|
||||
} else {
|
||||
// 返回选中单元的课程
|
||||
const unit = section.units.find(u => u.name === selectedUnit);
|
||||
return unit ? unit.courses : [];
|
||||
}
|
||||
}
|
||||
|
||||
// 兼容旧结构
|
||||
return section.list || [];
|
||||
};
|
||||
|
||||
// 正常渲染作业列表
|
||||
return (
|
||||
<div className="homework-page-wrapper">
|
||||
<ul className="homework-page-content">
|
||||
{homework.map((item) => (
|
||||
<li key={item.id} className="homework-page-content-list">
|
||||
<p className="homework-page-content-list-title">{item.name}</p>
|
||||
<div className="homework-page-content-list-header">
|
||||
<p className="homework-page-content-list-title">{item.name}</p>
|
||||
</div>
|
||||
{item.units && (
|
||||
<div className="homework-page-unit-nav">
|
||||
<span
|
||||
className={`unit-nav-item ${selectedUnits[item.id] === "全部" ? "active" : ""}`}
|
||||
onClick={() => setSelectedUnits({...selectedUnits, [item.id]: "全部"})}
|
||||
>
|
||||
全部
|
||||
</span>
|
||||
{item.units.map((unit, index) => (
|
||||
<span
|
||||
key={index}
|
||||
className={`unit-nav-item ${selectedUnits[item.id] === unit.name ? "active" : ""}`}
|
||||
onClick={() => setSelectedUnits({...selectedUnits, [item.id]: unit.name})}
|
||||
>
|
||||
{unit.name}
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
<ul
|
||||
className="homework-page-content-list-class"
|
||||
ref={item.id === 1 ? scrollContainerRef : null}
|
||||
ref={item.id === 1 ? scrollContainerRef : item.id === 2 ? verticalScrollContainerRef : null}
|
||||
>
|
||||
{item.list.map((contentItem) => (
|
||||
{getFilteredCourses(item.id).map((contentItem) => (
|
||||
<li
|
||||
key={contentItem.id}
|
||||
className="homework-page-content-list-content-item"
|
||||
@@ -133,9 +205,9 @@ const HomeworkPage = () => {
|
||||
</Tooltip>
|
||||
<div
|
||||
className={`homework-page-content-list-content-item-btn ${
|
||||
isDisabled(item.id, contentItem.id) ? "disabled" : "completed"
|
||||
isDisabled(item.id, contentItem) ? "disabled" : "completed"
|
||||
}`}
|
||||
onClick={() => !isDisabled(item.id, contentItem.id) && handleClickBtn(item.id, contentItem.id)}
|
||||
onClick={() => !isDisabled(item.id, contentItem) && handleClickBtn(item.id, contentItem)}
|
||||
>
|
||||
已完成
|
||||
</div>
|
||||
|
||||
@@ -11,7 +11,7 @@ export default ({ selectedItem = "求职面试初体验" }) => {
|
||||
case "求职面试初体验":
|
||||
return "https://ddcz-1315997005.cos.ap-nanjing.myqcloud.com/static/video/teach_sys/interview_simulation/3years_ago.mov";
|
||||
case "未来的自己":
|
||||
return "https://ddcz-1315997005.cos.ap-nanjing.myqcloud.com/static/video/teach_sys/interview_offline_vedio/recuUpJT02CMM5.mp4";
|
||||
return "https://ddcz-1315997005.cos.ap-nanjing.myqcloud.com/static/video/teach_sys/interview_simulation/3years_later.mov";
|
||||
case "第一次线下面试模拟":
|
||||
return "https://ddcz-1315997005.cos.ap-nanjing.myqcloud.com/static/video/teach_sys/interview_offline_vedio/recuUpJSOKoqAm.mov"; // 使用相同视频作为示例
|
||||
case "第二次线下面试模拟":
|
||||
|
||||
@@ -18,7 +18,8 @@ const PublicCourses = () => {
|
||||
<CoursesVideoPlayer
|
||||
selectedCourse={selectedCourse}
|
||||
teacherData={mockData.teacherData}
|
||||
unitPosters={mockData.unitPosters}
|
||||
unitPosters={mockData.publicCourseBackgrounds}
|
||||
isPublicCourse={true}
|
||||
/>
|
||||
<PublicCourseList onCourseClick={handleCourseClick} />
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user