From 4419c9265f6b64d896cb2031db315e3b5fe30c8e Mon Sep 17 00:00:00 2001 From: Pine Date: Thu, 27 Aug 2026 20:15:01 +0800 Subject: [PATCH] =?UTF-8?q?feat(user-center):=20=E9=A1=B6=E6=A0=8F?= =?UTF-8?q?=E7=94=A8=E6=88=B7=E4=B8=8B=E6=8B=89=20+=20=E7=8E=BB=E7=92=83?= =?UTF-8?q?=E4=B8=AA=E4=BA=BA=E4=B8=AD=E5=BF=83=20+=20=E6=88=91=E7=9A=84?= =?UTF-8?q?=E6=8A=A5=E5=90=8D/=E4=BB=BB=E5=8A=A1/=E5=9B=AD=E5=8C=BA/?= =?UTF-8?q?=E8=AE=A4=E8=AF=81=E9=A1=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Header: 登录后头像+用户名玻璃下拉(个人中心/我的报名/我的任务/我的园区/认证中心/退出);未登录显示登录/注册;移动端同步 - Profile: 玻璃用户卡(头像上传/身份标签) + 我的服务宫格(实时状态) + 报名资料 + 登录绑定 - 新增 my-bookings/my-tasks/my-park/my-cert 四页;auth.pickUser 保留 role/account_type;booking/park 新增 myBookings/myParkAdmission - 新增 user-center.css 玻璃样式;vite build 通过 --- website/src/App.jsx | 10 +- website/src/components/organisms/index.jsx | 70 ++++++++--- website/src/main.jsx | 1 + website/src/services/auth.js | 6 + website/src/services/booking.js | 11 ++ website/src/services/park.js | 11 ++ website/src/styles/user-center.css | 109 ++++++++++++++++ website/src/user/MyBookings.jsx | 76 +++++++++++ website/src/user/MyCert.jsx | 68 ++++++++++ website/src/user/MyPark.jsx | 69 ++++++++++ website/src/user/MyTasks.jsx | 60 +++++++++ website/src/user/Profile.jsx | 140 ++++++++++++++++++--- 12 files changed, 591 insertions(+), 40 deletions(-) create mode 100644 website/src/styles/user-center.css create mode 100644 website/src/user/MyBookings.jsx create mode 100644 website/src/user/MyCert.jsx create mode 100644 website/src/user/MyPark.jsx create mode 100644 website/src/user/MyTasks.jsx diff --git a/website/src/App.jsx b/website/src/App.jsx index edd8438..dcb861d 100644 --- a/website/src/App.jsx +++ b/website/src/App.jsx @@ -35,6 +35,10 @@ import Tasks from './user/Tasks'; import TaskDetail from './user/TaskDetail'; import Enterprise from './platform/Enterprise'; import ParkCenter from './platform/ParkCenter'; +import MyBookings from './user/MyBookings'; +import MyTasks from './user/MyTasks'; +import MyPark from './user/MyPark'; +import MyCert from './user/MyCert'; /* 公开路由(无需登录) */ const PUBLIC_ROUTES = [ @@ -54,7 +58,11 @@ const PUBLIC_ROUTES = [ { path: 'task-detail', Page: TaskDetail }, { path: 'enterprise', Page: Enterprise }, { path: 'park-center', Page: ParkCenter }, - { path: 'profile', Page: Profile } + { path: 'profile', Page: Profile }, + { path: 'my-bookings', Page: MyBookings }, + { path: 'my-tasks', Page: MyTasks }, + { path: 'my-park', Page: MyPark }, + { path: 'my-cert', Page: MyCert } ]; /* 内部路由(/pine,需登录) */ diff --git a/website/src/components/organisms/index.jsx b/website/src/components/organisms/index.jsx index b16e06f..a1ae50b 100644 --- a/website/src/components/organisms/index.jsx +++ b/website/src/components/organisms/index.jsx @@ -1,24 +1,37 @@ import React from 'react'; import { Logo } from '../atoms'; import { NAV, PINE_NAV } from '../../data/site'; -import { logout, getUser } from '../../services/auth'; +import { logout, getUser, isAuthed } from '../../services/auth'; -/* ---------- 组织:Header(含移动端汉堡 + 菜单) ---------- */ +/* 用户下拉菜单项:个人中心 / 我的报名 / 我的任务 / 我的园区 / 认证中心 */ +const USER_MENU = [ + ['个人中心', '#/profile'], + ['我的报名', '#/my-bookings'], + ['我的任务', '#/my-tasks'], + ['我的园区', '#/my-park'], + ['认证中心', '#/my-cert'], +]; + +/* ---------- 组织:Header(含移动端汉堡 + 菜单 + 用户下拉) ---------- */ export function Header({ active }) { const [open, setOpen] = React.useState(false); + const [menuOpen, setMenuOpen] = React.useState(false); + const menuRef = React.useRef(null); const pine = !!active && active.startsWith('pine'); const nav = pine ? PINE_NAV : NAV; - const user = pine ? null : getUser(); // 公开端显示登录昵称(像素字) + const user = pine ? null : getUser(); // 公开端显示登录昵称 React.useEffect(() => { - const onKey = (e) => { if (e.key === 'Escape') setOpen(false); }; + const onKey = (e) => { if (e.key === 'Escape') { setOpen(false); setMenuOpen(false); } }; const onResize = () => { if (window.innerWidth > 720) setOpen(false); }; + const onDown = (e) => { if (menuRef.current && !menuRef.current.contains(e.target)) setMenuOpen(false); }; window.addEventListener('keydown', onKey); window.addEventListener('resize', onResize); - return () => { window.removeEventListener('keydown', onKey); window.removeEventListener('resize', onResize); }; + window.addEventListener('mousedown', onDown); + return () => { window.removeEventListener('keydown', onKey); window.removeEventListener('resize', onResize); window.removeEventListener('mousedown', onDown); }; }, []); - const doLogout = () => { setOpen(false); logout(); window.location.hash = '#/'; }; + const doLogout = () => { setOpen(false); setMenuOpen(false); logout(); window.location.hash = '#/'; }; return ( <> @@ -33,13 +46,32 @@ export function Header({ active }) {
{pine ? ( + ) : isAuthed() ? ( +
+ + {menuOpen && ( +
+ {USER_MENU.map(([label, href]) => ( + setMenuOpen(false)}>{label} + ))} + 退出登录 +
+ )} +
) : ( - <> - {user && user.role === 'service' && 企业中心} - {user && user.role === 'carrier' && 园区管理} - {user && {user.name}} - 个人中心 - + 登录 / 注册 )}