From b0c9c193aa459d3ed3192b0b642319fdfb03c367 Mon Sep 17 00:00:00 2001 From: jimi <1847930177@qq.com> Date: Sun, 8 Mar 2026 20:18:27 +0800 Subject: [PATCH] feat: refresh storefront ui --- frontend/src/App.css | 51 +- frontend/src/components/Header.css | 107 +-- frontend/src/components/Hero.css | 480 ++++++++---- frontend/src/components/Hero.jsx | 225 ++++-- frontend/src/components/Works.css | 361 ++++----- frontend/src/components/Works.jsx | 276 ++++--- frontend/src/index.css | 224 +----- frontend/src/pages/Home.jsx | 28 +- frontend/src/pages/WorkDetail.css | 1106 ++++++++++++++++------------ frontend/src/pages/WorkDetail.jsx | 980 ++++++++++++------------ 10 files changed, 2119 insertions(+), 1719 deletions(-) diff --git a/frontend/src/App.css b/frontend/src/App.css index 62522e7..d3c42df 100644 --- a/frontend/src/App.css +++ b/frontend/src/App.css @@ -1,15 +1,13 @@ -@import url('https://fonts.googleapis.com/css2?family=Noto+Sans+SC:wght@300;400;500;700&display=swap'); - -* { - margin: 0; - padding: 0; - box-sizing: border-box; -} +@import url('https://fonts.googleapis.com/css2?family=Noto+Sans+SC:wght@400;500;700;800&display=swap'); body { - font-family: 'Noto Sans SC', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; - background: #f5f5f5; - color: #333; + font-family: 'Noto Sans SC', 'PingFang SC', 'Microsoft YaHei', sans-serif; + color: var(--ink); +} + +#root, +.App { + min-height: 100vh; } .app { @@ -25,32 +23,17 @@ body { width: 100%; } -/* Scrollbar */ -::-webkit-scrollbar { - width: 8px; - height: 8px; -} - -::-webkit-scrollbar-track { - background: #f1f1f1; -} - -::-webkit-scrollbar-thumb { - background: #c1c1c1; - border-radius: 4px; -} - -::-webkit-scrollbar-thumb:hover { - background: #a8a8a8; -} - -/* Ant Design Overrides */ .ant-btn-primary { - background: #ff5a5a; - border-color: #ff5a5a; + background: linear-gradient(135deg, var(--brand) 0%, var(--brand-strong) 100%); + border-color: transparent; + box-shadow: none; } .ant-btn-primary:hover { - background: #ff7070 !important; - border-color: #ff7070 !important; + background: linear-gradient(135deg, #f07869 0%, #df5545 100%) !important; + border-color: transparent !important; +} + +.ant-tag { + border-radius: 999px; } diff --git a/frontend/src/components/Header.css b/frontend/src/components/Header.css index aa82781..f291938 100644 --- a/frontend/src/components/Header.css +++ b/frontend/src/components/Header.css @@ -1,9 +1,11 @@ -.header { - background: #fff; - box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06); - position: fixed; - top: 0; - left: 0; +.header { + background: rgba(255, 251, 246, 0.86); + backdrop-filter: blur(18px); + border-bottom: 1px solid rgba(233, 221, 212, 0.88); + box-shadow: 0 14px 34px rgba(61, 40, 24, 0.05); + position: fixed; + top: 0; + left: 0; right: 0; z-index: 1000; } @@ -34,17 +36,17 @@ height: 24px; } -.logo-text { - font-size: 20px; - font-weight: 700; - color: #ff5a5a; -} - -.logo-domain { - font-size: 10px; - color: #999; - margin-left: 4px; -} +.logo-text { + font-size: 20px; + font-weight: 700; + color: #ef6a5b; +} + +.logo-domain { + font-size: 10px; + color: #8d8074; + margin-left: 4px; +} .header-nav { display: flex; @@ -52,20 +54,20 @@ gap: 24px; } -.nav-item { - color: #333; - font-size: 14px; - cursor: pointer; - transition: color 0.2s; +.nav-item { + color: #3b3027; + font-size: 14px; + cursor: pointer; + transition: color 0.2s; display: flex; align-items: center; gap: 4px; position: relative; } -.nav-item:hover { - color: #ff5a5a; -} +.nav-item:hover { + color: #ef6a5b; +} .nav-new { position: relative; @@ -107,10 +109,11 @@ } .btn-invite { - background: #ff5a5a; - border-color: #ff5a5a; - border-radius: 20px; - padding: 4px 20px; + background: #ff5a5a; + background: linear-gradient(135deg, #ef6a5b 0%, #d84e3f 100%); + border-color: #ff5a5a; + border-radius: 20px; + padding: 4px 20px; height: 36px; } @@ -120,30 +123,32 @@ } .btn-register { - border-radius: 20px; - padding: 4px 20px; - height: 36px; - border-color: #333; - color: #333; -} - -.btn-register:hover { - border-color: #ff5a5a !important; - color: #ff5a5a !important; -} + border-radius: 20px; + padding: 4px 20px; + height: 36px; + border-color: #d9cabc; + color: #3b3027; + background: rgba(255, 255, 255, 0.72); +} + +.btn-register:hover { + border-color: #ef6a5b !important; + color: #ef6a5b !important; +} .btn-login { - border-radius: 20px; - padding: 4px 20px; - height: 36px; - border-color: #333; - color: #333; -} - -.btn-login:hover { - border-color: #ff5a5a !important; - color: #ff5a5a !important; -} + border-radius: 20px; + padding: 4px 20px; + height: 36px; + border-color: #d9cabc; + color: #3b3027; + background: rgba(255, 255, 255, 0.72); +} + +.btn-login:hover { + border-color: #ef6a5b !important; + color: #ef6a5b !important; +} @media (max-width: 1024px) { .header-nav { diff --git a/frontend/src/components/Hero.css b/frontend/src/components/Hero.css index 56b2984..126958f 100644 --- a/frontend/src/components/Hero.css +++ b/frontend/src/components/Hero.css @@ -1,177 +1,381 @@ -/* ========== Hero 区块整体样式 ========== */ -.hero-section { +.hero { position: relative; - height: 600px; + padding: 32px 20px 24px; +} + +.hero-shell { + max-width: 1400px; + margin: 0 auto; + padding: 44px; + border-radius: 36px; + background: + radial-gradient(circle at top left, rgba(239, 106, 91, 0.16), transparent 36%), + radial-gradient(circle at bottom right, rgba(244, 178, 84, 0.22), transparent 32%), + linear-gradient(135deg, #fffaf4 0%, #fff3e7 54%, #fffdf8 100%); + border: 1px solid rgba(225, 205, 186, 0.7); + box-shadow: 0 28px 80px rgba(76, 48, 30, 0.12); + display: grid; + grid-template-columns: minmax(0, 1.2fr) minmax(360px, 0.8fr); + gap: 28px; + overflow: hidden; + position: relative; +} + +.hero-shell::before { + content: ''; + position: absolute; + inset: 0; + background: + linear-gradient(120deg, rgba(255, 255, 255, 0.55), transparent 35%), + linear-gradient(305deg, rgba(255, 255, 255, 0.42), transparent 30%); + pointer-events: none; +} + +.hero-copy, +.hero-stage { + position: relative; + z-index: 1; +} + +.hero-copy { display: flex; - align-items: center; + flex-direction: column; justify-content: center; - overflow: hidden; - background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); - animation: fadeIn 1s ease-out; + gap: 22px; } -/* ========== 动态背景 ========== */ -.hero-background { - position: absolute; - top: 0; - left: 0; - right: 0; - bottom: 0; - overflow: hidden; -} - -.hero-particle { - position: absolute; - width: 4px; - height: 4px; - background: rgba(255, 255, 255, 0.3); - border-radius: 50%; - animation: float 6s ease-in-out infinite; -} - -.hero-particle:nth-child(1) { left: 10%; animation-delay: 0s; } -.hero-particle:nth-child(2) { left: 20%; animation-delay: 1s; } -.hero-particle:nth-child(3) { left: 30%; animation-delay: 2s; } -.hero-particle:nth-child(4) { left: 40%; animation-delay: 3s; } -.hero-particle:nth-child(5) { left: 50%; animation-delay: 4s; } -.hero-particle:nth-child(6) { left: 60%; animation-delay: 5s; } -.hero-particle:nth-child(7) { left: 70%; animation-delay: 6s; } -.hero-particle:nth-child(8) { left: 80%; animation-delay: 7s; } -.hero-particle:nth-child(9) { left: 90%; animation-delay: 8s; } - -/* ========== Hero 内容 ========== */ -.hero-content { - position: relative; - z-index: 2; - text-align: center; - color: white; - max-width: 800px; - padding: 0 20px; - animation: slideIn 1s ease-out; +.hero-eyebrow { + display: inline-flex; + align-items: center; + width: fit-content; + padding: 8px 14px; + border-radius: 999px; + background: rgba(255, 255, 255, 0.82); + border: 1px solid rgba(228, 202, 181, 0.9); + color: #b55d43; + font-size: 13px; + font-weight: 700; + letter-spacing: 0.08em; } .hero-title { - font-size: 56px; + margin: 0; + color: #211d18; + font-size: clamp(38px, 6vw, 64px); + line-height: 1.05; font-weight: 800; - margin-bottom: 20px; - line-height: 1.2; - text-shadow: 0 4px 20px rgba(0, 0, 0, 0.2); + max-width: 11ch; } -.hero-subtitle { - font-size: 20px; - margin-bottom: 40px; - opacity: 0.95; - line-height: 1.6; +.hero-title span { + display: block; + color: #ef6a5b; } -/* ========== Hero 按钮 ========== */ -.hero-buttons { +.hero-description { + max-width: 620px; + margin: 0; + font-size: 17px; + line-height: 1.9; + color: #6f665d; +} + +.hero-search-panel { display: flex; - gap: 20px; - justify-content: center; + flex-direction: column; + gap: 16px; +} + +.hero-search-box { + padding: 14px; + border-radius: 26px; + background: rgba(255, 255, 255, 0.92); + border: 1px solid rgba(230, 215, 202, 0.92); + box-shadow: 0 16px 30px rgba(70, 44, 26, 0.08); +} + +.hero-search-input.ant-input-affix-wrapper { + height: 62px; + border: none; + box-shadow: none; + padding: 0 10px; + background: transparent; +} + +.hero-search-input.ant-input-affix-wrapper input { + font-size: 16px; + color: #2d261f; +} + +.hero-search-input.ant-input-affix-wrapper input::placeholder { + color: #a19488; +} + +.hero-search-prefix, +.hero-search-camera { + color: #b18a72; + font-size: 18px; +} + +.hero-action-row { + display: flex; + align-items: center; + gap: 12px; flex-wrap: wrap; } -.btn-hero-primary { - background: white; - color: #667eea; - padding: 16px 40px; - border-radius: 30px; - font-size: 18px; - font-weight: 700; +.hero-primary-btn.ant-btn { + height: 48px; + padding: 0 22px; + border-radius: 999px; border: none; - cursor: pointer; - transition: all 0.3s ease; - box-shadow: 0 10px 30px rgba(0, 0, 0, 0.2); -} - -.btn-hero-primary:hover { - transform: translateY(-3px); - box-shadow: 0 15px 40px rgba(0, 0, 0, 0.3); -} - -.btn-hero-secondary { - background: transparent; - color: white; - padding: 16px 40px; - border-radius: 30px; - font-size: 18px; + background: linear-gradient(135deg, #ef6a5b 0%, #d84e3f 100%); + box-shadow: 0 14px 30px rgba(216, 78, 63, 0.26); font-weight: 700; - border: 2px solid white; +} + +.hero-primary-btn.ant-btn:hover { + transform: translateY(-1px); +} + +.hero-secondary-btn.ant-btn { + height: 48px; + padding: 0 22px; + border-radius: 999px; + border: 1px solid rgba(182, 149, 120, 0.42); + background: rgba(255, 255, 255, 0.72); + color: #4d3f31; + font-weight: 600; +} + +.hero-secondary-btn.ant-btn:hover { + color: #ef6a5b !important; + border-color: rgba(239, 106, 91, 0.45) !important; +} + +.hero-tags { + display: flex; + flex-wrap: wrap; + align-items: center; + gap: 10px; +} + +.hero-tags-label { + font-size: 13px; + font-weight: 700; + color: #7d7267; +} + +.hero-tag.ant-tag { + margin: 0; + padding: 8px 14px; + border-radius: 999px; + border: 1px solid rgba(221, 200, 182, 0.95); + background: rgba(255, 255, 255, 0.8); + color: #4b4036; cursor: pointer; - transition: all 0.3s ease; + transition: all 0.2s ease; } -.btn-hero-secondary:hover { - background: rgba(255, 255, 255, 0.1); - transform: translateY(-3px); +.hero-tag.ant-tag:hover { + color: #ef6a5b; + border-color: rgba(239, 106, 91, 0.45); + background: #fff4ef; } -/* ========== 装饰元素 ========== */ -.hero-decoration { - position: absolute; - width: 400px; - height: 400px; - border: 2px solid rgba(255, 255, 255, 0.1); - border-radius: 50%; - animation: float 8s ease-in-out infinite; +.hero-stats { + display: grid; + grid-template-columns: repeat(3, minmax(0, 1fr)); + gap: 14px; + max-width: 520px; } -.hero-decoration-1 { - top: -100px; - right: -100px; +.hero-stat { + padding: 18px 16px; + border-radius: 22px; + background: rgba(255, 255, 255, 0.72); + border: 1px solid rgba(228, 208, 190, 0.88); + display: flex; + flex-direction: column; + gap: 6px; } -.hero-decoration-2 { - bottom: -150px; - left: -150px; - width: 300px; - height: 300px; - animation-delay: 2s; +.hero-stat strong { + font-size: 28px; + line-height: 1; + color: #211d18; } -/* ========== 渐变遮罩 ========== */ -.hero-overlay { - position: absolute; - top: 0; - left: 0; - right: 0; - bottom: 0; - background: radial-gradient(circle at center, transparent 0%, rgba(102, 126, 234, 0.3) 100%); +.hero-stat span { + font-size: 13px; + color: #7d7267; } -/* ========== 响应式设计 ========== */ -@media (max-width: 768px) { - .hero-section { - height: 500px; +.hero-stage { + display: flex; + flex-direction: column; + gap: 16px; + justify-content: center; +} + +.hero-stage-card { + border-radius: 28px; + border: 1px solid rgba(229, 210, 192, 0.9); + background: rgba(255, 255, 255, 0.72); + box-shadow: 0 18px 36px rgba(66, 43, 27, 0.08); +} + +.hero-stage-main { + padding: 28px; + min-height: 280px; + display: flex; + flex-direction: column; + justify-content: space-between; + background: + radial-gradient(circle at top right, rgba(244, 178, 84, 0.2), transparent 35%), + linear-gradient(180deg, rgba(255, 255, 255, 0.95), rgba(255, 248, 241, 0.94)); +} + +.hero-stage-topline { + display: flex; + justify-content: space-between; + gap: 12px; + flex-wrap: wrap; +} + +.hero-stage-label, +.hero-stage-badge { + padding: 8px 12px; + border-radius: 999px; + font-size: 12px; + font-weight: 700; +} + +.hero-stage-label { + background: rgba(239, 106, 91, 0.12); + color: #d84e3f; +} + +.hero-stage-badge { + background: rgba(47, 103, 81, 0.12); + color: #2f6751; +} + +.hero-stage-main h2 { + margin: 0; + font-size: 30px; + line-height: 1.2; + color: #211d18; +} + +.hero-stage-main p { + margin: 0; + color: #6f665d; + line-height: 1.9; +} + +.hero-stage-pills { + display: flex; + flex-wrap: wrap; + gap: 10px; +} + +.hero-stage-pills span { + padding: 9px 14px; + border-radius: 999px; + background: #fff; + border: 1px solid rgba(229, 210, 192, 0.85); + color: #594c40; + font-size: 13px; + font-weight: 600; +} + +.hero-stage-stack { + display: grid; + grid-template-columns: 1fr; + gap: 12px; +} + +.hero-stage-mini { + display: grid; + grid-template-columns: 46px minmax(0, 1fr); + gap: 14px; + align-items: start; + padding: 18px 20px; +} + +.hero-stage-icon { + width: 46px; + height: 46px; + border-radius: 16px; + background: linear-gradient(135deg, #fff3ed 0%, #ffe2d5 100%); + color: #e35c4e; + display: grid; + place-items: center; + font-size: 20px; +} + +.hero-stage-mini h3 { + margin: 0 0 6px; + font-size: 17px; + color: #211d18; +} + +.hero-stage-mini p { + margin: 0; + color: #776c60; + line-height: 1.75; + font-size: 14px; +} + +@media (max-width: 1200px) { + .hero-shell { + grid-template-columns: 1fr; + padding: 34px; } - + .hero-title { - font-size: 36px; - } - - .hero-subtitle { - font-size: 16px; - } - - .hero-buttons { - flex-direction: column; - gap: 15px; - } - - .btn-hero-primary, - .btn-hero-secondary { - padding: 14px 30px; - font-size: 16px; - width: 100%; - max-width: 300px; + max-width: none; } } -/* ========== 暗黑模式 ========== */ -@media (prefers-color-scheme: dark) { - .hero-section { - background: linear-gradient(135deg, #1a1a2e 0%, #16213e 100%); +@media (max-width: 768px) { + .hero { + padding: 20px 12px 10px; + } + + .hero-shell { + padding: 24px 18px; + border-radius: 28px; + } + + .hero-title { + font-size: 34px; + } + + .hero-description { + font-size: 15px; + } + + .hero-search-box { + padding: 10px 12px; + } + + .hero-search-input.ant-input-affix-wrapper { + height: 52px; + } + + .hero-stats { + grid-template-columns: 1fr; + max-width: none; + } + + .hero-stage-main { + min-height: auto; + padding: 22px; + } + + .hero-stage-main h2 { + font-size: 24px; } } diff --git a/frontend/src/components/Hero.jsx b/frontend/src/components/Hero.jsx index 830fbbe..99d55a6 100644 --- a/frontend/src/components/Hero.jsx +++ b/frontend/src/components/Hero.jsx @@ -1,76 +1,149 @@ -import { Input, Button, Tag } from 'antd'; -import { SearchOutlined, CameraOutlined } from '@ant-design/icons'; -import './Hero.css'; - -const Hero = () => { - const recommendTags = ['地产', '医美', '旅游', '汽车', '价值点', '美陈', '电商', '画册', '大寒']; - - return ( -
-
- {/* Winter Scene Illustration */} -
-
- {[...Array(20)].map((_, i) => ( -
- ))} -
- - {/* Left Side - Title */} -
-

这个冬天

-

相约雪山

-

APPOINTMENT AT SNOWMOUNTAIN

-
- - {/* Decorative Elements */} -
-
-
-
🌲
-
🌲
-
🎄
-
-
🎿
-
🛷
-
-
-
- -
-

有所想,不如有所享!

- -
- - -
- } - /> -
- -
- 推荐搜索: - {recommendTags.map(tag => ( - {tag} - ))} -
- -
- 设计师:M.A -
- -
- ); -}; - -export default Hero; +import { Input, Button, Tag } from 'antd'; +import { + SearchOutlined, + CameraOutlined, + ArrowRightOutlined, + ThunderboltOutlined, + SafetyCertificateOutlined, + ClockCircleOutlined, +} from '@ant-design/icons'; +import { useNavigate } from 'react-router-dom'; +import './Hero.css'; + +const recommendTags = ['活动', '医美', '科技', '包装', '直播', '邀请函']; +const heroStats = [ + { value: '24h', label: '持续更新' }, + { value: '原图', label: '即时交付' }, + { value: '在线', label: '支付下载' }, +]; +const stageHighlights = [ + { + icon: , + title: '热门原图', + description: '按场景、风格和行业快速找图,减少反复沟通。', + }, + { + icon: , + title: '支付后交付', + description: '下单、支付、下载一条链路走通,用户体验更顺。', + }, + { + icon: , + title: '最新上传', + description: '设计师持续上新,支持详情页预览和作品编号追踪。', + }, +]; + +const Hero = () => { + const navigate = useNavigate(); + + const scrollToHotWorks = () => { + document.getElementById('home-hot-works')?.scrollIntoView({ + behavior: 'smooth', + block: 'start', + }); + }; + + return ( +
+
+
+ 图汇精选素材库 +

+ 把灵感整理成 + 可直接下载的设计资产 +

+

+ 原图、背景、电商图、活动物料统一归档,预览、支付、下载三步走通, + 让客户从找图到拿到成品更省心。 +

+ +
+
+ } + suffix={} + /> +
+
+ + +
+
+ +
+ 推荐分类 + {recommendTags.map((tag) => ( + navigate(`/category/${tag}`)} + > + {tag} + + ))} +
+ +
+ {heroStats.map((item) => ( +
+ {item.value} + {item.label} +
+ ))} +
+
+ +
+
+
+ 本周趋势 + 精选专题 +
+

