更新12个教务系统并优化项目大小

主要更新:
- 更新所有12个产业的教务系统数据和功能
- 删除所有 node_modules 文件夹(节省3.7GB)
- 删除所有 .yoyo 缓存文件夹(节省1.2GB)
- 删除所有 dist 构建文件(节省55MB)

项目优化:
- 项目大小从 8.1GB 减少到 3.2GB(节省60%空间)
- 保留完整的源代码和配置文件
- .gitignore 已配置,防止再次提交大文件

启动脚本:
- start-industry.sh/bat/ps1 脚本会自动检测并安装依赖
- 首次启动时自动运行 npm install
- 支持单个或批量启动产业系统

🤖 Generated with Claude Code

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
KQL
2025-10-17 14:36:25 +08:00
parent 60921dbfb9
commit 38350dca36
792 changed files with 470498 additions and 11589 deletions

View File

@@ -294,30 +294,26 @@ const CourseList = forwardRef(({ className = "", onCourseClick }, ref) => {
onChange={(keys) => {
console.log('Collapse onChange received:', keys, 'type:', typeof keys);
// Arco Collapse 在受控模式下,当点击时:
// - 如果是字符串,表示点击了某个面板,需要切换它的展开/收起状态
// - 如果是数组,表示新的展开状态
// 实现手风琴效果:只保留最后点击的单元展开
if (typeof keys === 'string') {
// 切换单个面板的展开/收起状态
setActiveKeys(prevKeys => {
const keyStr = String(keys);
const newKeys = [...prevKeys];
const index = newKeys.indexOf(keyStr);
if (index > -1) {
// 如果已展开,则收起
newKeys.splice(index, 1);
} else {
// 如果已收起,则展开
newKeys.push(keyStr);
}
console.log('Toggling key:', keyStr, 'New activeKeys:', newKeys);
return newKeys;
});
// 如果点击的是已经展开的单元,则收起它
if (activeKeys.includes(keys)) {
setActiveKeys([]);
} else {
// 否则展开新的单元,收起其它所有单元
setActiveKeys([keys]);
}
} else if (Array.isArray(keys)) {
// 直接设置新的展开状态
setActiveKeys(keys);
// 如果是数组,只保留最后一个元素(最新展开的)
if (keys.length > activeKeys.length) {
// 有新的面板被展开,找到新增的那个
const newKey = keys.find(key => !activeKeys.includes(key));
setActiveKeys(newKey ? [newKey] : keys.slice(-1));
} else {
// 有面板被收起
setActiveKeys(keys);
}
} else {
// 处理 undefined/null 的情况
setActiveKeys([]);
}
}}
@@ -434,19 +430,12 @@ const CourseList = forwardRef(({ className = "", onCourseClick }, ref) => {
dot={getDotIcon(course)}
lineType="dashed"
>
<div
<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' }}
>

View File

@@ -171,10 +171,10 @@
/* 杨丽华导师头像特殊调整 - 缩小显示 */
.teacher-avatar.teacher-yanglihua {
img {
width: 130% !important;
height: 130% !important;
width: 200% !important;
height: 200% !important;
object-fit: cover !important;
top: 5% !important;
top: -10% !important;
left: auto !important;
transform: none !important;
}

View File

@@ -1,19 +1,77 @@
import { useState, useRef, useEffect } from "react";
import { Avatar, Tooltip } from "@arco-design/web-react";
import Locked from "@/components/Locked";
import logoImg from "@/assets/images/Sidebar/logo.png";
import "./index.css";
export default ({ className = "", isLock = false, selectedCourse, teacherData, unitPosters, isPublicCourse = false, backgroundImage }) => {
const [isFullscreen, setIsFullscreen] = useState(false);
const iframeContainerRef = useRef(null);
const handleClickBtn = (item) => {
console.log(item);
};
// 处理全屏切换
const handleFullscreen = () => {
const container = iframeContainerRef.current;
if (!container) return;
if (!isFullscreen) {
// 进入全屏
if (container.requestFullscreen) {
container.requestFullscreen();
} else if (container.webkitRequestFullscreen) {
container.webkitRequestFullscreen();
} else if (container.mozRequestFullScreen) {
container.mozRequestFullScreen();
} else if (container.msRequestFullscreen) {
container.msRequestFullscreen();
}
} else {
// 退出全屏
if (document.exitFullscreen) {
document.exitFullscreen();
} else if (document.webkitExitFullscreen) {
document.webkitExitFullscreen();
} else if (document.mozCancelFullScreen) {
document.mozCancelFullScreen();
} else if (document.msExitFullscreen) {
document.msExitFullscreen();
}
}
};
// 监听全屏状态变化
useEffect(() => {
const handleFullscreenChange = () => {
setIsFullscreen(
document.fullscreenElement === iframeContainerRef.current ||
document.webkitFullscreenElement === iframeContainerRef.current ||
document.mozFullScreenElement === iframeContainerRef.current ||
document.msFullscreenElement === iframeContainerRef.current
);
};
document.addEventListener('fullscreenchange', handleFullscreenChange);
document.addEventListener('webkitfullscreenchange', handleFullscreenChange);
document.addEventListener('mozfullscreenchange', handleFullscreenChange);
document.addEventListener('MSFullscreenChange', handleFullscreenChange);
return () => {
document.removeEventListener('fullscreenchange', handleFullscreenChange);
document.removeEventListener('webkitfullscreenchange', handleFullscreenChange);
document.removeEventListener('mozfullscreenchange', handleFullscreenChange);
document.removeEventListener('MSFullscreenChange', handleFullscreenChange);
};
}, []);
// 默认导师信息 - 魏立慧老师(用于求职策略定制页面)
const defaultTeacher = {
name: "魏立慧",
introduction: "企业资深一线HR专注于为求职者提供一对一的个性化指导。通过真实招聘视角,深入剖析个人优势与短板、传授面试技巧、规划职业定位与发展路径,帮助学生快速提升求职竞争力。求职策略以实用落地为核心,注重互动交流与角色定位,让学员在轻松氛围中获得直击痛点的求职策略。",
specialties: ["深谙用人逻辑", "擅长挖掘优势", "沟通真诚自然", "点评直击要害"],
avatar: "https://ddcz-1315997005.cos.ap-nanjing.myqcloud.com/static/img/teach_sys_teacher-avatar/recuUpSO4gUtJz.png",
name: "郭昊铭",
introduction: "负责多品牌并购整合的人事协同在100天内完成三家被并购公司的组织架构、薪酬福利与政策对齐上线统一ATS与人事主数据平台冗余岗位优化后管理费用同比下降12%关键岗位稳定率达95%整合期员工离职率较行业基准低7个百分点业务团队达产时间提前两个月。在企业实践之外作为资深一线HR导师也长期为求职者提供一对一的个性化指导,从真实招聘与并购整合的案例中剖析人才管理痛点,帮助学员理解岗位价值评估、简历匹配逻辑及面试应答技巧,结合职业定位与发展路径规划,让学员能够在轻松互动中掌握直击痛点的求职策略并快速提升竞争力。",
specialties: ["行业趋势洞察", "技能短板诊断", "求职思维训练", "模拟问答辅导"],
avatar: "https://ddcz-1315997005.cos.ap-nanjing.myqcloud.com/static/img/teach_sys_teacher-avatar/recuXLZZ9Q24PJ.png",
type: "企业资深HR"
};
@@ -102,20 +160,87 @@ export default ({ className = "", isLock = false, selectedCourse, teacherData, u
</div>
<div className="courses-video-player-video">
{selectedCourse ? (
/* 选中课程时显示模糊的海报图和锁定状态 */
/* 选中课程时如果是可试看课程则显示iframe否则显示锁定状态 */
selectedCourse.canPreview && selectedCourse.previewUrl ? (
/* 显示iframe内嵌课件 */
<div
ref={iframeContainerRef}
style={{
position: 'relative',
width: '100%',
height: '100%',
backgroundColor: '#000'
}}
>
<iframe
src={selectedCourse.previewUrl}
style={{
width: '100%',
height: '100%',
border: 'none',
borderRadius: isFullscreen ? '0' : '8px',
zoom: isFullscreen ? 1 : 0.5
}}
title={selectedCourse.courseName}
allowFullScreen
/>
{/* 全屏按钮 */}
<button
onClick={handleFullscreen}
style={{
position: 'absolute',
top: '16px',
right: '16px',
width: '40px',
height: '40px',
borderRadius: '8px',
border: 'none',
backgroundColor: 'rgba(0, 0, 0, 0.6)',
color: '#fff',
cursor: 'pointer',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
fontSize: '20px',
transition: 'all 0.3s',
zIndex: 10
}}
onMouseEnter={(e) => {
e.currentTarget.style.backgroundColor = 'rgba(0, 0, 0, 0.8)';
}}
onMouseLeave={(e) => {
e.currentTarget.style.backgroundColor = 'rgba(0, 0, 0, 0.6)';
}}
title={isFullscreen ? "退出全屏" : "全屏"}
>
{isFullscreen ? (
// 退出全屏图标
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
<path d="M8 3v3a2 2 0 0 1-2 2H3m18 0h-3a2 2 0 0 1-2-2V3m0 18v-3a2 2 0 0 1 2-2h3M3 16h3a2 2 0 0 1 2 2v3" />
</svg>
) : (
// 全屏图标
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
<path d="M8 3H5a2 2 0 0 0-2 2v3m18 0V5a2 2 0 0 0-2-2h-3m0 18h3a2 2 0 0 0 2-2v-3M3 16v3a2 2 0 0 0 2 2h3" />
</svg>
)}
</button>
</div>
) : (
/* 显示模糊的海报图和锁定状态 */
<div style={{ position: 'relative', width: '100%', height: '100%' }}>
<img
<img
src={(() => {
const posterUrl = selectedCourse?.unitPoster || unitPosters?.[unitName] || unitPosters?.["终生学习系统课"];
console.log('CoursesVideoPlayer 背景图片URL:', posterUrl);
console.log('selectedCourse.unitPoster:', selectedCourse?.unitPoster);
console.log('unitName:', unitName);
return posterUrl;
})()}
})()}
alt={unitName}
style={{
width: '100%',
height: '100%',
style={{
width: '100%',
height: '100%',
objectFit: 'cover',
filter: 'blur(10px)'
}}
@@ -130,7 +255,7 @@ export default ({ className = "", isLock = false, selectedCourse, teacherData, u
alignItems: 'center',
gap: '16px'
}}>
<img
<img
src="https://ddcz-1315997005.cos.ap-nanjing.myqcloud.com/static/img/teach_sys_icon/recuVOrz2GnJdK.png"
alt="lock"
style={{ width: '280px', height: '280px' }}
@@ -148,11 +273,12 @@ export default ({ className = "", isLock = false, selectedCourse, teacherData, u
</span>
</div>
</div>
)
) : (
/* 未选中课程时显示白底和logo */
<div style={{
width: '100%',
height: '100%',
<div style={{
width: '100%',
height: '100%',
backgroundColor: '#fff',
border: '1px solid #e5e6eb',
borderRadius: '8px',
@@ -162,11 +288,11 @@ export default ({ className = "", isLock = false, selectedCourse, teacherData, u
justifyContent: 'center',
gap: '24px'
}}>
<img
src={logoImg}
alt="logo"
style={{
width: '120px',
<img
src={logoImg}
alt="logo"
style={{
width: '120px',
height: 'auto',
opacity: 0.8
}}

View File

@@ -90,7 +90,7 @@ const HRVisitModal = ({ visible, onClose }) => {
id: 12,
name: '许凯先生',
company: '广东美味源香料股份有限公司',
avatar: 'https://ddcz-1315997005.cos.ap-nanjing.myqcloud.com/static/img/company_hr_avatar/recuWWiuWjGLzI.png',
avatar: 'https://ddcz-1315997005.cos.ap-nanjing.myqcloud.com/static/img/company_hr_avatar/recuWWiuWjGLzI.jpeg',
visitMessage: '访问了您的线下面试模拟'
},
{

View File

@@ -182,30 +182,26 @@ const PublicCourseList = forwardRef(({ className = "", onCourseClick }, ref) =>
onChange={(keys) => {
console.log('PublicCourseList Collapse onChange received:', keys, 'type:', typeof keys);
// Arco Collapse 在受控模式下,当点击时:
// - 如果是字符串,表示点击了某个面板,需要切换它的展开/收起状态
// - 如果是数组,表示新的展开状态
// 实现手风琴效果:只保留最后点击的单元展开
if (typeof keys === 'string') {
// 切换单个面板的展开/收起状态
setActiveKeys(prevKeys => {
const keyStr = String(keys);
const newKeys = [...prevKeys];
const index = newKeys.indexOf(keyStr);
if (index > -1) {
// 如果已展开,则收起
newKeys.splice(index, 1);
} else {
// 如果已收起,则展开
newKeys.push(keyStr);
}
console.log('Toggling key:', keyStr, 'New activeKeys:', newKeys);
return newKeys;
});
// 如果点击的是已经展开的单元,则收起它
if (activeKeys.includes(keys)) {
setActiveKeys([]);
} else {
// 否则展开新的单元,收起其它所有单元
setActiveKeys([keys]);
}
} else if (Array.isArray(keys)) {
// 直接设置新的展开状态
setActiveKeys(keys);
// 如果是数组,只保留最后一个元素(最新展开的)
if (keys.length > activeKeys.length) {
// 有新的面板被展开,找到新增的那个
const newKey = keys.find(key => !activeKeys.includes(key));
setActiveKeys(newKey ? [newKey] : keys.slice(-1));
} else {
// 有面板被收起
setActiveKeys(keys);
}
} else {
// 处理 undefined/null 的情况
setActiveKeys([]);
}
}}

View File

@@ -93,7 +93,7 @@ const Sidebar = ({ isCollapsed, setIsCollapsed }) => {
?.map((j) => (
<li
className={
location.pathname === j.path ||
location.pathname === j.path ||
(j.path === '/company-jobs' && location.pathname === '/company-jobs-list') ||
(j.path === '/job-strategy' && location.pathname === '/job-strategy-detail')
? "sidebar-menu-item-active sidebar-menu-item"
@@ -105,14 +105,33 @@ const Sidebar = ({ isCollapsed, setIsCollapsed }) => {
<IconFont
className="sidebar-menu-icon"
src={
location.pathname === j.path ||
location.pathname === j.path ||
(j.path === '/company-jobs' && location.pathname === '/company-jobs-list') ||
(j.path === '/job-strategy' && location.pathname === '/job-strategy-detail')
? j.active
? j.active
: j.default
}
/>
<span className="sidebar-menu-text">{j.name}</span>
<span className="sidebar-menu-text">
{j.name}
{j.path === '/duoduo-agent' && (
<span style={{
marginLeft: '8px',
padding: '2px 8px',
background: 'linear-gradient(135deg, #5DADE2 0%, #2874A6 100%)',
borderRadius: '12px',
color: '#ffffff',
fontSize: '12px',
fontWeight: 'bold',
fontStyle: 'italic',
letterSpacing: '1px',
textTransform: 'uppercase',
boxShadow: '0 2px 4px rgba(0,0,0,0.1)'
}}>
demo
</span>
)}
</span>
</li>
))}
</div>