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:
@@ -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;
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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' }}>
|
||||
|
||||
Reference in New Issue
Block a user