fix: v2.1.0补充提交 - 配置完善和功能修复
修复内容: - 完善tag_generator.py的generate_description方法 - 优化index_new.html首页图标CSS - 更新templates/admin登录页面和后台模板 - 完善config.py配置 - 更新requirements.txt依赖列表 - 优化.gitignore规则 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -20,7 +20,15 @@
|
||||
"Bash(git commit:*)",
|
||||
"Bash(curl:*)",
|
||||
"WebFetch(domain:zjpb.net)",
|
||||
"Bash(del import_bookmarks.py test_bookmark_parse.py test_simple_parse.py result.txt)"
|
||||
"Bash(del import_bookmarks.py test_bookmark_parse.py test_simple_parse.py result.txt)",
|
||||
"Bash(git tag:*)",
|
||||
"Bash(if [ -f .env ])",
|
||||
"Bash(then echo \"exists\")",
|
||||
"Bash(else echo \"not exists\")",
|
||||
"Bash(timeout /t 3 /nobreak)",
|
||||
"Bash(ping:*)",
|
||||
"Bash(git diff-tree:*)",
|
||||
"Bash(git format-patch:*)"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
8
.gitignore
vendored
8
.gitignore
vendored
@@ -33,6 +33,8 @@ instance/
|
||||
# 环境变量
|
||||
.env
|
||||
.env.local
|
||||
.env.production
|
||||
.env.*.local
|
||||
|
||||
# 数据库
|
||||
*.db
|
||||
@@ -58,6 +60,12 @@ Thumbs.db
|
||||
static/uploads/*
|
||||
!static/uploads/.gitkeep
|
||||
|
||||
# 导出文件
|
||||
urls.txt
|
||||
sites_export.csv
|
||||
backup_*.sql
|
||||
*.sql
|
||||
|
||||
# 临时文件
|
||||
*.tmp
|
||||
*.bak
|
||||
|
||||
@@ -42,6 +42,10 @@ class Config:
|
||||
MAX_CONTENT_LENGTH = 5 * 1024 * 1024 # 5MB
|
||||
ALLOWED_EXTENSIONS = {'png', 'jpg', 'jpeg', 'gif', 'webp'}
|
||||
|
||||
# DeepSeek API配置
|
||||
DEEPSEEK_API_KEY = os.environ.get('DEEPSEEK_API_KEY')
|
||||
DEEPSEEK_BASE_URL = os.environ.get('DEEPSEEK_BASE_URL') or 'https://api.deepseek.com'
|
||||
|
||||
class DevelopmentConfig(Config):
|
||||
"""开发环境配置"""
|
||||
DEBUG = True
|
||||
|
||||
@@ -62,6 +62,7 @@ def init_database():
|
||||
print("\n正在创建示例网站...")
|
||||
sites_data = [
|
||||
{
|
||||
'code': '10001001',
|
||||
'name': 'ChatGPT',
|
||||
'url': 'https://chat.openai.com',
|
||||
'slug': 'chatgpt',
|
||||
@@ -72,6 +73,7 @@ def init_database():
|
||||
'sort_order': 100
|
||||
},
|
||||
{
|
||||
'code': '10001002',
|
||||
'name': 'Midjourney',
|
||||
'url': 'https://www.midjourney.com',
|
||||
'slug': 'midjourney',
|
||||
@@ -82,6 +84,7 @@ def init_database():
|
||||
'sort_order': 95
|
||||
},
|
||||
{
|
||||
'code': '10001003',
|
||||
'name': 'GitHub Copilot',
|
||||
'url': 'https://github.com/features/copilot',
|
||||
'slug': 'github-copilot',
|
||||
|
||||
@@ -11,3 +11,6 @@ requests==2.31.0
|
||||
beautifulsoup4==4.12.2
|
||||
Pillow>=10.2.0
|
||||
openai>=1.0.0
|
||||
gunicorn==21.2.0
|
||||
pypinyin==0.51.0
|
||||
markdown==3.5.1
|
||||
|
||||
@@ -85,6 +85,12 @@
|
||||
<span class="nav-text">批量导入</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a href="{{ url_for('change_password') }}" class="nav-link">
|
||||
<span class="material-symbols-outlined nav-icon">lock_reset</span>
|
||||
<span class="nav-text">修改密码</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a href="{{ url_for('index') }}" class="nav-link" target="_blank">
|
||||
<span class="material-symbols-outlined nav-icon">open_in_new</span>
|
||||
|
||||
@@ -1,67 +1,97 @@
|
||||
{% extends "base.html" %}
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>管理员登录 - ZJPB 焦提示词</title>
|
||||
|
||||
{% block title %}管理员登录 - ZJPB 焦提示词{% 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">
|
||||
|
||||
{% block extra_css %}
|
||||
<!-- 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 = {
|
||||
theme: {
|
||||
extend: {
|
||||
colors: {
|
||||
"primary": "#0ea5e9", // Sky 500
|
||||
"primary-dark": "#0284c7", // Sky 600
|
||||
"background": "#f8fafc", // Slate 50
|
||||
"surface": "#ffffff",
|
||||
"input-bg": "#ffffff",
|
||||
"input-border": "#e2e8f0", // Slate 200
|
||||
"text-main": "#0f172a", // Slate 900
|
||||
"text-secondary": "#334155", // Slate 700
|
||||
"text-muted": "#64748b", // Slate 500
|
||||
},
|
||||
fontFamily: {
|
||||
"display": ["Space Grotesk", "sans-serif"],
|
||||
"body": ["Noto Sans", "sans-serif"],
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
<style>
|
||||
.glass-panel {
|
||||
background: rgba(22, 33, 37, 0.7);
|
||||
backdrop-filter: blur(12px);
|
||||
-webkit-backdrop-filter: blur(12px);
|
||||
background: rgba(255, 255, 255, 0.75);
|
||||
backdrop-filter: blur(20px);
|
||||
-webkit-backdrop-filter: blur(20px);
|
||||
}
|
||||
.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-image: radial-gradient(circle at center, rgba(14, 165, 233, 0.04) 0%, transparent 60%),
|
||||
linear-gradient(rgba(14, 165, 233, 0.05) 1px, transparent 1px),
|
||||
linear-gradient(90deg, rgba(14, 165, 233, 0.05) 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">
|
||||
</head>
|
||||
<body class="bg-[#f8fafc] font-display text-[#0f172a] min-h-screen flex flex-col items-center justify-center relative overflow-hidden selection:bg-sky-500/20 selection:text-sky-700">
|
||||
<!-- 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>
|
||||
<div class="absolute top-[-20%] right-[-10%] w-[600px] h-[600px] bg-sky-200/40 rounded-full blur-[120px] pointer-events-none mix-blend-multiply"></div>
|
||||
<div class="absolute bottom-[-10%] left-[-10%] w-[500px] h-[500px] bg-indigo-100/60 rounded-full blur-[100px] pointer-events-none mix-blend-multiply"></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">
|
||||
<a class="group flex items-center gap-2 text-[#64748b] hover:text-[#0f172a] transition-colors w-fit" href="{{ url_for('index') }}">
|
||||
<div class="flex items-center justify-center w-8 h-8 rounded-full border border-[#e2e8f0] bg-white group-hover:border-sky-500/50 group-hover:bg-sky-500/5 transition-all shadow-sm">
|
||||
<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">
|
||||
<div class="glass-panel border border-white rounded-2xl shadow-[0_4px_30px_rgba(0,0,0,0.03)] p-8 md:p-12 relative overflow-hidden ring-1 ring-black/5">
|
||||
<!-- 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>
|
||||
<div class="absolute top-0 left-0 w-full h-[2px] bg-gradient-to-r from-transparent via-sky-500/50 to-transparent"></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">
|
||||
<div class="p-2 rounded-lg bg-sky-500/10 text-sky-500">
|
||||
<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>
|
||||
<span class="text-xs font-bold tracking-widest uppercase text-sky-500">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>
|
||||
<h1 class="text-3xl font-black tracking-tight text-[#0f172a] leading-tight">管理员登录</h1>
|
||||
<p class="text-[#64748b] 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 class="mb-6 p-3 rounded-lg bg-red-50 border border-red-200 flex items-start gap-3">
|
||||
<span class="material-symbols-outlined text-red-500 text-sm mt-0.5">error</span>
|
||||
<p class="text-red-600 text-sm">{{ message }}</p>
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
@@ -71,16 +101,16 @@
|
||||
<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>
|
||||
<label class="text-[#334155] text-sm font-semibold 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"
|
||||
<input class="form-input flex w-full h-12 pl-11 pr-4 rounded-lg text-[#0f172a] focus:outline-0 focus:ring-2 focus:ring-sky-500/20 border border-[#e2e8f0] bg-white focus:border-sky-500 placeholder:text-slate-400 text-base font-normal leading-normal transition-all shadow-sm"
|
||||
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">
|
||||
<div class="absolute left-3 top-1/2 -translate-y-1/2 text-slate-400 group-focus-within:text-sky-500 transition-colors flex items-center justify-center pointer-events-none">
|
||||
<span class="material-symbols-outlined text-[20px]">person</span>
|
||||
</div>
|
||||
</div>
|
||||
@@ -88,34 +118,56 @@
|
||||
|
||||
<!-- Password Field -->
|
||||
<div class="space-y-2">
|
||||
<label class="text-white text-sm font-medium leading-normal" for="password">密码</label>
|
||||
<div class="flex justify-between items-end">
|
||||
<label class="text-[#334155] text-sm font-semibold leading-normal" for="password">密码</label>
|
||||
</div>
|
||||
<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"
|
||||
<input class="form-input flex w-full h-12 pl-11 pr-11 rounded-lg text-[#0f172a] focus:outline-0 focus:ring-2 focus:ring-sky-500/20 border border-[#e2e8f0] bg-white focus:border-sky-500 placeholder:text-slate-400 text-base font-normal leading-normal transition-all shadow-sm 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">
|
||||
<div class="absolute left-3 top-1/2 -translate-y-1/2 text-slate-400 group-focus-within:text-sky-500 transition-colors flex items-center justify-center pointer-events-none z-20">
|
||||
<span class="material-symbols-outlined text-[20px]">lock</span>
|
||||
</div>
|
||||
<button class="absolute right-0 top-0 h-full px-3 text-slate-400 hover:text-[#334155] transition-colors flex items-center justify-center z-20 focus:outline-none"
|
||||
type="button"
|
||||
onclick="togglePassword()">
|
||||
<span class="material-symbols-outlined text-[20px]" id="toggleIcon">visibility_off</span>
|
||||
</button>
|
||||
</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)]"
|
||||
<button class="mt-4 flex w-full h-12 cursor-pointer items-center justify-center overflow-hidden rounded-lg bg-sky-500 text-white text-base font-bold leading-normal tracking-wide hover:bg-sky-600 transition-all active:scale-[0.98] shadow-lg shadow-sky-500/25 hover:shadow-sky-500/40"
|
||||
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">
|
||||
<div class="mt-8 flex justify-center gap-6 border-t border-slate-100 pt-6">
|
||||
<p class="text-[#64748b] text-xs text-center font-medium">
|
||||
ZJPB - 焦提示词 管理系统
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function togglePassword() {
|
||||
const passwordInput = document.getElementById('password');
|
||||
const toggleIcon = document.getElementById('toggleIcon');
|
||||
|
||||
if (passwordInput.type === 'password') {
|
||||
passwordInput.type = 'text';
|
||||
toggleIcon.textContent = 'visibility';
|
||||
} else {
|
||||
passwordInput.type = 'password';
|
||||
toggleIcon.textContent = 'visibility_off';
|
||||
}
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
{% endblock %}
|
||||
</html>
|
||||
|
||||
@@ -41,6 +41,7 @@
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
flex-wrap: wrap;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.category-tab {
|
||||
@@ -58,6 +59,74 @@
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.category-tab .tag-count {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-width: 20px;
|
||||
height: 20px;
|
||||
padding: 0 6px;
|
||||
background: rgba(100, 116, 139, 0.1);
|
||||
color: var(--text-secondary);
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
border-radius: 10px;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
|
||||
.category-tab.active .tag-count {
|
||||
background: rgba(255, 255, 255, 0.25);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.category-tab:hover .tag-count {
|
||||
background: rgba(59, 130, 246, 0.15);
|
||||
color: var(--primary-blue);
|
||||
}
|
||||
|
||||
.category-tab.active:hover .tag-count {
|
||||
background: rgba(255, 255, 255, 0.35);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.category-tab.hidden-tag {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.category-tab.show-all .hidden-tag {
|
||||
display: inline-flex;
|
||||
}
|
||||
|
||||
.more-tags-btn {
|
||||
padding: 10px 20px;
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 50px;
|
||||
background: var(--bg-white);
|
||||
color: var(--primary-blue);
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.more-tags-btn:hover {
|
||||
border-color: var(--primary-blue);
|
||||
background: rgba(59, 130, 246, 0.05);
|
||||
}
|
||||
|
||||
.more-tags-btn .expand-icon {
|
||||
font-size: 14px;
|
||||
transition: transform 0.2s;
|
||||
}
|
||||
|
||||
.more-tags-btn.expanded .expand-icon {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
|
||||
.category-tab:hover {
|
||||
border-color: var(--primary-blue);
|
||||
color: var(--primary-blue);
|
||||
@@ -69,8 +138,8 @@
|
||||
color: white;
|
||||
}
|
||||
|
||||
.category-tab .material-symbols-outlined {
|
||||
font-size: 18px;
|
||||
.category-tab .icon {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
/* 工具网格 */
|
||||
@@ -187,10 +256,6 @@
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.tool-views .material-symbols-outlined {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
/* 分页 */
|
||||
.pagination {
|
||||
display: flex;
|
||||
@@ -243,7 +308,7 @@
|
||||
padding: 80px 20px;
|
||||
}
|
||||
|
||||
.empty-state .material-symbols-outlined {
|
||||
.empty-state .icon {
|
||||
font-size: 64px;
|
||||
color: var(--text-muted);
|
||||
margin-bottom: 16px;
|
||||
@@ -286,25 +351,32 @@
|
||||
|
||||
<!-- 分类过滤 -->
|
||||
<div class="categories" id="categories">
|
||||
<div class="category-tabs">
|
||||
<div class="category-tabs" id="categoryTabs">
|
||||
<a href="/" class="category-tab {% if not selected_tag %}active{% endif %}">
|
||||
All Tools
|
||||
</a>
|
||||
{% for tag in tags %}
|
||||
<a href="/?tag={{ tag.slug }}" class="category-tab {% if selected_tag and selected_tag.id == tag.id %}active{% endif %}">
|
||||
<span class="material-symbols-outlined">
|
||||
{% if 'Chat' in tag.name or 'GPT' in tag.name %}chat
|
||||
{% elif 'Image' in tag.name or '图' in tag.name %}image
|
||||
{% elif 'Video' in tag.name or '视频' in tag.name %}videocam
|
||||
{% elif 'Writing' in tag.name or '写作' in tag.name %}edit_note
|
||||
{% elif 'Coding' in tag.name or '代码' in tag.name %}code
|
||||
{% elif 'Audio' in tag.name or '音频' in tag.name %}graphic_eq
|
||||
{% elif '3D' in tag.name %}view_in_ar
|
||||
{% else %}label{% endif %}
|
||||
<a href="/?tag={{ tag.slug }}" class="category-tab {% if selected_tag and selected_tag.id == tag.id %}active{% endif %} {% if loop.index > 10 %}hidden-tag{% endif %}" data-tag-index="{{ loop.index }}">
|
||||
<span>
|
||||
{% if 'Chat' in tag.name or 'GPT' in tag.name or '对话' in tag.name %}💬
|
||||
{% elif 'Image' in tag.name or '图' in tag.name %}🖼️
|
||||
{% elif 'Video' in tag.name or '视频' in tag.name %}🎬
|
||||
{% elif 'Writing' in tag.name or '写作' in tag.name %}✍️
|
||||
{% elif 'Coding' in tag.name or '代码' in tag.name %}💻
|
||||
{% elif 'Audio' in tag.name or '音频' in tag.name %}🎵
|
||||
{% elif '3D' in tag.name %}🎨
|
||||
{% else %}🏷️{% endif %}
|
||||
</span>
|
||||
{{ tag.name }}
|
||||
<span class="tag-count">{{ tag_counts.get(tag.id, 0) }}</span>
|
||||
</a>
|
||||
{% endfor %}
|
||||
{% if tags|length > 10 %}
|
||||
<div class="more-tags-btn" id="moreTagsBtn">
|
||||
<span>更多</span>
|
||||
<span class="expand-icon">▼</span>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -313,7 +385,7 @@
|
||||
{% if search_query %}
|
||||
<div style="margin-bottom: 24px; padding: 16px; background: #f1f5f9; border-radius: 8px;">
|
||||
<p style="margin: 0; color: #64748b; font-size: 14px;">
|
||||
<span class="material-symbols-outlined" style="font-size: 18px; vertical-align: middle;">search</span>
|
||||
<span style="font-size: 18px; vertical-align: middle;">🔍</span>
|
||||
搜索 "{{ search_query }}" 的结果:找到 {{ pagination.total if pagination else sites|length }} 个工具
|
||||
<a href="/" style="margin-left: 12px; color: #0ea5e9; text-decoration: none;">清除搜索</a>
|
||||
</p>
|
||||
@@ -329,7 +401,7 @@
|
||||
{% else %}
|
||||
<div class="tool-logo" style="background: linear-gradient(135deg, #0ea5e9 0%, #8b5cf6 100%);"></div>
|
||||
{% endif %}
|
||||
<span class="material-symbols-outlined tool-link-icon">north_east</span>
|
||||
<span class="tool-link-icon">↗</span>
|
||||
</div>
|
||||
<h3 class="tool-name">{{ site.name }}</h3>
|
||||
<p class="tool-description">{{ site.short_desc or site.description }}</p>
|
||||
@@ -340,7 +412,7 @@
|
||||
{% endfor %}
|
||||
</div>
|
||||
<div class="tool-views">
|
||||
<span class="material-symbols-outlined">visibility</span>
|
||||
<span>👁</span>
|
||||
<span>{% if site.view_count >= 1000 %}{{ (site.view_count / 1000) | round(1) }}k{% else %}{{ site.view_count | default(0) }}{% endif %}</span>
|
||||
</div>
|
||||
</div>
|
||||
@@ -354,11 +426,11 @@
|
||||
<!-- 上一页 -->
|
||||
{% if pagination.has_prev %}
|
||||
<a href="?page={{ pagination.prev_num }}{% if selected_tag %}&tag={{ selected_tag.slug }}{% endif %}{% if search_query %}&q={{ search_query }}{% endif %}">
|
||||
<span class="material-symbols-outlined">chevron_left</span>
|
||||
<span>◀</span>
|
||||
</a>
|
||||
{% else %}
|
||||
<a href="#" class="disabled">
|
||||
<span class="material-symbols-outlined">chevron_left</span>
|
||||
<span>◀</span>
|
||||
</a>
|
||||
{% endif %}
|
||||
|
||||
@@ -391,22 +463,52 @@
|
||||
<!-- 下一页 -->
|
||||
{% if pagination.has_next %}
|
||||
<a href="?page={{ pagination.next_num }}{% if selected_tag %}&tag={{ selected_tag.slug }}{% endif %}{% if search_query %}&q={{ search_query }}{% endif %}">
|
||||
<span class="material-symbols-outlined">chevron_right</span>
|
||||
<span>▶</span>
|
||||
</a>
|
||||
{% else %}
|
||||
<a href="#" class="disabled">
|
||||
<span class="material-symbols-outlined">chevron_right</span>
|
||||
<span>▶</span>
|
||||
</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
<div class="empty-state">
|
||||
<span class="material-symbols-outlined">search_off</span>
|
||||
<span class="icon">🔍</span>
|
||||
<h3>暂无工具</h3>
|
||||
<p>{% if selected_tag %}该分类下还没有工具{% else %}还没有添加任何工具{% endif %}</p>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// 标签展开/收起功能
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
const moreTagsBtn = document.getElementById('moreTagsBtn');
|
||||
if (moreTagsBtn) {
|
||||
moreTagsBtn.addEventListener('click', function() {
|
||||
const categoryTabs = document.getElementById('categoryTabs');
|
||||
const hiddenTags = categoryTabs.querySelectorAll('.hidden-tag');
|
||||
const isExpanded = moreTagsBtn.classList.contains('expanded');
|
||||
|
||||
if (isExpanded) {
|
||||
// 收起
|
||||
hiddenTags.forEach(tag => {
|
||||
tag.style.display = 'none';
|
||||
});
|
||||
moreTagsBtn.classList.remove('expanded');
|
||||
moreTagsBtn.querySelector('span:first-child').textContent = '更多';
|
||||
} else {
|
||||
// 展开
|
||||
hiddenTags.forEach(tag => {
|
||||
tag.style.display = 'inline-flex';
|
||||
});
|
||||
moreTagsBtn.classList.add('expanded');
|
||||
moreTagsBtn.querySelector('span:first-child').textContent = '收起';
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
||||
@@ -83,6 +83,141 @@ class TagGenerator:
|
||||
print(f"DeepSeek标签生成失败: {str(e)}")
|
||||
return []
|
||||
|
||||
def generate_features(self, name, description, url=""):
|
||||
"""
|
||||
根据产品名称和描述生成主要功能列表
|
||||
|
||||
Args:
|
||||
name: 产品名称
|
||||
description: 产品描述
|
||||
url: 产品URL(可选)
|
||||
|
||||
Returns:
|
||||
str: 生成的功能列表(Markdown格式)
|
||||
"""
|
||||
# 检查是否配置了API key
|
||||
if not self.client:
|
||||
raise ValueError("DEEPSEEK_API_KEY未配置,请在.env文件中添加")
|
||||
|
||||
try:
|
||||
# 构建提示词
|
||||
url_info = f"\n产品网址: {url}" if url else ""
|
||||
|
||||
prompt = f"""你是一个AI工具导航网站的内容编辑助手。根据以下产品信息,生成详细的主要功能列表。
|
||||
|
||||
产品名称: {name}
|
||||
|
||||
产品描述: {description}{url_info}
|
||||
|
||||
要求:
|
||||
1. 生成5-8个主要功能点
|
||||
2. 每个功能点要具体、清晰、有吸引力
|
||||
3. 使用Markdown无序列表格式(以"- "开头)
|
||||
4. 每个功能点一行,简洁有力(10-30字)
|
||||
5. 突出产品的核心价值和特色功能
|
||||
6. 使用专业但易懂的语言
|
||||
7. 不要添加任何标题或额外说明,直接输出功能列表
|
||||
|
||||
示例输出格式:
|
||||
- 智能文本生成,支持多种写作场景
|
||||
- 实时语法检查和优化建议
|
||||
- 多语言翻译,准确率高达95%
|
||||
- 一键生成营销文案和广告语
|
||||
- 团队协作,支持多人同时编辑
|
||||
|
||||
请生成功能列表:"""
|
||||
|
||||
# 调用DeepSeek API
|
||||
response = self.client.chat.completions.create(
|
||||
model="deepseek-chat",
|
||||
messages=[
|
||||
{"role": "system", "content": "你是一个专业的AI产品文案专家,擅长提炼产品核心功能和价值点。"},
|
||||
{"role": "user", "content": prompt}
|
||||
],
|
||||
temperature=0.7,
|
||||
max_tokens=500
|
||||
)
|
||||
|
||||
# 获取返回的功能列表
|
||||
features_text = response.choices[0].message.content.strip()
|
||||
|
||||
return features_text
|
||||
|
||||
except Exception as e:
|
||||
print(f"DeepSeek功能生成失败: {str(e)}")
|
||||
return ""
|
||||
|
||||
def generate_description(self, name, short_desc="", url=""):
|
||||
"""
|
||||
根据产品名称和简短描述生成详细介绍
|
||||
|
||||
Args:
|
||||
name: 产品名称
|
||||
short_desc: 简短描述
|
||||
url: 产品URL(可选)
|
||||
|
||||
Returns:
|
||||
str: 生成的详细介绍(Markdown格式)
|
||||
"""
|
||||
# 检查是否配置了API key
|
||||
if not self.client:
|
||||
raise ValueError("DEEPSEEK_API_KEY未配置,请在.env文件中添加")
|
||||
|
||||
try:
|
||||
# 构建提示词
|
||||
url_info = f"\n产品网址: {url}" if url else ""
|
||||
short_desc_info = f"\n简短描述: {short_desc}" if short_desc else ""
|
||||
|
||||
prompt = f"""你是一个AI工具导航网站的内容编辑助手。根据以下产品信息,生成详细、专业且吸引人的产品介绍。
|
||||
|
||||
产品名称: {name}{short_desc_info}{url_info}
|
||||
|
||||
要求:
|
||||
1. 生成200-400字的详细介绍
|
||||
2. 包含以下内容:
|
||||
- 产品定位和核心价值(这是什么产品,解决什么问题)
|
||||
- 主要特点和优势(为什么选择这个产品)
|
||||
- 适用场景和目标用户(谁会用,用在哪里)
|
||||
3. 使用Markdown格式,可以包含:
|
||||
- 段落分隔(空行)
|
||||
- 加粗重点内容(**文字**)
|
||||
- 列表(- 列表项)
|
||||
4. 语言专业但易懂,突出产品价值
|
||||
5. 不要添加标题,直接输出正文内容
|
||||
6. 语气客观、事实性强,避免过度营销
|
||||
|
||||
示例输出格式:
|
||||
ChatGPT是由OpenAI开发的**先进对话式AI助手**,基于GPT-4大语言模型构建。它能够理解和生成自然语言,为用户提供智能对话、内容创作、代码编写等多种服务。
|
||||
|
||||
**核心优势:**
|
||||
- 强大的语言理解和生成能力
|
||||
- 支持多轮对话,上下文连贯
|
||||
- 覆盖编程、写作、翻译等多个领域
|
||||
|
||||
适用于内容创作者、程序员、学生等各类用户,可用于日常问答、文案撰写、学习辅导、编程助手等多种场景。
|
||||
|
||||
请生成详细介绍:"""
|
||||
|
||||
# 调用DeepSeek API
|
||||
response = self.client.chat.completions.create(
|
||||
model="deepseek-chat",
|
||||
messages=[
|
||||
{"role": "system", "content": "你是一个专业的AI产品文案专家,擅长撰写准确、客观、有吸引力的产品介绍。"},
|
||||
{"role": "user", "content": prompt}
|
||||
],
|
||||
temperature=0.7,
|
||||
max_tokens=800
|
||||
)
|
||||
|
||||
# 获取返回的详细介绍
|
||||
description_text = response.choices[0].message.content.strip()
|
||||
|
||||
return description_text
|
||||
|
||||
except Exception as e:
|
||||
print(f"DeepSeek详细介绍生成失败: {str(e)}")
|
||||
return ""
|
||||
|
||||
def generate_news_summary(self, url, content):
|
||||
"""
|
||||
生成新闻摘要(未来功能)
|
||||
|
||||
Reference in New Issue
Block a user