diff --git a/package-lock.json b/package-lock.json index c10c2d7..853c437 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,7 +11,8 @@ "@ant-design/icons": "^5.3.0", "antd": "^5.15.3", "react": "^18.2.0", - "react-dom": "^18.2.0" + "react-dom": "^18.2.0", + "react-router-dom": "^6.22.3" }, "devDependencies": { "@babel/core": "^7.23.7", @@ -2143,6 +2144,14 @@ "react-dom": ">=16.9.0" } }, + "node_modules/@remix-run/router": { + "version": "1.23.2", + "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.23.2.tgz", + "integrity": "sha512-Ic6m2U/rMjTkhERIa/0ZtXJP17QUi2CbWE7cqx4J58M8aA3QTfW+2UlQ4psvTX9IO1RfNVhK3pcpdjej7L+t2w==", + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/@rtsao/scc": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz", @@ -9206,6 +9215,36 @@ "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", "dev": true }, + "node_modules/react-router": { + "version": "6.30.3", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.30.3.tgz", + "integrity": "sha512-XRnlbKMTmktBkjCLE8/XcZFlnHvr2Ltdr1eJX4idL55/9BbORzyZEaIkBFDhFGCEWBBItsVrDxwx3gnisMitdw==", + "dependencies": { + "@remix-run/router": "1.23.2" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "react": ">=16.8" + } + }, + "node_modules/react-router-dom": { + "version": "6.30.3", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.30.3.tgz", + "integrity": "sha512-pxPcv1AczD4vso7G4Z3TKcvlxK7g7TNt3/FNGMhfqyntocvYKj+GCatfigGDjbLozC4baguJ0ReCigoDJXb0ag==", + "dependencies": { + "@remix-run/router": "1.23.2", + "react-router": "6.30.3" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "react": ">=16.8", + "react-dom": ">=16.8" + } + }, "node_modules/readable-stream": { "version": "3.6.2", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", @@ -12794,6 +12833,11 @@ "rc-util": "^5.44.0" } }, + "@remix-run/router": { + "version": "1.23.2", + "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.23.2.tgz", + "integrity": "sha512-Ic6m2U/rMjTkhERIa/0ZtXJP17QUi2CbWE7cqx4J58M8aA3QTfW+2UlQ4psvTX9IO1RfNVhK3pcpdjej7L+t2w==" + }, "@rtsao/scc": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz", @@ -17827,6 +17871,23 @@ "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", "dev": true }, + "react-router": { + "version": "6.30.3", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.30.3.tgz", + "integrity": "sha512-XRnlbKMTmktBkjCLE8/XcZFlnHvr2Ltdr1eJX4idL55/9BbORzyZEaIkBFDhFGCEWBBItsVrDxwx3gnisMitdw==", + "requires": { + "@remix-run/router": "1.23.2" + } + }, + "react-router-dom": { + "version": "6.30.3", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.30.3.tgz", + "integrity": "sha512-pxPcv1AczD4vso7G4Z3TKcvlxK7g7TNt3/FNGMhfqyntocvYKj+GCatfigGDjbLozC4baguJ0ReCigoDJXb0ag==", + "requires": { + "@remix-run/router": "1.23.2", + "react-router": "6.30.3" + } + }, "readable-stream": { "version": "3.6.2", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", diff --git a/package.json b/package.json index 0bc20fb..badc8da 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,8 @@ "@ant-design/icons": "^5.3.0", "antd": "^5.15.3", "react": "^18.2.0", - "react-dom": "^18.2.0" + "react-dom": "^18.2.0", + "react-router-dom": "^6.22.3" }, "devDependencies": { "@babel/core": "^7.23.7", diff --git a/src/App.jsx b/src/App.jsx index e5bf60e..8aaae9b 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -1,6 +1,15 @@ import React from "react"; +import { BrowserRouter, Route, Routes } from "react-router-dom"; import Home from "./components/home"; +import Explore from "./components/explore"; export default function App() { - return ; + return ( + + + } /> + } /> + + + ); } diff --git a/src/components/explore/content.jsx b/src/components/explore/content.jsx new file mode 100644 index 0000000..54440b5 --- /dev/null +++ b/src/components/explore/content.jsx @@ -0,0 +1,80 @@ +import React from "react"; +import { Card, Col, Row, Tabs, Typography } from "antd"; + +const { Title, Text } = Typography; + +const featureCards = [ + { title: "爆品追踪", desc: "最近有什么商品有机会爆单?" }, + { title: "竞对监控", desc: "竞争对手新品和策略变化随时掌握" }, + { title: "达人筛选", desc: "寻找最适合的联盟达人合作" }, + { title: "潜力类目", desc: "竞争小、机会大的类目是什么?" }, + { title: "爆款视频", desc: "最近哪些视频带货效果好?" }, + { title: "热门直播", desc: "识别爆单直播间,找到有效打法" } +]; + +const products = Array.from({ length: 5 }, (_, index) => ({ + id: `product-${index}`, + title: "热卖商品标题", + price: "$59.90", + sales: "成交金额 $348.87万", + volume: "销量 58243" +})); + +export default function ExploreContent() { + return ( +
+
+
+ + 如何使用Hoyidata + +
+ + {featureCards.map((item) => ( + + +
{item.title}
+
{item.desc}
+
+ + + ))} + +
+
+
+ +
+
+ + +
+ 热卖商品 + (01/02 ~ 01/31) +
+ + {products.map((item) => ( + + +
+
{item.title}
+
{item.price}
+
{item.sales}
+
{item.volume}
+ + + ))} + + +
+
+
+ ); +} diff --git a/src/components/explore/index.jsx b/src/components/explore/index.jsx new file mode 100644 index 0000000..59f27b4 --- /dev/null +++ b/src/components/explore/index.jsx @@ -0,0 +1,22 @@ +import React from "react"; +import { Layout } from "antd"; +import HomeHeader from "../home/header"; +import HomeFooter from "../home/footer"; +import ExploreContent from "./content"; +import "../../css/explore/index.css"; + +const { Header, Content } = Layout; + +export default function Explore() { + return ( + +
+ +
+ + + + +
+ ); +} diff --git a/src/components/home/header.jsx b/src/components/home/header.jsx index e090901..5fc0925 100644 --- a/src/components/home/header.jsx +++ b/src/components/home/header.jsx @@ -1,17 +1,126 @@ import React from "react"; import { Button, Divider, Dropdown, Menu, Space, Typography } from "antd"; import { DownOutlined } from "@ant-design/icons"; +import { useLocation, useNavigate } from "react-router-dom"; import logoWhite from "../../assets/images/redhare.png"; const { Text } = Typography; -const navItems = ["探索", "类目", "店铺", "达人", "商品", "视频与广告", "直播"]; +const navItems = [ + { key: "explore", label: "探索", path: "/explore" }, + { key: "category", label: "类目" }, + { key: "shop", label: "店铺" }, + { key: "creator", label: "达人" }, + { key: "product", label: "商品" }, + { key: "media", label: "视频与广告" }, + { key: "live", label: "直播" } +]; const langItems = [ { key: "zh", label: "中文(简体)" }, { key: "en", label: "English(US)" } ]; +const dataSources = [ + { + key: "tiktok", + name: "TikTok", + desc: "全球最火热的内容电商平台", + badge: "New", + icon: ( + + + + + + + + + + ) + }, + { + key: "amazon", + name: "Amazon", + desc: "全球最大的综合电商平台", + icon: () + }, + { + key: "shopee", + name: "Shopee", + desc: "东南亚最流行的电商平台", + badge: "Beta", + icon: ( + + + + ) + } +]; + export default function HomeHeader() { + const navigate = useNavigate(); + const location = useLocation(); + const selectedKey = location.pathname.startsWith("/explore") ? "explore" : ""; + const isHome = location.pathname === "/"; + const [activeSourceKey, setActiveSourceKey] = React.useState("tiktok"); + const activeSource = + dataSources.find((item) => item.key === activeSourceKey) || dataSources[0]; + const sourceMenu = { + items: dataSources.map((item) => ({ + key: item.key, + label: ( +
+ {item.icon || item.name.slice(0, 1)} +
+
{item.name}
+
{item.desc}
+
+ {item.key === activeSource.key && } +
+ ) + })), + onClick: ({ key }) => { + setActiveSourceKey(key); + } + }; + return ( <> + {!isHome && ( + + + + )} ({ - key: item, - label: item + key: item.key, + label: item.label }))} selectable={false} + selectedKeys={selectedKey ? [selectedKey] : []} + onClick={({ key }) => { + const target = navItems.find((item) => item.key === key); + if (target?.path) { + navigate(target.path); + } + }} />
diff --git a/src/css/explore/index.css b/src/css/explore/index.css new file mode 100644 index 0000000..ccbbca7 --- /dev/null +++ b/src/css/explore/index.css @@ -0,0 +1,122 @@ +.explore { + background: #f5f7fb; +} + +.explore-content { + padding-bottom: 40px; +} + +.explore-how { + background: linear-gradient(180deg, #e9f3ff 0%, #f7fbff 100%); + padding: 32px 0 24px; +} + +.explore-how-inner, +.explore-hot-inner { + width: 1200px; + margin: 0 auto; +} + +.explore-how-title { + margin: 0 0 16px; + font-weight: 600; +} + +.explore-how-card { + background: #ffffff; + border-radius: 16px; + padding: 20px; + box-shadow: 0 12px 30px rgba(15, 23, 42, 0.08); +} + +.explore-feature-card { + background: #f0f6ff; + border-radius: 12px; + padding: 18px; + min-height: 140px; +} + +.explore-feature-title { + font-weight: 600; + color: #1f2a37; +} + +.explore-feature-desc { + color: #6b7280; + font-size: 12px; + margin-top: 6px; +} + +.explore-feature-visual { + margin-top: 12px; + height: 46px; + border-radius: 8px; + background: #d9e9ff; +} + +.explore-hot { + padding: 24px 0 40px; +} + +.explore-hot-tabs { + margin-bottom: 16px; +} + +.explore-hot-card { + border-radius: 16px; + box-shadow: 0 12px 30px rgba(15, 23, 42, 0.08); +} + +.explore-hot-header { + display: flex; + align-items: center; + gap: 8px; + margin-bottom: 16px; +} + +.explore-hot-title { + font-weight: 600; + color: #1f2a37; +} + +.explore-hot-range { + color: #94a3b8; + font-size: 12px; +} + +.explore-product-card { + border-radius: 12px; + background: #fff; +} + +.explore-product-image { + height: 120px; + border-radius: 12px; + background: #f2f4f8; + margin-bottom: 10px; +} + +.explore-product-title { + font-size: 12px; + color: #1f2a37; + margin-bottom: 6px; +} + +.explore-product-price { + font-weight: 600; + color: #1f2a37; + margin-bottom: 6px; +} + +.explore-product-meta { + font-size: 11px; + color: #94a3b8; +} + +@media (max-width: 1200px) { + .explore-how-inner, + .explore-hot-inner { + width: auto; + padding: 0 24px; + } +} diff --git a/src/css/homecss/content.css b/src/css/homecss/content.css index c3a9e3b..64f1f89 100644 --- a/src/css/homecss/content.css +++ b/src/css/homecss/content.css @@ -110,9 +110,9 @@ .home-logos { margin-top: 24px; - height: 108px; + height: 92px; display: flex; - padding: 32px 48px; + padding: 16px 48px; align-items: center; overflow: hidden; background: #fff; @@ -123,7 +123,7 @@ .home-logos-track { display: flex; align-items: center; - gap: 40px; + gap: 32px; height: 100%; animation: home-logo-scroll 30s linear infinite; will-change: transform; diff --git a/src/css/homecss/header.css b/src/css/homecss/header.css index 4e5c0d6..325be60 100644 --- a/src/css/homecss/header.css +++ b/src/css/homecss/header.css @@ -30,6 +30,89 @@ transform-origin: center; } +.home-source-trigger { + border: 0; + background: transparent; + height: 28px; + padding: 0; + font-size: 0px; + line-height: 0px; + cursor: pointer; +} + +.home-source-chip { + display: inline-flex; + height: 28px; + width: 120px; + align-items: center; + justify-content: center; + background: #111827; + color: #fff; + border-radius: 14px; + font-size: 14px; +} + +.home-source-chip--shopee { + background: #f04e23; +} + +.home-source-icon svg { + width: 24px; + height: 24px; + border-radius: 4px; +} + +.home-source-text { + padding: 0 2px; + width: 52px; + font-weight: 600; + line-height: 28px !important; + height: 28px !important; +} + +.home-source-badge { + background: #ef4444; + color: #fff; + font-size: 10px; + padding: 1px 6px; + border-radius: 999px; + position: absolute; + right: 0px; + top: 0px; +} + +.home-source-caret { + font-size: 10px; + margin-left: 4px; +} + +.home-source-option { + display: flex; + align-items: center; + gap: 10px; +} + +.home-source-meta { + flex: 1; +} + +.home-source-name { + font-size: 13px; + font-weight: 600; + color: #111827; +} + +.home-source-desc { + font-size: 11px; + color: #6b7280; +} + +.home-source-check { + color: #2563eb; + font-size: 14px; + font-weight: 700; +} + .home-nav-menu { flex: 1; background: transparent;