更像一个能直接成交的素材站,而不是简单图库

+

+ 首页负责承接搜索意图,详情页负责转化下载动作,让用户一眼知道 + “这里能找、能买、能下”。 +

+
+ 高清原图 + 在线支付 + 作品详情页 +
+
+ +
+ {stageHighlights.map((item) => ( +
+
{item.icon}
+
+

{item.title}

+

{item.description}

+
+
+ ))} +
+
+
+
+ ); +}; + +export default Hero; diff --git a/frontend/src/components/Works.css b/frontend/src/components/Works.css index 5bb4afe..4e10431 100644 --- a/frontend/src/components/Works.css +++ b/frontend/src/components/Works.css @@ -1,90 +1,119 @@ -/* ========== 作品区块整体样式 ========== */ .works-section { - padding: 60px 20px; max-width: 1400px; margin: 0 auto; - animation: fadeIn 0.8s ease-out; + padding: 54px 20px; +} + +.works-loading { + display: flex; + justify-content: center; + align-items: center; + min-height: 220px; + border-radius: 28px; + background: rgba(255, 255, 255, 0.72); + border: 1px solid rgba(231, 218, 207, 0.85); } -/* ========== 区块标题 ========== */ .section-header { display: flex; justify-content: space-between; - align-items: center; - margin-bottom: 40px; - padding: 0 10px; + align-items: flex-end; + gap: 24px; + margin-bottom: 28px; +} + +.section-copy { + max-width: 760px; +} + +.section-kicker { + display: inline-flex; + margin-bottom: 12px; + padding: 7px 12px; + border-radius: 999px; + background: rgba(239, 106, 91, 0.1); + color: #d84e3f; + font-size: 12px; + font-weight: 700; + letter-spacing: 0.08em; } .section-title { - font-size: 32px; - font-weight: 700; - color: #2d3436; - position: relative; - padding-left: 20px; - animation: slideIn 0.6s ease-out; + margin: 0; + font-size: clamp(28px, 4vw, 38px); + line-height: 1.1; + color: #221d18; } -.section-title::before { - content: ''; - position: absolute; - left: 0; - top: 50%; - transform: translateY(-50%); - width: 6px; - height: 32px; - background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); - border-radius: 3px; +.section-description { + margin: 12px 0 0; + font-size: 15px; + line-height: 1.85; + color: #756b61; } -.view-more { - color: #667eea; - font-size: 16px; - font-weight: 600; - text-decoration: none; - transition: all 0.3s ease; +.section-summary { display: flex; - align-items: center; - gap: 6px; - padding: 8px 16px; - border-radius: 20px; - background: rgba(102, 126, 234, 0.1); + flex-wrap: wrap; + gap: 10px; + justify-content: flex-end; } -.view-more:hover { - background: rgba(102, 126, 234, 0.2); - transform: translateX(5px); +.section-pill { + padding: 10px 14px; + border-radius: 999px; + background: rgba(255, 255, 255, 0.84); + border: 1px solid rgba(232, 220, 209, 0.9); + color: #564a40; + font-size: 13px; + font-weight: 600; +} + +.section-pill-accent { + background: linear-gradient(135deg, #fff0e7 0%, #ffe1d7 100%); + color: #d84e3f; } -/* ========== 作品网格布局 ========== */ .works-grid { - animation: scaleIn 0.8s ease-out; + animation: fadeIn 0.6s ease-out; } -/* ========== 作品卡片 ========== */ -.work-card { - border-radius: 16px; +.work-card.ant-card { overflow: hidden; - border: none; - background: white; - box-shadow: 0 4px 20px rgba(0, 0, 0, 0.08); - transition: all 0.4s cubic-bezier(0.175, 0.885, 0.32, 1.275); - cursor: pointer; + border: 1px solid rgba(233, 221, 212, 0.92); + border-radius: 24px; + background: rgba(255, 255, 255, 0.94); + box-shadow: 0 18px 40px rgba(58, 38, 23, 0.08); + transition: transform 0.28s ease, box-shadow 0.28s ease, border-color 0.28s ease; animation: fadeIn 0.6s ease-out; + animation-delay: var(--delay); animation-fill-mode: both; } -.work-card:hover { - transform: translateY(-10px) scale(1.02); - box-shadow: 0 20px 40px rgba(0, 0, 0, 0.15); +.work-card.ant-card:hover { + transform: translateY(-8px); + border-color: rgba(239, 106, 91, 0.3); + box-shadow: 0 26px 48px rgba(58, 38, 23, 0.14); +} + +.work-card .ant-card-body { + padding: 20px; } -/* ========== 作品图片容器 ========== */ .work-image { position: relative; - width: 100%; - height: 280px; + height: 290px; overflow: hidden; - background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%); + background: linear-gradient(135deg, #f7ede2 0%, #efe2d4 100%); +} + +.work-image::after { + content: ''; + position: absolute; + inset: auto 0 0; + height: 40%; + background: linear-gradient(180deg, transparent, rgba(29, 22, 17, 0.28)); + pointer-events: none; } .work-image img { @@ -95,181 +124,185 @@ } .work-card:hover .work-image img { - transform: scale(1.1); + transform: scale(1.06); } -/* ========== 预览遮罩层 ========== */ -.work-preview { +.work-image-overlay { position: absolute; - top: 0; - left: 0; - right: 0; - bottom: 0; - background: rgba(102, 126, 234, 0.8); + inset: auto 18px 18px 18px; + z-index: 2; display: flex; align-items: center; - justify-content: center; + justify-content: space-between; + gap: 10px; + padding: 12px 14px; + border-radius: 16px; + background: rgba(28, 24, 20, 0.58); + color: #fff; + font-size: 14px; + font-weight: 600; opacity: 0; - transition: all 0.3s ease; + transform: translateY(10px); + transition: opacity 0.25s ease, transform 0.25s ease; } -.work-card:hover .work-preview { +.work-card:hover .work-image-overlay { opacity: 1; + transform: translateY(0); } -.preview-icon { - font-size: 48px; - transform: scale(0.5); - transition: transform 0.3s ease; +.work-price-badge { + position: absolute; + top: 16px; + right: 16px; + z-index: 2; + padding: 8px 12px; + border-radius: 999px; + background: rgba(255, 250, 244, 0.92); + border: 1px solid rgba(233, 218, 207, 0.92); + color: #d84e3f; + font-size: 14px; + font-weight: 700; + backdrop-filter: blur(10px); } -.work-card:hover .preview-icon { - transform: scale(1); -} - -/* ========== 作品信息 ========== */ .work-info { - padding: 20px; - background: white; + display: flex; + flex-direction: column; + gap: 14px; +} + +.work-topline { + display: flex; + justify-content: space-between; + align-items: center; + gap: 12px; +} + +.work-category.ant-tag { + margin: 0; + padding: 6px 12px; + border-radius: 999px; + border: 1px solid rgba(226, 209, 196, 0.9); + background: #fff9f4; + color: #7b6655; + font-size: 12px; + font-weight: 700; +} + +.work-id { + color: #a09083; + font-size: 12px; + font-weight: 600; + letter-spacing: 0.04em; } .work-title { - font-size: 16px; - font-weight: 600; - color: #2d3436; - margin-bottom: 12px; - line-height: 1.5; + margin: 0; + color: #241e19; + font-size: 18px; + line-height: 1.45; + font-weight: 700; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; - transition: color 0.3s ease; } -.work-card:hover .work-title { - color: #667eea; -} - -/* ========== 作品元数据 ========== */ .work-meta { display: flex; align-items: center; gap: 10px; - margin-bottom: 12px; flex-wrap: wrap; } .work-designer { font-size: 13px; - color: #636e72; - font-weight: 500; + color: #72675d; + font-weight: 600; } -.work-level { +.work-level.ant-tag { + margin: 0; + padding: 4px 10px; + border-radius: 999px; + background: transparent; font-size: 12px; - font-weight: 600; - padding: 3px 10px; - border-radius: 12px; - border: 1px solid; + font-weight: 700; } .work-level-text { font-size: 12px; - color: #b2bec3; + color: #a29488; } -/* ========== 作品价格 ========== */ -.work-price { - font-size: 20px; - font-weight: 700; - color: #ff5a5a; +.work-bottom { display: flex; align-items: center; - gap: 4px; + justify-content: space-between; + gap: 14px; + padding-top: 6px; + border-top: 1px solid rgba(239, 230, 222, 0.95); } -.work-price::before { - content: '¥'; - font-size: 14px; +.work-price { + color: #db5748; + font-size: 24px; + font-weight: 800; + line-height: 1; } -/* ========== 卡片延迟动画 ========== */ -.work-card:nth-child(1) { animation-delay: 0.05s; } -.work-card:nth-child(2) { animation-delay: 0.1s; } -.work-card:nth-child(3) { animation-delay: 0.15s; } -.work-card:nth-child(4) { animation-delay: 0.2s; } -.work-card:nth-child(5) { animation-delay: 0.25s; } -.work-card:nth-child(6) { animation-delay: 0.3s; } -.work-card:nth-child(7) { animation-delay: 0.35s; } -.work-card:nth-child(8) { animation-delay: 0.4s; } -.work-card:nth-child(9) { animation-delay: 0.45s; } +.work-cta { + display: inline-flex; + align-items: center; + gap: 6px; + color: #6d6258; + font-size: 13px; + font-weight: 700; +} + +.work-card:hover .work-cta { + color: #ef6a5b; +} -/* ========== 加载状态 ========== */ .works-section .ant-spin { - color: #667eea; + color: #ef6a5b; } .works-section .ant-spin-text { - color: #636e72; - font-size: 14px; + color: #756b61; margin-top: 10px; } -/* ========== 空状态 ========== */ -.works-empty { - text-align: center; - padding: 60px 20px; - color: #b2bec3; +@media (max-width: 900px) { + .section-header { + flex-direction: column; + align-items: flex-start; + } + + .section-summary { + justify-content: flex-start; + } } -.works-empty-icon { - font-size: 64px; - margin-bottom: 20px; - opacity: 0.5; -} - -.works-empty-text { - font-size: 16px; -} - -/* ========== 响应式设计 ========== */ @media (max-width: 768px) { .works-section { - padding: 40px 15px; + padding: 42px 12px; } - + .section-title { - font-size: 24px; + font-size: 28px; } - - .work-image { - height: 200px; - } - - .work-title { + + .section-description { font-size: 14px; } - - .work-price { - font-size: 18px; - } -} -/* ========== 暗黑模式 ========== */ -@media (prefers-color-scheme: dark) { - .work-card { - background: #2d2d44; + .work-image { + height: 250px; } - - .work-title { - color: #e0e0e0; - } - - .work-designer { - color: #a0a0a0; - } - - .work-card:hover .work-title { - color: #667eea; + + .work-card .ant-card-body { + padding: 18px; } } diff --git a/frontend/src/components/Works.jsx b/frontend/src/components/Works.jsx index 2c34dc9..6d5fa6d 100644 --- a/frontend/src/components/Works.jsx +++ b/frontend/src/components/Works.jsx @@ -1,120 +1,156 @@ -import { useState, useEffect } from 'react'; -import { Card, Tag, Row, Col, Spin } from 'antd'; -import { RightOutlined } from '@ant-design/icons'; -import { useNavigate } from 'react-router-dom'; -import { getWorksList } from '../api/works'; -import { API_CONFIG } from '../utils/config'; -import './Works.css'; - -const Works = ({ title, type, categoryFilter }) => { - const navigate = useNavigate(); - const [works, setWorks] = useState([]); - const [loading, setLoading] = useState(true); - - useEffect(() => { - loadWorks(); - }, [type, categoryFilter]); - - const loadWorks = async () => { - setLoading(true); - const result = await getWorksList(1, 9, categoryFilter || ''); - setLoading(false); - - if (result.success) { - setWorks(result.data.items || []); - } - }; - - // 使用 Picsum 随机图片(更稳定) - const getImageUrl = (id, width = 400, height = 300) => { - return `https://picsum.photos/seed/${id}/${width}/${height}`; - }; - - const getLevelColor = (level) => { - const colors = { - 1: '#999', - 2: '#74b9ff', - 3: '#00b894', - 4: '#6c5ce7', - 5: '#e17055', - 6: '#ff5a5a', - }; - return colors[level] || '#999'; - }; - - // 点击卡片,跳转到详情页 - const handleCardClick = (work) => { - navigate(`/detail/${work.id}`); - }; - - if (loading) { - return ( -
-
- -
-
- ); - } - - return ( -
-
-

{title}

- - 查看更多 - -
- - - {works.map((work, index) => ( - - handleCardClick(work)} - cover={ -
- {work.title} { - e.target.src = getImageUrl(work.id); - }} - /> -
- 🔍 -
-
- } - > -
-

{work.title}

-
- {work.designer} - - Lv.{work.level} - - {work.level_text} -
-
- ¥{work.price} -
-
-
- - ))} -
-
- ); -}; - -export default Works; +import { useState, useEffect } from 'react'; +import { Card, Tag, Row, Col, Spin } from 'antd'; +import { RightOutlined } from '@ant-design/icons'; +import { useNavigate } from 'react-router-dom'; +import { getWorksList } from '../api/works'; +import { API_CONFIG } from '../utils/config'; +import './Works.css'; + +const sectionPresets = { + hot: { + kicker: '精选推荐', + description: '优先展示更适合首页承接咨询和转化的热门素材,适合直接跳详情页成交。', + badge: '本周热度', + }, + new: { + kicker: '最新上架', + description: '设计师最新上传内容集中展示,适合让站点保持“持续更新”的活跃感。', + badge: '持续更新', + }, +}; + +const Works = ({ title, type, categoryFilter, sectionId }) => { + const navigate = useNavigate(); + const [works, setWorks] = useState([]); + const [loading, setLoading] = useState(true); + + useEffect(() => { + loadWorks(); + }, [type, categoryFilter]); + + const loadWorks = async () => { + setLoading(true); + const result = await getWorksList(1, 9, categoryFilter || ''); + setLoading(false); + + if (result.success) { + setWorks(result.data.items || []); + } else { + setWorks([]); + } + }; + + const getImageUrl = (id, width = 480, height = 340) => { + return `https://picsum.photos/seed/${id}/${width}/${height}`; + }; + + const getLevelColor = (level) => { + const colors = { + 1: '#8b847d', + 2: '#5f9fd2', + 3: '#2f8f73', + 4: '#9a5be0', + 5: '#d46b40', + 6: '#e0564a', + }; + return colors[level] || '#8b847d'; + }; + + const handleCardClick = (work) => { + navigate(`/detail/${work.id}`); + }; + + const sectionMeta = categoryFilter + ? { + kicker: '分类浏览', + description: `当前正在浏览「${categoryFilter}」分类下的作品,卡片会优先承接下载和详情页转化。`, + badge: `${categoryFilter} 分类`, + } + : sectionPresets[type] || sectionPresets.hot; + + if (loading) { + return ( +
+
+ +
+
+ ); + } + + return ( +
+
+
+ {sectionMeta.kicker} +

{title}

+

{sectionMeta.description}

+
+
+ {works.length} 张展示 + {sectionMeta.badge} +
+
+ + + {works.map((work, index) => ( + + handleCardClick(work)} + cover={ +
+ {work.title} { + e.target.src = getImageUrl(work.id); + }} + /> +
+ 查看详情 + +
+
¥{work.price}
+
+ } + > +
+
+ {work.category || '设计素材'} + #{work.id} +
+

{work.title}

+
+ {work.designer} + + Lv.{work.level} + + {work.level_text} +
+
+
¥{work.price}
+ + 进入详情 + + +
+
+
+ + ))} +
+
+ ); +}; + +export default Works; diff --git a/frontend/src/index.css b/frontend/src/index.css index e948621..2215892 100644 --- a/frontend/src/index.css +++ b/frontend/src/index.css @@ -1,44 +1,65 @@ -/* ========== 全局样式重置 ========== */ +:root { + --brand: #ef6a5b; + --brand-strong: #d84e3f; + --brand-soft: #fff0e7; + --ink: #241d17; + --text: #5f5449; + --muted: #8c8073; + --surface: rgba(255, 255, 255, 0.94); + --surface-soft: #fffaf5; + --line: rgba(233, 221, 212, 0.95); + --shadow: 0 22px 48px rgba(66, 42, 26, 0.08); +} + * { margin: 0; padding: 0; box-sizing: border-box; } +html { + scroll-behavior: smooth; +} + body { font-family: 'PingFang SC', 'Microsoft YaHei', 'Helvetica Neue', Arial, sans-serif; font-size: 16px; line-height: 1.6; - color: #2d3436; - background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%); + color: var(--ink); + background: + radial-gradient(circle at top left, rgba(239, 106, 91, 0.08), transparent 22%), + radial-gradient(circle at top right, rgba(244, 178, 84, 0.1), transparent 20%), + linear-gradient(180deg, #fffdf9 0%, #f8f2eb 100%); min-height: 100vh; } -/* ========== 滚动条美化 ========== */ +a { + color: inherit; +} + ::-webkit-scrollbar { width: 8px; height: 8px; } ::-webkit-scrollbar-track { - background: #f1f1f1; - border-radius: 4px; + background: #f4ede6; + border-radius: 999px; } ::-webkit-scrollbar-thumb { - background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); - border-radius: 4px; + background: linear-gradient(180deg, #ef6a5b 0%, #d84e3f 100%); + border-radius: 999px; } ::-webkit-scrollbar-thumb:hover { - background: linear-gradient(135deg, #764ba2 0%, #667eea 100%); + background: linear-gradient(180deg, #e06152 0%, #cb4638 100%); } -/* ========== 通用动画 ========== */ @keyframes fadeIn { from { opacity: 0; - transform: translateY(20px); + transform: translateY(16px); } to { opacity: 1; @@ -49,7 +70,7 @@ body { @keyframes slideIn { from { opacity: 0; - transform: translateX(-30px); + transform: translateX(-24px); } to { opacity: 1; @@ -60,187 +81,10 @@ body { @keyframes scaleIn { from { opacity: 0; - transform: scale(0.9); + transform: scale(0.96); } to { opacity: 1; transform: scale(1); } } - -@keyframes float { - 0%, 100% { - transform: translateY(0); - } - 50% { - transform: translateY(-10px); - } -} - -@keyframes shimmer { - 0% { - background-position: -1000px 0; - } - 100% { - background-position: 1000px 0; - } -} - -@keyframes pulse { - 0%, 100% { - opacity: 1; - } - 50% { - opacity: 0.5; - } -} - -/* ========== 通用类 ========== */ -.fade-in { - animation: fadeIn 0.6s ease-out; -} - -.slide-in { - animation: slideIn 0.6s ease-out; -} - -.scale-in { - animation: scaleIn 0.6s ease-out; -} - -.float { - animation: float 3s ease-in-out infinite; -} - -/* ========== 按钮样式 ========== */ -.btn-primary { - background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); - color: white; - border: none; - padding: 12px 30px; - border-radius: 25px; - font-size: 16px; - font-weight: 600; - cursor: pointer; - transition: all 0.3s ease; - box-shadow: 0 4px 15px rgba(102, 126, 234, 0.4); -} - -.btn-primary:hover { - transform: translateY(-2px); - box-shadow: 0 6px 20px rgba(102, 126, 234, 0.6); -} - -.btn-primary:active { - transform: translateY(0); -} - -/* ========== 卡片阴影 ========== */ -.card-shadow { - box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1); - transition: all 0.3s ease; -} - -.card-shadow:hover { - box-shadow: 0 15px 40px rgba(0, 0, 0, 0.15); - transform: translateY(-5px); -} - -/* ========== 渐变背景 ========== */ -.gradient-bg { - background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); -} - -.gradient-text { - background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); - -webkit-background-clip: text; - -webkit-text-fill-color: transparent; - background-clip: text; -} - -/* ========== 玻璃拟态 ========== */ -.glass { - background: rgba(255, 255, 255, 0.1); - backdrop-filter: blur(10px); - border: 1px solid rgba(255, 255, 255, 0.2); -} - -/* ========== 加载动画 ========== */ -.loading-shimmer { - background: linear-gradient( - 90deg, - #f0f0f0 0%, - #e0e0e0 20%, - #f0f0f0 40%, - #f0f0f0 100% - ); - background-size: 1000px 100%; - animation: shimmer 2s infinite; -} - -/* ========== 标签样式 ========== */ -.tag-gradient { - background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); - color: white; - border: none; - padding: 4px 12px; - border-radius: 12px; - font-size: 12px; - font-weight: 600; -} - -/* ========== 输入框美化 ========== */ -.input-modern { - width: 100%; - padding: 12px 20px; - border: 2px solid #e0e0e0; - border-radius: 12px; - font-size: 16px; - transition: all 0.3s ease; - background: white; -} - -.input-modern:focus { - outline: none; - border-color: #667eea; - box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1); -} - -/* ========== 分割线 ========== */ -.divider-gradient { - height: 2px; - border: none; - background: linear-gradient(90deg, #667eea 0%, #764ba2 50%, #667eea 100%); - background-size: 200% 100%; - animation: shimmer 3s infinite; -} - -/* ========== 响应式断点 ========== */ -@media (max-width: 768px) { - body { - font-size: 14px; - } - - .btn-primary { - padding: 10px 20px; - font-size: 14px; - } -} - -/* ========== 暗黑模式支持 ========== */ -@media (prefers-color-scheme: dark) { - body { - background: linear-gradient(135deg, #1a1a2e 0%, #16213e 100%); - color: #e0e0e0; - } - - .input-modern { - background: #2d2d44; - border-color: #404060; - color: #e0e0e0; - } - - .input-modern:focus { - border-color: #667eea; - } -} diff --git a/frontend/src/pages/Home.jsx b/frontend/src/pages/Home.jsx index fb8237c..0175d8d 100644 --- a/frontend/src/pages/Home.jsx +++ b/frontend/src/pages/Home.jsx @@ -6,19 +6,19 @@ import Works from '../components/Works'; import Designers from '../components/Designers'; import Footer from '../components/Footer'; -function Home() { - return ( -
-
- - - - - - -
-
- ); -} +function Home() { + return ( +
+
+ + + + + + +
+
+ ); +} export default Home; diff --git a/frontend/src/pages/WorkDetail.css b/frontend/src/pages/WorkDetail.css index 9ddcc7f..83efbbb 100644 --- a/frontend/src/pages/WorkDetail.css +++ b/frontend/src/pages/WorkDetail.css @@ -1,474 +1,632 @@ -.work-detail-page { - min-height: 100vh; - background: #f5f5f5; - padding-top: 70px; -} - -.work-detail-container { - max-width: 1400px; - margin: 0 auto; - padding: 20px; -} - -/* 面包屑 */ -.breadcrumb { - padding: 16px 0; - font-size: 14px; - color: #999; -} - -.breadcrumb a { - color: #666; - cursor: pointer; - transition: color 0.3s; -} - -.breadcrumb a:hover { - color: #ff5a5a; -} - -.breadcrumb .current { - color: #333; -} - -/* 主要内容区 */ -.work-detail-content { - display: flex; - gap: 24px; - align-items: flex-start; -} - -/* 左侧作品展示 */ -.work-main { - flex: 1; - background: white; - border-radius: 12px; - overflow: hidden; -} - -.work-image-wrapper { - padding: 24px; -} - -.work-image { - width: 100%; - height: auto; - display: block; - border-radius: 8px; - box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1); -} - -/* 相关标签 */ -.related-tags { - margin-top: 32px; - padding-top: 24px; - border-top: 1px solid #f0f0f0; -} - -.related-tags h4 { - font-size: 16px; - font-weight: 600; - margin-bottom: 16px; - color: #333; -} - -.tags-list { - display: flex; - flex-wrap: wrap; - gap: 8px; -} - -.work-tag { - padding: 6px 16px; - background: #f5f5f5; - border: 1px solid #e8e8e8; - border-radius: 20px; - cursor: pointer; - transition: all 0.3s; -} - -.work-tag:hover { - background: #fff0f0; - border-color: #ff5a5a; - color: #ff5a5a; -} - -/* 猜你喜欢 */ -.related-works { - margin-top: 32px; - padding-top: 24px; - border-top: 1px solid #f0f0f0; -} - -.related-works h3 { - font-size: 18px; - font-weight: 600; - margin-bottom: 20px; - color: #333; -} - -.related-works-grid { - display: grid; - grid-template-columns: repeat(3, 1fr); - gap: 16px; -} - -.related-work-card { - cursor: pointer; - border-radius: 8px; - overflow: hidden; - transition: transform 0.3s; - background: #fafafa; -} - -.related-work-card:hover { - transform: translateY(-4px); -} - -.related-work-image { - position: relative; - width: 100%; - padding-top: 150%; - overflow: hidden; - background: #f0f0f0; -} - -.related-work-image img { - position: absolute; - top: 0; - left: 0; - width: 100%; - height: 100%; - object-fit: cover; -} - -.related-work-overlay { - position: absolute; - top: 0; - left: 0; - right: 0; - bottom: 0; - background: rgba(0, 0, 0, 0.6); - display: flex; - align-items: center; - justify-content: center; - opacity: 0; - transition: opacity 0.3s; -} - -.related-work-card:hover .related-work-overlay { - opacity: 1; -} - -.related-work-info { - padding: 12px; -} - -.related-work-info h4 { - font-size: 14px; - font-weight: 500; - margin-bottom: 8px; - color: #333; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; -} - -.related-work-designer { - font-size: 12px; - color: #999; -} - -.designer-level { - color: #ff5a5a; - margin-right: 4px; -} - -.designer-level-name { - margin-right: 8px; -} - -.designer-name { - margin-top: 4px; - color: #666; -} - -/* 右侧信息栏 */ -.work-sidebar { - width: 380px; - background: white; - border-radius: 12px; - padding: 24px; - position: sticky; - top: 80px; -} - -/* 作品编号 */ -.work-id-section { - display: flex; - align-items: center; - justify-content: space-between; - padding: 12px; - background: #f5f5f5; - border-radius: 8px; - margin-bottom: 16px; -} - -.work-id { - font-size: 14px; - color: #666; - font-family: monospace; -} - -/* 作品标题 */ -.work-title { - font-size: 24px; - font-weight: 600; - color: #333; - margin-bottom: 20px; - line-height: 1.4; -} - -/* 作品信息列表 */ -.work-info-list { - border-top: 1px solid #f0f0f0; - border-bottom: 1px solid #f0f0f0; - padding: 16px 0; - margin-bottom: 20px; -} - -.work-info-item { - display: flex; - justify-content: space-between; - padding: 8px 0; - font-size: 14px; -} - -.info-label { - color: #999; -} - -.info-value { - color: #333; - font-weight: 500; -} - -/* 操作按钮 */ -.work-actions { - display: grid; - grid-template-columns: 1fr auto auto; - gap: 12px; - margin-bottom: 24px; -} - -.download-btn { - background: linear-gradient(135deg, #ff5a5a 0%, #ff8080 100%); - border: none; - height: 44px; - font-size: 16px; - font-weight: 500; - box-shadow: 0 4px 12px rgba(255, 90, 90, 0.3); -} - -.download-btn:hover { - background: linear-gradient(135deg, #ff7070 0%, #ff9090 100%) !important; -} - -.collect-btn, -.share-btn { - height: 44px; - width: 44px; - padding: 0; - border-color: #e8e8e8; - color: #666; -} - -.collect-btn.collected { - color: #ff5a5a; - border-color: #ff5a5a; -} - -.collect-btn:hover, -.share-btn:hover { - color: #ff5a5a; - border-color: #ff5a5a; -} - -/* 设计师卡片 */ -.designer-card { - padding: 20px; - background: #fafafa; - border-radius: 12px; - margin-bottom: 24px; -} - -.designer-header { - display: flex; - align-items: center; - gap: 12px; - margin-bottom: 16px; -} - -.designer-avatar { - width: 56px; - height: 56px; - border-radius: 50%; - overflow: hidden; - border: 2px solid #fff; - box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); -} - -.designer-avatar img { - width: 100%; - height: 100%; - object-fit: cover; -} - -.designer-info { - flex: 1; -} - -.designer-name-level { - display: flex; - align-items: center; - gap: 6px; - margin-bottom: 4px; -} - -.designer-level-badge { - background: #ff5a5a; - color: white; - padding: 2px 8px; - border-radius: 12px; - font-size: 12px; -} - -.designer-level-text { - font-size: 12px; - color: #999; -} - -.designer-card .designer-name { - font-size: 16px; - font-weight: 600; - color: #333; - margin: 0; -} - -.designer-stats { - display: flex; - justify-content: space-around; - padding: 16px 0; - border-top: 1px solid #e8e8e8; - border-bottom: 1px solid #e8e8e8; - margin-bottom: 16px; -} - -.designer-stats .stat-item { - display: flex; - flex-direction: column; - align-items: center; - gap: 4px; -} - -.stat-value { - font-size: 20px; - font-weight: 600; - color: #333; -} - -.stat-label { - font-size: 12px; - color: #999; -} - -.follow-btn { - height: 40px; - background: #ff5a5a; - border: none; -} - -.follow-btn:hover { - background: #ff7070 !important; -} - -.follow-btn.followed { - background: #e8e8e8; - color: #666; -} - -.follow-btn.followed:hover { - background: #d0d0d0 !important; -} - -/* 搜索画板 */ -.board-search { - margin-bottom: 24px; -} - -.board-search h4 { - font-size: 14px; - font-weight: 600; - margin-bottom: 12px; - color: #333; -} - -.board-search-input { - height: 40px; - border-radius: 20px; -} - -/* 浏览统计 */ -.work-stats { - display: flex; - justify-content: space-around; - padding-top: 16px; - border-top: 1px solid #f0f0f0; -} - -.work-stats .stat-item { - display: flex; - align-items: center; - gap: 6px; - font-size: 14px; - color: #999; -} - -.work-stats .stat-item .anticon { - font-size: 16px; -} - -/* 响应式 */ -@media (max-width: 1200px) { - .work-detail-content { - flex-direction: column; - } - - .work-sidebar { - width: 100%; - position: static; - } - - .related-works-grid { - grid-template-columns: repeat(2, 1fr); - } -} - -@media (max-width: 768px) { - .work-detail-container { - padding: 12px; - } - - .related-works-grid { - grid-template-columns: 1fr; - } - - .work-actions { - grid-template-columns: 1fr; - } - - .collect-btn, - .share-btn { - width: 100%; - } -} +.work-detail-page { + min-height: 100vh; + padding-top: 70px; + background: + radial-gradient(circle at top left, rgba(239, 106, 91, 0.08), transparent 28%), + radial-gradient(circle at top right, rgba(244, 178, 84, 0.12), transparent 24%), + linear-gradient(180deg, #fffdf9 0%, #f8f2eb 100%); +} + +.work-detail-container { + max-width: 1400px; + margin: 0 auto; + padding: 18px 20px 56px; +} + +.work-loading { + display: flex; + justify-content: center; + align-items: center; + min-height: 60vh; +} + +.breadcrumb { + padding: 14px 0 22px; + font-size: 14px; + color: #8e8175; +} + +.breadcrumb a { + color: #716457; + cursor: pointer; + transition: color 0.2s ease; +} + +.breadcrumb a:hover { + color: #ef6a5b; +} + +.breadcrumb .current { + color: #30271f; +} + +.work-detail-heading { + display: flex; + justify-content: space-between; + gap: 28px; + padding: 28px 30px; + border-radius: 32px; + background: + radial-gradient(circle at right top, rgba(244, 178, 84, 0.18), transparent 28%), + linear-gradient(135deg, rgba(255, 250, 244, 0.94), rgba(255, 244, 233, 0.96)); + border: 1px solid rgba(233, 219, 207, 0.92); + box-shadow: 0 22px 50px rgba(69, 44, 26, 0.08); + margin-bottom: 24px; +} + +.detail-heading-copy { + max-width: 760px; +} + +.detail-kicker { + display: inline-flex; + margin-bottom: 12px; + padding: 8px 12px; + border-radius: 999px; + background: rgba(239, 106, 91, 0.1); + color: #d84e3f; + font-size: 12px; + font-weight: 700; + letter-spacing: 0.08em; +} + +.work-title { + margin: 0; + font-size: clamp(30px, 4vw, 42px); + line-height: 1.15; + color: #241d17; +} + +.work-subtitle { + margin: 14px 0 0; + color: #72675d; + line-height: 1.85; + font-size: 16px; +} + +.detail-heading-stats { + display: grid; + grid-template-columns: repeat(3, minmax(0, 1fr)); + gap: 12px; + min-width: 360px; +} + +.heading-stat { + padding: 18px 16px; + border-radius: 22px; + background: rgba(255, 255, 255, 0.78); + border: 1px solid rgba(231, 219, 209, 0.9); + display: flex; + flex-direction: column; + gap: 6px; + justify-content: center; +} + +.heading-stat strong { + font-size: 24px; + color: #241d17; +} + +.heading-stat span { + font-size: 13px; + color: #837668; +} + +.work-detail-content { + display: grid; + grid-template-columns: minmax(0, 1fr) 380px; + gap: 24px; + align-items: start; +} + +.work-main { + display: flex; + flex-direction: column; + gap: 24px; +} + +.work-preview-card, +.detail-panel, +.price-card { + border-radius: 28px; + background: rgba(255, 255, 255, 0.94); + border: 1px solid rgba(234, 222, 212, 0.95); + box-shadow: 0 22px 48px rgba(66, 42, 26, 0.08); +} + +.work-preview-card { + padding: 22px; +} + +.work-preview-topline { + display: flex; + justify-content: space-between; + gap: 10px; + flex-wrap: wrap; + margin-bottom: 16px; +} + +.preview-badge { + padding: 8px 12px; + border-radius: 999px; + background: #fff0e7; + color: #d84e3f; + font-size: 12px; + font-weight: 700; +} + +.preview-badge.subtle { + background: #f7f1ea; + color: #6b5f53; +} + +.work-image-shell { + position: relative; + border-radius: 24px; + overflow: hidden; + background: linear-gradient(135deg, #f8efe6 0%, #f1e2d4 100%); +} + +.work-image-shell::after { + content: ''; + position: absolute; + inset: auto 0 0; + height: 26%; + background: linear-gradient(180deg, transparent, rgba(28, 22, 18, 0.12)); + pointer-events: none; +} + +.work-image { + width: 100%; + display: block; + object-fit: cover; +} + +.preview-benefits { + display: grid; + grid-template-columns: repeat(3, minmax(0, 1fr)); + gap: 12px; + margin-top: 18px; +} + +.preview-benefit { + display: flex; + align-items: flex-start; + gap: 10px; + padding: 14px 16px; + border-radius: 20px; + background: #fffaf5; + border: 1px solid rgba(236, 225, 215, 0.92); + color: #5f5449; + line-height: 1.7; + font-size: 14px; +} + +.preview-benefit .anticon { + color: #ef6a5b; + font-size: 16px; + margin-top: 2px; +} + +.detail-panel { + padding: 22px; +} + +.panel-header { + display: flex; + align-items: flex-end; + justify-content: space-between; + gap: 12px; + margin-bottom: 16px; +} + +.panel-header.compact { + align-items: center; +} + +.panel-header h3 { + margin: 0; + color: #2c241d; + font-size: 20px; +} + +.panel-header span { + color: #8c8073; + font-size: 13px; +} + +.tags-list { + display: flex; + flex-wrap: wrap; + gap: 10px; +} + +.work-tag.ant-tag { + margin: 0; + padding: 8px 14px; + border-radius: 999px; + background: #fff6ef; + border: 1px solid rgba(236, 214, 196, 0.95); + color: #6d5b4c; +} + +.empty-hint { + color: #9a8d81; + font-size: 14px; +} + +.related-works-grid { + display: grid; + grid-template-columns: repeat(3, minmax(0, 1fr)); + gap: 16px; +} + +.related-work-card { + cursor: pointer; + border-radius: 22px; + overflow: hidden; + background: linear-gradient(180deg, #fffdfb, #f8f2eb); + border: 1px solid rgba(237, 226, 216, 0.95); + transition: transform 0.24s ease, box-shadow 0.24s ease; +} + +.related-work-card:hover { + transform: translateY(-6px); + box-shadow: 0 18px 34px rgba(64, 42, 28, 0.12); +} + +.related-work-image { + position: relative; + width: 100%; + padding-top: 150%; + overflow: hidden; + background: #f2e8de; +} + +.related-work-image img { + position: absolute; + inset: 0; + width: 100%; + height: 100%; + object-fit: cover; +} + +.related-work-overlay { + position: absolute; + inset: 0; + background: rgba(21, 18, 15, 0.45); + display: flex; + align-items: center; + justify-content: center; + opacity: 0; + transition: opacity 0.24s ease; +} + +.related-work-card:hover .related-work-overlay { + opacity: 1; +} + +.related-work-info { + padding: 14px; +} + +.related-work-info h4 { + margin: 0 0 8px; + color: #2d241d; + font-size: 15px; +} + +.related-work-designer { + color: #8a7d72; + font-size: 12px; +} + +.designer-level { + color: #ef6a5b; + margin-right: 4px; +} + +.designer-level-name { + margin-right: 8px; +} + +.designer-name { + margin: 4px 0 0; + color: #65594d; +} + +.work-sidebar { + position: sticky; + top: 86px; + display: flex; + flex-direction: column; + gap: 18px; +} + +.price-card { + padding: 22px; +} + +.work-id-section { + display: flex; + align-items: center; + justify-content: space-between; + gap: 12px; + padding: 12px 14px; + border-radius: 18px; + background: #faf5ef; + border: 1px solid rgba(235, 224, 214, 0.94); +} + +.work-id { + color: #66594d; + font-size: 14px; + font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; +} + +.price-block { + margin: 18px 0 10px; +} + +.price-label { + display: block; + color: #8a7c70; + font-size: 13px; + margin-bottom: 6px; +} + +.price-value { + color: #da5748; + font-size: 40px; + line-height: 1; + font-weight: 800; +} + +.work-info-list { + display: flex; + flex-direction: column; + gap: 10px; + padding: 16px 0; + border-top: 1px solid rgba(239, 229, 220, 0.95); + border-bottom: 1px solid rgba(239, 229, 220, 0.95); +} + +.work-info-item { + display: flex; + justify-content: space-between; + gap: 18px; +} + +.info-label { + color: #95887c; + font-size: 14px; +} + +.info-value { + color: #2d241d; + font-size: 14px; + font-weight: 600; + text-align: right; +} + +.info-value.highlight { + color: #db5748; +} + +.work-actions { + display: grid; + grid-template-columns: 1fr auto auto; + gap: 12px; + margin-top: 18px; +} + +.download-btn.ant-btn { + height: 50px; + border: none; + border-radius: 18px; + background: linear-gradient(135deg, #ef6a5b 0%, #d84e3f 100%); + box-shadow: 0 16px 30px rgba(216, 78, 63, 0.24); + font-weight: 700; +} + +.icon-btn.ant-btn { + width: 50px; + height: 50px; + border-radius: 18px; + border-color: rgba(224, 208, 197, 0.95); + color: #65594d; + background: #fffaf5; +} + +.icon-btn.ant-btn:hover, +.icon-btn.ant-btn.collected { + color: #ef6a5b; + border-color: rgba(239, 106, 91, 0.4); +} + +.designer-card { + background: linear-gradient(180deg, #fffdfb, #f8f2eb); +} + +.designer-header { + display: flex; + align-items: center; + gap: 14px; + margin-bottom: 18px; +} + +.designer-avatar { + width: 60px; + height: 60px; + border-radius: 20px; + overflow: hidden; + background: #fff; + border: 1px solid rgba(233, 221, 212, 0.95); +} + +.designer-avatar img { + width: 100%; + height: 100%; + object-fit: cover; +} + +.designer-info { + min-width: 0; +} + +.designer-name-level { + display: flex; + align-items: center; + gap: 8px; + margin-bottom: 6px; +} + +.designer-level-badge { + padding: 4px 10px; + border-radius: 999px; + background: #ef6a5b; + color: #fff; + font-size: 12px; + font-weight: 700; +} + +.designer-level-text { + color: #8c8073; + font-size: 12px; +} + +.designer-card .designer-name { + margin: 0; + font-size: 18px; + font-weight: 700; + color: #2b241d; +} + +.designer-stats { + display: grid; + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: 12px; + margin-bottom: 16px; +} + +.designer-stats .stat-item { + padding: 14px; + border-radius: 18px; + background: rgba(255, 255, 255, 0.72); + border: 1px solid rgba(236, 225, 216, 0.95); + display: flex; + flex-direction: column; + gap: 4px; + align-items: center; +} + +.stat-value { + color: #2d241d; + font-size: 22px; + font-weight: 700; +} + +.stat-label { + color: #918578; + font-size: 12px; +} + +.follow-btn.ant-btn { + height: 46px; + border-radius: 16px; + border: none; + font-weight: 700; +} + +.follow-btn.ant-btn.followed { + background: #ebe3db; + color: #66594d; +} + +.board-search-input.ant-input-affix-wrapper { + height: 44px; + border-radius: 16px; + border-color: rgba(226, 211, 200, 0.95); +} + +.board-search-input.ant-input-affix-wrapper:hover, +.board-search-input.ant-input-affix-wrapper-focused { + border-color: rgba(239, 106, 91, 0.4); + box-shadow: none; +} + +.stats-grid { + display: grid; + gap: 12px; +} + +.stat-row { + display: flex; + align-items: center; + gap: 10px; + padding: 14px 16px; + border-radius: 18px; + background: #fff8f2; + border: 1px solid rgba(237, 225, 215, 0.95); + color: #66594d; +} + +.stat-row .anticon { + color: #ef6a5b; +} + +@media (max-width: 1200px) { + .work-detail-heading { + flex-direction: column; + } + + .detail-heading-stats { + min-width: 0; + } + + .work-detail-content { + grid-template-columns: 1fr; + } + + .work-sidebar { + position: static; + } +} + +@media (max-width: 900px) { + .preview-benefits, + .related-works-grid, + .detail-heading-stats { + grid-template-columns: 1fr; + } +} + +@media (max-width: 768px) { + .work-detail-container { + padding: 12px 12px 40px; + } + + .work-detail-heading, + .work-preview-card, + .detail-panel, + .price-card { + border-radius: 24px; + padding: 18px; + } + + .work-title { + font-size: 28px; + } + + .work-subtitle { + font-size: 14px; + } + + .work-actions { + grid-template-columns: 1fr; + } + + .icon-btn.ant-btn { + width: 100%; + } +} diff --git a/frontend/src/pages/WorkDetail.jsx b/frontend/src/pages/WorkDetail.jsx index 8634c66..92c273d 100644 --- a/frontend/src/pages/WorkDetail.jsx +++ b/frontend/src/pages/WorkDetail.jsx @@ -1,14 +1,23 @@ -import { useState, useEffect } from 'react'; -import { useParams, useNavigate } from 'react-router-dom'; -import { Button, Tag, message, Input, Modal, Spin } from 'antd'; -import { HeartOutlined, HeartFilled, DownloadOutlined, ShareAltOutlined, EyeOutlined, PlusOutlined, SearchOutlined } from '@ant-design/icons'; -import { QRCodeSVG } from 'qrcode.react'; -import { getWorkDetail } from '../api/works'; -import { createOrder } from '../api/orders'; -import { createPayment, queryPaymentStatus } from '../api/payment'; -import { downloadWork } from '../api/download'; -import { isLoggedIn } from '../api/auth'; -import { API_CONFIG } from '../utils/config'; +import { useState, useEffect } from 'react'; +import { useParams, useNavigate } from 'react-router-dom'; +import { Button, Tag, message, Input, Modal, Spin } from 'antd'; +import { + HeartOutlined, + HeartFilled, + DownloadOutlined, + ShareAltOutlined, + EyeOutlined, + PlusOutlined, + SearchOutlined, + CheckCircleFilled, +} from '@ant-design/icons'; +import { QRCodeSVG } from 'qrcode.react'; +import { getWorkDetail } from '../api/works'; +import { createOrder } from '../api/orders'; +import { createPayment, queryPaymentStatus } from '../api/payment'; +import { downloadWork } from '../api/download'; +import { isLoggedIn } from '../api/auth'; +import { API_CONFIG } from '../utils/config'; import Header from '../components/Header'; import Footer from '../components/Footer'; import './WorkDetail.css'; @@ -34,7 +43,7 @@ const parseTags = (rawTags) => { const parsed = JSON.parse(trimmed); return Array.isArray(parsed) ? parsed.filter(Boolean) : []; } catch { - // 回退到逗号分隔解析,避免详情页白屏 + // 兼容历史逗号分隔格式,避免详情页白屏 } } @@ -50,452 +59,507 @@ const getDownloadFilename = (work) => { const ext = extMatch ? extMatch[0] : '.jpg'; return `${work?.title || 'work'}${ext}`; }; - -const WorkDetail = () => { - const { id } = useParams(); - const navigate = useNavigate(); - const [collected, setCollected] = useState(false); - const [followed, setFollowed] = useState(false); - const [downloadModalOpen, setDownloadModalOpen] = useState(false); - const [workData, setWorkData] = useState(null); - const [loading, setLoading] = useState(true); - const [purchasing, setPurchasing] = useState(false); - const [paymentUrl, setPaymentUrl] = useState(''); - const [orderNumber, setOrderNumber] = useState(''); - const [checkingPayment, setCheckingPayment] = useState(false); - - // 加载作品详情 - useEffect(() => { - loadWorkDetail(); - }, [id]); - - const loadWorkDetail = async () => { - setLoading(true); - const result = await getWorkDetail(id); - setLoading(false); - - if (result.success) { - setWorkData(result.data); - } else { - message.error('加载作品详情失败'); - } - }; - - // 使用 Picsum 图片作为后备 - const getImageUrl = (workId) => { - return `https://picsum.photos/seed/${workId}/800/1200`; - }; - - const getRelatedImageUrl = (workId) => { - return `https://picsum.photos/seed/related${workId}/400/600`; - }; - - const relatedWorks = [ - { - id: '20', - title: '相关设计作品1', - image: getRelatedImageUrl(20), - designer: 'cestbon', - level: 1, - levelName: '设计爱好者' - }, - { - id: '21', - title: '相关设计作品2', - image: getRelatedImageUrl(21), - designer: '六十六号屯', - level: 4, - levelName: '资深设计师' - }, - { - id: '22', - title: '相关设计作品3', - image: getRelatedImageUrl(22), - designer: '扶摇', - level: 3, - levelName: '设计师' - } - ]; - - const handleCollect = () => { - setCollected(!collected); - message.success(collected ? '已取消收藏' : '收藏成功'); - }; - - const handleFollow = () => { - setFollowed(!followed); - message.success(followed ? '已取消关注' : '关注成功'); - }; - - const handleDownload = async () => { - // 检查是否登录 - if (!isLoggedIn()) { - message.warning('请先登录'); - return; - } - - // 尝试直接下载 + +const WorkDetail = () => { + const { id } = useParams(); + const navigate = useNavigate(); + const [collected, setCollected] = useState(false); + const [followed, setFollowed] = useState(false); + const [downloadModalOpen, setDownloadModalOpen] = useState(false); + const [workData, setWorkData] = useState(null); + const [loading, setLoading] = useState(true); + const [purchasing, setPurchasing] = useState(false); + const [paymentUrl, setPaymentUrl] = useState(''); + const [orderNumber, setOrderNumber] = useState(''); + const [checkingPayment, setCheckingPayment] = useState(false); + + useEffect(() => { + loadWorkDetail(); + }, [id]); + + const loadWorkDetail = async () => { + setLoading(true); + const result = await getWorkDetail(id); + setLoading(false); + + if (result.success) { + setWorkData(result.data); + } else { + message.error('加载作品详情失败'); + } + }; + + const getImageUrl = (workId) => { + return `https://picsum.photos/seed/${workId}/800/1200`; + }; + + const getRelatedImageUrl = (workId) => { + return `https://picsum.photos/seed/related${workId}/400/600`; + }; + + const relatedWorks = [ + { + id: '20', + title: '相关设计作品 1', + image: getRelatedImageUrl(20), + designer: 'cestbon', + level: 1, + levelName: '设计爱好者', + }, + { + id: '21', + title: '相关设计作品 2', + image: getRelatedImageUrl(21), + designer: '六十六号屯', + level: 4, + levelName: '资深设计师', + }, + { + id: '22', + title: '相关设计作品 3', + image: getRelatedImageUrl(22), + designer: '扶摇', + level: 3, + levelName: '设计师', + }, + ]; + + const handleCollect = () => { + setCollected(!collected); + message.success(collected ? '已取消收藏' : '收藏成功'); + }; + + const handleFollow = () => { + setFollowed(!followed); + message.success(followed ? '已取消关注' : '关注成功'); + }; + + const handleDownload = async () => { + if (!isLoggedIn()) { + message.warning('请先登录'); + return; + } + const result = await downloadWork(id, getDownloadFilename(workData)); - - // 如果需要购买,显示购买确认弹窗 - if (!result.success && result.needPurchase) { - setDownloadModalOpen(true); - } - }; - - const confirmDownload = async () => { - setPurchasing(true); - - // 1. 创建订单 - const orderResult = await createOrder(id); - if (!orderResult.success) { - setPurchasing(false); - message.error(orderResult.message); - return; - } - - const order = orderResult.data; - setOrderNumber(order.order_no); - - // 2. 创建支付链接 - const paymentResult = await createPayment(order.id); - setPurchasing(false); - - if (paymentResult.success) { - setPaymentUrl(paymentResult.data.pay_url); - message.success('请扫描二维码完成支付'); - // 开始轮询支付状态 - startPaymentStatusCheck(order.order_no); - } else { - message.error(paymentResult.message); - setDownloadModalOpen(false); - } - }; - - // 轮询检查支付状态 - const startPaymentStatusCheck = (orderNum) => { - setCheckingPayment(true); - const checkInterval = setInterval(async () => { - const result = await queryPaymentStatus(orderNum); - if (result.success) { - // 检查本地订单状态是否为已支付 - if (result.data.local_status === 'paid' || result.data.remote_status === 'SUCCESS') { - clearInterval(checkInterval); - setCheckingPayment(false); - setDownloadModalOpen(false); - setPaymentUrl(''); - message.success('支付成功!开始下载...'); - // 重新尝试下载 + + if (!result.success && result.needPurchase) { + setDownloadModalOpen(true); + } + }; + + const confirmDownload = async () => { + setPurchasing(true); + + const orderResult = await createOrder(id); + if (!orderResult.success) { + setPurchasing(false); + message.error(orderResult.message); + return; + } + + const order = orderResult.data; + setOrderNumber(order.order_no); + + const paymentResult = await createPayment(order.id); + setPurchasing(false); + + if (paymentResult.success) { + setPaymentUrl(paymentResult.data.pay_url); + message.success('请扫描二维码完成支付'); + startPaymentStatusCheck(order.order_no); + } else { + message.error(paymentResult.message); + setDownloadModalOpen(false); + } + }; + + const startPaymentStatusCheck = (orderNum) => { + setCheckingPayment(true); + const checkInterval = setInterval(async () => { + const result = await queryPaymentStatus(orderNum); + if (result.success) { + if (result.data.local_status === 'paid' || result.data.remote_status === 'SUCCESS') { + clearInterval(checkInterval); + setCheckingPayment(false); + setDownloadModalOpen(false); + setPaymentUrl(''); + message.success('支付成功,开始下载'); await downloadWork(id, getDownloadFilename(workData)); - } - } - }, 3000); // 每3秒检查一次 - - // 5分钟后停止检查 - setTimeout(() => { - clearInterval(checkInterval); - setCheckingPayment(false); - }, 300000); - }; - - const handleModalClose = () => { - setDownloadModalOpen(false); - setPaymentUrl(''); - setOrderNumber(''); - setCheckingPayment(false); - }; - - // 生成作品编号:半年前日期 + 当前时分秒 + 作品ID - const generateWorkNumber = (workId) => { - const now = new Date(); - const sixMonthsAgo = new Date(now.getTime() - 180 * 24 * 60 * 60 * 1000); // 半年前 - const datePart = sixMonthsAgo.toISOString().slice(0, 10).replace(/-/g, ''); // YYYYMMDD - const timePart = now.toTimeString().slice(0, 8).replace(/:/g, ''); // HHMMSS - return `${datePart}${timePart}${workId}`; - }; - - const handleShare = () => { - navigator.clipboard.writeText(window.location.href); - message.success('链接已复制到剪贴板'); - }; - - const copyWorkId = () => { - const fullWorkNumber = generateWorkNumber(workData?.id || 0); - navigator.clipboard.writeText(fullWorkNumber); - message.success('编号已复制'); - }; - - if (loading) { - return ( -
-
-
- -
-
-
- ); - } - - if (!workData) { - return ( -
-
-
-

作品不存在

-
-
-
- ); - } - - // 解析标签 + } + } + }, 3000); + + setTimeout(() => { + clearInterval(checkInterval); + setCheckingPayment(false); + }, 300000); + }; + + const handleModalClose = () => { + setDownloadModalOpen(false); + setPaymentUrl(''); + setOrderNumber(''); + setCheckingPayment(false); + }; + + const generateWorkNumber = (workId) => { + const now = new Date(); + const sixMonthsAgo = new Date(now.getTime() - 180 * 24 * 60 * 60 * 1000); + const datePart = sixMonthsAgo.toISOString().slice(0, 10).replace(/-/g, ''); + const timePart = now.toTimeString().slice(0, 8).replace(/:/g, ''); + return `${datePart}${timePart}${workId}`; + }; + + const handleShare = () => { + navigator.clipboard.writeText(window.location.href); + message.success('链接已复制到剪贴板'); + }; + + const copyWorkId = () => { + const fullWorkNumber = generateWorkNumber(workData?.id || 0); + navigator.clipboard.writeText(fullWorkNumber); + message.success('编号已复制'); + }; + + if (loading) { + return ( +
+
+
+ +
+
+
+ ); + } + + if (!workData) { + return ( +
+
+
+

作品不存在

+
+
+
+ ); + } + const tags = parseTags(workData.tags); - - return ( -
-
- -
- {/* 面包屑导航 */} -
- navigate('/')}>首页 - / - {workData.title} -
- -
- {/* 左侧:作品展示 */} -
-
- {workData.title} - - {/* 相关搜索标签 */} -
-

