前端页面提交

Change-Id: I096c8538ded26791a94a46d8f27b0307dbfb8300
diff --git a/front/src/AnimePage.js b/front/src/AnimePage.js
new file mode 100644
index 0000000..fd429b7
--- /dev/null
+++ b/front/src/AnimePage.js
@@ -0,0 +1,137 @@
+import React from "react";

+import MovieIcon from "@mui/icons-material/Movie";

+import EmailIcon from "@mui/icons-material/Email";

+import MusicNoteIcon from "@mui/icons-material/MusicNote";

+import EmojiPeopleIcon from "@mui/icons-material/EmojiPeople";

+import SportsEsportsIcon from "@mui/icons-material/SportsEsports";

+import SportsMartialArtsIcon from "@mui/icons-material/SportsMartialArts";

+import PersonIcon from "@mui/icons-material/Person";

+import AccountCircleIcon from "@mui/icons-material/AccountCircle";

+import "./App.css";

+import { useNavigate } from "react-router-dom";

+

+const navItems = [

+  { label: "电影", icon: <MovieIcon />, path: "/movie" },

+  { label: "剧集", icon: <EmailIcon />, path: "/tv" },

+  { label: "音乐", icon: <MusicNoteIcon />, path: "/music" },

+  { label: "动漫", icon: <EmojiPeopleIcon />, path: "/anime" },

+  { label: "游戏", icon: <SportsEsportsIcon />, path: "/game" },

+  { label: "体育", icon: <SportsMartialArtsIcon />, path: "/sport" },

+  { label: "资料", icon: <PersonIcon />, path: "/info" },

+];

+

+const animeTypes = [

+  "华语动漫(大陆)",

+  "欧美动漫",

+  "日韩动漫",

+  "港台动漫",

+  "其他"

+];

+

+const areaTabs = [

+  { label: "大陆", icon: <EmojiPeopleIcon fontSize="small" /> },

+  { label: "港台", icon: <EmailIcon fontSize="small" /> },

+  { label: "欧美", icon: <PersonIcon fontSize="small" /> },

+  { label: "日韩", icon: <EmojiPeopleIcon fontSize="small" /> },

+  { label: "其他", icon: <PersonIcon fontSize="small" /> },

+];

+

+export default function AnimePage() {

+  const navigate = useNavigate();

+  const [activeTab, setActiveTab] = React.useState(0);

+

+  // 每个tab对应的动漫类型

+  const animeTypesList = [

+    ["华语动漫(大陆)", "欧美动漫", "日韩动漫", "港台动漫", "其他"], // 大陆

+    ["港台热血", "港台搞笑", "港台其他"], // 港台

+    ["欧美冒险", "欧美科幻", "欧美其他"], // 欧美

+    ["日韩热血", "日韩恋爱", "日韩其他"], // 日韩

+    ["其他类型1", "其他类型2"] // 其他

+  ];

+  const animeTypes = animeTypesList[activeTab] || [];

+

+  return (

+    <div className="container">

+      {/* 顶部空白与音乐界面一致,用户栏绝对定位在页面右上角 */}

+      <div style={{ height: 80 }} />

+      <div className="user-bar" style={{ position: 'fixed', top: 18, right: 42, zIndex: 100, display: 'flex', alignItems: 'center', background: '#e0f3ff', borderRadius: 12, padding: '6px 18px', boxShadow: '0 2px 8px #b2d8ea', minWidth: 320, minHeight: 48, width: 420 }}>

+        <div style={{ cursor: 'pointer', marginRight: 16 }} onClick={() => navigate('/user')}>

+          <AccountCircleIcon style={{ fontSize: 38, color: '#1a237e', background: '#e0f3ff', borderRadius: '50%' }} />

+        </div>

+        <div style={{ color: '#222', fontWeight: 500, marginRight: 24 }}>用户栏</div>

+        <div style={{ display: 'flex', gap: 28, flex: 1, justifyContent: 'flex-end', alignItems: 'center' }}>

+          <span style={{ color: '#1976d2', fontWeight: 500 }}>魔力值: <b>12345</b></span>

+          <span style={{ color: '#1976d2', fontWeight: 500 }}>分享率: <b>2.56</b></span>

+          <span style={{ color: '#1976d2', fontWeight: 500 }}>上传量: <b>100GB</b></span>

+          <span style={{ color: '#1976d2', fontWeight: 500 }}>下载量: <b>50GB</b></span>

+        </div>

+      </div>

+      {/* 下方内容整体下移,留出与音乐界面一致的间距 */}

+      <div style={{ height: 32 }} />

+      <nav className="nav-bar">

+        {navItems.map((item) => (

+          <div

+            key={item.label}

+            className={item.label === "动漫" ? "nav-item active" : "nav-item"}

+            onClick={() => navigate(item.path)}

+          >

+            {item.icon}

+            <span>{item.label}</span>

+          </div>

+        ))}

+      </nav>

+      <div className="search-section card">

+        <input className="search-input" placeholder="输入搜索关键词" />

+        <button className="search-btn">

+          <span role="img" aria-label="search">🔍</span>

+        </button>

+      </div>

+      <div className="area-tabs" style={{ display: 'flex', justifyContent: 'center', gap: 24, margin: '18px 0' }}>

+        {areaTabs.map((tab, idx) => (

+          <div

+            key={tab.label}

+            className={activeTab === idx ? "area-tab active" : "area-tab"}

+            onClick={() => setActiveTab(idx)}

+          >

+            {tab.icon} <span>{tab.label}</span>

+          </div>

+        ))}

+      </div>

+      <div className="table-section">

+        <table className="movie-table">

+          <thead>

+            <tr>

+              <th>动漫类型</th>

+              <th>标题</th>

+              <th>发布者</th>

+            </tr>

+          </thead>

+          <tbody>

+            {animeTypes.map(type => (

+              <tr key={type}>

+                <td>{type}</td>

+                <td></td>

+                <td></td>

+              </tr>

+            ))}

+          </tbody>

+        </table>

+      </div>

+      <div style={{ height: 32 }} />

+      <Pagination />

+    </div>

+  );

+}

+

+function Pagination() {

+  const [page, setPage] = React.useState(3);

+  const total = 5;

+  return (

+    <div className="pagination">

+      <button onClick={() => setPage(p => Math.max(1, p - 1))} disabled={page === 1}>上一页</button>

+      <span className="page-num">{page}/{total}</span>

+      <button onClick={() => setPage(p => Math.min(total, p + 1))} disabled={page === total}>下一页</button>

+      <span className="page-info">第 <b>{page}</b> 页</span>

+    </div>

+  );

+}

