修改密码、管理员删帖、促销、退出登录

Change-Id: I2cc0e211ac5a04f9e89d0736fadd25541a5fccb9
diff --git a/src/pages/Forum/posts-main/components/PostList.jsx b/src/pages/Forum/posts-main/components/PostList.jsx
index 5d14fdd..533e726 100644
--- a/src/pages/Forum/posts-main/components/PostList.jsx
+++ b/src/pages/Forum/posts-main/components/PostList.jsx
@@ -4,9 +4,9 @@
 import { GoodTwo, Star, Delete } from '@icon-park/react';
 import { likePost } from '../../posts-detail/api';
 import { formatAvatarUrl } from '../../../../components/utils/avatar';
+import { useUser } from '../../../../context/UserContext';  // 路径根据实际调整
 import './PostList.css';
 
-// 修改后的封面图 URL 拼接函数
 const formatImageUrl = (url) => {
   if (!url) return '';
   const filename = url.split('/').pop(); // 提取文件名部分
@@ -14,6 +14,7 @@
 };
 
 const PostList = ({ search }) => {
+  const { user } = useUser();  // 获取当前登录用户
   const [posts, setPosts] = useState([]);
   const [page, setPage] = useState(1);
   const [total, setTotal] = useState(0);
@@ -116,7 +117,17 @@
     }
   };
 