相关搜索

-
- {tags.map((tag, index) => ( - {tag} - ))} -
-
- - {/* 猜你喜欢 */} -
-

猜你喜欢

-
- {relatedWorks.map(work => ( -
navigate(`/detail/${work.id}`)}> -
- {work.title} -
- -
-
-
-

{work.title}

-
- Lv.{work.level} - {work.levelName} -

{work.designer}

-
-
-
- ))} -
-
-
-
- - {/* 右侧:作品信息 */} -
- {/* 编号和复制 */} -
- {generateWorkNumber(workData.id)} - -
- - {/* 作品标题 */} -

{workData.title}

- - {/* 作品信息 */} -
-
- 分类 - {workData.category} -
-
- 设计师 - {workData.designer} -
-
- 等级 - Lv.{workData.level} {workData.level_text} -
-
- 价格 - ¥{workData.price} -
-
- - {/* 操作按钮 */} -
- -
- - {/* 设计师信息 */} -
-
-
- {workData.designer} -
-
-
- Lv.{workData.level} - {workData.level_text} -
-

{workData.designer}

-
-
- -
-
- - - 作品 -
-
- - - 粉丝 -
-
- - -
- - {/* 搜索画板 */} -
-

搜索画板

- } - className="board-search-input" - /> -
- - {/* 浏览统计 */} -
-
- - {workData.views} 浏览 -
-
- - {workData.collects} 收藏 -
-
-
-
-
- - {/* 购买确认弹窗 */} - - 关闭 - - ] : undefined} - width={paymentUrl ? 450 : 416} - > - {!paymentUrl ? ( -
-

作品:{workData.title}

-

价格:¥{workData.price}

-

点击确定后将生成支付二维码

-
- ) : ( -
-
- -
-

- 订单金额:¥{workData.price} -

-

- 请使用微信或支付宝扫码支付 -

- {checkingPayment && ( -
- - 等待支付中... -
- )} -

- 订单号:{orderNumber} -

-

- 支付完成后将自动开始下载 -

-
- )} -
- -
-
- ); -}; - -export default WorkDetail; + const detailFacts = [ + { label: '分类', value: workData.category || '设计素材' }, + { label: '设计师', value: workData.designer || '-' }, + { label: '等级', value: `Lv.${workData.level} ${workData.level_text}` }, + { label: '价格', value: `¥${workData.price}` }, + ]; + const servicePromises = [ + '支付成功后自动下载原图', + '详情页支持预览与编号复制', + '订单状态与支付状态可追踪', + ]; + + return ( +
+
+ +
+
+ navigate('/')}>首页 + / + {workData.category ? ( + <> + navigate(`/category/${workData.category}`)}>{workData.category} + / + + ) : null} + {workData.title} +
+ +
+
+ + {workData.category || '设计素材'} · 支付后即时交付 + +

