前段

Change-Id: I718d4d07ea03c6d2b6bcbd4d426c5d1af2201bf4
diff --git a/src/components/Personal/ActionCard.jsx b/src/components/Personal/ActionCard.jsx
new file mode 100644
index 0000000..7d3cd4f
--- /dev/null
+++ b/src/components/Personal/ActionCard.jsx
@@ -0,0 +1,23 @@
+import React from 'react';

+import PropTypes from 'prop-types';

+

+const ActionCard = ({ title, subtitle, icon, onClick }) => {

+  return (

+    <div className="action-card" onClick={onClick}>

+      {icon && <div className="action-icon">{icon}</div>}

+      <div className="action-content">

+        <h3>{title}</h3>

+        <p>{subtitle}</p>

+      </div>

+    </div>

+  );

+};

+

+ActionCard.propTypes = {

+  title: PropTypes.string.isRequired,

+  subtitle: PropTypes.string,

+  icon: PropTypes.node,

+  onClick: PropTypes.func

+};

+

+export default ActionCard;
\ No newline at end of file
diff --git a/src/components/Personal/Favorite.jsx b/src/components/Personal/Favorite.jsx
new file mode 100644
index 0000000..97aa1e6
--- /dev/null
+++ b/src/components/Personal/Favorite.jsx
@@ -0,0 +1,47 @@
+import React from 'react';

+import { useNavigate, useLocation } from 'react-router-dom';

+import ActionCard from './ActionCard';

+import './personalSubpage.css';

+

+const Favorites = () => {

+  const navigate = useNavigate();

+  const location = useLocation();

+  // 模拟数据

+  const [favorites] = React.useState([

+    { id: 1, name: '盗梦空间', type: 'movie', added: '2023-10-01' },

+    { id: 2, name: '权力的游戏', type: 'tv', added: '2023-09-15' }

+  ]);

+

+  const handleBack = () => {

+    // 返回个人中心,并携带来源标记

+    navigate('/personal', { 

+      state: { 

+        fromSubpage: true,  // 标记来自子页面

+        dashboardTab: location.state?.dashboardTab // 保留Dashboard的标签页状态

+      },

+      replace: true  // 替换当前历史记录

+    });

+  };

+

+  return (

+    <div className="personal-page">

+      <button className="back-button" onClick={(handleBack)}>

+        &larr; 返回个人中心

+      </button>

+

+      <h2>我的收藏</h2>

+      <div className="resource-grid">

+        {favorites.map(item => (

+          <ActionCard 

+            key={item.id}

+            title={item.name}

+            subtitle={`收藏于 ${item.added}`}

+            onClick={() => console.log('查看详情', item.id)}

+          />

+        ))}

+      </div>

+    </div>

+  );

+};

+

+export default Favorites;
\ No newline at end of file
diff --git a/src/components/Personal/Notice.jsx b/src/components/Personal/Notice.jsx
new file mode 100644
index 0000000..55bc955
--- /dev/null
+++ b/src/components/Personal/Notice.jsx
@@ -0,0 +1,60 @@
+import React from 'react';

+import { useNavigate,useLocation } from 'react-router-dom';

+import './personalSubpage.css';

+

+const Notice = ({ onLogout }) => {

+  const navigate = useNavigate();

+  const location = useLocation();

+  // 模拟数据

+  const [notices] = React.useState([

+    { 

+      id: 1, 

+      title: '积分奖励到账', 

+      content: '您上传的资源《盗梦空间》获得100积分奖励',

+      date: '2023-10-20',

+      read: false

+    },

+    { 

+      id: 2, 

+      title: '系统通知', 

+      content: '服务器将于今晚2:00-4:00进行维护',

+      date: '2023-10-18',

+      read: true

+    }

+  ]);

+

+  const handleBack = () => {

+    // 返回个人中心,并携带来源标记

+    navigate('/personal', { 

+      state: { 

+        fromSubpage: true,  // 标记来自子页面

+        dashboardTab: location.state?.dashboardTab // 保留Dashboard的标签页状态

+      },

+      replace: true  // 替换当前历史记录

+    });

+  };

+

+  return (

+    <div className="subpage-container">

+      <button className="back-button" onClick={(handleBack)}>

+        ← 返回个人中心

+      </button>

+

+      <h2 className="page-title">消息通知</h2>

+      

+      <div className="notice-list">

+        {notices.map(notice => (

+          <div key={notice.id} className={`list-item ${!notice.read ? 'unread' : ''}`}>

+            <div className="notice-header">

+              <h3>{notice.title}</h3>

+              <span className="notice-date">{notice.date}</span>

+            </div>

+            <p className="notice-content">{notice.content}</p>

+          </div>

+        ))}

+      </div>

+    </div>

+  );

+};

