diff --git a/src/App.jsx b/src/App.jsx index 2956cf7..f045f93 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -1,63 +1,42 @@ -import { - BrowserRouter as Router, - Routes, - Route, - Navigate, -} from "react-router-dom"; +import { BrowserRouter, Route, Routes } from "react-router-dom"; import Layout from "./components/Layout"; -import Dashboard from "./pages/Dashboard"; -import CalendarPage from "./pages/CalendarPage"; -import LivePage from "./pages/LivePage"; -import HomeworkPage from "./pages/HomeworkPage"; -import ProjectLibraryPage from "./pages/ProjectLibraryPage"; -import PersonalProfile from "./pages/PersonalProfile"; -import CareerTreePage from "./pages/CareerTreePage"; -import CompanyJobsPage from "./pages/CompanyJobsPage"; -import CompanyJobsListPage from "./pages/CompanyJobsListPage"; -import JobStrategyPage from "./pages/JobStrategyPage"; -import JobStrategyDetailPage from "./pages/JobStrategyDetailPage"; -import InterviewSimulationPage from "./pages/InterviewSimulationPage"; -import ExpertSupportPage from "./pages/ExpertSupportPage"; -import Portfolio from "./pages/Portfolio"; -import PublicCourses from "./pages/PublicCourses"; -import ResumeInterviewPage from "./pages/ResumeInterviewPage"; +import routes from "./routes"; // 样式文件导入 -import "./normalize.css"; +import "./global.css"; import "@arco-design/web-react/dist/css/arco.css"; +const getAllRoutes = (routes) => { + const result = []; + + const traverse = (routeItems) => { + routeItems.forEach((item) => { + if (item.routes) { + // 如果有子路由,递归处理 + traverse(item.routes); + } else if (item.path && item.element) { + // 如果是单个路由项,添加到结果数组 + result.push(item); + } + }); + }; + + traverse(routes); + return result; +}; + function App() { + const allRoutes = getAllRoutes(routes); return ( - + - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } - /> - } /> - } /> - - } - /> - } /> + {allRoutes?.map((item) => ( + + ))} - + ); } diff --git a/src/assets/iconfont/iconfont.js b/src/assets/iconfont/iconfont.js new file mode 100644 index 0000000..f0d7ee3 --- /dev/null +++ b/src/assets/iconfont/iconfont.js @@ -0,0 +1 @@ +window._iconfont_svg_string_4525204='',(l=>{var c=(a=(a=document.getElementsByTagName("script"))[a.length-1]).getAttribute("data-injectcss"),a=a.getAttribute("data-disable-injectsvg");if(!a){var o,h,i,t,s,e=function(c,a){a.parentNode.insertBefore(c,a)};if(c&&!l.__iconfont__svg__cssinject__){l.__iconfont__svg__cssinject__=!0;try{document.write("")}catch(c){console&&console.log(c)}}o=function(){var c,a=document.createElement("div");a.innerHTML=l._iconfont_svg_string_4525204,(a=a.getElementsByTagName("svg")[0])&&(a.setAttribute("aria-hidden","true"),a.style.position="absolute",a.style.width=0,a.style.height=0,a.style.overflow="hidden",a=a,(c=document.body).firstChild?e(a,c.firstChild):c.appendChild(a))},document.addEventListener?~["complete","loaded","interactive"].indexOf(document.readyState)?setTimeout(o,0):(h=function(){document.removeEventListener("DOMContentLoaded",h,!1),o()},document.addEventListener("DOMContentLoaded",h,!1)):document.attachEvent&&(i=o,t=l.document,s=!1,m(),t.onreadystatechange=function(){"complete"==t.readyState&&(t.onreadystatechange=null,v())})}function v(){s||(s=!0,i())}function m(){try{t.documentElement.doScroll("left")}catch(c){return void setTimeout(m,50)}v()}})(window); \ No newline at end of file diff --git a/src/assets/images/Sidebar/btn_icon.png b/src/assets/images/Sidebar/btn_icon.png new file mode 100644 index 0000000..ab39494 Binary files /dev/null and b/src/assets/images/Sidebar/btn_icon.png differ diff --git a/src/assets/images/Sidebar/logo.png b/src/assets/images/Sidebar/logo.png new file mode 100644 index 0000000..09203e9 Binary files /dev/null and b/src/assets/images/Sidebar/logo.png differ diff --git a/src/components/IconFont/index.jsx b/src/components/IconFont/index.jsx new file mode 100644 index 0000000..641f065 --- /dev/null +++ b/src/components/IconFont/index.jsx @@ -0,0 +1,13 @@ +import "@/assets/iconfont/iconfont.js"; + +const IconFont = (props) => { + const { className, name, onClick } = props; + + return ( + + ); +}; + +export default IconFont; diff --git a/src/components/Layout/MessageNotification.jsx b/src/components/Layout/MessageNotification.jsx deleted file mode 100644 index 7e418a6..0000000 --- a/src/components/Layout/MessageNotification.jsx +++ /dev/null @@ -1,165 +0,0 @@ -import React, { useEffect, useRef } from "react"; -import { mockData } from "@/data/mockData"; -import Portal from "../common/Portal"; - -const MessageNotification = ({ isOpen, onClose, onMarkAllRead }) => { - const { notifications } = mockData; - const popupRef = useRef(null); - - // 点击外部关闭浮窗 - useEffect(() => { - const handleClickOutside = (event) => { - if (popupRef.current && !popupRef.current.contains(event.target)) { - onClose(); - } - }; - - if (isOpen) { - document.addEventListener("mousedown", handleClickOutside); - } - - return () => { - document.removeEventListener("mousedown", handleClickOutside); - }; - }, [isOpen, onClose]); - - // 获取消息类型图标 - const getMessageIcon = (type) => { - const icons = { - system: "⚙️", - course: "📚", - assignment: "📝", - announcement: "📢", - }; - return icons[type] || "📬"; - }; - - // 获取优先级颜色 - const getPriorityColor = (priority) => { - const colors = { - high: "#ef4444", - medium: "#f59e0b", - low: "#10b981", - }; - return colors[priority] || "#6b7280"; - }; - - // 格式化时间显示 - const formatTime = (timeString) => { - const date = new Date(timeString); - const now = new Date(); - const diffInHours = Math.floor((now - date) / (1000 * 60 * 60)); - - if (diffInHours < 1) { - return "刚刚"; - } else if (diffInHours < 24) { - return `${diffInHours}小时前`; - } else { - const diffInDays = Math.floor(diffInHours / 24); - return `${diffInDays}天前`; - } - }; - - // 处理消息点击 - const handleMessageClick = (message) => { - // 这里可以添加具体的消息处理逻辑 - }; - - // 处理全部标记已读 - const handleMarkAllReadClick = () => { - onMarkAllRead(); - }; - - if (!isOpen) { - return null; - } - - return ( - -
- {/* 浮窗头部 */} -
-