-  const handleDeletePost = async (postNo) => {
+  const handleDeletePost = async (postNo, postUserId) => {
+    // 权限判断:管理员或者帖子发布者本人
+    if (!user) {
+      alert('请先登录');
+      return;
+    }
+    if (user.role !== 'admin' && user.userId !== postUserId) {
+      alert('您没有权限删除此帖子');
+      return;
+    }
+
     if (window.confirm('确定要删除这篇帖子吗?')) {
       try {
         await axios.delete(`/echo/forum/posts/${postNo}/deletePost`);
@@ -144,17 +155,10 @@
                 coverImage = imgs.length > 0 ? formatImageUrl(imgs[0]) : null;
               }
 
+              const canDelete = user && (user.role === 'admin' || user.userId === post.user_id);
+
               return (
                 <div key={post.postNo} className="post-card" style={{ backgroundColor: '#e9ded2' }}>
-                  {/* <div className="user-info">
-                    <img
-                      className="avatar"
-                      src={post.avatarUrl}
-                      alt="头像"
-                    />
-                    <span className="nickname" style={{ color: '#755e50' }}>{post.username}</span>
-                  </div> */}
-
                   <div className="user-info">
                     <Link href={`/information/${post.user_id}`}>
                       <img
@@ -167,7 +171,6 @@
                     <span className="nickname" style={{ color: '#755e50' }}>{post.username}</span>
                   </div>
 
-
                   {coverImage && <img className="cover-image" src={coverImage} alt="封面" />}
 
                   <h3 style={{ color: '#000000' }}>{post.title || '无标题'}</h3>
@@ -182,9 +185,12 @@
                         <Star theme="outline" size="24" fill={post.collected ? '#ffd700' : '#fff'} />
                         <span>{post.collectCount}</span>
                       </button>
-                      <button className="icon-btn" onClick={() => handleDeletePost(post.postNo)}>
-                        <Delete theme="outline" size="24" fill="#333" />
-                      </button>
+
+                      {canDelete && (
+                        <button className="icon-btn" onClick={() => handleDeletePost(post.postNo, post.user_id)}>
+                          <Delete theme="outline" size="24" fill="#333" />
+                        </button>
+                      )}
                     </div>
                   </div>
                   <div className="detail-button-wrapper">
@@ -205,3 +211,4 @@
 };
 
 export default PostList;
+
diff --git a/src/pages/Forum/promotion-part/Promotion.css b/src/pages/Forum/promotion-part/Promotion.css
index 2232ba5..46e7a0d 100644
--- a/src/pages/Forum/promotion-part/Promotion.css
+++ b/src/pages/Forum/promotion-part/Promotion.css
@@ -108,3 +108,49 @@
   font-size: 16px;
   text-align: center;
 }
+
+.dialog-overlay {
+  position: fixed;
+  top: 0;
+  left: 0;
+  right: 0;
+  bottom: 0;
+  background: rgba(0,0,0,0.4);
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  z-index: 999;
+}
+.dialog {
+  background: #fff;
+  padding: 20px;
+  border-radius: 6px;
+  width: 400px;
+  max-width: 90%;
+  box-shadow: 0 0 10px rgba(0,0,0,0.25);
+}
+.form-item {
+  margin-bottom: 12px;
+  display: flex;
+  flex-direction: column;
+}
+.form-item label {
+  font-weight: bold;
+  margin-bottom: 4px;
+}
+.form-item input, .form-item textarea {
+  padding: 6px 8px;
+  font-size: 14px;
+  border: 1px solid #ccc;
+  border-radius: 4px;
+}
+.dialog-buttons {
+  display: flex;
+  justify-content: flex-end;
+  gap: 10px;
+}
+.dialog-buttons button {
+  padding: 6px 16px;
+  cursor: pointer;
+}
+
diff --git a/src/pages/Forum/promotion-part/Promotion.jsx b/src/pages/Forum/promotion-part/Promotion.jsx
index 28fd7ac..8ca4067 100644
--- a/src/pages/Forum/promotion-part/Promotion.jsx
+++ b/src/pages/Forum/promotion-part/Promotion.jsx
@@ -1,17 +1,32 @@
-// export default Promotion;
-
 import React, { useEffect, useState, useRef } from 'react';
 import './Promotion.css';
+import { useUser } from '../../../context/UserContext';
 
 const Promotion = () => {
+  const { user } = useUser();
   const [promotions, setPromotions] = useState([]);
+  const [torrents, setTorrents] = useState([]);
   const [loading, setLoading] = useState(true);
-
   const [promoIndex, setPromoIndex] = useState(0);
   const promoTimerRef = useRef(null);
 
+  // 新增:控制创建对话框显示
+  const [showCreateDialog, setShowCreateDialog] = useState(false);
+
+  // 创建促销活动表单状态
+  const [formData, setFormData] = useState({
+    name: '',
+    startTime: '',
+    endTime: '',
+    discountPercentage: '',
+    uploadCoeff: '',
+    downloadCoeff: '',
+    description: ''
+  });
+
   useEffect(() => {
     fetchData();
+    fetchTorrentList();
   }, []);
 
   useEffect(() => {
@@ -25,11 +40,9 @@
 
   const fetchData = async () => {
     try {
-      // ✅ 获取促销活动列表(新接口)
-      const promoResponse = await fetch(`/seeds/promotions`);
-      const promoJson = await promoResponse.json();
-      console.log('接口返回数据:', promoJson);
-      const promoData = Array.isArray(promoJson?.result) ? promoJson.result : [];
+      const response = await fetch('/seeds/promotions');
+      const json = await response.json();
+      const promoData = Array.isArray(json?.data) ? json.data : [];
       setPromotions(promoData);
     } catch (error) {
       console.error('获取促销活动失败:', error);
@@ -38,19 +51,140 @@
     }
   };
 
+  const fetchTorrentList = async () => {
+    try {
+      const response = await fetch('/seeds/list');
+      const json = await response.json();
+      const torrentList = Array.isArray(json?.data) ? json.data : [];
+      setTorrents(torrentList);
+    } catch (error) {
+      console.error('获取种子列表失败:', error);
+    }
+  };
+
+  // 打开创建促销活动弹窗
+  const openCreateDialog = () => {
+    // 重置表单数据
+    setFormData({
+      name: '',
+      startTime: '',
+      endTime: '',
+      discountPercentage: '',
+      uploadCoeff: '',
+      downloadCoeff: '',
+      description: ''
+    });
+    setShowCreateDialog(true);
+  };
+
+  // 关闭弹窗
+  const closeCreateDialog = () => {
+    setShowCreateDialog(false);
+  };
+
+  // 处理表单输入变化
+  const handleInputChange = (e) => {
+    const { name, value } = e.target;
+    setFormData(prev => ({
+      ...prev,
+      [name]: value
+    }));
+  };
+
+  // 提交创建促销活动
+  const handleCreatePromotion = async () => {
+    if (torrents.length === 0) {
+      alert('没有可用的种子,请先上传种子');
+      return;
+    }
+    if (!formData.name.trim()) {
+      alert('促销名称不能为空');
+      return;
+    }
+    if (!formData.startTime || !formData.endTime) {
+      alert('促销开始时间和结束时间不能为空');
+      return;
+    }
+    if (new Date(formData.startTime) >= new Date(formData.endTime)) {
+      alert('促销结束时间必须晚于开始时间');
+      return;
+    }
+    if (!formData.discountPercentage || isNaN(formData.discountPercentage)) {
+      alert('折扣百分比必须是数字');
+      return;
+    }
+
+    const applicableTorrentIds = torrents.map(t => t.id);
+
+    const newPromo = {
+      name: formData.name,
+      startTime: new Date(formData.startTime).toISOString(),
+      endTime: new Date(formData.endTime).toISOString(),
+      discountPercentage: Number(formData.discountPercentage),
+      uploadCoeff: formData.uploadCoeff ? Number(formData.uploadCoeff) : undefined,
+      downloadCoeff: formData.downloadCoeff ? Number(formData.downloadCoeff) : undefined,
+      applicableTorrentIds: JSON.stringify(applicableTorrentIds), // ✅ 关键修改
+      description: formData.description
+    };
+
+
+    try {
+      const res = await fetch('/seeds/promotions', {
+        method: 'POST',
+        headers: { 'Content-Type': 'application/json' },
+        body: JSON.stringify(newPromo)
+      });
+      const json = await res.json();
+      if (json.code === 200) {
+        alert('促销活动创建成功');
+        fetchData();
+        setShowCreateDialog(false);
+      } else {
+        alert('创建失败: ' + (json.msg || '未知错误'));
+      }
+    } catch (err) {
+      console.error('创建促销失败:', err);
+      alert('创建促销失败');
+    }
+  };
+
+  const handleDeletePromotion = async (promotionId) => {
+    if (!window.confirm('确认删除该促销活动吗?')) return;
+
+    try {
+      const res = await fetch(`/seeds/promotions/${promotionId}`, { method: 'DELETE' });
+      const json = await res.json();
+      if (json.success) {
+        alert('删除成功');
+        fetchData();
+      } else {
+        alert('删除失败: ' + json.message);
+      }
+    } catch (err) {
+      console.error('删除失败:', err);
+    }
+  };
+
+  const isAdmin = user?.role === 'admin';
+  const prevPromo = () => setPromoIndex((promoIndex - 1 + promotions.length) % promotions.length);
+    const nextPromo = () => setPromoIndex((promoIndex + 1) % promotions.length);
+  const currentPromo = promotions[promoIndex];
+
   if (loading) {
     return <div className="promotion-container">加载中...</div>;
   }
 
-  const prevPromo = () => setPromoIndex((promoIndex - 1 + promotions.length) % promotions.length);
-  const nextPromo = () => setPromoIndex((promoIndex + 1) % promotions.length);
-  const currentPromo = promotions[promoIndex];
-
   return (
     <div className="promotion-container carousel-container">
-      {/* 促销活动轮播 */}
       <section className="carousel-section">
         <h2>当前促销活动</h2>
+
+        {isAdmin && (
+          <button className="create-btn" onClick={openCreateDialog}>
+            创建促销活动
+          </button>
+        )}
+
         {promotions.length === 0 || !currentPromo ? (
           <div className="empty-state">暂无促销活动</div>
         ) : (
@@ -76,14 +210,616 @@
               {currentPromo?.description && (
                 <div><strong>描述:</strong>{currentPromo.description}</div>
               )}
+              {isAdmin && (
+                <button className="delete-btn" onClick={() => handleDeletePromotion(currentPromo.id)}>
+                  删除该活动
+                </button>
+              )}
             </div>
             <button className="arrow right" onClick={nextPromo}>&gt;</button>
           </div>
         )}
       </section>
+
+      {/* 创建促销活动弹窗 */}
+      {showCreateDialog && (
+        <div className="dialog-overlay">
+          <div className="dialog">
+            <h3>创建促销活动</h3>
+            <div className="form-item">
+              <label>促销名称:</label>
+              <input
+                type="text"
+                name="name"
+                value={formData.name}
+                onChange={handleInputChange}
+                placeholder="请输入促销名称"
+              />
+            </div>
+            <div className="form-item">
+              <label>开始时间:</label>
+              <input
+                type="datetime-local"
+                name="startTime"
+                value={formData.startTime}
+                onChange={handleInputChange}
+              />
+            </div>
+            <div className="form-item">
+              <label>结束时间:</label>
+              <input
+                type="datetime-local"
+                name="endTime"
+                value={formData.endTime}
+                onChange={handleInputChange}
+              />
+            </div>
+            <div className="form-item">
+              <label>折扣百分比(数字):</label>
+              <input
+                type="number"
+                name="discountPercentage"
+                value={formData.discountPercentage}
+                onChange={handleInputChange}
+                placeholder="例如:20 表示 20% 折扣"
+                min="0"
+                max="100"
+              />
+            </div>
+            <div className="form-item">
+              <label>上传奖励系数(可选):</label>
+              <input
+                type="number"
+                name="uploadCoeff"
+                value={formData.uploadCoeff}
+                onChange={handleInputChange}
+                placeholder="例如:1.5"
+                step="0.1"
+              />
+            </div>
+            <div className="form-item">
+              <label>下载折扣系数(可选):</label>
+              <input
+                type="number"
+                name="downloadCoeff"
+                value={formData.downloadCoeff}
+                onChange={handleInputChange}
+                placeholder="例如:0.8"
+                step="0.1"
+              />
+            </div>
+            <div className="form-item">
+              <label>描述(可选):</label>
+              <textarea
+                name="description"
+                value={formData.description}
+                onChange={handleInputChange}
+                placeholder="促销活动描述"
+                rows={3}
+              />
+            </div>
+            <div className="dialog-buttons">
+              <button onClick={handleCreatePromotion}>确定</button>
+              <button onClick={closeCreateDialog}>取消</button>
+            </div>
+          </div>
+        </div>
+      )}
     </div>
   );
 };
 
 export default Promotion;
 
+
+// import React, { useEffect, useState, useRef } from 'react';
+// import './Promotion.css';
+// import { useUser } from '../../../context/UserContext';
+
+// const Promotion = () => {
+//   const { user } = useUser();
+//   const [promotions, setPromotions] = useState([]);
+//   const [torrents, setTorrents] = useState([]);
+//   const [loading, setLoading] = useState(true);
+//   const [promoIndex, setPromoIndex] = useState(0);
+//   const promoTimerRef = useRef(null);
+
+//   // 新增:控制模态框显示与表单状态
+//   const [showCreateModal, setShowCreateModal] = useState(false);
+//   const [formData, setFormData] = useState({
+//     name: '',
+//     description: '',
+//     discountPercentage: 0,
+//     startTime: '',
+//     endTime: '',
+//     applicableTorrentIds: [],
+//   });
+
+//   useEffect(() => {
+//     fetchData();
+//     fetchTorrentList();
+//   }, []);
+
+//   useEffect(() => {
+//     if (promotions.length === 0) return;
+//     clearInterval(promoTimerRef.current);
+//     promoTimerRef.current = setInterval(() => {
+//       setPromoIndex(prev => (prev + 1) % promotions.length);
+//     }, 5000);
+//     return () => clearInterval(promoTimerRef.current);
+//   }, [promotions]);
+
+//   const fetchData = async () => {
+//     try {
+//       const response = await fetch('/seeds/promotions');
+//       const json = await response.json();
+//       const promoData = Array.isArray(json?.data) ? json.data : [];
+//       setPromotions(promoData);
+//     } catch (error) {
+//       console.error('获取促销活动失败:', error);
+//     } finally {
+//       setLoading(false);
+//     }
+//   };
+
+//   const fetchTorrentList = async () => {
+//     try {
+//       const response = await fetch('/seeds/list');
+//       const json = await response.json();
+//       const torrentList = Array.isArray(json?.data) ? json.data : [];
+//       setTorrents(torrentList);
+//     } catch (error) {
+//       console.error('获取种子列表失败:', error);
+//     }
+//   };
+
+//   // 打开模态框时,重置表单数据,默认设置时间并填入所有种子ID
+//   const openCreateModal = () => {
+//     if (torrents.length === 0) {
+//       alert('没有可用的种子,请先上传种子');
+//       return;
+//     }
+//     setFormData({
+//       name: '',
+//       description: '',
+//       discountPercentage: 20,
+//       startTime: new Date().toISOString().slice(0, 16), // 用于datetime-local输入框,格式 YYYY-MM-DDTHH:mm
+//       endTime: new Date(Date.now() + 7 * 86400000).toISOString().slice(0, 16),
+//       applicableTorrentIds: torrents.map(t => t.id),
+//     });
+//     setShowCreateModal(true);
+//   };
+
+//   // 表单输入处理
+//   const handleInputChange = (e) => {
+//     const { name, value } = e.target;
+//     setFormData(prev => ({
+//       ...prev,
+//       [name]: name === 'discountPercentage' ? Number(value) : value,
+//     }));
+//   };
+
+//   // 点击确定提交创建
+//   const handleCreateConfirm = async () => {
+//     if (!formData.name) {
+//       alert('促销名称不能为空');
+//       return;
+//     }
+//     if (!formData.startTime || !formData.endTime) {
+//       alert('请选择开始时间和结束时间');
+//       return;
+//     }
+//     if (formData.discountPercentage <= 0 || formData.discountPercentage >= 100) {
+//       alert('折扣百分比应在1-99之间');
+//       return;
+//     }
+//     if (!formData.applicableTorrentIds.length) {
+//       alert('请选择适用的种子');
+//       return;
+//     }
+
+//     // 准备发送数据,适配后端字段名
+//     const newPromo = {
+//       name: formData.name,
+//       description: formData.description,
+//       discountPercentage: formData.discountPercentage,
+//       startTime: new Date(formData.startTime).toISOString(),
+//       endTime: new Date(formData.endTime).toISOString(),
+//       applicableTorrentIds: formData.applicableTorrentIds,
+//     };
+
+//     try {
+//       const res = await fetch('/seeds/promotions', {
+//         method: 'POST',
+//         headers: { 'Content-Type': 'application/json' },
+//         body: JSON.stringify(newPromo),
+//       });
+//       const json = await res.json();
+//       if (json.code === 200) {
+//         alert('促销活动创建成功');
+//         setShowCreateModal(false);
+//         fetchData();
+//       } else {
+//         alert('创建失败: ' + (json.msg || '未知错误'));
+//       }
+//     } catch (err) {
+//       console.error('创建促销失败:', err);
+//       alert('创建促销失败');
+//     }
+//   };
+
+//   const handleCancel = () => {
+//     setShowCreateModal(false);
+//   };
+
+//   const handleDeletePromotion = async (promotionId) => {
+//     if (!window.confirm('确认删除该促销活动吗?')) return;
+
+//     try {
+//       const res = await fetch(`/seeds/promotions/${promotionId}`, { method: 'DELETE' });
+//       const json = await res.json();
+//       if (json.success) {
+//         alert('删除成功');
+//         fetchData();
+//       } else {
+//         alert('删除失败: ' + json.message);
+//       }
+//     } catch (err) {
+//       console.error('删除失败:', err);
+//     }
+//   };
+
+//   const isAdmin = user?.role === 'admin';
+//   const prevPromo = () => setPromoIndex((promoIndex - 1 + promotions.length) % promotions.length);
+//   const nextPromo = () => setPromoIndex((promoIndex + 1) % promotions.length);
+//   const currentPromo = promotions[promoIndex];
+
+//   if (loading) {
+//     return <div className="promotion-container">加载中...</div>;
+//   }
+
+//   return (
+//     <div className="promotion-container carousel-container">
+//       <section className="carousel-section">
+//         <h2>当前促销活动</h2>
+
+//         {isAdmin && (
+//           <button className="create-btn" onClick={openCreateModal}>
+//             创建促销活动
+//           </button>
+//         )}
+
+//         {promotions.length === 0 || !currentPromo ? (
+//           <div className="empty-state">暂无促销活动</div>
+//         ) : (
+//           <div
+//             className="carousel"
+//             onMouseEnter={() => clearInterval(promoTimerRef.current)}
+//             onMouseLeave={() => {
+//               promoTimerRef.current = setInterval(() => {
+//                 setPromoIndex(prev => (prev + 1) % promotions.length);
+//               }, 3000);
+//             }}
+//           >
+//             <button className="arrow left" onClick={prevPromo}>&lt;</button>
+//             <div className="slide">
+//               <div><strong>促销名称:</strong>{currentPromo?.name ?? '未知'}</div>
+//               <div><strong>促销时间:</strong>
+//                 {currentPromo?.startTime && currentPromo?.endTime
+//                   ? `${new Date(currentPromo.startTime).toLocaleString()} ~ ${new Date(currentPromo.endTime).toLocaleString()}`
+//                   : '未知'}
+//               </div>
+//               <div><strong>折扣百分比:</strong>{currentPromo?.discountPercentage ?? '无'}</div>
+//               {currentPromo?.description && (
+//                 <div><strong>描述:</strong>{currentPromo.description}</div>
+//               )}
+//               {isAdmin && (
+//                 <button className="delete-btn" onClick={() => handleDeletePromotion(currentPromo.id)}>
+//                   删除该活动
+//                 </button>
+//               )}
+//             </div>
+//             <button className="arrow right" onClick={nextPromo}>&gt;</button>
+//           </div>
+//         )}
+//       </section>
+
+//       {/* 创建促销模态框 */}
+//       {showCreateModal && (
+//         <div className="modal-overlay">
+//           <div className="modal-content">
+//             <h3>创建促销活动</h3>
+//             <label>
+//               促销名称:
+//               <input
+//                 type="text"
+//                 name="name"
+//                 value={formData.name}
+//                 onChange={handleInputChange}
+//               />
+//             </label>
+//             <label>
+//               描述:
+//               <textarea
+//                 name="description"
+//                 value={formData.description}
+//                 onChange={handleInputChange}
+//                 rows={3}
+//               />
+//             </label>
+//             <label>
+//               折扣百分比:
+//               <input
+//                 type="number"
+//                 name="discountPercentage"
+//                 value={formData.discountPercentage}
+//                 min={1}
+//                 max={99}
+//                 onChange={handleInputChange}
+//               />
+//             </label>
+//             <label>
+//               开始时间:
+//               <input
+//                 type="datetime-local"
+//                 name="startTime"
+//                 value={formData.startTime}
+//                 onChange={handleInputChange}
+//               />
+//             </label>
+//             <label>
+//               结束时间:
+//               <input
+//                 type="datetime-local"
+//                 name="endTime"
+//                 value={formData.endTime}
+//                 onChange={handleInputChange}
+//               />
+//             </label>
+//             <label>
+//               适用种子ID(逗号分隔,可留空默认所有):
+//               <input
+//                 type="text"
+//                 name="applicableTorrentIds"
+//                 value={formData.applicableTorrentIds.join(',')}
+//                 onChange={(e) => {
+//                   const ids = e.target.value
+//                     .split(',')
+//                     .map(id => id.trim())
+//                     .filter(id => id !== '')
+//                     .map(id => Number(id))
+//                     .filter(id => !isNaN(id));
+//                   setFormData(prev => ({ ...prev, applicableTorrentIds: ids }));
+//                 }}
+//               />
+//             </label>
+
+//             <div className="modal-buttons">
+//               <button onClick={handleCreateConfirm}>确定</button>
+//               <button onClick={handleCancel}>取消</button>
+//             </div>
+//           </div>
+//         </div>
+//       )}
+
+//       {/* 模态框简单样式 */}
+//       <style>{`
+//         .modal-overlay {
+//           position: fixed;
+//           top: 0; left: 0; right: 0; bottom: 0;
+//           background: rgba(0,0,0,0.4);
+//           display: flex;
+//           justify-content: center;
+//           align-items: center;
+//           z-index: 999;
+//         }
+//         .modal-content {
+//           background: white;
+//           padding: 20px;
+//           border-radius: 6px;
+//           width: 320px;
+//           max-width: 90%;
+//         }
+//         .modal-content label {
+//           display: block;
+//           margin-bottom: 10px;
+//           font-size: 14px;
+//         }
+//         .modal-content input[type="text"],
+//         .modal-content input[type="number"],
+//         .modal-content input[type="datetime-local"],
+//         .modal-content textarea {
+//           width: 100%;
+//           box-sizing: border-box;
+//           padding: 5px;
+//           font-size: 14px;
+//           margin-top: 4px;
+//         }
+//         .modal-buttons {
+//           margin-top: 15px;
+//           text-align: right;
+//         }
+//         .modal-buttons button {
+//           margin-left: 10px;
+//           padding: 6px 12px;
+//           font-size: 14px;
+//         }
+//       `}</style>
+//     </div>
+//   );
+// };
+
+// export default Promotion;
+
+
+// import React, { useEffect, useState, useRef } from 'react';
+// import './Promotion.css';
+// import { useUser } from '../../../context/UserContext';
+
+// const Promotion = () => {
+//   const { user } = useUser();
+//   const [promotions, setPromotions] = useState([]);
+//   const [torrents, setTorrents] = useState([]); // 新增,存放种子列表
+//   const [loading, setLoading] = useState(true);
+//   const [promoIndex, setPromoIndex] = useState(0);
+//   const promoTimerRef = useRef(null);
+
+//   useEffect(() => {
+//     fetchData();
+//     fetchTorrentList();  // 新增,获取种子列表
+//   }, []);
+
+//   useEffect(() => {
+//     if (promotions.length === 0) return;
+//     clearInterval(promoTimerRef.current);
+//     promoTimerRef.current = setInterval(() => {
+//       setPromoIndex(prev => (prev + 1) % promotions.length);
+//     }, 5000);
+//     return () => clearInterval(promoTimerRef.current);
+//   }, [promotions]);
+
+//   // 获取促销数据
+//   const fetchData = async () => {
+//     try {
+//       const response = await fetch('/seeds/promotions');
+//       const json = await response.json();
+//       const promoData = Array.isArray(json?.data) ? json.data : [];
+//       setPromotions(promoData);
+//     } catch (error) {
+//       console.error('获取促销活动失败:', error);
+//     } finally {
+//       setLoading(false);
+//     }
+//   };
+
+//   // 获取种子列表,赋值给torrents
+//   const fetchTorrentList = async () => {
+//     try {
+//       const response = await fetch('/seeds/list');
+//       const json = await response.json();
+//       const torrentList = Array.isArray(json?.data) ? json.data : [];
+//       setTorrents(torrentList);
+//     } catch (error) {
+//       console.error('获取种子列表失败:', error);
+//     }
+//   };
+
+//   // 创建促销时,自动使用当前种子的id列表,而不是写死
+//   const handleCreatePromotion = async () => {
+//     if (torrents.length === 0) {
+//       alert('没有可用的种子,请先上传种子');
+//       return;
+//     }
+
+//     const applicableTorrentIds = torrents.map(t => t.id); // 获取所有种子id数组
+
+//     const newPromo = {
+//       name: '测试促销活动',
+//       startTime: new Date().toISOString(),
+//       endTime: new Date(Date.now() + 7 * 86400000).toISOString(),
+//       discountPercentage: 20,
+//       applicableTorrentIds: applicableTorrentIds, // 动态传入种子ID数组
+//       description: '这是一个测试促销活动'
+//     };
+
+//     try {
+//       const res = await fetch('/seeds/promotions', {
+//         method: 'POST',
+//         headers: {
+//           'Content-Type': 'application/json'
+//         },
+//         body: JSON.stringify(newPromo)
+//       });
+//       const json = await res.json();
+//       if (json.code === 200) {
+//         alert('促销活动创建成功');
+//         fetchData();
+//       } else {
+//         alert('创建失败: ' + (json.msg || '未知错误'));
+//       }
+//     } catch (err) {
+//       console.error('创建促销失败:', err);
+//       alert('创建促销失败');
+//     }
+//   };
+
+//   const handleDeletePromotion = async (promotionId) => {
+//     if (!window.confirm('确认删除该促销活动吗?')) return;
+
+//     try {
+//       const res = await fetch(`/seeds/promotions/${promotionId}`, {
+//         method: 'DELETE'
+//       });
+//       const json = await res.json();
+//       if (json.success) {
+//         alert('删除成功');
+//         fetchData();
+//       } else {
+//         alert('删除失败: ' + json.message);
+//       }
+//     } catch (err) {
+//       console.error('删除失败:', err);
+//     }
+//   };
+
+//   const isAdmin = user?.role === 'admin';
+//   const prevPromo = () => setPromoIndex((promoIndex - 1 + promotions.length) % promotions.length);
+//   const nextPromo = () => setPromoIndex((promoIndex + 1) % promotions.length);
+//   const currentPromo = promotions[promoIndex];
+
+//   if (loading) {
+//     return <div className="promotion-container">加载中...</div>;
+//   }
+
+//   return (
+//     <div className="promotion-container carousel-container">
+//       <section className="carousel-section">
+//         <h2>当前促销活动</h2>
+
+//         {isAdmin && (
+//           <button className="create-btn" onClick={handleCreatePromotion}>
+//             创建促销活动
+//           </button>
+//         )}
+
+//         {promotions.length === 0 || !currentPromo ? (
+//           <div className="empty-state">暂无促销活动</div>
+//         ) : (
+//           <div
+//             className="carousel"
+//             onMouseEnter={() => clearInterval(promoTimerRef.current)}
+//             onMouseLeave={() => {
+//               promoTimerRef.current = setInterval(() => {
+//                 setPromoIndex(prev => (prev + 1) % promotions.length);
+//               }, 3000);
+//             }}
+//           >
+//             <button className="arrow left" onClick={prevPromo}>&lt;</button>
+//             <div className="slide">
+//               <div><strong>促销名称:</strong>{currentPromo?.name ?? '未知'}</div>
+//               <div><strong>促销时间:</strong>
+//                 {currentPromo?.pStartTime && currentPromo?.pEndTime
+//                   ? `${new Date(currentPromo.pStartTime).toLocaleString()} ~ ${new Date(currentPromo.pEndTime).toLocaleString()}`
+//                   : '未知'}
+//               </div>
+//               <div><strong>上传奖励系数:</strong>{currentPromo?.uploadCoeff ?? '无'}</div>
+//               <div><strong>下载折扣系数:</strong>{currentPromo?.downloadCoeff ?? '无'}</div>
+//               {currentPromo?.description && (
+//                 <div><strong>描述:</strong>{currentPromo.description}</div>
+//               )}
+//               {isAdmin && (
+//                 <button className="delete-btn" onClick={() => handleDeletePromotion(currentPromo.id)}>
+//                   删除该活动
+//                 </button>
+//               )}
+//             </div>
+//             <button className="arrow right" onClick={nextPromo}>&gt;</button>
+//           </div>
+//         )}
+//       </section>
+//     </div>
+//   );
+// };
+
+// export default Promotion;
+
diff --git a/src/pages/InterestGroup/GroupItem.jsx b/src/pages/InterestGroup/GroupItem.jsx
index 8fdc640..736f88d 100644
--- a/src/pages/InterestGroup/GroupItem.jsx
+++ b/src/pages/InterestGroup/GroupItem.jsx
@@ -1,20 +1,182 @@
-import React, { useState } from 'react';
+// import React, { useState, useEffect } from 'react';
+// import { useGroupStore } from '../../context/useGroupStore';
+// import { useUser } from '../../context/UserContext';
+// import CreatePostForm from './CreatePostForm';
+// import axios from 'axios'; // 新增
+
+// const GroupItem = ({ group }) => {
+//   const { handleJoinGroup, joinStatus, setJoinStatus } = useGroupStore(); // 假设你有 setJoinStatus 方法
+//   const { user } = useUser();
+
+//   const userId = user?.userId;
+//   const groupId = group.groupId;
+
+//   const [isMember, setIsMember] = useState(false);
+
+//   useEffect(() => {
+//     setIsMember(joinStatus[groupId] === '加入成功');
+//   }, [joinStatus, groupId]);
+
+//   const [showCreatePost, setShowCreatePost] = useState(false);
+
+//   // 退出小组函数(新增)
+//   const handleLeaveGroup = async () => {
+//     try {
+//       const res = await axios.post(`/echo/groups/${groupId}/leave`, {
+//         user_id: userId,
+//       });
+//       if (res.data.status === 'success') {
+//         setJoinStatus(groupId, '未加入'); // 更新全局状态(需确保 useGroupStore 中有此方法)
+//         setIsMember(false); // 本地状态也更新
+//       } else {
+//         alert(res.data.message || '退出失败');
+//       }
+//     } catch (error) {
+//       console.error('退出小组失败:', error);
+//       alert('退出小组失败');
+//     }
+//   };
+
+//   return (
+//     <div className="group-item">
+//       <div className="group-content">
+//         <img
+//           style={{ width: '40%', height: '40%' }}
+//           src={group.coverImage || 'https://picsum.photos/200/200'}
+//           alt={group.groupName}
+//           className="group-cover"
+//         />
+//         <div className="group-info-right">
+//           <h3>{group.groupName}</h3>
+//           <p style={{ color: '#BA929A' }}>{group.memberCount || 0}人加入了小组</p>
+
+//           {/* 加入/退出按钮逻辑 */}
+//           {userId && (
+//             <button
+//               onClick={() => {
+//                 if (isMember) {
+//                   handleLeaveGroup(); // 已加入 -> 退出
+//                 } else {
+//                   handleJoinGroup(groupId, userId); // 未加入 -> 加入
+//                 }
+//               }}
+//             >
+//               {isMember ? '退出小组' : '+加入小组'}
+//             </button>
+//           )}
+//           {!userId && <button disabled>请登录</button>}
+
+//           {/* 发布帖子按钮 */}
+//           {userId && isMember && (
+//             <button onClick={() => setShowCreatePost(!showCreatePost)}>
+//               +发布帖子
+//             </button>
+//           )}
+//         </div>
+//       </div>
+
+//       <div className="group-description">
+//         <p>{group.description}</p>
+//       </div>
+//       <p>分类:{group.category}</p>
+
+//       {showCreatePost && (
+//         <CreatePostForm 
+//           groupId={groupId}
+//           onClose={() => setShowCreatePost(false)}
+//         />
+//       )}
+//     </div>
+//   );
+// };
+
+// export default GroupItem;
+
+import React, { useState, useEffect } from 'react';
 import { useGroupStore } from '../../context/useGroupStore';
 import { useUser } from '../../context/UserContext';
 import CreatePostForm from './CreatePostForm';
+import axios from 'axios';
 
 const GroupItem = ({ group }) => {
-
-  console.log('group:', group);
-  const { handleJoinGroup, joinStatus } = useGroupStore();
+  const { handleJoinGroup, joinStatus, setJoinStatus } = useGroupStore();
   const { user } = useUser();
-  
+
   const userId = user?.userId;
-  const groupId = group.groupId; // ✅ 使用正确字段
-console.log('加入小组请求 - groupId:', group.group_id, 'userId:', userId);
+  const groupId = group.groupId;
+
+  const [isMember, setIsMember] = useState(false);
+  const [loading, setLoading] = useState(false); // 新增:加载状态
+  const [error, setError] = useState(''); // 新增:错误信息
+
+  useEffect(() => {
+    console.log('joinStatus updated:', joinStatus);
+    setIsMember(joinStatus[groupId] === '加入成功');
+  }, [joinStatus, groupId]);
+
+  // 初始挂载时检查成员状态(新增)
+  useEffect(() => {
+    if (userId && groupId) {
+      checkMembershipStatus();
+    }
+  }, [userId, groupId]);
+
+  // 检查成员状态(新增)
+  const checkMembershipStatus = async () => {
+    try {
+      const res = await axios.get(`/echo/groups/${groupId}/members`);
+      const isMember = res.data.members.some(member => member.user_id === userId);
+      setJoinStatus(groupId, isMember ? '加入成功' : '未加入');
+    } catch (error) {
+      console.error('检查成员状态失败:', error);
+    }
+  };
 
   const [showCreatePost, setShowCreatePost] = useState(false);
 
+  const handleLeaveGroup = async () => {
+    setLoading(true);
+    try {
+      const res = await axios.post(`/echo/groups/${groupId}/leave`, {
+        user_id: userId,
+      });
+      if (res.data.status === 'success') {
+        setJoinStatus(groupId, '未加入');
+        setIsMember(false);
+        // 可选:刷新小组成员计数
+        group.memberCount = (group.memberCount || 0) - 1;
+      } else {
+        setError(res.data.message || '退出失败');
+      }
+    } catch (error) {
+      console.error('退出小组失败:', error);
+      setError('退出小组失败');
+    } finally {
+      setLoading(false);
+    }
+  };
+
+  // 修改加入小组逻辑(新增)
+  const handleJoin = async () => {
+    setLoading(true);
+    try {
+      const res = await handleJoinGroup(groupId, userId);
+      if (res && res.status === 'success') {
+        setJoinStatus(groupId, '加入成功');
+        setIsMember(true);
+        // 可选:刷新小组成员计数
+        group.memberCount = (group.memberCount || 0) + 1;
+      } else {
+        setError(res?.message || '加入失败');
+      }
+    } catch (error) {
+      console.error('加入小组失败:', error);
+      setError('加入小组失败');
+    } finally {
+      setLoading(false);
+    }
+  };
+
   return (
     <div className="group-item">
       <div className="group-content">
@@ -25,18 +187,31 @@
           className="group-cover"
         />
         <div className="group-info-right">
-          <h3>{group.groupName}</h3> {/* ✅ 使用 groupName */}
+          <h3>{group.groupName}</h3>
           <p style={{ color: '#BA929A' }}>{group.memberCount || 0}人加入了小组</p>
 
-          <button
-            onClick={() => handleJoinGroup(groupId, userId)}
-            disabled={joinStatus[groupId] === '加入成功' || !userId}
-          >
-            
-            {joinStatus[groupId] === '加入成功' ? '已加入' : userId ? '+加入小组' : '请登录'}
-          </button>
+          {/* 加入/退出按钮逻辑 */}
+          {userId && (
+            <button
+              onClick={() => {
+                if (isMember) {
+                  handleLeaveGroup();
+                } else {
+                  handleJoin();
+                }
+              }}
+              disabled={loading}
+            >
+              {loading ? '处理中...' : isMember ? '退出小组' : '+加入小组'}
+            </button>
+          )}
+          {!userId && <button disabled>请登录</button>}
 
-          {userId && joinStatus[groupId] === '加入成功' && (
+          {/* 显示错误信息(新增) */}
+          {error && <p style={{ color: 'red' }}>{error}</p>}
+
+          {/* 发布帖子按钮 */}
+          {userId && isMember && (
             <button onClick={() => setShowCreatePost(!showCreatePost)}>
               +发布帖子
             </button>
@@ -59,4 +234,4 @@
   );
 };
 
-export default GroupItem;
\ No newline at end of file
+export default GroupItem;
diff --git a/src/pages/PromotionsPage/PromotionsPage.jsx b/src/pages/PromotionsPage/PromotionsPage.jsx
index 834ad73..ff01ef3 100644
--- a/src/pages/PromotionsPage/PromotionsPage.jsx
+++ b/src/pages/PromotionsPage/PromotionsPage.jsx
@@ -1,435 +1,435 @@
-import React, { useState, useEffect } from 'react';
-import './PromotionsPage.css';
+// import React, { useState, useEffect } from 'react';
+// import './PromotionsPage.css';
 
 
-function PromotionsPage() {
-  const [promotions, setPromotions] = useState([]);
-  const [currentPromotion, setCurrentPromotion] = useState(null);
-  const [isAdmin, setIsAdmin] = useState(false);
-  const [isLoading, setIsLoading] = useState(false);
-  const [error, setError] = useState(null);
-  const [formData, setFormData] = useState({
-    name: '',
-    uploadCoeff: 1,
-    downloadCoeff: 1,
-    timeRange: 0,
-    criteria: 1,
-    pStartTime: '',
-    pEndTime: ''
-  });
-  const [isCreating, setIsCreating] = useState(false);
-  const [currentPage, setCurrentPage] = useState(1);
-  const [perPage] = useState(10);
-  const [totalPromotions, setTotalPromotions] = useState(0);
+// function PromotionsPage() {
+//   const [promotions, setPromotions] = useState([]);
+//   const [currentPromotion, setCurrentPromotion] = useState(null);
+//   const [isAdmin, setIsAdmin] = useState(false);
+//   const [isLoading, setIsLoading] = useState(false);
+//   const [error, setError] = useState(null);
+//   const [formData, setFormData] = useState({
+//     name: '',
+//     uploadCoeff: 1,
+//     downloadCoeff: 1,
+//     timeRange: 0,
+//     criteria: 1,
+//     pStartTime: '',
+//     pEndTime: ''
+//   });
+//   const [isCreating, setIsCreating] = useState(false);
+//   const [currentPage, setCurrentPage] = useState(1);
+//   const [perPage] = useState(10);
+//   const [totalPromotions, setTotalPromotions] = useState(0);
 
-  const getAuthHeaders = () => {
-    const token = localStorage.getItem('token');
-    return {
-      'Authorization': token ? `Bearer ${token}` : '',
-      'Content-Type': 'application/json'
-    };
-  };
+//   const getAuthHeaders = () => {
+//     const token = localStorage.getItem('token');
+//     return {
+//       'Authorization': token ? `Bearer ${token}` : '',
+//       'Content-Type': 'application/json'
+//     };
+//   };
 
-  const fetchPromotions = async (page = 1) => {
-    setIsLoading(true);
-    try {
-      const response = await fetch(`/promotions/list?page=${page}&per_page=${perPage}`, {
-        headers: getAuthHeaders()
-      });
+//   const fetchPromotions = async (page = 1) => {
+//     setIsLoading(true);
+//     try {
+//       const response = await fetch(`/promotions/list?page=${page}&per_page=${perPage}`, {
+//         headers: getAuthHeaders()
+//       });
 
-      if (!response.ok) {
-        throw new Error('获取促销活动失败');
-      }
+//       if (!response.ok) {
+//         throw new Error('获取促销活动失败');
+//       }
 
-      const data = await response.json();
-      if (data.code === 0 && data.result) {
-        setPromotions(data.rows || []);
-        setTotalPromotions(data.total || 0);
-      } else {
-        throw new Error(data.msg || '获取促销活动失败');
-      }
-    } catch (err) {
-      console.error('获取促销活动错误:', err);
-      setError(err.message);
-    } finally {
-      setIsLoading(false);
-    }
-  };
+//       const data = await response.json();
+//       if (data.code === 0 && data.result) {
+//         setPromotions(data.rows || []);
+//         setTotalPromotions(data.total || 0);
+//       } else {
+//         throw new Error(data.msg || '获取促销活动失败');
+//       }
+//     } catch (err) {
+//       console.error('获取促销活动错误:', err);
+//       setError(err.message);
+//     } finally {
+//       setIsLoading(false);
+//     }
+//   };
 
-  const fetchPromotionDetails = async (promoId) => {
-    setIsLoading(true);
-    try {
-      const response = await fetch(`/promotions/${promoId}`, {
-        headers: getAuthHeaders()
-      });
+//   const fetchPromotionDetails = async (promoId) => {
+//     setIsLoading(true);
+//     try {
+//       const response = await fetch(`/promotions/${promoId}`, {
+//         headers: getAuthHeaders()
+//       });
 
-      if (!response.ok) {
-        throw new Error('获取促销详情失败');
-      }
+//       if (!response.ok) {
+//         throw new Error('获取促销详情失败');
+//       }
 
-      const data = await response.json();
-      if (data.code === 0 && data.result) {
-        setCurrentPromotion(data.rows);
-      } else {
-        throw new Error(data.msg || '获取促销详情失败');
-      }
-    } catch (err) {
-      console.error('获取促销详情错误:', err);
-      setError(err.message);
-    } finally {
-      setIsLoading(false);
-    }
-  };
+//       const data = await response.json();
+//       if (data.code === 0 && data.result) {
+//         setCurrentPromotion(data.rows);
+//       } else {
+//         throw new Error(data.msg || '获取促销详情失败');
+//       }
+//     } catch (err) {
+//       console.error('获取促销详情错误:', err);
+//       setError(err.message);
+//     } finally {
+//       setIsLoading(false);
+//     }
+//   };
 
-  const createPromotion = async () => {
-    if (!formData.name || !formData.pStartTime || !formData.pEndTime) {
-      alert('请填写完整活动信息');
-      return;
-    }
+//   const createPromotion = async () => {
+//     if (!formData.name || !formData.pStartTime || !formData.pEndTime) {
+//       alert('请填写完整活动信息');
+//       return;
+//     }
 
-    if (new Date(formData.pStartTime) >= new Date(formData.pEndTime)) {
-      alert('活动时间设置不正确,请重新设定');
-      return;
-    }
+//     if (new Date(formData.pStartTime) >= new Date(formData.pEndTime)) {
+//       alert('活动时间设置不正确,请重新设定');
+//       return;
+//     }
 
-    setIsLoading(true);
-    try {
-      const response = await fetch('/promotions/add', {
-        method: 'POST',
-        headers: getAuthHeaders(),
-        body: JSON.stringify(formData)
-      });
+//     setIsLoading(true);
+//     try {
+//       const response = await fetch('/promotions/add', {
+//         method: 'POST',
+//         headers: getAuthHeaders(),
+//         body: JSON.stringify(formData)
+//       });
 
-      if (!response.ok) {
-        throw new Error('创建促销活动失败');
-      }
+//       if (!response.ok) {
+//         throw new Error('创建促销活动失败');
+//       }
 
-      const data = await response.json();
-      if (data.code === 0 && data.result) {
-        alert(`活动创建成功!活动ID: ${data.msg}`);
-        setIsCreating(false);
-        setFormData({
-          name: '',
-          uploadCoeff: 1,
-          downloadCoeff: 1,
-          timeRange: 0,
-          criteria: 1,
-          pStartTime: '',
-          pEndTime: ''
-        });
-        fetchPromotions();
-      } else {
-        throw new Error(data.msg || '创建促销活动失败');
-      }
-    } catch (err) {
-      console.error('创建促销活动错误:', err);
-      alert(err.message);
-    } finally {
-      setIsLoading(false);
-    }
-  };
+//       const data = await response.json();
+//       if (data.code === 0 && data.result) {
+//         alert(`活动创建成功!活动ID: ${data.msg}`);
+//         setIsCreating(false);
+//         setFormData({
+//           name: '',
+//           uploadCoeff: 1,
+//           downloadCoeff: 1,
+//           timeRange: 0,
+//           criteria: 1,
+//           pStartTime: '',
+//           pEndTime: ''
+//         });
+//         fetchPromotions();
+//       } else {
+//         throw new Error(data.msg || '创建促销活动失败');
+//       }
+//     } catch (err) {
+//       console.error('创建促销活动错误:', err);
+//       alert(err.message);
+//     } finally {
+//       setIsLoading(false);
+//     }
+//   };
 
-  const deletePromotion = async (promoId) => {
-    if (!window.confirm('确定要删除这个促销活动吗?')) {
-      return;
-    }
+//   const deletePromotion = async (promoId) => {
+//     if (!window.confirm('确定要删除这个促销活动吗?')) {
+//       return;
+//     }
 
-    setIsLoading(true);
-    try {
-      const response = await fetch(`/promotions/delete/${promoId}`, {
-        method: 'DELETE',
-        headers: getAuthHeaders()
-      });
+//     setIsLoading(true);
+//     try {
+//       const response = await fetch(`/promotions/delete/${promoId}`, {
+//         method: 'DELETE',
+//         headers: getAuthHeaders()
+//       });
 
-      if (!response.ok) {
-        throw new Error('删除促销活动失败');
-      }
+//       if (!response.ok) {
+//         throw new Error('删除促销活动失败');
+//       }
 
-      const data = await response.json();
-      if (data.code === 0 && data.result) {
-        alert('促销活动删除成功');
-        fetchPromotions();
-        if (currentPromotion && currentPromotion.promoId === promoId) {
-          setCurrentPromotion(null);
-        }
-      } else {
-        throw new Error(data.msg || '删除促销活动失败');
-      }
-    } catch (err) {
-      console.error('删除促销活动错误:', err);
-      alert(err.message);
-    } finally {
-      setIsLoading(false);
-    }
-  };
+//       const data = await response.json();
+//       if (data.code === 0 && data.result) {
+//         alert('促销活动删除成功');
+//         fetchPromotions();
+//         if (currentPromotion && currentPromotion.promoId === promoId) {
+//           setCurrentPromotion(null);
+//         }
+//       } else {
+//         throw new Error(data.msg || '删除促销活动失败');
+//       }
+//     } catch (err) {
+//       console.error('删除促销活动错误:', err);
+//       alert(err.message);
+//     } finally {
+//       setIsLoading(false);
+//     }
+//   };
 
-  const checkAdminStatus = () => {
-    const role = localStorage.getItem('role');
-    setIsAdmin(role === 'admin');
-  };
+//   const checkAdminStatus = () => {
+//     const role = localStorage.getItem('role');
+//     setIsAdmin(role === 'admin');
+//   };
 
-  useEffect(() => {
-    checkAdminStatus();
-    fetchPromotions();
-  }, []);
+//   useEffect(() => {
+//     checkAdminStatus();
+//     fetchPromotions();
+//   }, []);
 
-  const handleInputChange = (e) => {
-    const { name, value } = e.target;
-    setFormData(prev => ({
-      ...prev,
-      [name]: name === 'uploadCoeff' || name === 'downloadCoeff' || name === 'timeRange' || name === 'criteria'
-          ? parseFloat(value)
-          : value
-    }));
-  };
+//   const handleInputChange = (e) => {
+//     const { name, value } = e.target;
+//     setFormData(prev => ({
+//       ...prev,
+//       [name]: name === 'uploadCoeff' || name === 'downloadCoeff' || name === 'timeRange' || name === 'criteria'
+//           ? parseFloat(value)
+//           : value
+//     }));
+//   };
 
-  const handlePageChange = (newPage) => {
-    setCurrentPage(newPage);
-    fetchPromotions(newPage);
-  };
+//   const handlePageChange = (newPage) => {
+//     setCurrentPage(newPage);
+//     fetchPromotions(newPage);
+//   };
 
-  const getPromotionType = (promo) => {
-    if (promo.downloadCoeff === 0 && promo.uploadCoeff > 1) {
-      return '免费';
-    } else if (promo.downloadCoeff < 1 && promo.uploadCoeff > 1) {
-      return '折扣+上传奖励';
-    } else if (promo.downloadCoeff < 1) {
-      return '折扣';
-    } else if (promo.uploadCoeff > 1) {
-      return '上传奖励';
-    }
-    return '普通';
-  };
+//   const getPromotionType = (promo) => {
+//     if (promo.downloadCoeff === 0 && promo.uploadCoeff > 1) {
+//       return '免费';
+//     } else if (promo.downloadCoeff < 1 && promo.uploadCoeff > 1) {
+//       return '折扣+上传奖励';
+//     } else if (promo.downloadCoeff < 1) {
+//       return '折扣';
+//     } else if (promo.uploadCoeff > 1) {
+//       return '上传奖励';
+//     }
+//     return '普通';
+//   };
 
-  return (
-      <div className="promotions-page">
-        <div className="promotions-container">
-          <h1>促销活动</h1>
+//   return (
+//       <div className="promotions-page">
+//         <div className="promotions-container">
+//           <h1>促销活动</h1>
 
-          {isAdmin && (
-              <div className="admin-actions">
-                <button
-                    className="create-button"
-                    onClick={() => setIsCreating(!isCreating)}
-                >
-                  {isCreating ? '取消创建' : '创建新活动'}
-                </button>
-              </div>
-          )}
+//           {isAdmin && (
+//               <div className="admin-actions">
+//                 <button
+//                     className="create-button"
+//                     onClick={() => setIsCreating(!isCreating)}
+//                 >
+//                   {isCreating ? '取消创建' : '创建新活动'}
+//                 </button>
+//               </div>
+//           )}
 
-          {isCreating && isAdmin && (
-              <div className="create-promotion-form">
-                <h2>创建新促销活动</h2>
-                <div className="form-group">
-                  <label>活动名称</label>
-                  <input
-                      type="text"
-                      name="name"
-                      value={formData.name}
-                      onChange={handleInputChange}
-                      placeholder="例如: 春节特惠"
-                  />
-                </div>
+//           {isCreating && isAdmin && (
+//               <div className="create-promotion-form">
+//                 <h2>创建新促销活动</h2>
+//                 <div className="form-group">
+//                   <label>活动名称</label>
+//                   <input
+//                       type="text"
+//                       name="name"
+//                       value={formData.name}
+//                       onChange={handleInputChange}
+//                       placeholder="例如: 春节特惠"
+//                   />
+//                 </div>
 
-                <div className="form-row">
-                  <div className="form-group">
-                    <label>上传量系数</label>
-                    <input
-                        type="number"
-                        name="uploadCoeff"
-                        min="0"
-                        step="0.1"
-                        value={formData.uploadCoeff}
-                        onChange={handleInputChange}
-                    />
-                  </div>
+//                 <div className="form-row">
+//                   <div className="form-group">
+//                     <label>上传量系数</label>
+//                     <input
+//                         type="number"
+//                         name="uploadCoeff"
+//                         min="0"
+//                         step="0.1"
+//                         value={formData.uploadCoeff}
+//                         onChange={handleInputChange}
+//                     />
+//                   </div>
 
-                  <div className="form-group">
-                    <label>下载量系数</label>
-                    <input
-                        type="number"
-                        name="downloadCoeff"
-                        min="0"
-                        step="0.1"
-                        value={formData.downloadCoeff}
-                        onChange={handleInputChange}
-                    />
-                  </div>
-                </div>
+//                   <div className="form-group">
+//                     <label>下载量系数</label>
+//                     <input
+//                         type="number"
+//                         name="downloadCoeff"
+//                         min="0"
+//                         step="0.1"
+//                         value={formData.downloadCoeff}
+//                         onChange={handleInputChange}
+//                     />
+//                   </div>
+//                 </div>
 
-                <div className="form-row">
-                  <div className="form-group">
-                    <label>资源时间范围</label>
-                    <select
-                        name="timeRange"
-                        value={formData.timeRange}
-                        onChange={handleInputChange}
-                    >
-                      <option value="0">全站资源</option>
-                      <option value="1">当天上传</option>
-                      <option value="2">最近两天</option>
-                      <option value="7">最近一周</option>
-                      <option value="30">最近一个月</option>
-                    </select>
-                  </div>
+//                 <div className="form-row">
+//                   <div className="form-group">
+//                     <label>资源时间范围</label>
+//                     <select
+//                         name="timeRange"
+//                         value={formData.timeRange}
+//                         onChange={handleInputChange}
+//                     >
+//                       <option value="0">全站资源</option>
+//                       <option value="1">当天上传</option>
+//                       <option value="2">最近两天</option>
+//                       <option value="7">最近一周</option>
+//                       <option value="30">最近一个月</option>
+//                     </select>
+//                   </div>
 
-                  <div className="form-group">
-                    <label>最低用户等级</label>
-                    <input
-                        type="number"
-                        name="criteria"
-                        min="1"
-                        value={formData.criteria}
-                        onChange={handleInputChange}
-                    />
-                  </div>
-                </div>
+//                   <div className="form-group">
+//                     <label>最低用户等级</label>
+//                     <input
+//                         type="number"
+//                         name="criteria"
+//                         min="1"
+//                         value={formData.criteria}
+//                         onChange={handleInputChange}
+//                     />
+//                   </div>
+//                 </div>
 
-                <div className="form-row">
-                  <div className="form-group">
-                    <label>开始时间</label>
-                    <input
-                        type="datetime-local"
-                        name="pStartTime"
-                        value={formData.pStartTime}
-                        onChange={handleInputChange}
-                    />
-                  </div>
+//                 <div className="form-row">
+//                   <div className="form-group">
+//                     <label>开始时间</label>
+//                     <input
+//                         type="datetime-local"
+//                         name="pStartTime"
+//                         value={formData.pStartTime}
+//                         onChange={handleInputChange}
+//                     />
+//                   </div>
 
-                  <div className="form-group">
-                    <label>结束时间</label>
-                    <input
-                        type="datetime-local"
-                        name="pEndTime"
-                        value={formData.pEndTime}
-                        onChange={handleInputChange}
-                    />
-                  </div>
-                </div>
+//                   <div className="form-group">
+//                     <label>结束时间</label>
+//                     <input
+//                         type="datetime-local"
+//                         name="pEndTime"
+//                         value={formData.pEndTime}
+//                         onChange={handleInputChange}
+//                     />
+//                   </div>
+//                 </div>
 
-                <button
-                    className="submit-button"
-                    onClick={createPromotion}
-                    disabled={isLoading}
-                >
-                  {isLoading ? '创建中...' : '提交创建'}
-                </button>
-              </div>
-          )}
+//                 <button
+//                     className="submit-button"
+//                     onClick={createPromotion}
+//                     disabled={isLoading}
+//                 >
+//                   {isLoading ? '创建中...' : '提交创建'}
+//                 </button>
+//               </div>
+//           )}
 
-          {error && <div className="error-message">{error}</div>}
+//           {error && <div className="error-message">{error}</div>}
 
-          <div className="promotions-grid">
-            {/* 促销活动列表 */}
-            <div className="promotions-list">
-              <h2>当前促销活动</h2>
-              {isLoading && promotions.length === 0 ? (
-                  <div className="loading">加载中...</div>
-              ) : promotions.length === 0 ? (
-                  <div className="no-promotions">暂无促销活动</div>
-              ) : (
-                  <div className="promotion-items">
-                    {promotions.map(promo => (
-                        <div
-                            key={promo.promoId}
-                            className={`promotion-item ${currentPromotion && currentPromotion.promoId === promo.promoId ? 'active' : ''}`}
-                            onClick={() => fetchPromotionDetails(promo.promoId)}
-                        >
-                          <div className="promotion-header">
-                            <h3>{promo.name}</h3>
-                            <span className="promotion-type">{getPromotionType(promo)}</span>
-                          </div>
-                          <div className="promotion-dates">
-                            {new Date(promo.pStartTime).toLocaleString()} - {new Date(promo.pEndTime).toLocaleString()}
-                          </div>
-                          <div className="promotion-coeffs">
-                            <span>上传: {promo.uploadCoeff}x</span>
-                            <span>下载: {promo.downloadCoeff}x</span>
-                          </div>
-                          {isAdmin && (
-                              <button
-                                  className="delete-button"
-                                  onClick={(e) => {
-                                    e.stopPropagation();
-                                    deletePromotion(promo.promoId);
-                                  }}
-                                  disabled={isLoading}
-                              >
-                                删除
-                              </button>
-                          )}
-                        </div>
-                    ))}
-                  </div>
-              )}
+//           <div className="promotions-grid">
+//             {/* 促销活动列表 */}
+//             <div className="promotions-list">
+//               <h2>当前促销活动</h2>
+//               {isLoading && promotions.length === 0 ? (
+//                   <div className="loading">加载中...</div>
+//               ) : promotions.length === 0 ? (
+//                   <div className="no-promotions">暂无促销活动</div>
+//               ) : (
+//                   <div className="promotion-items">
+//                     {promotions.map(promo => (
+//                         <div
+//                             key={promo.promoId}
+//                             className={`promotion-item ${currentPromotion && currentPromotion.promoId === promo.promoId ? 'active' : ''}`}
+//                             onClick={() => fetchPromotionDetails(promo.promoId)}
+//                         >
+//                           <div className="promotion-header">
+//                             <h3>{promo.name}</h3>
+//                             <span className="promotion-type">{getPromotionType(promo)}</span>
+//                           </div>
+//                           <div className="promotion-dates">
+//                             {new Date(promo.pStartTime).toLocaleString()} - {new Date(promo.pEndTime).toLocaleString()}
+//                           </div>
+//                           <div className="promotion-coeffs">
+//                             <span>上传: {promo.uploadCoeff}x</span>
+//                             <span>下载: {promo.downloadCoeff}x</span>
+//                           </div>
+//                           {isAdmin && (
+//                               <button
+//                                   className="delete-button"
+//                                   onClick={(e) => {
+//                                     e.stopPropagation();
+//                                     deletePromotion(promo.promoId);
+//                                   }}
+//                                   disabled={isLoading}
+//                               >
+//                                 删除
+//                               </button>
+//                           )}
+//                         </div>
+//                     ))}
+//                   </div>
+//               )}
 
-              {totalPromotions > perPage && (
-                  <div className="pagination">
-                    <button
-                        disabled={currentPage === 1}
-                        onClick={() => handlePageChange(currentPage - 1)}
-                    >
-                      上一页
-                    </button>
-                    <span>第 {currentPage} 页</span>
-                    <button
-                        disabled={currentPage * perPage >= totalPromotions}
-                        onClick={() => handlePageChange(currentPage + 1)}
-                    >
-                      下一页
-                    </button>
-                  </div>
-              )}
-            </div>
+//               {totalPromotions > perPage && (
+//                   <div className="pagination">
+//                     <button
+//                         disabled={currentPage === 1}
+//                         onClick={() => handlePageChange(currentPage - 1)}
+//                     >
+//                       上一页
+//                     </button>
+//                     <span>第 {currentPage} 页</span>
+//                     <button
+//                         disabled={currentPage * perPage >= totalPromotions}
+//                         onClick={() => handlePageChange(currentPage + 1)}
+//                     >
+//                       下一页
+//                     </button>
+//                   </div>
+//               )}
+//             </div>
 
-            {/* 促销活动详情 */}
-            <div className="promotion-details">
-              {currentPromotion ? (
-                  <>
-                    <h2>{currentPromotion.name}</h2>
-                    <div className="detail-item">
-                      <label>活动ID:</label>
-                      <span>{currentPromotion.promoId}</span>
-                    </div>
-                    <div className="detail-item">
-                      <label>活动时间:</label>
-                      <span>
-                    {new Date(currentPromotion.pStartTime).toLocaleString()} - {new Date(currentPromotion.pEndTime).toLocaleString()}
-                  </span>
-                    </div>
-                    <div className="detail-item">
-                      <label>促销类型:</label>
-                      <span>{getPromotionType(currentPromotion)}</span>
-                    </div>
-                    <div className="detail-item">
-                      <label>上传量系数:</label>
-                      <span>{currentPromotion.uploadCoeff}x</span>
-                    </div>
-                    <div className="detail-item">
-                      <label>下载量系数:</label>
-                      <span>{currentPromotion.downloadCoeff}x</span>
-                    </div>
-                    <div className="detail-item">
-                      <label>适用资源:</label>
-                      <span>
-                    {currentPromotion.timeRange === 0
-                        ? '全站资源'
-                        : `最近${currentPromotion.timeRange}天内上传的资源`}
-                  </span>
-                    </div>
-                    <div className="detail-item">
-                      <label>参与条件:</label>
-                      <span>用户等级 ≥ {currentPromotion.criteria}</span>
-                    </div>
-                  </>
-              ) : (
-                  <div className="no-selection">请从左侧选择一个促销活动查看详情</div>
-              )}
-            </div>
-          </div>
-        </div>
-      </div>
-  );
-}
+//             {/* 促销活动详情 */}
+//             <div className="promotion-details">
+//               {currentPromotion ? (
+//                   <>
+//                     <h2>{currentPromotion.name}</h2>
+//                     <div className="detail-item">
+//                       <label>活动ID:</label>
+//                       <span>{currentPromotion.promoId}</span>
+//                     </div>
+//                     <div className="detail-item">
+//                       <label>活动时间:</label>
+//                       <span>
+//                     {new Date(currentPromotion.pStartTime).toLocaleString()} - {new Date(currentPromotion.pEndTime).toLocaleString()}
+//                   </span>
+//                     </div>
+//                     <div className="detail-item">
+//                       <label>促销类型:</label>
+//                       <span>{getPromotionType(currentPromotion)}</span>
+//                     </div>
+//                     <div className="detail-item">
+//                       <label>上传量系数:</label>
+//                       <span>{currentPromotion.uploadCoeff}x</span>
+//                     </div>
+//                     <div className="detail-item">
+//                       <label>下载量系数:</label>
+//                       <span>{currentPromotion.downloadCoeff}x</span>
+//                     </div>
+//                     <div className="detail-item">
+//                       <label>适用资源:</label>
+//                       <span>
+//                     {currentPromotion.timeRange === 0
+//                         ? '全站资源'
+//                         : `最近${currentPromotion.timeRange}天内上传的资源`}
+//                   </span>
+//                     </div>
+//                     <div className="detail-item">
+//                       <label>参与条件:</label>
+//                       <span>用户等级 ≥ {currentPromotion.criteria}</span>
+//                     </div>
+//                   </>
+//               ) : (
+//                   <div className="no-selection">请从左侧选择一个促销活动查看详情</div>
+//               )}
+//             </div>
+//           </div>
+//         </div>
+//       </div>
+//   );
+// }
 
-export default PromotionsPage;
\ No newline at end of file
+// export default PromotionsPage;
\ No newline at end of file
diff --git a/src/pages/SeedList/SeedList.jsx b/src/pages/SeedList/SeedList.jsx
index a010840..adb2bb3 100644
--- a/src/pages/SeedList/SeedList.jsx
+++ b/src/pages/SeedList/SeedList.jsx
@@ -74,6 +74,7 @@
             const params = buildQueryParams();
             const response = await axios.get('/seeds/list', params);
             // const response = await axios.get('/seeds/list', { params });
+
             const data = response.data;
 
             if (data.code !== 0) {
diff --git a/src/pages/UserCenter/UserDynamics.css b/src/pages/UserCenter/UserDynamics.css
index e69de29..40a11b4 100644
--- a/src/pages/UserCenter/UserDynamics.css
+++ b/src/pages/UserCenter/UserDynamics.css
@@ -0,0 +1,79 @@
+.user-dynamics-container {
+  padding: 40px 10% 40px 5%;
+  max-width: 900px;
+}
+
+.user-dynamics-title {
+  font-size: 24px;
+  font-weight: bold;
+  margin-bottom: 20px;
+}
+
+.user-dynamics-loading,
+.user-dynamics-empty {
+  font-size: 16px;
+  color: #888;
+  margin-top: 20px;
+}
+
+.dynamic-card {
+  background: #fff;
+  border: 1px solid #eee;
+  border-radius: 12px;
+  padding: 20px;
+  margin-bottom: 20px;
+  box-shadow: 0 4px 10px rgba(0, 0, 0, 0.05);
+}
+
+.dynamic-header {
+  display: flex;
+  align-items: center;
+  margin-bottom: 10px;
+}
+
+.dynamic-avatar {
+  width: 48px;
+  height: 48px;
+  border-radius: 50%;
+  margin-right: 12px;
+}
+
+.dynamic-userinfo {
+  display: flex;
+  flex-direction: column;
+}
+
+.dynamic-username {
+  font-weight: bold;
+  font-size: 16px;
+}
+
+.dynamic-time {
+  font-size: 12px;
+  color: #999;
+}
+
+.dynamic-title {
+  font-size: 18px;
+  margin: 10px 0 5px;
+}
+
+.dynamic-content p {
+  font-size: 15px;
+  line-height: 1.5;
+}
+
+.dynamic-images {
+  display: flex;
+  flex-wrap: wrap;
+  margin-top: 10px;
+  gap: 10px;
+}
+
+.dynamic-images img {
+  width: 120px;
+  height: 120px;
+  object-fit: cover;
+  border-radius: 8px;
+  border: 1px solid #ddd;
+}
diff --git a/src/pages/UserCenter/UserDynamics.jsx b/src/pages/UserCenter/UserDynamics.jsx
index e69de29..05d9046 100644
--- a/src/pages/UserCenter/UserDynamics.jsx
+++ b/src/pages/UserCenter/UserDynamics.jsx
@@ -0,0 +1,88 @@
+import React, { useEffect, useState } from 'react';
+import { useUser } from '../../context/UserContext';
+import './UserDynamics.css';
+
+const UserDynamics = () => {
+  const { user } = useUser();
+  const [dynamics, setDynamics] = useState([]);
+  const [loading, setLoading] = useState(true);
+
+  useEffect(() => {
+    if (!user?.userId) return;
+
+    const fetchDynamics = async () => {
+      try {
+        const res = await fetch(`/echo/dynamic/${user.userId}/getAdynamic`);
+        const data = await res.json();
+        setDynamics(data.dynamic || []);
+      } catch (err) {
+        console.error('获取动态失败:', err);
+      } finally {
+        setLoading(false);
+      }
+    };
+
+    fetchDynamics();
+  }, [user]);
+
+  if (loading) return <div className="user-dynamics-loading">加载中...</div>;
+
+  return (
+    <div className="user-dynamics-container">
+      <h2 className="user-dynamics-title">我的动态</h2>
+      {dynamics.length === 0 ? (
+        <div className="user-dynamics-empty">暂无动态</div>
+      ) : (
+        dynamics.map((item) => (
+          <div key={item.dynamic_id} className="dynamic-card">
+            <div className="dynamic-header">
+              <img
+                className="dynamic-avatar"
+                src={item.avatar_url}
+                alt={item.username}
+              />
+              <div className="dynamic-userinfo">
+                <span className="dynamic-username">{item.username}</span>
+                <span className="dynamic-time">{new Date(item.time).toLocaleString()}</span>
+              </div>
+            </div>
+            <div className="dynamic-content">
+              {item.title && <h4 className="dynamic-title">{item.title}</h4>}
+              <p>{item.content}</p>
+              {/* {item.images && (
+                <div className="dynamic-images">
+                  {JSON.parse(item.images).map((img, index) => (
+                    <img key={index} src={img} alt={`图${index + 1}`} />
+                  ))}
+                </div>
+              )} */}
+              {item.images && (
+                <div className="dynamic-images">
+                    {(() => {
+                    let imageList = [];
+                    try {
+                        if (item.images.startsWith('[')) {
+                        imageList = JSON.parse(item.images);
+                        } else {
+                        imageList = [item.images];
+                        }
+                    } catch (e) {
+                        console.error('解析 images 出错:', e);
+                    }
+
+                    return imageList.map((img, index) => (
+                        <img key={index} src={img} alt={`图${index + 1}`} />
+                    ));
+                    })()}
+                </div>
+                )}
+
+            </div>
+          </div>
+        ))
+      )}
+    </div>
+  );
+};
+
+export default UserDynamics;
diff --git a/src/pages/UserCenter/UserProfile.css b/src/pages/UserCenter/UserProfile.css
index de2ecac..b230c1e 100644
--- a/src/pages/UserCenter/UserProfile.css
+++ b/src/pages/UserCenter/UserProfile.css
@@ -52,9 +52,11 @@
   border-radius: 16px;
   margin: 0 auto;
   margin-top: 40px;
-  padding: 10% 20%;
-  margin-left: 5%;
-  margin-right: 5%;
+  width: 80%;
+  padding-top: 10%;
+  padding-right: 15%;
+  padding-bottom: 10%;
+  padding-left: 10%;
 }  
 .avatar-wrapper {
   position: relative;
@@ -208,7 +210,7 @@
 }
 
 .task-btn:hover {
-  background-color: #357abd;
+  background-color: #bd7035;
 }
 
 .task-btn-group {
@@ -218,10 +220,69 @@
   gap: 8px;
 }
 
-.loading {
-  text-align: center;
-  font-size: 18px;
-  color: #333;
+
+.exp-bar-wrapper {
+  width: 100%;
+  height: 14px;
+  background-color: #fbfafa;
+  border-radius: 8px;
+  margin: 10px 0;
+  overflow: hidden;
 }
 
+.exp-bar {
+  height: 100%;
+  background-color: #0eb813;
+  transition: width 0.3s ease;
+}
 
+.exp-progress-text {
+  font-size: 0.9em;
+  color: #555;
+  margin-bottom: 10px;
+}
+
+.profile-actions {
+  margin-top: 1em;
+}
+
+.profile-actions button {
+  padding: 8px 16px;
+  background-color: #4677f5;
+  color: white;
+  border: none;
+  border-radius: 6px;
+  cursor: pointer;
+}
+
+.modal {
+  position: fixed;
+  top: 0;
+  left: 0;
+  right: 0;
+  bottom: 0;
+  background-color: rgba(0, 0, 0, 0.4);
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  z-index: 99;
+}
+
+.modal-content {
+  background-color: white;
+  padding: 20px;
+  border-radius: 8px;
+  width: 300px;
+}
+
+.modal-content input {
+  display: block;
+  width: 100%;
+  margin: 10px 0;
+  padding: 8px;
+}
+
+.modal-buttons {
+  display: flex;
+  justify-content: space-between;
+}
diff --git a/src/pages/UserCenter/UserProfile.jsx b/src/pages/UserCenter/UserProfile.jsx
index 9170652..c6e80e0 100644
--- a/src/pages/UserCenter/UserProfile.jsx
+++ b/src/pages/UserCenter/UserProfile.jsx
@@ -2,13 +2,25 @@
 import axios from 'axios';
 import './UserProfile.css';
 import { useUser } from '../../context/UserContext';
+import { useLocation } from 'wouter';
+
 const DEFAULT_AVATAR_URL = `${process.env.PUBLIC_URL}/default-avatar.png`;
 
 const UserProfile = () => {
-  const { user, loading } = useUser();
+  const { user, loading, logout } = useUser();
   const [userProfile, setUserProfile] = useState(null);
+  const [experienceInfo, setExperienceInfo] = useState(null);
   const [error, setError] = useState(null);
 
+  // 修改密码状态
+  const [showPwdModal, setShowPwdModal] = useState(false);
+  const [oldPassword, setOldPassword] = useState('');
+  const [newPassword, setNewPassword] = useState('');
+  const [confirmPassword, setConfirmPassword] = useState('');
+
+  // 退出登录
+  const [, setLocation] = useLocation();
+
   useEffect(() => {
     if (loading) return;
     if (!user || !user.userId) {
@@ -21,7 +33,6 @@
       try {
         setError(null);
         const { data: raw } = await axios.get(`/echo/user/${user.userId}/getProfile`);
-
         if (!raw) {
           setError('用户数据为空');
           setUserProfile(null);
@@ -29,9 +40,9 @@
         }
 
         const profile = {
-        avatarUrl: raw.avatarUrl
-          ? `${process.env.REACT_APP_AVATAR_BASE_URL}${raw.avatarUrl}`
-          : DEFAULT_AVATAR_URL,
+          avatarUrl: raw.avatarUrl
+            ? `${process.env.REACT_APP_AVATAR_BASE_URL}${raw.avatarUrl}`
+            : DEFAULT_AVATAR_URL,
           nickname: raw.username || '未知用户',
           email: raw.email || '未填写',
           gender: raw.gender || '保密',
@@ -52,7 +63,19 @@
       }
     };
 
+    const fetchExperienceInfo = async () => {
+      try {
+        const { data } = await axios.get('/echo/level/getExperience', {
+          params: { user_id: user.userId },
+        });
+        setExperienceInfo(data);
+      } catch (err) {
+        console.error('经验信息获取失败:', err);
+      }
+    };
+
     fetchUserProfile();
+    fetchExperienceInfo();
   }, [user, loading]);
 
   const handleAvatarUpload = async (e) => {
@@ -63,11 +86,11 @@
     formData.append('file', file);
 
     try {
-        const { data } = await axios.post(
-          `/echo/user/${user.userId}/uploadAvatar`,
-          formData,
-          { headers: { 'Content-Type': 'multipart/form-data' } }
-        );
+      const { data } = await axios.post(
+        `/echo/user/${user.userId}/uploadAvatar`,
+        formData,
+        { headers: { 'Content-Type': 'multipart/form-data' } }
+      );
 
       if (data?.avatarUrl) {
         setUserProfile((prev) => ({
@@ -84,6 +107,42 @@
     }
   };
 
+  const handleLogout = () => {
+    logout();
+    setLocation('/auth'); // 退出后跳转登录页
+    // window.location.reload(); // 或跳转登录页
+  };
+
+  const handleChangePassword = async () => {
+    if (!oldPassword || !newPassword || !confirmPassword) {
+      alert('请填写所有字段');
+      return;
+    }
+    if (newPassword !== confirmPassword) {
+      alert('两次输入的新密码不一致');
+      return;
+    }
+
+    try {
+      // await axios.post('/echo/user/password', {
+      //   user_id: user.userId,
+      //   oldPassword,
+      //   newPassword,
+      // });
+      await axios.post('/echo/user/password', {
+        user_id: user.userId,
+        old_password: oldPassword,
+        new_password: newPassword,
+        confirm_password: confirmPassword,
+      });
+      alert('密码修改成功,请重新登录');
+      logout();
+      window.location.reload();
+    } catch (err) {
+      alert(err.response?.data?.message || '密码修改失败,请检查原密码是否正确');
+    }
+  };
+
   if (loading) return <p>正在加载用户信息...</p>;
   if (error) return <p className="error">{error}</p>;
   if (!userProfile) return null;
@@ -103,6 +162,19 @@
     joinedDate,
   } = userProfile;
 
+  const progressPercent = experienceInfo
+    ? Math.min(
+        100,
+        ((experienceInfo.current_experience || 0) /
+          (experienceInfo.next_level_experience || 1)) *
+          100
+      ).toFixed(2)
+    : 0;
+
+  const expToNextLevel = experienceInfo
+    ? (experienceInfo.next_level_experience - experienceInfo.current_experience)
+    : null;
+
   return (
     <div className="common-card">
       <div className="right-content">
@@ -134,10 +206,58 @@
           <p><strong>下载量:</strong>{downloadAmount}</p>
           <p><strong>分享率:</strong>{(shareRate * 100).toFixed(2)}%</p>
           <p><strong>加入时间:</strong>{new Date(joinedDate).toLocaleDateString()}</p>
+
+          {experienceInfo && (
+            <>
+              <p><strong>距离下一等级还需:</strong>{expToNextLevel} 经验值</p>
+              <div className="exp-bar-wrapper">
+                <div className="exp-bar" style={{ width: `${progressPercent}%` }} />
+              </div>
+              <p className="exp-progress-text">{progressPercent}%</p>
+            </>
+          )}
+
+          {/* 修改密码与退出登录按钮 */}
+          <div className="profile-actions">
+            <button onClick={() => setShowPwdModal(true)}>修改密码</button>
+            <button onClick={handleLogout}>退出登录</button>
+          </div>
+
+          {/* 修改密码弹窗 */}
+          {showPwdModal && (
+            <div className="modal">
+              <div className="modal-content">
+                <h3>修改密码</h3>
+                <input
+                  type="password"
+                  placeholder="原密码"
+                  value={oldPassword}
+                  onChange={(e) => setOldPassword(e.target.value)}
+                />
+                <input
+                  type="password"
+                  placeholder="新密码"
+                  value={newPassword}
+                  onChange={(e) => setNewPassword(e.target.value)}
+                />
+                <input
+                  type="password"
+                  placeholder="确认新密码"
+                  value={confirmPassword}
+                  onChange={(e) => setConfirmPassword(e.target.value)}
+                />
+                <div className="modal-buttons">
+                  <button onClick={handleChangePassword}>确认修改</button>
+                  <button onClick={() => setShowPwdModal(false)}>取消</button>
+                </div>
+              </div>
+            </div>
+          )}
         </div>
       </div>
     </div>
   );
 };
 
-export default UserProfile; 
\ No newline at end of file
+export default UserProfile;
+