diff --git a/front/src/App.css b/front/src/App.css
new file mode 100644
index 0000000..c359190
--- /dev/null
+++ b/front/src/App.css
@@ -0,0 +1,208 @@
+.App {
+  text-align: center;
+}
+
+.App-logo {
+  height: 40vmin;
+  pointer-events: none;
+}
+
+@media (prefers-reduced-motion: no-preference) {
+  .App-logo {
+    animation: App-logo-spin infinite 20s linear;
+  }
+}
+
+.App-header {
+  background-color: #282c34;
+  min-height: 100vh;
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  justify-content: center;
+  font-size: calc(10px + 2vmin);
+  color: white;
+}
+
+.App-link {
+  color: #61dafb;
+}
+
+@keyframes App-logo-spin {
+  from {
+    transform: rotate(0deg);
+  }
+  to {
+    transform: rotate(360deg);
+  }
+}
+
+body {
+  background: linear-gradient(135deg, #f0f4ff 0%, #e0e7ff 100%);
+  margin: 0;
+  font-family: "Segoe UI", "Microsoft YaHei", Arial, sans-serif;
+  min-height: 100vh;
+  min-width: 100vw;
+}
+
+.container {
+  width: 100vw;
+  min-height: 100vh;
+  margin: 0;
+  background: #fff;
+  border-radius: 0;
+  box-shadow: none;
+  padding: 0 0 32px 0;
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+}
+
+.nav-bar {
+  display: flex;
+  justify-content: center;
+  gap: 36px;
+  margin-bottom: 40px;
+}
+
+.nav-item {
+  display: flex;
+  align-items: center;
+  gap: 8px;
+  font-size: 22px;
+  color: #3a4a6b;
+  cursor: pointer;
+  transition: color 0.2s;
+  padding: 6px 12px;
+  border-radius: 8px;
+}
+.nav-item:hover {
+  background: #e0e7ff;
+  color: #1a237e;
+}
+.nav-item.active {
+  background: #e0e7ff;
+  color: #1a237e;
+  border-bottom: 3px solid #1a237e;
+}
+
+.search-section {
+  display: flex;
+  justify-content: center;
+  margin-bottom: 24px;
+}
+.search-input {
+  width: 340px;
+  font-size: 18px;
+  padding: 10px 14px;
+  border: 1px solid #bfcfff;
+  border-radius: 8px 0 0 8px;
+  outline: none;
+}
+.search-btn {
+  font-size: 20px;
+  padding: 10px 18px;
+  border: 1px solid #bfcfff;
+  border-left: none;
+  background: #e0e7ff;
+  border-radius: 0 8px 8px 0;
+  cursor: pointer;
+}
+
+.advanced-search {
+  margin: 18px 0 24px 0;
+  font-size: 16px;
+}
+.advanced-input {
+  margin-left: 8px;
+  padding: 6px 10px;
+  border-radius: 6px;
+  border: 1px solid #bfcfff;
+  width: 220px;
+}
+
+.pagination {
+  display: flex;
+  align-items: center;
+  gap: 18px;
+  margin-bottom: 32px;
+  justify-content: center;
+}
+.pagination button {
+  padding: 6px 18px;
+  border-radius: 6px;
+  border: 1px solid #bfcfff;
+  background: #f5f7ff;
+  cursor: pointer;
+  font-size: 15px;
+}
+.page-num {
+  color: #1a237e;
+  font-weight: bold;
+}
+.page-info {
+  margin-left: 8px;
+  font-size: 15px;
+}
+
+.table-section {
+  width: 90vw;
+  margin: 18px auto 0 auto;
+  background: #f8faff;
+  border-radius: 12px;
+  padding: 18px;
+  border: 1px solid #e0e7ff;
+  box-sizing: border-box;
+}
+
+table {
+  width: 100%;
+  border-collapse: collapse;
+}
+th, td {
+  padding: 14px 10px;
+  text-align: left;
+  font-size: 18px;
+}
+th {
+  font-size: 22px;
+  color: #1a237e;
+  border-bottom: 2px solid #bfcfff;
+}
+td {
+  color: #3a4a6b;
+  border-bottom: 1px solid #e0e7ff;
+}
+
+.area-tabs {
+  margin-bottom: 10px;
+}
+.area-tab {
+  display: flex;
+  align-items: center;
+  gap: 4px;
+  font-size: 17px;
+  color: #3a4a6b;
+  padding: 4px 16px;
+  border-radius: 6px;
+  cursor: pointer;
+  transition: background 0.2s, color 0.2s;
+}
+.area-tab:hover {
+  background: #e0e7ff;
+  color: #1a237e;
+}
+.area-tab.active {
+  background: #e0e7ff;
+  color: #1a237e;
+  border-bottom: 3px solid #1a237e;
+}
+
+.movie-table {
+  width: 100%;
+  table-layout: fixed;
+}
+.movie-table th, .movie-table td {
+  width: 33.33%;
+  text-align: center;
+}
diff --git a/front/src/App.js b/front/src/App.js
new file mode 100644
index 0000000..3a49898
--- /dev/null
+++ b/front/src/App.js
@@ -0,0 +1,154 @@
+import React from "react";
+import { BrowserRouter as Router, Routes, Route, useNavigate, Link } from "react-router-dom";
+import MovieIcon from "@mui/icons-material/Movie";
+import EmailIcon from "@mui/icons-material/Email";
+import MusicNoteIcon from "@mui/icons-material/MusicNote";
+import EmojiPeopleIcon from "@mui/icons-material/EmojiPeople";
+import SportsEsportsIcon from "@mui/icons-material/SportsEsports";
+import SportsMartialArtsIcon from "@mui/icons-material/SportsMartialArts";
+import PersonIcon from "@mui/icons-material/Person";
+import AccountCircleIcon from "@mui/icons-material/AccountCircle";
+import "./App.css";
+import MoviePage from "./MoviePage";
+import TVPage from "./TVPage";
+import MusicPage from "./MusicPage";
+import AnimePage from "./AnimePage";
+import GamePage from "./GamePage";
+import SportPage from "./SportPage";
+import InfoPage from "./InfoPage";
+import UserProfile from "./UserProfile";
+
+const navItems = [
+  { label: "电影", icon: <MovieIcon />, path: "/movie" },
+  { label: "剧集", icon: <EmailIcon />, path: "/tv" },
+  { label: "音乐", icon: <MusicNoteIcon />, path: "/music" },
+  { label: "动漫", icon: <EmojiPeopleIcon />, path: "/anime" },
+  { label: "游戏", icon: <SportsEsportsIcon />, path: "/game" },
+  { label: "体育", icon: <SportsMartialArtsIcon />, path: "/sport" },
+  { label: "资料", icon: <PersonIcon />, path: "/info" },
+];
+
+function Home() {
+  const navigate = useNavigate();
+  return (
+    <div className="container">
+      {/* 顶部空白与电影界面一致 */}
+      <div style={{ height: 80 }} />
+      {/* 用户栏 */}
+      <div className="user-bar" style={{ position: 'fixed', top: 18, right: 42, zIndex: 100, display: 'flex', alignItems: 'center', background: '#e0f3ff', borderRadius: 12, padding: '6px 18px', boxShadow: '0 2px 8px #b2d8ea', minWidth: 320, minHeight: 48, width: 420 }}>
+        <div style={{ cursor: 'pointer', marginRight: 16 }} onClick={() => navigate('/user')}>
+          <AccountCircleIcon style={{ fontSize: 38, color: '#1a237e', background: '#e0f3ff', borderRadius: '50%' }} />
+        </div>
+        <div style={{ color: '#222', fontWeight: 500, marginRight: 24 }}>用户栏</div>
+        <div style={{ display: 'flex', gap: 28, flex: 1, justifyContent: 'flex-end', alignItems: 'center' }}>
+          <span style={{ color: '#1976d2', fontWeight: 500 }}>魔力值: <b>12345</b></span>
+          <span style={{ color: '#1976d2', fontWeight: 500 }}>分享率: <b>2.56</b></span>
+          <span style={{ color: '#1976d2', fontWeight: 500 }}>上传量: <b>100GB</b></span>
+          <span style={{ color: '#1976d2', fontWeight: 500 }}>下载量: <b>50GB</b></span>
+        </div>
+      </div>
+      {/* 下方内容整体下移,留出与电影界面一致的间距 */}
+      <div style={{ height: 32 }} />
+      <nav className="nav-bar card">
+        {navItems.map((item) => (
+          <div
+            key={item.label}
+            className={"nav-item"}
+            onClick={() => navigate(item.path)}
+          >
+            {item.icon}
+            <span>{item.label}</span>
+          </div>
+        ))}
+      </nav>
+      <div className="search-section card">
+        <input className="search-input" placeholder="输入搜索关键词" />
+        <button className="search-btn">
+          <span role="img" aria-label="search">🔍</span>
+        </button>
+      </div>
+      {/* 删除高频搜索栏 */}
+      <div className="table-section card">
+        <table className="movie-table">
+          <thead>
+            <tr>
+              <th>类型</th>
+              <th>标题</th>
+              <th>发布者</th>
+            </tr>
+          </thead>
+          <tbody>
+            <tr>
+              <td>电影</td>
+              <td></td>
+              <td></td>
+            </tr>
+            <tr>
+              <td>剧集</td>
+              <td></td>
+              <td></td>
+            </tr>
+            <tr>
+              <td>音乐</td>
+              <td></td>
+              <td></td>
+            </tr>
+            <tr>
+              <td>动漫</td>
+              <td></td>
+              <td></td>
+            </tr>
+            <tr>
+              <td>游戏</td>
+              <td></td>
+              <td></td>
+            </tr>
+          </tbody>
+        </table>
+      </div>
+      <div style={{ height: 32 }} />
+      <Pagination />
+    </div>
+  );
+}
+
+function Pagination() {
+  const [page, setPage] = React.useState(3);
+  const total = 5;
+  return (
+    <div className="pagination">
+      <button onClick={() => setPage(p => Math.max(1, p - 1))} disabled={page === 1}>上一页</button>
+      <span className="page-num">{page}/{total}</span>
+      <button onClick={() => setPage(p => Math.min(total, p + 1))} disabled={page === total}>下一页</button>
+      <span className="page-info">第 <b>{page}</b> 页</span>
+    </div>
+  );
+}
+
+function Page({ label }) {
+  return (
+    <div style={{ padding: 40, fontSize: 32 }}>
+      {label} 页面(可自定义内容)
+      <br />
+      <Link to="/">返回首页</Link>
+    </div>
+  );
+}
+
+export default function App() {
+  return (
+    <Router>
+      <Routes>
+        <Route path="/" element={<Home />} />
+        <Route path="/movie" element={<MoviePage />} />
+        <Route path="/tv" element={<TVPage />} />
+        <Route path="/music" element={<MusicPage />} />
+        <Route path="/anime" element={<AnimePage />} />
+        <Route path="/game" element={<GamePage />} />
+        <Route path="/sport" element={<SportPage />} />
+        <Route path="/info" element={<InfoPage />} />
+        <Route path="/user" element={<UserProfile />} />
+      </Routes>
+    </Router>
+  );
+}
\ No newline at end of file
diff --git a/front/src/App.test.js b/front/src/App.test.js
new file mode 100644
index 0000000..1f03afe
--- /dev/null
+++ b/front/src/App.test.js
@@ -0,0 +1,8 @@
+import { render, screen } from '@testing-library/react';
+import App from './App';
+
+test('renders learn react link', () => {
+  render(<App />);
+  const linkElement = screen.getByText(/learn react/i);
+  expect(linkElement).toBeInTheDocument();
+});
diff --git a/front/src/GamePage.js b/front/src/GamePage.js
new file mode 100644
index 0000000..f7331bb
--- /dev/null
+++ b/front/src/GamePage.js
@@ -0,0 +1,190 @@
+import React, { useState } from "react";

+import MovieIcon from "@mui/icons-material/Movie";

+import EmailIcon from "@mui/icons-material/Email";

+import MusicNoteIcon from "@mui/icons-material/MusicNote";

+import EmojiPeopleIcon from "@mui/icons-material/EmojiPeople";

+import SportsEsportsIcon from "@mui/icons-material/SportsEsports";

+import SportsMartialArtsIcon from "@mui/icons-material/SportsMartialArts";

+import PersonIcon from "@mui/icons-material/Person";

+import AccountCircleIcon from "@mui/icons-material/AccountCircle";

+import "./App.css";

+import { useNavigate } from "react-router-dom";

+

+const navItems = [

+  { label: "电影", icon: <MovieIcon />, path: "/movie" },

+  { label: "剧集", icon: <EmailIcon />, path: "/tv" },

+  { label: "音乐", icon: <MusicNoteIcon />, path: "/music" },

+  { label: "动漫", icon: <EmojiPeopleIcon />, path: "/anime" },

+  { label: "游戏", icon: <SportsEsportsIcon />, path: "/game" },

+  { label: "体育", icon: <SportsMartialArtsIcon />, path: "/sport" },

+  { label: "资料", icon: <PersonIcon />, path: "/info" },

+];

+

+const gameTypes = ["PC", "主机", "移动", "掌机", "视频"];

+

+const areaTabs = [

+  { label: "PC", icon: <MovieIcon fontSize="small" /> },

+  { label: "主机", icon: <EmailIcon fontSize="small" /> },

+  { label: "移动", icon: <PersonIcon fontSize="small" /> },

+  { label: "掌机", icon: <EmojiPeopleIcon fontSize="small" /> },

+  { label: "视频", icon: <PersonIcon fontSize="small" /> },

+];

+

+export default function GamePage() {

+  const navigate = useNavigate();

+  const [activeTab, setActiveTab] = useState(0);

+

+  return (

+    <div className="container">

+      {/* 预留顶部空间用于放图片 */}

+      <div style={{ height: 80 }} />

+      <div

+        className="user-bar"

+        style={{

+          position: "fixed",

+          top: 18,

+          right: 42,

+          zIndex: 100,

+          display: "flex",

+          alignItems: "center",

+          background: "#e0f3ff",

+          borderRadius: 12,

+          padding: "6px 18px",

+          boxShadow: "0 2px 8px #b2d8ea",

+          minWidth: 320,

+          minHeight: 48,

+          width: 420,

+        }}

+      >

+        <div

+          style={{ cursor: "pointer", marginRight: 16 }}

+          onClick={() => navigate("/user")}

+        >

+          <AccountCircleIcon

+            style={{

+              fontSize: 38,

+              color: "#1a237e",

+              background: "#e0f3ff",

+              borderRadius: "50%",

+            }}

+          />

+        </div>

+        <div style={{ color: "#222", fontWeight: 500, marginRight: 24 }}>

+          用户栏

+        </div>

+        <div

+          style={{

+            display: "flex",

+            gap: 28,

+            flex: 1,

+            justifyContent: "flex-end",

+            alignItems: "center",

+          }}

+        >

+          <span style={{ color: "#1976d2", fontWeight: 500 }}>

+            魔力值: <b>12345</b>

+          </span>

+          <span style={{ color: "#1976d2", fontWeight: 500 }}>

+            分享率: <b>2.56</b>

+          </span>

+          <span style={{ color: "#1976d2", fontWeight: 500 }}>

+            上传量: <b>100GB</b>

+          </span>

+          <span style={{ color: "#1976d2", fontWeight: 500 }}>

+            下载量: <b>50GB</b>

+          </span>

+        </div>

+      </div>

+      {/* 下方内容整体下移,留出与音乐界面一致的间距 */}

+      <div style={{ height: 32 }} />

+      <nav className="nav-bar card">

+        {navItems.map((item) => (

+          <div

+            key={item.label}

+            className={item.label === "游戏" ? "nav-item active" : "nav-item"}

+            onClick={() => navigate(item.path)}

+          >

+            {item.icon}

+            <span>{item.label}</span>

+          </div>

+        ))}

+      </nav>

+      <div className="search-section card">

+        <input className="search-input" placeholder="输入搜索关键词" />

+        <button className="search-btn">

+          <span role="img" aria-label="search">

+            🔍

+          </span>

+        </button>

+      </div>

+      <div

+        className="area-tabs"

+        style={{

+          display: "flex",

+          justifyContent: "center",

+          gap: 24,

+          margin: "18px 0",

+        }}

+      >

+        {areaTabs.map((tab, idx) => (

+          <div

+            key={tab.label}

+            className={activeTab === idx ? "area-tab active" : "area-tab"}

+            onClick={() => setActiveTab(idx)}

+          >

+            {tab.icon} <span>{tab.label}</span>

+          </div>

+        ))}

+      </div>

+      <div className="table-section">

+        <table className="movie-table">

+          <thead>

+            <tr>

+              <th>游戏类型</th>

+              <th>标题</th>

+              <th>发布者</th>

+            </tr>

+          </thead>

+          <tbody>

+            {gameTypes.map((type, idx) => (

+              <tr key={type}>

+                <td>{type}</td>

+                <td></td>

+                <td></td>

+              </tr>

+            ))}

+          </tbody>

+        </table>

+      </div>

+      <div style={{ height: 32 }} />

+      <Pagination />

+    </div>

+  );

+}

+

+function Pagination() {

+  const [page, setPage] = React.useState(3);

+  const total = 5;

+  return (

+    <div className="pagination">

+      <button

+        onClick={() => setPage((p) => Math.max(1, p - 1))}

+        disabled={page === 1}

+      >

+        上一页

+      </button>

+      <span className="page-num">

+        {page}/{total}

+      </span>

+      <button

+        onClick={() => setPage((p) => Math.min(total, p + 1))}

+        disabled={page === total}

+      >

+        下一页

+      </button>

+      <span className="page-info">

+        第 <b>{page}</b> 页

+      </span>

+    </div>

+  );

+}

diff --git a/front/src/InfoPage.js b/front/src/InfoPage.js
new file mode 100644
index 0000000..a919afd
--- /dev/null
+++ b/front/src/InfoPage.js
@@ -0,0 +1,200 @@
+import React from "react";

+import MovieIcon from "@mui/icons-material/Movie";

+import EmailIcon from "@mui/icons-material/Email";

+import MusicNoteIcon from "@mui/icons-material/MusicNote";

+import EmojiPeopleIcon from "@mui/icons-material/EmojiPeople";

+import SportsEsportsIcon from "@mui/icons-material/SportsEsports";

+import SportsMartialArtsIcon from "@mui/icons-material/SportsMartialArts";

+import PersonIcon from "@mui/icons-material/Person";

+import AccountCircleIcon from "@mui/icons-material/AccountCircle";

+import "./App.css";

+import { useNavigate } from "react-router-dom";

+

+const navItems = [

+  { label: "电影", icon: <MovieIcon />, path: "/movie" },

+  { label: "剧集", icon: <EmailIcon />, path: "/tv" },

+  { label: "音乐", icon: <MusicNoteIcon />, path: "/music" },

+  { label: "动漫", icon: <EmojiPeopleIcon />, path: "/anime" },

+  { label: "游戏", icon: <SportsEsportsIcon />, path: "/game" },

+  { label: "体育", icon: <SportsMartialArtsIcon />, path: "/sport" },

+  { label: "资料", icon: <PersonIcon />, path: "/info" },

+];

+

+const infoTypes = [""];

+

+const areaTabs = [

+  { label: "出版物", icon: <MovieIcon fontSize="small" /> },

+  { label: "学习教程", icon: <EmailIcon fontSize="small" /> },

+  { label: "素材模板", icon: <PersonIcon fontSize="small" /> },

+  { label: "演讲交流", icon: <EmojiPeopleIcon fontSize="small" /> },

+  { label: "日常娱乐", icon: <PersonIcon fontSize="small" />, active: true },

+];

+

+export default function InfoPage() {

+  const navigate = useNavigate();

+  const [activeTab, setActiveTab] = React.useState(0);

+

+  // 每个tab对应的资料类型

+  const infoTypesList = [

+    ["出版物A", "出版物B", "出版物C"], // 出版物

+    ["教程A", "教程B", "教程C"], // 学习教程

+    ["模板A", "模板B"], // 素材模板

+    ["演讲A", "演讲B"], // 演讲交流

+    ["娱乐A", "娱乐B"], // 日常娱乐

+  ];

+  const infoTypes = infoTypesList[activeTab] || [];

+

+  return (

+    <div className="container">

+      {/* 顶部空白与音乐界面一致,用户栏绝对定位在页面右上角 */}

+      <div style={{ height: 80 }} />

+      <div

+        className="user-bar"

+        style={{

+          position: "fixed",

+          top: 18,

+          right: 42,

+          zIndex: 100,

+          display: "flex",

+          alignItems: "center",

+          background: "#e0f3ff",

+          borderRadius: 12,

+          padding: "6px 18px",

+          boxShadow: "0 2px 8px #b2d8ea",

+          minWidth: 320,

+          minHeight: 48,

+          width: 420,

+        }}

+      >

+        <div

+          style={{ cursor: "pointer", marginRight: 16 }}

+          onClick={() => navigate("/user")}

+        >

+          <AccountCircleIcon

+            style={{

+              fontSize: 38,

+              color: "#1a237e",

+              background: "#e0f3ff",

+              borderRadius: "50%",

+            }}

+          />

+        </div>

+        <div style={{ color: "#222", fontWeight: 500, marginRight: 24 }}>

+          用户栏

+        </div>

+        <div

+          style={{

+            display: "flex",

+            gap: 28,

+            flex: 1,

+            justifyContent: "flex-end",

+            alignItems: "center",

+          }}

+        >

+          <span style={{ color: "#1976d2", fontWeight: 500 }}>

+            魔力值: <b>12345</b>

+          </span>

+          <span style={{ color: "#1976d2", fontWeight: 500 }}>

+            分享率: <b>2.56</b>

+          </span>

+          <span style={{ color: "#1976d2", fontWeight: 500 }}>

+            上传量: <b>100GB</b>

+          </span>

+          <span style={{ color: "#1976d2", fontWeight: 500 }}>

+            下载量: <b>50GB</b>

+          </span>

+        </div>

+      </div>

+      {/* 下方内容整体下移,留出与音乐界面一致的间距 */}

+      <div style={{ height: 32 }} />

+      <nav className="nav-bar card">

+        {navItems.map((item) => (

+          <div

+            key={item.label}

+            className={item.label === "资料" ? "nav-item active" : "nav-item"}

+            onClick={() => navigate(item.path)}

+          >

+            {item.icon}

+            <span>{item.label}</span>

+          </div>

+        ))}

+      </nav>

+      <div className="search-section card">

+        <input className="search-input" placeholder="输入搜索关键词" />

+        <button className="search-btn">

+          <span role="img" aria-label="search">

+            🔍

+          </span>

+        </button>

+      </div>

+      <div

+        className="area-tabs"

+        style={{

+          display: "flex",

+          justifyContent: "center",

+          gap: 24,

+          margin: "18px 0",

+        }}

+      >

+        {areaTabs.map((tab, idx) => (

+          <div

+            key={tab.label}

+            className={activeTab === idx ? "area-tab active" : "area-tab"}

+            onClick={() => setActiveTab(idx)}

+          >

+            {tab.icon} <span>{tab.label}</span>

+          </div>

+        ))}

+      </div>

+      <div className="table-section">

+        <table className="movie-table">

+          <thead>

+            <tr>

+              <th>类型</th>

+              <th>标题</th>

+              <th>发布者</th>

+            </tr>

+          </thead>

+          <tbody>

+            {infoTypes.map((type) => (

+              <tr key={type}>

+                <td>{type}</td>

+                <td></td>

+                <td></td>

+              </tr>

+            ))}

+          </tbody>

+        </table>

+      </div>

+      <div style={{ height: 32 }} />

+      <Pagination />

+    </div>

+  );

+}

+

+function Pagination() {

+  const [page, setPage] = React.useState(3);

+  const total = 5;

+  return (

+    <div className="pagination">

+      <button

+        onClick={() => setPage((p) => Math.max(1, p - 1))}

+        disabled={page === 1}

+      >

+        上一页

+      </button>

+      <span className="page-num">

+        {page}/{total}

+      </span>

+      <button

+        onClick={() => setPage((p) => Math.min(total, p + 1))}

+        disabled={page === total}

+      >

+        下一页

+      </button>

+      <span className="page-info">

+        第 <b>{page}</b> 页

+      </span>

+    </div>

+  );

+}

diff --git a/front/src/MoviePage.js b/front/src/MoviePage.js
new file mode 100644
index 0000000..f8c765c
--- /dev/null
+++ b/front/src/MoviePage.js
@@ -0,0 +1,129 @@
+import React from "react";

+import MovieIcon from "@mui/icons-material/Movie";

+import EmailIcon from "@mui/icons-material/Email";

+import MusicNoteIcon from "@mui/icons-material/MusicNote";

+import EmojiPeopleIcon from "@mui/icons-material/EmojiPeople";

+import SportsEsportsIcon from "@mui/icons-material/SportsEsports";

+import SportsMartialArtsIcon from "@mui/icons-material/SportsMartialArts";

+import PersonIcon from "@mui/icons-material/Person";

+import AccountCircleIcon from "@mui/icons-material/AccountCircle";

+import { useNavigate } from "react-router-dom";

+import "./App.css";

+

+const navItems = [

+  { label: "电影", icon: <MovieIcon />, path: "/movie" },

+  { label: "剧集", icon: <EmailIcon />, path: "/tv" },

+  { label: "音乐", icon: <MusicNoteIcon />, path: "/music" },

+  { label: "动漫", icon: <EmojiPeopleIcon />, path: "/anime" },

+  { label: "游戏", icon: <SportsEsportsIcon />, path: "/game" },

+  { label: "体育", icon: <SportsMartialArtsIcon />, path: "/sport" },

+  { label: "资料", icon: <PersonIcon />, path: "/info" },

+];

+

+const areaTabs = [

+  { label: "大陆", icon: <MovieIcon fontSize="small" /> },

+  { label: "港台", icon: <EmailIcon fontSize="small" /> },

+  { label: "欧美", icon: <PersonIcon fontSize="small" /> },

+  { label: "日韩", icon: <EmojiPeopleIcon fontSize="small" /> },

+  { label: "其他", icon: <PersonIcon fontSize="small" /> },

+];

+

+export default function MoviePage() {

+  const navigate = useNavigate();

+  const [activeTab, setActiveTab] = React.useState(0);

+

+  // 每个tab对应的电影类型

+  const movieTypesList = [

+    ["华语电影(大陆)", "欧美电影", "日韩电影", "港台电影", "其他"], // 大陆

+    ["港台动作", "港台爱情", "港台喜剧", "港台其他"], // 港台

+    ["欧美动作", "欧美科幻", "欧美剧情", "欧美其他"], // 欧美

+    ["日韩动画", "日韩爱情", "日韩其他"], // 日韩

+    ["其他类型1", "其他类型2"] // 其他

+  ];

+  const movieTypes = movieTypesList[activeTab] || [];

+

+  return (

+    <div className="container">

+      {/* 顶部空白与音乐界面一致,用户栏绝对定位在页面右上角 */}

+      <div style={{ height: 80 }} />

+      <div className="user-bar" style={{ position: 'fixed', top: 18, right: 42, zIndex: 100, display: 'flex', alignItems: 'center', background: '#e0f3ff', borderRadius: 12, padding: '6px 18px', boxShadow: '0 2px 8px #b2d8ea', minWidth: 320, minHeight: 48, width: 420 }}>

+        <div style={{ cursor: 'pointer', marginRight: 16 }} onClick={() => navigate('/user')}>

+          <AccountCircleIcon style={{ fontSize: 38, color: '#1a237e', background: '#e0f3ff', borderRadius: '50%' }} />

+        </div>

+        <div style={{ color: '#222', fontWeight: 500, marginRight: 24 }}>用户栏</div>

+        <div style={{ display: 'flex', gap: 28, flex: 1, justifyContent: 'flex-end', alignItems: 'center' }}>

+          <span style={{ color: '#1976d2', fontWeight: 500 }}>魔力值: <b>12345</b></span>

+          <span style={{ color: '#1976d2', fontWeight: 500 }}>分享率: <b>2.56</b></span>

+          <span style={{ color: '#1976d2', fontWeight: 500 }}>上传量: <b>100GB</b></span>

+          <span style={{ color: '#1976d2', fontWeight: 500 }}>下载量: <b>50GB</b></span>

+        </div>

+      </div>

+      {/* 下方内容整体下移,留出与音乐界面一致的间距 */}

+      <div style={{ height: 32 }} />

+      <nav className="nav-bar card">

+        {navItems.map((item) => (

+          <div

+            key={item.label}

+            className={item.label === "电影" ? "nav-item active" : "nav-item"}

+            onClick={() => navigate(item.path)}

+          >

+            {item.icon}

+            <span>{item.label}</span>

+          </div>

+        ))}

+      </nav>

+      <div className="search-section card">

+        <input className="search-input" placeholder="输入搜索关键词" />

+        <button className="search-btn">

+          <span role="img" aria-label="search">🔍</span>

+        </button>

+      </div>

+      <div className="area-tabs" style={{ display: 'flex', justifyContent: 'center', gap: 24, margin: '18px 0' }}>

+        {areaTabs.map((tab, idx) => (

+          <div

+            key={tab.label}

+            className={activeTab === idx ? "area-tab active" : "area-tab"}

+            onClick={() => setActiveTab(idx)}

+          >

+            {tab.icon} <span>{tab.label}</span>

+          </div>

+        ))}

+      </div>

+      <div className="table-section">

+        <table className="movie-table">

+          <thead>

+            <tr>

+              <th>电影类型</th>

+              <th>标题</th>

+              <th>发布者</th>

+            </tr>

+          </thead>

+          <tbody>

+            {movieTypes.map(type => (

+              <tr key={type}>

+                <td>{type}</td>

+                <td></td>

+                <td></td>

+              </tr>

+            ))}

+          </tbody>

+        </table>

+      </div>

+      <div style={{ height: 32 }} />

+      <Pagination />

+    </div>

+  );

+}

+

+function Pagination() {

+  const [page, setPage] = React.useState(3);

+  const total = 5;

+  return (

+    <div className="pagination">

+      <button onClick={() => setPage(p => Math.max(1, p - 1))} disabled={page === 1}>上一页</button>

+      <span className="page-num">{page}/{total}</span>

+      <button onClick={() => setPage(p => Math.min(total, p + 1))} disabled={page === total}>下一页</button>

+      <span className="page-info">第 <b>{page}</b> 页</span>

+    </div>

+  );

+}

diff --git a/front/src/MusicPage.js b/front/src/MusicPage.js
new file mode 100644
index 0000000..1a63a89
--- /dev/null
+++ b/front/src/MusicPage.js
@@ -0,0 +1,129 @@
+import React from "react";

+import MovieIcon from "@mui/icons-material/Movie";

+import EmailIcon from "@mui/icons-material/Email";

+import MusicNoteIcon from "@mui/icons-material/MusicNote";

+import EmojiPeopleIcon from "@mui/icons-material/EmojiPeople";

+import SportsEsportsIcon from "@mui/icons-material/SportsEsports";

+import SportsMartialArtsIcon from "@mui/icons-material/SportsMartialArts";

+import PersonIcon from "@mui/icons-material/Person";

+import AccountCircleIcon from "@mui/icons-material/AccountCircle";

+import "./App.css";

+import { useNavigate } from "react-router-dom";

+

+const navItems = [

+  { label: "电影", icon: <MovieIcon />, path: "/movie" },

+  { label: "剧集", icon: <EmailIcon />, path: "/tv" },

+  { label: "音乐", icon: <MusicNoteIcon />, path: "/music" },

+  { label: "动漫", icon: <EmojiPeopleIcon />, path: "/anime" },

+  { label: "游戏", icon: <SportsEsportsIcon />, path: "/game" },

+  { label: "体育", icon: <SportsMartialArtsIcon />, path: "/sport" },

+  { label: "资料", icon: <PersonIcon />, path: "/info" },

+];

+

+const areaTabs = [

+  { label: "大陆", icon: <MovieIcon fontSize="small" /> },

+  { label: "港台", icon: <EmailIcon fontSize="small" /> },

+  { label: "欧美", icon: <PersonIcon fontSize="small" /> },

+  { label: "日韩", icon: <EmojiPeopleIcon fontSize="small" /> },

+  { label: "其他", icon: <PersonIcon fontSize="small" /> },

+];

+

+export default function MusicPage() {

+  const navigate = useNavigate();

+  const [activeTab, setActiveTab] = React.useState(0);

+

+  // 每个tab对应的音乐类型

+  const musicTypesList = [

+    ["华语音乐(大陆)", "欧美音乐", "日韩音乐", "港台音乐", "其他"], // 大陆

+    ["港台流行", "港台摇滚", "港台其他"], // 港台

+    ["欧美流行", "欧美摇滚", "欧美其他"], // 欧美

+    ["日韩流行", "日韩其他"], // 日韩

+    ["其他类型1", "其他类型2"] // 其他

+  ];

+  const musicTypes = musicTypesList[activeTab] || [];

+

+  return (

+    <div className="container music-bg">

+      {/* 顶部空白与音乐界面一致,用户栏绝对定位在页面右上角 */}

+      <div style={{ height: 80 }} />

+      <div className="user-bar" style={{ position: 'fixed', top: 18, right: 42, zIndex: 100, display: 'flex', alignItems: 'center', background: '#e0f3ff', borderRadius: 12, padding: '6px 18px', boxShadow: '0 2px 8px #b2d8ea', minWidth: 320, minHeight: 48, width: 420 }}>

+        <div style={{ cursor: 'pointer', marginRight: 16 }} onClick={() => navigate('/user')}>

+          <AccountCircleIcon style={{ fontSize: 38, color: '#1a237e', background: '#e0f3ff', borderRadius: '50%' }} />

+        </div>

+        <div style={{ color: '#222', fontWeight: 500, marginRight: 24 }}>用户栏</div>

+        <div style={{ display: 'flex', gap: 28, flex: 1, justifyContent: 'flex-end', alignItems: 'center' }}>

+          <span style={{ color: '#1976d2', fontWeight: 500 }}>魔力值: <b>12345</b></span>

+          <span style={{ color: '#1976d2', fontWeight: 500 }}>分享率: <b>2.56</b></span>

+          <span style={{ color: '#1976d2', fontWeight: 500 }}>上传量: <b>100GB</b></span>

+          <span style={{ color: '#1976d2', fontWeight: 500 }}>下载量: <b>50GB</b></span>

+        </div>

+      </div>

+      {/* 下方内容整体下移,留出与其他页面一致的间距 */}

+      <div style={{ height: 32 }} />

+      <nav className="nav-bar card">

+        {navItems.map((item) => (

+          <div

+            key={item.label}

+            className={item.label === "音乐" ? "nav-item active" : "nav-item"}

+            onClick={() => navigate(item.path)}

+          >

+            {item.icon}

+            <span>{item.label}</span>

+          </div>

+        ))}

+      </nav>

+      <div className="search-section card">

+        <input className="search-input" placeholder="输入搜索关键词" />

+        <button className="search-btn">

+          <span role="img" aria-label="search">🔍</span>

+        </button>

+      </div>

+      <div className="area-tabs card" style={{ display: 'flex', justifyContent: 'center', gap: 24, margin: '18px 0' }}>

+        {areaTabs.map((tab, idx) => (

+          <div

+            key={tab.label}

+            className={activeTab === idx ? "area-tab active" : "area-tab"}

+            onClick={() => setActiveTab(idx)}

+          >

+            {tab.icon} <span>{tab.label}</span>

+          </div>

+        ))}

+      </div>

+      <div className="table-section card">

+        <table className="movie-table">

+          <thead>

+            <tr>

+              <th>音乐类型</th>

+              <th>标题</th>

+              <th>发布者</th>

+            </tr>

+          </thead>

+          <tbody>

+            {musicTypes.map(type => (

+              <tr key={type}>

+                <td>{type}</td>

+                <td></td>

+                <td></td>

+              </tr>

+            ))}

+          </tbody>

+        </table>

+      </div>

+      <div style={{ height: 32 }} />

+      <Pagination />

+    </div>

+  );

+}

+

+function Pagination() {

+  const [page, setPage] = React.useState(3);

+  const total = 5;

+  return (

+    <div className="pagination">

+      <button onClick={() => setPage(p => Math.max(1, p - 1))} disabled={page === 1}>上一页</button>

+      <span className="page-num">{page}/{total}</span>

+      <button onClick={() => setPage(p => Math.min(total, p + 1))} disabled={page === total}>下一页</button>

+      <span className="page-info">第 <b>{page}</b> 页</span>

+    </div>

+  );

+}

diff --git a/front/src/SportPage.js b/front/src/SportPage.js
new file mode 100644
index 0000000..81bc2a2
--- /dev/null
+++ b/front/src/SportPage.js
@@ -0,0 +1,200 @@
+import React from "react";

+import MovieIcon from "@mui/icons-material/Movie";

+import EmailIcon from "@mui/icons-material/Email";

+import MusicNoteIcon from "@mui/icons-material/MusicNote";

+import EmojiPeopleIcon from "@mui/icons-material/EmojiPeople";

+import SportsEsportsIcon from "@mui/icons-material/SportsEsports";

+import SportsMartialArtsIcon from "@mui/icons-material/SportsMartialArts";

+import PersonIcon from "@mui/icons-material/Person";

+import AccountCircleIcon from "@mui/icons-material/AccountCircle";

+import "./App.css";

+import { useNavigate } from "react-router-dom";

+

+const navItems = [

+  { label: "电影", icon: <MovieIcon />, path: "/movie" },

+  { label: "剧集", icon: <EmailIcon />, path: "/tv" },

+  { label: "音乐", icon: <MusicNoteIcon />, path: "/music" },

+  { label: "动漫", icon: <EmojiPeopleIcon />, path: "/anime" },

+  { label: "游戏", icon: <SportsEsportsIcon />, path: "/game" },

+  { label: "体育", icon: <SportsMartialArtsIcon />, path: "/sport" },

+  { label: "资料", icon: <PersonIcon />, path: "/info" },

+];

+

+const sportTypes = [""];

+

+const areaTabs = [

+  { label: "篮球", icon: <MovieIcon fontSize="small" /> },

+  { label: "足球", icon: <EmailIcon fontSize="small" /> },

+  { label: "羽毛球", icon: <EmojiPeopleIcon fontSize="small" /> },

+  { label: "排球", icon: <PersonIcon fontSize="small" /> },

+  { label: "电竞", icon: <PersonIcon fontSize="small" />, active: true },

+];

+

+export default function SportPage() {

+  const navigate = useNavigate();

+  const [activeTab, setActiveTab] = React.useState(0);

+

+  // 每个tab对应的运动类型

+  const sportTypesList = [

+    ["篮球赛事", "篮球技巧", "篮球明星"], // 篮球

+    ["足球赛事", "足球技巧", "足球明星"], // 足球

+    ["羽毛球赛事", "羽毛球技巧"], // 羽毛球

+    ["排球赛事", "排球技巧"], // 排球

+    ["电竞赛事", "电竞技巧"], // 电竞

+  ];

+  const sportTypes = sportTypesList[activeTab] || [];

+

+  return (

+    <div className="container">

+      {/* 顶部空白与音乐界面一致,用户栏绝对定位在页面右上角 */}

+      <div style={{ height: 80 }} />

+      <div

+        className="user-bar"

+        style={{

+          position: "fixed",

+          top: 18,

+          right: 42,

+          zIndex: 100,

+          display: "flex",

+          alignItems: "center",

+          background: "#e0f3ff",

+          borderRadius: 12,

+          padding: "6px 18px",

+          boxShadow: "0 2px 8px #b2d8ea",

+          minWidth: 320,

+          minHeight: 48,

+          width: 420,

+        }}

+      >

+        <div

+          style={{ cursor: "pointer", marginRight: 16 }}

+          onClick={() => navigate("/user")}

+        >

+          <AccountCircleIcon

+            style={{

+              fontSize: 38,

+              color: "#1a237e",

+              background: "#e0f3ff",

+              borderRadius: "50%",

+            }}

+          />

+        </div>

+        <div style={{ color: "#222", fontWeight: 500, marginRight: 24 }}>

+          用户栏

+        </div>

+        <div

+          style={{

+            display: "flex",

+            gap: 28,

+            flex: 1,

+            justifyContent: "flex-end",

+            alignItems: "center",

+          }}

+        >

+          <span style={{ color: "#1976d2", fontWeight: 500 }}>

+            魔力值: <b>12345</b>

+          </span>

+          <span style={{ color: "#1976d2", fontWeight: 500 }}>

+            分享率: <b>2.56</b>

+          </span>

+          <span style={{ color: "#1976d2", fontWeight: 500 }}>

+            上传量: <b>100GB</b>

+          </span>

+          <span style={{ color: "#1976d2", fontWeight: 500 }}>

+            下载量: <b>50GB</b>

+          </span>

+        </div>

+      </div>

+      {/* 下方内容整体下移,留出与音乐界面一致的间距 */}

+      <div style={{ height: 32 }} />

+      <nav className="nav-bar card">

+        {navItems.map((item) => (

+          <div

+            key={item.label}

+            className={item.label === "体育" ? "nav-item active" : "nav-item"}

+            onClick={() => navigate(item.path)}

+          >

+            {item.icon}

+            <span>{item.label}</span>

+          </div>

+        ))}

+      </nav>

+      <div className="search-section card">

+        <input className="search-input" placeholder="输入搜索关键词" />

+        <button className="search-btn">

+          <span role="img" aria-label="search">

+            🔍

+          </span>

+        </button>

+      </div>

+      <div

+        className="area-tabs"

+        style={{

+          display: "flex",

+          justifyContent: "center",

+          gap: 24,

+          margin: "18px 0",

+        }}

+      >

+        {areaTabs.map((tab, idx) => (

+          <div

+            key={tab.label}

+            className={activeTab === idx ? "area-tab active" : "area-tab"}

+            onClick={() => setActiveTab(idx)}

+          >

+            {tab.icon} <span>{tab.label}</span>

+          </div>

+        ))}

+      </div>

+      <div className="table-section">

+        <table className="movie-table">

+          <thead>

+            <tr>

+              <th>类型</th>

+              <th>标题</th>

+              <th>发布者</th>

+            </tr>

+          </thead>

+          <tbody>

+            {sportTypes.map((type) => (

+              <tr key={type}>

+                <td>{type}</td>

+                <td></td>

+                <td></td>

+              </tr>

+            ))}

+          </tbody>

+        </table>

+      </div>

+      <div style={{ height: 32 }} />

+      <Pagination />

+    </div>

+  );

+}

+

+function Pagination() {

+  const [page, setPage] = React.useState(3);

+  const total = 5;

+  return (

+    <div className="pagination">

+      <button

+        onClick={() => setPage((p) => Math.max(1, p - 1))}

+        disabled={page === 1}

+      >

+        上一页

+      </button>

+      <span className="page-num">

+        {page}/{total}

+      </span>

+      <button

+        onClick={() => setPage((p) => Math.min(total, p + 1))}

+        disabled={page === total}

+      >

+        下一页

+      </button>

+      <span className="page-info">

+        第 <b>{page}</b> 页

+      </span>

+    </div>

+  );

+}

diff --git a/front/src/TVPage.js b/front/src/TVPage.js
new file mode 100644
index 0000000..02ec0f0
--- /dev/null
+++ b/front/src/TVPage.js
@@ -0,0 +1,137 @@
+import React from "react";

+import MovieIcon from "@mui/icons-material/Movie";

+import EmailIcon from "@mui/icons-material/Email";

+import MusicNoteIcon from "@mui/icons-material/MusicNote";

+import EmojiPeopleIcon from "@mui/icons-material/EmojiPeople";

+import SportsEsportsIcon from "@mui/icons-material/SportsEsports";

+import SportsMartialArtsIcon from "@mui/icons-material/SportsMartialArts";

+import PersonIcon from "@mui/icons-material/Person";

+import AccountCircleIcon from "@mui/icons-material/AccountCircle";

+import { useNavigate } from "react-router-dom";

+import "./App.css";

+

+const navItems = [

+  { label: "电影", icon: <MovieIcon />, path: "/movie" },

+  { label: "剧集", icon: <EmailIcon />, path: "/tv" },

+  { label: "音乐", icon: <MusicNoteIcon />, path: "/music" },

+  { label: "动漫", icon: <EmojiPeopleIcon />, path: "/anime" },

+  { label: "游戏", icon: <SportsEsportsIcon />, path: "/game" },

+  { label: "体育", icon: <SportsMartialArtsIcon />, path: "/sport" },

+  { label: "资料", icon: <PersonIcon />, path: "/info" },

+];

+

+const tvTypes = [

+  "华语剧集(大陆)",

+  "欧美剧集",

+  "日韩剧集",

+  "港台剧集",

+  "其他"

+];

+

+const areaTabs = [

+  { label: "大陆", icon: <MovieIcon fontSize="small" /> },

+  { label: "港台", icon: <EmailIcon fontSize="small" /> },

+  { label: "欧美", icon: <PersonIcon fontSize="small" /> },

+  { label: "日韩", icon: <EmojiPeopleIcon fontSize="small" /> },

+  { label: "其他", icon: <PersonIcon fontSize="small" /> },

+];

+

+export default function TVPage() {

+  const navigate = useNavigate();

+  const [activeTab, setActiveTab] = React.useState(0);

+

+  // 每个tab对应的剧集类型

+  const tvTypesList = [

+    ["华语剧集(大陆)", "欧美剧集", "日韩剧集", "港台剧集", "其他"], // 大陆

+    ["港台都市", "港台爱情", "港台悬疑", "港台其他"], // 港台

+    ["欧美悬疑", "欧美历史", "欧美其他"], // 欧美

+    ["日韩青春", "日韩家庭", "日韩其他"], // 日韩

+    ["其他类型1", "其他类型2"] // 其他

+  ];

+  const tvTypes = tvTypesList[activeTab] || [];

+

+  return (

+    <div className="container">

+      {/* 顶部空白与音乐界面一致,用户栏绝对定位在页面右上角 */}

+      <div style={{ height: 80 }} />

+      <div className="user-bar" style={{ position: 'fixed', top: 18, right: 42, zIndex: 100, display: 'flex', alignItems: 'center', background: '#e0f3ff', borderRadius: 12, padding: '6px 18px', boxShadow: '0 2px 8px #b2d8ea', minWidth: 320, minHeight: 48, width: 420 }}>

+        <div style={{ cursor: 'pointer', marginRight: 16 }} onClick={() => navigate('/user')}>

+          <AccountCircleIcon style={{ fontSize: 38, color: '#1a237e', background: '#e0f3ff', borderRadius: '50%' }} />

+        </div>

+        <div style={{ color: '#222', fontWeight: 500, marginRight: 24 }}>用户栏</div>

+        <div style={{ display: 'flex', gap: 28, flex: 1, justifyContent: 'flex-end', alignItems: 'center' }}>

+          <span style={{ color: '#1976d2', fontWeight: 500 }}>魔力值: <b>12345</b></span>

+          <span style={{ color: '#1976d2', fontWeight: 500 }}>分享率: <b>2.56</b></span>

+          <span style={{ color: '#1976d2', fontWeight: 500 }}>上传量: <b>100GB</b></span>

+          <span style={{ color: '#1976d2', fontWeight: 500 }}>下载量: <b>50GB</b></span>

+        </div>

+      </div>

+      {/* 下方内容整体下移,留出与音乐界面一致的间距 */}

+      <div style={{ height: 32 }} />

+      <nav className="nav-bar card">

+        {navItems.map((item) => (

+          <div

+            key={item.label}

+            className={item.label === "剧集" ? "nav-item active" : "nav-item"}

+            onClick={() => navigate(item.path)}

+          >

+            {item.icon}

+            <span>{item.label}</span>

+          </div>

+        ))}

+      </nav>

+      <div className="search-section card">

+        <input className="search-input" placeholder="输入搜索关键词" />

+        <button className="search-btn">

+          <span role="img" aria-label="search">🔍</span>

+        </button>

+      </div>

+      <div className="area-tabs" style={{ display: 'flex', justifyContent: 'center', gap: 24, margin: '18px 0' }}>

+        {areaTabs.map((tab, idx) => (

+          <div

+            key={tab.label}

+            className={activeTab === idx ? "area-tab active" : "area-tab"}

+            onClick={() => setActiveTab(idx)}

+          >

+            {tab.icon} <span>{tab.label}</span>

+          </div>

+        ))}

+      </div>

+      <div className="table-section">

+        <table className="movie-table">

+          <thead>

+            <tr>

+              <th>剧集类型</th>

+              <th>标题</th>

+              <th>发布者</th>

+            </tr>

+          </thead>

+          <tbody>

+            {tvTypes.map(type => (

+              <tr key={type}>

+                <td>{type}</td>

+                <td></td>

+                <td></td>

+              </tr>

+            ))}

+          </tbody>

+        </table>

+      </div>

+      <div style={{ height: 32 }} />

+      <Pagination />

+    </div>

+  );

+}

+

+function Pagination() {

+  const [page, setPage] = React.useState(3);

+  const total = 5;

+  return (

+    <div className="pagination">

+      <button onClick={() => setPage(p => Math.max(1, p - 1))} disabled={page === 1}>上一页</button>

+      <span className="page-num">{page}/{total}</span>

+      <button onClick={() => setPage(p => Math.min(total, p + 1))} disabled={page === total}>下一页</button>

+      <span className="page-info">第 <b>{page}</b> 页</span>

+    </div>

+  );

+}

diff --git a/front/src/UserProfile.js b/front/src/UserProfile.js
new file mode 100644
index 0000000..0269723
--- /dev/null
+++ b/front/src/UserProfile.js
@@ -0,0 +1,203 @@
+import React, { useState } from "react";

+import AccountCircleIcon from "@mui/icons-material/AccountCircle";

+import { useNavigate } from "react-router-dom";

+import "./App.css";

+

+export default function UserProfile() {

+  const navigate = useNavigate();

+  const [userInfo, setUserInfo] = useState({

+    username: "示例用户",

+    email: "user@example.com",

+    company: "",

+    school: "",

+    birthday: "",

+  });

+  const [tempUserInfo, setTempUserInfo] = useState({ ...userInfo });

+

+  const handleInputChange = (field, value) => {

+    setTempUserInfo({ ...tempUserInfo, [field]: value });

+  };

+

+  const handleSave = () => {

+    setUserInfo({ ...tempUserInfo });

+    alert("信息已保存!");

+  };

+

+  const handleAvatarClick = () => {

+    const avatarUrl = prompt("请输入头像的URL:");

+    if (avatarUrl) {

+      setTempUserInfo({ ...tempUserInfo, avatar: avatarUrl });

+    }

+  };

+

+  return (

+    <div className="container" style={{ minHeight: '100vh', background: 'linear-gradient(135deg, #f0f4ff 0%, #e0e7ff 100%)', display: 'grid', gridTemplateColumns: '1fr 2fr', gridTemplateRows: 'auto 1fr', gap: '20px', padding: '40px' }}>

+      {/* 左侧:用户资料 */}

+      <div style={{ gridColumn: '1 / 2', gridRow: '1 / 3', display: 'flex', flexDirection: 'column', alignItems: 'center', background: '#fff', borderRadius: 18, boxShadow: '0 4px 24px #e0e7ff', padding: '20px' }}>

+        <div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', marginBottom: 16 }}>

+          <div onClick={handleAvatarClick} style={{ cursor: 'pointer', position: 'relative' }}>

+            <AccountCircleIcon style={{ fontSize: 90, color: '#1a237e', marginBottom: 12 }} />

+            {tempUserInfo.avatar && (

+              <img

+                src={tempUserInfo.avatar}

+                alt="用户头像"

+                style={{

+                  position: 'absolute',

+                  top: 0,

+                  left: 0,

+                  width: 90,

+                  height: 90,

+                  borderRadius: '50%',

+                  objectFit: 'cover',

+                }}

+              />

+            )}

+          </div>

+          <h2 style={{ color: '#1a237e', marginBottom: 0, fontSize: 24 }}>用户个人资料</h2>

+        </div>

+        <div className="card" style={{ padding: 28, width: '100%', background: '#fff', borderRadius: 18, boxShadow: '0 4px 24px #e0e7ff', flex: 1 }}>

+          <div style={{ marginBottom: 18, display: 'flex', alignItems: 'center' }}>

+            <b style={{ width: 72, textAlign: 'left', marginRight: 0, fontSize: 16 }}>用户名:</b>

+            <input

+              type="text"

+              value={tempUserInfo.username}

+              onChange={(e) => handleInputChange("username", e.target.value)}

+              style={{ flex: 1, padding: '6px 10px', borderRadius: 7, border: '1px solid #b2b2b2', minWidth: 0, fontSize: 15 }}

+            />

+          </div>

+          <div style={{ marginBottom: 18, display: 'flex', alignItems: 'center' }}>

+            <b style={{ width: 72, textAlign: 'left', marginRight: 0, fontSize: 16 }}>邮箱:</b>

+            <input

+              type="email"

+              value={tempUserInfo.email}

+              onChange={(e) => handleInputChange("email", e.target.value)}

+              style={{ flex: 1, padding: '6px 10px', borderRadius: 7, border: '1px solid #b2b2b2', minWidth: 0, fontSize: 15 }}

+            />

+          </div>

+          <div style={{ marginBottom: 18, display: 'flex', alignItems: 'center' }}>

+            <b style={{ width: 72, textAlign: 'left', marginRight: 0, fontSize: 16 }}>公司:</b>

+            <input

+              type="text"

+              value={tempUserInfo.company}

+              onChange={(e) => handleInputChange("company", e.target.value)}

+              style={{ flex: 1, padding: '6px 10px', borderRadius: 7, border: '1px solid #b2b2b2', minWidth: 0, fontSize: 15 }}

+            />

+          </div>

+          <div style={{ marginBottom: 18, display: 'flex', alignItems: 'center' }}>

+            <b style={{ width: 72, textAlign: 'left', marginRight: 0, fontSize: 16 }}>学校:</b>

+            <input

+              type="text"

+              value={tempUserInfo.school}

+              onChange={(e) => handleInputChange("school", e.target.value)}

+              style={{ flex: 1, padding: '6px 10px', borderRadius: 7, border: '1px solid #b2b2b2', minWidth: 0, fontSize: 15 }}

+            />

+          </div>

+          <div style={{ marginBottom: 18, display: 'flex', alignItems: 'center' }}>

+            <b style={{ width: 72, textAlign: 'left', marginRight: 0, fontSize: 16 }}>生日:</b>

+            <input

+              type="date"

+              value={tempUserInfo.birthday}

+              onChange={(e) => handleInputChange("birthday", e.target.value)}

+              style={{ flex: 1, padding: '6px 10px', borderRadius: 7, border: '1px solid #b2b2b2', minWidth: 0, fontSize: 15 }}

+            />

+          </div>

+          <div style={{ marginBottom: 18, display: 'flex', alignItems: 'center' }}>

+            <b style={{ width: 72, textAlign: 'left', marginRight: 0, fontSize: 16 }}>密码:</b>

+            <input

+              type="password"

+              value={tempUserInfo.password || ""}

+              onChange={(e) => handleInputChange("password", e.target.value)}

+              style={{ flex: 1, padding: '6px 10px', borderRadius: 7, border: '1px solid #b2b2b2', minWidth: 0, fontSize: 15 }}

+            />

+          </div>

+          <div style={{ marginBottom: 18, display: 'flex', alignItems: 'center' }}>

+            <b style={{ width: 72, textAlign: 'left', marginRight: 0, fontSize: 16 }}>性别:</b>

+            <div style={{ position: 'relative', flex: 1 }}>

+              <button

+                onClick={() => setTempUserInfo({ ...tempUserInfo, showGenderOptions: !tempUserInfo.showGenderOptions })}

+                style={{

+                  width: '100%',

+                  padding: '6px 10px',

+                  borderRadius: 7,

+                  border: '1px solid #b2b2b2',

+                  textAlign: 'left',

+                  backgroundColor: '#fff',

+                  fontSize: 15,

+                  cursor: 'pointer',

+                }}

+              >

+                {tempUserInfo.gender || "性别"}

+              </button>

+              {tempUserInfo.showGenderOptions && (

+                <ul

+                  style={{

+                    position: 'absolute',

+                    top: '100%',

+                    left: 0,

+                    right: 0,

+                    backgroundColor: '#fff',

+                    border: '1px solid #b2b2b2',

+                    borderRadius: 7,

+                    listStyle: 'none',

+                    margin: 0,

+                    padding: 0,

+                    zIndex: 10,

+                  }}

+                >

+                  {["男", "女", "跨性别男", "跨性别女", "无性别"].map((option) => (

+                    <li

+                      key={option}

+                      onClick={() => {

+                        setTempUserInfo({ ...tempUserInfo, gender: option, showGenderOptions: false });

+                      }}

+                      style={{

+                        padding: '6px 10px',

+                        cursor: 'pointer',

+                        borderBottom: '1px solid #e0e0e0',

+                        backgroundColor: option === tempUserInfo.gender ? '#f0f0f0' : '#fff',

+                      }}

+                    >

+                      {option}

+                    </li>

+                  ))}

+                </ul>

+              )}

+            </div>

+          </div>

+          <button

+            onClick={handleSave}

+            style={{

+              marginTop: 20,

+              padding: '10px 20px',

+              backgroundColor: '#1a237e',

+              color: '#fff',

+              border: 'none',

+              borderRadius: 7,

+              cursor: 'pointer',

+              fontSize: 16,

+              alignSelf: 'center', // Center the button horizontally

+            }}

+            onMouseOver={(e) => (e.target.style.backgroundColor = '#0d1b5e')}

+            onMouseOut={(e) => (e.target.style.backgroundColor = '#1a237e')}

+          >

+            保存

+          </button>

+        </div>

+      </div>

+      {/* 上传种子列表 */}

+      <div style={{ gridColumn: '2 / 3', gridRow: '1 / 2', background: '#fff', borderRadius: 18, boxShadow: '0 4px 24px #e0e7ff', padding: '20px' }}>

+        <h3 style={{ color: '#1a237e', fontSize: 22, marginBottom: 18 }}>个人上传种子列表</h3>

+        <div style={{ border: '1px dashed #b2b2b2', borderRadius: 12, height: '100%', display: 'flex', alignItems: 'center', justifyContent: 'center', color: '#b2b2b2', fontSize: 18 }}>

+          (此处显示上传种子列表)

+        </div>

+      </div>

+      {/* 活跃度模块 */}

+      <div style={{ gridColumn: '2 / 3', gridRow: '2 / 3', background: '#fff', borderRadius: 18, boxShadow: '0 4px 24px #e0e7ff', padding: '20px' }}>

+        <h3 style={{ color: '#1a237e', fontSize: 22, marginBottom: 18 }}>活跃度</h3>

+        <div style={{ border: '1px dashed #b2b2b2', borderRadius: 12, height: '100%', display: 'flex', alignItems: 'center', justifyContent: 'center', color: '#b2b2b2', fontSize: 18 }}>

+          (此处显示活跃度信息)

+        </div>

+      </div>

+    </div>

+  );

+}
\ No newline at end of file
diff --git a/front/src/index.css b/front/src/index.css
new file mode 100644
index 0000000..ec2585e
--- /dev/null
+++ b/front/src/index.css
@@ -0,0 +1,13 @@
+body {
+  margin: 0;
+  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
+    'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
+    sans-serif;
+  -webkit-font-smoothing: antialiased;
+  -moz-osx-font-smoothing: grayscale;
+}
+
+code {
+  font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
+    monospace;
+}
diff --git a/front/src/index.js b/front/src/index.js
new file mode 100644
index 0000000..d563c0f
--- /dev/null
+++ b/front/src/index.js
@@ -0,0 +1,17 @@
+import React from 'react';
+import ReactDOM from 'react-dom/client';
+import './index.css';
+import App from './App';
+import reportWebVitals from './reportWebVitals';
+
+const root = ReactDOM.createRoot(document.getElementById('root'));
+root.render(
+  <React.StrictMode>
+    <App />
+  </React.StrictMode>
+);
+
+// If you want to start measuring performance in your app, pass a function
+// to log results (for example: reportWebVitals(console.log))
+// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
+reportWebVitals();
diff --git a/front/src/logo.svg b/front/src/logo.svg
new file mode 100644
index 0000000..9dfc1c0
--- /dev/null
+++ b/front/src/logo.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 841.9 595.3"><g fill="#61DAFB"><path d="M666.3 296.5c0-32.5-40.7-63.3-103.1-82.4 14.4-63.6 8-114.2-20.2-130.4-6.5-3.8-14.1-5.6-22.4-5.6v22.3c4.6 0 8.3.9 11.4 2.6 13.6 7.8 19.5 37.5 14.9 75.7-1.1 9.4-2.9 19.3-5.1 29.4-19.6-4.8-41-8.5-63.5-10.9-13.5-18.5-27.5-35.3-41.6-50 32.6-30.3 63.2-46.9 84-46.9V78c-27.5 0-63.5 19.6-99.9 53.6-36.4-33.8-72.4-53.2-99.9-53.2v22.3c20.7 0 51.4 16.5 84 46.6-14 14.7-28 31.4-41.3 49.9-22.6 2.4-44 6.1-63.6 11-2.3-10-4-19.7-5.2-29-4.7-38.2 1.1-67.9 14.6-75.8 3-1.8 6.9-2.6 11.5-2.6V78.5c-8.4 0-16 1.8-22.6 5.6-28.1 16.2-34.4 66.7-19.9 130.1-62.2 19.2-102.7 49.9-102.7 82.3 0 32.5 40.7 63.3 103.1 82.4-14.4 63.6-8 114.2 20.2 130.4 6.5 3.8 14.1 5.6 22.5 5.6 27.5 0 63.5-19.6 99.9-53.6 36.4 33.8 72.4 53.2 99.9 53.2 8.4 0 16-1.8 22.6-5.6 28.1-16.2 34.4-66.7 19.9-130.1 62-19.1 102.5-49.9 102.5-82.3zm-130.2-66.7c-3.7 12.9-8.3 26.2-13.5 39.5-4.1-8-8.4-16-13.1-24-4.6-8-9.5-15.8-14.4-23.4 14.2 2.1 27.9 4.7 41 7.9zm-45.8 106.5c-7.8 13.5-15.8 26.3-24.1 38.2-14.9 1.3-30 2-45.2 2-15.1 0-30.2-.7-45-1.9-8.3-11.9-16.4-24.6-24.2-38-7.6-13.1-14.5-26.4-20.8-39.8 6.2-13.4 13.2-26.8 20.7-39.9 7.8-13.5 15.8-26.3 24.1-38.2 14.9-1.3 30-2 45.2-2 15.1 0 30.2.7 45 1.9 8.3 11.9 16.4 24.6 24.2 38 7.6 13.1 14.5 26.4 20.8 39.8-6.3 13.4-13.2 26.8-20.7 39.9zm32.3-13c5.4 13.4 10 26.8 13.8 39.8-13.1 3.2-26.9 5.9-41.2 8 4.9-7.7 9.8-15.6 14.4-23.7 4.6-8 8.9-16.1 13-24.1zM421.2 430c-9.3-9.6-18.6-20.3-27.8-32 9 .4 18.2.7 27.5.7 9.4 0 18.7-.2 27.8-.7-9 11.7-18.3 22.4-27.5 32zm-74.4-58.9c-14.2-2.1-27.9-4.7-41-7.9 3.7-12.9 8.3-26.2 13.5-39.5 4.1 8 8.4 16 13.1 24 4.7 8 9.5 15.8 14.4 23.4zM420.7 163c9.3 9.6 18.6 20.3 27.8 32-9-.4-18.2-.7-27.5-.7-9.4 0-18.7.2-27.8.7 9-11.7 18.3-22.4 27.5-32zm-74 58.9c-4.9 7.7-9.8 15.6-14.4 23.7-4.6 8-8.9 16-13 24-5.4-13.4-10-26.8-13.8-39.8 13.1-3.1 26.9-5.8 41.2-7.9zm-90.5 125.2c-35.4-15.1-58.3-34.9-58.3-50.6 0-15.7 22.9-35.6 58.3-50.6 8.6-3.7 18-7 27.7-10.1 5.7 19.6 13.2 40 22.5 60.9-9.2 20.8-16.6 41.1-22.2 60.6-9.9-3.1-19.3-6.5-28-10.2zM310 490c-13.6-7.8-19.5-37.5-14.9-75.7 1.1-9.4 2.9-19.3 5.1-29.4 19.6 4.8 41 8.5 63.5 10.9 13.5 18.5 27.5 35.3 41.6 50-32.6 30.3-63.2 46.9-84 46.9-4.5-.1-8.3-1-11.3-2.7zm237.2-76.2c4.7 38.2-1.1 67.9-14.6 75.8-3 1.8-6.9 2.6-11.5 2.6-20.7 0-51.4-16.5-84-46.6 14-14.7 28-31.4 41.3-49.9 22.6-2.4 44-6.1 63.6-11 2.3 10.1 4.1 19.8 5.2 29.1zm38.5-66.7c-8.6 3.7-18 7-27.7 10.1-5.7-19.6-13.2-40-22.5-60.9 9.2-20.8 16.6-41.1 22.2-60.6 9.9 3.1 19.3 6.5 28.1 10.2 35.4 15.1 58.3 34.9 58.3 50.6-.1 15.7-23 35.6-58.4 50.6zM320.8 78.4z"/><circle cx="420.9" cy="296.5" r="45.7"/><path d="M520.5 78.1z"/></g></svg>
\ No newline at end of file
diff --git a/front/src/reportWebVitals.js b/front/src/reportWebVitals.js
new file mode 100644
index 0000000..5253d3a
--- /dev/null
+++ b/front/src/reportWebVitals.js
@@ -0,0 +1,13 @@
+const reportWebVitals = onPerfEntry => {
+  if (onPerfEntry && onPerfEntry instanceof Function) {
+    import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {
+      getCLS(onPerfEntry);
+      getFID(onPerfEntry);
+      getFCP(onPerfEntry);
+      getLCP(onPerfEntry);
+      getTTFB(onPerfEntry);
+    });
+  }
+};
+
+export default reportWebVitals;
diff --git a/front/src/setupTests.js b/front/src/setupTests.js
new file mode 100644
index 0000000..8f2609b
--- /dev/null
+++ b/front/src/setupTests.js
@@ -0,0 +1,5 @@
+// jest-dom adds custom jest matchers for asserting on DOM nodes.
+// allows you to do things like:
+// expect(element).toHaveTextContent(/react/i)
+// learn more: https://github.com/testing-library/jest-dom
+import '@testing-library/jest-dom';