mirror of
https://github.com/justhtmls/html-tools.git
synced 2026-08-31 02:03:51 +08:00
4d18d4236e
2.取消加载远程资源,修复因onerror的错误写法导致内网使用时无法切换加载本地静态资源的问题 Closes #8
916 lines
30 KiB
HTML
916 lines
30 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="zh-CN">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>JustHTMLs - 开源 HTML 工具集</title>
|
|
<meta name="description" content="JustHTMLs 是一个开源的 HTML 工具集平台,汇集各种轻量级的在线工具,无需安装,即开即用。">
|
|
<link rel="canonical" href="https://www.htmls.dev/">
|
|
|
|
<!-- Open Graph / Facebook -->
|
|
<meta property="og:type" content="website">
|
|
<meta property="og:url" content="https://www.htmls.dev/">
|
|
<meta property="og:title" content="JustHTMLs - 开源 HTML 工具集">
|
|
<meta property="og:description" content="JustHTMLs 是一个开源的 HTML 工具集平台,汇集各种轻量级的在线工具,无需安装,即开即用。">
|
|
|
|
<!-- Twitter -->
|
|
<meta property="twitter:card" content="summary_large_image">
|
|
<meta property="twitter:url" content="https://www.htmls.dev/">
|
|
<meta property="twitter:title" content="JustHTMLs - 开源 HTML 工具集">
|
|
<meta property="twitter:description" content="JustHTMLs 是一个开源的 HTML 工具集平台,汇集各种轻量级的在线工具,无需安装,即开即用。">
|
|
|
|
<link rel="icon" href="favicon.svg">
|
|
|
|
<!-- Structured Data (JSON-LD) -->
|
|
<script type="application/ld+json">
|
|
{
|
|
"@context": "https://schema.org",
|
|
"@type": "WebSite",
|
|
"name": "JustHTMLs",
|
|
"url": "https://www.htmls.dev",
|
|
"description": "开源 HTML 工具集 - 轻量、隐私、无需安装",
|
|
"inLanguage": "zh-CN",
|
|
"publisher": {
|
|
"@type": "Organization",
|
|
"name": "JustHTMLs",
|
|
"url": "https://www.htmls.dev",
|
|
"logo": {
|
|
"@type": "ImageObject",
|
|
"url": "https://www.htmls.dev/favicon.svg"
|
|
},
|
|
"sameAs": [
|
|
"https://github.com/justhtmls/html-tools"
|
|
]
|
|
},
|
|
"potentialAction": {
|
|
"@type": "SearchAction",
|
|
"target": {
|
|
"@type": "EntryPoint",
|
|
"urlTemplate": "https://www.htmls.dev/?search={search_term_string}"
|
|
},
|
|
"query-input": "required name=search_term_string"
|
|
}
|
|
}
|
|
</script>
|
|
<script type="application/ld+json">
|
|
{
|
|
"@context": "https://schema.org",
|
|
"@type": "Organization",
|
|
"name": "JustHTMLs",
|
|
"url": "https://www.htmls.dev",
|
|
"logo": "https://www.htmls.dev/favicon.svg",
|
|
"description": "开源 HTML 工具集平台,汇集各种轻量级的在线工具",
|
|
"sameAs": [
|
|
"https://github.com/justhtmls/html-tools"
|
|
]
|
|
}
|
|
</script>
|
|
|
|
<link rel="stylesheet" href="assets/vendor/fontawesome/css/all.min.css">
|
|
<style>
|
|
:root {
|
|
--primary: #6366f1;
|
|
--primary-dark: #4f46e5;
|
|
--secondary: #8b5cf6;
|
|
--bg: #0f172a;
|
|
--bg-card: #1e293b;
|
|
--bg-hover: #334155;
|
|
--text: #f1f5f9;
|
|
--text-muted: #94a3b8;
|
|
--border: #334155;
|
|
--shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.3);
|
|
--shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, 0.4);
|
|
}
|
|
|
|
* {
|
|
margin: 0;
|
|
padding: 0;
|
|
box-sizing: border-box;
|
|
}
|
|
|
|
body {
|
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, sans-serif;
|
|
background: var(--bg);
|
|
color: var(--text);
|
|
line-height: 1.6;
|
|
min-height: 100vh;
|
|
}
|
|
|
|
.container {
|
|
max-width: 1200px;
|
|
margin: 0 auto;
|
|
padding: 0 20px;
|
|
}
|
|
|
|
/* Header */
|
|
header {
|
|
background: var(--bg-card);
|
|
border-bottom: 1px solid var(--border);
|
|
padding: 20px 0;
|
|
position: sticky;
|
|
top: 0;
|
|
z-index: 100;
|
|
}
|
|
|
|
.header-content {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
flex-wrap: wrap;
|
|
gap: 20px;
|
|
}
|
|
|
|
.logo {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 12px;
|
|
text-decoration: none;
|
|
color: var(--text);
|
|
}
|
|
|
|
.logo-icon {
|
|
width: 40px;
|
|
height: 40px;
|
|
background: linear-gradient(135deg, var(--primary), var(--secondary));
|
|
border-radius: 10px;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
font-size: 20px;
|
|
}
|
|
|
|
.logo-text {
|
|
font-size: 24px;
|
|
font-weight: 700;
|
|
background: linear-gradient(135deg, var(--primary), var(--secondary));
|
|
-webkit-background-clip: text;
|
|
-webkit-text-fill-color: transparent;
|
|
background-clip: text;
|
|
}
|
|
|
|
.logo-tagline {
|
|
font-size: 12px;
|
|
color: var(--text-muted);
|
|
margin-left: 8px;
|
|
}
|
|
|
|
.header-actions {
|
|
display: flex;
|
|
gap: 12px;
|
|
}
|
|
|
|
.btn {
|
|
padding: 10px 20px;
|
|
border-radius: 8px;
|
|
border: none;
|
|
cursor: pointer;
|
|
font-size: 14px;
|
|
font-weight: 500;
|
|
text-decoration: none;
|
|
display: inline-flex;
|
|
align-items: center;
|
|
gap: 8px;
|
|
transition: all 0.2s;
|
|
}
|
|
|
|
.btn-primary {
|
|
background: linear-gradient(135deg, var(--primary), var(--secondary));
|
|
color: white;
|
|
}
|
|
|
|
.btn-primary:hover {
|
|
transform: translateY(-2px);
|
|
box-shadow: 0 4px 12px rgba(99, 102, 241, 0.4);
|
|
}
|
|
|
|
.btn-secondary {
|
|
background: var(--bg-hover);
|
|
color: var(--text);
|
|
}
|
|
|
|
.btn-secondary:hover {
|
|
background: #475569;
|
|
}
|
|
|
|
/* Hero */
|
|
.hero {
|
|
padding: 60px 0 40px;
|
|
text-align: center;
|
|
}
|
|
|
|
.hero h1 {
|
|
font-size: 42px;
|
|
font-weight: 800;
|
|
margin-bottom: 16px;
|
|
background: linear-gradient(135deg, var(--primary), var(--secondary), #ec4899);
|
|
-webkit-background-clip: text;
|
|
-webkit-text-fill-color: transparent;
|
|
background-clip: text;
|
|
}
|
|
|
|
.hero p {
|
|
font-size: 18px;
|
|
color: var(--text-muted);
|
|
max-width: 600px;
|
|
margin: 0 auto 40px;
|
|
}
|
|
|
|
.stats {
|
|
display: flex;
|
|
justify-content: center;
|
|
gap: 40px;
|
|
margin-top: 30px;
|
|
}
|
|
|
|
.stat-item {
|
|
text-align: center;
|
|
}
|
|
|
|
.stat-value {
|
|
font-size: 28px;
|
|
font-weight: 700;
|
|
color: var(--primary);
|
|
}
|
|
|
|
.stat-label {
|
|
font-size: 14px;
|
|
color: var(--text-muted);
|
|
}
|
|
|
|
/* Search & Filter */
|
|
.search-section {
|
|
padding: 30px 0;
|
|
}
|
|
|
|
.search-box {
|
|
display: flex;
|
|
gap: 12px;
|
|
margin-bottom: 20px;
|
|
}
|
|
|
|
.sort-wrapper {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 8px;
|
|
padding: 0 12px;
|
|
background: var(--bg-card);
|
|
border: 1px solid var(--border);
|
|
border-radius: 12px;
|
|
color: var(--text-muted);
|
|
}
|
|
|
|
.sort-select {
|
|
background: transparent;
|
|
border: none;
|
|
color: var(--text);
|
|
font-size: 14px;
|
|
padding: 12px 4px 12px 0;
|
|
cursor: pointer;
|
|
}
|
|
|
|
.sort-select:focus {
|
|
outline: none;
|
|
}
|
|
|
|
.search-input-wrapper {
|
|
flex: 1;
|
|
position: relative;
|
|
}
|
|
|
|
.search-icon {
|
|
position: absolute;
|
|
left: 16px;
|
|
top: 50%;
|
|
transform: translateY(-50%);
|
|
color: var(--text-muted);
|
|
}
|
|
|
|
.search-input {
|
|
width: 100%;
|
|
padding: 14px 16px 14px 44px;
|
|
background: var(--bg-card);
|
|
border: 1px solid var(--border);
|
|
border-radius: 12px;
|
|
color: var(--text);
|
|
font-size: 16px;
|
|
transition: all 0.2s;
|
|
}
|
|
|
|
.search-input:focus {
|
|
outline: none;
|
|
border-color: var(--primary);
|
|
box-shadow: 0 0 0 3px rgba(99, 102, 241, 0.2);
|
|
}
|
|
|
|
.category-tabs {
|
|
display: flex;
|
|
gap: 8px;
|
|
flex-wrap: wrap;
|
|
justify-content: center;
|
|
}
|
|
|
|
.category-tab {
|
|
padding: 8px 16px;
|
|
background: var(--bg-card);
|
|
border: 1px solid var(--border);
|
|
border-radius: 20px;
|
|
color: var(--text-muted);
|
|
cursor: pointer;
|
|
transition: all 0.2s;
|
|
font-size: 14px;
|
|
}
|
|
|
|
.category-tab:hover {
|
|
background: var(--bg-hover);
|
|
}
|
|
|
|
.category-tab.active {
|
|
background: linear-gradient(135deg, var(--primary), var(--secondary));
|
|
border-color: transparent;
|
|
color: white;
|
|
}
|
|
|
|
/* Tools Grid */
|
|
.tools-section {
|
|
padding: 30px 0 70px;
|
|
}
|
|
|
|
.section-header {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
margin-bottom: 24px;
|
|
}
|
|
|
|
.section-title {
|
|
font-size: 24px;
|
|
font-weight: 700;
|
|
}
|
|
|
|
.tools-count {
|
|
color: var(--text-muted);
|
|
font-size: 14px;
|
|
}
|
|
|
|
.tools-grid {
|
|
display: grid;
|
|
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
|
|
gap: 20px;
|
|
}
|
|
|
|
|
|
.tool-card {
|
|
background: var(--bg-card);
|
|
border: 1px solid var(--border);
|
|
border-radius: 16px;
|
|
padding: 24px;
|
|
cursor: pointer;
|
|
transition: all 0.3s;
|
|
text-decoration: none;
|
|
color: inherit;
|
|
display: block;
|
|
}
|
|
|
|
.tool-card:hover {
|
|
transform: translateY(-4px);
|
|
box-shadow: var(--shadow-lg);
|
|
border-color: var(--primary);
|
|
}
|
|
|
|
.tool-header {
|
|
display: flex;
|
|
align-items: flex-start;
|
|
gap: 16px;
|
|
margin-bottom: 16px;
|
|
}
|
|
|
|
.tool-icon {
|
|
width: 56px;
|
|
height: 56px;
|
|
border-radius: 12px;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
font-size: 24px;
|
|
flex-shrink: 0;
|
|
}
|
|
|
|
.tool-info {
|
|
flex: 1;
|
|
min-width: 0;
|
|
}
|
|
|
|
.tool-title {
|
|
font-size: 18px;
|
|
font-weight: 600;
|
|
margin-bottom: 4px;
|
|
white-space: nowrap;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
}
|
|
|
|
.tool-category {
|
|
font-size: 12px;
|
|
color: var(--text-muted);
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 4px;
|
|
}
|
|
|
|
.tool-updated {
|
|
font-size: 12px;
|
|
color: var(--text-muted);
|
|
display: inline-flex;
|
|
align-items: center;
|
|
gap: 6px;
|
|
margin-top: 6px;
|
|
}
|
|
|
|
.tool-updated i {
|
|
font-size: 12px;
|
|
}
|
|
|
|
.tool-description {
|
|
color: var(--text-muted);
|
|
font-size: 14px;
|
|
line-height: 1.5;
|
|
margin-bottom: 16px;
|
|
display: -webkit-box;
|
|
-webkit-line-clamp: 2;
|
|
-webkit-box-orient: vertical;
|
|
overflow: hidden;
|
|
}
|
|
|
|
.tool-footer {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
}
|
|
|
|
.tool-clicks {
|
|
font-size: 12px;
|
|
color: var(--text-muted);
|
|
display: inline-flex;
|
|
flex-direction: column;
|
|
align-items: flex-end;
|
|
gap: 6px;
|
|
}
|
|
|
|
.tool-meta {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
gap: 10px;
|
|
}
|
|
|
|
.tool-clicks span {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
gap: 6px;
|
|
white-space: nowrap;
|
|
}
|
|
|
|
.tool-tags {
|
|
display: flex;
|
|
gap: 6px;
|
|
flex-wrap: wrap;
|
|
}
|
|
|
|
.tool-tag {
|
|
font-size: 11px;
|
|
padding: 4px 8px;
|
|
background: var(--bg-hover);
|
|
border-radius: 4px;
|
|
color: var(--text-muted);
|
|
}
|
|
|
|
.tool-arrow {
|
|
color: var(--primary);
|
|
opacity: 0;
|
|
transition: opacity 0.2s;
|
|
}
|
|
|
|
.tool-card:hover .tool-arrow {
|
|
opacity: 1;
|
|
}
|
|
|
|
/* Featured Badge */
|
|
.featured-badge {
|
|
position: absolute;
|
|
top: 12px;
|
|
right: 12px;
|
|
background: linear-gradient(135deg, #f59e0b, #ef4444);
|
|
color: white;
|
|
font-size: 10px;
|
|
font-weight: 600;
|
|
padding: 4px 8px;
|
|
border-radius: 12px;
|
|
}
|
|
|
|
.tool-card {
|
|
position: relative;
|
|
}
|
|
|
|
/* Empty State */
|
|
.empty-state {
|
|
text-align: center;
|
|
padding: 60px 20px;
|
|
color: var(--text-muted);
|
|
}
|
|
|
|
.empty-icon {
|
|
font-size: 64px;
|
|
margin-bottom: 20px;
|
|
opacity: 0.5;
|
|
}
|
|
|
|
/* Footer */
|
|
footer {
|
|
background: var(--bg-card);
|
|
border-top: 1px solid var(--border);
|
|
padding: 40px 0;
|
|
margin-top: 60px;
|
|
}
|
|
|
|
.footer-content {
|
|
display: grid;
|
|
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
|
|
gap: 40px;
|
|
}
|
|
|
|
.footer-section h3 {
|
|
font-size: 16px;
|
|
font-weight: 600;
|
|
margin-bottom: 16px;
|
|
}
|
|
|
|
.footer-section p {
|
|
color: var(--text-muted);
|
|
font-size: 14px;
|
|
line-height: 1.6;
|
|
}
|
|
|
|
.footer-links {
|
|
list-style: none;
|
|
}
|
|
|
|
.footer-links li {
|
|
margin-bottom: 8px;
|
|
}
|
|
|
|
.footer-links a {
|
|
color: var(--text-muted);
|
|
text-decoration: none;
|
|
font-size: 14px;
|
|
transition: color 0.2s;
|
|
}
|
|
|
|
.footer-links a:hover {
|
|
color: var(--primary);
|
|
}
|
|
|
|
.footer-bottom {
|
|
text-align: center;
|
|
padding-top: 30px;
|
|
margin-top: 30px;
|
|
border-top: 1px solid var(--border);
|
|
color: var(--text-muted);
|
|
font-size: 14px;
|
|
}
|
|
|
|
/* Loading */
|
|
.loading {
|
|
text-align: center;
|
|
padding: 40px;
|
|
color: var(--text-muted);
|
|
}
|
|
|
|
@media (max-width: 768px) {
|
|
.hero h1 {
|
|
font-size: 32px;
|
|
}
|
|
|
|
.tools-grid {
|
|
grid-template-columns: 1fr;
|
|
}
|
|
|
|
.stats {
|
|
gap: 20px;
|
|
}
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<header>
|
|
<div class="container">
|
|
<div class="header-content">
|
|
<a href="./" class="logo">
|
|
<div class="logo-icon">⚡</div>
|
|
<div>
|
|
<div class="logo-text">JustHTMLs</div>
|
|
<div class="logo-tagline">开源 HTML 工具集</div>
|
|
</div>
|
|
</a>
|
|
<div class="header-actions">
|
|
<a href="https://github.com/justhtmls/html-tools/issues/new?template=feedback.md" target="_blank" class="btn btn-secondary">
|
|
<i class="fas fa-comment-dots"></i> 问题反馈
|
|
</a>
|
|
<a href="tools-rank.html" class="btn btn-secondary">
|
|
<i class="fas fa-chart-line"></i> 工具排行
|
|
</a>
|
|
<a href="https://github.com/justhtmls/html-tools/issues/new?template=tool-request.md" target="_blank" class="btn btn-secondary">
|
|
<i class="fas fa-lightbulb"></i> 提交需求
|
|
</a>
|
|
<a href="https://github.com/justhtmls/html-tools" target="_blank" class="btn btn-secondary">
|
|
<i class="fab fa-github"></i> GitHub
|
|
</a>
|
|
<a href="https://github.com/justhtmls/html-tools/issues/new?template=tool-submission.md" target="_blank" class="btn btn-primary">
|
|
<i class="fas fa-plus"></i> 贡献工具
|
|
</a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</header>
|
|
|
|
<main>
|
|
<section class="hero">
|
|
<div class="container">
|
|
<h1>轻量级 HTML 工具集合</h1>
|
|
<p>所有工具均为单文件 HTML,无需安装,打开即用。保护隐私,数据本地处理。</p>
|
|
<div class="stats">
|
|
<div class="stat-item">
|
|
<div class="stat-value" id="tools-count">100</div>
|
|
<div class="stat-label">工具数量</div>
|
|
</div>
|
|
<div class="stat-item">
|
|
<div class="stat-value" id="categories-count">5</div>
|
|
<div class="stat-label">分类数量</div>
|
|
</div>
|
|
<div class="stat-item">
|
|
<div class="stat-value" id="home-day-count">--/--</div>
|
|
<div class="stat-label">今日 PV/UV</div>
|
|
</div>
|
|
<div class="stat-item">
|
|
<div class="stat-value" id="home-total-count">--/--</div>
|
|
<div class="stat-label">总 PV/UV</div>
|
|
</div>
|
|
<div class="stat-item">
|
|
<div class="stat-value">100%</div>
|
|
<div class="stat-label">开源免费</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<section class="search-section">
|
|
<div class="container">
|
|
<div class="search-box">
|
|
<div class="search-input-wrapper">
|
|
<i class="fas fa-search search-icon"></i>
|
|
<input type="text" class="search-input" id="search-input" placeholder="搜索工具名称、描述或标签...">
|
|
</div>
|
|
<div class="sort-wrapper">
|
|
<i class="far fa-clock"></i>
|
|
<select class="sort-select" id="sort-select">
|
|
<option value="updated-desc">更新时间:新 → 旧</option>
|
|
<option value="updated-asc">更新时间:旧 → 新</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
<div class="category-tabs" id="category-tabs">
|
|
<button class="category-tab active" data-category="all">
|
|
<i class="fas fa-th-large"></i> 全部
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<section class="tools-section">
|
|
<div class="container">
|
|
<div class="section-header">
|
|
<h2 class="section-title">工具列表</h2>
|
|
<span class="tools-count" id="tools-count-label">加载中...</span>
|
|
</div>
|
|
<div class="tools-grid" id="tools-grid">
|
|
<div class="loading">
|
|
<i class="fas fa-spinner fa-spin"></i> 加载工具列表...
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<section class="container" style="padding: 60px 20px;">
|
|
<div style="background: linear-gradient(135deg, var(--bg-card), var(--bg-hover)); border-radius: 20px; padding: 40px; text-align: center; border: 1px solid var(--border);">
|
|
<h2 style="font-size: 28px; margin-bottom: 16px;">🚀 贡献你的工具</h2>
|
|
<p style="color: var(--text-muted); max-width: 500px; margin: 0 auto 24px;">
|
|
JustHTMLs 欢迎社区贡献!创建一个新的 HTML 工具,通过 GitHub Issues 提交,让我们一起构建最完整的工具集合。
|
|
</p>
|
|
<a href="https://github.com/justhtmls/html-tools/issues/new?template=tool-submission.md" target="_blank" class="btn btn-primary" style="font-size: 16px; padding: 12px 24px;">
|
|
<i class="fas fa-plus"></i> 提交新工具
|
|
</a>
|
|
</div>
|
|
</section>
|
|
</main>
|
|
|
|
<footer>
|
|
<div class="container">
|
|
<div class="footer-content">
|
|
<div class="footer-section">
|
|
<h3>关于 JustHTMLs</h3>
|
|
<p>JustHTMLs 是一个开源的 HTML 工具集平台,所有工具均为单文件 HTML,无需构建步骤,可直接在浏览器中运行。</p>
|
|
</div>
|
|
<div class="footer-section">
|
|
<h3>快速链接</h3>
|
|
<ul class="footer-links">
|
|
<li><a href="https://github.com/justhtmls" target="_blank"><i class="fab fa-github"></i> GitHub 组织</a></li>
|
|
<li><a href="tools-rank.html"><i class="fas fa-chart-line"></i> 工具排行</a></li>
|
|
<li><a href="https://github.com/justhtmls/html-tools" target="_blank"><i class="fas fa-globe"></i> 网站仓库</a></li>
|
|
<li><a href="https://github.com/justhtmls/html-tools/issues" target="_blank"><i class="fas fa-bug"></i> 报告问题</a></li>
|
|
<li><a href="https://github.com/justhtmls/html-tools/blob/main/CONTRIBUTING.md" target="_blank">贡献指南</a></li>
|
|
</ul>
|
|
</div>
|
|
<div class="footer-section">
|
|
<h3>设计理念</h3>
|
|
<ul class="footer-links">
|
|
<li><a href="reference.html">设计文档</a></li>
|
|
<li>单文件 HTML,内联 CSS/JS</li>
|
|
<li>无需构建,复制即用</li>
|
|
<li>数据本地处理,保护隐私</li>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
<div class="footer-bottom">
|
|
<p>© 2025 JustHTMLs. 基于 MIT 许可证开源。</p>
|
|
</div>
|
|
</div>
|
|
</footer>
|
|
|
|
<script>
|
|
let toolsData = null;
|
|
let currentCategory = 'all';
|
|
let searchQuery = '';
|
|
let sortOption = 'updated-desc';
|
|
|
|
async function loadTools() {
|
|
try {
|
|
const response = await fetch('index.json');
|
|
const data = await response.json();
|
|
toolsData = data;
|
|
renderCategories();
|
|
renderTools();
|
|
updateStats();
|
|
} catch (error) {
|
|
console.error('Failed to load tools:', error);
|
|
document.getElementById('tools-grid').innerHTML = `
|
|
<div class="empty-state">
|
|
<div class="empty-icon">⚠️</div>
|
|
<p>加载工具列表失败,请刷新页面重试。</p>
|
|
</div>
|
|
`;
|
|
}
|
|
}
|
|
|
|
function updateStats() {
|
|
document.getElementById('tools-count').textContent = toolsData.tools.length;
|
|
document.getElementById('categories-count').textContent = toolsData.categories.length;
|
|
}
|
|
|
|
function renderCategories() {
|
|
const container = document.getElementById('category-tabs');
|
|
container.innerHTML = `
|
|
<button class="category-tab ${currentCategory === 'all' ? 'active' : ''}" data-category="all">
|
|
<i class="fas fa-th-large"></i> 全部
|
|
</button>
|
|
`;
|
|
const allButton = container.querySelector('[data-category="all"]');
|
|
allButton.addEventListener('click', () => selectCategory('all'));
|
|
|
|
toolsData.categories.forEach(cat => {
|
|
const btn = document.createElement('button');
|
|
btn.className = `category-tab ${currentCategory === cat.id ? 'active' : ''}`;
|
|
btn.dataset.category = cat.id;
|
|
btn.innerHTML = `${cat.icon} ${cat.name}`;
|
|
btn.addEventListener('click', () => selectCategory(cat.id));
|
|
container.appendChild(btn);
|
|
});
|
|
}
|
|
|
|
function selectCategory(category) {
|
|
currentCategory = category;
|
|
document.querySelectorAll('.category-tab').forEach(tab => {
|
|
tab.classList.toggle('active', tab.dataset.category === category);
|
|
});
|
|
renderTools();
|
|
}
|
|
|
|
function filterTools() {
|
|
const filtered = toolsData.tools.filter(tool => {
|
|
const matchesCategory = currentCategory === 'all' || tool.category === currentCategory;
|
|
const matchesSearch = !searchQuery ||
|
|
tool.name.toLowerCase().includes(searchQuery) ||
|
|
tool.description.toLowerCase().includes(searchQuery) ||
|
|
tool.tags.some(tag => tag.toLowerCase().includes(searchQuery));
|
|
return matchesCategory && matchesSearch;
|
|
});
|
|
const withTimestamp = (tool) => new Date(tool.updatedAt || tool.createdAt || 0).getTime();
|
|
if (sortOption === 'updated-asc') {
|
|
return filtered.sort((a, b) => withTimestamp(a) - withTimestamp(b));
|
|
}
|
|
return filtered.sort((a, b) => withTimestamp(b) - withTimestamp(a));
|
|
}
|
|
|
|
function renderTools() {
|
|
const container = document.getElementById('tools-grid');
|
|
const filteredTools = filterTools();
|
|
|
|
document.getElementById('tools-count-label').textContent = `共 ${filteredTools.length} 个工具`;
|
|
|
|
if (filteredTools.length === 0) {
|
|
container.innerHTML = `
|
|
<div class="empty-state">
|
|
<div class="empty-icon">🔍</div>
|
|
<p>未找到匹配的工具</p>
|
|
</div>
|
|
`;
|
|
return;
|
|
}
|
|
|
|
container.innerHTML = filteredTools.map(tool => {
|
|
const category = toolsData.categories.find(c => c.id === tool.category);
|
|
const updatedAt = tool.updatedAt || tool.createdAt || '未知';
|
|
return `
|
|
<a href="${tool.entry}" class="tool-card" style="--tool-color: ${tool.color}">
|
|
${tool.featured ? '<span class="featured-badge">精选</span>' : ''}
|
|
<div class="tool-header">
|
|
<div class="tool-icon" style="background: ${tool.color}20; color: ${tool.color};">
|
|
${tool.icon}
|
|
</div>
|
|
<div class="tool-info">
|
|
<div class="tool-title">${tool.name}</div>
|
|
<div class="tool-category">${category?.icon || ''} ${category?.name || '未分类'}</div>
|
|
<div class="tool-updated"><i class="far fa-clock"></i> ${updatedAt}</div>
|
|
</div>
|
|
</div>
|
|
<div class="tool-description">${tool.description}</div>
|
|
<div class="tool-footer">
|
|
<div class="tool-tags">
|
|
${tool.tags.slice(0, 3).map(tag => `<span class="tool-tag">${tag}</span>`).join('')}
|
|
</div>
|
|
<div class="tool-meta">
|
|
<i class="fas fa-arrow-right tool-arrow"></i>
|
|
</div>
|
|
</div>
|
|
</a>
|
|
`;
|
|
}).join('');
|
|
}
|
|
|
|
function formatCount(value) {
|
|
if (typeof value !== 'number') {
|
|
return '--';
|
|
}
|
|
return value.toLocaleString('zh-CN');
|
|
}
|
|
|
|
function formatPair(pv, uv) {
|
|
return `${formatCount(pv)}/${formatCount(uv)}`;
|
|
}
|
|
|
|
async function loadHomeStats() {
|
|
try {
|
|
const response = await fetch('api/clicks?ids=home');
|
|
if (!response.ok) {
|
|
return;
|
|
}
|
|
const data = await response.json();
|
|
const stats = data.counts?.home;
|
|
if (!stats) {
|
|
return;
|
|
}
|
|
document.getElementById('home-day-count').textContent = formatPair(stats.pv?.day, stats.uv?.day);
|
|
document.getElementById('home-total-count').textContent = formatPair(stats.pv?.total, stats.uv?.total);
|
|
} catch (error) {
|
|
console.warn('Failed to load home stats', error);
|
|
}
|
|
}
|
|
|
|
|
|
// Search functionality
|
|
document.getElementById('search-input').addEventListener('input', (e) => {
|
|
searchQuery = e.target.value.toLowerCase();
|
|
renderTools();
|
|
});
|
|
|
|
document.getElementById('sort-select').addEventListener('change', (e) => {
|
|
sortOption = e.target.value;
|
|
renderTools();
|
|
});
|
|
|
|
// Initialize
|
|
loadHomeStats();
|
|
loadTools();
|
|
|
|
</script>
|
|
<script src="assets/clicks.js" defer></script>
|
|
</body>
|
|
</html>
|