样式调整

This commit is contained in:
海龙 2025-08-30 22:59:25 +08:00
parent 596e695a1d
commit c9735fe15a
4 changed files with 166 additions and 100 deletions

View File

@ -102,9 +102,6 @@ function HomeModule5() {
}, [plans, billingType]);
const handleSubscribe = async (planName: string) => {
if (planName === "Kickoff") {
return;
}
try {
// 使用新的Checkout Session方案更简单

View File

@ -0,0 +1,90 @@
import React from 'react';
interface UserCardProps {
/** 用户计划名称 */
plan_name?: string;
/** 子组件内容 */
children: React.ReactNode;
}
/**
*
*
*/
export default function UserCard({
plan_name = "none",
children
}: UserCardProps) {
// 根据计划名称获取对应的样式
const getCardStyles = () => {
switch (plan_name) {
case "Kickoff":
return {
outerBg: "bg-[#3d3c3d]",
glowColor: "bg-[#c0c0c0]",
glowShadow: "shadow-[0_0_50px_rgba(192,192,192,0.3)]"
};
case "Pro":
return {
outerBg: "bg-gradient-to-br from-[#1a1a2e] via-[#16213e] to-[#0f3460]",
glowColor: "bg-[#4facfe]",
glowShadow: "shadow-[0_0_50px_rgba(79,172,254,0.4)]"
};
case "Ultra":
return {
outerBg: "bg-gradient-to-br from-[#0a4a4a] via-[#2a1f4a] to-[#4a0a4a]",
glowColor: "bg-gradient-to-r from-[rgb(106,244,249)] to-[rgb(199,59,255)]",
glowShadow: "shadow-[0_0_50px_rgba(106,244,249,0.5)]"
};
default: // none
return {
outerBg: "bg-[#1a1a1a]",
glowColor: "bg-[#333]",
glowShadow: "shadow-[0_0_50px_rgba(51,51,51,0.3)]"
};
}
};
const styles = getCardStyles();
return (
<div
data-alt="user-card-outer"
className={`relative w-[18.75rem] h-[11.5rem] overflow-hidden rounded-xl ${styles.outerBg} ${styles.glowShadow}`}
>
{/* 发光背景 */}
<div
className={`absolute w-56 h-48 ${styles.glowColor} blur-[50px] -left-1/2 -top-1/2 opacity-40`}
/>
{/* 卡片内容区域 */}
<div
data-alt="user-card"
className={`relative z-[1] w-full h-full flex items-center justify-center text-white rounded-xl overflow-hidden `}
style={{ margin: '2px' }}
>
{/* 计划名称背景文字 - 非 none 时显示 */}
{plan_name !== "none" && (
<div
data-alt="plan-name-background"
className="absolute inset-0 flex items-center justify-center z-[1] text-white/10 font-bold text-6xl tracking-wider"
style={{
transform: 'rotate(-15deg) scale(1.2)',
textShadow: '0 0 20px rgba(255,255,255,0.1)'
}}
>
{plan_name}
</div>
)}
{/* 内容区域 */}
<div
data-alt="user-card-content"
className="relative z-[2] w-full h-full flex flex-col items-center justify-center rounded-xl"
>
{children}
</div>
</div>
</div>
);
};

View File

@ -2,6 +2,7 @@
import "../pages/style/top-bar.css";
import { Button } from "@/components/ui/button";
import { GradientText } from "@/components/ui/gradient-text";
import { useTheme } from "next-themes";
import {
@ -23,6 +24,7 @@ import {
redirectToPortal,
getUserSubscriptionInfo,
} from "@/lib/stripe";
import UserCard from "@/components/common/userCard";
interface User {
id: string;
@ -33,11 +35,7 @@ interface User {
plan_name?: string;
}
export function TopBar({
collapsed,
}: {
collapsed: boolean;
}) {
export function TopBar({ collapsed }: { collapsed: boolean }) {
const router = useRouter();
const [isOpen, setIsOpen] = React.useState(false);
const menuRef = useRef<HTMLDivElement>(null);
@ -68,7 +66,7 @@ export function TopBar({
// 更新 currentUser 的 plan_name
setCurrentUser((prev) => ({
...prev,
plan_name: response.data.plan_name,
plan_name: response.data.plan_name, // HACK
}));
}
} catch (error) {
@ -169,7 +167,7 @@ export function TopBar({
return (
<div
className="fixed right-0 top-0 h-16 header z-[999]"
style={{ isolation: "isolate", left: collapsed ? '3rem' : '16rem' }}
style={{ isolation: "isolate", left: collapsed ? "3rem" : "16rem" }}
>
<div className="h-full flex items-center justify-between pr-6 pl-4">
<div className="flex items-center space-x-4">
@ -264,11 +262,11 @@ export function TopBar({
}}
data-alt="user-menu-trigger"
style={{
background: 'unset !important'
background: "unset !important",
}}
>
<div className="h-10 w-10 rounded-full bg-[#C73BFF] flex items-center justify-center text-white font-semibold">
{currentUser.username ? currentUser.username.charAt(0) : 'MF'}
{currentUser.username ? currentUser.username.charAt(0) : "MF"}
</div>
</Button>
@ -284,89 +282,73 @@ export function TopBar({
position: "fixed",
top: "4rem",
right: "1rem",
width: "18rem",
zIndex: 9999,
}}
className="bg-[#1E1E1E] rounded-lg shadow-lg overflow-hidden"
className="overflow-hidden rounded-xl"
data-alt="user-menu-dropdown"
onClick={(e) => e.stopPropagation()}
>
{/* User Info */}
<div className="p-4">
<div className="flex items-center space-x-3">
<div className="h-10 w-10 rounded-full bg-[#C73BFF] flex items-center justify-center text-white font-semibold">
{currentUser.username ? currentUser.username.charAt(0) : 'MF'}
</div>
<div className="flex-1">
<div className="flex items-center gap-2">
<p className="text-sm font-medium">
{currentUser.name || currentUser.username}
</p>
{currentUser.plan_name &&
currentUser.plan_name !== "none" && (
<span
className={`inline-flex items-center px-2 py-1 text-xs font-medium rounded-full ${
currentUser.plan_name === "Kickoff"
? "bg-blue-100 text-blue-800"
: currentUser.plan_name === "Pro"
? "bg-purple-100 text-purple-800"
: currentUser.plan_name === "Ultra"
? "bg-orange-100 text-orange-800"
: ""
}`}
>
{currentUser.plan_name}
</span>
)}
</div>
<p className="text-xs text-gray-500">
{currentUser.email}
</p>
</div>
<div
className="cursor-pointer hover:text-red-400 transition-colors duration-200"
onClick={() => {
logoutUser();
}}
title="退出登录"
>
<LogOut className="h-4 w-4" />
</div>
</div>
</div>
<UserCard plan_name={currentUser.plan_name}>
<div className="relative z-[2] w-full h-full flex flex-col text-white p-3">
{/* 顶部用户信息 */}
<div className="flex items-center space-x-3 mb-3">
<div className="h-10 w-10 rounded-full bg-[#C73BFF] flex items-center justify-center text-white font-bold text-sm flex-shrink-0">
{currentUser.username
? currentUser.username.charAt(0)
: "MF"}
</div>
<div className="flex-1 min-w-0">
<h3 className="text-sm font-semibold text-white truncate">
{currentUser.name || currentUser.username}
</h3>
<p className="text-xs text-gray-300 truncate">
{currentUser.email}
</p>
</div>
</div>
{/* AI Points */}
<div className="px-4 py-3 flex items-center justify-between">
<div className="flex items-center space-x-2">
<Sparkles className="h-4 w-4" />
<span className="text-white underline text-sm">
{isLoadingSubscription
? "..."
: `${credits} credits`}
</span>
</div>
<Button
variant="outline"
size="sm"
className="text-white border-white hover:bg-white/10 rounded-full px-3 py-1 text-[10px]"
onClick={() => {
window.open("/pricing", "_blank");
}}
>
Upgrade
</Button>
{subscriptionStatus === "ACTIVE" && (
<Button
variant="outline"
size="sm"
className="text-white border-white hover:bg-white/10 rounded-full px-3 py-1 text-[10px]"
onClick={handleManageSubscription}
disabled={isManagingSubscription}
>
Manage
</Button>
)}
</div>
{/* AI 积分 */}
<div className="flex items-center justify-center space-x-3 mb-4">
<div className="p-2 rounded-full bg-white/10 backdrop-blur-sm">
<Sparkles className="h-5 w-5 text-white" />
</div>
<span className="text-white text-base font-semibold">
{isLoadingSubscription
? "Loading..."
: `${credits} credits`}
</span>
</div>
{/* 操作按钮区域 */}
<div className="flex-1 flex flex-row justify-center items-end space-x-2 pb-2">
<button
className="flex-1 bg-transparent border border-white/30 text-white text-xs py-0.5 h-6 rounded hover:bg-white/10 transition-colors"
onClick={() => {
window.open("/pricing", "_blank");
}}
>
Upgrade
</button>
{currentUser.plan_name !== "none" && (
<button
className="flex-1 bg-transparent border border-gray-400/30 text-gray-300 text-xs py-0.5 h-6 rounded hover:bg-gray-400/10 transition-colors disabled:opacity-50"
onClick={handleManageSubscription}
disabled={isManagingSubscription}
>
Manage
</button>
)}
<button
className="flex-1 bg-transparent border border-red-400/50 text-red-300 text-xs py-0.5 h-6 rounded hover:bg-red-400/10 transition-colors"
onClick={() => logoutUser()}
>
Logout
</button>
</div>
</div>
</UserCard>
</motion.div>,
document.body
)

View File

@ -254,21 +254,21 @@ function HomeModule2() {
const videoList = [
{
title: "Text to Movie",
video: "https://cdn.qikongjian.com/videos/module2 (1).mp4",
video: "https://cdn.qikongjian.com/1756559467841_kn9fr9.mp4",
},
{
title: "Image to Movie",
video: "https://cdn.qikongjian.com/videos/module2 (2).mp4",
video: "https://cdn.qikongjian.com/1756559467840_m4ijd7.mp4",
},
{
title: "Template to Movie",
video: "https://cdn.qikongjian.com/videos/module2 (3).mp4",
video: "https://cdn.qikongjian.com/1756559467836_ij9y54.mp4",
},
];
return (
<div
data-alt="core-value-section"
className="home-module2 relative flex flex-col items-center justify-center w-full h-[1300px] bg-black snap-start"
className="home-module2 relative flex flex-col items-center justify-center w-full h-[1000px] bg-black snap-start"
>
<div
data-alt="core-value-content"
@ -277,7 +277,7 @@ function HomeModule2() {
<h2 className="text-white text-[3.375rem] leading-[100%] font-normal mb-[3rem]">
Just Drop A Thought
</h2>
<p className="text-white text-[1.125rem] leading-[140%] font-normal text-center">
<p className="text-white text-[1.7rem] leading-[140%] font-normal text-center">
Say your idea in a single line,and MovieFlow will bring it to life.
</p>
</div>
@ -355,7 +355,7 @@ function HomeModule3() {
<h2 className="text-white text-[3.375rem] leading-[100%] font-normal mb-[3rem]">
Ideas Made Real
</h2>
<p className="text-white text-[1.125rem] leading-[140%] font-normal text-center">
<p className="text-white text-[1.7rem] leading-[140%] font-normal text-center">
High-quality films, any style, made with MovieFlow.
</p>
</div>
@ -479,11 +479,11 @@ function HomeModule4() {
return (
<div
data-alt="core-value-section"
className="home-module4 h-[1280px] relative flex flex-col items-center justify-center w-full bg-black snap-start"
className="home-module4 h-[1000px] relative flex flex-col items-center justify-center w-full bg-black snap-start"
>
<div
data-alt="core-value-content"
className="center z-10 flex flex-col items-center mb-[14rem]"
className="center z-10 flex flex-col items-center mb-[10rem]"
>
<h2 className="text-white text-[3.375rem] leading-[100%] font-normal ">
Create Your Way
@ -588,9 +588,6 @@ function HomeModule5() {
}, [plans, billingType]);
const handleSubscribe = async (planName: string) => {
if (planName === "hobby") {
return;
}
localStorage.setItem("callBackUrl", pathname);
try {
// 使用新的Checkout Session方案更简单