merge

Change-Id: I5227831adac7f85854cbe7321c2a3aa39d8c1d7a
diff --git a/src/views/frame/frame.tsx b/src/views/frame/frame.tsx
index f41e53f..83106a1 100644
--- a/src/views/frame/frame.tsx
+++ b/src/views/frame/frame.tsx
@@ -12,8 +12,8 @@
 import logo from "&/assets/logo.png";
 import { useAppDispatch } from "@/hooks/store";
 import { useSelector } from "react-redux";
-
 import { checkAndRefreshToken } from "@/utils/jwt";
+import { useNavigate } from "react-router-dom";
 const Frame:React.FC = () => {
 
     useEffect(() => {
@@ -23,6 +23,11 @@
 
     const showSearch = useSelector((state: any) => state.setting.showSearch); 
     const theme= useSelector((state: any) => state.setting.theme);
+    
+    const navigate = useNavigate(); // ✅ 用于跳转
+    const [searchText, setSearchText] = useState(""); // ✅ 存储搜索输入内容
+
+
     const toggleSearch = () => {
         dispatch({ type: "setting/toggleSearch" });
     }
@@ -35,12 +40,27 @@
         dispatch({ type: "setting/toggleTheme" });
     };
 
+    // ✅ 用于跳转
+    const handleSearch = (e: React.KeyboardEvent<HTMLInputElement>) => {
+        if (e.key === "Enter" && searchText.trim() !== "") {
+            navigate(`/search?keyword=${encodeURIComponent(searchText.trim())}`);
+        }
+    };
+
 
     return (
         <div style={{ display: 'block', height: '100vh' }}>
             <header className={style.header}>
                 <img className={style.logo} src={logo} alt="website logo"></img>
-                {showSearch && (<input className={style.searchInput} placeholder="输入关键词进行搜索"/>)}
+                {showSearch && (
+                    // <input className={style.searchInput} placeholder="输入关键词进行搜索"/>
+                    <input
+                        className={style.searchInput}
+                        placeholder="输入关键词进行搜索"
+                        value={searchText}
+                        onChange={(e) => setSearchText(e.target.value)}
+                        onKeyDown={handleSearch} // ⌨️ 按下回车时执行跳转
+                    />)}
                 <div className={style.toollist}>
                     <SearchOutlined onClick={toggleSearch}/>
                     <FontSizeOutlined onClick={toggleFontSize}/>
diff --git a/src/views/homepage/homepage.module.css b/src/views/homepage/homepage.module.css
new file mode 100644
index 0000000..fdcc060
--- /dev/null
+++ b/src/views/homepage/homepage.module.css
@@ -0,0 +1,249 @@
+/* 主题色变量 */
+/* :root {
+    --primary-color: #3498db; 
+    --primary-hover: #2980b9;
+    --secondary-color: #f1c40f; 
+    --dark-color: #2c3e50; 
+    --light-color: #ecf0f1;
+    --text-color: #333;
+    --text-secondary: #7f8c8d;
+    --border-color: #ddd;
+  } */
+  
+    /* --bg-color: #2b2b2b;
+    --text-color: #f1f1f1;
+    --card-bg: #1e1e1e;
+    --border-color: #444444; */
+
+  .container {
+    min-height: 100vh;
+    background-color: var(--bg-color);
+    color: var(--text-color);
+    font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
+  }
+  
+  /* 顶部导航栏 */
+  .header {
+    display: flex;
+    align-items: center;
+    padding: 15px 30px;
+    background-color: solid var(--card-bg);
+    box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
+  }
+  
+  .logo {
+    height: 40px;
+    cursor: pointer;
+    transition: transform 0.3s;
+  }
+  
+  .logo:hover {
+    transform: scale(1.05);
+  }
+  
+  .siteTitle {
+    margin: 0 0 0 15px;
+    color: var(--text-color);
+    font-size: 24px;
+  }
+  
+  /* 主内容区 */
+  .mainContent {
+    display: flex;
+    min-height: calc(100vh - 70px);
+    padding: 20px;
+    gap: 20px;
+  }
+  
+  /* 左侧用户信息区 */
+  .userProfile {
+    flex: 2;
+    background-color: var(--card-bg);
+    border-radius: 10px;
+    padding: 25px;
+    box-shadow: 0 2px 5px rgba(0, 0, 0, 0.05);
+  }
+  
+  .userHeader {
+    display: flex;
+    align-items: center;
+    margin-bottom: 20px;
+  }
+  
+  .userAvatar {
+    width: 80px;
+    height: 80px;
+    border-radius: 50%;
+    object-fit: cover;
+    border: 3px solid var(--primary-color);
+    margin-right: 20px;
+  }
+  
+  .userInfo {
+    flex: 1;
+  }
+  
+  .username {
+    margin: 0 0 5px 0;
+    color: var(--text-color);
+    font-size: 22px;
+  }
+  
+  .inviteCode {
+    color: var(--text-color);
+    font-size: 14px;
+    margin-bottom: 10px;
+  }
+  
+  .editButton {
+    padding: 8px 20px;
+    background-color: var(--primary-color);
+    color: white;
+    border: none;
+    border-radius: 20px;
+    cursor: pointer;
+    font-size: 14px;
+    transition: background-color 0.3s;
+  }
+  
+  .editButton:hover {
+    background-color: var(--primary-hover);
+  }
+  
+  /* 用户统计 */
+  .userStats {
+    display: flex;
+    justify-content: space-between;
+    margin: 25px 0;
+    padding: 15px 0;
+    border-top: 1px solid var(--border-color);
+    border-bottom: 1px solid var(--border-color);
+  }
+  
+  .statItem {
+    text-align: center;
+    padding: 0 10px;
+  }
+  
+  .statNumber {
+    font-size: 22px;
+    font-weight: bold;
+    color: var(--primary-color);
+  }
+  
+  .statLabel {
+    font-size: 14px;
+    color: var(--text-secondary);
+    margin-top: 5px;
+  }
+  
+  /* 用户数据 */
+  .userData {
+    background-color: var(--light-color);
+    padding: 15px;
+    border-radius: 8px;
+    margin-bottom: 25px;
+  }
+  
+  .dataItem {
+    margin-bottom: 8px;
+    font-size: 15px;
+  }
+  
+  .dataItem strong {
+    color: var(--primary-color);
+  }
+  
+  /* 作品区 */
+  .worksSection {
+    margin-top: 30px;
+  }
+  
+  .sectionTitle {
+    margin: 0 0 20px 0;
+    color: var(--dark-color);
+    font-size: 18px;
+    padding-bottom: 10px;
+    border-bottom: 2px solid var(--primary-color);
+  }
+  
+  .workItem {
+    padding: 15px;
+    margin-bottom: 15px;
+    background-color: var(--primary-card);
+    border-radius: 8px;
+    border-left: 4px solid var(--primary-color);
+    box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
+    transition: transform 0.3s, box-shadow 0.3s;
+  }
+  
+  .workItem:hover {
+    transform: translateY(-3px);
+    box-shadow: 0 5px 15px rgba(0, 0, 0, 0.1);
+  }
+  
+  .workTitle {
+    margin: 0 0 10px 0;
+    color: var(--text-color);
+    font-size: 16px;
+  }
+  
+  .workMeta {
+    display: flex;
+    justify-content: space-between;
+    font-size: 14px;
+    color: var(--text-color);
+  }
+  
+  /* 右侧内容区 */
+  .rightContent {
+    flex: 1;
+    min-width: 300px;
+    display: flex;
+    flex-direction: column;
+    gap: 20px;
+  }
+  
+  .petSection, .trafficSection {
+    background-color: var(--card-bg);
+    border-radius: 10px;
+    padding: 20px;
+    box-shadow: 0 2px 5px rgba(0, 0, 0, 0.05);
+  }
+  
+  .petContainer {
+    background-color: var(--card-bg);
+    border-radius: 8px;
+    padding: 15px;
+    text-align: center;
+  }
+  
+  .petImage {
+    max-width: 100%;
+    height: auto;
+    border-radius: 5px;
+  }
+  
+  .trafficContainer {
+    background-color: white;
+    border-radius: 8px;
+    padding: 10px;
+    text-align: center;
+    border: 1px solid var(--border-color);
+  }
+  
+  .trafficImage {
+    max-width: 100%;
+    height: auto;
+  }
+  
+  /* 加载和错误状态 */
+  .loading, .error {
+    padding: 20px;
+    text-align: center;
+    color: var(--text-color);
+  }
+  
+  .error {
+    color: #e74c3c;
+  }
\ No newline at end of file
diff --git a/src/views/homepage/homepage.tsx b/src/views/homepage/homepage.tsx
new file mode 100644
index 0000000..58c910f
--- /dev/null
+++ b/src/views/homepage/homepage.tsx
@@ -0,0 +1,164 @@
+import React, { useCallback, useEffect } from 'react';
+import styles from './homepage.module.css';
+import { useApi } from '@/hooks/request';
+import { useSelector } from 'react-redux';
+import { RootState } from '@/store';
+import { useNavigate } from 'react-router';
+import logo from '&/assets/logo.png';
+import { getUserMessage } from '@/api/homepage';
+import request from '@/utils/request'
+import { hotPosts } from '@/api/post';
+import { postUserLogin } from '@/api/auth';
+
+interface WorkItem {  
+  postId: number,
+  userId: number,
+  postTitle: string,
+  postContent: string,
+  createdAt: number,
+  postType: string,
+  viewCount: number,
+  hotScore: number,
+  lastCalculated: number
+
+}
+
+interface UserStats {
+  likes: number;
+  following: number;
+  followers: number;
+  mutualFollows: number;
+}
+
+interface UserResponse {
+  username: string;
+  inviteCode: string;
+  stats: UserStats;
+  upload: string;
+  level: string;
+  works: WorkItem[];
+  petImage: string;
+  trafficImage: string;
+}
+
+
+const Homepage: React.FC =() => {
+  const navigate = useNavigate();
+  const userInfo = useSelector((state: RootState) => state.user);
+  
+  // 获取用户作品数据
+  // const {data: response, loading, error, refresh} =useApi(()=>request.get(getUserMessage), false);
+  const { data: response, loading, error, refresh } = useApi<UserResponse>(() => request.get(getUserMessage), false);
+  useEffect(() => {
+    refresh(); // 页面首次加载时触发请求
+  }, []);
+  // 用户统计数据
+  const userStats = {
+    likes: response?.stats?.likes ?? 0,
+    following: response?.stats?.following ?? 0,
+    followers: response?.stats?.followers ?? 0,
+    mutualFollows: response?.stats?.mutualFollows ?? 0,
+    uploadAmount: response?.upload ?? '--',
+    level: response?.level ?? '--'
+  };
+  
+  const handleLogoClick = () => {
+    navigate('/');
+  };
+
+  return (
+    <div className={styles.container}>
+
+
+      {/* 用户信息主区域 */}
+      <div className={styles.mainContent}>
+        {/* 左侧用户信息区 */}
+        <div className={styles.userProfile}>
+          <div className={styles.userHeader}>
+            <img 
+              src={userInfo.avatar || '/default-avatar.png'} 
+              alt="用户头像" 
+              className={styles.userAvatar}
+            />
+            <div className={styles.userInfo}>
+              <h2 className={styles.username}>阳菜,放睛!</h2>
+              <div className={styles.inviteCode}>邀请码:1314520</div>
+              <button className={styles.editButton}>编辑主页</button>
+               <button 
+                className={styles.editButton}
+                onClick={() => navigate('/postDetails', {
+                  state: { isNewPost: true }
+                })}
+              >
+                发布种子
+              </button>
+            </div>
+          </div>
+
+          <div className={styles.userStats}>
+            <div className={styles.statItem}>
+              <div className={styles.statNumber}>{userStats.likes}</div>
+              <div className={styles.statLabel}>获赞</div>
+            </div>
+            <div className={styles.statItem}>
+              <div className={styles.statNumber}>{userStats.following}</div>
+              <div className={styles.statLabel}>关注</div>
+            </div>
+            <div className={styles.statItem}>
+              <div className={styles.statNumber}>{userStats.followers}</div>
+              <div className={styles.statLabel}>粉丝</div>
+            </div>
+            <div className={styles.statItem}>
+              <div className={styles.statNumber}>{userStats.mutualFollows}</div>
+              <div className={styles.statLabel}>互关</div>
+            </div>
+          </div>
+
+          <div className={styles.userData}>
+            <div className={styles.dataItem}>
+              <span>您的总上传量为:</span>
+              <strong>{userStats.uploadAmount}</strong>
+            </div>
+            <div className={styles.dataItem}>
+              <span>您的用户等级为:</span>
+              <strong>{userStats.level}</strong>
+            </div>
+          </div>
+
+          <div className={styles.worksSection}>
+            <h3 className={styles.sectionTitle}>我的作品</h3>
+            {loading && <div className={styles.loading}>加载中...</div>}
+            {error && <div className={styles.error}>{error.message}</div>}
+            
+            {response && response.works.map(work => (
+              <div key={work.postId} className={styles.workItem}>
+                <h4 className={styles.workTitle}>{work.postTitle}</h4>
+                <div className={styles.workMeta}>
+                  <span>发布时间:{work.createdAt}</span>
+                  <span>下载量:{work.viewCount} 做种数:{'待定'}</span>
+                </div>
+              </div>
+            )) }
+          </div>
+        </div>
+
+        {/* 右侧内容区 */}
+        <div className={styles.rightContent}>
+          <div className={styles.petSection}>
+            <h3 className={styles.sectionTitle}>宠物图</h3>
+            <div className={styles.petContainer}>
+              <img 
+                src="/assets/pet-blue-star.png" 
+                alt="蓝色星星宠物" 
+                className={styles.petImage}
+              />
+            </div>
+          </div>
+
+        </div>
+      </div>
+    </div>
+  );
+};
+
+export default Homepage;
\ No newline at end of file
diff --git a/src/views/login/login.module.css b/src/views/login/login.module.css
index dca1df4..a7fc6f4 100644
--- a/src/views/login/login.module.css
+++ b/src/views/login/login.module.css
@@ -74,6 +74,18 @@
     max-width: 300px;
 }
 
+.sendCode {
+    padding:5px;
+    border-radius: 5px;
+    border: none;
+    font-size: 8px;
+    background-color: #ff7300; /* Blue */
+    color: white;
+    cursor: pointer;
+    width: 60px;
+    max-width: 300px;
+}
+
 .form .register:hover {
     background-color: #0056b3; /* Darker blue */
 }
@@ -90,4 +102,20 @@
     text-decoration: underline;
     align-content: flex-end;
     color: #007BFF; /* Blue */
+}
+
+.back {
+    background: none;
+    border: none;
+    text-decoration: underline;
+    cursor: pointer;
+    margin-top: 8px;
+    font-size: 16px;
+    padding: 0;
+    transition: color 0.2s;
+}
+
+.back:hover {
+    color: #0056b3;
+    text-decoration: underline;
 }
\ No newline at end of file
diff --git a/src/views/login/login.tsx b/src/views/login/login.tsx
index 562f178..fef6e32 100644
--- a/src/views/login/login.tsx
+++ b/src/views/login/login.tsx
@@ -1,37 +1,46 @@
-import React, { useEffect } from 'react';
+import React, { useState } from 'react';
 import { useApi } from '@/hooks/request';
 import request from '@/utils/request';
-import { postUserLogin} from '@/api/auth';
+import { postUserLogin } from '@/api/auth';
 import { useAppDispatch } from '@/hooks/store';
-import { RootState } from '@/store';
 import style from './login.module.css';
-import { useState } from 'react';
-import { useSelector } from 'react-redux';
 import { useNavigate } from 'react-router';
 import logo from '&/assets/logo.png';
 import { getUserInfo } from '@/api/user';
 import debounce from 'lodash/debounce';
-import {message} from 'antd';
-
+import { message } from 'antd';
 
 const Login: React.FC = () => {
     const [email, setEmail] = useState('');
     const [password, setPassword] = useState('');
-    const dispatch = useAppDispatch();
-    const { refresh: postUserLoginRefresh } = useApi(() => request.post(postUserLogin, {email, password}), false);
-    const { refresh: getUserInfoRefresh } = useApi(() => request.get(getUserInfo), false);
-    const [messageApi, contextHolder] = message.useMessage();
+    const [showRegister, setShowRegister] = useState(false);
+    const [inviteCode, setInviteCode] = useState('');
+    const [registerEmail, setRegisterEmail] = useState('');
+    const [registerPassword, setRegisterPassword] = useState('');
+    const [emailCode, setEmailCode] = useState('');
+    const [codeBtnDisabled, setCodeBtnDisabled] = useState(false);
+    const [codeBtnText, setCodeBtnText] = useState('发送验证码');
+    const [codeTimer, setCodeTimer] = useState<NodeJS.Timeout | null>(null);
 
+    const dispatch = useAppDispatch();
+    const [messageApi, contextHolder] = message.useMessage();
+    const { refresh: postUserLoginRefresh } = useApi(
+        () => request.post(postUserLogin, { email, password}), false);
+    const { refresh: getUserInfoRefresh } = useApi(
+        () => request.get(getUserInfo), false);
     const nav = useNavigate();
-    const showErrorMessage = async (message: string) => {
-        messageApi.error(message);
+    const showErrorMessage = async (msg: string) => {
+        messageApi.error(msg);
     };
+
+    // 登录逻辑
     const handleLogin = debounce(async () => {
         try {
-            const res = await postUserLoginRefresh();
-            console.log(res);
-            if (res == null || (res as any).error) {
-                throw new Error('登录失败');
+            const res =await postUserLoginRefresh({email, password});
+            console.log("res", res);
+            if (res==null ||(res as any).error) {
+                alert('Login failed. Please check your credentials.');
+                return;
             }
             dispatch({ type: "user/login", payload: res });
 
@@ -42,23 +51,133 @@
             dispatch({ type: "user/getUserInfo", payload: userInfo });
             nav('/');
         } catch (error) {
-            // 将错误信息传递给一个异步函数
             showErrorMessage('登录失败,请检查您的用户名和密码');
         }
     }, 1000) as () => void;
 
+    // 发送验证码逻辑
+    const handleSendCode = async () => {
+        if (!registerEmail) {
+            showErrorMessage('请填写邮箱');
+            return;
+        }
+        setCodeBtnDisabled(true);
+        let seconds = 60;
+        setCodeBtnText(`已发送(${seconds}s)`);
+        const timer = setInterval(() => {
+            seconds -= 1;
+            setCodeBtnText(`已发送(${seconds}s)`);
+            if (seconds <= 0) {
+                clearInterval(timer);
+                setCodeBtnDisabled(false);
+                setCodeBtnText('发送验证码');
+            }
+        }, 1000);
+        setCodeTimer(timer);
+        // TODO: 调用发送验证码接口
+        message.success('验证码已发送');
+    };
+
+    // 切换回登录
+    const handleBackToLogin = () => {
+        setShowRegister(false);
+        setInviteCode('');
+        setRegisterEmail('');
+        setRegisterPassword('');
+        setEmailCode('');
+        if (codeTimer) clearInterval(codeTimer);
+        setCodeBtnDisabled(false);
+        setCodeBtnText('发送验证码');
+    };
+
+    // 注册逻辑(仅前端校验,实际应调用注册接口)
+    const handleRegister = async () => {
+        if (!inviteCode || !registerEmail || !registerPassword || !emailCode) {
+            showErrorMessage('请填写完整信息');
+            return;
+        }
+        // TODO: 调用注册接口
+        message.success('注册成功,请登录');
+        handleBackToLogin();
+    };
+
     const handleLogoClick = () => {
         nav('/');
-    }
+    };
+
     return (
         <div className={style.form}>
             {contextHolder}
-            <img className={style.logo} src={logo} alt="logo" onClick={handleLogoClick}></img>
-            <input type="email" value={email} onChange={(e) => setEmail(e.target.value)} className={style.email} placeholder="Enter your email" />
-            <input type="password" value={password} onChange={(e) => setPassword(e.target.value)} className={style.password} placeholder="Enter your password" />
-            <button className={style.submit} onClick={() => handleLogin()}>登录</button>
-            <button className={style.register}>注册</button>
-            <button className={style.forget}> 忘记密码</button>
+            <img className={style.logo} src={logo} alt="logo" onClick={handleLogoClick} />
+            {!showRegister ? (
+                <>
+                    <input
+                        type="email"
+                        value={email}
+                        onChange={(e) => setEmail(e.target.value)}
+                        className={style.email}
+                        placeholder="Enter your email"
+                    />
+                    <input
+                        type="password"
+                        value={password}
+                        onChange={(e) => setPassword(e.target.value)}
+                        className={style.password}
+                        placeholder="Enter your password"
+                    />
+                    <button className={style.submit} onClick={() => handleLogin()}>登录</button>
+                    <button className={style.register} onClick={() => setShowRegister(true)}>注册</button>
+                    <button className={style.forget}>忘记密码</button>
+                </>
+            ) : (
+                <>
+                    <input
+                        type="text"
+                        value={inviteCode}
+                        onChange={(e) => setInviteCode(e.target.value)}
+                        className={style.invite}
+                        placeholder="邀请码"
+                    />
+                    <input
+                        type="email"
+                        value={registerEmail}
+                        onChange={(e) => setRegisterEmail(e.target.value)}
+                        className={style.email}
+                        placeholder="邮箱"
+                    />
+                    <input
+                        type="password"
+                        value={registerPassword}
+                        onChange={(e) => setRegisterPassword(e.target.value)}
+                        className={style.password}
+                        placeholder="密码"
+                    />
+                    <div style={{ display: 'flex',width:'80%', alignItems: 'center', gap: 8, padding:'10px' }}>
+                        <input
+                            type="text"
+                            value={emailCode}
+                            onChange={(e) => setEmailCode(e.target.value)}
+                            className={style.code}
+                            placeholder="邮箱验证码"
+                            style={{ flex: 1 }}
+                        />
+                        <button
+                            className={style.sendCode}
+                            onClick={handleSendCode}
+                            disabled={codeBtnDisabled}
+                            style={{
+                                background: codeBtnDisabled ? '#ccc' : undefined,
+                                color: codeBtnDisabled ? '#888' : undefined,
+                                cursor: codeBtnDisabled ? 'not-allowed' : 'pointer'
+                            }}
+                        >
+                            {codeBtnText}
+                        </button>
+                    </div>
+                    <button className={style.submit} onClick={handleRegister}>注册</button>
+                    <button className={style.back} onClick={handleBackToLogin}>返回登录</button>
+                </>
+            )}
         </div>
     );
 };
diff --git a/src/views/pet/pet.module.css b/src/views/pet/pet.module.css
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/src/views/pet/pet.module.css
diff --git a/src/views/pet/pet.tsx b/src/views/pet/pet.tsx
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/src/views/pet/pet.tsx
diff --git a/src/views/postDetail/postDetail.module.css b/src/views/postDetail/postDetail.module.css
index e69de29..46c2176 100644
--- a/src/views/postDetail/postDetail.module.css
+++ b/src/views/postDetail/postDetail.module.css
@@ -0,0 +1,89 @@
+.commentList .ant-list-item {
+    min-height: 300px;
+    height: 300px;
+    box-sizing: border-box;
+    display: flex;
+    align-items: flex-start;
+    /* 可选:让内容垂直居中可用 align-items: center; */
+}
+.contentArea {
+  width: 100%;
+  max-width: 900px;
+  margin: 32px auto 0 auto;
+  padding: 0 16px 32px 16px;
+  display: flex;
+  flex-direction: column;
+  gap: 24px;
+}
+
+.card {
+  border-radius: 10px;
+  background: var(--card-bg);
+}
+
+.metaRow {
+  display: flex;
+  flex-wrap: wrap;
+  gap: 16px;
+  margin-bottom: 8px;
+}
+
+.locked {
+  margin: 12px 0;
+  color: #d4380d;
+  font-weight: bold;
+}
+
+.contentText {
+  font-size: 17px;
+  color: var(--text-color);
+  line-height: 1.8;
+  margin-top: 12px;
+}
+
+.addCommentCard {
+  border-radius: 10px;
+  background: var(--card-bg);
+}
+
+.textarea {
+  font-size: 15px;
+}
+
+.commentListCard {
+  border-radius: 10px;
+  background: var(--card-bg);
+}
+
+.commentList .ant-list-item,
+.replyList .ant-list-item {
+  min-height: 120px;
+  height: auto;
+  box-sizing: border-box;
+  display: flex;
+  align-items: flex-start;
+  border-bottom: 1px solid var(--border-color);
+  padding: 24px 16px;
+}
+
+.replyList {
+  margin-left: 32px;
+  background: transparent;
+}
+
+.replyItem {
+  background: #f6f8fa;
+  border-radius: 6px;
+  margin-bottom: 8px;
+  padding: 12px 16px;
+}
+
+@media (max-width: 600px) {
+  .contentArea {
+    max-width: 100%;
+    padding: 0 4px 24px 4px;
+  }
+  .card, .addCommentCard, .commentListCard {
+    padding: 0;
+  }
+}
\ No newline at end of file
diff --git a/src/views/postDetail/postDetail.tsx b/src/views/postDetail/postDetail.tsx
index a40fb68..d6029a8 100644
--- a/src/views/postDetail/postDetail.tsx
+++ b/src/views/postDetail/postDetail.tsx
@@ -1,114 +1,206 @@
 import React, { useEffect, useState } from 'react';
-import { useParams } from 'react-router-dom';
 import styles from './PostDetail.module.css';
-import { Card, List, Typography, Button, Input, Spin, Empty } from 'antd';
-type CommentProps = {
-	children?: React.ReactNode;
-};
+import { Card, List, Typography, Button, Input, Spin, Empty, Divider } from 'antd';
 import { getPostDetail } from '@/api/post';
 import { getPostComments } from '@/api/comment';
 import { useSearchParams } from 'react-router-dom';
 import request from '@/utils/request';
 import { useApi } from '@/hooks/request';
 import Navbar from '@/components/navbar/navbar';
+import { DownloadOutlined, LikeOutlined, LikeFilled } from '@ant-design/icons';
 
 const { Title, Text, Paragraph } = Typography;
 const { TextArea } = Input;
 
 export interface PostResponse {
-	createdAt?: number;
-	hotScore?: number;
-	lastCalculated?: number;
-	postContent?: string;
-	postId?: number;
-	postTitle?: string;
-	postType?: string;
-	userId?: number;
-	viewCount?: number;
-	[property: string]: any;
+  postId?: number;
+  userId?: number;
+  postTitle?: string;
+  postContent?: string;
+  createdAt?: number;
+  postType?: string;
+  isLocked?: boolean;
+  lockedReason?: string;
+  lockedAt?: string;
+  lockedBy?: number;
+  viewCount?: number;
+  hotScore?: number;
+  lastCalculated?: number;
+  [property: string]: any;
 }
 
 export interface CommentResponse {
-	commentId?: number;
-	content?: string;
-	createdAt?: number;
-	parentCommentId?: number | null;
-	postId?: number;
-	replies?: CommentResponse[];
-	userId?: number;
-	[property: string]: any;
+  commentId?: number;
+  content?: string;
+  createdAt?: number;
+  parentCommentId?: number | null;
+  postId?: number;
+  replies?: CommentResponse[];
+  userId?: number;
+  [property: string]: any;
 }
 
 const PostDetail: React.FC = () => {
-	const [searchParams] = useSearchParams();
-	const postId = searchParams.get('postId');
-	const { refresh: getPostDetailRefresh } = useApi(() => request.get(getPostDetail + `/${postId}`), false);
-	const { refresh: getPostCommentsRefresh } = useApi(() => request.get(getPostComments + `/${postId}`), false);
-	const [post, setPost] = useState<PostResponse | null>(null);
-	const [comments, setComments] = useState<CommentResponse[]>([]);
-	const [newComment, setNewComment] = useState<string>('');
-	const [loading, setLoading] = useState<boolean>(true);
+  const [searchParams] = useSearchParams();
+  const postId = searchParams.get('postId');
+  const { refresh: getPostDetailRefresh } = useApi(() => request.get(getPostDetail + `/${postId}`), false);
+  const { refresh: getPostCommentsRefresh } = useApi(() => request.get(getPostComments + `/${postId}`), false);
+  const [post, setPost] = useState<PostResponse | null>(null);
+  const [comments, setComments] = useState<CommentResponse[]>([]);
+  const [newComment, setNewComment] = useState<string>('');
+  const [loading, setLoading] = useState<boolean>(true);
+  const [liked, setLiked] = useState(false);
 
-	useEffect(() => {
-		console.log('postId', postId);
-		if (!postId) return;
-		const fetchData = async () => {
-			setLoading(true);
-			const res = await getPostDetailRefresh();
-			if (res == null || (res as any).error) {
-				setLoading(false);
-				return;
-			}
-			setPost(res as PostResponse);
-			await getPostCommentsRefresh();
-			setComments(res as CommentResponse[]);
-			setLoading(false);
-		};
-		fetchData();
-	}, [postId]);
+  useEffect(() => {
+    if (!postId) return;
+    const fetchData = async () => {
+      setLoading(true);
+      const postRes = await getPostDetailRefresh();
+      if (!postRes || (postRes as any).error) {
+        setLoading(false);
+        return;
+      }
+      setPost(postRes as PostResponse);
 
-	if (loading) return <div className={styles.center}><Spin /></div>;
-	if (!post) return <div className={styles.center}><Empty description="未找到帖子" /></div>;
+      const commentsRes = await getPostCommentsRefresh();
+      setComments(commentsRes as CommentResponse[]);
+      setLoading(false);
+    };
+    fetchData();
+  }, [postId]);
 
-	return (
-		<div className={styles.container}>
-			<div className={styles.nav}>
-				<Navbar current={post.postType} />
-			</div>
-			<div className={styles.content}>
-				<div className={styles.postDetail}>
-						
-				</div >
-				<Card title={post.postTitle} className={styles.card}>
-					<Paragraph>{post.postContent}</Paragraph>
-					<div className={styles.actions}>
-						<Button type="primary" onClick={() => setNewComment('')}>评论</Button>
-					</div>
-				</Card>
+  if (loading) return <div className={styles.center}><Spin /></div>;
+  if (!post) return <div className={styles.center}><Empty description="未找到帖子" /></div>;
 
-				<List
-					className={styles.commentList}
-					header={<Title level={4}>评论区</Title>}
-					dataSource={comments}
-					renderItem={(item) => (
-						<List.Item key={item.commentId}>
-							<List.Item.Meta
-								title={<Text strong>{item.userId}</Text>}
-								description={<Text>{item.content}</Text>}
-							/>
-						</List.Item>
-					)}
-				/>
+  return (
+    <div className={styles.container}>
+      {/* 固定导航栏 */}
+      <div className={styles.nav}>
+        <Navbar current={post.postType} />
+      </div>
+      {/* 内容区域 */}
+      <div className={styles.contentArea}>
+        <Card
+          title={<Title level={3} style={{ margin: 0 }}>{post.postTitle || "帖子标题"}</Title>}
+          className={styles.card}
+          bordered={false}
+          style={{ marginBottom: 24, boxShadow: '0 4px 24px rgba(0,0,0,0.08)' }}
+          extra={
+            <div style={{ display: 'flex', gap: 16 }}>
+              <Button
+                type="primary"
+                icon={<DownloadOutlined />}
+                onClick={() => {
+                  // 下载逻辑
+                  window.open(`/api/download/post/${post.postId}`, '_blank');
+                }}
+              >
+                下载
+              </Button>
+              <Button
+                type="primary"
+                icon={liked ? <LikeFilled /> : <LikeOutlined />}
+                style={liked ? { background: '#ccc', borderColor: '#ccc', color: '#888', cursor: 'not-allowed' } : {}}
+                disabled={liked}
+                onClick={() => setLiked(true)}
+              >
+                {liked ? '已点赞' : '点赞'}
+              </Button>
+            </div>
+          }
+        >
+          <div className={styles.metaRow}>
+            <Text type="secondary">作者ID: {post.userId}</Text>
+            <Text type="secondary">发布时间: {post.createdAt ? new Date(post.createdAt).toLocaleString() : "未知"}</Text>
+            <Text type="secondary">浏览量: {post.viewCount}</Text>
+            <Text type="secondary">类型: {post.postType}</Text>
+            <Text type="secondary">热度: {post.hotScore}</Text>
+            <Text type="secondary">最后计算: {post.lastCalculated ? new Date(post.lastCalculated).toLocaleString() : "无"}</Text>
+          </div>
+          {post.isLocked && (
+            <div className={styles.locked}>
+              <Text type="danger">本帖已锁定</Text>
+              {post.lockedReason && <Text type="secondary">(原因:{post.lockedReason})</Text>}
+              {post.lockedAt && <Text style={{ marginLeft: 8 }}>锁定时间: {post.lockedAt}</Text>}
+              {post.lockedBy !== 0 && <Text style={{ marginLeft: 8 }}>锁定人ID: {post.lockedBy}</Text>}
+            </div>
+          )}
+          <Divider style={{ margin: '16px 0' }} />
+          <Paragraph className={styles.contentText}>{post.postContent || "暂无内容"}</Paragraph>
+        </Card>
 
-				<TextArea
-					rows={4}
-					value={newComment}
-					onChange={(e) => setNewComment(e.target.value)}
-					placeholder="写下你的评论..."
-				/>
-				</div>
-		</div>
-	);
+        {/* 发布评论区域 */}
+        <Card className={styles.addCommentCard} style={{ marginBottom: 32, boxShadow: '0 2px 12px rgba(0,0,0,0.06)' }}>
+          <Title level={5} style={{ marginBottom: 12 }}>发布评论</Title>
+          <TextArea
+            rows={4}
+            value={newComment}
+            onChange={(e) => setNewComment(e.target.value)}
+            placeholder="写下你的评论..."
+            className={styles.textarea}
+          />
+          <Button
+            type="primary"
+            style={{ marginTop: 12, float: 'right' }}
+            onClick={() => setNewComment('')}
+            disabled={!newComment.trim()}
+          >
+            评论
+          </Button>
+          <div style={{ clear: 'both' }} />
+        </Card>
+
+        <Card
+          className={styles.commentListCard}
+          title={<Title level={4} style={{ margin: 0 }}>评论区</Title>}
+          bodyStyle={{ padding: 0 }}
+        >
+          <List
+            className={styles.commentList}
+            dataSource={comments}
+            locale={{ emptyText: <Empty description="暂无评论" /> }}
+            renderItem={(item) => (
+              <List.Item className={styles.commentItem} key={item.commentId}>
+                <List.Item.Meta
+                  title={<Text strong>用户ID: {item.userId}</Text>}
+                  description={
+                    <>
+                      <Text>{item.content}</Text>
+                      <div style={{ fontSize: 12, color: '#888', marginTop: 8 }}>
+                        {item.createdAt && new Date(item.createdAt).toLocaleString()}
+                      </div>
+                    </>
+                  }
+                />
+                {/* 可递归渲染子评论 */}
+                {item.replies && item.replies.length > 0 && (
+                  <List
+                    className={styles.replyList}
+                    dataSource={item.replies}
+                    renderItem={reply => (
+                      <List.Item className={styles.replyItem} key={reply.commentId}>
+                        <List.Item.Meta
+                          title={<Text strong>用户ID: {reply.userId}</Text>}
+                          description={
+                            <>
+                              <Text>{reply.content}</Text>
+                              <div style={{ fontSize: 12, color: '#888', marginTop: 8 }}>
+                                {reply.createdAt && new Date(reply.createdAt).toLocaleString()}
+                              </div>
+                            </>
+                          }
+                        />
+                      </List.Item>
+                    )}
+                  />
+                )}
+              </List.Item>
+            )}
+          />
+        </Card>
+      </div>
+    </div>
+  );
 };
 
 export default PostDetail;
\ No newline at end of file
diff --git a/src/views/search/search.module.css b/src/views/search/search.module.css
new file mode 100644
index 0000000..2e1f40a
--- /dev/null
+++ b/src/views/search/search.module.css
@@ -0,0 +1,132 @@
+:root {
+  --primary-color: #3498db;
+  --primary-hover: #2980b9;
+  --secondary-color: #f1c40f;
+  --dark-color: #2c3e50;
+  --light-color: #ecf0f1;
+  --text-color: #333;
+  --text-secondary: #7f8c8d;
+  --border-color: #ddd;
+  --bg-color: #2b2b2b;
+  --card-bg: #1e1e1e;
+}
+
+.container {
+  min-height: 100vh;
+  background-color: var(--bg-color);
+  color: var(--text-color);
+  font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
+  padding: 20px;
+}
+
+.secondaryHeader {
+  background-color: var(--card-bg);
+  padding: 15px 20px;
+  margin-bottom: 20px;
+  border-radius: 8px;
+  box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
+  display: flex;
+  flex-wrap: wrap; /* 可换成 nowrap + overflow-x: auto 实现强制一行 + 横向滚动 */
+  gap: 15px;
+  align-items: center;
+  justify-content: flex-start;
+}
+
+.selectBox {
+  background-color: var(--light-color);
+  color: var(--text-color);
+  border: 1px solid var(--border-color);
+  border-radius: 5px;
+  padding: 8px 10px;
+  font-size: 14px;
+}
+
+.selectBox:focus {
+  outline: none;
+  border-color: var(--primary-color);
+}
+
+.tagFilters {
+  display: flex;
+  flex-wrap: wrap;
+  align-items: center;
+  gap: 10px;
+  background-color: var(--light-color);
+  color: var(--text-color);
+  border: 1px solid var(--border-color);
+  border-radius: 5px;
+  padding: 8px 10px;
+  font-size: 14px;
+}
+
+.tagFilters label {
+  margin: 0;
+  color: var(--text-color);
+}
+
+.filterButton {
+  padding: 8px 16px;
+  background-color: var(--primary-color);
+  color: white;
+  border: none;
+  border-radius: 5px;
+  font-size: 14px;
+  cursor: pointer;
+  transition: background-color 0.3s;
+}
+
+.filterButton:hover {
+  background-color: var(--primary-hover);
+}
+
+.results {
+  display: flex;
+  flex-direction: column;
+  gap: 15px;
+}
+
+.postItem {
+  background-color: var(--card-bg);
+  padding: 20px;
+  border-radius: 8px;
+  border-left: 4px solid var(--primary-color);
+  box-shadow: 0 1px 4px rgba(0, 0, 0, 0.15);
+}
+
+.postItem h3 {
+  margin: 0 0 10px 0;
+  color: var(--text-color);
+}
+
+.postItem p {
+  margin: 4px 0;
+  color: var(--text-secondary);
+  font-size: 14px;
+}
+
+.secondaryHeader {
+  background-color: var(--card-bg);
+  padding: 15px 20px;
+  margin-bottom: 20px;
+  border-radius: 8px;
+  box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+}
+
+.leftSection {
+  display: flex;
+  gap: 10px;
+}
+
+.centerSection {
+  flex: 1;
+  display: flex;
+  justify-content: center;
+}
+
+.rightSection {
+  display: flex;
+  justify-content: flex-end;
+}
diff --git a/src/views/search/search.tsx b/src/views/search/search.tsx
new file mode 100644
index 0000000..cd9db91
--- /dev/null
+++ b/src/views/search/search.tsx
@@ -0,0 +1,131 @@
+// search.tsx
+import React, { useState, useEffect } from 'react';
+import styles from './search.module.css';
+import { useLocation } from "react-router-dom";
+
+interface PostItem {
+  postId: number;
+  userId: number;
+  postTitle: string;
+  postContent: string;
+  createdAt: number;
+  postType: string;
+  viewCount: number;
+  hotScore: number;
+  lastCalculated: number;
+  tags?: number[];
+}
+
+const tagMap: Record<number, string> = {
+  1: "搞笑",
+  2: "悬疑",
+  3: "教育",
+  4: "动作",
+  5: "剧情"
+};
+
+const SearchPage: React.FC = () => {
+  const [posts, setPosts] = useState<PostItem[]>([]);
+  const [filteredPosts, setFilteredPosts] = useState<PostItem[]>([]);
+  const [selectedPostType, setSelectedPostType] = useState<string>('all');
+  const [selectedRating, setSelectedRating] = useState<number | null>(null);
+  const [selectedTags, setSelectedTags] = useState<number[]>([]);
+
+  const location = useLocation();
+  const params = new URLSearchParams(location.search);
+  const keyword = params.get("keyword");
+
+  useEffect(() => {
+    fetch('/api/posts')
+      .then((res) => res.json())
+      .then((data) => {
+        setPosts(data);
+        setFilteredPosts(data);
+      });
+  }, []);
+
+  const applyFilters = () => {
+    let filtered = posts;
+
+    if (selectedPostType !== 'all') {
+      filtered = filtered.filter((post) => post.postType === selectedPostType);
+    }
+
+    if (selectedRating !== null) {
+      filtered = filtered.filter((post) => post.hotScore >= selectedRating);
+    }
+
+    if (selectedTags.length > 0) {
+      filtered = filtered.filter((post) =>
+        post.tags?.some((tag) => selectedTags.includes(tag))
+      );
+    }
+
+    setFilteredPosts(filtered);
+  };
+
+  return (
+    <div className={styles.secondaryHeader}>
+    <div className={styles.leftSection}>
+        <select
+        value={selectedPostType}
+        onChange={(e) => setSelectedPostType(e.target.value)}
+        className={styles.selectBox}
+        >
+        <option value="all">所有分区</option>
+        <option value="影视">影视</option>
+        <option value="音乐">音乐</option>
+        <option value="游戏">游戏</option>
+        <option value="软件">软件</option>
+        </select>
+
+        <select
+        value={selectedRating || ''}
+        onChange={(e) =>
+            setSelectedRating(e.target.value ? Number(e.target.value) : null)
+        }
+        className={styles.selectBox}
+        >
+        <option value="">所有评分</option>
+        <option value="1">1星及以上</option>
+        <option value="2">2星及以上</option>
+        <option value="3">3星及以上</option>
+        <option value="4">4星及以上</option>
+        <option value="5">5星</option>
+        </select>
+    </div>
+
+    <div className={styles.centerSection}>
+        <div className={styles.tagFilters}>
+        {Object.entries(tagMap).map(([tagId, tagName]) => (
+            <label key={tagId}>
+            <input
+                type="checkbox"
+                value={tagId}
+                checked={selectedTags.includes(Number(tagId))}
+                onChange={(e) => {
+                const value = Number(e.target.value);
+                setSelectedTags((prev) =>
+                    prev.includes(value)
+                    ? prev.filter((t) => t !== value)
+                    : [...prev, value]
+                );
+                }}
+            />
+            {tagName}
+            </label>
+        ))}
+        </div>
+    </div>
+
+    <div className={styles.rightSection}>
+        <button className={styles.filterButton} onClick={applyFilters}>
+        筛选
+        </button>
+    </div>
+    </div>
+
+  );
+};
+
+export default SearchPage;
diff --git a/src/views/upload/upload.module.css b/src/views/upload/upload.module.css
new file mode 100644
index 0000000..5a9952a
--- /dev/null
+++ b/src/views/upload/upload.module.css
@@ -0,0 +1,150 @@
+.container {
+  background-color: var(--card-bg);
+  padding: 32px;
+  border-radius: 12px;
+  width: 100%;
+  height: 100%;
+  margin: auto;
+  border: 1px solid var(--border-color);
+  color: var(--text-color);
+}
+
+.formGroup {
+  margin-bottom: 20px;
+}
+
+.input,
+.select,
+.textarea {
+  width: 100%;
+  padding: 8px 12px;
+  margin-top: 4px;
+  border: 1px solid var(--border-color);
+  border-radius: 8px;
+  background-color: var(--bg-color);
+  color: var(--text-color);
+}
+
+.upload {
+  margin-top: 8px;
+}
+
+.textarea {
+  height: 100px;
+  resize: none;
+}
+
+.charCount {
+  text-align: right;
+  font-size: 12px;
+  color: var(--text-color);
+}
+
+.requirement {
+  font-size: 14px;
+  color: var(--primary-color);
+  margin-bottom: 12px;
+}
+
+.checkbox {
+  display: flex;
+  align-items: center;
+  gap: 8px;
+  margin-bottom: 20px;
+}
+
+.submitBtn {
+  background-color: var(--primary-color);
+  color: white;
+  border: none;
+  padding: 10px 20px;
+  border-radius: 8px;
+  cursor: pointer;
+  transition: background-color 0.3s ease;
+}
+
+.submitBtn:hover {
+  background-color: var(--primary-hover);
+}
+
+
+.wrapper {
+  display: flex;
+  justify-content: center;
+  margin-top: 50px;
+}
+
+.form {
+  width: 400px;
+  background: #ffffff;
+  border: 1px solid #ddd;
+  border-radius: 12px;
+  padding: 24px;
+  box-shadow: 0 4px 12px rgba(0,0,0,0.1);
+}
+
+.title {
+  font-size: 22px;
+  margin-bottom: 20px;
+  text-align: center;
+}
+
+.input,
+.textarea {
+  width: 100%;
+  padding: 10px;
+  margin-bottom: 16px;
+  border-radius: 6px;
+  border: 1px solid #ccc;
+  font-size: 14px;
+}
+
+.textarea {
+  resize: vertical;
+  height: 100px;
+}
+
+.uploadArea {
+  padding: 12px;
+  border: 2px dashed #999;
+  border-radius: 8px;
+  text-align: center;
+  cursor: pointer;
+  background: #f9f9f9;
+  margin-bottom: 16px;
+  transition: all 0.2s;
+}
+
+.uploadArea:hover {
+  background: #f0f0f0;
+  border-color: #666;
+}
+
+.fileName {
+  margin-top: 8px;
+  font-size: 14px;
+  color: #333;
+}
+
+.error {
+  color: red;
+  margin-bottom: 10px;
+  font-size: 14px;
+  text-align: center;
+}
+
+.uploadButton {
+  width: 100%;
+  padding: 10px;
+  background-color: #409eff;
+  color: white;
+  border: none;
+  border-radius: 6px;
+  font-size: 16px;
+  cursor: pointer;
+  transition: background-color 0.3s;
+}
+
+.uploadButton:hover {
+  background-color: #317ee7;
+}
diff --git a/src/views/upload/upload.tsx b/src/views/upload/upload.tsx
new file mode 100644
index 0000000..4a60330
--- /dev/null
+++ b/src/views/upload/upload.tsx
@@ -0,0 +1,123 @@
+import instance from '@/utils/axios';
+import React, { useState } from 'react';
+import styles from './upload.module.css';
+import { Upload } from '@/api/upload';
+import { useNavigate } from 'react-router-dom'; // 用于跳转
+
+const PostDetails = () => {
+  const [postTitle, setPostTitle] = useState('');
+  const [postType, setPostType] = useState('');
+  const [postContent, setPostContent] = useState('');
+  const [isChecked, setIsChecked] = useState(false);
+
+  const navigate = useNavigate();
+
+  const handleSubmit = async () => {
+    if (!postTitle.trim() || !postType || !postContent.trim()) {
+      alert('请填写完整内容(资源名、类型、内容介绍)');
+      return;
+    }
+
+    if (!isChecked) {
+      alert('请先确认您已知晓以上内容');
+      return;
+    }
+
+    const payload = {
+      post: {
+        postId: 0,
+        userId: 0,
+        postTitle,
+        postContent,
+        createdAt: Date.now(),
+        postType,
+        viewCount: 0,
+        hotScore: 5,
+        lastCalculated: Date.now()
+      },
+      tagIds: [0]
+    };
+
+    try {
+      const res = await instance.post(Upload, payload);
+
+      console.log('mock返回内容:', res.code);
+
+      // 判断返回内容是否成功(根据你 mock 接口返回的 code 字段)
+      if (res.code !== 0) throw new Error('发布失败');
+
+      alert('发布成功!');
+      navigate(-1); // 返回上一页(homepage)
+    } catch (error) {
+      alert('发布失败,请稍后重试');
+      console.error(error);
+    }
+  };
+
+  return (
+    <div className={styles.container}>
+      <div className={styles.formGroup}>
+        <label>资源名:</label>
+        <input
+          type="text"
+          value={postTitle}
+          placeholder="请输入文本"
+          onChange={(e) => setPostTitle(e.target.value)}
+          className={styles.input}
+        />
+      </div>
+
+      <div className={styles.formGroup}>
+        <label>类型选择:</label>
+        <select
+          value={postType}
+          onChange={(e) => setPostType(e.target.value)}
+          className={styles.select}
+        >
+          <option value="">下拉选择</option>
+          <option value="type1">类型一</option>
+          <option value="type2">类型二</option>
+        </select>
+      </div>
+
+      {/* 暂时移除上传文件表单 */}
+      {/* <div className={styles.formGroup}>
+        <label>上传资源:</label>
+        <input
+          type="file"
+          onChange={(e) => setFile(e.target.files?.[0] || null)}
+          className={styles.upload}
+        />
+      </div> */}
+
+      <div className={styles.formGroup}>
+        <label>内容介绍:</label>
+        <textarea
+          placeholder="请输入内容介绍"
+          value={postContent}
+          onChange={(e) => setPostContent(e.target.value)}
+          maxLength={200}
+          className={styles.textarea}
+        />
+        <div className={styles.charCount}>{postContent.length}/200</div>
+      </div>
+
+      <div className={styles.requirement}>【发布内容要求】</div>
+
+      <div className={styles.checkbox}>
+        <input
+          type="checkbox"
+          checked={isChecked}
+          onChange={() => setIsChecked(!isChecked)}
+        />
+        <span>我已知晓以上内容</span>
+      </div>
+
+      <button onClick={handleSubmit} className={styles.submitBtn}>
+        我已知晓
+      </button>
+    </div>
+  );
+};
+
+export default PostDetails;