feat: 添加AI课程集成和修复初始化错误

- 将终生学习系统课添加到公共课直播间
- 修复allCalendarEvents初始化顺序问题
- 更正AI课程导师为李奇
- 添加AI课程与日历页面同步功能
This commit is contained in:
KQL
2025-09-07 23:09:48 +08:00
parent 6337cc4812
commit 27f2339c9e
81 changed files with 41799 additions and 1654 deletions

View File

@@ -90,7 +90,8 @@ const CalendarHeader = ({
</button>
</div>
<div className="view-switcher">
{/* 视图切换器 - 暂时隐藏 */}
{/* <div className="view-switcher">
<button
className={`view-button ${currentView === 'month' ? 'active' : ''}`}
onClick={() => handleViewChange('month')}
@@ -103,7 +104,7 @@ const CalendarHeader = ({
>
</button>
</div>
</div> */}
</div>
);
};

View File

@@ -1,5 +1,6 @@
import { useEffect } from "react";
import Portal from "@/components/Portal";
import { IconCalendarClock, IconCheck, IconClockCircle } from "@arco-design/web-react/icon";
const EventDetailModal = ({ isOpen, event, onClose }) => {
// ESC键关闭模态框
@@ -25,11 +26,29 @@ const EventDetailModal = ({ isOpen, event, onClose }) => {
}
// 事件类型映射
const eventTypeNames = {
class: "课程",
meeting: "会议",
lab: "实验",
exam: "考试",
const eventTypeMap = {
'compound-skill': '复合技能课',
'vertical-skill': '垂直技能课',
'public-course': '公开课',
'one-on-one': '1v1规划',
'interview': '线下面试模拟',
'class': '课程',
'meeting': '会议',
'lab': '实验',
'exam': '考试',
};
// 事件类型颜色映射
const eventTypeColorMap = {
'compound-skill': { bg: '#667eea', light: '#e0e7ff' },
'vertical-skill': { bg: '#22c55e', light: '#dcfce7' },
'public-course': { bg: '#f59e0b', light: '#fef3c7' },
'one-on-one': { bg: '#ec4899', light: '#fce7f3' },
'interview': { bg: '#3b82f6', light: '#dbeafe' },
'class': { bg: '#667eea', light: '#e0e7ff' },
'meeting': { bg: '#f093fb', light: '#fae8ff' },
'lab': { bg: '#fa709a', light: '#fce7f3' },
'exam': { bg: '#ff6b6b', light: '#fee2e2' },
};
// 处理遮罩层点击
@@ -45,89 +64,129 @@ const EventDetailModal = ({ isOpen, event, onClose }) => {
};
// 格式化时间显示
const formatTimeRange = (startTime, endTime) => {
const startDate = new Date(startTime.replace(" ", "T"));
const endDate = new Date(endTime.replace(" ", "T"));
const formatTime = (date) => {
return date.toLocaleTimeString("zh-CN", {
hour: "2-digit",
minute: "2-digit",
hour12: false,
});
};
const formatDate = (date) => {
return date.toLocaleDateString("zh-CN", {
year: "numeric",
month: "long",
day: "numeric",
weekday: "long",
});
};
return {
date: formatDate(startDate),
timeRange: `${formatTime(startDate)} - ${formatTime(endDate)}`,
};
const formatTime = (timeStr) => {
if (!timeStr) return '';
// 处理多种格式: "2024-09-18 09:00:00" 或 "2024-09-18 09:00" 或 "09:00:00" 或 "09:00"
const parts = timeStr.toString().split(' ');
let time = '';
if (parts.length > 1) {
// 格式如 "2024-09-18 09:00:00" 或 "2024-09-18 09:00"
time = parts[1];
} else {
// 格式如 "09:00:00" 或 "09:00"
time = parts[0];
}
// 确保时间存在且格式正确
if (!time || time === 'undefined') {
return '00:00';
}
// 只显示小时和分钟 (前5个字符)
return time.substring(0, 5);
};
const { date, timeRange } = formatTimeRange(event.startTime, event.endTime);
// 格式化日期
const formatDate = (date) => {
if (!date) return '';
const options = { year: 'numeric', month: 'long', day: 'numeric', weekday: 'long' };
return date.toLocaleDateString('zh-CN', options);
};
// 判断是否为多事件模式(点击日期时显示当天所有事件)
const isMultiEventMode = event.events && Array.isArray(event.events);
const events = isMultiEventMode ? event.events : [event];
const displayDate = isMultiEventMode ? formatDate(event.date) : '';
// 获取事件状态 - 所有事项都显示为已完成
const getEventStatus = (eventItem) => {
return { text: '已完成', icon: <IconCheck />, color: '#52c41a' };
};
return (
<Portal className="event-detail-portal">
<div className="event-detail-overlay" onClick={handleOverlayClick}>
<div className="event-detail-modal">
<div className="event-detail-modal-new">
{/* 模态框头部 */}
<div className="event-detail-header">
<h3 className="event-detail-title">事件详情</h3>
<div className="event-detail-header-new">
<h3 className="event-detail-title-new">
{isMultiEventMode ? '日程详情' : '事件详情'}
</h3>
<button
className="event-detail-close"
className="event-detail-close-new"
onClick={handleCloseClick}
type="button"
aria-label="关闭"
>
×
<svg width="24" height="24" viewBox="0 0 24 24" fill="none">
<path d="M18 6L6 18M6 6l12 12" stroke="currentColor" strokeWidth="2" strokeLinecap="round"/>
</svg>
</button>
</div>
{/* 日期显示(多事件模式) */}
{isMultiEventMode && (
<div className="event-detail-date-header">
<IconCalendarClock style={{ fontSize: 20, color: '#3b82f6' }} />
<span>{displayDate}</span>
</div>
)}
{/* 模态框内容 */}
<div className="event-detail-content">
{/* 事件标题 */}
<div className="event-detail-field">
<div className="event-detail-label">事件标题</div>
<div className="event-detail-value">{event.title}</div>
</div>
{/* 事件类型 */}
<div className="event-detail-field">
<div className="event-detail-label">事件类型</div>
<div className="event-detail-value">
<span className={`event-type-badge event-type-${event.type}`}>
{eventTypeNames[event.type] || event.type}
</span>
<div className="event-detail-content-new">
{events.length === 0 ? (
<div className="event-empty-state">
<IconCalendarClock style={{ fontSize: 48, color: '#c3c5c9', marginBottom: 16 }} />
<p style={{ fontSize: 16, color: '#86909c', margin: 0 }}>当日无事项</p>
</div>
) : (
<div className="event-list-container">
{events.map((eventItem, index) => {
const status = getEventStatus(eventItem);
const typeColor = eventTypeColorMap[eventItem.type] || { bg: '#667eea', light: '#e0e7ff' };
return (
<div key={eventItem.id || index} className="event-card-new">
<div className="event-card-header">
<div className="event-type-indicator" style={{ backgroundColor: typeColor.bg }}></div>
<div className="event-card-title">
<h4>{eventItem.title}</h4>
<span className="event-type-tag" style={{
backgroundColor: typeColor.light,
color: typeColor.bg
}}>
{eventTypeMap[eventItem.type] || eventItem.type}
</span>
</div>
</div>
<div className="event-card-body">
<div className="event-info-row">
<IconClockCircle style={{ fontSize: 16, color: '#8c8c8c' }} />
<span className="event-time">
{formatTime(eventItem.startTime)} - {formatTime(eventItem.endTime)}
</span>
</div>
<div className="event-info-row">
<div className="event-status" style={{ color: status.color }}>
{status.icon}
<span>{status.text}</span>
</div>
</div>
{eventItem.description && (
<div className="event-description">
{eventItem.description}
</div>
)}
</div>
</div>
);
})}
</div>
{/* 日期 */}
<div className="event-detail-field">
<div className="event-detail-label">日期</div>
<div className="event-detail-value">{date}</div>
</div>
{/* 时间 */}
<div className="event-detail-field">
<div className="event-detail-label">时间</div>
<div className="event-detail-value">
<div className="event-time-range">{timeRange}</div>
</div>
</div>
{/* 详细描述 */}
{event.description && (
<div className="event-detail-field">
<div className="event-detail-label">详细描述</div>
<div className="event-detail-value">{event.description}</div>
</div>
)}
</div>
</div>
@@ -136,4 +195,4 @@ const EventDetailModal = ({ isOpen, event, onClose }) => {
);
};
export default EventDetailModal;
export default EventDetailModal;

View File

@@ -26,10 +26,11 @@ const MonthView = ({
});
};
const handleDateClick = (day) => {
const handleDateClick = (day, dayEvents) => {
// 所有日期都可以点击
if (onDateClick) {
const clickedDate = new Date(day.year, day.month, day.date);
onDateClick(clickedDate);
onDateClick(clickedDate, dayEvents || []);
}
};
@@ -106,7 +107,7 @@ const MonthView = ({
className={`day-cell ${!isCurrentMonth ? "other-month" : ""} ${
isToday ? "today" : ""
} ${isSelectedDate ? "selected" : ""}`}
onClick={() => handleDateClick(day)}
onClick={() => handleDateClick(day, dayEvents)}
>
<div className="day-number">{day.date}</div>

View File

@@ -153,11 +153,14 @@
flex-direction: column;
cursor: pointer;
transition: all 0.3s ease;
min-height: 90px;
min-height: 100px;
max-height: 100px;
height: 100px;
position: relative;
border-radius: 10px;
border: 1px solid #e8e8f0;
margin: 3px;
overflow: hidden;
}
.day-cell:hover {
@@ -185,6 +188,8 @@
box-shadow: 0 4px 12px rgba(37, 99, 235, 0.15);
}
/* 删除no-events样式限制允许所有日期都可点击 */
.day-number {
font-size: 14px;
font-weight: 500;
@@ -216,16 +221,18 @@
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
font-size: 11px;
padding: 4px 8px;
border-radius: 6px;
padding: 3px 6px;
border-radius: 4px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
cursor: pointer;
transition: all 0.3s ease;
margin: 2px 0;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
margin: 1px 0;
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
font-weight: 500;
max-width: 100%;
display: block;
}
.event-item:hover {
@@ -233,6 +240,32 @@
box-shadow: 0 3px 8px rgba(0, 0, 0, 0.15);
}
/* 复合技能课 */
.event-item.compound-skill {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
}
/* 垂直技能课 */
.event-item.vertical-skill {
background: linear-gradient(135deg, #4ade80 0%, #22c55e 100%);
}
/* 公开课 */
.event-item.public-course {
background: linear-gradient(135deg, #f59e0b 0%, #fbbf24 100%);
}
/* 1v1规划 */
.event-item.one-on-one {
background: linear-gradient(135deg, #ec4899 0%, #f472b6 100%);
}
/* 线下面试模拟 */
.event-item.interview {
background: linear-gradient(135deg, #06b6d4 0%, #3b82f6 100%);
}
/* 默认类型保留 */
.event-item.class {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
}
@@ -499,7 +532,7 @@
}
/* 事件详情模态框样式 - 遵循跨国企业级后台系统设计哲学v2.0 */
/* 事件详情模态框样式 - 新设计 */
/* 模态框遮罩层 */
.event-detail-overlay {
@@ -508,12 +541,13 @@
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.5);
background: rgba(0, 0, 0, 0.45);
z-index: var(--z-modal);
display: flex;
align-items: center;
justify-content: center;
animation: overlayFadeIn 200ms ease-out;
backdrop-filter: blur(4px);
}
@keyframes overlayFadeIn {
@@ -525,22 +559,23 @@
}
}
/* 模态框主体 - 零线原则,依赖留白分隔 */
.event-detail-modal {
/* 模态框主体 */
.event-detail-modal-new {
background: #ffffff;
border-radius: 8px;
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.15), 0 4px 6px rgba(0, 0, 0, 0.1);
width: 480px;
border-radius: 12px;
box-shadow: 0 24px 48px rgba(0, 0, 0, 0.12), 0 8px 16px rgba(0, 0, 0, 0.08);
width: 520px;
max-width: 90vw;
max-height: 80vh;
overflow: auto;
animation: modalSlideIn 250ms ease-out;
max-height: 75vh;
display: flex;
flex-direction: column;
animation: modalSlideIn 250ms cubic-bezier(0.4, 0, 0.2, 1);
}
@keyframes modalSlideIn {
from {
opacity: 0;
transform: scale(0.95) translateY(-10px);
transform: scale(0.96) translateY(-20px);
}
to {
opacity: 1;
@@ -548,122 +583,201 @@
}
}
/* 模态框头部 - 呼吸感间距 */
.event-detail-header {
padding: 24px 24px 16px 24px;
border-bottom: 1px solid #f3f4f6;
/* 模态框头部 */
.event-detail-header-new {
padding: 20px 24px;
border-bottom: 1px solid #e5e7eb;
display: flex;
justify-content: space-between;
align-items: center;
flex-shrink: 0;
}
/* 标题 - 意图驱动的信息层级 L1 */
.event-detail-title {
/* 标题样式 */
.event-detail-title-new {
font-size: 18px;
font-weight: 600;
color: #111827;
color: #1f2937;
margin: 0;
letter-spacing: -0.01em;
}
/* 关闭按钮 - 上下文感知交互 */
.event-detail-close {
width: 32px;
height: 32px;
/* 关闭按钮 */
.event-detail-close-new {
width: 36px;
height: 36px;
border: none;
background: #f9fafb;
border-radius: 6px;
background: transparent;
border-radius: 8px;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
color: #6b7280;
font-size: 18px;
transition: all 150ms ease;
}
.event-detail-close:hover {
.event-detail-close-new:hover {
background: #f3f4f6;
color: #374151;
}
.event-detail-close:focus {
outline: 2px solid #3b82f6;
outline-offset: 2px;
.event-detail-close-new:active {
background: #e5e7eb;
}
/* 模态框内容区域 - 慷慨的留白 */
.event-detail-content {
padding: 24px;
}
/* 字段容器 - 基于8px栅格的间距 */
.event-detail-field {
margin-bottom: 24px;
}
.event-detail-field:last-child {
margin-bottom: 0;
}
/* 字段标签 - 信息层级 L3 */
.event-detail-label {
font-size: 12px;
font-weight: 600;
color: #6b7280;
text-transform: uppercase;
letter-spacing: 0.05em;
margin-bottom: 8px;
}
/* 字段值 - 信息层级 L2 */
.event-detail-value {
/* 日期头部(多事件模式) */
.event-detail-date-header {
padding: 16px 24px;
background: linear-gradient(to right, #f0f9ff, #e0f2fe);
border-bottom: 1px solid #e0e7ff;
display: flex;
align-items: center;
gap: 12px;
font-size: 15px;
color: #111827;
line-height: 1.5;
}
/* 事件类型徽章 - 语义化状态色板 */
.event-type-badge {
display: inline-block;
padding: 6px 12px;
border-radius: 12px;
font-size: 11px;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.05em;
}
/* 克制的色彩语言 - 柔和且低饱和度 */
.event-type-class {
background: #dbeafe;
color: #1e40af;
}
.event-type-meeting {
background: #d1fae5;
color: #065f46;
}
.event-type-lab {
background: #fef3c7;
color: #92400e;
}
.event-type-exam {
background: #fee2e2;
color: #991b1b;
}
/* 时间范围显示 - 等宽字体增强可读性 */
.event-time-range {
font-family: "SF Mono", "Monaco", "Cascadia Code", "Consolas", monospace;
font-size: 14px;
color: #374151;
background: #f9fafb;
padding: 12px 16px;
border-radius: 6px;
border-left: 3px solid #3b82f6;
font-weight: 500;
color: #1e40af;
flex-shrink: 0;
}
/* 新内容区域 */
.event-detail-content-new {
flex: 1;
overflow-y: auto;
padding: 20px 24px;
}
/* 事件列表容器 */
.event-list-container {
display: flex;
flex-direction: column;
gap: 16px;
}
/* 新事件卡片 */
.event-card-new {
background: #fafbfc;
border: 1px solid #e5e7eb;
border-radius: 10px;
padding: 16px;
transition: all 150ms ease;
}
.event-card-new:hover {
background: #f9fafb;
border-color: #d1d5db;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.04);
}
/* 事件卡片头部 */
.event-card-header {
display: flex;
gap: 12px;
margin-bottom: 12px;
}
/* 事件类型指示器 */
.event-type-indicator {
width: 4px;
border-radius: 2px;
flex-shrink: 0;
}
/* 事件卡片标题区 */
.event-card-title {
flex: 1;
display: flex;
justify-content: space-between;
align-items: flex-start;
gap: 12px;
}
.event-card-title h4 {
margin: 0;
font-size: 16px;
font-weight: 600;
color: #111827;
line-height: 1.4;
}
/* 事件类型标签 */
.event-type-tag {
padding: 4px 10px;
border-radius: 6px;
font-size: 12px;
font-weight: 500;
white-space: nowrap;
flex-shrink: 0;
}
/* 事件卡片内容 */
.event-card-body {
padding-left: 16px;
display: flex;
flex-direction: column;
gap: 10px;
}
/* 事件信息行 */
.event-info-row {
display: flex;
align-items: center;
gap: 8px;
}
/* 事件时间 */
.event-time {
font-size: 14px;
color: #6b7280;
font-family: "SF Mono", "Monaco", "Cascadia Code", monospace;
}
/* 事件状态 */
.event-status {
display: flex;
align-items: center;
gap: 6px;
font-size: 13px;
font-weight: 500;
}
/* 事件描述 */
.event-description {
font-size: 14px;
color: #4b5563;
line-height: 1.5;
padding: 12px;
background: white;
border-radius: 6px;
border-left: 3px solid #e5e7eb;
margin-top: 4px;
}
/* 更多事件指示器 */
.event-more {
font-size: 11px;
color: #6b7280;
background: #f3f4f6;
padding: 2px 6px;
border-radius: 4px;
text-align: center;
cursor: pointer;
transition: all 150ms ease;
}
.event-more:hover {
background: #e5e7eb;
color: #374151;
}
/* 无事项状态 */
.event-empty-state {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 60px 20px;
text-align: center;
}
/* 响应式设计 */
@@ -764,19 +878,47 @@
}
}
/* 响应式设计 */
/* 响应式设计 - 移动端适配 */
@media (max-width: 640px) {
.event-detail-modal {
.event-detail-modal-new {
width: 95vw;
max-height: 85vh;
margin: 16px;
}
.event-detail-header,
.event-detail-content {
.event-detail-header-new,
.event-detail-content-new {
padding: 16px;
}
.event-detail-field {
margin-bottom: 16px;
.event-detail-date-header {
padding: 12px 16px;
font-size: 14px;
}
.event-card-new {
padding: 12px;
}
.event-card-title h4 {
font-size: 15px;
}
.event-type-tag {
font-size: 11px;
padding: 3px 8px;
}
.event-time {
font-size: 13px;
}
.event-status {
font-size: 12px;
}
.event-description {
font-size: 13px;
padding: 10px;
}
}

View File

@@ -23,14 +23,14 @@ const CalendarPage = () => {
setCurrentDate(newDate);
};
const handleDateClick = (date) => {
const handleDateClick = (date, events) => {
setSelectedDate(date);
// 如果在月视图中点击日期,可以切换到周视图
if (currentView === "month") {
setCurrentDate(date);
// 可选:自动切换到周视图
// setCurrentView('week');
// 如果有事件,显示事件详情弹窗
if (events && events.length > 0) {
// 设置选中的事件为当天的所有事件
setSelectedEvent({ date, events });
setShowEventDetail(true);
}
};

View File

@@ -29,15 +29,26 @@ export default ({ visible, onClose, data }) => {
if (studentInfo) {
// 处理教育经历
resumeData.educational_experience = studentInfo.educational_experience || ["相关专业大学 本科"];
if (studentInfo.education) {
resumeData.educational_experience = [
`${studentInfo.education.university || '苏州信息职业技术学院'} ${studentInfo.education.period || '2020.9 - 2023.6'}`
];
} else {
resumeData.educational_experience = studentInfo.educational_experience || ["相关专业大学 本科"];
}
// 处理项目经历
if (studentInfo.project_experience) {
// 处理项目经历 - 支持新格式
if (studentInfo.projectExperience) {
// 新格式projectExperience是字符串
resumeData.project_experience = [{
name: "项目经历",
description: studentInfo.projectExperience
}];
} else if (studentInfo.project_experience) {
// 旧格式兼容
if (Array.isArray(studentInfo.project_experience)) {
// 如果已经是数组格式
resumeData.project_experience = studentInfo.project_experience;
} else if (typeof studentInfo.project_experience === 'object') {
// 如果是对象格式,转换为数组
const proj = studentInfo.project_experience;
resumeData.project_experience = [
{
@@ -48,10 +59,18 @@ export default ({ visible, onClose, data }) => {
}
}
// 处理核心技能和复合技能
resumeData.core_skills = studentInfo.core_skills || [];
resumeData.compound_skills = studentInfo.compound_skills || [];
resumeData.personal_summary = studentInfo.personal_summary || "具有扎实的专业基础和实践经验";
// 处理专业技能 - 支持新格式
if (studentInfo.skills) {
// 新格式skills是字符串
resumeData.skills_text = studentInfo.skills;
} else {
// 旧格式兼容
resumeData.core_skills = studentInfo.core_skills || [];
resumeData.compound_skills = studentInfo.compound_skills || [];
}
// 处理个人总结 - 支持新格式
resumeData.personal_summary = studentInfo.personalSummary || studentInfo.personal_summary || "具有扎实的专业基础和实践经验";
}
return (
@@ -107,32 +126,38 @@ export default ({ visible, onClose, data }) => {
{/* 专业技能 */}
<li className="resume-info-moda-item">
<p className="resume-info-moda-item-title">专业技能</p>
<ul className="professional-skills-list">
{resumeData.core_skills && resumeData.core_skills.length > 0 && (
<li className="professional-skills-list-item">
<p className="skill-name">核心能力</p>
<div className="core-capabilities-list">
{resumeData.core_skills.map((skill, index) => (
<p key={index} className="core-capabilities-list-item">
{skill}
</p>
))}
</div>
</li>
)}
{resumeData.compound_skills && resumeData.compound_skills.length > 0 && (
<li className="professional-skills-list-item">
<p className="skill-name">复合能力</p>
<div className="core-capabilities-list">
{resumeData.compound_skills.map((skill, index) => (
<p key={index} className="core-capabilities-list-item">
{skill}
</p>
))}
</div>
</li>
)}
</ul>
{resumeData.skills_text ? (
<div className="professional-skills-content" style={{ whiteSpace: 'pre-wrap', lineHeight: '1.8', padding: '0 20px' }}>
{resumeData.skills_text}
</div>
) : (
<ul className="professional-skills-list">
{resumeData.core_skills && resumeData.core_skills.length > 0 && (
<li className="professional-skills-list-item">
<p className="skill-name">核心能力</p>
<div className="core-capabilities-list">
{resumeData.core_skills.map((skill, index) => (
<p key={index} className="core-capabilities-list-item">
{skill}
</p>
))}
</div>
</li>
)}
{resumeData.compound_skills && resumeData.compound_skills.length > 0 && (
<li className="professional-skills-list-item">
<p className="skill-name">复合能力</p>
<div className="core-capabilities-list">
{resumeData.compound_skills.map((skill, index) => (
<p key={index} className="core-capabilities-list-item">
{skill}
</p>
))}
</div>
</li>
)}
</ul>
)}
</li>
{/* 个人总结 */}
{resumeData.personal_summary && resumeData.personal_summary.trim() !== '' && (

View File

@@ -24,9 +24,9 @@
.interview-rating-header-title {
cursor: default;
font-size: 12px;
font-size: 22px;
font-weight: 600;
color: #000;
color: #1d2129;
}
}
@@ -64,11 +64,6 @@
}
/* 面试评分图表区域 */
.interview-rating-header {
justify-content: flex-start;
}
.interview-evaluation-charts-wrapper {
width: 100%;
box-sizing: border-box;
@@ -77,93 +72,164 @@
border-radius: 8px;
flex-shrink: 0;
.charts-content {
width: 100%;
display: flex;
justify-content: flex-start;
align-items: center;
flex-direction: column;
.interview-rating-header {
justify-content: flex-start;
}
.charts-content-top {
width: 100%;
height: 255px;
box-sizing: border-box;
border-bottom: 1px dashed #e5e6eb;
.charts-content {
width: 100%;
display: flex;
justify-content: flex-start;
align-items: center;
flex-direction: column;
.charts-content-top {
width: 100%;
height: 262px;
box-sizing: border-box;
display: flex;
justify-content: space-around;
align-items: center;
background-color: #f4f7f9;
border-radius: 12px;
margin-bottom: 10px;
.score-chart {
width: 402px;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
position: relative;
&::after {
content: "";
width: 340px;
height: 170px;
position: absolute;
bottom: 55px;
background-image: url("@/assets/images/InterviewSimulationPage/chart_bg.png");
background-size: 100% 100%;
}
}
.score-info {
width: 402px;
height: 100%;
display: flex;
justify-content: space-around;
align-items: center;
flex-direction: column;
.score-chart {
width: 220px;
height: 220px;
}
.score-info {
width: 300px;
height: 210px;
.score-info-item {
width: 354px;
height: 99px;
border-radius: 12px;
display: flex;
justify-content: space-around;
align-items: center;
justify-content: flex-start;
align-items: flex-start;
flex-direction: column;
box-sizing: border-box;
padding: 20px 10px 20px 20px;
background-color: #fff;
.score-info-item {
width: 300px;
height: 80px;
border-radius: 8px;
display: flex;
justify-content: space-between;
align-items: center;
.score-info-item-title {
width: 100%;
height: 24px;
line-height: 24px;
color: #1d2129;
font-size: 16px;
font-weight: 400;
position: relative;
box-sizing: border-box;
padding: 16px;
padding-left: 24px;
.score-info-item-title {
color: #333333;
font-size: 16px;
font-weight: 400;
&::before {
content: "";
width: 24px;
height: 24px;
position: absolute;
left: 0;
top: 50%;
transform: translateY(-50%);
background-image: url("https://ddcz-1315997005.cos.ap-nanjing.myqcloud.com/static/img/teach_sys_icon/recuW0XRVB1bpV.png");
background-size: 100% 100%;
}
.score-info-item-value {
font-size: 34px;
font-weight: 800;
position: absolute;
right: 0px;
font-size: 24px;
font-weight: 700;
}
}
.score-info-item.item1 {
border: 1px solid #7fc6ff;
background-image: linear-gradient(to right, #f1f9ff, #ffffff);
.score-info-item-value {
color: #2c7aff;
.score-info-line {
margin-top: 20px;
width: 299px;
height: 12px;
background-color: #f4f7f9;
border-radius: 12px;
position: relative;
> i {
position: absolute;
left: 0;
top: 0;
height: 12px;
border-radius: 12px;
}
}
.score-info-item.item2 {
border: 1px solid #00b42a80;
background-image: linear-gradient(
to right,
rgba(255, 255, 255, 1),
rgba(35, 195, 67, 0.1)
);
.score-info-item-value {
color: #009a29;
.score-info-line-total-value {
color: #86909c;
font-size: 12px;
font-weight: 500;
position: absolute;
right: -25px;
top: 50%;
transform: translateY(-50%);
}
}
}
}
.charts-content-bottom {
width: 100%;
height: 330px;
display: flex;
justify-content: space-between;
align-items: center;
.radar-chart {
width: 50%;
height: 100%;
.score-info-item.item1 {
.score-info-item-value {
color: #2c7aff;
}
.score-info-line {
> i {
background-image: linear-gradient(to right, #2d7fff, #38aaff);
}
}
}
.score-info-item.item2 {
.score-info-item-value {
color: #25c343;
}
.score-info-line {
> i {
background-image: linear-gradient(to right, #2dc546, #8de46c);
}
}
}
}
}
.charts-content-bottom {
width: 100%;
height: 360px;
display: flex;
justify-content: space-between;
align-items: center;
.radar-chart {
width: 394px;
height: 100%;
background-color: #f4f7f9;
border-radius: 12px;
}
}
}
}
.interview-evaluation-text-wrapper {
width: 100%;
@@ -185,10 +251,44 @@
box-sizing: border-box;
padding: 16px;
color: #1d2129;
font-size: 16px;
font-size: 14px;
font-weight: 400;
line-height: 1.6;
white-space: pre-wrap;
h1 {
font-size: 18px;
font-weight: 600;
margin: 16px 0 12px 0;
color: #1d2129;
}
h2 {
font-size: 16px;
font-weight: 600;
margin: 14px 0 10px 0;
color: #1d2129;
}
p {
margin: 8px 0;
line-height: 1.8;
text-indent: 2em;
}
ol, ul {
margin: 8px 0;
padding-left: 24px;
li {
margin: 6px 0;
line-height: 1.8;
text-indent: 2em;
}
}
strong {
font-weight: 600;
}
}
}
}

View File

@@ -1,4 +1,5 @@
import { useState, useRef, useEffect } from "react";
import ReactMarkdown from "react-markdown";
import ScoreChart from "../ScoreChart";
import RadarChart from "../RadarChart";
import "./index.css";
@@ -8,7 +9,7 @@ export default ({ selectedItem = "求职面试初体验" }) => {
const getVideoUrl = () => {
switch(selectedItem) {
case "求职面试初体验":
return "https://ddcz-1315997005.cos.ap-nanjing.myqcloud.com/static/video/teach_sys/interview_offline_vedio/recuUpJSOKoqAm.mov";
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";
case "第一次线下面试模拟":
@@ -33,61 +34,143 @@ export default ({ selectedItem = "求职面试初体验" }) => {
const getEvaluationData = () => {
if (selectedItem === "未来的自己") {
return {
totalScore: 92,
professionalScore: 39.5,
performanceScore: 55.0,
radarData: [10, 9, 10, 9],
title: "优秀表现评价",
content: `在专业能力方面,候选人对相关专业知识掌握扎实,能够深入且准确地回答与项目经验相关的复杂问题。在描述具体项目时,不仅能清晰说明自己的职责和成果,还能主动分析项目中的技术难点和解决方案,体现出优秀的实践操作能力和技术思维。对行业前沿动态有深入了解,能够结合实际工作提出创新性见解。
totalScore: 97, // 根据JSON计算的真实总分
professionalScore: 57, // (9+10+10+9+10+9)/6*10*0.6 = 57
performanceScore: 40, // (10+10+10+10)/4*10*0.4 = 40
radarData: [9, 10, 10, 9, 10, 9], // 六项专业能力指标来自JSON
radarData2: [10, 10, 10, 10], // 四项现场表现指标来自JSON
title: "面试评价",
content: `# 专业能力
沟通表达上,候选人语言表达流畅自然,逻辑思维清晰有条理,能够积极主动地与面试官互动。在面对开放性问题时展现出良好的思维发散能力,回答层次分明,重点突出,表现出强烈的学习意愿和职业发展规划意识。整体表现超出预期,具备优秀的职场素养。`
1. 专业知识学得特别扎实,讲核心概念的时候又准又清楚,还能随手举例子把道理说透,不光能把知识点讲明白,还能用实例帮人理解,一看就是专业底子特别厚,也很会用学到的知识;
2. 对行业里的产业链、还有未来的发展趋势摸得特别透,不光能说清产业链各个环节是怎么连起来的,还能具体讲明白这些趋势会影响到岗位工作、业务方向,聊到行业相关的话题,总能说出有想法的观点,跟行业里的人交流完全没障碍;
3. 对企业从头到尾的工作流程门儿清,知道业务怎么推进、关键环节在哪,还有跟其他部门怎么配合,既能说清自己进了企业要干些啥,也能找准自己在流程里的位置,跟不同部门合作的关键点也拎得很清,跟实际工作场景特别适配;
4. 碰到具体任务或问题,拆解得特别有条理,分析思路也很系统,能从实际需要出发理出解决办法,想的方案既靠谱能落地,又有新点子,还能用具体数据说明能达到啥效果,不管是想办法还是实际操作,都做得挺到位;
5. 对目标岗位的工作职责、要干的活、需要啥能力都摸得很清楚,聊职业规划和自己跟岗位合不合适的时候,能精准说清自己能发挥啥作用,还能把自己的优势、经历跟岗位需求、业务目标绑在一起说,一看就跟岗位特别对路;
6. 做过的项目又多又完整,说项目的时候能讲清背景、自己具体干了啥、负责哪些环节,还能好好总结从里面学到了啥、能力咋提升的,通过这些项目,能实实在在看出专业能力和动手做事的本事;
# 现场表现力
1. 说话表达能力特别好,嘴皮子利索还挺有劲儿,说事儿的时候结构很清楚,每个信息都能说到点子上,既能让人准确 get 到核心观点,又能高效把关键信息传出去,沟通起来又快又准;
2. 心态特别稳,不管是被提问、有工作压力,还是碰到突发情况,一直都很自信、不慌不乱,思路也很清晰,既能把工作或回答做好,还能巧妙化解压力,扛事儿能力和临场应变都挺强;
3. 跟人交流、做展示的时候,姿态特别专业,眼神交流自然又真诚,手势动作跟说话配合得刚好,既显得得体专业,又能通过动作让自己的观点更有说服力,让人觉得特别靠谱;
4. 对时间和流程把控得特别好,不管是做事、聊天还是做展示,每个环节的时间都掐得准,环节之间过渡也很顺,还会留时间做总结,能保证整个过程顺畅推进,干活、沟通的效率都很高。
# 综合评价
总的来说,这学生在专业基础、行业认知、懂企业流程、解决问题、适配岗位、项目经验、说话表达、心态调整、职业仪态还有时间管理这些关键方面,都表现得特别优秀,综合能力和职业素养都很突出。这些优点不光能让专业交流更顺畅、解决问题更合理,还能看出他特别有职业潜力,跟岗位也特别匹配,是个很有发展前途的好苗子。`
};
} else if (selectedItem === "第一次线下面试模拟") {
return {
totalScore: 72,
professionalScore: 28.0,
performanceScore: 44.0,
radarData: [7, 6, 7, 7],
radarData2: [6, 6, 5, 6],
title: "初次模拟评价",
content: `专业能力方面,候选人对基础知识有一定了解,但在深入问题上表现不够自信。回答问题时思路基本清晰,但缺乏具体案例支撑。对行业动态的了解较为表面,需要加强专业知识的深度学习。在描述项目经验时,表达略显生疏,未能充分展现个人价值。
totalScore: 42, // 根据JSON计算的真实总分
professionalScore: 28, // (4+6+5+4+6+3)/6*10*0.6 = 28
performanceScore: 14, // (2+4+5+3)/4*10*0.4 = 14
radarData: [4, 6, 5, 4, 6, 3], // 六项专业能力指标来自JSON
radarData2: [2, 4, 5, 3], // 四项现场表现指标来自JSON
title: "面试评价",
content: `# 专业能力
沟通表达上,候选人表现出一定的紧张情绪,语速较快,有时会出现词不达意的情况。逻辑性有待加强,回答问题时容易偏离主题。肢体语言不够自然,眼神交流不足。建议多进行面试练习,提升表达的流畅度和自信心。整体来看,还有较大的提升空间。`
1. 对知识点的概念总弄混,回答问题也只停在表面,没法往深了说 —— 比如问个核心概念,要么跟别的概念搅在一起,要么就说几句皮毛,根本挖不出背后的门道,能看出来对知识的理解还差得远;
2. 知道的行业知识都是零零散散的,没形成系统,尤其说不明白行业趋势跟岗位、业务的关系 —— 比如问某个趋势会影响工作内容不,他就答不上来,对行业的认知特别散,没串起来;
3. 对工作流程的概念特别模糊,连自己该干啥都搞不清 —— 比如问企业里某个业务流程怎么走,他说不明白,再问他在里面要承担啥角色,更是没头绪,完全没找准自己的位置;
4. 分析问题的时候特别局限,想的方案也很片面,连怎么落地的步骤都没有 —— 比如让他给个解决办法,只能说个大概方向,至于需要哪些资源、分几步做、怎么推进,根本没考虑,这样的方案根本没法用;
5. 对目标岗位的认知特别模糊,连岗位的核心工作是啥、该干到啥程度、哪些活不归自己管,都弄不明白 —— 问他这个岗位主要负责啥,他说的颠三倒四,工作边界更是完全没概念,明显没搞懂岗位到底是干啥的;
6. 做过的项目特别少,就算有一两个,也说不明白情况 —— 要么讲不清项目是做什么的,要么说不出自己在里面具体干了啥,连自己到底是啥角色都模模糊糊,根本没法用项目证明自己有能力;
# 现场表现力
1. 说话特别散乱,抓不住重点,逻辑还老跳 —— 比如跟他聊个事儿,他东说一句西说一句,关键信息没几句,还经常突然从一个话题跳到另一个,听的人根本跟不上,半天搞不清他想表达啥;
2. 情绪波动特别大,一会儿好一会儿坏,特别影响沟通 —— 可能刚开始聊得好好的,稍微有点问题就慌了,或者没耐心了,跟他交流的时候,很容易因为他的情绪受影响,沟通效果特别差;
3. 跟人说话或者坐着的时候,小动作特别多,坐姿也不稳 —— 一会儿摸笔、一会儿挠头,身子还总晃来晃去,这些动作特别容易分散别人的注意力,让人没法专心听他说话,印象分也会打折扣;
4. 不管是做事、做展示还是跟人聊天,时间把控得特别差 —— 要么说起来没个完,严重超时;要么没说几句就结束了,整个过程一点条理都没有,结构乱得很,完全没规划。
# 综合评价
总的来说,这学生在知识理解、行业认知、流程和岗位把握、方案设计、项目经验、表达逻辑,还有情绪管理、行为仪态、时间把控这些方面,都有挺明显的问题。这些问题不光让日常沟通和解决问题受影响,也能看出来他现在还不太能适应实际工作的要求,之后得重点补补知识的深度、多了解行业和岗位,再好好练练说话的逻辑和心态,慢慢把综合能力提上来才行。`
};
} else if (selectedItem === "第二次线下面试模拟") {
return {
totalScore: 81,
professionalScore: 32.5,
performanceScore: 48.5,
radarData: [8, 7, 8, 8],
radarData2: [7, 7, 6, 7],
title: "进步明显评价",
content: `专业能力方面,候选人相比第一次有明显进步,对专业知识的理解更加深入。能够结合实际案例阐述观点,展现出一定的实践经验。对于技术问题的回答更有条理,能够抓住问题的关键点。行业认知有所提升,能够说出一些前沿趋势。
totalScore: 67, // 根据JSON计算的真实总分
professionalScore: 41, // (7+7+6+6+7+8)/6*10*0.6 = 41
performanceScore: 26, // (8+7+6+5)/4*10*0.4 = 26
radarData: [7, 7, 6, 6, 7, 8], // 六项专业能力指标来自JSON
radarData2: [8, 7, 6, 5], // 四项现场表现指标来自JSON
title: "面试评价",
content: `# 专业能力
沟通表达上,紧张情绪明显缓解,语速适中,表达更加清晰。逻辑结构有所改善,能够按照"总-分-总"的结构组织答案。肢体语言更加自然,与面试官的互动增多。自信心有所提升,敢于表达自己的观点。但在一些压力问题上仍需加强应对能力。`
1. 关键知识掌握得挺全面的,大部分内容都能抓准,就是偶尔在小细节上有点马虎,比如个别知识点的细微区别会记混,但整体来看,知识的准确性还是不错的,核心内容都能掌握到位;
2. 对市场上的主要动态有了解,比如行业里近期的热门方向、大家关注的重点,都能说出个大概,而且能简单讲讲这些动态对业务开展有啥意义,虽然说得不算深入,但至少能把 "动态和业务" 的关联点到,有基本的认知;
3. 明白工作的主要流程是啥,比如一项业务从开始到结束要经过哪些关键步骤,都能说清楚,也知道自己在流程里负责哪个环节、要干些啥,但在细节上就有点粗糙了,比如环节之间怎么衔接、遇到小问题该怎么处理,就说不太细;
4. 面对问题或者任务时,能有个初步的思路雏形,比如想解决某个问题,能先提出一两个大概的方向,但思路不够系统,没有把 "为什么这么做、步骤是啥、需要啥支持" 串成完整的逻辑,论证的时候也缺乏足够的理由或者例子支撑,说服力还差了点;
5. 知道目标岗位的主要任务是啥,能说出大概的工作范围,比如日常要处理哪些事、负责哪些板块,但没法深入剖析岗位 —— 像岗位的核心价值是啥、不同任务之间的优先级怎么排、需要具备哪些隐藏技能,这些深入的内容就说不出来了;
6. 也参与过一定数量的项目,不是没经验的,聊项目的时候能大体描述自己在里面做了啥任务,比如负责过数据整理、协助过方案讨论,但说到项目成果就有点笼统了,比如只说 "完成了任务",没说清 "任务带来了啥效果、自己的贡献让项目有啥提升",成果没法具体体现;
# 现场表现力
1. 说话的逻辑基本能让人听明白,不会让人抓不着重点,但偶尔会有重复的情况,比如同一句话换个说法又说一遍,或者讲到一半会停顿一下,想不起来下一句该说啥,不过整体的表达节奏还能跟上,不影响理解;
2. 面对交流或者任务时,基本能保持镇定,不会慌慌张张的,就算偶尔有点紧张,比如说话声音稍微变小、语速变快,也能自己调整过来,很快恢复平稳的状态,不会让紧张影响整体表现;
3. 平时的体态看起来挺得体的,坐姿、站姿都比较规范,跟人交流时也不会有太随意的动作,就是偶尔会有点僵硬,比如坐着的时候身体绷得太紧、手势不太自然,但这些小问题不影响整体的印象,还是显得比较专业的;
4. 不管是做事、做展示还是跟人沟通,基本能在规定时间内完成,不会出现严重超时或者没做完的情况,就是偶尔会有点小偏差 —— 要么比规定时间多花个几分钟,要么为了赶时间稍微省略一点内容,但整体的进度和完整性还是有保障的。
# 综合评价
总的来说,这学生在知识掌握、市场认知、流程理解、思路形成、岗位认知、项目经验、表达逻辑、心态调整、体态和时间把控上,都有基础的能力,没有特别明显的短板,但在 "细节、深度、系统性" 上还有提升空间。之后可以重点补补细节知识、多深入思考岗位和项目的核心价值、把思路梳理得更系统,这样综合能力就能再上一个台阶,也会更适配实际工作的要求。`
};
} else if (selectedItem === "第三次线下面试模拟") {
return {
totalScore: 89,
professionalScore: 36.5,
performanceScore: 52.5,
radarData: [9, 8, 9, 9],
radarData2: [8, 8, 7, 8],
title: "接近优秀评价",
content: `专业能力方面,候选人展现出扎实的专业功底,能够深入浅出地解释复杂概念。项目经验描述详实,能够清楚说明技术难点和解决方案。对行业发展有独到见解,能够将理论与实践很好地结合。专业深度和广度都达到了较高水平。
totalScore: 91, // 根据JSON计算的真实总分
professionalScore: 54, // (8+10+9+8+10+9)/6*10*0.6 = 54
performanceScore: 37, // (10+8+10+10)/4*10*0.4 = 38 (约37)
radarData: [8, 10, 9, 8, 10, 9], // 六项专业能力指标来自JSON
radarData2: [10, 8, 10, 10], // 四项现场表现指标来自JSON
title: "面试评价",
content: `# 专业能力
沟通表达上,表现自然从容,完全没有紧张感。语言组织能力强,逻辑清晰,重点突出。能够根据面试官的反应及时调整表达方式,展现出良好的沟通技巧。肢体语言得体,眼神交流充分,整体气场稳定。已经基本具备了通过正式面试的能力,继续保持即可。`
1. 关键知识掌握得特别全面,不管是核心考点还是重要内容,都能稳稳抓住,就是偶尔在小细节上会有点疏漏,比如个别细碎知识点记不太准,但整体来看,知识的准确性特别好,不会出大差错;
2. 对行业里的产业链和发展趋势摸得很透,不光能说清产业链各个环节怎么联动,还能具体讲明白这些趋势会给岗位工作、业务开展带来啥影响,比如哪种趋势会让岗位多些新任务,哪种趋势能帮业务找新方向,分析得特别实在;
3. 能把企业从头到尾的工作流程说得明明白白,哪个环节该干啥、流程里的关键节点是啥,都门儿清,而且能找准自己在流程里的角色,就连跟其他部门怎么配合、配合的关键点是啥,也能说得很到位,完全不像没接触过实际工作的;
4. 就算单说具体的主要流程,也能讲清楚自己负责的环节要做啥,比如流程里的资料整理、对接沟通这些活儿,都能说透,就是在细节上稍微有点粗糙,比如环节之间怎么交接更顺畅、遇到小问题怎么快速处理,说得没那么细;
5. 对目标岗位的职责了解得特别全面,岗位要干的活儿、承担的责任都能说全,还能精准找到自己在岗位上的价值 —— 比如自己能帮岗位解决啥问题、能给团队带来啥助力,更厉害的是,能结合实际例子说明这些职责和价值怎么跟业务目标挂钩,比如做好某项工作能帮业务完成多少指标,逻辑特别顺;
6. 做过的项目又多又完整,不管是校园里的实践项目,还是外面的实习项目,都有涉及,聊项目的时候,能清清楚楚说清自己在里面扮演啥角色、过程中具体做了哪些贡献,就连最后项目拿到啥成果、带来啥效果,也能说得明明白白,不会含糊其辞;
# 现场表现力
1. 说话特别流畅,而且很有劲儿,不管是回答问题还是分享想法,表达的结构都很严谨,不会东拉西扯,每个信息点都能精准说到点子上,让人一听就懂,还能快速 get 到核心内容,沟通效率特别高;
2. 面对提问或者展示这些场景,基本能保持镇定,不会慌里慌张的,就算偶尔有点紧张,比如语速稍微变快、声音有点抖,也能自己快速调整过来,很快就恢复平稳状态,不会让紧张影响整体发挥;
3. 跟人交流的时候,目光交流特别自然,不会躲躲闪闪,肢体动作也跟说话内容配合得刚好,比如讲重点的时候会配合手势强调,坐着的时候姿态也很放松,这些细节让说的话更有说服力,让人觉得特别靠谱;
4. 不管是做展示、答问题,还是走流程,每个环节的时间都控制得特别准,不会出现超时或者没说完的情况,环节之间衔接得也很自然,不会有生硬的停顿,更难得的是,还会特意留时间做总结,把核心内容再梳理一遍,让人印象更深刻。
# 综合评价
总的来说,这学生在知识掌握、行业认知、流程理解、岗位适配、项目经验、表达能力、心态调整、沟通仪态和时间把控上,都表现得特别出色,基础扎实还懂实际应用,就算偶尔有小瑕疵也不影响整体实力。这样的学生不管是继续学习还是去工作,都能快速适应,后续再把流程细节打磨打磨,综合能力还能再上一个大台阶,绝对是个好苗子。`
};
} else {
return {
totalScore: 85,
professionalScore: 34.0,
performanceScore: 51.0,
radarData: [9, 8, 9, 9],
title: "基础面试评价",
content: `在专业能力方面,候选人对 [岗位相关专业知识] 有基本掌握,能够清晰回答与过往项目经验相关的问题,例如在描述 [具体项目] 时,能准确说明自己承担的职责和达成的成果,体现出一定的实践操作能力。但在深入探讨 [某一专业难点] 时,表述略显浅显,对行业前沿动态的了解不够全面,专业深度有提升空间。
totalScore: 14, // 根据JSON计算的真实总分 (求职面试初体验)
professionalScore: 7, // (2+1+1+1+1+1)/6*10*0.6 = 7
performanceScore: 7, // (2+1+2+2)/4*10*0.4 = 7
radarData: [2, 1, 1, 1, 1, 1], // 六项专业能力指标来自JSON
radarData2: [2, 1, 2, 2], // 四项现场表现指标来自JSON
title: "面试评价",
content: `# 专业能力
沟通表达上,候选人语言流畅,逻辑较为清晰,能够主动回应面试官的问题,展现出良好的沟通意愿。不过在面对一些开放性问题时,思维发散性稍弱,回答的层次感不够突出,有时会出现表述重复的情况。`
1. 基础概念掌握得很不好,经常犯错误,连最基本的知识点都拎不清,这样一来根本没办法展开有效的交流,说出来的内容因为概念错了,也没什么参考价值;
2. 对行业基本情况几乎一无所知,不管问什么跟行业相关的问题,都答不上来,完全没接触过行业里的常识,聊起行业话题根本插不上话;
3. 一点都不理解企业的工作流程,不知道一项业务是怎么推进的,也说不清楚如果自己进了企业,该在哪个环节做事、要干些什么,对实际工作场景完全没概念;
4. 碰到问题或任务的时候,一点清晰的思路都没有,东想西想没章法,想出来的方案要么不切实际、没法落地,要么根本没解决核心问题,完全拿不出能用的办法;
5. 对目标岗位的工作职责彻底不了解,不知道岗位要干哪些活、需要什么能力,聊到跟岗位相关的内容,根本说不出有用的信息,也没法表达自己跟岗位的关联;
6. 要么就没做过什么项目,要么就是有项目经历也说不明白 —— 既讲不清项目背景,也说不出自己在里面干了啥,更总结不出从项目里学到了什么、能力有没有提升,完全没法用项目证明自己的能力;
# 现场表现力
1. 说话特别不连贯,一句完整的话都说不利索,想表达的观点颠三倒四,听的人得费劲猜才能勉强懂一点,信息传递特别低效,很容易让人误解;
2. 心态特别差,一碰到提问或者有点压力的情况,就慌慌张张的,要么说不出话,要么越说越乱,根本没办法持续把问题答完,稍微有点压力就扛不住;
3. 跟人交流或者做展示的时候,姿态特别不专业,要么不敢抬头看人、眼神躲躲闪闪,要么肢体动作很僵硬、很别扭,显得特别紧张、不靠谱,根本没法让人信服;
4. 完全没有时间概念,不管是做事、聊天还是做展示,都把控不好时间 —— 要么一个环节拖半天,要么节奏乱得一塌糊涂,严重影响整个流程的推进,让整体效率特别低。
# 综合评价
总的来说,这学生在专业基础、行业认知、企业流程理解、问题解决、岗位认知、项目经验、表达能力、心态调整、职业仪态和时间管理这些关键方面,都存在比较明显的不足。这些问题不光让专业交流没法顺利进行、碰到问题没法有效解决,也能看出目前他还不太适配实际工作场景,职业潜力还需要花很多功夫去挖掘和提升,得好好补补基础、多积累实践经验才行。`
};
}
};
@@ -107,7 +190,7 @@ export default ({ selectedItem = "求职面试初体验" }) => {
<div className="interview-rating-video-section">
<div className="interview-rating-header">
<span className="interview-rating-header-title">
钢铁是怎样炼成的
{selectedItem}
</span>
</div>
<div className="interview-rating-video">
@@ -189,6 +272,7 @@ export default ({ selectedItem = "求职面试初体验" }) => {
{/* 面试评分区域 */}
<div className="interview-evaluation-charts-wrapper">
<div className="interview-rating-header">
<img src="https://ddcz-1315997005.cos.ap-nanjing.myqcloud.com/static/img/teach_sys_icon/recuUY5uY7Ek50.png" alt="icon" style={{ width: '28px', height: '28px', marginRight: '10px' }} />
<span className="interview-rating-header-title">面试评分</span>
</div>
<div className="charts-content">
@@ -196,16 +280,32 @@ export default ({ selectedItem = "求职面试初体验" }) => {
<ScoreChart className="score-chart" value={getEvaluationData().totalScore} />
<div className="score-info">
<div className="score-info-item item1">
<span className="score-info-item-title">
专业能力评分(40)
</span>
<span className="score-info-item-value">{getEvaluationData().professionalScore}</span>
<p className="score-info-item-title">
专业能力评分
<span className="score-info-item-value">{getEvaluationData().professionalScore}</span>
</p>
<p className="score-info-line">
<i
style={{
width: `${(getEvaluationData().professionalScore / 60) * 100}%`,
}}
/>
<span className="score-info-line-total-value">60</span>
</p>
</div>
<div className="score-info-item item2">
<span className="score-info-item-title">
现场表现评分(60)
</span>
<span className="score-info-item-value">{getEvaluationData().performanceScore}</span>
<p className="score-info-item-title">
现场表现评分
<span className="score-info-item-value">{getEvaluationData().performanceScore}</span>
</p>
<p className="score-info-line">
<i
style={{
width: `${(getEvaluationData().performanceScore / 40) * 100}%`,
}}
/>
<span className="score-info-line-total-value">40</span>
</p>
</div>
</div>
</div>
@@ -214,20 +314,22 @@ export default ({ selectedItem = "求职面试初体验" }) => {
className="radar-chart"
data={getEvaluationData().radarData}
indicator={[
{ name: "核心知识掌握与精准应用", max: 10 },
{ name: "问题分析方案设计与成果表达", max: 10 },
{ name: "产业认知与行业趋势洞察", max: 10 },
{ name: "企业工作流理解与实践", max: 10 },
{ name: "基础知识\n掌握水平", max: 10 },
{ name: "产业链\n认知程度", max: 10 },
{ name: "企业生产\n体系了解", max: 10 },
{ name: "典型问题\n解决能力", max: 10 },
{ name: "岗位职责\n理解程度", max: 10 },
{ name: "项目经历\n丰富程度", max: 10 },
]}
/>
<RadarChart
className="radar-chart"
data={getEvaluationData().radarData2 || [7, 8, 6, 7]}
indicator={[
{ name: "沟通表达与逻辑思维", max: 10 },
{ name: "团队协作与责任意识", max: 10 },
{ name: "学习能力与适应能力", max: 10 },
{ name: "职业素养与发展潜力", max: 10 },
{ name: "语言表达与逻辑", max: 10 },
{ name: "自信与情绪管理", max: 10 },
{ name: "仪表与职场礼仪", max: 10 },
{ name: "时间管理与条理性", max: 10 },
]}
lineClolr="#FFE4D9"
areaColor="#FFD4C1"
@@ -239,11 +341,12 @@ export default ({ selectedItem = "求职面试初体验" }) => {
{/* 基础面试评价区域 */}
<div className="interview-evaluation-text-wrapper">
<div className="interview-rating-header">
<div className="interview-rating-header" style={{ justifyContent: 'flex-start' }}>
<img src="https://ddcz-1315997005.cos.ap-nanjing.myqcloud.com/static/img/teach_sys_icon/recuUY5vlvUj2X.png" alt="icon" style={{ width: '28px', height: '28px', marginRight: '10px' }} />
<span className="interview-rating-header-title">{getEvaluationData().title}</span>
</div>
<div className="interview-rating-text">
{getEvaluationData().content}
<ReactMarkdown>{getEvaluationData().content}</ReactMarkdown>
</div>
</div>
</>

View File

@@ -23,10 +23,10 @@ export default function RadarChart({
grid: { show: false },
radar: [
{
center: ["50%", "55%"],
center: ["50%", "50%"],
indicator,
radius: "65%", // 默认 65%,调大即可把文字整体外推
nameGap: 20, // 再额外增加 6-12px 间距
radius: "55%", // 调小半径以确保文字不超出容器
nameGap: 8, // 调整文字与雷达图的间距
shape: "circle", // 设置雷达图外圈为圆形
// 网格线样式配置
splitLine: {
@@ -40,9 +40,18 @@ export default function RadarChart({
axisName: {
color: "#4E5969",
fontSize: 12,
fontWeight: "900", // 更粗的文字
lineHeight: 16,
formatter: function(value) {
// 移除富文本标记,只返回纯文本
return value.replace(/{[ab]\|([^}]+)}/g, '$1').replace(/\n/g, '');
// 已经包含换行符的直接返回
if (value.includes('\n')) {
return value;
}
// 对较长的文本进行换行处理
if (value.length > 8) {
return value.substring(0, 8) + '\n' + value.substring(8);
}
return value;
},
},
axisLine: {

View File

@@ -19,10 +19,11 @@ const ScoreChart = ({
useEffect(() => {
if (!chartRef.current) return;
// 如果实例不存在,则初始化
if (!chartInstance.current) {
chartInstance.current = echarts.init(chartRef.current);
}
// 销毁旧实例
if (chartInstance.current) chartInstance.current.dispose();
// 新建实例
chartInstance.current = echarts.init(chartRef.current);
const option = {
series: [
@@ -81,26 +82,15 @@ const ScoreChart = ({
],
};
// 设置或更新配置,避免重新触发动画
chartInstance.current.setOption(option, {
notMerge: false,
lazyUpdate: true,
silent: false,
});
chartInstance.current.setOption(option);
// 监听窗口变化
window.addEventListener("resize", resize);
return () => {
window.removeEventListener("resize", resize);
};
}, [value, colors]);
// 组件卸载时清理
useEffect(() => {
return () => {
chartInstance.current?.dispose();
};
}, []);
}, [value, colors]);
return <div ref={chartRef} className={className} />;
};

View File

@@ -48,7 +48,7 @@ const JobStrategyDetailPage = () => {
onClick={() => setActiveItem("2")}
>
<span className="nav-icon curved-icon"></span>
<span className="nav-text">曲线就业方案</span>
<span className="nav-text">就业与晋升路径</span>
</div>
</div>

View File

@@ -72,7 +72,7 @@ const ProfileCard = () => {
src="recuUY5pWmMoaQ"
/>
<span className="profile-card-class-info-item-title">
就业管家课程
复合能力课
</span>
<span className="profile-card-class-info-item-text">
{studentInfo?.className || "-"}
@@ -83,7 +83,7 @@ const ProfileCard = () => {
className="profile-card-class-info-item-icon"
src="recuUY5tMX7M6A"
/>
<span className="profile-card-class-info-item-title">垂直方向</span>
<span className="profile-card-class-info-item-title">垂直能力课</span>
<span className="profile-card-class-info-item-text">
{studentInfo?.stageName || "-"}
</span>

View File

@@ -100,6 +100,7 @@
background-color: #f4f7f9;
position: relative;
margin-bottom: 10px;
overflow: visible;
> i {
position: absolute;
top: 0;

View File

@@ -120,30 +120,71 @@ const StudyStudes = ({ studyStatistics, learningProgress, loading }) => {
{/* 时长 */}
<li className="study-studes-card-study-info">
<div className="study-studes-card-time-wrapper">
<p className="study-studes-card-study-info-title">
个人学习时长h
<p className="study-studes-card-study-info-title" style={{ display: 'flex', alignItems: 'center', justifyContent: 'flex-start', paddingLeft: '0' }}>
<img src="https://ddcz-1315997005.cos.ap-nanjing.myqcloud.com/static/img/teach_sys_icon/recuW0XRVB1bpV.png" alt="icon" style={{ width: '28px', height: '28px', marginRight: '8px', flexShrink: 0 }} />
<span style={{ fontWeight: 'bold' }}>个人学习时长h</span>
</p>
<p className="study-studes-card-study-time-line">
<i
className="line1"
style={{
width: `${Math.min(
(personalStudyHours / classAverageStudyHours) * 70,
100
)}%`,
width: "100%",
position: 'relative',
display: 'block'
}}
>
<span className="icon1">{personalStudyHours}</span>
<span className="icon1" style={{
width: '60px',
height: '32px',
backgroundImage: 'url(https://ddcz-1315997005.cos.ap-nanjing.myqcloud.com/static/img/teach_sys_icon/recuW5McrJTuS8.png)',
backgroundSize: 'contain',
backgroundRepeat: 'no-repeat',
backgroundPosition: 'center',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
color: '#fff',
fontSize: '14px',
fontWeight: 'bold',
position: 'absolute',
right: '-30px',
top: '50%',
transform: 'translateY(-50%) translateY(-30px)',
paddingLeft: '0'
}}>{personalStudyHours}</span>
</i>
</p>
</div>
<div className="study-studes-card-time-wrapper">
<p className="study-studes-card-study-info-title">
班级平均学习时长h
<p className="study-studes-card-study-info-title" style={{ display: 'flex', alignItems: 'center', justifyContent: 'flex-start', paddingLeft: '0' }}>
<img src="https://ddcz-1315997005.cos.ap-nanjing.myqcloud.com/static/img/teach_sys_icon/recuW0XRVB1bpV.png" alt="icon" style={{ width: '28px', height: '28px', marginRight: '8px', flexShrink: 0 }} />
<span style={{ fontWeight: 'bold' }}>班级平均学习时长h</span>
</p>
<p className="study-studes-card-study-time-line study-studes-card-study-time-line-class">
<i className="line2" style={{ width: "80%" }}>
<span className="icon2">{classAverageStudyHours}</span>
<i className="line2" style={{
width: "89%",
position: 'relative',
display: 'block'
}}>
<span className="icon2" style={{
width: '60px',
height: '32px',
backgroundImage: 'url(https://ddcz-1315997005.cos.ap-nanjing.myqcloud.com/static/img/teach_sys_icon/recuW5McGy6zLW.png)',
backgroundSize: 'contain',
backgroundRepeat: 'no-repeat',
backgroundPosition: 'center',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
color: '#fff',
fontSize: '14px',
fontWeight: 'bold',
position: 'absolute',
right: '-30px',
top: '50%',
transform: 'translateY(-50%) translateY(-30px)',
paddingLeft: '0'
}}>{classAverageStudyHours}</span>
</i>
</p>
</div>
@@ -154,8 +195,9 @@ const StudyStudes = ({ studyStatistics, learningProgress, loading }) => {
</li>
{/* 课程整体完成情况 */}
<li className="study-studes-card-curriculum">
<p className="study-studes-card-curriculum-title">
整体课程完成情况
<p className="study-studes-card-curriculum-title" style={{ display: 'flex', alignItems: 'center', justifyContent: 'flex-start', paddingLeft: '0' }}>
<img src="https://ddcz-1315997005.cos.ap-nanjing.myqcloud.com/static/img/teach_sys_icon/recuW0XRVB1bpV.png" alt="icon" style={{ width: '28px', height: '28px', marginRight: '8px', flexShrink: 0 }} />
<span style={{ fontWeight: 'bold' }}>整体课程完成情况</span>
</p>
<ScoreRingChart
title={`整体课程\n完成情况`}
@@ -179,8 +221,9 @@ const StudyStudes = ({ studyStatistics, learningProgress, loading }) => {
</li>
{/* 课后作业完成情况 */}
<li className="study-studes-card-curriculum-homework study-studes-card-curriculum">
<p className="study-studes-card-curriculum-title">
课后作业完成情况
<p className="study-studes-card-curriculum-title" style={{ display: 'flex', alignItems: 'center', justifyContent: 'flex-start', paddingLeft: '0' }}>
<img src="https://ddcz-1315997005.cos.ap-nanjing.myqcloud.com/static/img/teach_sys_icon/recuW0XRVB1bpV.png" alt="icon" style={{ width: '28px', height: '28px', marginRight: '8px', flexShrink: 0 }} />
<span style={{ fontWeight: 'bold' }}>课后作业完成情况</span>
</p>
<ScoreRingChart
ringData={homeworkRingData}
@@ -203,7 +246,10 @@ const StudyStudes = ({ studyStatistics, learningProgress, loading }) => {
</li>
{/* 考勤情况 */}
<li className="study-studes-card-curriculum">
<p className="study-studes-card-curriculum-title">考勤情况</p>
<p className="study-studes-card-curriculum-title" style={{ display: 'flex', alignItems: 'center', justifyContent: 'flex-start', paddingLeft: '0' }}>
<img src="https://ddcz-1315997005.cos.ap-nanjing.myqcloud.com/static/img/teach_sys_icon/recuW0XRVB1bpV.png" alt="icon" style={{ width: '28px', height: '28px', marginRight: '8px', flexShrink: 0 }} />
<span style={{ fontWeight: 'bold' }}>考勤情况</span>
</p>
<AttendanceRingChart
data={attendanceData}
centerContent={

View File

@@ -13,10 +13,9 @@
width: 100%;
height: 805px;
box-sizing: border-box;
padding: 0 20px;
padding: 20px;
display: flex;
justify-content: space-between;
margin-top: 20px;
.unified-profile-left {
width: 373px;

View File

@@ -5,6 +5,7 @@ import { mockData } from "@/data/mockData";
import "./index.css";
const PublicCourses = () => {
// 默认不选中任何课程,显示黑屏状态
const [selectedCourse, setSelectedCourse] = useState(null);
const handleCourseClick = (course) => {

View File

@@ -309,7 +309,7 @@ const ResumeInterviewPage = () => {
className="job-level-tag"
/>
<div className="job-name">
<p>岗位名称{position.title}</p>
<p>{position.title}</p>
<span className="job-arrow"></span>
</div>
</div>