feat: 完成全站UI优化 - 科技感/未来风设计
- 前台页面全面升级为Tailwind CSS框架 - 引入Google Fonts (Space Grotesk, Noto Sans) - 主色调更新为#25c0f4 (cyan blue) - 实现玻璃态效果和渐变背景 - 优化首页网格卡片布局和悬停动画 - 优化详情页双栏布局和渐变Logo光晕 - 优化管理员登录页,添加科技网格背景 - Flask-Admin后台完整深色主题 - 统一Material Symbols图标系统 - 网站自动抓取功能界面优化 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
17
templates/admin/custom_base.html
Normal file
17
templates/admin/custom_base.html
Normal file
@@ -0,0 +1,17 @@
|
||||
{% extends 'admin/base.html' %}
|
||||
|
||||
{% block head_css %}
|
||||
{{ super() }}
|
||||
<!-- Google Fonts -->
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@300;400;500;600;700&family=Noto+Sans:wght@400;500;700&display=swap" rel="stylesheet">
|
||||
<!-- Custom Admin Theme -->
|
||||
<link href="{{ url_for('static', filename='css/admin-theme.css') }}" rel="stylesheet">
|
||||
{% endblock %}
|
||||
|
||||
{% block body %}
|
||||
<body class="admin-theme">
|
||||
{{ super() }}
|
||||
</body>
|
||||
{% endblock %}
|
||||
124
templates/admin/site/create.html
Normal file
124
templates/admin/site/create.html
Normal file
@@ -0,0 +1,124 @@
|
||||
{% extends 'admin/model/create.html' %}
|
||||
|
||||
{% block tail %}
|
||||
{{ super() }}
|
||||
<style>
|
||||
.auto-fetch-btn {
|
||||
margin-top: 10px;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
.fetch-status {
|
||||
margin-top: 10px;
|
||||
padding: 10px;
|
||||
border-radius: 8px;
|
||||
display: none;
|
||||
}
|
||||
.fetch-status.success {
|
||||
background-color: rgba(34, 197, 94, 0.1);
|
||||
border: 1px solid rgba(34, 197, 94, 0.3);
|
||||
color: #4ade80;
|
||||
}
|
||||
.fetch-status.error {
|
||||
background-color: rgba(239, 68, 68, 0.1);
|
||||
border: 1px solid rgba(239, 68, 68, 0.3);
|
||||
color: #f87171;
|
||||
}
|
||||
.auto-fetch-btn .loading-icon {
|
||||
display: none;
|
||||
}
|
||||
.auto-fetch-btn.loading .loading-icon {
|
||||
display: inline-block;
|
||||
animation: spin 1s linear infinite;
|
||||
}
|
||||
.auto-fetch-btn.loading .normal-icon {
|
||||
display: none;
|
||||
}
|
||||
@keyframes spin {
|
||||
from { transform: rotate(0deg); }
|
||||
to { transform: rotate(360deg); }
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
// 在URL字段后添加"自动获取"按钮
|
||||
const urlField = document.querySelector('input[name="url"]');
|
||||
if (urlField) {
|
||||
const fetchBtn = document.createElement('button');
|
||||
fetchBtn.type = 'button';
|
||||
fetchBtn.className = 'btn btn-info auto-fetch-btn';
|
||||
fetchBtn.innerHTML = '<span class="normal-icon">↻</span><span class="loading-icon">↻</span> 自动获取网站信息';
|
||||
|
||||
const statusDiv = document.createElement('div');
|
||||
statusDiv.className = 'fetch-status';
|
||||
|
||||
urlField.parentNode.appendChild(fetchBtn);
|
||||
urlField.parentNode.appendChild(statusDiv);
|
||||
|
||||
fetchBtn.addEventListener('click', function() {
|
||||
const url = urlField.value.trim();
|
||||
|
||||
if (!url) {
|
||||
showStatus('请先输入网站URL', 'error');
|
||||
return;
|
||||
}
|
||||
|
||||
// 显示加载状态
|
||||
fetchBtn.disabled = true;
|
||||
fetchBtn.classList.add('loading');
|
||||
statusDiv.style.display = 'none';
|
||||
|
||||
// 调用API获取网站信息
|
||||
fetch('/api/fetch-website-info', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({ url: url })
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data.success) {
|
||||
// 自动填充表单字段
|
||||
const nameField = document.querySelector('input[name="name"]');
|
||||
const shortDescField = document.querySelector('input[name="short_desc"]');
|
||||
const descriptionField = document.querySelector('textarea[name="description"]');
|
||||
const logoField = document.querySelector('input[name="logo"]');
|
||||
|
||||
if (nameField && data.data.name) {
|
||||
nameField.value = data.data.name;
|
||||
}
|
||||
if (shortDescField && data.data.description) {
|
||||
shortDescField.value = data.data.description.substring(0, 100);
|
||||
}
|
||||
if (descriptionField && data.data.description) {
|
||||
descriptionField.value = data.data.description;
|
||||
}
|
||||
if (logoField && data.data.logo) {
|
||||
logoField.value = data.data.logo;
|
||||
}
|
||||
|
||||
showStatus('✓ 网站信息获取成功!已自动填充表单', 'success');
|
||||
} else {
|
||||
showStatus('✗ ' + (data.message || '获取失败,请手动填写'), 'error');
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error:', error);
|
||||
showStatus('✗ 网络请求失败,请手动填写', 'error');
|
||||
})
|
||||
.finally(() => {
|
||||
fetchBtn.disabled = false;
|
||||
fetchBtn.classList.remove('loading');
|
||||
});
|
||||
});
|
||||
|
||||
function showStatus(message, type) {
|
||||
statusDiv.textContent = message;
|
||||
statusDiv.className = 'fetch-status ' + type;
|
||||
statusDiv.style.display = 'block';
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
124
templates/admin/site/edit.html
Normal file
124
templates/admin/site/edit.html
Normal file
@@ -0,0 +1,124 @@
|
||||
{% extends 'admin/model/edit.html' %}
|
||||
|
||||
{% block tail %}
|
||||
{{ super() }}
|
||||
<style>
|
||||
.auto-fetch-btn {
|
||||
margin-top: 10px;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
.fetch-status {
|
||||
margin-top: 10px;
|
||||
padding: 10px;
|
||||
border-radius: 8px;
|
||||
display: none;
|
||||
}
|
||||
.fetch-status.success {
|
||||
background-color: rgba(34, 197, 94, 0.1);
|
||||
border: 1px solid rgba(34, 197, 94, 0.3);
|
||||
color: #4ade80;
|
||||
}
|
||||
.fetch-status.error {
|
||||
background-color: rgba(239, 68, 68, 0.1);
|
||||
border: 1px solid rgba(239, 68, 68, 0.3);
|
||||
color: #f87171;
|
||||
}
|
||||
.auto-fetch-btn .loading-icon {
|
||||
display: none;
|
||||
}
|
||||
.auto-fetch-btn.loading .loading-icon {
|
||||
display: inline-block;
|
||||
animation: spin 1s linear infinite;
|
||||
}
|
||||
.auto-fetch-btn.loading .normal-icon {
|
||||
display: none;
|
||||
}
|
||||
@keyframes spin {
|
||||
from { transform: rotate(0deg); }
|
||||
to { transform: rotate(360deg); }
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
// 在URL字段后添加"自动获取"按钮
|
||||
const urlField = document.querySelector('input[name="url"]');
|
||||
if (urlField) {
|
||||
const fetchBtn = document.createElement('button');
|
||||
fetchBtn.type = 'button';
|
||||
fetchBtn.className = 'btn btn-info auto-fetch-btn';
|
||||
fetchBtn.innerHTML = '<span class="normal-icon">↻</span><span class="loading-icon">↻</span> 自动获取网站信息';
|
||||
|
||||
const statusDiv = document.createElement('div');
|
||||
statusDiv.className = 'fetch-status';
|
||||
|
||||
urlField.parentNode.appendChild(fetchBtn);
|
||||
urlField.parentNode.appendChild(statusDiv);
|
||||
|
||||
fetchBtn.addEventListener('click', function() {
|
||||
const url = urlField.value.trim();
|
||||
|
||||
if (!url) {
|
||||
showStatus('请先输入网站URL', 'error');
|
||||
return;
|
||||
}
|
||||
|
||||
// 显示加载状态
|
||||
fetchBtn.disabled = true;
|
||||
fetchBtn.classList.add('loading');
|
||||
statusDiv.style.display = 'none';
|
||||
|
||||
// 调用API获取网站信息
|
||||
fetch('/api/fetch-website-info', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({ url: url })
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data.success) {
|
||||
// 自动填充表单字段
|
||||
const nameField = document.querySelector('input[name="name"]');
|
||||
const shortDescField = document.querySelector('input[name="short_desc"]');
|
||||
const descriptionField = document.querySelector('textarea[name="description"]');
|
||||
const logoField = document.querySelector('input[name="logo"]');
|
||||
|
||||
if (nameField && data.data.name) {
|
||||
nameField.value = data.data.name;
|
||||
}
|
||||
if (shortDescField && data.data.description) {
|
||||
shortDescField.value = data.data.description.substring(0, 100);
|
||||
}
|
||||
if (descriptionField && data.data.description) {
|
||||
descriptionField.value = data.data.description;
|
||||
}
|
||||
if (logoField && data.data.logo) {
|
||||
logoField.value = data.data.logo;
|
||||
}
|
||||
|
||||
showStatus('✓ 网站信息获取成功!已自动填充表单', 'success');
|
||||
} else {
|
||||
showStatus('✗ ' + (data.message || '获取失败,请手动填写'), 'error');
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error:', error);
|
||||
showStatus('✗ 网络请求失败,请手动填写', 'error');
|
||||
})
|
||||
.finally(() => {
|
||||
fetchBtn.disabled = false;
|
||||
fetchBtn.classList.remove('loading');
|
||||
});
|
||||
});
|
||||
|
||||
function showStatus(message, type) {
|
||||
statusDiv.textContent = message;
|
||||
statusDiv.className = 'fetch-status ' + type;
|
||||
statusDiv.style.display = 'block';
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
121
templates/admin_login.html
Normal file
121
templates/admin_login.html
Normal file
@@ -0,0 +1,121 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}管理员登录 - ZJPB 焦提示词{% endblock %}
|
||||
|
||||
{% block extra_css %}
|
||||
<style>
|
||||
.glass-panel {
|
||||
background: rgba(22, 33, 37, 0.7);
|
||||
backdrop-filter: blur(12px);
|
||||
-webkit-backdrop-filter: blur(12px);
|
||||
}
|
||||
.tech-bg-grid {
|
||||
background-image: radial-gradient(circle at center, rgba(37, 192, 244, 0.05) 0%, transparent 70%),
|
||||
linear-gradient(rgba(37, 192, 244, 0.03) 1px, transparent 1px),
|
||||
linear-gradient(90deg, rgba(37, 192, 244, 0.03) 1px, transparent 1px);
|
||||
background-size: 100% 100%, 40px 40px, 40px 40px;
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<!-- Override default layout -->
|
||||
</div> <!-- Close main wrapper -->
|
||||
|
||||
<body class="bg-background-dark font-display text-white min-h-screen flex flex-col items-center justify-center relative overflow-hidden selection:bg-primary/30">
|
||||
<!-- Ambient Background -->
|
||||
<div class="absolute inset-0 z-0 tech-bg-grid pointer-events-none"></div>
|
||||
<div class="absolute top-[-20%] right-[-10%] w-[600px] h-[600px] bg-primary/10 rounded-full blur-[120px] pointer-events-none"></div>
|
||||
<div class="absolute bottom-[-10%] left-[-10%] w-[500px] h-[500px] bg-purple-600/10 rounded-full blur-[100px] pointer-events-none"></div>
|
||||
|
||||
<!-- Main Content Wrapper -->
|
||||
<div class="w-full max-w-[480px] px-4 z-10 flex flex-col gap-6">
|
||||
<!-- Back Navigation -->
|
||||
<a class="group flex items-center gap-2 text-gray-400 hover:text-white transition-colors w-fit" href="{{ url_for('index') }}">
|
||||
<div class="flex items-center justify-center w-8 h-8 rounded-full border border-border-dark bg-surface-dark group-hover:border-primary/50 transition-colors">
|
||||
<span class="material-symbols-outlined text-sm">arrow_back</span>
|
||||
</div>
|
||||
<span class="text-sm font-medium">返回首页</span>
|
||||
</a>
|
||||
|
||||
<!-- Login Card -->
|
||||
<div class="glass-panel border border-white/5 dark:border-border-dark/50 rounded-xl shadow-2xl p-8 md:p-10 relative overflow-hidden">
|
||||
<!-- Decorative Accent -->
|
||||
<div class="absolute top-0 left-0 w-full h-[2px] bg-gradient-to-r from-transparent via-primary to-transparent opacity-70"></div>
|
||||
|
||||
<!-- Header -->
|
||||
<div class="flex flex-col gap-2 mb-8">
|
||||
<div class="flex items-center gap-3 mb-2">
|
||||
<div class="p-2 rounded-lg bg-primary/10 text-primary">
|
||||
<span class="material-symbols-outlined text-2xl">shield_person</span>
|
||||
</div>
|
||||
<span class="text-xs font-bold tracking-widest uppercase text-primary/80">System Access</span>
|
||||
</div>
|
||||
<h1 class="text-3xl font-black tracking-tight text-white leading-tight">管理员登录</h1>
|
||||
<p class="text-gray-400 text-base font-normal">输入您的登录凭据以访问后台管理系统</p>
|
||||
</div>
|
||||
|
||||
<!-- Error Messages -->
|
||||
{% with messages = get_flashed_messages(with_categories=true) %}
|
||||
{% if messages %}
|
||||
{% for category, message in messages %}
|
||||
<div class="mb-6 p-3 rounded-lg bg-red-500/10 border border-red-500/20 flex items-start gap-3">
|
||||
<span class="material-symbols-outlined text-red-400 text-sm mt-0.5">error</span>
|
||||
<p class="text-red-400 text-sm">{{ message }}</p>
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% endwith %}
|
||||
|
||||
<!-- Form -->
|
||||
<form method="POST" action="{{ url_for('admin_login') }}" class="flex flex-col gap-5">
|
||||
<!-- Username Field -->
|
||||
<div class="space-y-2">
|
||||
<label class="text-white text-sm font-medium leading-normal" for="username">用户名</label>
|
||||
<div class="relative group">
|
||||
<input class="form-input flex w-full h-14 pl-12 pr-4 rounded-lg text-white focus:outline-0 focus:ring-0 border border-border-dark bg-surface-dark focus:border-primary placeholder:text-gray-500 text-base font-normal leading-normal transition-colors"
|
||||
id="username"
|
||||
name="username"
|
||||
placeholder="输入您的用户名或邮箱"
|
||||
type="text"
|
||||
required
|
||||
autofocus/>
|
||||
<div class="absolute left-4 top-1/2 -translate-y-1/2 text-gray-400 group-focus-within:text-primary transition-colors flex items-center justify-center pointer-events-none">
|
||||
<span class="material-symbols-outlined text-[20px]">person</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Password Field -->
|
||||
<div class="space-y-2">
|
||||
<label class="text-white text-sm font-medium leading-normal" for="password">密码</label>
|
||||
<div class="flex w-full items-stretch rounded-lg group relative">
|
||||
<input class="form-input flex w-full h-14 pl-12 pr-12 rounded-lg text-white focus:outline-0 focus:ring-0 border border-border-dark bg-surface-dark focus:border-primary placeholder:text-gray-500 text-base font-normal leading-normal transition-colors z-10"
|
||||
id="password"
|
||||
name="password"
|
||||
placeholder="输入您的密码"
|
||||
type="password"
|
||||
required/>
|
||||
<div class="absolute left-4 top-1/2 -translate-y-1/2 text-gray-400 group-focus-within:text-primary transition-colors flex items-center justify-center pointer-events-none z-20">
|
||||
<span class="material-symbols-outlined text-[20px]">lock</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Login Button -->
|
||||
<button class="mt-4 flex w-full h-12 cursor-pointer items-center justify-center overflow-hidden rounded-lg bg-primary text-black text-base font-bold leading-normal tracking-[0.015em] hover:bg-primary/90 transition-all active:scale-[0.98] shadow-[0_0_20px_rgba(37,192,244,0.3)] hover:shadow-[0_0_30px_rgba(37,192,244,0.5)]"
|
||||
type="submit">
|
||||
<span class="truncate">登录</span>
|
||||
</button>
|
||||
</form>
|
||||
|
||||
<!-- Footer Meta -->
|
||||
<div class="mt-8 flex justify-center gap-6 border-t border-border-dark/30 pt-6">
|
||||
<p class="text-gray-400 text-xs text-center">
|
||||
ZJPB - 焦提示词 管理系统
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
{% endblock %}
|
||||
125
templates/base.html
Normal file
125
templates/base.html
Normal file
@@ -0,0 +1,125 @@
|
||||
<!DOCTYPE html>
|
||||
<html class="dark" lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>{% block title %}ZJPB - 焦提示词{% endblock %}</title>
|
||||
<meta name="description" content="{% block description %}焦提示词 - 精选AI工具和产品导航,发现最新最好用的人工智能应用{% endblock %}">
|
||||
<meta name="keywords" content="{% block keywords %}ZJPB,焦提示词,AI工具,人工智能,ChatGPT,AI导航{% endblock %}">
|
||||
|
||||
<!-- Google Fonts -->
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@300;400;500;600;700&family=Noto+Sans:wght@400;500;700&display=swap" rel="stylesheet">
|
||||
|
||||
<!-- Material Symbols -->
|
||||
<link href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:wght,FILL@100..700,0..1&display=swap" rel="stylesheet">
|
||||
|
||||
<!-- Tailwind CSS -->
|
||||
<script src="https://cdn.tailwindcss.com?plugins=forms,container-queries"></script>
|
||||
<script>
|
||||
tailwind.config = {
|
||||
darkMode: "class",
|
||||
theme: {
|
||||
extend: {
|
||||
colors: {
|
||||
primary: "#25c0f4",
|
||||
secondary: "#7c3aed",
|
||||
"background-light": "#f5f8f8",
|
||||
"background-dark": "#111618",
|
||||
"surface-dark": "#1b2427",
|
||||
"border-dark": "#283539",
|
||||
},
|
||||
fontFamily: {
|
||||
"display": ["Space Grotesk", "sans-serif"],
|
||||
"body": ["Noto Sans", "sans-serif"],
|
||||
},
|
||||
backgroundImage: {
|
||||
'gradient-tech': 'linear-gradient(135deg, #25c0f4 0%, #7c3aed 100%)',
|
||||
'gradient-text': 'linear-gradient(to right, #25c0f4, #a855f7)',
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
<style>
|
||||
body {
|
||||
font-family: 'Space Grotesk', sans-serif;
|
||||
}
|
||||
.text-gradient {
|
||||
background: linear-gradient(to right, #25c0f4, #c084fc);
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
}
|
||||
.card-hover:hover {
|
||||
transform: translateY(-4px);
|
||||
box-shadow: 0 10px 30px -10px rgba(37, 192, 244, 0.15);
|
||||
border-color: #25c0f4;
|
||||
}
|
||||
</style>
|
||||
{% block extra_css %}{% endblock %}
|
||||
</head>
|
||||
<body class="bg-background-light dark:bg-background-dark text-slate-900 dark:text-white antialiased selection:bg-primary selection:text-black">
|
||||
<div class="relative flex min-h-screen w-full flex-col overflow-x-hidden">
|
||||
<!-- Background Gradient Elements -->
|
||||
<div class="fixed top-0 left-0 w-full h-96 bg-primary/5 blur-[120px] rounded-full pointer-events-none -translate-y-1/2"></div>
|
||||
<div class="fixed top-20 right-0 w-96 h-96 bg-secondary/10 blur-[100px] rounded-full pointer-events-none translate-x-1/3"></div>
|
||||
|
||||
<!-- Top Navigation -->
|
||||
<header class="sticky top-0 z-50 w-full border-b border-solid border-border-dark bg-background-dark/80 backdrop-blur-md">
|
||||
<div class="mx-auto flex h-16 max-w-[1440px] items-center justify-between px-6 lg:px-10">
|
||||
<div class="flex items-center gap-8">
|
||||
<!-- Logo -->
|
||||
<a class="flex items-center gap-3 text-white hover:opacity-90 transition-opacity" href="{{ url_for('index') }}">
|
||||
<div class="flex items-center justify-center size-8 rounded-lg bg-gradient-tech text-black">
|
||||
<span class="material-symbols-outlined" style="font-size: 20px; font-weight: 700;">bolt</span>
|
||||
</div>
|
||||
<h2 class="text-white text-lg font-bold leading-tight tracking-tight">ZJPB</h2>
|
||||
</a>
|
||||
<!-- Desktop Nav -->
|
||||
<nav class="hidden md:flex items-center gap-8">
|
||||
<a class="text-gray-300 hover:text-primary text-sm font-medium transition-colors" href="{{ url_for('index') }}">首页</a>
|
||||
{% if current_user.is_authenticated %}
|
||||
<a class="text-gray-300 hover:text-primary text-sm font-medium transition-colors" href="{{ url_for('admin.index') }}">后台管理</a>
|
||||
{% endif %}
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
<!-- Actions -->
|
||||
<div class="flex items-center gap-4">
|
||||
{% if current_user.is_authenticated %}
|
||||
<button onclick="window.location.href='{{ url_for('admin_logout') }}'" class="hidden sm:flex h-9 px-4 items-center justify-center rounded-lg text-sm font-bold text-white hover:bg-white/5 transition-colors">
|
||||
退出
|
||||
</button>
|
||||
{% else %}
|
||||
<button onclick="window.location.href='{{ url_for('admin_login') }}'" class="hidden sm:flex h-9 px-4 items-center justify-center rounded-lg text-sm font-bold text-white hover:bg-white/5 transition-colors">
|
||||
登录
|
||||
</button>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<!-- Main Content -->
|
||||
<main class="flex-1">
|
||||
{% block content %}{% endblock %}
|
||||
</main>
|
||||
|
||||
<!-- Footer -->
|
||||
<footer class="w-full border-t border-border-dark bg-background-dark py-8 mt-auto">
|
||||
<div class="mx-auto max-w-[1200px] flex flex-col md:flex-row items-center justify-between gap-6 px-6 lg:px-10">
|
||||
<div class="flex items-center gap-2 text-gray-400 text-sm">
|
||||
<span>© 2024 ZJPB - 焦提示词. All rights reserved.</span>
|
||||
</div>
|
||||
<div class="flex gap-6">
|
||||
<a class="text-gray-500 hover:text-primary transition-colors text-sm" href="#">
|
||||
发现最好用的AI工具
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
</div>
|
||||
|
||||
{% block extra_js %}{% endblock %}
|
||||
</body>
|
||||
</html>
|
||||
136
templates/detail.html
Normal file
136
templates/detail.html
Normal file
@@ -0,0 +1,136 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}{{ site.name }} - ZJPB 焦提示词{% endblock %}
|
||||
{% block description %}{{ site.short_desc or site.description[:150] }}{% endblock %}
|
||||
{% block keywords %}{{ site.name }},{% for tag in site.tags %}{{ tag.name }},{% endfor %}ZJPB,焦提示词,AI工具{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<main class="flex-grow w-full max-w-[1280px] mx-auto px-4 sm:px-6 lg:px-8 py-8">
|
||||
<!-- Back Button -->
|
||||
<div class="mb-8">
|
||||
<a href="{{ url_for('index') }}" class="group flex items-center gap-2 text-gray-400 hover:text-primary transition-colors text-sm font-bold">
|
||||
<span class="material-symbols-outlined text-[20px] group-hover:-translate-x-1 transition-transform">arrow_back</span>
|
||||
<span>返回首页</span>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-1 lg:grid-cols-12 gap-8 lg:gap-12">
|
||||
<!-- Left Column: Main Content -->
|
||||
<div class="lg:col-span-8 flex flex-col gap-8">
|
||||
<!-- Header Section -->
|
||||
<div class="flex flex-col sm:flex-row gap-6 items-start">
|
||||
<!-- Logo Container -->
|
||||
<div class="relative shrink-0">
|
||||
<div class="absolute -inset-1 bg-gradient-to-br from-primary via-purple-500 to-secondary rounded-2xl opacity-30 blur-md"></div>
|
||||
{% if site.logo %}
|
||||
<div class="relative size-[120px] rounded-xl bg-surface-dark flex items-center justify-center overflow-hidden border border-border-dark" style="background-image: url('{{ site.logo }}'); background-size: cover; background-position: center;"></div>
|
||||
{% else %}
|
||||
<div class="relative size-[120px] rounded-xl bg-gradient-to-br from-primary to-secondary flex items-center justify-center border border-border-dark">
|
||||
<span class="text-5xl font-bold text-black">{{ site.name[0] }}</span>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<!-- Title & Meta -->
|
||||
<div class="flex flex-col gap-3 flex-1">
|
||||
<div>
|
||||
<h1 class="text-4xl font-bold text-white tracking-tight font-display mb-1">{{ site.name }}</h1>
|
||||
<a class="text-primary hover:text-primary-dark hover:underline text-base font-medium flex items-center gap-1" href="{{ site.url }}" target="_blank">
|
||||
{{ site.url }}
|
||||
<span class="material-symbols-outlined text-[16px]">open_in_new</span>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<!-- Tags -->
|
||||
<div class="flex flex-wrap gap-2">
|
||||
{% for tag in site.tags %}
|
||||
<span class="inline-flex items-center px-3 py-1 rounded-full text-xs font-medium bg-surface-dark text-gray-300 border border-border-dark hover:border-primary/30 transition-colors cursor-default">
|
||||
{{ tag.name }}
|
||||
</span>
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
||||
<!-- Stats Row -->
|
||||
<div class="flex items-center gap-6 mt-1 text-gray-400 text-sm">
|
||||
<div class="flex items-center gap-2">
|
||||
<span class="material-symbols-outlined text-[18px]">visibility</span>
|
||||
<span>{{ site.view_count }} 次浏览</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<span class="material-symbols-outlined text-[18px]">calendar_today</span>
|
||||
<span>{{ site.created_at.strftime('%Y年%m月%d日') }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr class="border-border-dark/60"/>
|
||||
|
||||
<!-- Main Content Body -->
|
||||
<div class="space-y-8">
|
||||
<!-- Overview -->
|
||||
{% if site.short_desc %}
|
||||
<section>
|
||||
<h3 class="text-xl font-bold text-white mb-4 flex items-center gap-2">
|
||||
<span class="material-symbols-outlined text-primary">info</span>
|
||||
产品简介
|
||||
</h3>
|
||||
<p class="text-gray-400 leading-relaxed text-lg">
|
||||
{{ site.short_desc }}
|
||||
</p>
|
||||
</section>
|
||||
{% endif %}
|
||||
|
||||
<!-- Detailed Description -->
|
||||
{% if site.description %}
|
||||
<section>
|
||||
<h3 class="text-xl font-bold text-white mb-4 flex items-center gap-2">
|
||||
<span class="material-symbols-outlined text-primary">article</span>
|
||||
详细介绍
|
||||
</h3>
|
||||
<div class="text-gray-400 space-y-4 leading-relaxed whitespace-pre-wrap">
|
||||
{{ site.description|safe }}
|
||||
</div>
|
||||
</section>
|
||||
{% endif %}
|
||||
|
||||
<!-- Features -->
|
||||
{% if site.features %}
|
||||
<section>
|
||||
<h3 class="text-xl font-bold text-white mb-4 flex items-center gap-2">
|
||||
<span class="material-symbols-outlined text-primary">star</span>
|
||||
主要功能
|
||||
</h3>
|
||||
<div class="text-gray-400 space-y-4 leading-relaxed whitespace-pre-wrap">
|
||||
{{ site.features|safe }}
|
||||
</div>
|
||||
</section>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Right Column: Sidebar -->
|
||||
<div class="lg:col-span-4 space-y-6">
|
||||
<!-- CTA Card -->
|
||||
<div class="rounded-2xl p-6 flex flex-col gap-4 shadow-xl shadow-black/20 sticky top-24 bg-surface-dark/60 backdrop-blur-xl border border-border-dark">
|
||||
<div class="flex items-center justify-between mb-2">
|
||||
<span class="text-white font-bold text-lg">立即体验</span>
|
||||
</div>
|
||||
|
||||
<a class="group relative flex items-center justify-center w-full overflow-hidden rounded-xl bg-primary hover:bg-primary/90 p-4 focus:outline-none transition-all hover:scale-[1.02] shadow-[0_0_20px_rgba(37,192,244,0.3)] hover:shadow-[0_0_30px_rgba(37,192,244,0.5)]" href="{{ site.url }}" target="_blank">
|
||||
<span class="flex items-center gap-2 text-black font-bold text-base uppercase tracking-wide">
|
||||
访问网站
|
||||
<span class="material-symbols-outlined text-[20px] font-bold">arrow_outward</span>
|
||||
</span>
|
||||
</a>
|
||||
|
||||
<div class="pt-4 border-t border-border-dark">
|
||||
<p class="text-gray-500 text-xs text-center">
|
||||
{{ site.url }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
{% endblock %}
|
||||
82
templates/index.html
Normal file
82
templates/index.html
Normal file
@@ -0,0 +1,82 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}ZJPB - 焦提示词 - 发现最好用的AI产品{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<main class="flex-1 flex flex-col items-center w-full px-6 lg:px-10 py-8">
|
||||
<div class="w-full max-w-[1200px] flex flex-col gap-10">
|
||||
<!-- Hero Section -->
|
||||
<div class="flex flex-col md:flex-row items-start md:items-end justify-between gap-6 pb-6 border-b border-border-dark/50">
|
||||
<div class="flex flex-col gap-2 max-w-2xl">
|
||||
<h1 class="text-4xl md:text-5xl lg:text-6xl font-black tracking-tighter text-white mb-2">
|
||||
ZJPB - <span class="text-gradient">焦提示词</span>
|
||||
</h1>
|
||||
<p class="text-gray-400 text-lg md:text-xl font-light">
|
||||
发现最新最好用的AI工具和产品
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Filter Chips -->
|
||||
<div class="w-full overflow-x-auto pb-2 scrollbar-hide">
|
||||
<div class="flex gap-3 min-w-max">
|
||||
<a href="{{ url_for('index') }}" class="flex items-center h-9 px-5 rounded-full {% if not selected_tag %}bg-primary text-background-dark shadow-[0_0_10px_rgba(37,192,244,0.4)]{% else %}bg-surface-dark border border-border-dark text-gray-300 hover:text-white hover:border-gray-500{% endif %} font-bold text-sm transition-all">
|
||||
全部
|
||||
</a>
|
||||
{% for tag in tags %}
|
||||
<a href="{{ url_for('index', tag=tag.slug) }}" class="flex items-center gap-2 h-9 px-5 rounded-full {% if selected_tag and selected_tag.id == tag.id %}bg-primary text-background-dark shadow-[0_0_10px_rgba(37,192,244,0.4)]{% else %}bg-surface-dark border border-border-dark text-gray-300 hover:text-white hover:border-gray-500{% endif %} font-medium text-sm transition-all">
|
||||
{% if tag.icon %}<span class="material-symbols-outlined text-sm">{{ tag.icon.replace('fas fa-', '').replace('fab fa-', '') }}</span>{% endif %}
|
||||
{{ tag.name }}
|
||||
</a>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Tool Grid -->
|
||||
{% if sites %}
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-6">
|
||||
{% for site in sites %}
|
||||
<div class="group relative flex flex-col gap-4 rounded-xl border border-border-dark bg-surface-dark p-5 card-hover transition-all duration-300 cursor-pointer overflow-hidden" onclick="window.location.href='{{ url_for('site_detail', slug=site.slug) }}'">
|
||||
<div class="absolute top-0 left-0 w-full h-1 bg-gradient-to-r from-primary to-secondary opacity-0 group-hover:opacity-100 transition-opacity"></div>
|
||||
|
||||
<div class="flex items-start justify-between">
|
||||
{% if site.logo %}
|
||||
<div class="size-12 rounded-lg bg-cover bg-center shadow-lg" style="background-image: url('{{ site.logo }}');"></div>
|
||||
{% else %}
|
||||
<div class="size-12 rounded-lg bg-gradient-to-br from-primary to-secondary flex items-center justify-center text-black font-bold text-xl shadow-lg">
|
||||
{{ site.name[0] }}
|
||||
</div>
|
||||
{% endif %}
|
||||
<div class="flex items-center justify-center size-8 rounded-full bg-background-dark text-gray-400 group-hover:text-primary group-hover:bg-primary/10 transition-colors">
|
||||
<span class="material-symbols-outlined text-lg">arrow_outward</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h3 class="text-white text-lg font-bold leading-tight group-hover:text-primary transition-colors">{{ site.name }}</h3>
|
||||
<p class="text-gray-400 text-sm mt-2 line-clamp-2">{{ site.short_desc or '暂无描述' }}</p>
|
||||
</div>
|
||||
|
||||
<div class="mt-auto flex items-center justify-between pt-4 border-t border-border-dark">
|
||||
<div class="flex gap-2 flex-wrap">
|
||||
{% for tag in site.tags[:2] %}
|
||||
<span class="px-2 py-0.5 rounded text-[10px] font-bold uppercase tracking-wider bg-white/5 text-gray-300 border border-white/10">{{ tag.name }}</span>
|
||||
{% endfor %}
|
||||
</div>
|
||||
<div class="flex items-center gap-1 text-gray-500 text-xs">
|
||||
<span class="material-symbols-outlined text-[14px]">visibility</span>
|
||||
<span>{{ site.view_count }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="text-center py-20">
|
||||
<span class="material-symbols-outlined text-6xl text-gray-600 mb-4 block">search_off</span>
|
||||
<p class="text-gray-400 text-lg">暂无相关AI工具</p>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</main>
|
||||
{% endblock %}
|
||||
Reference in New Issue
Block a user