系统消息

-
- {notifications.unreadCount > 0 && ( - - )} - -
-
- - {/* 消息列表 */} -
- {notifications.messages.length === 0 ? ( -
-
📭
-

暂无消息通知

-
- ) : ( -
- {notifications.messages.map((message) => ( -
handleMessageClick(message)} - > -
- - {getMessageIcon(message.type)} - - {!message.isRead && ( -
- )} -
- -
-
-
{message.title}
- - {formatTime(message.time)} - -
-

{message.content}

-
-
- ))} -
- )} -
- - {/* 浮窗底部 */} -
- - 共 {notifications.messages.length} 条消息 - -
-
-
- ); -}; - -export default MessageNotification; diff --git a/src/components/Layout/Sidebar.jsx b/src/components/Layout/Sidebar.jsx deleted file mode 100644 index de73bfb..0000000 --- a/src/components/Layout/Sidebar.jsx +++ /dev/null @@ -1,143 +0,0 @@ -import { useState, useEffect } from "react"; -import { useNavigate, useLocation } from "react-router-dom"; -import { mockData } from "@/data/mockData"; - -const navigation = { - sections: [ - { - title: "个人区块", - items: [ - { name: "🏠 主页", path: "/dashboard", active: true }, - { name: "👤 个人档案", path: "/profile" }, - { name: "📅 日历", path: "/calendar" }, - ], - }, - { - title: "课程区块", - items: [ - { name: "📺 公共课直播间", path: "/public-courses" }, - { name: "📺 课程直播间", path: "/live" }, - { name: "🌳 就业管家知识树", path: "/career-tree" }, - { name: "📝 课后作业", path: "/homework" }, - { - name: "🎯 1V1定制求职策略", - path: ["/job-strategy", "/job-strategy-detail"], - }, - { name: "🎭 线下面试模拟", path: "/interview-simulation" }, - ], - }, - { - title: "资源区块", - items: [ - { name: "🏥 专家支持中心", path: "/expert-support" }, - { - name: "🏢 企业内推岗位", - path: ["/company-jobs", "/company-jobs-list"], - }, - { name: "📄 我的简历与面试题", path: "/resume-interview" }, - { name: "📚 我的项目库", path: "/project-library" }, - { name: "📚 我的作品集", path: "/portfolio" }, - ], - }, - ], -}; - -const Sidebar = () => { - const navigate = useNavigate(); - const location = useLocation(); - const { user } = mockData; - - // 侧边栏折叠状态,从localStorage恢复状态 - const [isCollapsed, setIsCollapsed] = useState(() => { - const saved = localStorage.getItem("sidebar-collapsed"); - return saved === "true"; - }); - - // 保存状态到localStorage并发送事件 - useEffect(() => { - localStorage.setItem("sidebar-collapsed", isCollapsed.toString()); - // 发送自定义事件通知Layout组件状态变化 - const event = new CustomEvent("sidebarToggle", { - detail: { isCollapsed }, - }); - window.dispatchEvent(event); - }, [isCollapsed]); - - const handleNavClick = (path) => { - if (Array.isArray(path)) { - navigate(path[0]); - } else { - navigate(path); - } - }; - - // 切换侧边栏展开/折叠状态 - const toggleSidebar = () => { - setIsCollapsed(!isCollapsed); - }; - - return ( - <> -
- {/* 顶部Logo和标题 */} -
-
- {!isCollapsed && ( -
多多畅职教务系统
- )} -
- - {/* 用户信息 - 纯静态展示 */} -
-
- {!isCollapsed && ( -
-

{user.name}

-
- )} -
- - {/* 导航菜单 */} -
- {navigation.sections.map((section, sectionIndex) => ( -
- {isCollapsed ? ( -
- {section.title.charAt(0)} -
- ) : ( -
{section.title}
- )} - {section.items.map((item, itemIndex) => ( - - ))} -
- ))} -
-
- - {/* 悬浮的折叠/展开按钮 */} - - - ); -}; - -export default Sidebar; diff --git a/src/components/Layout/index.css b/src/components/Layout/index.css index fb65b2a..9be72e6 100644 --- a/src/components/Layout/index.css +++ b/src/components/Layout/index.css @@ -1,583 +1,15 @@ -/* 全局z-index层级系统 - 确保弹窗层级的最佳实践 */ -:root { - /* z-index层级标准 - 数值间隔1000确保充足的层级空间 */ - --z-modal: 10000; /* 模态框 - 最高层级,包括系统消息弹窗 */ - --z-popup: 9000; /* 弹出框 - 次高层级,如用户菜单、下拉框 */ - --z-tooltip: 8000; /* 提示框 - 中等层级,如悬浮提示 */ - --z-dropdown: 7000; /* 下拉菜单 - 较低层级 */ - --z-header: 1000; /* 页头导航 - 基础层级 */ - --z-content: 1; /* 页面内容 - 默认层级 */ -} - -/* Layout组件专用变量定义 - 不与其他文件共享 */ -.app-layout, -.sidebar, -.app-layout *, -.sidebar * { - --primary-color: #3b82f6; - --text-primary: #111827; - --text-secondary: #6b7280; - --card-bg: #ffffff; - --sidebar-bg: #ffffff; - --border-color: #e5e7eb; -} - /* 布局相关样式 */ .app-layout { display: flex; min-height: 100vh; - width: 100vw; -} - -/* 侧边栏样式 */ -.sidebar { - width: 280px; /* 增加宽度避免内容换行 */ - background: var(--sidebar-bg); - border-right: 1px solid var(--border-color); - display: flex; - flex-direction: column; - position: fixed; /* 固定定位同时作为子元素的定位上下文 */ - height: 100%; - overflow: hidden; - transition: width 0.3s ease; -} - -/* 侧边栏滚动条样式 - 极简清透设计 */ -.sidebar::-webkit-scrollbar { - width: 6px; /* 纤细的滚动条 */ -} - -.sidebar::-webkit-scrollbar-track { - background: transparent; /* 透明轨道 */ - margin: 10px 0; /* 上下留边 */ -} - -.sidebar::-webkit-scrollbar-thumb { - background: rgba(0, 0, 0, 0.1); /* 半透明滑块 */ - border-radius: 3px; - transition: background 0.2s ease; -} - -.sidebar::-webkit-scrollbar-thumb:hover { - background: rgba(0, 0, 0, 0.2); /* 悬停时加深 */ -} - -/* Firefox滚动条样式 */ -.sidebar { - scrollbar-width: thin; - scrollbar-color: rgba(0, 0, 0, 0.1) transparent; -} - -/* 折叠状态的侧边栏 */ -.sidebar.collapsed { - width: 64px; -} - -.sidebar-header { - padding: 20px 16px; - border-bottom: 1px solid var(--border-color); - display: flex; - align-items: center; - justify-content: flex-start; - gap: 12px; -} - -.sidebar-logo { - width: 32px; - height: 32px; - background: var(--primary-color); - border-radius: 6px; - display: flex; - align-items: center; - justify-content: center; - color: white; - font-weight: 600; -} - -.sidebar-title { - font-size: 14px; - font-weight: 500; - color: var(--text-primary); -} - -.user-profile { - padding: 16px; - display: flex; - align-items: center; - gap: 12px; - border-bottom: 1px solid var(--border-color); -} - -.user-avatar { - width: 40px; - height: 40px; - min-width: 40px; - min-height: 40px; - border-radius: 50% !important; - background: #e5e7eb; - background-image: url('data:image/svg+xml;utf8,'); - background-size: 20px; - background-repeat: no-repeat; - background-position: center; - flex-shrink: 0; - flex-grow: 0; -} - -.user-info h4 { - font-size: 14px; - font-weight: 500; - color: var(--text-primary); -} - -/* 导航菜单样式 */ -.nav-menu { - flex: 1; - padding: 8px 0; - overflow-y: auto; -} - -.nav-section { - margin-bottom: 8px; -} - -.nav-section-title { - padding: 8px 16px; - font-size: 13px; - font-weight: 600; - color: var(--text-secondary); - text-transform: uppercase; - letter-spacing: 0.05em; -} - -.nav-item { - display: block; - padding: 8px 16px; - color: var(--text-secondary); - text-decoration: none; - font-size: 14px; - transition: all 0.15s ease; - border: none; - background: none; width: 100%; - text-align: left; - cursor: pointer; + background-color: #f2f3f5; } - -.nav-item:hover { - background: #f3f4f6; - color: var(--text-primary); -} - -.nav-item.active { - background: #eff6ff; - color: var(--primary-color); - font-weight: 500; -} - -/* 折叠状态下的区块标题 */ -.nav-section-title-collapsed { - padding: 8px 16px; - font-size: 12px; - font-weight: 600; - color: var(--text-secondary); - text-align: center; - border-bottom: 1px solid #f3f4f6; - margin-bottom: 4px; -} - -/* 悬浮的折叠/展开按钮 */ -.sidebar-float-toggle { - position: fixed; - top: 5%; /* 垂直居中 */ - left: 260px; /* 默认位置:侧边栏宽度 */ - transform: translateY(-50%); - width: 20px; - height: 50px; - background: linear-gradient(135deg, #3b82f6, #2563eb); - border: none; - border-radius: 0 10px 10px 0; /* 右侧圆角 */ - color: white; - cursor: pointer; - font-size: 10px; - transition: all 0.3s ease; - display: flex; - align-items: center; - justify-content: center; - box-shadow: 2px 0 10px rgba(59, 130, 246, 0.3); - z-index: 1001; /* 确保在侧边栏上方 */ - opacity: 0.9; - backdrop-filter: blur(4px); -} - -/* 折叠状态的按钮位置 */ -.sidebar-float-toggle.collapsed { - left: 64px; /* 折叠时的侧边栏宽度 */ -} - -/* 悬停效果 */ -.sidebar-float-toggle:hover { - opacity: 1; - width: 28px; - background: linear-gradient(135deg, #2563eb, #1d4ed8); - box-shadow: 3px 0 15px rgba(59, 130, 246, 0.4); - transform: translateY(-50%) translateX(2px); -} - -/* 图标动画 */ -.toggle-icon { - transition: transform 0.3s ease; - display: inline-block; -} - -.sidebar-float-toggle:hover .toggle-icon { - transform: scale(1.2); -} - -/* 旧的toggle-btn样式已被sidebar-float-toggle替代 */ - -/* 折叠状态下的特殊样式 */ -.sidebar.collapsed .nav-item { - padding: 8px 12px; - text-align: center; - font-size: 16px; - min-height: 40px; - display: flex; - align-items: center; - justify-content: center; -} - -.sidebar.collapsed .user-profile { - justify-content: center; - padding: 16px 12px; -} - -.sidebar.collapsed .sidebar-header { - justify-content: center; - padding: 20px 12px; -} - /* 主内容区域 */ .main-content { flex: 1; - margin-left: 280px; /* 配合侧边栏宽度调整 */ overflow: hidden; transition: margin-left 0.3s ease; display: flex; flex-direction: column; - background-color: #f2f3f5; -} - -/* 当侧边栏折叠时的主内容区域 */ -.main-content.sidebar-collapsed { - margin-left: 64px; -} - -/* 用户信息区域优化 */ -.user-info { - display: flex; - align-items: center; - justify-content: space-between; - width: 100%; -} - -/* 消息图标容器 */ -.message-icon-container { - position: relative; - cursor: pointer; - padding: 4px; - border-radius: 6px; - transition: background-color 150ms ease; - display: flex; - align-items: center; - justify-content: center; -} - -.message-icon-container:hover { - background-color: #f3f4f6; -} - -/* 消息图标 */ -.message-icon { - font-size: 18px; - color: #6b7280; - transition: color 150ms ease; -} - -.message-icon-container:hover .message-icon { - color: #374151; -} - -/* 未读消息徽章 */ -.message-badge { - position: absolute; - top: -2px; - right: -2px; - background: #ef4444; - color: white; - border-radius: 50%; - min-width: 16px; - height: 16px; - font-size: 10px; - font-weight: 600; - display: flex; - align-items: center; - justify-content: center; - animation: messagePulse 2s ease-in-out infinite; -} - -/* 未读徽章动画 */ -@keyframes messagePulse { - 0%, - 100% { - transform: scale(1); - } - 50% { - transform: scale(1.1); - } -} - -/* 消息通知浮窗遮罩 - 已由Portal替代,保留样式以防回退 */ -.message-notification-overlay { - position: fixed; - top: 0; - left: 0; - right: 0; - bottom: 0; - z-index: var(--z-modal); - pointer-events: none; -} - -/* 消息通知浮窗 - 使用全局z-index系统 */ -.message-notification-popup { - position: fixed; /* Portal渲染到body,使用fixed定位 */ - top: 120px; - left: 280px; - width: 320px; - max-height: 400px; - background: #ffffff; - border-radius: 8px; - box-shadow: 0 10px 25px rgba(0, 0, 0, 0.15), 0 4px 6px rgba(0, 0, 0, 0.1); - border: 1px solid #e5e7eb; - pointer-events: auto; - opacity: 1; - transform: translateY(0); - z-index: var(--z-modal); /* 使用全局变量确保最高层级 */ - display: flex; - flex-direction: column; - overflow: hidden; -} - -/* 浮窗淡入动画 */ -@keyframes notificationFadeIn { - from { - opacity: 0; - transform: translateY(-8px) scale(0.95); - } - to { - opacity: 1; - transform: translateY(0) scale(1); - } -} - -/* 浮窗头部 */ -.message-notification-header { - display: flex; - justify-content: space-between; - align-items: center; - padding: 16px 16px 12px 16px; - border-bottom: 1px solid #f3f4f6; -} - -.message-notification-title { - margin: 0; - font-size: 14px; - font-weight: 600; - color: #111827; -} - -.message-notification-actions { - display: flex; - align-items: center; - gap: 8px; -} - -.mark-all-read-btn { - font-size: 12px; - color: #3b82f6; - background: none; - border: none; - cursor: pointer; - padding: 4px 8px; - border-radius: 4px; - transition: background-color 150ms ease; -} - -.mark-all-read-btn:hover { - background-color: #eff6ff; -} - -.close-btn { - font-size: 18px; - color: #9ca3af; - background: none; - border: none; - cursor: pointer; - padding: 4px; - border-radius: 4px; - transition: all 150ms ease; - line-height: 1; -} - -.close-btn:hover { - background-color: #f3f4f6; - color: #6b7280; -} - -/* 浮窗内容区域 */ -.message-notification-content { - flex: 1; - max-height: 300px; - overflow-y: auto; - padding: 8px 0; - background: #ffffff; -} - -/* 滚动条样式 */ -.message-notification-content::-webkit-scrollbar { - width: 4px; -} - -.message-notification-content::-webkit-scrollbar-track { - background: transparent; -} - -.message-notification-content::-webkit-scrollbar-thumb { - background-color: #e5e7eb; - border-radius: 2px; -} - -.message-notification-content::-webkit-scrollbar-thumb:hover { - background-color: #d1d5db; -} - -/* 消息列表 */ -.message-list { - display: flex; - flex-direction: column; -} - -/* 消息项 */ -.message-item { - display: flex; - gap: 12px; - padding: 12px 16px; - cursor: pointer; - transition: background-color 150ms ease; - border-left: 2px solid transparent; -} - -.message-item:hover { - background-color: #f9fafb; -} - -.message-item.unread { - background-color: #fefefe; - border-left-color: #3b82f6; -} - -.message-item.read { - opacity: 0.8; -} - -/* 消息图标包装器 */ -.message-icon-wrapper { - position: relative; - flex-shrink: 0; -} - -.message-type-icon { - font-size: 16px; - display: block; -} - -.message-priority-dot { - position: absolute; - top: -2px; - right: -2px; - width: 6px; - height: 6px; - border-radius: 50%; -} - -/* 消息内容 */ -.message-content { - flex: 1; - min-width: 0; -} - -.message-header { - display: flex; - justify-content: space-between; - align-items: flex-start; - gap: 8px; - margin-bottom: 4px; -} - -.message-title { - margin: 0; - font-size: 13px; - font-weight: 500; - color: #111827; - line-height: 1.3; -} - -.message-time { - font-size: 11px; - color: #9ca3af; - flex-shrink: 0; -} - -.message-text { - margin: 0; - font-size: 12px; - color: #6b7280; - line-height: 1.4; - display: -webkit-box; - -webkit-line-clamp: 2; - -webkit-box-orient: vertical; - overflow: hidden; -} - -/* 空消息状态 */ -.empty-messages { - text-align: center; - padding: 32px 16px; - color: #9ca3af; -} - -.empty-icon { - font-size: 32px; - margin-bottom: 8px; -} - -.empty-messages p { - margin: 0; - font-size: 13px; -} - -/* 浮窗底部 */ -.message-notification-footer { - padding: 12px 16px; - border-top: 1px solid #f3f4f6; - text-align: center; - background: #fafafa; - border-bottom-left-radius: 8px; - border-bottom-right-radius: 8px; - flex-shrink: 0; -} - -.message-count { - font-size: 11px; - color: #9ca3af; - font-weight: 500; } diff --git a/src/components/Layout/index.jsx b/src/components/Layout/index.jsx index 7a701c3..e6adb1b 100644 --- a/src/components/Layout/index.jsx +++ b/src/components/Layout/index.jsx @@ -1,42 +1,14 @@ import { useState, useEffect } from "react"; -import Sidebar from "./Sidebar"; +import Sidebar from "../Sidebar"; import "./index.css"; const Layout = ({ children }) => { - const [isCollapsed, setIsCollapsed] = useState(() => { - const saved = localStorage.getItem("sidebar-collapsed"); - return saved === "true"; - }); - - useEffect(() => { - const handleStorageChange = () => { - const saved = localStorage.getItem("sidebar-collapsed"); - setIsCollapsed(saved === "true"); - }; - - window.addEventListener("storage", handleStorageChange); - - // 监听自定义事件 - const handleSidebarToggle = (event) => { - setIsCollapsed(event.detail.isCollapsed); - }; - - window.addEventListener("sidebarToggle", handleSidebarToggle); - - return () => { - window.removeEventListener("storage", handleStorageChange); - window.removeEventListener("sidebarToggle", handleSidebarToggle); - }; - }, []); + const [isCollapsed, setIsCollapsed] = useState(true); return (
- -
- {children} -
+ +
{children}
); }; diff --git a/src/components/common/Portal.jsx b/src/components/Portal/index.jsx similarity index 100% rename from src/components/common/Portal.jsx rename to src/components/Portal/index.jsx diff --git a/src/components/Sidebar/index.css b/src/components/Sidebar/index.css new file mode 100644 index 0000000..1c3ca1c --- /dev/null +++ b/src/components/Sidebar/index.css @@ -0,0 +1,194 @@ +.sidebar-expand-wrapper { + width: 280px; + box-sizing: border-box; + position: relative; + display: flex; + justify-content: flex-start; + align-items: center; + flex-direction: column; + transition: width 0.3s ease; + overflow-x: hidden; + overflow-y: auto; + background-color: #fff; + + .sidebar-title { + width: 100%; + height: 80px; + display: flex; + justify-content: center; + align-items: center; + + > img { + width: 48px; + height: 48px; + margin-right: 10px; + transition: margin 0.3s ease; + } + > p { + color: #262626; + font-size: 20px; + font-weight: 400; + opacity: 1; + transform: translateX(0); + transition: opacity 0.3s ease, transform 0.3s ease; + white-space: nowrap; + } + } + .user-info { + width: 100%; + height: 80px; + display: flex; + justify-content: center; + align-items: center; + .user-avatar { + width: 64px; + height: 64px; + border-radius: 12px; + } + } + .visitor-count { + width: 100%; + height: 41px; + text-align: center; + font-size: 12px; + font-weight: 700; + line-height: 41px; + } + .sidebar-menu { + width: 100%; + overflow-x: hidden; + overflow-y: auto; + box-sizing: border-box; + padding: 0 20px; + display: flex; + justify-content: flex-start; + align-items: center; + flex-direction: column; + + .sidebar-menu-title { + width: 100%; + height: 36px; + text-align: left; + line-height: 36px; + color: #bfbfbf; + font-size: 14px; + font-weight: 400; + } + + .sidebar-menu-item-active { + background-color: #e8f3ff; + + .sidebar-menu-icon { + color: #0275f2 !important; + } + .sidebar-menu-text { + color: #0275f2 !important; + } + } + + .sidebar-menu-item { + width: 100%; + height: 40px; + display: flex; + justify-content: flex-start; + align-items: center; + border-radius: 8px; + cursor: pointer; + + .sidebar-menu-icon { + margin: 0 10px; + font-size: 20px; + } + .sidebar-menu-text { + margin-left: 10px; + font-size: 16px; + font-weight: 400; + color: #616065; + } + } + } + + .sidebar-btn { + width: 190px; + height: 22px; + position: absolute; + bottom: 20px; + left: 50%; + transform: translateX(-50%); + display: flex; + justify-content: center; + align-items: center; + cursor: pointer; + + > img { + width: 22px; + height: 22px; + } + + .sidebar-btn-text { + margin-left: 10px; + color: #616065; + font-size: 16px; + font-weight: 400; + opacity: 1; + transform: translateX(0); + transition: opacity 0.3s ease, transform 0.3s ease; + white-space: nowrap; + } + } +} + +.sidebar-retract-wrapper { + width: 95px; + .sidebar-title { + > img { + margin: 0; + transition: margin 0.3s ease; + } + > p { + opacity: 0; + transform: translateX(-10px); + pointer-events: none; + transition: opacity 0.3s ease, transform 0.3s ease; + width: 0; + } + } + .user-info { + margin-bottom: 41px; + } + .visitor-count { + display: none; + } + .sidebar-menu { + .sidebar-menu-title { + text-align: center; + opacity: 0; + transition: opacity 0.3s ease; + } + .sidebar-menu-item { + justify-content: center; + + .sidebar-menu-icon { + margin: 0; + } + .sidebar-menu-text { + opacity: 0; + transform: translateX(-10px); + pointer-events: none; + transition: opacity 0.3s ease, transform 0.3s ease; + width: 0; + } + } + } + + .sidebar-btn { + width: 80px; + .sidebar-btn-text { + opacity: 0; + transform: translateX(-10px); + pointer-events: none; + transition: opacity 0.3s ease, transform 0.3s ease; + width: 0; + } + } +} diff --git a/src/components/Sidebar/index.jsx b/src/components/Sidebar/index.jsx new file mode 100644 index 0000000..5629086 --- /dev/null +++ b/src/components/Sidebar/index.jsx @@ -0,0 +1,81 @@ +import { useNavigate, useLocation } from "react-router-dom"; +import { mockData } from "@/data/mockData"; +import IconFont from "@/components/IconFont"; +import Logo from "@/assets/images/Sidebar/logo.png"; +import BTNICON from "@/assets/images/Sidebar/btn_icon.png"; +import routes from "@/routes"; +import "./index.css"; + +const Sidebar = ({ isCollapsed, setIsCollapsed }) => { + const navigate = useNavigate(); + const location = useLocation(); + const { user } = mockData; + + const handleNavClick = (path) => { + navigate(path); + }; + + // 切换侧边栏展开/折叠状态 + const toggleSidebar = () => { + setIsCollapsed((prev) => !prev); + }; + + return ( +
+
+ logo +

多多畅职教育系统

+
+
+ avatar +
+
访客总数:1000
+ +
+ btn + 展开/收起 +
+
+ ); +}; + +export default Sidebar; diff --git a/src/normalize.css b/src/global.css similarity index 96% rename from src/normalize.css rename to src/global.css index ab332a7..e550dee 100644 --- a/src/normalize.css +++ b/src/global.css @@ -153,3 +153,11 @@ html { .arco-icon { font-size: 32; } + +.icon { + width: 1em; + height: 1em; + vertical-align: -0.15em; + fill: currentcolor; + overflow: hidden; +} diff --git a/src/pages/CalendarPage/components/EventDetailModal.jsx b/src/pages/CalendarPage/components/EventDetailModal.jsx index e4fb5bf..b03d438 100644 --- a/src/pages/CalendarPage/components/EventDetailModal.jsx +++ b/src/pages/CalendarPage/components/EventDetailModal.jsx @@ -1,5 +1,5 @@ -import React, { useEffect } from "react"; -import Portal from "@/components/common/Portal"; +import { useEffect } from "react"; +import Portal from "@/components/Portal"; const EventDetailModal = ({ isOpen, event, onClose }) => { // ESC键关闭模态框 diff --git a/src/pages/CompanyJobsPage/components/JobList/index.css b/src/pages/CompanyJobsPage/components/JobList/index.css index 4db65ff..1ccb78b 100644 --- a/src/pages/CompanyJobsPage/components/JobList/index.css +++ b/src/pages/CompanyJobsPage/components/JobList/index.css @@ -1,6 +1,5 @@ .company-jobs-page-left-list { width: 100%; - height: 100%; overflow-y: auto; display: flex; justify-content: flex-start; diff --git a/src/pages/ExpertSupportPage/index.css b/src/pages/ExpertSupportPage/index.css index 2b0e2cf..c624b2d 100644 --- a/src/pages/ExpertSupportPage/index.css +++ b/src/pages/ExpertSupportPage/index.css @@ -31,7 +31,7 @@ gap: 12px; } -.sidebar-title { +.expert-support-sidebar-title { font-size: 18px; font-weight: 600; color: #111827; @@ -164,7 +164,8 @@ } @keyframes pulse-red { - 0%, 100% { + 0%, + 100% { opacity: 1; transform: scale(1); } @@ -541,32 +542,32 @@ .expert-support-page { grid-template-columns: 300px 1fr; } - + .conversation-sidebar { width: 300px; } - + .sidebar-header { padding: 20px 16px; } - + .new-conversation-btn .btn-text { display: none; } - + .new-conversation-btn { padding: 8px; border-radius: 6px; } - + .chat-header { padding: 20px 24px; } - + .messages-container { padding: 20px 24px; } - + .chat-input-area { padding: 16px 24px; } @@ -577,40 +578,40 @@ grid-template-columns: 1fr; grid-template-rows: auto 1fr; } - + .conversation-sidebar { height: 200px; width: 100%; border-right: none; border-bottom: 1px solid #e5e7eb; } - + .sidebar-header { padding: 16px; flex-direction: column; gap: 8px; align-items: stretch; } - + .new-conversation-btn { width: 100%; justify-content: center; } - + .new-conversation-btn .btn-text { display: inline; } - - .conversation-groups { - overflow-x: auto; - } - - .conversation-list { - display: flex; - gap: 8px; - padding: 8px 16px; - } - + + .conversation-groups { + overflow-x: auto; + } + + .conversation-list { + display: flex; + gap: 8px; + padding: 8px 16px; + } + .conversation-item { min-width: 280px; flex-shrink: 0; @@ -619,27 +620,27 @@ border: 1px solid #e5e7eb; margin-bottom: 0; } - + .chat-header { padding: 16px 20px; } - + .service-title { font-size: 20px; } - + .messages-container { padding: 16px 20px; } - + .chat-input-area { padding: 16px 20px; } - + .message-bubble { max-width: 85%; } - + .chat-title-bar { padding: 16px 20px; flex-direction: column; @@ -648,33 +649,32 @@ } } - @media (max-width: 480px) { - +@media (max-width: 480px) { .chat-header { padding: 12px 16px; } - + .service-title { font-size: 18px; } - + .service-subtitle { font-size: 13px; } - + .messages-container { padding: 12px 16px; } - + .chat-input-area { padding: 12px 16px; } - + .input-container { flex-direction: column; gap: 8px; } - + .send-button { width: 100%; } @@ -703,7 +703,8 @@ border-radius: 12px; width: 90%; max-width: 480px; - box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04); + box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), + 0 10px 10px -5px rgba(0, 0, 0, 0.04); overflow: hidden; animation: modalAppear 0.2s ease-out; } @@ -826,22 +827,22 @@ width: 95%; margin: 0 10px; } - + .modal-header { padding: 16px 20px; } - + .modal-content { padding: 20px; } - + .modal-actions { padding: 16px 20px; flex-direction: column; } - + .btn-secondary, .btn-primary { width: 100%; } -} \ No newline at end of file +} diff --git a/src/pages/ExpertSupportPage/index.jsx b/src/pages/ExpertSupportPage/index.jsx index 738a6ca..3db4c22 100644 --- a/src/pages/ExpertSupportPage/index.jsx +++ b/src/pages/ExpertSupportPage/index.jsx @@ -383,7 +383,7 @@ const ExpertSupportPage = () => { {/* 左侧对话记录区域 */}
-

对话记录

+

对话记录