+

+export default Notice;
\ No newline at end of file
diff --git a/src/components/Personal/Personal.css b/src/components/Personal/Personal.css
new file mode 100644
index 0000000..c087ac6
--- /dev/null
+++ b/src/components/Personal/Personal.css
@@ -0,0 +1,162 @@
+/* Personal.css */

+.personal-container {

+    max-width: 800px;

+    margin: 0 auto;

+    padding: 20px;

+  }

+  

+  .back-button {

+    background: none;

+    border: none;

+    color: #1890ff;

+    cursor: pointer;

+    font-size: 16px;

+    margin-bottom: 20px;

+    padding: 5px 0;

+  }

+  

+  .profile-card {

+    background: #fff;

+    border-radius: 8px;

+    padding: 20px;

+    margin-bottom: 20px;

+    box-shadow: 0 1px 3px rgba(0,0,0,0.1);

+  }

+  

+  .profile-header {

+    display: flex;

+    align-items: center;

+    margin-bottom: 20px;

+  }

+  

+  .profile-avatar {

+    width: 80px;

+    height: 80px;

+    border-radius: 50%;

+    margin-right: 20px;

+    object-fit: cover;

+  }

+  

+  .profile-info {

+    flex-grow: 1;

+  }

+  

+  .username {

+    font-size: 24px;

+    margin: 0 0 5px;

+  }

+  

+  .user-meta {

+    display: flex;

+    gap: 15px;

+    color: #666;

+    font-size: 14px;

+  }

+  

+  .stats-grid {

+    display: grid;

+    grid-template-columns: repeat(4, 1fr);

+    gap: 15px;

+  }

+  

+  .stat-item {

+    background: #f5f5f5;

+    border-radius: 6px;

+    padding: 15px;

+    text-align: center;

+  }

+  

+  .stat-label {

+    font-size: 14px;

+    color: #666;

+    margin-bottom: 5px;

+  }

+  

+  .stat-value {

+    font-size: 18px;

+    font-weight: bold;

+  }

+  

+  .quota-card {

+    background: #fff;

+    border-radius: 8px;

+    padding: 20px;

+    margin-bottom: 20px;

+    box-shadow: 0 1px 3px rgba(0,0,0,0.1);

+  }

+  

+  .quota-card h3 {

+    margin-top: 0;

+    margin-bottom: 15px;

+  }

+  

+  .quota-info {

+    display: flex;

+    justify-content: space-between;

+    margin-bottom: 10px;

+  }

+  

+  .quota-used {

+    color: #1890ff;

+  }

+  

+  .quota-remaining {

+    color: #52c41a;

+  }

+  

+  .progress-bar {

+    height: 10px;

+    background: #f0f0f0;

+    border-radius: 5px;

+    margin-bottom: 10px;

+    overflow: hidden;

+  }

+  

+  .progress-fill {

+    height: 100%;

+    background: #1890ff;

+    border-radius: 5px;

+    transition: width 0.3s ease;

+  }

+  

+  .quota-total {

+    text-align: right;

+    color: #666;

+    font-size: 14px;

+  }

+  

+  .action-cards {

+    display: grid;

+    grid-template-columns: repeat(2, 1fr);

+    gap: 15px;

+  }

+  

+  .action-card {

+    background: #fff;

+    border-radius: 8px;

+    padding: 20px;

+    box-shadow: 0 1px 3px rgba(0,0,0,0.1);

+    cursor: pointer;

+    transition: transform 0.2s ease;

+  }

+  

+  .action-card:hover {

+    transform: translateY(-3px);

+    box-shadow: 0 4px 8px rgba(0,0,0,0.1);

+  }

+  

+  .action-card h3 {

+    margin-top: 0;

+    color: #1890ff;

+  }

+  

+  .action-card p {

+    color: #666;

+    margin-bottom: 0;

+  }

+

+  .subpage-container {

+    margin-top: 20px;

+    border-top: 1px solid #f0f0f0;

+    padding-top: 20px;

+  }
\ No newline at end of file
diff --git a/src/components/Personal/Personal.jsx b/src/components/Personal/Personal.jsx
new file mode 100644
index 0000000..033952d
--- /dev/null
+++ b/src/components/Personal/Personal.jsx
@@ -0,0 +1,149 @@
+import React from 'react';

