refactor: redesign Profile page, fix navigation & interaction logic

Profile page:
- Add page title '我的账户' in settings-section h2
- Restructure points section: horizontal action buttons (充值/明细/定价)
- Remove '产品定价' from menu list (already in points section)
- Remove '更多' heading

Interaction fixes:
- Profile logout now uses onNavigate('logout') → App.tsx ConfirmModal
- Sidebar dropdown: only '我的账户' + '退出登录' (others go through Profile)
- Add back button to usage-detail / system-update / about-us → navigate to profile
- content-management click: only expand/collapse, no auto-navigate to first child

CSS:
- Add --bg-secondary / --bg-tertiary to variables.css
- Add page-back-btn CSS class
- Convert profile-points-actions to horizontal row layout
This commit is contained in:
小鱼开发
2026-05-22 11:27:41 +08:00
parent abf03712a5
commit 5386a1dbf4
6 changed files with 65 additions and 30 deletions
@@ -35,9 +35,6 @@ interface SidebarProps {
const userMenuItems = [
{ id: 'profile', label: '我的账户' },
{ id: 'usage-detail', label: '使用明细' },
{ id: 'system-update', label: '系统设置' },
{ id: 'about-us', label: '关于我们' },
];
export default function Sidebar({ currentPath, onNavigate }: SidebarProps) {
@@ -83,10 +80,6 @@ export default function Sidebar({ currentPath, onNavigate }: SidebarProps) {
const handleClick = (item: NavItem) => {
if (item.children) {
toggleExpand(item.id);
// Navigate to first child
if (!expandedItems.has(item.id)) {
onNavigate(item.children[0].id);
}
} else {
onNavigate(item.id);
}
@@ -475,7 +475,7 @@
.profile-points-section {
display: flex;
align-items: flex-end;
flex-direction: column;
gap: var(--spacing-lg);
padding: 20px 28px;
}
@@ -525,14 +525,14 @@
color: var(--text-tertiary);
}
.profile-points-actions {
.profile-points-actions-row {
display: flex;
flex-direction: column;
gap: 10px;
gap: var(--spacing-sm);
flex-wrap: wrap;
}
.profile-points-actions .btn {
padding: 10px 24px;
.profile-points-actions-row .btn {
padding: 10px 20px;
font-size: var(--font-sm);
white-space: nowrap;
}
@@ -754,6 +754,32 @@
overflow: hidden;
}
/* ── 页面返回按钮 ── */
.page-back-btn {
display: inline-flex;
align-items: center;
gap: 6px;
padding: var(--spacing-sm) var(--spacing-md);
border: none;
background: transparent;
color: var(--text-secondary);
font-size: var(--font-sm);
font-family: inherit;
cursor: pointer;
border-radius: var(--radius-md);
transition: all var(--transition-fast);
margin-bottom: var(--spacing-sm);
}
.page-back-btn:hover {
color: var(--primary);
background: var(--bg-hover);
}
.page-back-btn svg {
flex-shrink: 0;
}
.profile-edit-wrap {
display: flex;
align-items: center;
+9 -16
View File
@@ -38,12 +38,6 @@ const FileTextIcon = () => (
</svg>
);
const DollarSignIcon = () => (
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
<line x1="12" y1="1" x2="12" y2="23" /><path d="M17 5H9.5a3.5 3.5 0 0 0 0 7h5a3.5 3.5 0 0 1 0 7H6" />
</svg>
);
const SettingsIcon = () => (
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
<circle cx="12" cy="12" r="3" /><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z" />
@@ -77,7 +71,6 @@ const EditIcon = () => (
export default function Profile() {
const { navigate } = useNavigation();
const authUser = useAuthStore((s) => s.user);
const logout = useAuthStore((s) => s.logout);
const [user, setUser] = useState<UserProfile | null>(null);
const [balance, setBalance] = useState<PointBalance | null>(null);
@@ -156,10 +149,8 @@ export default function Profile() {
}
};
const handleLogout = async () => {
if (!window.confirm('确定要退出登录吗?')) { return; }
await logout();
window.location.reload();
const handleLogout = () => {
navigate('logout');
};
const displayName = user?.nickname || authUser?.nickname || '用户';
@@ -168,14 +159,17 @@ export default function Profile() {
const menuItems = [
{ label: '使用明细', icon: <FileTextIcon />, onClick: () => navigate('usage-detail') },
{ label: '产品定价', icon: <DollarSignIcon />, onClick: handleOpenPricing },
{ label: '系统设置', icon: <SettingsIcon />, onClick: () => navigate('system-update') },
{ label: '关于我们', icon: <InfoIcon />, onClick: () => navigate('about-us') },
];
return (
<div className="settings-page">
{/* 个人资料卡片 */}
<div className="settings-section">
<h2></h2>
</div>
{/* 个人信息 + 积分 */}
<div className="card profile-card-flat">
{/* 用户区 */}
<div className="profile-user-section">
@@ -253,7 +247,7 @@ export default function Profile() {
</div>
</div>
<div className="profile-points-actions">
<div className="profile-points-actions-row">
<button className="btn btn-primary btn-sm" onClick={() => setShowRechargeModal(true)}>
</button>
@@ -273,9 +267,8 @@ export default function Profile() {
</div>
</div>
{/* 功能入口 */}
{/* 设置入口 */}
<div className="profile-section-spaced">
<h3 className="profile-section-title"></h3>
<div className="card profile-menu-list">
{menuItems.map((item) => (
<button key={item.label} className="profile-menu-item" onClick={item.onClick}>
@@ -1,4 +1,5 @@
import { useEffect, useState, useCallback, useRef } from 'react';
import { useNavigation } from '../../contexts/NavigationContext';
import { pointsApi, type PointTransaction } from '../../api/modules/points';
import DateRangePicker from '../../components/DatePicker/DateRangePicker';
import '../ContentManagement/ContentManagement.css';
@@ -360,8 +361,16 @@ export default function UsageDetail() {
return `${sign}${Math.abs(amount)}`;
};
const { navigate } = useNavigation();
return (
<div className="settings-page">
<button className="page-back-btn" onClick={() => navigate('profile')}>
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
<polyline points="15 18 9 12 15 6" />
</svg>
</button>
<div className="settings-section">
<h2></h2>
+7 -1
View File
@@ -9,7 +9,7 @@ import { toast } from '../../store/uiStore';
const CURRENT_VERSION = __APP_VERSION__;
export default function AboutUs() {
const { appEnvironment } = useNavigation();
const { navigate, appEnvironment } = useNavigation();
const [showModal, setShowModal] = useState(false);
// 版本号连击检测
@@ -56,6 +56,12 @@ export default function AboutUs() {
return (
<div className="settings-page">
<button className="page-back-btn" onClick={() => navigate('profile')}>
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
<polyline points="15 18 9 12 15 6" />
</svg>
</button>
<div className="settings-section">
<h2></h2>
<div className="card" style={{ padding: 0, overflow: 'hidden' }}>
@@ -6,6 +6,7 @@
*/
import { useState } from 'react';
import { useNavigation } from '../../contexts/NavigationContext';
import { check, type Update, type DownloadEvent } from '@tauri-apps/plugin-updater';
import { relaunch } from '@tauri-apps/plugin-process';
import '../ContentManagement/ContentManagement.css';
@@ -13,6 +14,7 @@ import '../ContentManagement/ContentManagement.css';
const CURRENT_VERSION = __APP_VERSION__;
export default function SystemUpdate() {
const { navigate } = useNavigation();
const [checking, setChecking] = useState(false);
const [checkResult, setCheckResult] = useState<'none' | 'latest' | 'available'>('none');
const [updateInfo, setUpdateInfo] = useState<Update | null>(null);
@@ -107,6 +109,12 @@ export default function SystemUpdate() {
return (
<div className="settings-page">
<button className="page-back-btn" onClick={() => navigate('profile')}>
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
<polyline points="15 18 9 12 15 6" />
</svg>
</button>
<div className="settings-section">
<h2></h2>
<div className="card" style={{ padding: 0, overflow: 'hidden' }}>