update: 2026-03-28 20:59

This commit is contained in:
2026-03-28 20:59:52 +08:00
parent e21d58e603
commit 1c81d4e6ea
611 changed files with 27847 additions and 65061 deletions

View File

@@ -0,0 +1,196 @@
import React, { useState } from 'react';
import styled, { keyframes } from 'styled-components';
import { FiHardDrive, FiCloud } from 'react-icons/fi';
import { TOOLBOX_CATEGORIES } from '../config/Api60sConfig';
import FullscreenEmbed from '../components/FullscreenEmbed';
import {
PageWrapper,
PageHeader,
PageTitle,
FeatureGrid,
FeatureCard,
FeatureCardIcon,
FeatureCardTitle,
FeatureCardDesc,
} from '../styles/shared';
const fadeUp = keyframes`from{opacity:0;transform:translateY(12px)}to{opacity:1;transform:translateY(0)}`;
const Tabs = styled.div`
display: flex;
gap: 10px;
margin: 0 auto 18px;
max-width: 1200px;
padding: 0 20px;
overflow-x: auto;
justify-content: center;
scrollbar-width: none;
&::-webkit-scrollbar { display: none; }
@media (max-width: 640px) {
justify-content: flex-start;
padding: 0 16px;
gap: 8px;
}
`;
const Tab = styled.button`
flex-shrink: 0;
display: flex;
align-items: center;
gap: 6px;
padding: 10px 20px;
border: 1px solid ${({ $on }) => ($on ? 'transparent' : 'rgba(255,255,255,0.55)')};
border-radius: 12px;
font-size: 14px;
font-weight: 600;
font-family: inherit;
cursor: pointer;
transition: all 0.2s;
white-space: nowrap;
background: ${({ $on, $c }) =>
$on ? `linear-gradient(135deg, ${$c} 0%, #22c55e 100%)` : 'rgba(255,255,255,0.92)'};
color: ${({ $on }) => ($on ? '#fff' : '#166534')};
text-shadow: ${({ $on }) => ($on ? '0 1px 2px rgba(0,0,0,0.12)' : 'none')};
box-shadow: ${({ $on }) =>
$on ? '0 4px 16px rgba(34,197,94,0.35)' : '0 2px 8px rgba(255,255,255,0.5)'};
&:hover {
transform: translateY(-1px);
border-color: ${({ $on }) => ($on ? 'transparent' : 'rgba(129,199,132,0.8)')};
}
@media (max-width: 640px) {
padding: 9px 16px;
font-size: 13px;
border-radius: 10px;
}
`;
const Grid = styled(FeatureGrid)``;
const Card = styled(FeatureCard)`
&::before {
background: linear-gradient(90deg, ${({ $c }) => $c || '#4ade80'}, #86efac);
}
`;
const CardIcon = styled(FeatureCardIcon)``;
const CardTitle = styled(FeatureCardTitle)``;
const CardDesc = styled(FeatureCardDesc)``;
/** 右下角:可离线(本地缓存即可用) / 需联网(云端) */
const CardCornerHint = styled.span`
position: absolute;
right: 7px;
bottom: 7px;
width: 30px;
height: 30px;
border-radius: 9px;
display: flex;
align-items: center;
justify-content: center;
pointer-events: none;
background: ${({ $variant }) =>
$variant === 'offline' ? 'rgba(236, 253, 245, 0.98)' : 'rgba(255, 251, 235, 0.98)'};
color: ${({ $variant }) => ($variant === 'offline' ? '#15803d' : '#c2410c')};
border: 1px solid
${({ $variant }) =>
$variant === 'offline' ? 'rgba(34, 197, 94, 0.28)' : 'rgba(251, 146, 60, 0.35)'};
box-shadow: 0 1px 4px rgba(15, 80, 40, 0.06);
svg {
width: 16px;
height: 16px;
stroke-width: 2.25;
}
@media (max-width: 640px) {
width: 26px;
height: 26px;
right: 5px;
bottom: 5px;
border-radius: 8px;
svg {
width: 14px;
height: 14px;
}
}
`;
/** 根据工具项解析所属分类色(嵌入打开后切换 Tab 仍正确) */
function headerColorForItem(item) {
if (!item) return '#4ade80';
for (const c of TOOLBOX_CATEGORIES) {
if (c.items.some((i) => i.id === item.id)) return c.color;
}
return '#4ade80';
}
const ToolboxPage = () => {
const [catId, setCatId] = useState(TOOLBOX_CATEGORIES[0].id);
const [embedItem, setEmbedItem] = useState(null);
const cat = TOOLBOX_CATEGORIES.find((c) => c.id === catId) || TOOLBOX_CATEGORIES[0];
return (
<PageWrapper>
<PageHeader>
<PageTitle>工具箱</PageTitle>
</PageHeader>
<Tabs>
{TOOLBOX_CATEGORIES.map((c) => (
<Tab
key={c.id}
type="button"
$on={catId === c.id}
$c={c.color}
onClick={() => setCatId(c.id)}
>
{c.icon} {c.title}
</Tab>
))}
</Tabs>
<Grid key={catId}>
{cat.items.map((item) => (
<Card
key={item.id}
$c={cat.color}
onClick={() => setEmbedItem(item)}
>
<CardIcon>{item.icon}</CardIcon>
<CardTitle>{item.title}</CardTitle>
<CardDesc>{item.desc}</CardDesc>
{item.offlineOk ? (
<CardCornerHint
$variant="offline"
title="可离线PWA 缓存后页面与脚本可本地加载(使用网络流/API 时仍需联网)"
aria-label="可离线"
>
<FiHardDrive aria-hidden />
</CardCornerHint>
) : (
<CardCornerHint
$variant="online"
title="需联网:依赖云端服务"
aria-label="需联网"
>
<FiCloud aria-hidden />
</CardCornerHint>
)}
</Card>
))}
</Grid>
{embedItem && (
<FullscreenEmbed
title={embedItem.title}
url={embedItem.link}
headerColor={headerColorForItem(embedItem)}
onClose={() => setEmbedItem(null)}
/>
)}
</PageWrapper>
);
};
export default ToolboxPage;