+import { useNavigate,useLocation, Outlet } from 'react-router-dom';

+import './Personal.css';

+import ActionCard from './ActionCard';

+

+const Personal = () => {

+  const navigate = useNavigate();

+  const location = useLocation(); // 获取路由信息

+  

+  // 模拟用户数据

+  const userData = {

+    username: 'PT爱好者',

+    avatar: 'https://via.placeholder.com/150',

+    joinDate: '2023-01-15',

+    level: '中级会员',

+    points: 1250,

+    upload: '3.2TB',

+    download: '1.5TB',

+    ratio: '2.13',

+    downloadQuota: {

+      total: 10,  // 10GB

+      used: 3.7,  // 已使用3.7GB

+      remaining: 6.3  // 剩余6.3GB

+    }

+  };

+

+  const features = [

+    { 

+      title: '我的收藏', 

+      description: '查看收藏的资源',

+      path: '/personal/Favorite' // 相对路径

+    },

+    { 

+      title: '上传记录', 

+      description: '管理上传的资源',

+      path: '/personal/Upload' 

+    },

+    { 

+      title: '消息通知', 

+      description: '查看系统消息',

+      path: '/personal/Notice' 

+    },

+    { 

+      title: '设置', 

+      description: '修改个人资料',

+      path: '/personal/Setting' 

+    }

+  ];

+

+  const handleCardClick = (path) => {

+    navigate(path); // 相对导航

+  };

+

+  const handleBack = () => {

+    if (location.state?.fromSubpage) {

+      // 如果是从子页面返回,则继续返回到Dashboard

+      navigate(`/dashboard/${location.state.dashboardTab || ''}`, {

+        replace: true

+      });

+    } else {

+      // 普通返回逻辑

+      navigate(-1);

+    }

+  };

+

+  return (

+    <div className="personal-container">

+      {/* 返回按钮 */}

+      <button className="back-button" onClick={handleBack}>

+        &larr; 返回

+      </button>

+      

+      {/* 用户基本信息卡片 */}

+      <div className="profile-card">

+        <div className="profile-header">

+          <img 

+            src={userData.avatar} 

+            alt={userData.username} 

+            className="profile-avatar"

+          />

+          <div className="profile-info">

+            <h2 className="username">{userData.username}</h2>

+            <div className="user-meta">

+              <span>加入时间: {userData.joinDate}</span>

+              <span>等级: {userData.level}</span>

+            </div>

+          </div>

+        </div>

+        

+        {/* 用户数据统计 */}

+        <div className="stats-grid">

+          <div className="stat-item">

+            <div className="stat-label">积分</div>

+            <div className="stat-value">{userData.points}</div>

+          </div>

+          <div className="stat-item">

+            <div className="stat-label">上传量</div>

+            <div className="stat-value">{userData.upload}</div>

+          </div>

+          <div className="stat-item">

+            <div className="stat-label">下载量</div>

+            <div className="stat-value">{userData.download}</div>

+          </div>

+          <div className="stat-item">

+            <div className="stat-label">分享率</div>

+            <div className="stat-value">{userData.ratio}</div>

+          </div>

+        </div>

+      </div>

+      

+      {/* 下载额度卡片 */}

+      <div className="quota-card">

+        <h3>下载额度</h3>

+        <div className="quota-info">

+          <span className="quota-used">{userData.downloadQuota.used}GB 已使用</span>

+          <span className="quota-remaining">{userData.downloadQuota.remaining}GB 剩余</span>

+        </div>

+        <div className="progress-bar">

+          <div 

+            className="progress-fill"

+            style={{ width: `${(userData.downloadQuota.used / userData.downloadQuota.total) * 100}%` }}

+          ></div>

+        </div>

+        <div className="quota-total">总额度: {userData.downloadQuota.total}GB</div>

+      </div>

+      

+      {/* 功能卡片区 */}

+      <div className="action-cards">

+        {features.map((feature) => (

+          <div 

+            key={feature.path}

+            className="action-card"

+            onClick={() => handleCardClick(feature.path)}

+          >

+            <h3>{feature.title}</h3>

+            <p>{feature.description}</p>

+          </div>

+        ))}

+      </div>

+

+      {/* 子路由出口 */}

+      <div className="subpage-container">

+        <Outlet />

+      </div>

+    </div>

+  );

+};