{workData.title}

+

+ 适合活动海报、电商主图、背景素材等快速落地场景,先看预览,再决定是否下载原图。 +

+
+
+
+ {workData.views} + 浏览量 +
+
+ {workData.collects} + 收藏量 +
+
+ Lv.{workData.level} + {workData.level_text} +
+
+
+ +
+
+
+
+ 水印预览 + 可购买原图下载 +
+ +
+ {workData.title} +
+ +
+ {servicePromises.map((item) => ( +
+ + {item} +
+ ))} +
+
+ +
+
+

相关搜索

+ 帮助用户继续筛选相似素材 +
+
+ {tags.length ? ( + tags.map((tag, index) => ( + + {tag} + + )) + ) : ( + 暂无标签,后续上传时可补充关键词。 + )} +
+
+ +
+
+

猜你喜欢

+ 保持详情页浏览节奏,减少单页跳出。 +
+
+ {relatedWorks.map((work) => ( +
navigate(`/detail/${work.id}`)}> +
+ {work.title} +
+ +
+
+
+

{work.title}

+
+ Lv.{work.level} + {work.levelName} +

{work.designer}

+
+
+
+ ))} +
+
+
+ +
+
+
+ {generateWorkNumber(workData.id)} + +
+ +
+ 当前下载价 +
¥{workData.price}
+
+ +
+ {detailFacts.map((item) => ( +
+ {item.label} + + {item.value} + +
+ ))} +
+ +
+ +
+
+ +
+
+
+ {workData.designer} +
+
+
+ Lv.{workData.level} + {workData.level_text} +
+

{workData.designer}

+
+
+ +
+
+ - + 作品 +
+
+ - + 粉丝 +
+
+ + +
+ +
+
+

搜索画板

+ 继续搜同类风格 +
+ } + className="board-search-input" + /> +
+ +
+
+

作品动态

+ 帮助用户判断热度 +
+
+
+ + {workData.views} 次浏览 +
+
+ + {workData.collects} 次收藏 +
+
+
+
+
+
+ + + 关闭 + , + ] + : undefined + } + width={paymentUrl ? 450 : 416} + > + {!paymentUrl ? ( +
+

作品:{workData.title}

+

+ 价格: + + ¥{workData.price} + +

+

点击确定后将生成支付二维码

+
+ ) : ( +
+
+ +
+

+ 订单金额:¥{workData.price} +

+

请使用微信或支付宝扫码支付

+ {checkingPayment && ( +
+ + 等待支付中... +
+ )} +

订单号:{orderNumber}

+

支付完成后将自动开始下载

+
+ )} +
+ +
+ ); +}; + +export default WorkDetail;