修改提示框样式、完成付费片单、推荐跳转

Change-Id: Ie84c53d4e306435144b1f26ceb39cc182e99d57a
diff --git a/src/App.js b/src/App.js
index af1f022..11219d3 100644
--- a/src/App.js
+++ b/src/App.js
@@ -1,4 +1,5 @@
 import React from 'react';
+import { Toaster } from 'react-hot-toast';
 import { Route, useLocation } from 'wouter';
 import { UserProvider, useUser } from './context/UserContext';
 
@@ -27,6 +28,7 @@
 import NewUserGuide from './pages/NewUserGuide/NewUserGuide';
 import UserRecharge from './pages/UserCenter/UserRecharge';
 import GroupDetail from './pages/InterestGroup/GroupDetail';
+import PlaylistDetailPage from './pages/SeedList/Recommend/PlaylistDetailPage';
 
 function RedirectToAuth() {
   if (typeof window !== 'undefined') {
@@ -55,6 +57,7 @@
 function App() {
   return (
     <UserProvider>
+      <Toaster />
       <GroupProvider>
         <>
           {/* 公开路由 */}
@@ -78,6 +81,10 @@
           <Route path="/information/:userId" component={({ userId }) => <PrivateRoute component={() => <UserInfo userId={userId} />} />} />
           <Route path="/new-user-guide" component={() => <PrivateRoute component={NewUserGuide} />} />
           <Route path="/group/:groupId" component={({ groupId }) => <PrivateRoute component={() => <GroupDetail groupId={groupId} />} />} />
+          <Route path="/playlist/:id" component={({ id }) => (
+            <PrivateRoute component={() => <PlaylistDetailPage id={id} />} />
+          )} />
+
 
           {/* 用户中心路由 */}
           <Route path="/user/profile" component={() => (
diff --git a/src/api/file.js b/src/api/file.js
new file mode 100644
index 0000000..fdfe571
--- /dev/null
+++ b/src/api/file.js
@@ -0,0 +1,8 @@
+import axios from "axios";
+
+
+export const uploadFile = async (file) => {
+    const formData = new FormData();
+    formData.append('file', file);
+    return axios.post('/file', formData, { headers: { 'Content-Type': 'multipart/form-data' } });
+}
\ No newline at end of file
diff --git a/src/components/AuthButton.jsx b/src/components/AuthButton.jsx
new file mode 100644
index 0000000..b5cc431
--- /dev/null
+++ b/src/components/AuthButton.jsx
@@ -0,0 +1,22 @@
+import {useContext} from "react";
+import { UserContext } from "../context/UserContext";
+import toast from "react-hot-toast";
+const AuthButton = ({children, roles,onClick, ...rest }) => {
+    const {user} = useContext(UserContext);
+    const {levelRole} = user;
+    let clickFunc = onClick
+    if(!roles || roles.length === 0 || roles.includes(levelRole)){
+      clickFunc = onClick;
+    }else{
+        clickFunc = () => {
+            toast.error("权限不足");
+        }
+    }
+   
+    return (
+        <button onClick={clickFunc} {...rest}>
+            {children}
+        </button>
+    )
+}
+export default AuthButton;
\ No newline at end of file
diff --git a/src/components/Header.css b/src/components/Header.css
index 017ba1d..66fe1a0 100644
--- a/src/components/Header.css
+++ b/src/components/Header.css
@@ -3,9 +3,9 @@
   display: flex;
   justify-content: space-between;
   align-items: center;
-  padding: 10px 20px; /* 增加左右间距,避免元素靠得太近 */
-  background-color: #5F4437; /* 深棕色背景 */
-  box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); /* 增加阴影效果 */
+  padding: 10px 20px;
+  background-color: #6b4f3b; /* 深棕色背景 */
+  box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
 }
 
 /* Logo 和网站名称 */
@@ -20,9 +20,9 @@
 }
 
 .site-name {
-  font-size:32px;
+  font-size: 32px;
   font-weight: bold;
-  color: #FBF2E3; /* 更加柔和的字体颜色 */
+  color: #fbf2e3; /* 柔和淡米色字体 */
 }
 
 /* 用户头像和消息中心 */
@@ -33,69 +33,68 @@
 
 .user-avatar {
   height: 50px;
-  width: 50px; /* 确保头像是圆形 */
-  border-radius: 50%; /* 圆形 */
+  width: 50px;
+  border-radius: 50%;
   margin-right: 10px;
 }
 
 .message-center {
   font-size: 24px;
-  color: #FBF2E3; /* 统一字体颜色 */
+  color: #fbf2e3;
   cursor: pointer;
-  transition: color 0.3s ease; /* 平滑过渡 */
+  transition: color 0.3s ease;
 }
 
 .message-center:hover {
-  color: #D8C0A1; /* 当鼠标悬停时字体颜色变亮 */
+  color: #eecfc1; /* 清透粉 hover */
 }
 
 /* 导航栏样式 */
 .nav {
   display: flex;
   justify-content: center;
-  background-color: #dab8c2;
-  box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); /* 增加阴影 */
-  transition: background 0.3s ease; 
+  background-color: #fffaf7; /* 粉米底 */
+  box-shadow: 0 4px 8px rgba(0, 0, 0, 0.05);
+  border-bottom: 2px solid #e2cfc3;
 }
 
 /* 每个导航项 */
 .nav-item {
   padding: 14px 12px;
   text-decoration: none;
-  color: #FBF2E3;
+  color: #6b4f3b; /* 主文字棕色 */
   font-size: 18px;
   font-weight: 500;
   text-align: center;
-  border-radius: 8px; /* 圆角效果 */
-  transition: background-color 0.3s, color 0.3s, transform 0.3s ease; /* 平滑过渡 */
-  position: relative; /* 为阴影和动画提供定位 */
+  border-radius: 8px;
+  transition: background-color 0.3s, color 0.3s, transform 0.3s ease;
+  position: relative;
 }
 
-/* 鼠标悬停时 */
 .nav-item:hover {
-  background-color: #5F4437; /* 背景色变深 */
-  color: #FBF2E3; /* 字体颜色与背景形成对比 */
-  transform: scale(1.05); /* 微微放大,增加互动感 */
+  background-color: #f3ded7; /* 柔粉 hover 背景 */
+  color: #4b3325;
+  transform: scale(1.05);
 }
 
 /* 选中状态 */
 .nav-item.active {
-  background-color: #5F4437; /* 活动状态时的背景色 */
-  color: #FBF2E3; /* 活动状态时的字体颜色 */
+  background-color: #eecfc1;
+  color: #3c271b;
   font-weight: bold;
-  border-radius: 16px; /* 更圆的圆角效果 */
-  padding: 4px 12px; /* 减小上下和左右的padding,框变小 */
-  box-shadow: 0 4px 10px rgba(0, 0, 0, 0.15); /* 增加阴影,带来浮动感 */
-  transition: background-color 0.3s, box-shadow 0.3s, transform 0.3s ease; /* 平滑过渡 */
-  transform: scale(1.1); /* 选中项微微放大 */
-  line-height: 40px; /* 调整line-height,使文字更居中 */
+  border-radius: 16px;
+  padding: 4px 12px;
+  box-shadow: 0 4px 10px rgba(0, 0, 0, 0.08);
+  transform: scale(1.1);
+  line-height: 40px;
 }
+
 /* 新手指南按钮 */
 .guide-button {
   display: flex;
   align-items: center;
-  background-color: #a67c6a; /* 柔棕底色 */
-  color: #fff;
+  background-color: #eecfc1; /* 清透粉底色 */
+  color: #4e342e; /* 深棕文字 */
   border: none;
   border-radius: 20px;
   padding: 6px 12px;
@@ -106,12 +105,10 @@
 }
 
 .guide-button:hover {
-  background-color: #8d6e63; /* 深一点 hover */
+  background-color: #f3ded7; /* 更柔和 hover */
   transform: scale(1.05);
 }
 
 .guide-button span {
   user-select: none;
 }
-
-
diff --git a/src/pages/FriendMoments/FriendMoments.css b/src/pages/FriendMoments/FriendMoments.css
index cca399f..edab68f 100644
--- a/src/pages/FriendMoments/FriendMoments.css
+++ b/src/pages/FriendMoments/FriendMoments.css
@@ -9,7 +9,7 @@
 
 .friend-moments-container {
   margin: 0 auto;
-  background: #333;
+  background: #f8f3ef;
   padding-bottom: 40px;
 }
 
diff --git a/src/pages/InterestGroup/InterestGroup.css b/src/pages/InterestGroup/InterestGroup.css
index e44eea1..b2cf86f 100644
--- a/src/pages/InterestGroup/InterestGroup.css
+++ b/src/pages/InterestGroup/InterestGroup.css
@@ -1,6 +1,6 @@
 /* 设置整个兴趣小组页面的背景色和布局 */
 .interest-group-container {
-  background: #333;
+  background: #f8f3ef;
   width: 100%;
   height: 2000px;
 }
diff --git a/src/pages/PublishSeed/PublishSeed.css b/src/pages/PublishSeed/PublishSeed.css
index 7525fb7..ff7fcad 100644
--- a/src/pages/PublishSeed/PublishSeed.css
+++ b/src/pages/PublishSeed/PublishSeed.css
@@ -1,12 +1,12 @@
-.publish-seed-container {
-  background: #333;
+.ps-container {
+  background: #f8f3ef;
   color: #333;
   min-height: 100vh;
   padding: 20px 0;
   font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
 }
 
-.pub-card {
+.ps-card {
   background-color: #e9ded2;
   border-radius: 16px;
   max-width: 800px;
@@ -16,20 +16,20 @@
   box-shadow: 0 4px 16px rgba(0, 0, 0, 0.1);
 }
 
-form > div {
+.ps-form > div {
   margin-bottom: 20px;
 }
 
-label {
+.ps-label {
   display: block;
   font-weight: bold;
   margin-bottom: 8px;
   color: #5F4437;
 }
 
-input[type="text"],
-textarea,
-select {
+.ps-input-text,
+.ps-textarea,
+.ps-select {
   width: 100%;
   padding: 10px 12px;
   border-radius: 6px;
@@ -40,20 +40,20 @@
   transition: border-color 0.3s;
 }
 
-input[type="text"]:focus,
-textarea:focus,
-select:focus {
+.ps-input-text:focus,
+.ps-textarea:focus,
+.ps-select:focus {
   outline: none;
   border-color: #c49c6b;
 }
 
-textarea {
+.ps-textarea {
   resize: vertical;
   min-height: 80px;
 }
 
-.seed-file-label,
-.cover-upload button {
+.ps-seed-file-label,
+.ps-cover-upload-button {
   display: inline-block;
   padding: 8px 16px;
   border: 1px solid #e0c4a1;
@@ -66,17 +66,17 @@
   transition: all 0.2s ease;
 }
 
-.seed-file-label:hover,
-.cover-upload button:hover {
+.ps-seed-file-label:hover,
+.ps-cover-upload-button:hover {
   background-color: #f1e0d0;
 }
 
-.seed-file input,
-.cover-upload input {
+.ps-seed-file-input,
+.ps-cover-upload-input {
   display: none;
 }
 
-.message {
+.ps-message {
   background-color: #fff3cd;
   color: #856404;
   padding: 12px;
@@ -85,11 +85,11 @@
   border: 1px solid #ffeeba;
 }
 
-.upload-button {
+.ps-upload-button {
   text-align: center;
 }
 
-.upload-button button {
+.ps-upload-button button {
   background-color: #5F4437;
   color: #fff;
   padding: 12px 32px;
@@ -100,16 +100,16 @@
   transition: background-color 0.2s ease;
 }
 
-.upload-button button:hover {
+.ps-upload-button button:hover {
   background-color: #472f23;
 }
 
-.upload-button button:disabled {
+.ps-upload-button button:disabled {
   background-color: #9c8c84;
   cursor: not-allowed;
 }
 
-img {
+.ps-img-preview {
   border-radius: 8px;
   border: 1px solid #ccc;
   max-width: 100%;
diff --git a/src/pages/PublishSeed/PublishSeed.jsx b/src/pages/PublishSeed/PublishSeed.jsx
index a3ac4ad..27717ca 100644
--- a/src/pages/PublishSeed/PublishSeed.jsx
+++ b/src/pages/PublishSeed/PublishSeed.jsx
@@ -4,6 +4,7 @@
 import Header from '../../components/Header';
 import './PublishSeed.css';
 import { useUser } from '../../context/UserContext';
+import { uploadFile } from '../../api/file';
 
 const PublishSeed = () => {
   const [title, setTitle] = useState('');
@@ -86,7 +87,6 @@
     formData.append('category', category);
     formData.append('tags', tags.join(',')); // 逗号分隔字符串
     formData.append('uploader', user.userId);
-
     if (imageFile) {
       formData.append('coverImage', imageFile);
     }
@@ -113,20 +113,22 @@
   };
 
   return (
-    <div className="publish-seed-container">
+    <div className="ps-container">
       <Header />
-      <div className="pub-card">
-        {message && <div className="message">{message}</div>}
+      <div className="ps-card">
+        {message && <div className="ps-message">{message}</div>}
         <form
+          className="ps-form"
           onSubmit={(e) => {
             console.log('[DEBUG] form onSubmit 触发');
             handleSubmit(e);
           }}
           encType="multipart/form-data"
         >
-          <div className="title-tag">
-            <label>标题</label>
+          <div>
+            <label className="ps-label">标题</label>
             <input
+              className="ps-input-text"
               type="text"
               value={title}
               onChange={(e) => {
@@ -137,9 +139,10 @@
             />
           </div>
 
-          <div className="discription">
-            <label>描述</label>
+          <div>
+            <label className="ps-label">描述</label>
             <textarea
+              className="ps-textarea"
               value={description}
               onChange={(e) => {
                 console.log('[DEBUG] 描述输入变化:', e.target.value);
@@ -149,9 +152,10 @@
             />
           </div>
 
-          <div className="title-tag">
-            <label>标签 (逗号分隔)</label>
+          <div>
+            <label className="ps-label">标签 (逗号分隔)</label>
             <input
+              className="ps-input-text"
               type="text"
               value={tags.join(', ')}
               onChange={handleTagsChange}
@@ -160,9 +164,10 @@
             />
           </div>
 
-          <div className="pub-categoty">
-            <label>分类</label>
+          <div>
+            <label className="ps-label">分类</label>
             <select
+              className="ps-select"
               value={category}
               onChange={(e) => {
                 console.log('[DEBUG] 分类选择变化:', e.target.value);
@@ -184,10 +189,10 @@
             </select>
           </div>
 
-          <div className="seed-file">
-            <label>种子文件</label>
+          <div>
+            <label className="ps-label">种子文件</label>
             <div
-              className="seed-file-label"
+              className="ps-seed-file-label"
               onClick={handleFileButtonClick}
               style={{ cursor: 'pointer' }}
             >
@@ -198,15 +203,19 @@
               accept=".torrent"
               ref={fileInputRef}
               onChange={handleFileChange}
-              style={{ display: 'none' }}
+              className="ps-seed-file-input"
             />
-            {fileName && <div style={{ marginTop: '5px' }}>{fileName}</div>}
+            {fileName && <div style={{ marginTop: '5px', color: '#5F4437' }}>{fileName}</div>}
           </div>
 
-          <div className="form-group">
-            <label>封面图</label>
-            <div className="cover-upload">
-              <button type="button" onClick={handleImageButtonClick}>
+          <div>
+            <label className="ps-label">封面图</label>
+            <div>
+              <button
+                type="button"
+                onClick={handleImageButtonClick}
+                className="ps-cover-upload-button"
+              >
                 上传图片
               </button>
               <input
@@ -214,7 +223,7 @@
                 accept="image/*"
                 ref={imageInputRef}
                 onChange={handleImageChange}
-                style={{ display: 'none' }}
+                className="ps-cover-upload-input"
               />
             </div>
             {previewUrl && (
@@ -222,13 +231,13 @@
                 <img
                   src={previewUrl}
                   alt="封面预览"
-                  style={{ maxWidth: '100%', maxHeight: '200px' }}
+                  className="ps-img-preview"
                 />
               </div>
             )}
           </div>
 
-          <div className="upload-button">
+          <div className="ps-upload-button">
             <button
               type="submit"
               disabled={isLoading}
diff --git a/src/pages/SeedList/Recommend/CreatePlaylistModal.css b/src/pages/SeedList/Recommend/CreatePlaylistModal.css
new file mode 100644
index 0000000..022e190
--- /dev/null
+++ b/src/pages/SeedList/Recommend/CreatePlaylistModal.css
@@ -0,0 +1,153 @@
+/* CreatePlaylistModal.css */
+
+.create-playlist-modal.modal-overlay {
+  position: fixed;
+  inset: 0;
+  background-color: rgba(0, 0, 0, 0.65);
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  z-index: 9999;
+  padding: 16px;
+}
+
+.create-playlist-modal .modal {
+  background: #fff;
+  border-radius: 14px;
+  width: 420px;
+  max-width: 100%;
+  padding: 32px 40px;
+  box-shadow:
+    0 10px 15px rgba(0, 0, 0, 0.1),
+    0 4px 6px rgba(0, 0, 0, 0.05);
+  font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
+  transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1);
+}
+
+.create-playlist-modal .modal:hover {
+  transform: translateY(-6px);
+}
+
+.create-playlist-modal .modal h3 {
+  margin: 0 0 28px 0;
+  font-weight: 700;
+  font-size: 1.9rem;
+  color: #222;
+  text-align: center;
+  letter-spacing: 0.02em;
+}
+
+/* Label纵向排列 */
+.create-playlist-modal .modal label {
+  display: flex !important;
+  flex-direction: column !important;
+  margin-bottom: 20px;
+  font-weight: 600;
+  color: #444;
+  font-size: 1.05rem;
+  user-select: none;
+  cursor: default; /* 加上避免label内容被选中时光标变形 */
+}
+
+.create-playlist-modal .modal input[type="text"],
+.create-playlist-modal .modal input[type="number"],
+.create-playlist-modal .modal textarea {
+  margin-top: 8px;
+  padding: 12px 16px;
+  border: 1.6px solid #bbb;
+  border-radius: 8px;
+  font-size: 1rem;
+  color: #333;
+  box-sizing: border-box;
+  resize: vertical;
+  transition: border-color 0.25s ease, box-shadow 0.25s ease;
+  font-family: inherit;
+}
+
+.create-playlist-modal .modal input[type="text"]:focus,
+.create-playlist-modal .modal input[type="number"]:focus,
+.create-playlist-modal .modal textarea:focus {
+  outline: none;
+  border-color: #4a69bd;
+  box-shadow: 0 0 10px rgba(74, 105, 189, 0.5);
+}
+
+.create-playlist-modal .modal textarea {
+  min-height: 90px;
+  line-height: 1.5;
+  font-family: inherit;
+}
+
+.create-playlist-modal .modal-actions {
+  display: flex;
+  justify-content: flex-end;
+  gap: 16px;
+  margin-top: 32px;
+}
+
+.create-playlist-modal .modal-actions button {
+  padding: 12px 26px;
+  font-size: 1.15rem;
+  font-weight: 700;
+  border: none;
+  border-radius: 10px;
+  cursor: pointer;
+  user-select: none;
+  transition:
+    background-color 0.35s ease,
+    box-shadow 0.35s ease,
+    transform 0.2s ease;
+  font-family: inherit;
+  box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
+}
+
+.create-playlist-modal .modal-actions button:first-child {
+  background: linear-gradient(135deg, #4a69bd 0%, #3f51b5 100%);
+  color: #fff;
+  box-shadow: 0 6px 12px rgba(63, 81, 181, 0.5);
+}
+
+.create-playlist-modal .modal-actions button:first-child:hover {
+  background: linear-gradient(135deg, #3f51b5 0%, #2c3e9f 100%);
+  box-shadow: 0 8px 18px rgba(44, 62, 159, 0.75);
+  transform: translateY(-2px);
+}
+
+.create-playlist-modal .modal-actions button:first-child:disabled {
+  background: #a0a5b8;
+  box-shadow: none;
+  cursor: not-allowed;
+  transform: none;
+}
+
+.create-playlist-modal .modal-actions button:last-child {
+  background-color: #f3f4f6;
+  color: #555;
+  box-shadow: 0 2px 6px rgba(0, 0, 0, 0.08);
+  transition: background-color 0.25s ease;
+}
+
+.create-playlist-modal .modal-actions button:last-child:hover {
+  background-color: #d9dade;
+  box-shadow: 0 4px 10px rgba(0, 0, 0, 0.12);
+}
+.torrent-selector {
+  border: 1px solid #ccc;
+  padding: 8px;
+  max-height: 200px;
+  overflow-y: auto;
+  margin-top: 8px;
+}
+.torrent-list {
+  list-style: none;
+  padding: 0;
+  margin: 0;
+}
+.torrent-list li {
+  margin: 4px 0;
+}
+.selected-torrents {
+  margin-top: 6px;
+  font-size: 0.9em;
+  color: #555;
+}
diff --git a/src/pages/SeedList/Recommend/CreatePlaylistModal.jsx b/src/pages/SeedList/Recommend/CreatePlaylistModal.jsx
new file mode 100644
index 0000000..60a5b76
--- /dev/null
+++ b/src/pages/SeedList/Recommend/CreatePlaylistModal.jsx
@@ -0,0 +1,153 @@
+import React, { useState } from 'react';
+import axios from 'axios';
+import toast from 'react-hot-toast';
+import TorrentSelector from './TorrentSelector'; // 引入种子选择组件
+import './CreatePlaylistModal.css';
+import { uploadFile } from '../../../api/file';
+
+const CreatePlaylistModal = ({ onClose, onSuccess }) => {
+  const [newPlaylist, setNewPlaylist] = useState({
+    title: '',
+    price: '',
+    description: '',
+    torrentList: [], // 选中的种子ID数组
+  });
+
+  const [coverFile, setCoverFile] = useState(null); // 新增封面文件状态
+  const [previewUrl, setPreviewUrl] = useState(''); // 封面预览URL
+
+  const handleSelect = (id, checked) => {
+    setNewPlaylist(prev => {
+      let newList = [...prev.torrentList];
+      if (checked) {
+        if (!newList.includes(id)) newList.push(id);
+      } else {
+        newList = newList.filter(i => i !== id);
+      }
+      return { ...prev, torrentList: newList };
+    });
+  };
+
+  const handleCoverChange = (e) => {
+    const file = e.target.files?.[0];
+    if (file) {
+      setCoverFile(file);
+      setPreviewUrl(URL.createObjectURL(file)); // 生成预览图
+    } else {
+      setCoverFile(null);
+      setPreviewUrl('');
+    }
+  };
+
+  const handleCreate = async () => {
+    const { title, price, description, torrentList } = newPlaylist;
+
+    if (!title.trim()) {
+      toast.error('标题不能为空');
+      return;
+    }
+    if (!price || isNaN(Number(price)) || Number(price) < 0) {
+      toast.error('请输入合法的价格');
+      return;
+    }
+    if (torrentList.length === 0) {
+      toast.error('请至少选择一个种子');
+      return;
+    }
+
+    const toastId = toast.loading('正在创建...');
+    const playlistData = { title, price, description, torrentList }; // 构建片单数据
+    
+    try {
+
+      if (coverFile) {
+        const { data } = await uploadFile(coverFile);
+        if (data.code === 0) {
+          playlistData['coverUrl'] = data.data;
+        } else {
+          toast.error('封面上传失败: ' + data.msg, { id: toastId});
+          return;
+        }
+      }
+
+      const res = await axios.post('/playlist', playlistData, {
+      });
+
+      if (res.data.code === 0) {
+        toast.success('片单创建成功', { id: toastId });
+        onSuccess(res.data.data);
+        onClose();
+      } else {
+        toast.error(`创建失败:${res.data.msg}`, { id: toastId });
+      }
+    } catch (err) {
+      console.error('创建片单失败', err);
+      toast.error('创建失败,请稍后重试', { id: toastId });
+    }
+  };
+
+  return (
+    <div className="modal-overlay create-playlist-modal">
+      <div className="modal">
+        <h3>创建新片单</h3>
+
+        <label>
+          标题:
+          <input
+            type="text"
+            value={newPlaylist.title}
+            onChange={(e) => setNewPlaylist({ ...newPlaylist, title: e.target.value })}
+          />
+        </label>
+
+        <label>
+          封面图:
+          <input
+            type="file"
+            accept="image/*"
+            onChange={handleCoverChange}
+          />
+          {previewUrl && (
+            <div style={{ marginTop: '10px' }}>
+              <img src={previewUrl} alt="封面预览" style={{ maxWidth: '100%', maxHeight: '150px' }} />
+            </div>
+          )}
+        </label>
+
+        <label>
+          价格(元):
+          <input
+            type="number"
+            step="0.01"
+            min="0"
+            value={newPlaylist.price}
+            onChange={(e) => setNewPlaylist({ ...newPlaylist, price: e.target.value })}
+          />
+        </label>
+
+        <label>
+          片单描述:
+          <textarea
+            value={newPlaylist.description}
+            onChange={(e) => setNewPlaylist({ ...newPlaylist, description: e.target.value })}
+          />
+        </label>
+
+        <label>
+          关联种子选择:
+          <TorrentSelector
+            selectedIds={newPlaylist.torrentList}
+            onSelect={handleSelect}
+          />
+        </label>
+
+        <div className="modal-actions">
+          <button onClick={handleCreate}>提交</button>
+          <button onClick={onClose}>取消</button>
+        </div>
+      </div>
+    </div>
+  );
+};
+
+export default CreatePlaylistModal;
diff --git a/src/pages/SeedList/Recommend/PlaylistDetailPage.css b/src/pages/SeedList/Recommend/PlaylistDetailPage.css
new file mode 100644
index 0000000..78e7068
--- /dev/null
+++ b/src/pages/SeedList/Recommend/PlaylistDetailPage.css
@@ -0,0 +1,83 @@
+.playlist-detail {
+  background-color: #fffaf7; /* 和上一页主背景色保持一致 */
+  border-radius: 12px; /* 统一圆角 */
+  box-shadow: 0 10px 30px rgba(0, 0, 0, 0.08); /* 统一阴影 */
+  color: #6b4f3b; /* 主文字颜色,棕色系 */
+}
+
+.playlist-detail h1 {
+  font-size: 2rem;
+  margin-bottom: 16px;
+  color: #4e342e; /* 深棕色标题 */
+  font-weight: 700;
+}
+
+.cover-image {
+  width: 100%;
+  max-height: 400px;
+  object-fit: cover;
+  border-radius: 12px; /* 与容器圆角匹配 */
+  margin-bottom: 20px;
+  box-shadow: 0 4px 12px rgb(107 79 59 / 0.2); /* 柔和阴影 */
+}
+
+.playlist-detail p {
+  font-size: 1rem;
+  line-height: 1.6;
+  margin-bottom: 20px;
+  color: #6b4f3b;
+}
+
+.playlist-detail h3 {
+  font-size: 1.4rem;
+  margin-bottom: 12px;
+  color: #4e342e;
+  font-weight: 600;
+}
+
+.playlist-detail ul {
+  padding-left: 20px;
+  list-style: disc;
+  color: #6b4f3b;
+}
+
+.playlist-detail li {
+  padding: 6px 0;
+  border-bottom: 1px solid #e2cfc3;
+  font-weight: 500;
+  color: #5d4037;
+}
+
+.playlist-detail li:last-child {
+  border-bottom: none;
+}
+
+/* 错误提示 */
+.error {
+  max-width: 600px;
+  margin: 40px auto;
+  padding: 20px;
+  background: #f8d7da; /* 柔和粉棕 */
+  color: #a94442;
+  border: 1px solid #a94442;
+  border-radius: 12px;
+  text-align: center;
+  font-weight: 600;
+}
+
+/* 重试按钮 */
+.retry-button {
+  margin-top: 12px;
+  padding: 8px 16px;
+  background-color: #6b4f3b;
+  border: none;
+  border-radius: 8px;
+  color: #fffaf7;
+  font-weight: 600;
+  cursor: pointer;
+  transition: background-color 0.25s ease;
+}
+
+.retry-button:hover {
+  background-color: #4b3325;
+}
diff --git a/src/pages/SeedList/Recommend/PlaylistDetailPage.jsx b/src/pages/SeedList/Recommend/PlaylistDetailPage.jsx
new file mode 100644
index 0000000..07558d6
--- /dev/null
+++ b/src/pages/SeedList/Recommend/PlaylistDetailPage.jsx
@@ -0,0 +1,92 @@
+import React, { useEffect, useState } from 'react';
+import { useRoute } from 'wouter'; // ✅ 修改这里
+import axios from 'axios';
+import Header from '../../../components/Header';
+import './PlaylistDetailPage.css';
+import { useUser } from '../../../context/UserContext';
+import toast from 'react-hot-toast';
+import { confirmAlert } from 'react-confirm-alert';
+import 'react-confirm-alert/src/react-confirm-alert.css';
+
+const PlaylistDetailPage = () => {
+  const [match, params] = useRoute('/playlist/:id'); // ✅ 使用 useRoute
+  const id = params?.id;
+  const { user } = useUser();
+  const userId = user?.userId;
+
+  const [detail, setDetail] = useState(null);
+  const [loading, setLoading] = useState(true);
+  const [error, setError] = useState('');
+
+  const fetchDetail = async () => {
+    if (!id) {
+      setError('无效的片单ID');
+      setLoading(false);
+      return;
+    }
+    if (!userId) {
+      setError('请先登录');
+      setLoading(false);
+      return;
+    }
+
+    try {
+      setLoading(true);
+      setError('');
+      const res = await axios.get(`/playlist/${id}`, {
+        params: { userId }
+      });
+      if (res.data.code === 0) {
+        setDetail(res.data.data);
+      } else {
+        setError(res.data.msg || '加载失败');
+      }
+    } catch (err) {
+      console.error(err);
+      setError('请求失败,请检查网络');
+    } finally {
+      setLoading(false);
+    }
+  };
+
+  useEffect(() => {
+    if (id && userId) fetchDetail(); // ✅ 防止空 ID 反复触发
+  }, [id, userId]);
+
+  if (loading) return <div>加载中...</div>;
+
+  if (error) {
+    return (
+      <div className="error">
+        {error}
+        <button onClick={fetchDetail} className="retry-button">重试</button>
+      </div>
+    );
+  }
+
+return (
+  <div className="playlist-detail">
+    <Header />
+    <h1>{detail.title}</h1>
+    <img
+      src={detail.coverUrl || '/default-cover.jpg'}
+      alt={detail.title}
+      onError={e => { e.target.src = '/default-cover.jpg'; }}
+      className="cover-image"
+    />
+    <p>{detail.description}</p>
+
+    <h3>包含的种子:</h3>
+    {detail.torrentList && detail.torrentList.length > 0 ? (
+      <ul>
+        {detail.torrentList.map(seed => (
+          <li key={seed.id}>{seed.title}</li>
+        ))}
+      </ul>
+    ) : (
+      <p>暂无种子数据</p>
+    )}
+  </div>
+);
+};
+export default PlaylistDetailPage;
diff --git a/src/pages/SeedList/Recommend/Recommend.css b/src/pages/SeedList/Recommend/Recommend.css
index cf46b86..f1e34da 100644
--- a/src/pages/SeedList/Recommend/Recommend.css
+++ b/src/pages/SeedList/Recommend/Recommend.css
@@ -1,90 +1,153 @@
-/* .recommend-wrapper {
-  padding: 20px;
-  background-color: #fff;
+/* 推荐页整体容器 */
+.recommendation-page {
+  padding: 24px 32px;
+  background-color: #f8f3ef; /* 米棕底色 */
+  min-height: 100vh;
+  font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
+  color: #4e342e; /* 深米棕文字 */
 }
 
-.recommend-section-title {
-  font-size: 20px;
-  font-weight: bold;
-  margin-bottom: 15px;
+/* 各个版块标题 */
+.recommendation-page h2 {
+  font-size: 1.75rem;
+  font-weight: 700;
+  margin-bottom: 18px;
+  border-left: 6px solid #eecfc1; /* 粉米色强调条 */
+  padding-left: 12px;
+  color: #4e342e; /* 标题米棕色 */
+  user-select: none;
 }
 
+/* 付费片单 - 横向滚动行 */
 .recommend-paid-row {
   display: flex;
   gap: 20px;
-  flex-wrap: nowrap;
   overflow-x: auto;
+  padding-bottom: 12px;
+  width: 100%;
+  scroll-behavior: smooth;
 }
 
+.recommend-paid-row::-webkit-scrollbar {
+  height: 8px;
+}
+.recommend-paid-row::-webkit-scrollbar-thumb {
+  background-color: rgba(0, 0, 0, 0.3);
+  border-radius: 4px;
+}
+
+/* 付费片单卡片 */
 .paid-card {
-  width: 200px;
-  flex-shrink: 0;
-  border: 1px solid #eee;
-  border-radius: 8px;
-  overflow: hidden;
-  background-color: #f9f9f9;
-  text-align: center;
+  width: 450px; /* 固定宽度 */
+  flex-shrink: 0; /* 不允许缩小 */
+  border-radius: 10px;
+  /* overflow: hidden; */
+    overflow: visible;
+  background: #fffaf7; /* 粉米背景 */
+  box-shadow: 0 10px 20px rgba(78, 52, 46, 0.1); /* 柔和阴影 */
+  cursor: pointer;
+  transition: transform 0.25s ease, box-shadow 0.25s ease;
+}
+
+.paid-card:hover {
+  transform: translateY(-6px);
+  box-shadow: 0 14px 28px rgba(78, 52, 46, 0.2);
 }
 
 .paid-cover {
   width: 100%;
-  height: 120px;
+  height: 130px;
   object-fit: cover;
+  border-bottom: 1px solid #e2cfc3; /* 米棕分割线 */
+  border-radius: 10px 10px 0 0;
 }
 
 .paid-title {
-  font-size: 16px;
-  padding: 10px;
-} */
-
-
-.recommendation-page {
-  padding: 20px;
-  background-color: #f8f9fa;
-}
-
-.recommendation-page h2 {
-  font-size: 1.5rem;
-  margin-bottom: 10px;
-  color: #333;
-}
-
-.seed-list {
-  display: flex;
-  flex-wrap: wrap;
-  gap: 16px;
-  margin-bottom: 32px;
-}
-
-.seed-card {
-  width: 160px;
-  background: white;
-  border-radius: 12px;
-  box-shadow: 0 4px 10px rgba(0, 0, 0, 0.08);
+  font-size: 1.1rem;
+  font-weight: 600;
+  padding: 12px 10px;
+  color: #6b4f3b; /* 深米棕文字 */
+  white-space: nowrap;
   overflow: hidden;
-  transition: transform 0.2s ease;
-  cursor: pointer;
+  text-overflow: ellipsis;
 }
 
+/* 热门资源 - 一行横向滚动 */
+.seed-list.popular-row {
+  display: flex;
+  overflow-x: auto;
+  white-space: nowrap;
+  gap: 16px;
+  padding-bottom: 12px;
+  scrollbar-width: none;
+}
+.seed-list.popular-row::-webkit-scrollbar {
+  display: none;
+}
+
+/* 热门资源卡片 */
+.seed-card {
+  width: 150px;
+  flex-shrink: 0;
+  background: #fffaf7; /* 粉米背景 */
+  border-radius: 12px;
+  box-shadow: 0 8px 16px rgba(78, 52, 46, 0.08);
+  overflow: hidden;
+  transition: transform 0.3s ease, box-shadow 0.3s ease;
+  cursor: pointer;
+  user-select: none;
+}
 .seed-card:hover {
-  transform: translateY(-4px);
+  transform: translateY(-6px);
+  box-shadow: 0 12px 24px rgba(78, 52, 46, 0.15);
 }
 
 .seed-card img {
   width: 100%;
-  height: 220px;
+  height: 210px;
   object-fit: cover;
+  border-radius: 12px 12px 0 0;
+  border-bottom: 1px solid #e2cfc3;
 }
 
 .seed-card .title {
-  padding: 8px;
-  font-size: 0.95rem;
+  padding: 10px 8px;
+  font-size: 1rem;
+  color: #6b4f3b; /* 深米棕文字 */
   text-align: center;
-  color: #333;
+  white-space: nowrap;
+  overflow: hidden;
+  text-overflow: ellipsis;
 }
 
+/* 猜你喜欢 - 多行换行排列 */
+.seed-list {
+  display: flex;
+  flex-wrap: wrap;
+  gap: 18px;
+  margin-bottom: 32px;
+  user-select: none;
+}
+
+/* 登录提醒 */
 .login-reminder {
-  font-size: 1rem;
-  color: #888;
-  padding: 10px;
+  font-size: 1.1rem;
+  color: #a1887f; /* 柔和棕色 */
+  padding: 14px 0;
+  text-align: center;
+  font-style: italic;
+  user-select: none;
+}
+
+/* 统一h2的上下间距 */
+.recommendation-page h2 {
+  margin-top: 36px;
+  margin-bottom: 18px;
+}
+
+
+/* 给滚动区域内种子卡片添加最小宽度避免缩得太小 */
+.seed-list.popular-row .seed-card,
+.recommend-paid-row .paid-card {
+  min-width: 150px;
 }
diff --git a/src/pages/SeedList/Recommend/Recommend.jsx b/src/pages/SeedList/Recommend/Recommend.jsx
index 8270145..f58c73d 100644
--- a/src/pages/SeedList/Recommend/Recommend.jsx
+++ b/src/pages/SeedList/Recommend/Recommend.jsx
@@ -2,48 +2,205 @@
 import axios from 'axios';
 import './Recommend.css';
 import { useUser } from '../../../context/UserContext';
+import { useLocation } from 'wouter';
+import toast from 'react-hot-toast';
+import CreatePlaylistModal from './CreatePlaylistModal';
+import { confirmAlert } from 'react-confirm-alert';
+import 'react-confirm-alert/src/react-confirm-alert.css';
 
 const Recommend = () => {
   const { user } = useUser();
+  const [paidLists, setPaidLists] = useState([]);
   const [popularSeeds, setPopularSeeds] = useState([]);
-  const [recommendedSeeds, setRecommendedSeeds] = useState([]);
+  const [recommendedSeeds, setRecommendedSeeds] = useState({
+    movie: [],
+    tv: [],
+    anime: []
+  });
+
+  const [showModal, setShowModal] = useState(false);
+  const [, navigate] = useLocation();
 
   useEffect(() => {
-    // 获取热门资源
+    axios
+      .get('/playlist/page', { params: { page: 1, size: 8 } })
+      .then((res) => {
+        if (res.data.code === 0) {
+          setPaidLists(res.data.data);
+        } else {
+          toast.error(`获取片单失败:${res.data.msg}`);
+        }
+      })
+      .catch((err) => {
+        console.error('请求片单失败', err);
+        toast.error('请求片单失败,请稍后重试');
+      });
+
     axios
       .get('/echo/recommendation/popular', { params: { limit: 16 } })
       .then((res) => setPopularSeeds(res.data))
-      .catch((err) => console.error('获取热门资源失败', err));
+      .catch((err) => {
+        console.error('获取热门资源失败', err);
+        toast.error('获取热门资源失败');
+      });
   }, []);
 
   useEffect(() => {
-    // 获取个性化推荐
-    if (user && user.userId) {
+    if (user?.userId) {
       axios
         .get(`/echo/recommendation/seeds/${user.userId}`)
-        .then((res) => setRecommendedSeeds(res.data))
-        .catch((err) => console.error('获取个性化推荐失败', err));
+        .then((res) => {
+          const categorized = { movie: [], tv: [], anime: [] };
+          res.data.forEach((seed) => {
+            if (seed.category === 'movie') categorized.movie.push(seed);
+            else if (seed.category === 'tv') categorized.tv.push(seed);
+            else if (seed.category === 'anime') categorized.anime.push(seed);
+          });
+          setRecommendedSeeds(categorized);
+        })
+        .catch((err) => {
+          console.error('获取个性化推荐失败', err);
+          toast.error('获取个性化推荐失败');
+        });
     }
   }, [user]);
 
-  const renderSeedCard = (seed) => (
-    <div className="seed-card" key={seed.id}>
-      <img src={seed.coverUrl || '/default-cover.jpg'} alt={seed.title} />
-      <div className="title">{seed.title}</div>
-    </div>
+  const handleDelete = (id) => {
+    confirmAlert({
+      title: '确认删除',
+      message: '确定删除此片单吗?',
+      buttons: [
+        {
+          label: '确定',
+          onClick: async () => {
+            const toastId = toast.loading('正在删除...');
+            try {
+              await axios.delete('/playlist', {
+                params: { ids: id },
+                paramsSerializer: (params) =>
+                  `ids=${Array.isArray(params.ids) ? params.ids.join(',') : params.ids}`
+              });
+              setPaidLists(paidLists.filter((list) => list.id !== id));
+              toast.success('删除成功', { id: toastId });
+            } catch (error) {
+              console.error('删除失败', error);
+              toast.error('删除失败,请稍后重试', { id: toastId });
+            }
+          }
+        },
+        { label: '取消' }
+      ]
+    });
+  };
+
+  const handlePurchase = (id) => {
+    confirmAlert({
+      title: '确认购买',
+      message: '确定支付该片单?',
+      buttons: [
+        {
+          label: '确定',
+          onClick: async () => {
+            const toastId = toast.loading('购买中...');
+            try {
+              const res = await axios.post(`/playlist/${id}/pay`);
+              if (res.data.code === 0) {
+                toast.success('购买成功', { id: toastId });
+                navigate(`/playlist/${id}`);
+              } else {
+                toast.error(`购买失败:${res.data.msg}`, { id: toastId });
+              }
+            } catch (err) {
+              console.error('支付失败', err);
+              toast.error('购买失败,请稍后重试', { id: toastId });
+            }
+          }
+        },
+        { label: '取消' }
+      ]
+    });
+  };
+
+const renderSeedCard = (seed) => (
+  <div
+    className="seed-card"
+    key={seed.id}
+    onClick={() => navigate(`/seed/${seed.id}`)}
+    style={{ cursor: 'pointer' }}
+  >
+    <img src={seed.imageUrl || '/default-cover.jpg'} alt={seed.title} />
+    <div className="title">{seed.title}</div>
+  </div>
+);
+
+
+  const renderSection = (title, seeds) => (
+    <>
+      <h2>{title}</h2>
+      <div className="seed-list">{seeds.map(renderSeedCard)}</div>
+    </>
   );
 
   return (
     <div className="recommendation-page">
-      <h2>🎬 正在热映</h2>
-      <div className="seed-list">{popularSeeds.map(renderSeedCard)}</div>
+      <h2>💰 付费片单</h2>
+      {user && user.role === 'admin' && (
+        <button className="create-button" onClick={() => setShowModal(true)}>
+          ➕ 创建片单
+        </button>
+      )}
 
-      <h2>🎯 个性化推荐</h2>
+      <div className="recommend-paid-row">
+        {paidLists.map((list) => (
+          <div className="paid-card" key={list.id}>
+            <img
+              className="paid-cover"
+              src={list.coverUrl || '/default-cover.jpg'}
+              alt={list.title}
+            />
+            <div className="paid-title">{list.title}</div>
+
+            {user && user.role === 'admin' ? (
+              <div className="admin-actions">
+                <button onClick={() => handleDelete(list.id)}>删除</button>
+              </div>
+            ) : list.isPaid ? (
+              <button onClick={() => navigate(`/playlist/${list.id}`)}>详情</button>
+            ) : (
+              <button onClick={() => handlePurchase(list.id)}>购买</button>
+            )}
+          </div>
+        ))}
+      </div>
+
+      <h2>🎬 正在热映</h2>
+      <div className="seed-list popular-row">
+        {popularSeeds.slice(0, 8).map(renderSeedCard)}
+      </div>
+
+      <h2>🎯 猜你喜欢</h2>
       {user ? (
-        <div className="seed-list">{recommendedSeeds.map(renderSeedCard)}</div>
+        <>
+          {recommendedSeeds.movie.length > 0 &&
+            renderSection('🎞️ 电影推荐', recommendedSeeds.movie)}
+          {recommendedSeeds.tv.length > 0 &&
+            renderSection('📺 电视剧推荐', recommendedSeeds.tv)}
+          {recommendedSeeds.anime.length > 0 &&
+            renderSection('🎌 动漫推荐', recommendedSeeds.anime)}
+        </>
       ) : (
         <div className="login-reminder">请登录以获取个性化推荐</div>
       )}
+
+      {showModal && (
+        <CreatePlaylistModal
+          onClose={() => setShowModal(false)}
+          onSuccess={(newPlaylist) => {
+            setPaidLists([newPlaylist, ...paidLists]);
+            setShowModal(false);
+          }}
+        />
+      )}
     </div>
   );
 };
diff --git a/src/pages/SeedList/Recommend/TorrentSelector.css b/src/pages/SeedList/Recommend/TorrentSelector.css
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/src/pages/SeedList/Recommend/TorrentSelector.css
diff --git a/src/pages/SeedList/Recommend/TorrentSelector.jsx b/src/pages/SeedList/Recommend/TorrentSelector.jsx
new file mode 100644
index 0000000..abe2806
--- /dev/null
+++ b/src/pages/SeedList/Recommend/TorrentSelector.jsx
@@ -0,0 +1,84 @@
+import React, { useState, useEffect } from 'react';
+import axios from 'axios';
+import toast from 'react-hot-toast';
+
+const TorrentSelector = ({ selectedIds, onSelect }) => {
+  const [query, setQuery] = useState('');
+  const [torrents, setTorrents] = useState([]);
+  const [loading, setLoading] = useState(false);
+
+  useEffect(() => {
+    const timer = setTimeout(() => {
+      if (query.trim()) {
+        fetchTorrents(query.trim());
+      } else {
+        setTorrents([]);
+      }
+    }, 500);
+
+    return () => clearTimeout(timer);
+  }, [query]);
+
+  const fetchTorrents = async (keyword) => {
+    setLoading(true);
+    try {
+      const res = await axios.get('/seeds/list', {
+        params: {
+          title: keyword,  // 与 SeedList 统一参数名
+          page: 1,
+          size: 20,
+          orderKey: 'upload_time',
+          orderDesc: true,
+        }
+      });
+      if (res.data.code === 0) {
+        // 如果接口返回 data.list 格式
+        setTorrents(res.data.data.list || res.data.data || []);
+      } else {
+        setTorrents([]);
+      }
+    } catch (error) {
+      console.error('请求失败', error);
+      setTorrents([]);
+    }
+    setLoading(false);
+  };
+
+  const toggleSelect = (id, checked) => {
+    onSelect(id, checked);
+  };
+
+  return (
+    <div className="torrent-selector">
+      <input
+        placeholder="搜索种子名称"
+        value={query}
+        onChange={(e) => setQuery(e.target.value)}
+      />
+      {loading && <div>加载中...</div>}
+      <ul>
+        {torrents.map(t => (
+          <li key={t.id}>
+            <label>
+              <input
+                type="checkbox"
+                value={t.id}
+                checked={selectedIds.includes(t.id)}
+                onChange={(e) => toggleSelect(t.id, e.target.checked)}
+              />
+              {t.title || t.name}
+            </label>
+          </li>
+        ))}
+      </ul>
+      {selectedIds.length > 0 && (
+        <div className="selected-torrents">
+          <b>已选种子:</b>
+          {selectedIds.join(', ')}
+        </div>
+      )}
+    </div>
+  );
+};
+
+export default TorrentSelector;
diff --git a/src/pages/SeedList/SeedDetail/SeedDetail.css b/src/pages/SeedList/SeedDetail/SeedDetail.css
index 9e1a10e..d5a5148 100644
--- a/src/pages/SeedList/SeedDetail/SeedDetail.css
+++ b/src/pages/SeedList/SeedDetail/SeedDetail.css
@@ -1,5 +1,5 @@
 .seed-detail-page {
-  background: #333;
+  background: #f8f3ef;
   min-height: 100vh;
   padding-bottom: 40px;
   font-family: 'Helvetica Neue', sans-serif;
diff --git a/src/pages/SeedList/SeedList.css b/src/pages/SeedList/SeedList.css
index cf5502c..cdbf651 100644
--- a/src/pages/SeedList/SeedList.css
+++ b/src/pages/SeedList/SeedList.css
@@ -1,99 +1,109 @@
 .seed-list-container {
-  background: #333;
+  background: #f8f3ef; /* 柔和米棕背景 */
 }
 
 /* 搜索、排序控件 */
 .controls {
+  background-color: #fffaf7; /* 粉米白 */
+  padding: 10px 20px;
   display: flex;
   justify-content: center;
   gap: 16px;
-  padding: 10px 20px;
-  background-color: #5F4437;
+  border-radius: 12px;
+  border: 1px solid #e2cfc3; /* 浅棕边框 */
 }
 
 .search-input {
   padding: 6px 10px;
-  border-radius: 6px;
-  border: none;
+  border-radius: 8px;
+  border: 1px solid #e2cfc3;
   width: 200px;
+  font-size: 1rem;
+  color: #6b4f3b;
+  background-color: #fffaf7;
 }
 
 .sort-select {
   padding: 6px;
-  border-radius: 6px;
-  border: none;
+  border-radius: 8px;
+  border: 1px solid #e2cfc3;
+  background-color: #fffaf7;
+  color: #6b4f3b;
+  cursor: pointer;
 }
 
 /* 标签过滤 */
 .tag-filters {
-  background-color: #5F4437;
+  background-color: #fffaf7;
   display: flex;
   justify-content: center;
   flex-wrap: wrap;
   gap: 8px;
   padding: 10px;
+  border-radius: 12px;
+  border: 1px solid #e2cfc3;
 }
 
 .tag-button {
-  background-color: #b38867;
-  color: white;
+  background-color: #eecfc1;
+  color: #6b4f3b;
   border: none;
   border-radius: 20px;
   padding: 6px 12px;
   cursor: pointer;
+  font-weight: 600;
+  transition: background-color 0.3s ease;
+}
+
+.tag-button:hover {
+  background-color: #f3ded7;
+  color: #4b3325;
 }
 
 .active-tag {
-  background-color: #d17c4f;
+  background-color: #6b4f3b;
+  color: #fffaf7;
 }
 
 .clear-filter-btn {
   background: transparent;
   border: none;
-  color: #888;
+  color: #6b4f3b;
   font-size: 1rem;
   cursor: pointer;
   margin-left: 4px;
+  transition: color 0.3s ease;
 }
 
 .clear-filter-btn:hover {
-  color: red;
+  color: #4b3325;
 }
 
-/* 去除 Link 组件默认的下划线和文字颜色变化 */
-.seed-item-link {
-  text-decoration: none;
-  color: inherit;
-  display: block;
-}
-
-/* 卡片展示 */
 .seed-list-content {
   padding: 20px;
-  background-color: #5F4437;
+  background-color: #fffaf7;
+  border-radius: 12px;
+  box-shadow: 0 10px 30px rgba(0, 0, 0, 0.08);
 }
 
 .seed-list-card {
-  background-color: #e9ded2;
-  color: #333;
+  background-color: #fffaf7;
+  color: #6b4f3b;
   border-radius: 8px;
-  box-shadow: 0 1px 4px rgba(0, 0, 0, 0.1);
+  box-shadow: 0 1px 4px rgba(107, 79, 59, 0.15);
   overflow: hidden;
 }
 
 .seed-list-header {
+  background-color: #eecfc1;
+  font-weight: bold;
+  color: #4e342e;
+  padding: 12px 16px;
   display: grid;
   grid-template-columns: 180px 2fr 1fr 1fr 1fr 1fr;
   align-items: center;
   justify-items: center;
-  padding: 12px 16px;
-  background-color: #BA929A;
-  font-weight: bold;
-}
-
-.seed-list-body {
-  display: flex;
-  flex-direction: column;
+  border-radius: 8px 8px 0 0;
 }
 
 .seed-item {
@@ -102,33 +112,31 @@
   align-items: center;
   justify-items: center;
   padding: 12px 16px;
-  border-top: 1px solid #ccc;
+  border-top: 1px solid #e2cfc3;
+  color: #6b4f3b;
+  transition: background-color 0.2s ease;
+  cursor: pointer;
+}
+
+.seed-item:hover {
+  background-color: #f3ded7;
 }
 
 .seed-item-cover {
   width: 100px;
-  height: 140px;
+  height: auto;
   object-fit: cover;
   border-radius: 6px;
-  flex-shrink: 0;
+  margin-right: 12px;
+  box-shadow: 0 3px 6px rgb(107 79 59 / 0.2);
 }
 
 .seed-item-title {
   width: 100%;
   text-align: center;
-}
-
-.seed-title-row {
-  display: flex;
-  flex-direction: column;
-  align-items: center;
-  gap: 8px;
-}
-
-.seed-title {
+  font-weight: 600;
   font-size: 1.1rem;
-  margin: 0;
-  font-weight: bold;
+  color: #4e342e;
 }
 
 .seed-tags {
@@ -141,81 +149,64 @@
   word-break: break-word;
 }
 
-.seed-item-actions {
-  display: flex;
-  flex-direction: row;
-  gap: 8px;
-  justify-content: center;
-}
-
-.seed-header-cover {
-  width: 180px;
-  flex-shrink: 0;
-  text-align: center;
-}
-
-.seed-header-title,
-.seed-header-size,
-.seed-header-upload-time,
-.seed-header-downloads,
-.seed-header-actions {
-  text-align: center;
-}
-
 .seed-info {
   display: flex;
   justify-content: space-between;
   font-size: 0.9rem;
-  color: #666;
+  color: #5d4037;
   margin-bottom: 8px;
 }
 
 .tag-label {
-  background-color: #eee;
+  background-color: #fffaf7;
   border-radius: 4px;
   padding: 2px 6px;
   font-size: 12px;
-}
-
-.btn-primary,
-.btn-secondary,
-.btn-outline {
-  padding: 6px 12px;
-  border: none;
-  border-radius: 6px;
-  cursor: pointer;
-  font-size: 0.9rem;
-  text-align: center;
-  white-space: nowrap;
-  transition: background-color 0.2s ease;
+  color: #6b4f3b;
+  font-weight: 500;
 }
 
 .btn-primary {
-  background-color: #007bff;
-  color: white;
+  background-color: #6b4f3b;
+  color: #fffaf7;
+  border-radius: 6px;
+  padding: 6px 12px;
+  cursor: pointer;
+  transition: background-color 0.3s ease;
+  font-weight: 600;
 }
 
 .btn-primary:hover {
-  background-color: #0056b3;
+  background-color: #4b3325;
 }
 
 .btn-secondary {
-  background-color: #28a745;
-  color: white;
+  background-color: #eecfc1;
+  color: #6b4f3b;
+  border-radius: 6px;
+  padding: 6px 12px;
+  cursor: pointer;
+  transition: background-color 0.3s ease;
+  font-weight: 600;
 }
 
 .btn-secondary:hover {
-  background-color: #218838;
+  background-color: #f3ded7;
 }
 
 .btn-outline {
   background-color: transparent;
-  border: 1px solid #ccc;
-  color: #333;
+  border: 1px solid #6b4f3b;
+  color: #6b4f3b;
+  border-radius: 6px;
+  padding: 6px 12px;
+  cursor: pointer;
+  transition: background-color 0.3s ease;
 }
 
 .btn-outline:hover {
-  background-color: #f8f9fa;
+  background-color: #fffaf7;
+  color: #4b3325;
 }
 
 .seed-cover {
@@ -224,11 +215,5 @@
   object-fit: cover;
   border-radius: 8px;
   margin-bottom: 12px;
-}
-.seed-item-cover {
-    width: 100px;
-    height: auto;
-    object-fit: cover;
-    border-radius: 4px;
-    margin-right: 12px;
-}
+  box-shadow: 0 4px 12px rgb(107 79 59 / 0.2);
+}
\ No newline at end of file
diff --git a/src/pages/SeedList/SeedList.jsx b/src/pages/SeedList/SeedList.jsx
index a74d008..263038b 100644
--- a/src/pages/SeedList/SeedList.jsx
+++ b/src/pages/SeedList/SeedList.jsx
@@ -1,11 +1,14 @@
-// export default SeedList;
 import React, { useState, useEffect } from 'react';
 import { Link } from 'wouter';
 import axios from 'axios';
 import Recommend from './Recommend/Recommend';
-import Header from '../../components/Header';  // 引入 Header 组件
+import Header from '../../components/Header';
 import './SeedList.css';
 import { useUser } from '../../context/UserContext';
+import toast from 'react-hot-toast';
+import { confirmAlert } from 'react-confirm-alert';
+import 'react-confirm-alert/src/react-confirm-alert.css';
+import AuthButton from '../../components/AuthButton';
 
 const SeedList = () => {
     const [seeds, setSeeds] = useState([]);
@@ -15,13 +18,13 @@
     const [activeTab, setActiveTab] = useState('种子列表');
     const [filters, setFilters] = useState({});
     const [selectedFilters, setSelectedFilters] = useState({});
-    const [tagMode, setTagMode] = useState('any'); // 与接口对应,any / all
+    const [tagMode, setTagMode] = useState('any');
     const [errorMsg, setErrorMsg] = useState('');
     const { user } = useUser();
 
     const TAGS = ['猜你喜欢', '电影', '电视剧', '动漫', '音乐', '游戏', '综艺', '软件', '体育', '学习', '纪录片', '其他'];
 
-const CATEGORY_MAP = {
+    const CATEGORY_MAP = {
         '电影': 'movie',
         '电视剧': 'tv',
         '动漫': 'anime',
@@ -33,10 +36,9 @@
         '学习': 'study',
         '纪录片': 'documentary',
         '其他': 'other',
-        '猜你喜欢': '',        
-        '种子列表': '',        
-};
-
+        '猜你喜欢': '',
+        '种子列表': '',
+    };
 
     const buildQueryParams = () => {
         const category = CATEGORY_MAP[activeTab] || '';
@@ -61,26 +63,22 @@
 
         if (tags.length > 0) {
             params.tags = tags;
-            params.tagMode = tagMode; // any 或 all
+            params.tagMode = tagMode;
         }
 
         return params;
     };
 
     const fetchSeeds = async () => {
-        if (activeTab === '猜你喜欢') return; 
+        if (activeTab === '猜你喜欢') return;
         setLoading(true);
         setErrorMsg('');
         try {
             const params = buildQueryParams();
             const response = await axios.get('/seeds/list', { params });
-
             const data = response.data;
 
-            if (data.code !== 0) {
-                throw new Error(data.msg || '获取失败');
-            }
-
+            if (data.code !== 0) throw new Error(data.msg || '获取失败');
             setSeeds(data.data || []);
         } catch (error) {
             console.error('获取种子列表失败:', error);
@@ -120,16 +118,9 @@
     }, [activeTab, sortOption, selectedFilters, tagMode, searchTerm]);
 
     const handleDownload = async (seedId) => {
-        if (!user || !user.userId) {
-            alert('请先登录再下载种子文件');
-            return;
-        }
-
         try {
             const response = await axios.get(`/seeds/${seedId}/download`, {
-                params: {
-                    passkey: user.userId,
-                },
+                params: { passkey: user.userId },
                 responseType: 'blob'
             });
 
@@ -142,22 +133,16 @@
             URL.revokeObjectURL(downloadUrl);
         } catch (error) {
             console.error('下载失败:', error);
-            alert('下载失败,请稍后再试。');
+            toast.error('下载失败,请稍后再试。');
         }
     };
 
     const handleFilterChange = (key, value) => {
-        setSelectedFilters(prev => ({
-            ...prev,
-            [key]: value
-        }));
+        setSelectedFilters(prev => ({ ...prev, [key]: value }));
     };
 
     const clearFilter = (key) => {
-        setSelectedFilters(prev => ({
-            ...prev,
-            [key]: '不限'
-        }));
+        setSelectedFilters(prev => ({ ...prev, [key]: '不限' }));
     };
 
     return (
@@ -172,19 +157,11 @@
                     onChange={(e) => setSearchTerm(e.target.value)}
                     className="search-input"
                 />
-                <select
-                    value={sortOption}
-                    onChange={(e) => setSortOption(e.target.value)}
-                    className="sort-select"
-                >
+                <select value={sortOption} onChange={(e) => setSortOption(e.target.value)} className="sort-select">
                     <option value="最新">最新</option>
                     <option value="最热">最热</option>
                 </select>
-                <select
-                    value={tagMode}
-                    onChange={(e) => setTagMode(e.target.value)}
-                    className="tag-mode-select"
-                >
+                <select value={tagMode} onChange={(e) => setTagMode(e.target.value)} className="tag-mode-select">
                     <option value="any">包含任意标签</option>
                     <option value="all">包含所有标签</option>
                 </select>
@@ -192,7 +169,8 @@
 
             <div className="tag-filters">
                 {TAGS.map(tag => (
-                    <button
+                    <AuthButton
+                        roles={["test"]}
                         key={tag}
                         className={`tag-button ${activeTab === tag ? 'active-tag' : ''}`}
                         onClick={() => {
@@ -202,7 +180,7 @@
                         }}
                     >
                         {tag}
-                    </button>
+                    </AuthButton>
                 ))}
             </div>
 
@@ -211,21 +189,13 @@
                     {Object.entries(filters).map(([key, options]) => (
                         <div className="filter-group" key={key}>
                             <label>{key}:</label>
-                            <select
-                                value={selectedFilters[key]}
-                                onChange={(e) => handleFilterChange(key, e.target.value)}
-                            >
+                            <select value={selectedFilters[key]} onChange={(e) => handleFilterChange(key, e.target.value)}>
                                 {options.map(opt => (
                                     <option key={opt} value={opt}>{opt}</option>
                                 ))}
                             </select>
                             {selectedFilters[key] !== '不限' && (
-                                <button
-                                    className="clear-filter-btn"
-                                    onClick={() => clearFilter(key)}
-                                >
-                                    ✕
-                                </button>
+                                <button className="clear-filter-btn" onClick={() => clearFilter(key)}>✕</button>
                             )}
                         </div>
                     ))}
@@ -270,16 +240,11 @@
                                 }
 
                                 return (
-                                   <Link to={`/seed/${seed.id}`} key={index} className="seed-item-link">
+                                    <Link to={`/seed/${seed.id}`} key={index} className="seed-item-link">
                                         <div className="seed-item">
                                             {seed.imageUrl && (
-                                                <img
-                                                    src={seed.imageUrl}
-                                                    alt={seed.title}
-                                                    className="seed-item-cover"
-                                                />
+                                                <img src={seed.imageUrl} alt={seed.title} className="seed-item-cover" />
                                             )}
-
                                             <div className="seed-item-title">
                                                 <div className="seed-title-row">
                                                     <h3 className="seed-title">{seed.title}</h3>
@@ -293,16 +258,30 @@
                                             <div className="seed-item-size">{seed.size || '未知'}</div>
                                             <div className="seed-item-upload-time">{seed.upload_time?.split('T')[0] || '未知'}</div>
                                             <div className="seed-item-downloads">{seed.downloads ?? 0} 次下载</div>
-                                            <div
-                                                className="seed-item-actions"
-                                                onClick={e => e.stopPropagation()}
-                                            >
+                                            <div className="seed-item-actions" onClick={e => e.stopPropagation()}>
                                                 <button
                                                     className="btn-primary"
                                                     onClick={e => {
                                                         e.preventDefault();
                                                         e.stopPropagation();
-                                                        handleDownload(seed.id);
+                                                        if (!user || !user.userId) {
+                                                            toast.error('请先登录再下载种子文件');
+                                                            return;
+                                                        }
+                                                        confirmAlert({
+                                                            title: '确认下载',
+                                                            message: `是否下载种子「${seed.title}」?`,
+                                                            buttons: [
+                                                                {
+                                                                    label: '确认',
+                                                                    onClick: () => handleDownload(seed.id)
+                                                                },
+                                                                {
+                                                                    label: '取消',
+                                                                    onClick: () => { }
+                                                                }
+                                                            ]
+                                                        });
                                                     }}
                                                 >
                                                     下载
@@ -314,7 +293,7 @@
                                                         e.stopPropagation();
 
                                                         if (!user || !user.userId) {
-                                                            alert('请先登录再收藏');
+                                                            toast.error('请先登录再收藏');
                                                             return;
                                                         }
 
@@ -324,13 +303,13 @@
                                                             });
 
                                                             if (res.data.code === 0) {
-                                                                alert('操作成功');
+                                                                toast.success('操作成功');
                                                             } else {
-                                                                alert(res.data.msg || '操作失败');
+                                                                toast.error(res.data.msg || '操作失败');
                                                             }
                                                         } catch (err) {
                                                             console.error('收藏失败:', err);
-                                                            alert('收藏失败,请稍后再试。');
+                                                            toast.error('收藏失败,请稍后再试。');
                                                         }
                                                     }}
                                                 >
diff --git a/src/pages/UserCenter/UserNav.css b/src/pages/UserCenter/UserNav.css
index 6a1b405..baf3cb9 100644
--- a/src/pages/UserCenter/UserNav.css
+++ b/src/pages/UserCenter/UserNav.css
@@ -35,11 +35,11 @@
 
 /* 鼠标悬浮时的背景颜色 */
 .user-nav-item:hover {
-  background-color: #5a1414;
+  background-color: #fffaf7;
 }
 
 /* 激活项的样式 */
 .user-nav-item.active {
-  background-color: #BA929A;
+  background-color: #eecfc1;
   color: white;
 }    
\ No newline at end of file
diff --git a/src/pages/UserCenter/UserProfile.css b/src/pages/UserCenter/UserProfile.css
index edc04e1..18f793d 100644
--- a/src/pages/UserCenter/UserProfile.css
+++ b/src/pages/UserCenter/UserProfile.css
@@ -4,7 +4,7 @@
   font-family: Arial, sans-serif;
   display: flex;
   gap: 10%;
-  background: #333;
+  background: #f8f3ef;
 }
 
 .right-content {
@@ -47,7 +47,7 @@
 }  
 
 .common-card {
-  background-color: #e9ded2;
+  background-color: #fffaf7;
   border-radius: 16px;
   margin: 0 auto;
   margin-top: 40px;
@@ -72,7 +72,7 @@
   position: absolute;
   bottom: 0;
   right: 0;
-  background: #3498db;
+  background: #4b3325;
   color: white;
   padding: 4px 8px;
   font-size: 12px;
@@ -135,7 +135,7 @@
   position: absolute;
   bottom: 0;
   right: 0;
-  background: #3498db;
+  background: #4b3325;
   color: white;
   padding: 4px 8px;
   font-size: 12px;
@@ -201,7 +201,7 @@
   padding: 6px 12px;
   border: none;
   border-radius: 6px;
-  background-color: #4a90e2;
+  background-color: #4b3325;
   color: white;
   cursor: pointer;
 }
@@ -245,14 +245,14 @@
 
 .profile-actions button {
   padding: 8px 16px;
-  background-color: #4677f5;
+  background-color: #4b3325;
   color: white;
   border: none;
   border-radius: 6px;
   cursor: pointer;
 }
 
-.modal {
+.user-modal {
   position: fixed;
   top: 0;
   left: 0;
@@ -265,21 +265,21 @@
   z-index: 99;
 }
 
-.modal-content {
+.user-modal-content {
   background-color: white;
   padding: 20px;
   border-radius: 8px;
   width: 300px;
 }
 
-.modal-content input {
+.user-modal-content input {
   display: block;
   width: 100%;
   margin: 10px 0;
   padding: 8px;
 }
 
-.modal-buttons {
+.user-modal-buttons {
   display: flex;
   justify-content: space-between;
 }
diff --git a/src/pages/UserCenter/UserProfile.jsx b/src/pages/UserCenter/UserProfile.jsx
index cf52d99..bcfbf0b 100644
--- a/src/pages/UserCenter/UserProfile.jsx
+++ b/src/pages/UserCenter/UserProfile.jsx
@@ -1,266 +1,3 @@
-// import React, { useEffect, useState } from 'react';
-// 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, 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) {
-//       setError('未登录或用户信息缺失');
-//       setUserProfile(null);
-//       return;
-//     }
-
-//     const fetchUserProfile = async () => {
-//       try {
-//         setError(null);
-//         const { data: raw } = await axios.get(`/echo/user/${user.userId}/getProfile`);
-//         if (!raw) {
-//           setError('用户数据为空');
-//           setUserProfile(null);
-//           return;
-//         }
-
-//         const profile = {
-//           avatarUrl: raw.avatarUrl
-//             ? `${process.env.REACT_APP_AVATAR_BASE_URL}${raw.avatarUrl}`
-//             : DEFAULT_AVATAR_URL,
-//           nickname: raw.username || '未知用户',
-//           email: raw.email || '未填写',
-//           gender: raw.gender || '保密',
-//           bio: raw.description || '无',
-//           interests: raw.hobbies ? raw.hobbies.split(',') : [],
-//           level: raw.level || '未知',
-//           experience: raw.experience ?? 0,
-//           uploadAmount: raw.uploadCount ?? 0,
-//           downloadAmount: raw.downloadCount ?? 0,
-//           shareRate: raw.shareRate ?? 0,
-//           joinedDate: raw.registrationTime,
-//         };
-
-//         setUserProfile(profile);
-//       } catch (err) {
-//         setError(err.response?.status === 404 ? '用户不存在' : '请求失败,请稍后再试');
-//         setUserProfile(null);
-//       }
-//     };
-
-//     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) => {
-//     const file = e.target.files[0];
-//     if (!file) return;
-
-//     const formData = new FormData();
-//     formData.append('file', file);
-
-//     try {
-//       const { data } = await axios.post(
-//         `/echo/user/${user.userId}/uploadAvatar`,
-//         formData,
-//         { headers: { 'Content-Type': 'multipart/form-data' } }
-//       );
-
-//       if (data?.avatarUrl) {
-//         setUserProfile((prev) => ({
-//           ...prev,
-//           avatarUrl: `${process.env.REACT_APP_AVATAR_BASE_URL}${data.avatarUrl}`,
-//         }));
-//         alert('头像上传成功');
-//       } else {
-//         alert('头像上传成功,但未返回新头像地址');
-//       }
-//     } catch (err) {
-//       console.error('上传失败:', err);
-//       alert('头像上传失败,请重试');
-//     }
-//   };
-
-//   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;
-
-//   const {
-//     avatarUrl,
-//     nickname,
-//     email,
-//     gender,
-//     bio,
-//     interests,
-//     level,
-//     experience,
-//     uploadAmount,
-//     downloadAmount,
-//     shareRate,
-//     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">
-//         <div className="profile-header">
-//           <div className="avatar-wrapper">
-//             <img src={avatarUrl} alt={nickname} className="avatar" />
-//             <label htmlFor="avatar-upload" className="avatar-upload-label">
-//               上传头像
-//             </label>
-//             <input
-//               type="file"
-//               id="avatar-upload"
-//               accept="image/*"
-//               style={{ display: 'none' }}
-//               onChange={handleAvatarUpload}
-//             />
-//           </div>
-//           <h1>{nickname}</h1>
-//         </div>
-
-//         <div className="profile-details">
-//           <p><strong>邮箱:</strong>{email}</p>
-//           <p><strong>性别:</strong>{gender}</p>
-//           <p><strong>个人简介:</strong>{bio}</p>
-//           <p><strong>兴趣:</strong>{interests.length > 0 ? interests.join(', ') : '无'}</p>
-//           <p><strong>等级:</strong>{level}</p>
-//           <p><strong>经验:</strong>{experience}</p>
-//           <p><strong>上传量:</strong>{uploadAmount}</p>
-//           <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;
-
 import React from 'react';
 import UserProfileBase from './UserProfileBase';
 import UserLevelExperience from './UserLevelExperience';
diff --git a/src/pages/UserCenter/UserProfileBase.jsx b/src/pages/UserCenter/UserProfileBase.jsx
index 7a1f726..791baca 100644
--- a/src/pages/UserCenter/UserProfileBase.jsx
+++ b/src/pages/UserCenter/UserProfileBase.jsx
@@ -2,6 +2,9 @@
 import axios from 'axios';
 import { useUser } from '../../context/UserContext';
 import { useLocation } from 'wouter';
+import toast from 'react-hot-toast';
+import { confirmAlert } from 'react-confirm-alert';
+import 'react-confirm-alert/src/react-confirm-alert.css';
 
 const DEFAULT_AVATAR_URL = `${process.env.PUBLIC_URL}/default-avatar.png`;
 
@@ -10,13 +13,11 @@
   const [userProfile, setUserProfile] = 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(() => {
@@ -55,7 +56,6 @@
         };
 
         setUserProfile(profile);
-        // 加载经验信息
         if (onLoadExperienceInfo) onLoadExperienceInfo(user.userId);
       } catch (err) {
         setError(err.response?.status === 404 ? '用户不存在' : '请求失败,请稍后再试');
@@ -85,28 +85,44 @@
           ...prev,
           avatarUrl: `${process.env.REACT_APP_AVATAR_BASE_URL}${data.avatarUrl}`,
         }));
-        alert('头像上传成功');
+        toast.success('头像上传成功');
       } else {
-        alert('头像上传成功,但未返回新头像地址');
+        toast.success('头像上传成功,但未返回新头像地址');
       }
     } catch (err) {
       console.error('上传失败:', err);
-      alert('头像上传失败,请重试');
+      toast.error('头像上传失败,请重试');
     }
   };
 
   const handleLogout = () => {
     logout();
-    setLocation('/auth'); // 退出后跳转登录页
+    setLocation('/auth');
+  };
+
+  const confirmPasswordChange = () => {
+    confirmAlert({
+      title: '确认修改密码',
+      message: '确定要修改密码吗?修改成功后将自动登出。',
+      buttons: [
+        {
+          label: '确认',
+          onClick: handleChangePassword,
+        },
+        {
+          label: '取消',
+        },
+      ],
+    });
   };
 
   const handleChangePassword = async () => {
     if (!oldPassword || !newPassword || !confirmPassword) {
-      alert('请填写所有字段');
+      toast.error('请填写所有字段');
       return;
     }
     if (newPassword !== confirmPassword) {
-      alert('两次输入的新密码不一致');
+      toast.error('两次输入的新密码不一致');
       return;
     }
 
@@ -117,11 +133,14 @@
         new_password: newPassword,
         confirm_password: confirmPassword,
       });
-      alert('密码修改成功,请重新登录');
+
+      toast.success('密码修改成功,请重新登录');
       logout();
-      window.location.reload();
+      setTimeout(() => {
+        window.location.reload();
+      }, 1500);
     } catch (err) {
-      alert(err.response?.data?.message || '密码修改失败,请检查原密码是否正确');
+      toast.error(err.response?.data?.message || '密码修改失败,请检查原密码是否正确');
     }
   };
 
@@ -169,23 +188,19 @@
           <p><strong>性别:</strong>{gender}</p>
           <p><strong>个人简介:</strong>{bio}</p>
           <p><strong>兴趣:</strong>{interests.length > 0 ? interests.join(', ') : '无'}</p>
-          {/* <p><strong>等级:</strong>{level}</p>
-          <p><strong>经验:</strong>{experience}</p> */}
           <p><strong>上传量:</strong>{uploadAmount}</p>
           <p><strong>下载量:</strong>{downloadAmount}</p>
           <p><strong>分享率:</strong>{(shareRate * 100).toFixed(2)}%</p>
           <p><strong>加入时间:</strong>{new Date(joinedDate).toLocaleDateString()}</p>
 
-          {/* 修改密码与退出登录按钮 */}
           <div className="profile-actions">
             <button onClick={() => setShowPwdModal(true)}>修改密码</button>
             <button onClick={handleLogout}>退出登录</button>
           </div>
 
-          {/* 修改密码弹窗 */}
           {showPwdModal && (
-            <div className="modal">
-              <div className="modal-content">
+            <div className="user-modal">
+              <div className="user-modal-content">
                 <h3>修改密码</h3>
                 <input
                   type="password"
@@ -205,8 +220,8 @@
                   value={confirmPassword}
                   onChange={(e) => setConfirmPassword(e.target.value)}
                 />
-                <div className="modal-buttons">
-                  <button onClick={handleChangePassword}>确认修改</button>
+                <div className="user-modal-buttons">
+                  <button onClick={confirmPasswordChange}>确认修改</button>
                   <button onClick={() => setShowPwdModal(false)}>取消</button>
                 </div>
               </div>
@@ -218,4 +233,4 @@
   );
 };
 
-export default UserProfileBase;  
\ No newline at end of file
+export default UserProfileBase;
diff --git a/src/pages/UserCenter/UserRecharge.jsx b/src/pages/UserCenter/UserRecharge.jsx
index 7a2f9b3..b1dff24 100644
--- a/src/pages/UserCenter/UserRecharge.jsx
+++ b/src/pages/UserCenter/UserRecharge.jsx
@@ -1,42 +1,183 @@
 
-// export default Recharge;
+// // export default Recharge;
+// import React, { useState } from 'react';
+// import axios from 'axios';
+// import { useUser } from '../../context/UserContext';
+// import toast from 'react-hot-toast';
+// import { confirmAlert } from 'react-confirm-alert';
+// import 'react-confirm-alert/src/react-confirm-alert.css';
+
+// const UserRecharge = () => {
+//   const [amount, setAmount] = useState('');
+//   const [loading, setLoading] = useState(false);
+//   const [message, setMessage] = useState('');
+//   const [showPayDialog, setShowPayDialog] = useState(false);
+//   const [payProcessing, setPayProcessing] = useState(false);
+
+//   const { user } = useUser();
+//   const userId = user?.userId;
+
+//   // 点击提交充值,打开模拟支付弹窗
+//   const handleSubmit = () => {
+//     if (!userId) {
+//       setMessage('未登录,无法充值');
+//       return;
+//     }
+//     if (!amount || isNaN(amount) || Number(amount) <= 0) {
+//       setMessage('请输入有效的充值金额');
+//       return;
+//     }
+//     setMessage('');
+//     setShowPayDialog(true);
+//   };
+
+//   // 模拟支付弹窗点击“确认支付”
+//   const handlePayConfirm = async () => {
+//     setPayProcessing(true);
+//     setMessage('');
+
+//     // 模拟支付等待 2 秒
+//     setTimeout(async () => {
+//       try {
+//         // 支付成功后调用后端充值接口
+//         const response = await axios.post('/echo/user/recharge', null, {
+//           params: {
+//             userId,
+//             amount: Number(amount),
+//           },
+//         });
+
+//         setMessage(response.data.message || '充值成功!');
+//         setAmount('');
+//       } catch (error) {
+//         setMessage(error.response?.data?.message || '充值失败,请重试');
+//       } finally {
+//         setPayProcessing(false);
+//         setShowPayDialog(false);
+//       }
+//     }, 2000);
+//   };
+
+//   // 取消支付弹窗
+//   const handlePayCancel = () => {
+//     setShowPayDialog(false);
+//     setMessage('支付已取消');
+//   };
+
+//   return (
+//     <div className="recharge-page" style={{ maxWidth: 400, margin: 'auto', padding: 20 }}>
+//       <h2>充值服务</h2>
+//       <div style={{ marginBottom: 12 }}>
+//         <label>
+//           充值金额:
+//           <input
+//             type="number"
+//             min="1"
+//             value={amount}
+//             onChange={(e) => setAmount(e.target.value)}
+//             disabled={loading || payProcessing}
+//             style={{ marginLeft: 8, width: 120 }}
+//           />
+//         </label>
+//       </div>
+//       <button onClick={handleSubmit} disabled={loading || payProcessing}>
+//         {loading || payProcessing ? '处理中...' : '提交充值'}
+//       </button>
+
+//       {message && <p style={{ marginTop: 12 }}>{message}</p>}
+
+//       {/* 模拟支付弹窗 */}
+//       {showPayDialog && (
+//         <div
+//           style={{
+//             position: 'fixed',
+//             top: 0, left: 0, right: 0, bottom: 0,
+//             backgroundColor: 'rgba(0,0,0,0.5)',
+//             display: 'flex',
+//             justifyContent: 'center',
+//             alignItems: 'center',
+//             zIndex: 1000,
+//           }}
+//         >
+//           <div
+//             style={{
+//               backgroundColor: '#fff',
+//               padding: 20,
+//               borderRadius: 8,
+//               width: 300,
+//               textAlign: 'center',
+//               boxShadow: '0 0 10px rgba(0,0,0,0.25)',
+//             }}
+//           >
+//             <h3>模拟支付</h3>
+//             <p>支付金额:{amount} 元</p>
+
+//             {payProcessing ? (
+//               <p>支付处理中,请稍候...</p>
+//             ) : (
+//               <>
+//                 <button onClick={handlePayConfirm} style={{ marginRight: 10 }}>
+//                   确认支付
+//                 </button>
+//                 <button onClick={handlePayCancel}>取消</button>
+//               </>
+//             )}
+//           </div>
+//         </div>
+//       )}
+//     </div>
+//   );
+// };
+
+// export default UserRecharge;
 import React, { useState } from 'react';
 import axios from 'axios';
 import { useUser } from '../../context/UserContext';
+import toast from 'react-hot-toast';
+import { confirmAlert } from 'react-confirm-alert';
+import 'react-confirm-alert/src/react-confirm-alert.css';
 
 const UserRecharge = () => {
   const [amount, setAmount] = useState('');
-  const [loading, setLoading] = useState(false);
-  const [message, setMessage] = useState('');
-  const [showPayDialog, setShowPayDialog] = useState(false);
   const [payProcessing, setPayProcessing] = useState(false);
 
   const { user } = useUser();
   const userId = user?.userId;
 
-  // 点击提交充值,打开模拟支付弹窗
+  // 点击提交充值
   const handleSubmit = () => {
     if (!userId) {
-      setMessage('未登录,无法充值');
+      toast.error('未登录,无法充值');
       return;
     }
     if (!amount || isNaN(amount) || Number(amount) <= 0) {
-      setMessage('请输入有效的充值金额');
+      toast.error('请输入有效的充值金额');
       return;
     }
-    setMessage('');
-    setShowPayDialog(true);
+
+    confirmAlert({
+      title: '确认支付',
+      message: `确认充值 ${amount} 元吗?`,
+      buttons: [
+        {
+          label: '确认支付',
+          onClick: () => handlePayConfirm(),
+        },
+        {
+          label: '取消',
+          onClick: () => toast('已取消充值'),
+        },
+      ],
+    });
   };
 
-  // 模拟支付弹窗点击“确认支付”
   const handlePayConfirm = async () => {
     setPayProcessing(true);
-    setMessage('');
 
-    // 模拟支付等待 2 秒
+    toast.loading('正在处理支付...', { id: 'recharge' });
+
     setTimeout(async () => {
       try {
-        // 支付成功后调用后端充值接口
         const response = await axios.post('/echo/user/recharge', null, {
           params: {
             userId,
@@ -44,23 +185,16 @@
           },
         });
 
-        setMessage(response.data.message || '充值成功!');
+        toast.success(response.data.message || '充值成功!', { id: 'recharge' });
         setAmount('');
       } catch (error) {
-        setMessage(error.response?.data?.message || '充值失败,请重试');
+        toast.error(error.response?.data?.message || '充值失败,请重试', { id: 'recharge' });
       } finally {
         setPayProcessing(false);
-        setShowPayDialog(false);
       }
     }, 2000);
   };
 
-  // 取消支付弹窗
-  const handlePayCancel = () => {
-    setShowPayDialog(false);
-    setMessage('支付已取消');
-  };
-
   return (
     <div className="recharge-page" style={{ maxWidth: 400, margin: 'auto', padding: 20 }}>
       <h2>充值服务</h2>
@@ -72,56 +206,14 @@
             min="1"
             value={amount}
             onChange={(e) => setAmount(e.target.value)}
-            disabled={loading || payProcessing}
+            disabled={payProcessing}
             style={{ marginLeft: 8, width: 120 }}
           />
         </label>
       </div>
-      <button onClick={handleSubmit} disabled={loading || payProcessing}>
-        {loading || payProcessing ? '处理中...' : '提交充值'}
+      <button onClick={handleSubmit} disabled={payProcessing}>
+        {payProcessing ? '处理中...' : '提交充值'}
       </button>
-
-      {message && <p style={{ marginTop: 12 }}>{message}</p>}
-
-      {/* 模拟支付弹窗 */}
-      {showPayDialog && (
-        <div
-          style={{
-            position: 'fixed',
-            top: 0, left: 0, right: 0, bottom: 0,
-            backgroundColor: 'rgba(0,0,0,0.5)',
-            display: 'flex',
-            justifyContent: 'center',
-            alignItems: 'center',
-            zIndex: 1000,
-          }}
-        >
-          <div
-            style={{
-              backgroundColor: '#fff',
-              padding: 20,
-              borderRadius: 8,
-              width: 300,
-              textAlign: 'center',
-              boxShadow: '0 0 10px rgba(0,0,0,0.25)',
-            }}
-          >
-            <h3>模拟支付</h3>
-            <p>支付金额:{amount} 元</p>
-
-            {payProcessing ? (
-              <p>支付处理中,请稍候...</p>
-            ) : (
-              <>
-                <button onClick={handlePayConfirm} style={{ marginRight: 10 }}>
-                  确认支付
-                </button>
-                <button onClick={handlePayCancel}>取消</button>
-              </>
-            )}
-          </div>
-        </div>
-      )}
     </div>
   );
 };