+

+export default Personal;
\ No newline at end of file
diff --git a/src/components/Personal/Setting.jsx b/src/components/Personal/Setting.jsx
new file mode 100644
index 0000000..967be6c
--- /dev/null
+++ b/src/components/Personal/Setting.jsx
@@ -0,0 +1,99 @@
+import React, { useState } from 'react';

+import { useNavigate,useLocation } from 'react-router-dom';

+import './personalSubpage.css';

+

+const Setting = ({ onLogout }) => {

+  const navigate = useNavigate();

+  const location = useLocation();

+  // 模拟数据

+  const [formData, setFormData] = useState({

+    username: 'user123',

+    email: 'user@example.com',

+    notification: true

+  });

+

+  const handleChange = (e) => {

+    const { name, value, type, checked } = e.target;

+    setFormData(prev => ({

+      ...prev,

+      [name]: type === 'checkbox' ? checked : value

+    }));

+  };

+

+  const handleSubmit = (e) => {

+    e.preventDefault();

+    alert('设置已保存');

+  };

+

+  const handleBack = () => {

+    // 返回个人中心,并携带来源标记

+    navigate('/personal', { 

+      state: { 

+        fromSubpage: true,  // 标记来自子页面

+        dashboardTab: location.state?.dashboardTab // 保留Dashboard的标签页状态

+      },

+      replace: true  // 替换当前历史记录

+    });

+  };

+

+  return (

+    <div className="subpage-container">

+      <button className="back-button" onClick={(handleBack)}>

+        ← 返回个人中心

+      </button>

+

+      <h2 className="page-title">账号设置</h2>

+      

+      <form onSubmit={handleSubmit}>

+        <div className="form-group">

+          <label className="form-label">用户名</label>

+          <input

+            type="text"

+            name="username"

+            value={formData.username}

+            onChange={handleChange}

+            className="form-input"

+          />

+        </div>

+

+        <div className="form-group">

+          <label className="form-label">电子邮箱</label>

+          <input

+            type="email"

+            name="email"

+            value={formData.email}

+            onChange={handleChange}

+            className="form-input"

+          />

+        </div>

+

+        <div className="form-group">

+          <label className="form-label">

+            <input

+              type="checkbox"

+              name="notification"

+              checked={formData.notification}

+              onChange={handleChange}

+            />

+            接收邮件通知

+          </label>

+        </div>

+

+        <div className="form-actions">

+          <button type="submit" className="action-btn">

+            保存设置

+          </button>

+          <button 

+            type="button" 

+            className="action-btn danger-btn"

+            onClick={onLogout}

+          >

+            退出登录

+          </button>

+        </div>

+      </form>

+    </div>

+  );

+};

+

+export default Setting;
\ No newline at end of file
diff --git a/src/components/Personal/Upload.jsx b/src/components/Personal/Upload.jsx
new file mode 100644
index 0000000..4d6e934
--- /dev/null
+++ b/src/components/Personal/Upload.jsx
@@ -0,0 +1,65 @@
+import React from 'react';

+import { useNavigate,useLocation } from 'react-router-dom';

+import './personalSubpage.css';

+

+const Upload = ({ onLogout }) => {

+  const navigate = useNavigate();

+  const location = useLocation();

+  const [uploads] = React.useState([

+    { id: 1, name: '星际穿越', status: '已发布', date: '2023-10-15', size: '15.2GB' },

+    { id: 2, name: '黑暗骑士', status: '审核中', date: '2023-10-18', size: '12.7GB' }

+  ]);

+

+  const handleBack = () => {

+    // 返回个人中心,并携带来源标记

+    navigate('/personal', { 

+      state: { 

+        fromSubpage: true,  // 标记来自子页面

+        dashboardTab: location.state?.dashboardTab // 保留Dashboard的标签页状态

+      },

+      replace: true  // 替换当前历史记录

+    });

+  };

+  return (

+    <div className="subpage-container">

+      <button className="back-button" onClick={(handleBack)}>

+        ← 返回个人中心

+      </button>

+

+      <h2 className="page-title">上传记录</h2>

+      

+      <table className="uploads-table">

+        <thead>

+          <tr>

+            <th>资源名称</th>

+            <th>大小</th>

+            <th>状态</th>

+            <th>上传时间</th>

+            <th>操作</th>

+          </tr>

+        </thead>

+        <tbody>

+          {uploads.map(item => (

+            <tr key={item.id} className="list-item">

+              <td>{item.name}</td>

+              <td>{item.size}</td>

+              <td>

+                <span className={`status-badge ${

+                  item.status === '已发布' ? 'published' : 'pending'

+                }`}>

+                  {item.status}

+                </span>

+              </td>

+              <td>{item.date}</td>

+              <td>

+                <button className="action-btn">详情</button>

+              </td>

+            </tr>

+          ))}

+        </tbody>

+      </table>

+    </div>

+  );

+};

