mirror of
https://github.com/Awuqing/BackupX.git
synced 2026-05-31 23:39:35 +08:00
重构: - 首页 Hero 重设计:双列布局(标题+CTA+指标 / macOS 风代码窗口) - 引入渐变文字、pulse 徽章、悬停带动画的主按钮 - 功能卡片加 SVG 图标、悬停提升效果、部分卡片变成可点击链接 - 新增 HomepageShowcase 截图轮播区:Tab 切换四个核心页面(仪表盘/任务/存储/多节点) - 全站换 Arco 蓝 (#165dff) 作为主色,紫色 (#8f4bff) 作为辅助 - 导航栏加毛玻璃效果、表格加圆角与边框、菜单项圆角化 - 深色模式配色整体收敛 内容修正: - API 参考补全遗漏的端点:auth logout/profile、records batch-delete、 storage-targets star/usage/google-drive、notifications test、dashboard timeline、settings - 把 API 表格改为"方法/端点/说明"三列,加响应结构说明 - 中英文 API 文档同步更新 i18n: - code.json 补充 Hero、Features、Showcase 全部新翻译键 - 校对:16 个中英文档 frontmatter 完全对齐,无漏译 构建:双语 build 通过、产物 3.3MB
121 lines
4.7 KiB
TypeScript
121 lines
4.7 KiB
TypeScript
import type {ReactNode} from 'react';
|
|
import {useState} from 'react';
|
|
import clsx from 'clsx';
|
|
import Heading from '@theme/Heading';
|
|
import Translate from '@docusaurus/Translate';
|
|
import useBaseUrl from '@docusaurus/useBaseUrl';
|
|
import Link from '@docusaurus/Link';
|
|
import styles from './styles.module.css';
|
|
|
|
type Tab = {
|
|
id: string;
|
|
label: ReactNode;
|
|
image: string;
|
|
title: ReactNode;
|
|
description: ReactNode;
|
|
};
|
|
|
|
function useTabs(): Tab[] {
|
|
return [
|
|
{
|
|
id: 'dashboard',
|
|
label: <Translate id="showcase.tab.dashboard">Dashboard</Translate>,
|
|
image: useBaseUrl('/img/screenshots/dashboard.png'),
|
|
title: <Translate id="showcase.dashboard.title">Know at a glance</Translate>,
|
|
description: (
|
|
<Translate id="showcase.dashboard.desc">
|
|
Backup success rates, storage usage, recent runs and upcoming schedules — all on one page with live data.
|
|
</Translate>
|
|
),
|
|
},
|
|
{
|
|
id: 'tasks',
|
|
label: <Translate id="showcase.tab.tasks">Backup Tasks</Translate>,
|
|
image: useBaseUrl('/img/screenshots/backup-tasks.png'),
|
|
title: <Translate id="showcase.tasks.title">Visual task editor</Translate>,
|
|
description: (
|
|
<Translate id="showcase.tasks.desc">
|
|
Files, MySQL, PostgreSQL, SQLite and SAP HANA with a three-step wizard. Cron editor, multi-target dispatch, retention, compression and encryption — point and click.
|
|
</Translate>
|
|
),
|
|
},
|
|
{
|
|
id: 'storage',
|
|
label: <Translate id="showcase.tab.storage">Storage Targets</Translate>,
|
|
image: useBaseUrl('/img/screenshots/storage-targets.png'),
|
|
title: <Translate id="showcase.storage.title">70+ backends, one flow</Translate>,
|
|
description: (
|
|
<Translate id="showcase.storage.desc">
|
|
Alibaba OSS, Tencent COS, S3, Google Drive, WebDAV — plus every rclone backend behind a uniform form. Test connection, favourite, and view live usage.
|
|
</Translate>
|
|
),
|
|
},
|
|
{
|
|
id: 'nodes',
|
|
label: <Translate id="showcase.tab.nodes">Multi-Node</Translate>,
|
|
image: useBaseUrl('/img/screenshots/nodes.png'),
|
|
title: <Translate id="showcase.nodes.title">Master-Agent in minutes</Translate>,
|
|
description: (
|
|
<Translate id="showcase.nodes.desc">
|
|
Create a node, copy the token, start the Agent on any remote host. Tasks routed to a node run locally there and upload directly to storage — no reverse connectivity required.
|
|
</Translate>
|
|
),
|
|
},
|
|
];
|
|
}
|
|
|
|
export default function HomepageShowcase(): ReactNode {
|
|
const tabs = useTabs();
|
|
const [active, setActive] = useState(tabs[0].id);
|
|
const current = tabs.find(t => t.id === active) ?? tabs[0];
|
|
return (
|
|
<section className={styles.section}>
|
|
<div className="container">
|
|
<div className={styles.sectionHead}>
|
|
<div className={styles.sectionTag}>
|
|
<Translate id="showcase.tag">PRODUCT</Translate>
|
|
</div>
|
|
<Heading as="h2" className={styles.sectionTitle}>
|
|
<Translate id="showcase.title">A polished console, not a DIY script</Translate>
|
|
</Heading>
|
|
<p className={styles.sectionSubtitle}>
|
|
<Translate id="showcase.subtitle">
|
|
Every screen designed for day-2 operations — visibility first, configuration second.
|
|
</Translate>
|
|
</p>
|
|
</div>
|
|
<div className={styles.tabs}>
|
|
{tabs.map(tab => (
|
|
<button
|
|
key={tab.id}
|
|
type="button"
|
|
className={clsx(styles.tabBtn, active === tab.id && styles.tabBtnActive)}
|
|
onClick={() => setActive(tab.id)}>
|
|
{tab.label}
|
|
</button>
|
|
))}
|
|
</div>
|
|
<div className={styles.stage}>
|
|
<div className={styles.browser}>
|
|
<div className={styles.browserBar}>
|
|
<span className={clsx(styles.browserDot, styles.browserDotRed)} />
|
|
<span className={clsx(styles.browserDot, styles.browserDotYellow)} />
|
|
<span className={clsx(styles.browserDot, styles.browserDotGreen)} />
|
|
<div className={styles.browserUrl}>backupx.local</div>
|
|
</div>
|
|
<img src={current.image} alt="" className={styles.screenshot} />
|
|
</div>
|
|
<div className={styles.caption}>
|
|
<Heading as="h3" className={styles.captionTitle}>{current.title}</Heading>
|
|
<p className={styles.captionDesc}>{current.description}</p>
|
|
<Link to="/docs/getting-started/quick-start" className={styles.captionLink}>
|
|
<Translate id="showcase.cta">Explore the docs</Translate>
|
|
<span aria-hidden="true"> →</span>
|
|
</Link>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
);
|
|
}
|