优化样式文件,统一引号格式,增强可读性。重构朋友圈页面,添加复制功能和全局背景装饰,提升用户体验。更新组件以支持新样式和动画效果,确保更流畅的视觉体验。

This commit is contained in:
宇阳
2026-01-31 17:32:50 +08:00
parent 244587bb04
commit e5127cf7e6
10 changed files with 303 additions and 87 deletions

View File

@@ -1,5 +1,5 @@
@use '@/styles/var' as *;
@use '@/styles/fun' as *;
@use "@/styles/var" as *;
@use "@/styles/fun" as *;
.ContentMdComponent {
.markdown-body {
@@ -17,7 +17,7 @@
font-family: inherit;
&::before {
content: '';
content: "";
position: absolute;
top: 0.75rem;
left: 1rem;
@@ -25,7 +25,9 @@
height: 0.75rem;
background: #ff5f56;
border-radius: 50%;
box-shadow: 1rem 0 0 #ffbd2e, 2rem 0 0 #27c93f;
box-shadow:
1rem 0 0 #ffbd2e,
2rem 0 0 #27c93f;
z-index: 2;
}
@@ -69,7 +71,7 @@
cursor: pointer;
&::after {
content: '点击展开代码';
content: "点击展开代码";
position: absolute;
bottom: 0;
left: 0;
@@ -118,7 +120,7 @@
}
img {
@apply blur-[20px] rounded-xl hover:scale-105 cursor-pointer transition-all;
@apply blur-[20px] rounded-xl hover:scale-105 cursor-pointer transition-transform;
}
h1,
@@ -179,7 +181,7 @@
counter-reset: counter;
}
ol > li:not([id^='user-content-fn-']) {
ol > li:not([id^="user-content-fn-"]) {
&::before {
@apply absolute w-4 h-4 mt-1 leading-none left-0 rounded-full text-center text-sm border border-[#11181C] dark:border-gray-400;
counter-increment: counter;
@@ -188,7 +190,7 @@
& ol > li::before {
@apply absolute left-7 border-0 text-base mt-0 leading-normal;
content: counter(counter) '.';
content: counter(counter) ".";
}
}
@@ -252,7 +254,7 @@
}
}
input[type='checkbox'] {
input[type="checkbox"] {
width: 16px;
height: 16px;
border-radius: 4px;
@@ -298,7 +300,7 @@
font-size: 0.95em;
font-family: inherit;
}
.line-content {
flex: 1;
word-break: break-all;

View File

@@ -43,7 +43,7 @@ export default function CopyableText({ text, children, className = '' }: Copyabl
};
return (
<span className={`hover:text-primary cursor-pointer transition-colors ${className}`} onClick={handleCopy} title="点击复制">
<span className={`hover:text-primary cursor-pointer ${className}`} onClick={handleCopy} title="点击复制">
{children}
</span>
);

View File

@@ -1,97 +1,268 @@
'use client';
import React, { useState } from 'react';
import Link from 'next/link';
import { ToastContainer } from 'react-toastify';
import Slide from '@/components/Slide';
import Starry from '@/components/Starry';
import ApplyForAdd from './components/ApplyForAdd';
import CopyableText from './components/CopyableText';
import { ToastContainer, toast } from 'react-toastify';
import 'react-toastify/dist/ReactToastify.css';
import { Web } from '@/types/app/web';
import { useConfigStore, useAuthorStore } from '@/stores';
const Icons = {
Copy: () => (
<svg className="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z" />
</svg>
),
Check: () => (
<svg className="w-4 h-4 text-green-500" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M5 13l4 4L19 7" />
</svg>
),
Link: () => (
<svg className="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M13.828 10.172a4 4 0 00-5.656 0l-4 4a4 4 0 105.656 5.656l1.102-1.101m-.758-4.899a4 4 0 005.656 0l4-4a4 4 0 00-5.656-5.656l-1.1 1.1" />
</svg>
),
Rss: () => (
<svg className="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M6 9a6 6 0 016 6m-6-9a9 9 0 019 9M3 3a18 18 0 0118 18M5 21h0" />
</svg>
),
User: () => (
<svg className="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z" />
</svg>
)
};
// 默认头像
const DEFAULT_AVATAR =
"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='64' height='64' viewBox='0 0 64 64'%3E%3Ccircle fill='%23e5e7eb' cx='32' cy='32' r='32'/%3E%3Cpath fill='%239ca3af' d='M32 32a8 8 0 1 1 0-16 8 8 0 0 1 0 16zm0 8c-8 0-16 4-16 12v4h32v-4c0-8-8-12-16-12z'/%3E%3C/svg%3E";
const CopyInput = ({ label, value, icon, isCode = false, highlight = false, truncate = false }: any) => {
const [copied, setCopied] = useState(false);
const handleCopy = () => {
if (!value) return;
navigator.clipboard.writeText(value);
setCopied(true);
toast.success(`已复制: ${label}`, { autoClose: 1500 });
setTimeout(() => setCopied(false), 2000);
};
return (
<div className="group flex items-center gap-3 p-2 pl-3 bg-white dark:bg-gray-800/60 rounded-xl border border-gray-200 dark:border-gray-700/80 hover:border-primary/50 dark:hover:border-primary/50 transition-[transform,box-shadow] duration-300 shadow-sm hover:shadow-md">
<div className={`shrink-0 w-9 h-9 flex items-center justify-center rounded-lg ${highlight ? 'bg-orange-100 text-orange-600 dark:bg-orange-900/30 dark:text-orange-400' : 'bg-gray-100 text-gray-500 dark:bg-gray-700/50 dark:text-gray-400 group-hover:text-primary dark:group-hover:text-primary'}`}>
{icon}
</div>
<div className="flex-1 min-w-0 flex flex-col justify-center">
<span className="text-[10px] uppercase tracking-wider text-gray-400 font-semibold mb-0.5">
{label}
</span>
<div className={`text-sm ${isCode ? 'font-mono text-xs tracking-tight' : 'font-medium'} text-gray-800 dark:text-gray-200 ${truncate ? 'truncate' : ''}`}>
{value || 'Wait for loading...'}
</div>
</div>
<button
onClick={handleCopy}
className="shrink-0 p-2 mr-1 rounded-lg hover:bg-gray-100 dark:hover:bg-gray-700 text-gray-400 hover:text-primary transition-transform active:scale-95"
title="点击复制"
>
{copied ? <Icons.Check /> : <Icons.Copy />}
</button>
</div>
);
};
export default ({ data }: { data: { [string: string]: { order: number; list: Web[] } } }) => {
const web = useConfigStore((state) => state.web);
const author = useAuthorStore((state) => state.author);
return (
<>
<Slide isRipple={false}>
{/* 星空背景组件 */}
<Starry />
<title>😇 </title>
<meta name="description" content="😇 朋友圈" />
<div className="absolute top-[30%] left-[50%] transform -translate-x-1/2 flex flex-col items-center">
<div className="text-white text-[20px] xs:text-[25px] sm:text-[30px] whitespace-nowrap custom_text_shadow"></div>
<div className="mt-4 sm:mt-8">
<ApplyForAdd />
</div>
</div>
</Slide>
{/* 全局背景装饰 */}
<div className="fixed inset-0 overflow-hidden pointer-events-none">
<div className="absolute inset-0 bg-[linear-gradient(rgba(148,163,184,0.06)_1px,transparent_1px),linear-gradient(90deg,rgba(148,163,184,0.06)_1px,transparent_1px)] bg-[size:64px_64px]" />
<div className="absolute -top-1/2 left-1/2 -translate-x-1/2 w-[800px] h-[800px] rounded-full bg-primary/5 blur-[120px]" />
<div className="absolute top-1/4 right-0 w-96 h-96 rounded-full bg-violet-400/5 blur-[80px]" />
<div className="absolute bottom-1/4 left-0 w-80 h-80 rounded-full bg-cyan-400/5 blur-[80px]" />
</div>
<div className="bg-[linear-gradient(180deg,#edf6ff_0%,#ffffff_100%)] dark:bg-[linear-gradient(to_right,#232931_0%,#232931_100%)]">
<div className="relative -top-20 xs:-top-20 sm:-top-32 md:-top-36 w-[90%] xl:w-[1200px] p-10 pt-2 mx-auto bg-white dark:bg-black-b border dark:border-black-b rounded-2xl space-y-8 ">
<div>
<h3 className="w-full text-center text-xl p-4 dark:text-white "></h3>
<div className="min-h-screen bg-gray-50 dark:bg-[#0f172a]">
<main className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 pb-20 pt-24 relative z-20 space-y-16">
<div className="mx-auto p-3 space-y-2 border-l-[3px] border-primary bg-[#ecf7fe] dark:bg-[#333b48] rounded-md text-sm text-black-b dark:text-gray-300">
<p>
<CopyableText text={web?.title}>{web?.title}</CopyableText>
</p>
<p>
<CopyableText text={web?.description}>{web?.description}</CopyableText>
</p>
<p>
<CopyableText text={author?.avatar || ''}>{author?.avatar}</CopyableText>
</p>
<p>
<CopyableText text={web?.url}>{web?.url}</CopyableText>
</p>
<p>
Rss地址<CopyableText text={web?.url + '/api/rss'}>{web?.url + '/api/rss'}</CopyableText>
</p>
<section className="relative group w-full mx-auto">
{/* 卡片背后的光晕装饰 */}
<div className="absolute -inset-0.5 bg-gradient-to-r from-primary/30 to-purple-600/30 rounded-[2.5rem] blur-2xl opacity-20 group-hover:opacity-40 transition duration-1000" />
{/* 卡片主体 */}
<div className="relative bg-white/70 dark:bg-[#1e293b]/60 backdrop-blur-xl border border-white/50 dark:border-gray-700/50 rounded-3xl shadow-2xl overflow-hidden ring-1 ring-gray-900/5">
<div className="p-6 md:p-8 lg:p-10">
<div className="grid grid-cols-1 lg:grid-cols-12 gap-8 lg:gap-12 items-center">
<div className="lg:col-span-5 flex flex-col justify-center items-center text-center">
{/* Logo/头像展示区 */}
<div
className="relative group/avatar cursor-pointer animate-avatar-in"
>
{/* 旋转光晕边框 */}
<div className="absolute -inset-1 rounded-full opacity-60 blur group-hover/avatar:opacity-100 transition duration-500 animate-tilt bg-gradient-to-r from-primary/40 to-purple-500/40" />
<div className="relative w-32 h-32 md:w-36 md:h-36 rounded-full p-1.5 bg-white dark:bg-gray-800 mb-4 transition-transform duration-300 group-hover/avatar:scale-105 animate-avatar-float">
<img
src={author?.avatar || '/favicon.ico'}
alt="Site Logo"
className="w-full h-full rounded-full object-cover border border-gray-100 dark:border-gray-700 shadow-inner bg-white dark:bg-gray-900 transition-transform duration-300 group-hover/avatar:rotate-6"
/>
</div>
</div>
{/* 标题与描述 */}
<div className="space-y-1">
<h2 className="text-2xl font-bold text-gray-900 dark:text-white text-center">
{web?.title}
</h2>
<p className="text-sm text-gray-500 dark:text-gray-400 font-medium">
{web?.description}
</p>
</div>
</div>
<div className="lg:col-span-7 w-full">
<div className="bg-gray-50/80 dark:bg-black/20 rounded-2xl p-6 border border-gray-100 dark:border-gray-700/30">
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
{/* 1. 站点名称 */}
<div className="md:col-span-1">
<CopyInput
label="Site Title"
value={web?.title}
icon={<span className="font-serif font-bold text-lg">T</span>}
/>
</div>
<div className="md:col-span-1">
<CopyInput
label="Site Desc"
value={web?.description}
icon={<span className="font-serif font-bold text-lg">D</span>}
truncate
/>
</div>
<div className="md:col-span-2">
<CopyInput
label="URL Address"
value={web?.url}
icon={<Icons.Link />}
isCode
/>
</div>
<div className="md:col-span-2">
<CopyInput
label="Avatar Source"
value={author?.avatar}
icon={<div className="text-[10px] font-bold">IMG</div>}
isCode
truncate
/>
</div>
<div className="md:col-span-2">
<CopyInput
label="RSS Feed"
value={web?.url ? `${web.url}/api/rss` : ''}
icon={<Icons.Rss />}
isCode
highlight
/>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</section>
{Object.keys(data)?.map((type, index) => (
<div key={index}>
<h3 className="w-full text-center text-xl p-4 dark:text-white ">{type}</h3>
<section key={index} className="animate-fade-in-up" style={{ animationDelay: `${index * 100}ms` }}>
<div className="flex items-center gap-4 mb-8">
<div className="relative">
<h3 className="text-2xl font-bold text-gray-900 dark:text-gray-100 z-10 relative px-2">
{type}
</h3>
<span className="absolute bottom-1 left-0 w-full h-3 bg-primary/20 -skew-x-12 rounded-sm"></span>
</div>
<div className="h-px flex-1 bg-gradient-to-r from-gray-200 to-transparent dark:from-gray-800"></div>
</div>
<div className="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-2">
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-6">
{type === '全站置顶' && (
<Link href="https://liuyuyang.net" target="_blank" className="group">
<div className="flex items-center p-3 border group-hover:border-2 dark:border-[#3d4653] group-hover:!border-primary group-hover:shadow-[0_10px_20px_1px_rgb(83,157,253,.1)] rounded-md ">
<img src="https://q1.qlogo.cn/g?b=qq&nk=3311118881&s=640" alt="项目作者" className="w-14 h-14 mr-4 rounded-full" />
<div className="flex flex-col space-y-2">
<h4 className="text-sm text-gray-700 dark:text-white group-hover:text-primary"></h4>
<p className="text-xs text-[#8c9ab1] line-clamp-2">ThriveX </p>
<Link href="https://liuyuyang.net" target="_blank" className="group block h-full">
<div className="h-full flex items-center p-5 bg-gradient-to-br from-primary/5 to-purple-500/5 dark:from-primary/10 dark:to-purple-500/10 border border-primary/20 dark:border-primary/30 rounded-2xl transition-[transform,box-shadow] duration-300 hover:-translate-y-1 hover:shadow-xl hover:shadow-primary/10 hover:border-primary/50 relative overflow-hidden">
<div className="absolute top-0 right-0 px-2 py-1 bg-primary text-[10px] font-bold text-white rounded-bl-xl shadow-sm">OWNER</div>
<img
src="https://q1.qlogo.cn/g?b=qq&nk=3311118881&s=640"
alt="项目作者"
className="w-16 h-16 rounded-full border-2 border-white dark:border-gray-700 shadow-md group-hover:rotate-6 transition-transform duration-300"
/>
<div className="ml-4 flex-1 min-w-0">
<h4 className="text-base font-bold text-gray-900 dark:text-white group-hover:text-primary"></h4>
<p className="text-xs text-gray-500 dark:text-gray-400 mt-1 line-clamp-2 font-medium">ThriveX </p>
</div>
</div>
</Link>
)}
{data[type].list?.map((item: Web) => (
<Link key={item.id} href={item.url} target="_blank" className="group">
<div key={item.id} className="flex items-center p-3 border group-hover:border-2 dark:border-[#3d4653] group-hover:!border-primary group-hover:shadow-[0_10px_20px_1px_rgb(83,157,253,.1)] rounded-md ">
<img src={item.image} alt={item.title} className="w-14 h-14 mr-4 rounded-full" />
<Link key={item.id} href={item.url} target="_blank" className="group block h-full">
<div className="h-full flex items-start p-4 bg-white dark:bg-[#1e293b]/80 backdrop-blur-sm border border-gray-100 dark:border-gray-700/60 rounded-2xl transition-[transform,box-shadow] duration-300 hover:-translate-y-1 hover:shadow-lg hover:shadow-gray-200/50 dark:hover:shadow-black/30 hover:border-primary/40 dark:hover:border-primary/40 group-hover:bg-white/80 dark:group-hover:bg-[#1e293b]">
<div className="relative shrink-0">
<img
src={item.image}
alt={item.title}
className="w-12 h-12 rounded-xl object-cover bg-gray-100 dark:bg-gray-800 border border-gray-100 dark:border-gray-700 transition-transform duration-300 group-hover:scale-110"
onError={(e) => {
const el = e.target as HTMLImageElement;
if (el.src !== DEFAULT_AVATAR) el.src = DEFAULT_AVATAR;
}}
/>
</div>
<div className="flex flex-col space-y-2">
<h4 className="text-sm text-gray-700 dark:text-white group-hover:text-primary">{item.title}</h4>
<p className="text-xs text-[#8c9ab1] line-clamp-2">{item.description}</p>
<div className="ml-4 flex-1 min-w-0">
<h4 className="text-sm font-bold text-gray-800 dark:text-gray-100 group-hover:text-primary truncate">
{item.title}
</h4>
<p className="text-xs text-gray-500 dark:text-gray-400 mt-1.5 leading-relaxed line-clamp-2 group-hover:text-gray-600 dark:group-hover:text-gray-300">
{item.description || '暂无介绍...'}
</p>
</div>
</div>
</Link>
))}
</div>
</div>
</section>
))}
</div>
</div>
</main>
<ToastContainer />
<ToastContainer
position="bottom-right"
autoClose={3000}
hideProgressBar={false}
newestOnTop={false}
closeOnClick
rtl={false}
pauseOnFocusLoss
draggable
pauseOnHover
theme="colored"
/>
</div>
</>
);
};

View File

@@ -69,17 +69,17 @@ export default function RecordCard({ id, content, images, createTime, user }: Re
{/* Footer: 互动栏 (装饰用) */}
{/* <div className="mt-5 pt-4 border-t border-slate-50 dark:border-slate-700/50 flex items-center justify-end gap-4 text-slate-400 dark:text-slate-500 text-sm">
<button type="button" className="flex items-center gap-1 hover:text-slate-600 dark:hover:text-slate-300 transition-colors" aria-label="喜欢">
<button type="button" className="flex items-center gap-1 hover:text-slate-600 dark:hover:text-slate-300 " aria-label="喜欢">
<svg className="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M4.318 6.318a4.5 4.5 0 000 6.364L12 20.364l7.682-7.682a4.5 4.5 0 00-6.364-6.364L12 7.636l-1.318-1.318a4.5 4.5 0 00-6.364 0z" />
</svg>
</button>
<button type="button" className="flex items-center gap-1 hover:text-slate-600 dark:hover:text-slate-300 transition-colors" aria-label="评论">
<button type="button" className="flex items-center gap-1 hover:text-slate-600 dark:hover:text-slate-300 " aria-label="评论">
<svg className="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M8 12h.01M12 12h.01M16 12h.01M21 12c0 4.418-4.03 8-9 8a9.863 9.863 0 01-4.255-.949L3 20l1.395-3.72C3.512 15.042 3 13.574 3 12c0-4.418 4.03-8 9-8s9 3.582 9 8z" />
</svg>
</button>
<button type="button" className="flex items-center gap-1 hover:text-slate-600 dark:hover:text-slate-300 transition-colors" aria-label="分享">
<button type="button" className="flex items-center gap-1 hover:text-slate-600 dark:hover:text-slate-300 " aria-label="分享">
<svg className="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M8.684 13.342C8.886 12.938 9 12.482 9 12c0-.482-.114-.938-.316-1.342m0 2.684a3 3 0 110-2.684m0 2.684l6.632 3.316m-6.632-6l6.632-3.316m0 0a3 3 0 105.367-2.684 3 3 0 00-5.367 2.684zm0 9.316a3 3 0 105.368 2.684 3 3 0 00-5.368-2.684z" />
</svg>

View File

@@ -240,19 +240,19 @@ export default ({ data }: { data: Resume }) => {
<div className="space-y-3">
{safeLinks?.github && (
<a href={safeLinks.github || defaultLinks.github} target="_blank" rel="noopener noreferrer" className="flex items-center text-blue-600 hover:text-blue-800 dark:text-blue-400 dark:hover:text-blue-300 text-sm font-medium transition-colors">
<a href={safeLinks.github || defaultLinks.github} target="_blank" rel="noopener noreferrer" className="flex items-center text-blue-600 hover:text-blue-800 dark:text-blue-400 dark:hover:text-blue-300 text-sm font-medium ">
<FaGithub className="mr-2" size={16} /> GitHub
</a>
)}
{safeLinks?.csdn && (
<a href={safeLinks.csdn || defaultLinks.csdn} target="_blank" rel="noopener noreferrer" className="flex items-center text-blue-600 hover:text-blue-800 dark:text-blue-400 dark:hover:text-blue-300 text-sm font-medium transition-colors">
<a href={safeLinks.csdn || defaultLinks.csdn} target="_blank" rel="noopener noreferrer" className="flex items-center text-blue-600 hover:text-blue-800 dark:text-blue-400 dark:hover:text-blue-300 text-sm font-medium ">
<FaGlobe className="mr-2" size={16} /> CSDN
</a>
)}
{safeLinks?.blog && (
<a href={safeLinks.blog || defaultLinks.blog} target="_blank" rel="noopener noreferrer" className="flex items-center text-blue-600 hover:text-blue-800 dark:text-blue-400 dark:hover:text-blue-300 text-sm font-medium transition-colors">
<a href={safeLinks.blog || defaultLinks.blog} target="_blank" rel="noopener noreferrer" className="flex items-center text-blue-600 hover:text-blue-800 dark:text-blue-400 dark:hover:text-blue-300 text-sm font-medium ">
<FaProjectDiagram className="mr-2" size={16} />
</a>
)}
@@ -375,7 +375,7 @@ export default ({ data }: { data: Resume }) => {
{project.repositories?.map((item, index) => (
<div key={index} className="flex items-center">
<span>{item.name || '未命名链接'}</span>
<a href={(item.url as string) || '#'} target="_blank" rel="noopener noreferrer" className="text-blue-600 hover:text-blue-800 dark:text-blue-400 dark:hover:text-blue-300 text-xs font-medium bg-blue-50 dark:bg-blue-900/30 px-2 py-1 rounded transition-colors">
<a href={(item.url as string) || '#'} target="_blank" rel="noopener noreferrer" className="text-blue-600 hover:text-blue-800 dark:text-blue-400 dark:hover:text-blue-300 text-xs font-medium bg-blue-50 dark:bg-blue-900/30 px-2 py-1 rounded ">
{item.url || '未提供链接'}
</a>
</div>

View File

@@ -306,7 +306,7 @@ export default ({ onEmojiSelect, className = '' }: Props) => {
const horizontalClass = colIndex === 0 ? 'left-0 translate-x-0' : colIndex === 5 ? 'right-0 translate-x-0' : 'left-1/2 -translate-x-1/2';
return (
<button key={`${emojiItem.emoji}-${index}`} onClick={() => handleEmojiClick(emojiItem)} className="w-10 h-10 flex items-center justify-center text-2xl hover:bg-gray-100 rounded-lg transition-colors duration-150 focus:outline-none focus:ring-2 focus:ring-blue-400 cursor-pointer group relative" aria-label={emojiItem.name}>
<button key={`${emojiItem.emoji}-${index}`} onClick={() => handleEmojiClick(emojiItem)} className="w-10 h-10 flex items-center justify-center text-2xl hover:bg-gray-100 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-400 cursor-pointer group relative" aria-label={emojiItem.name}>
{emojiItem.emoji}
{/* 悬停时显示名称:第一排向下,其余向上,并做左右边界处理 */}
<div className={`absolute ${isFirstRow ? 'top-full mt-2' : 'bottom-full mb-2'} ${horizontalClass} transform px-2 py-1 bg-gray-800 text-white text-xs rounded opacity-0 group-hover:opacity-100 transition-opacity duration-200 whitespace-nowrap z-10 pointer-events-none`}>{emojiItem.name}</div>

View File

@@ -23,7 +23,7 @@ export default () => {
const { isDark, setIsDark, theme } = useConfigStore();
// 这些路径段不需要改变导航样式
const isPathSty = ['/my', '/wall', '/record', '/equipment', '/tags', '/resume', '/album', '/fishpond'].some((path) => patchName.includes(path));
const isPathSty = ['/my', '/wall', '/record', '/equipment', '/tags', '/resume', '/album', '/fishpond', '/friend'].some((path) => patchName.includes(path));
// 是否改变导航样式
const [isScrolled, setIsScrolled] = useState(false);
@@ -69,7 +69,7 @@ export default () => {
return (
<>
<div className={`header fixed top-0 w-full h-16 backdrop-blur-[5px] z-50 after:content-[''] after:block after:w-full after:h-0 after:bg-[linear-gradient(#fff,transparent_70%)] dark:after:bg-[linear-gradient(#2b333e,transparent_70%)] after: ${isPathSty || isScrolled ? 'bg-[rgba(255,255,255,0.9)] dark:bg-[rgba(44,51,62,0.9)] border-b dark:border-[#2b333e] after:!h-8 after:transition-height]' : 'border-transparent'} transition-border`}>
<div className={`header fixed top-0 w-full h-16 backdrop-blur-[5px] z-50 after:content-[''] after:block after:w-full after:h-0 after:bg-[linear-gradient(#fff,transparent_70%)] dark:after:bg-[linear-gradient(#2b333e,transparent_70%)] after: ${isPathSty || isScrolled ? 'bg-[rgba(255,255,255,0.7)] dark:bg-[rgba(44,51,62,0.7)] backdrop-blur-md border-b dark:border-[#2b333e] after:!h-8 after:transition-height]' : 'border-transparent'}`}>
<div className="relative flex justify-center lg:justify-start w-full lg:w-[1500px] h-16 mx-auto">
<div className={`lg:hidden group absolute top-0 left-0 h-full py-2 px-3 pl-7 ${isPathSty || isScrolled ? 'hover:bg-[#e9edf4] dark:hover:bg-[#455162] rounded-lg' : ''} cursor-pointer `} onClick={() => setIsOpenSidebarNav(true)}>
<BsTextIndentLeft className={`group-hover:text-primary h-full text-[30px] ${isPathSty || isScrolled ? 'text-[#333] dark:text-white' : 'text-white'} `} />
@@ -104,7 +104,7 @@ export default () => {
<ul className="hidden group-hover/one:block overflow-hidden absolute top-[50px] w-full rounded-md backdrop-blur-[5px] bg-[rgba(255,255,255,0.95)] dark:bg-[rgba(44,51,62,0.95)]" style={{ boxShadow: '0 12px 32px rgba(0, 0, 0, 0.1), 0 2px 6px rgba(0, 0, 0, 0.08)' }}>
{one.children?.map((two) => (
<li key={two.id} className="group/two">
<Link href={`/cate/${two.id}?name=${two.name}`} target={`${two.url.startsWith('http') ? '_blank' : '_self'}`} className="relative inline-block w-full p-2.5 text-[15px] box-border text-[#666] dark:text-white hover:!text-primary transition-all after:content-[''] after:absolute after:left-2.5 after:top-1/2 after:-translate-y-1/2 after:w-0 after:h-[3px] after:bg-primary after:transition-width group-hover/two:bg-[#f2f2f2] dark:group-hover/two:bg-[#323e50] group-hover/two:pl-8 hover:after:w-2.5">
<Link href={`/cate/${two.id}?name=${two.name}`} target={`${two.url.startsWith('http') ? '_blank' : '_self'}`} className="relative inline-block w-full p-2.5 text-[15px] box-border text-[#666] dark:text-white hover:!text-primary transition-[padding] after:content-[''] after:absolute after:left-2.5 after:top-1/2 after:-translate-y-1/2 after:w-0 after:h-[3px] after:bg-primary after:transition-[width] group-hover/two:bg-[#f2f2f2] dark:group-hover/two:bg-[#323e50] group-hover/two:pl-8 hover:after:w-2.5">
{two.name}
</Link>
</li>
@@ -129,7 +129,7 @@ export default () => {
<ul className="hidden group-hover/one:block overflow-hidden absolute top-[50px] w-full rounded-md backdrop-blur-sm bg-[rgba(255,255,255,0.95)] dark:bg-[rgba(44,51,62,0.95)]" style={{ boxShadow: '0 12px 32px rgba(0, 0, 0, 0.1), 0 2px 6px rgba(0, 0, 0, 0.08)' }}>
{one.children?.map((two) => (
<li key={two.id} className="group/two relative">
<Link href={two.url} className={`relative inline-block w-full p-2.5 pl-5 text-[15px] box-border text-[#666] dark:text-white hover:!text-primary transition-all after:content-[''] after:absolute after:left-2.5 after:top-1/2 after:-translate-y-1/2 after:w-0 after:h-[3px] after:bg-primary after:transition-width group-hover/two:pl-8 hover:after:w-2.5`}>
<Link href={two.url} className={`relative inline-block w-full p-2.5 pl-5 text-[15px] box-border text-[#666] dark:text-white hover:!text-primary transition-[padding] after:content-[''] after:absolute after:left-2.5 after:top-1/2 after:-translate-y-1/2 after:w-0 after:h-[3px] after:bg-primary after:transition-[width] group-hover/two:pl-8 hover:after:w-2.5`}>
{two.icon} {two.name}
</Link>
</li>
@@ -148,7 +148,6 @@ export default () => {
</div>
{/* 侧边导航:移动端时候显示 */}
{/* <SidebarNav list={cateList} open={isOpenSidebarNav} onClose={() => setIsOpenSidebarNav(false)} /> */}
<SidebarNav list={cateList} open={isOpenSidebarNav} onClose={() => setIsOpenSidebarNav(false)} />
</>
);

View File

@@ -34,7 +34,7 @@ const HotArticle = () => {
<div className="w-full pt-2.5 mt-2 min-h-[120px] space-y-4">
{list?.map((item, index) => (
<div key={index} className="item relative h-32 bg-no-repeat bg-center rounded-md transition-all after:content-[''] after:absolute after:bottom-0 after:left-0 after:w-full after:h-12 after:transition-opacity after:rounded-md after:bg-[linear-gradient(transparent,#000)]" style={{ backgroundImage: `url(${item.cover || covers[getRandom(0, covers.length - 1)]})` }}>
<div key={index} className="item relative h-32 bg-no-repeat bg-center rounded-md after:content-[''] after:absolute after:bottom-0 after:left-0 after:w-full after:h-12 after:transition-opacity after:rounded-md after:bg-[linear-gradient(transparent,#000)]" style={{ backgroundImage: `url(${item.cover || covers[getRandom(0, covers.length - 1)]})` }}>
<Link href={`/article/${item.id}`} target="_blank" className="inline-block w-full h-full">
<h4 className=" absolute bottom-2.5 w-[95%] px-2.5 text-white text-[15px] font-normal line-clamp-1 z-10">{item.title}</h4>
</Link>

View File

@@ -41,7 +41,7 @@ export default ({ data, className }: { data: SwiperType[]; className?: string })
return (
<>
<div className={`group relative w-full h-[200px] sm:h-[270px] lg:h-[350px] rounded-2xl overflow-hidden after:content-[''] after:w-full after:h-[60%] after:absolute after:bottom-0 after:left-0 after:bg-[linear-gradient(to_top,#2c333e,transparent)] ${className}`} onMouseEnter={() => setIsHovered(true)} onMouseLeave={() => setIsHovered(false)}>
<div onClick={handlePrev} className="flex justify-center items-center w-11 h-11 bg-[#fff3] rounded-full hover:bg-transparent hover:backdrop-blur-md hover:scale-110 group-hover:opacity-100 opacity-0 transition-all absolute top-1/2 left-3.5 -translate-y-1/2 z-20 cursor-pointer">
<div onClick={handlePrev} className="flex justify-center items-center w-11 h-11 bg-[#fff3] rounded-full hover:bg-transparent hover:backdrop-blur-md hover:scale-110 group-hover:opacity-100 opacity-0 transition-[transform,opacity] absolute top-1/2 left-3.5 -translate-y-1/2 z-20 cursor-pointer">
<BiChevronLeft className="text-4xl text-gray-100" />
</div>
@@ -58,7 +58,7 @@ export default ({ data, className }: { data: SwiperType[]; className?: string })
</div>
))}
<div onClick={handleNext} className="flex justify-center items-center w-11 h-11 bg-[#fff3] rounded-full hover:bg-transparent hover:backdrop-blur-md hover:scale-110 group-hover:opacity-100 opacity-0 transition-all absolute top-1/2 right-3.5 -translate-y-1/2 z-20 cursor-pointer">
<div onClick={handleNext} className="flex justify-center items-center w-11 h-11 bg-[#fff3] rounded-full hover:bg-transparent hover:backdrop-blur-md hover:scale-110 group-hover:opacity-100 opacity-0 transition-[transform,opacity] absolute top-1/2 right-3.5 -translate-y-1/2 z-20 cursor-pointer">
<BiChevronRight className="text-4xl text-gray-100" />
</div>

View File

@@ -146,6 +146,50 @@ body {
// 确保 transform 在动画结束后可以被 hover 覆盖
}
// 头像区域:入场 + 轻微浮动 + 光晕旋转
@keyframes avatar-in {
from {
opacity: 0;
transform: scale(0.88);
}
to {
opacity: 1;
transform: scale(1);
}
}
@keyframes avatar-float {
0%,
100% {
transform: translateY(0);
}
50% {
transform: translateY(-6px);
}
}
@keyframes tilt {
0%,
100% {
transform: rotate(-5deg);
}
50% {
transform: rotate(5deg);
}
}
.animate-avatar-in {
animation: avatar-in 0.6s cubic-bezier(0.34, 1.56, 0.64, 1) forwards;
}
.animate-avatar-float {
animation: avatar-float 3s ease-in-out infinite;
}
.animate-tilt {
animation: tilt 5s ease-in-out infinite;
}
// 瀑布流布局
.masonry-grid {
display: -webkit-box;