+

+export default Upload;
\ No newline at end of file
diff --git a/src/components/Personal/personalSubpage.css b/src/components/Personal/personalSubpage.css
new file mode 100644
index 0000000..a8e5638
--- /dev/null
+++ b/src/components/Personal/personalSubpage.css
@@ -0,0 +1,161 @@
+/* 基础布局 */

+.subpage-container {

+    max-width: 1200px;

+    margin: 0 auto;

+    padding: 20px;

+    background: white;

+    border-radius: 8px;

+    box-shadow: 0 2px 8px rgba(0,0,0,0.1);

+  }

+  

+  .back-button {

+    background: none;

+    border: none;

+    color: #1890ff;

+    font-size: 16px;

+    cursor: pointer;

+    margin-bottom: 20px;

+    display: flex;

+    align-items: center;

+    gap: 5px;

+  }

+  

+  .back-button:hover {

+    color: #40a9ff;

+  }

+  

+  .page-title {

+    color: #333;

+    border-bottom: 1px solid #f0f0f0;

+    padding-bottom: 10px;

+    margin-bottom: 20px;

+  }

+  

+  /* 列表项样式 */

+  .list-item {

+    padding: 15px;

+    border-bottom: 1px solid #f5f5f5;

+    transition: background 0.3s;

+  }

+  

+  .list-item:hover {

+    background: #f9f9f9;

+  }

+  

+  /* 表单样式 */

+  .form-group {

+    margin-bottom: 20px;

+  }

+  

+  .form-label {

+    display: block;

+    margin-bottom: 8px;

+    font-weight: 500;

+  }

+  

+  .form-input {

+    width: 100%;

+    padding: 10px;

+    border: 1px solid #d9d9d9;

+    border-radius: 4px;

+  }

+  

+  /* 按钮样式 */

+  .action-btn {

+    padding: 8px 15px;

+    background: #1890ff;

+    color: white;

+    border: none;

+    border-radius: 4px;

+    cursor: pointer;

+    margin-right: 10px;

+  }

+  

+  .action-btn:hover {

+    background: #40a9ff;

+  }

+  

+  .danger-btn {

+    background: #ff4d4f;

+  }

+  

+  .danger-btn:hover {

+    background: #ff7875;

+  }

+

+  /* 收藏列表 */

+.favorite-list {

+    display: flex;

+    flex-direction: column;

+    gap: 10px;

+  }

+  

+  .item-header {

+    margin-bottom: 8px;

+  }

+  

+  .item-meta {

+    color: #666;

+    font-size: 14px;

+  }

+  

+  .item-actions {

+    display: flex;

+    gap: 10px;

+    margin-top: 10px;

+  }

+  

+  /* 上传表格 */

+  .uploads-table {

+    width: 100%;

+    border-collapse: collapse;

+  }

+  

+  .uploads-table th, .uploads-table td {

+    padding: 12px 15px;

+    text-align: left;

+  }

+  

+  .status-badge {

+    padding: 4px 8px;

+    border-radius: 4px;

+    font-size: 12px;

+  }

+  

+  .status-badge.published {

+    background: #f6ffed;

+    color: #52c41a;

+  }

+  

+  .status-badge.pending {

+    background: #fff7e6;

+    color: #fa8c16;

+  }

+  

+  /* 消息通知 */

+  .notice-header {

+    display: flex;

+    justify-content: space-between;

+    margin-bottom: 5px;

+  }

+  

+  .notice-date {

+    color: #999;

+    font-size: 14px;

+  }

+  

+  .notice-content {

+    color: #666;

+    margin: 0;

+  }

+  

+  .unread {

+    background: #f0f7ff;

+  }

+  

+  /* 设置表单 */

+  .form-actions {

+    margin-top: 30px;

+    display: flex;

+    gap: 15px;

+  }
\ No newline at end of file