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 */}
-
-
-
-
🌲
-
🌲
-
🎄
-
⛄
-
🎿
-
🛷
-
-
-
-
-
-
有所想,不如有所享!
-
-
-
-
- } className="search-btn" />
-
- }
- />
-
-
-
- 推荐搜索:
- {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={}
+ />
+
+
+ }
+ onClick={scrollToHotWorks}
+ >
+ 浏览热门作品
+
+
+
+
+
+
+ 推荐分类
+ {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 (
-
-
-
-
- {works.map((work, index) => (
-
- handleCardClick(work)}
- cover={
-
-

{
- 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={
+
+

{
+ 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 (
-
-
-
-
- {/* 面包屑导航 */}
-
-
-
- {/* 左侧:作品展示 */}
-
-
-

-
- {/* 相关搜索标签 */}
-
-
相关搜索
-
- {tags.map((tag, index) => (
- {tag}
- ))}
-
-
-
- {/* 猜你喜欢 */}
-
-
猜你喜欢
-
- {relatedWorks.map(work => (
-
navigate(`/detail/${work.id}`)}>
-
-

-
- }>
- 源文件下载
-
-
-
-
-
{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}
-
-
-
- {/* 操作按钮 */}
-
- }
- onClick={handleDownload}
- className="download-btn"
- >
- 我要下载
-
- : }
- onClick={handleCollect}
- className={`collect-btn ${collected ? 'collected' : ''}`}
- />
- }
- onClick={handleShare}
- className="share-btn"
- />
-
-
- {/* 设计师信息 */}
-
-
-
-

-
-
-
- Lv.{workData.level}
- {workData.level_text}
-
-
{workData.designer}
-
-
-
-
-
- -
- 作品
-
-
- -
- 粉丝
-
-
-
-
}
- onClick={handleFollow}
- className={`follow-btn ${followed ? 'followed' : ''}`}
- >
- {followed ? '已关注' : '关注'}
-
-
-
- {/* 搜索画板 */}
-
-
搜索画板
- }
- 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 (
+
+
+
+
+
+
+
+
+
+ {workData.category || '设计素材'} · 支付后即时交付
+
+
{workData.title}
+
+ 适合活动海报、电商主图、背景素材等快速落地场景,先看预览,再决定是否下载原图。
+
+
+
+
+ {workData.views}
+ 浏览量
+
+
+ {workData.collects}
+ 收藏量
+
+
+ Lv.{workData.level}
+ {workData.level_text}
+
+
+
+
+
+
+
+
+ 水印预览
+ 可购买原图下载
+
+
+
+

+
+
+
+ {servicePromises.map((item) => (
+
+
+ {item}
+
+ ))}
+
+
+
+
+
+
相关搜索
+ 帮助用户继续筛选相似素材
+
+
+ {tags.length ? (
+ tags.map((tag, index) => (
+
+ {tag}
+
+ ))
+ ) : (
+ 暂无标签,后续上传时可补充关键词。
+ )}
+
+
+
+
+
+
猜你喜欢
+ 保持详情页浏览节奏,减少单页跳出。
+
+
+ {relatedWorks.map((work) => (
+
navigate(`/detail/${work.id}`)}>
+
+

+
+ }>
+ 查看详情
+
+
+
+
+
{work.title}
+
+
Lv.{work.level}
+
{work.levelName}
+
{work.designer}
+
+
+
+ ))}
+
+
+
+
+
+
+
+ {generateWorkNumber(workData.id)}
+
+
+
+
+
当前下载价
+
¥{workData.price}
+
+
+
+ {detailFacts.map((item) => (
+
+ {item.label}
+
+ {item.value}
+
+
+ ))}
+
+
+
+ }
+ onClick={handleDownload}
+ className="download-btn"
+ >
+ 立即下载
+
+ : }
+ onClick={handleCollect}
+ className={`icon-btn ${collected ? 'collected' : ''}`}
+ />
+ }
+ onClick={handleShare}
+ className="icon-btn"
+ />
+
+
+
+
+
+
+

+
+
+
+ Lv.{workData.level}
+ {workData.level_text}
+
+
{workData.designer}
+
+
+
+
+
+ -
+ 作品
+
+
+ -
+ 粉丝
+
+
+
+
}
+ onClick={handleFollow}
+ className={`follow-btn ${followed ? 'followed' : ''}`}
+ >
+ {followed ? '已关注' : '关注设计师'}
+
+
+
+
+
+
搜索画板
+ 继续搜同类风格
+
+
}
+ 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;