diff --git a/video-gen-app/folder-alias.json b/video-gen-app/folder-alias.json index 9e26dfee..886c18d6 100644 --- a/video-gen-app/folder-alias.json +++ b/video-gen-app/folder-alias.json @@ -1 +1,5 @@ -{} \ No newline at end of file +{ + "src/pages/CreativePlazaPage.tsx": { + "description": "创意广场" + } +} \ No newline at end of file diff --git a/video-gen-app/src/components/Layout/AppLayout.css b/video-gen-app/src/components/Layout/AppLayout.css index 0afc97a1..69bc8dc1 100644 --- a/video-gen-app/src/components/Layout/AppLayout.css +++ b/video-gen-app/src/components/Layout/AppLayout.css @@ -131,6 +131,17 @@ box-shadow: 0 8px 30px rgba(99, 102, 241, 0.5); } +.recharge-drawer .ant-drawer-content-wrapper { + border-top-left-radius: 20px !important; + border-top-right-radius: 20px !important; + overflow: hidden !important; +} + +.recharge-drawer .ant-drawer-content { + border-top-left-radius: 20px !important; + border-top-right-radius: 20px !important; +} + @media (max-width: 767px) { .desktop-sidebar { display: none !important; diff --git a/video-gen-app/src/components/Layout/AppLayout.tsx b/video-gen-app/src/components/Layout/AppLayout.tsx index 14d7fe5d..814a442b 100644 --- a/video-gen-app/src/components/Layout/AppLayout.tsx +++ b/video-gen-app/src/components/Layout/AppLayout.tsx @@ -1,5 +1,5 @@ import React, { useEffect, useState, useCallback, useRef } from 'react'; -import { Layout, Avatar, Dropdown, Space, Modal, Form, Input, message, Tooltip, Tag, Button, Typography, Radio, Drawer, Tabs } from 'antd'; +import { Layout, Avatar, Dropdown, Space, Modal, Form, Input, message, Tooltip, Tag, Button, Typography, Radio, Drawer, Tabs, Collapse } from 'antd'; import { QRCodeSVG } from 'qrcode.react'; import { PlayCircleOutlined, @@ -20,7 +20,9 @@ import { StarFilled, FireFilled, CrownFilled, + CrownOutlined, BankFilled, + CheckCircleFilled, CloseOutlined, WechatOutlined, AlipayCircleOutlined, @@ -310,6 +312,72 @@ const GRADIENTS = [ { gradient: 'linear-gradient(135deg, #5a67d8, #434190)', shadow: 'rgba(90,103,216,0.25)', icon: }, ]; +const MEMBERSHIP_GRADIENTS = [ + { gradient: 'linear-gradient(135deg, #94a3b8, #64748b)', shadow: 'rgba(148,163,184,0.25)', icon: }, + { gradient: 'linear-gradient(135deg, #c9a96e, #a67c52)', shadow: 'rgba(201,169,110,0.25)', icon: }, + { gradient: 'linear-gradient(135deg, #718096, #4a5568)', shadow: 'rgba(113,128,150,0.25)', icon: }, + { gradient: 'linear-gradient(135deg, #5a67d8, #434190)', shadow: 'rgba(90,103,216,0.25)', icon: }, +]; + +const MEMBERSHIP_FEATURES = [ + '每日赠送积分 当日清零', + '可充值更多积分', + '基础加速通道', + '作品去除水印', + '可使用网页版和APP上全部AI功能', + '高分辨率', + '批量创作 更多同时生成任务', +]; + +const MEMBERSHIP_PLANS = [ + { + tier: '基础会员', + desc: '开启创作之旅', + monthlyPrice: 79, + monthlyCredits: 1000, + onlyMonthly: true, + features: MEMBERSHIP_FEATURES.map((f) => f.replace('基础加速通道', '基础加速通道')), + speedLabel: '基础加速通道', + }, + { + tier: '标准会员', + desc: '畅享基础创作权益', + monthlyPrice: 154, + monthlyCredits: 2000, + quarterlyPrice: 1029, + quarterlyCredits: 5000, + yearlyPrice: 7308, + yearlyCredits: 10000, + features: MEMBERSHIP_FEATURES, + speedLabel: '标准加速通道', + }, + { + tier: '高级会员', + desc: '解锁高级创作权益', + monthlyPrice: 221, + monthlyCredits: 3000, + quarterlyPrice: 1197, + quarterlyCredits: 6000, + yearlyPrice: 10710, + yearlyCredits: 15000, + features: MEMBERSHIP_FEATURES, + speedLabel: '高级加速通道', + }, + { + tier: '超级会员', + desc: '释放无限创作生产力', + monthlyPrice: 280, + monthlyCredits: 4000, + quarterlyPrice: 1367, + quarterlyCredits: 7000, + yearlyPrice: 13440, + yearlyCredits: 20000, + features: MEMBERSHIP_FEATURES, + speedLabel: '高级加速通道', + hot: true, + }, +]; + const AppLayout: React.FC = () => { const navigate = useNavigate(); const location = useLocation(); @@ -322,6 +390,9 @@ const AppLayout: React.FC = () => { const [selectedPlan, setSelectedPlan] = useState(null); const [menuItems, setMenuItems] = useState([]); const [rechargeOptions, setRechargeOptions] = useState([]); + const [creditsModalOpen, setCreditsModalOpen] = useState(false); + const [selectedTierIndex, setSelectedTierIndex] = useState(null); + const [selectedPeriod, setSelectedPeriod] = useState<'monthly' | 'quarterly' | 'yearly'>('monthly'); const [unreadCount, setUnreadCount] = useState(0); const [mobileMenuOpen, setMobileMenuOpen] = useState(false); const [contactHovered, setContactHovered] = useState(false); @@ -1357,72 +1428,274 @@ const AppLayout: React.FC = () => { - 积分充值} open={rechargeModalOpen} - onCancel={() => { setRechargeModalOpen(false); setSelectedPlan(null); }} - width={680} - className="recharge-modal" + { setRechargeModalOpen(false); setSelectedPlan(null); setSelectedTierIndex(null); }} + height="95vh" + className="recharge-drawer" + styles={{ + header: { display: 'none' }, + body: { padding: '20px 24px 0', overflowY: 'auto', position: 'relative' }, + }} footer={ -
- - + +
+ + } + > +
+
+ { setRechargeModalOpen(false); setSelectedPlan(null); setSelectedTierIndex(null); }} + onMouseEnter={(e) => { (e.target as HTMLElement).style.color = '#6366f1'; }} + onMouseLeave={(e) => { (e.target as HTMLElement).style.color = '#94a3b8'; }} + /> +
+
+ + 选择合适的计划,助力业务提升 + +
+
+ {[ + { key: 'yearly', label: '连续包年 8折' }, + { key: 'quarterly', label: '连续包季 8折' }, + { key: 'monthly', label: '连续包月' }, + ].map((p) => ( +
{ + const period = p.key as 'monthly' | 'quarterly' | 'yearly'; + setSelectedPeriod(period); + if (period !== 'monthly' && selectedTierIndex !== null && MEMBERSHIP_PLANS[selectedTierIndex]?.onlyMonthly) { + setSelectedTierIndex(null); + } + }} + style={{ + padding: '6px 16px', borderRadius: 8, cursor: 'pointer', fontSize: 13, fontWeight: 500, + background: selectedPeriod === p.key ? 'linear-gradient(135deg, #6366f1, #8b5cf6)' : 'transparent', + color: selectedPeriod === p.key ? '#fff' : '#64748b', + transition: 'all 0.2s', whiteSpace: 'nowrap', + }} + > + {p.label} +
+ ))} +
+ {/* + API服务 + */} +
+
+
+
setCreditsModalOpen(true)} + style={{ + display: 'flex', alignItems: 'center', gap: 6, cursor: 'pointer', + padding: '6px 14px', borderRadius: 20, + background: 'linear-gradient(135deg, rgba(99,102,241,0.08), rgba(139,92,246,0.08))', + border: '1px solid rgba(99,102,241,0.2)', + transition: 'all 0.2s', + }} + onMouseEnter={(e) => { + e.currentTarget.style.background = 'linear-gradient(135deg, rgba(99,102,241,0.15), rgba(139,92,246,0.15))'; + e.currentTarget.style.borderColor = 'rgba(99,102,241,0.4)'; }} - style={{ - borderRadius: 10, fontWeight: 600, - background: selectedPlan ? 'linear-gradient(135deg, #6366f1, #8b5cf6)' : '#d1d5db', - border: 'none', boxShadow: selectedPlan ? '0 8px 24px rgba(99,102,241,0.3)' : 'none', - }}> - 确认充值 - + onMouseLeave={(e) => { + e.currentTarget.style.background = 'linear-gradient(135deg, rgba(99,102,241,0.08), rgba(139,92,246,0.08))'; + e.currentTarget.style.borderColor = 'rgba(99,102,241,0.2)'; + }} + > + + 积分充值 + {/* 8折 */} +
+
+ +
+ {MEMBERSHIP_PLANS + .map((plan, idx) => ({ plan, idx })) + .filter(({ plan }) => selectedPeriod === 'monthly' || !plan.onlyMonthly) + .map(({ plan, idx }) => { + const g = MEMBERSHIP_GRADIENTS[idx % MEMBERSHIP_GRADIENTS.length]; + const price = selectedPeriod === 'monthly' ? plan.monthlyPrice + : selectedPeriod === 'quarterly' ? plan.quarterlyPrice : plan.yearlyPrice; + const credits = selectedPeriod === 'monthly' ? plan.monthlyCredits + : selectedPeriod === 'quarterly' ? plan.quarterlyCredits : plan.yearlyCredits; + const creditsPer10 = Math.round((credits / price) * 10); + const isSelected = selectedTierIndex === idx; + const isHot = plan.hot; + return ( +
setSelectedTierIndex(idx)} + style={{ + flex: 1, + borderRadius: 16, + padding: '20px 16px 16px', + background: isHot + ? 'linear-gradient(180deg, #f0edff 0%, #fafbff 30%)' + : '#fafbff', + border: isSelected + ? '2px solid #6366f1' + : isHot + ? '2px solid #8b5cf6' + : '1px solid #f0f0f5', + cursor: 'pointer', + position: 'relative', + transition: 'all 0.2s', + display: 'flex', + flexDirection: 'column', + alignItems: 'stretch', + }} + > + {isHot && ( +
最热销
+ )} +
+ {plan.tier} + {plan.desc} +
+
+ ¥{price} + /月 +
+ + 自动续订,可随时取消。 + +
+
+ 每月 {credits.toLocaleString()} 积分 +
+
+ ¥10 = {creditsPer10} 积分 +
+
+ +
+ {plan.features.map((feature: string, fi: number) => ( +
+ + {feature} +
+ ))} +
+ + + 解锁抢先购 + +
+
+
+ ); + })}
- }> -
- - - 当前积分余额 - {user?.credits ?? 0} - {(!enabledMethods.alipay && !enabledMethods.wechat) ? (
@@ -1481,13 +1754,121 @@ const AppLayout: React.FC = () => { >联系我们
+
+ + + {/* 积分充值独立弹窗 */} + 积分充值} + open={creditsModalOpen} + onCancel={() => { setCreditsModalOpen(false); setSelectedPlan(null); }} + width={560} + footer={ +
+ + +
+ } + > +
+ + + 当前积分余额 + {user?.credits ?? 0} + + + {(!enabledMethods.alipay && !enabledMethods.wechat) ? ( +
+ + ⚠️ 暂无可用的支付方式,请联系管理员开启支付功能 + +
+ ) : ( +
+ 选择支付方式 + setPaymentMethod(e.target.value)} + style={{ display: 'flex', gap: 12 }}> + {enabledMethods.alipay && ( + + + 支付宝 + + )} + {enabledMethods.wechat && ( + + + 微信支付 + + )} + +
+ )} +
{rechargeOptions.map((opt, idx) => { const g = GRADIENTS[idx % GRADIENTS.length]; const totalCredits = (opt.credits || 0) + (opt.bonus_credits || opt.bonusCredits || 0); return (
setSelectedPlan(opt.id)} style={{ - flex: '1 1 45%', minWidth: 200, borderRadius: 16, padding: '20px 16px', + flex: '1 1 45%', minWidth: 180, borderRadius: 16, padding: '18px 14px', background: selectedPlan === opt.id ? 'rgba(99,102,241,0.04)' : '#fafbff', border: selectedPlan === opt.id ? '2px solid #6366f1' : '1px solid #f0f0f5', cursor: 'pointer', position: 'relative', transition: 'all 0.2s', @@ -1495,19 +1876,19 @@ const AppLayout: React.FC = () => { {opt.description && ( {opt.description} )} -
+
{g.icon}
- {opt.name} - {totalCredits.toLocaleString()} 积分 + {opt.name} + {totalCredits.toLocaleString()} 积分
¥{opt.price}
diff --git a/video-gen-app/src/pages/CreativePlazaPage.tsx b/video-gen-app/src/pages/CreativePlazaPage.tsx index 0f34b088..76dc0b93 100644 --- a/video-gen-app/src/pages/CreativePlazaPage.tsx +++ b/video-gen-app/src/pages/CreativePlazaPage.tsx @@ -1,56 +1,184 @@ -import React from 'react'; -import { Button, Card, message, Typography } from 'antd'; +import React, { useState, useMemo } from 'react'; +import { Button, Typography, Modal, Input, Select } from 'antd'; import { StarOutlined, - LockOutlined, GiftOutlined, - ThunderboltOutlined, - PlayCircleOutlined, - CheckCircleOutlined, - HeartOutlined, - EyeOutlined, - ArrowRightOutlined, CrownOutlined, + DownloadOutlined, + VideoCameraOutlined, + PictureOutlined, + PlayCircleOutlined, + SearchOutlined, } from '@ant-design/icons'; + const CreativePlazaPage: React.FC = () => { - const handleUnlock = () => { - message.info('请联系客服开通会员'); + const [previewVisible, setPreviewVisible] = useState(false); + const [previewUrl, setPreviewUrl] = useState(''); + const [previewType, setPreviewType] = useState<'image' | 'video'>('image'); + const [searchText, setSearchText] = useState(''); + const [selectedIndustry, setSelectedIndustry] = useState('all'); + + const openPreview = (url: string, type: 'image' | 'video') => { + setPreviewUrl(url); + setPreviewType(type); + setPreviewVisible(true); }; - // 顶部特性卡片 - const features = [ + const handleDownload = (url: string, filename: string) => { + const link = document.createElement('a'); + link.href = url; + link.download = filename; + document.body.appendChild(link); + link.click(); + document.body.removeChild(link); + }; + + + + // 行业选项 + const industryOptions = [ + { value: 'all', label: '全部行业' }, + { value: 'fashion', label: '时尚穿搭' }, + { value: 'food', label: '美食探店' }, + { value: 'travel', label: '旅行风景' }, + { value: 'tech', label: '数码科技' }, + { value: 'beauty', label: '美妆护肤' }, + { value: 'home', label: '家居生活' }, + { value: '母婴', label: '母婴亲子' }, + ]; + + // 瀑布流数据 - 包含图片和视频 + const waterfallItems = [ { - icon: , - title: 'AI 智能生成', - description: '先进 AI 技术一键产出高质量视频内容', + id: 1, + type: 'image' as const, + url: 'https://images.unsplash.com/photo-1522337360788-8b13dee7a37e?w=400&q=80', + title: '时尚街拍', + tag: '热门', + industry: 'fashion', + aspectRatio: 1.3, }, { - icon: , - title: '多格式输出', - description: '支持多种视频格式,满足不同场景需求', + id: 2, + type: 'video' as const, + url: 'https://www.w3schools.com/html/mov_bbb.mp4', + poster: 'https://images.unsplash.com/photo-1492691527719-9d1e07e534b4?w=400&q=80', + title: '自然风景', + tag: '推荐', + industry: 'travel', + aspectRatio: 1.5, }, { - icon: , - title: '品质保证', - description: '专业级画质,细节清晰,色彩鲜艳', + id: 3, + type: 'image' as const, + url: 'https://images.unsplash.com/photo-1506744038136-46273834b3fb?w=400&q=80', + title: '湖光山色', + tag: '热门', + industry: 'travel', + aspectRatio: 0.75, }, { - icon: , - title: '创意无限', - description: '丰富模板与风格,激发创作灵感', + id: 4, + type: 'image' as const, + url: 'https://images.unsplash.com/photo-1506905925346-21bda4d32df4?w=400&q=80', + title: '山脉风光', + tag: '新品', + industry: 'travel', + aspectRatio: 1.4, + }, + { + id: 5, + type: 'video' as const, + url: 'https://www.w3schools.com/html/movie.mp4', + poster: 'https://images.unsplash.com/photo-1441974231531-c6227db76b6e?w=400&q=80', + title: '森林探索', + tag: '推荐', + industry: 'travel', + aspectRatio: 0.67, + }, + { + id: 6, + type: 'image' as const, + url: 'https://images.unsplash.com/photo-1470770841072-f978cf4d019e?w=400&q=80', + title: '山间湖泊', + tag: '热门', + industry: 'travel', + aspectRatio: 1.7, + }, + { + id: 7, + type: 'image' as const, + url: 'https://images.unsplash.com/photo-1501785888041-af3ef285b470?w=400&q=80', + title: '海岸风光', + tag: '推荐', + industry: 'travel', + aspectRatio: 0.8, + }, + { + id: 8, + type: 'video' as const, + url: 'https://www.w3schools.com/html/mov_bbb.mp4', + poster: 'https://images.unsplash.com/photo-1472214103451-9374bd1c798e?w=400&q=80', + title: '城市夜景', + tag: '新品', + industry: 'travel', + aspectRatio: 1.33, + }, + { + id: 9, + type: 'image' as const, + url: 'https://images.unsplash.com/photo-1447752875215-b2761acb3c5d?w=400&q=80', + title: '森林小径', + tag: '热门', + industry: 'travel', + aspectRatio: 1.5, + }, + { + id: 10, + type: 'image' as const, + url: 'https://images.unsplash.com/photo-1504674900247-0877df9cc836?w=400&q=80', + title: '美食摆盘', + tag: '热门', + industry: 'food', + aspectRatio: 1.2, + }, + { + id: 11, + type: 'image' as const, + url: 'https://images.unsplash.com/photo-1560448204-e02f11c3d0e2?w=400&q=80', + title: '时尚穿搭', + tag: '推荐', + industry: 'fashion', + aspectRatio: 1.0, + }, + { + id: 12, + type: 'video' as const, + url: 'https://www.w3schools.com/html/mov_bbb.mp4', + poster: 'https://images.unsplash.com/photo-1498050108023-c5249f4df085?w=400&q=80', + title: '数码产品', + tag: '新品', + industry: 'tech', + aspectRatio: 1.4, }, ]; - // 精选案例占位 - const caseList = [ - { title: '美妆直播切片', tag: '热门', tagColor: '#ef4444' }, - { title: '3C 数码测评', tag: '推荐', tagColor: '#6366f1' }, - { title: '美食探店 vlog', tag: '推荐', tagColor: '#6366f1' }, - { title: '服饰穿搭合集', tag: '热门', tagColor: '#ef4444' }, - { title: '家居场景展示', tag: '推荐', tagColor: '#6366f1' }, - { title: '母婴亲子内容', tag: '热门', tagColor: '#ef4444' }, - ]; + // 筛选后的数据 + const filteredItems = useMemo(() => { + return waterfallItems.filter((item) => { + const matchSearch = searchText + ? item.title.toLowerCase().includes(searchText.toLowerCase()) + : true; + const matchIndustry = selectedIndustry === 'all' ? true : item.industry === selectedIndustry; + return matchSearch && matchIndustry; + }); + }, [searchText, selectedIndustry]); + + const handleItemClick = (item: typeof waterfallItems[0]) => { + const previewUrl = item.type === 'image' ? item.url : item.url; + openPreview(previewUrl, item.type); + }; return (
@@ -100,54 +228,7 @@ const CreativePlazaPage: React.FC = () => {
{/* 特性卡片 */} - {/*
- {features.map((feature, index) => ( - -
-
- {feature.icon} -
-
- {feature.title} -
-
- {feature.description} -
-
-
- ))} -
*/} + {/* 精选案例展示区域 */}
{ 精选案例展示
- {/* */} +
+