release: v2.0 - 完整功能管理系统
主要功能: - 完整的Flask-Admin后台管理系统 - 网站/标签/新闻管理功能 - 用户登录认证系统 - 科技感/未来风UI设计 - 标签分类系统(取代传统分类) - 详情页面展示 - 数据库迁移脚本 - 书签导入解析工具 技术栈: - Flask + SQLAlchemy - Flask-Admin管理界面 - Bootstrap 4响应式设计 - 用户认证与权限管理 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
@@ -16,7 +16,11 @@
|
||||
"Bash(findstr:*)",
|
||||
"Bash(dir:*)",
|
||||
"Bash(git init:*)",
|
||||
"Bash(git add:*)"
|
||||
"Bash(git add:*)",
|
||||
"Bash(git commit:*)",
|
||||
"Bash(curl:*)",
|
||||
"WebFetch(domain:zjpb.net)",
|
||||
"Bash(del import_bookmarks.py test_bookmark_parse.py test_simple_parse.py result.txt)"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,3 +10,7 @@ SECRET_KEY=your-secret-key-here
|
||||
|
||||
# 运行环境 (development/production)
|
||||
FLASK_ENV=development
|
||||
|
||||
# DeepSeek API配置
|
||||
DEEPSEEK_API_KEY=your_deepseek_api_key_here
|
||||
DEEPSEEK_BASE_URL=https://api.deepseek.com
|
||||
|
||||
8
.mcp.json
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"mcpServers": {
|
||||
"tdesign-mcp-server": {
|
||||
"command": "npx",
|
||||
"args": ["-y", "tdesign-mcp-server"]
|
||||
}
|
||||
}
|
||||
}
|
||||
49
=1.0.0
Normal file
@@ -0,0 +1,49 @@
|
||||
Defaulting to user installation because normal site-packages is not writeable
|
||||
Collecting openai
|
||||
Downloading openai-2.14.0-py3-none-any.whl.metadata (29 kB)
|
||||
Collecting anyio<5,>=3.5.0 (from openai)
|
||||
Downloading anyio-4.12.0-py3-none-any.whl.metadata (4.3 kB)
|
||||
Collecting distro<2,>=1.7.0 (from openai)
|
||||
Downloading distro-1.9.0-py3-none-any.whl.metadata (6.8 kB)
|
||||
Collecting httpx<1,>=0.23.0 (from openai)
|
||||
Downloading httpx-0.28.1-py3-none-any.whl.metadata (7.1 kB)
|
||||
Collecting jiter<1,>=0.10.0 (from openai)
|
||||
Downloading jiter-0.12.0-cp313-cp313-win_amd64.whl.metadata (5.3 kB)
|
||||
Collecting pydantic<3,>=1.9.0 (from openai)
|
||||
Downloading pydantic-2.12.5-py3-none-any.whl.metadata (90 kB)
|
||||
Collecting sniffio (from openai)
|
||||
Downloading sniffio-1.3.1-py3-none-any.whl.metadata (3.9 kB)
|
||||
Collecting tqdm>4 (from openai)
|
||||
Downloading tqdm-4.67.1-py3-none-any.whl.metadata (57 kB)
|
||||
Requirement already satisfied: typing-extensions<5,>=4.11 in c:\users\linha\appdata\local\packages\pythonsoftwarefoundation.python.3.13_qbz5n2kfra8p0\localcache\local-packages\python313\site-packages (from openai) (4.15.0)
|
||||
Requirement already satisfied: idna>=2.8 in c:\users\linha\appdata\local\packages\pythonsoftwarefoundation.python.3.13_qbz5n2kfra8p0\localcache\local-packages\python313\site-packages (from anyio<5,>=3.5.0->openai) (3.11)
|
||||
Requirement already satisfied: certifi in c:\users\linha\appdata\local\packages\pythonsoftwarefoundation.python.3.13_qbz5n2kfra8p0\localcache\local-packages\python313\site-packages (from httpx<1,>=0.23.0->openai) (2025.11.12)
|
||||
Collecting httpcore==1.* (from httpx<1,>=0.23.0->openai)
|
||||
Downloading httpcore-1.0.9-py3-none-any.whl.metadata (21 kB)
|
||||
Collecting h11>=0.16 (from httpcore==1.*->httpx<1,>=0.23.0->openai)
|
||||
Downloading h11-0.16.0-py3-none-any.whl.metadata (8.3 kB)
|
||||
Collecting annotated-types>=0.6.0 (from pydantic<3,>=1.9.0->openai)
|
||||
Downloading annotated_types-0.7.0-py3-none-any.whl.metadata (15 kB)
|
||||
Collecting pydantic-core==2.41.5 (from pydantic<3,>=1.9.0->openai)
|
||||
Downloading pydantic_core-2.41.5-cp313-cp313-win_amd64.whl.metadata (7.4 kB)
|
||||
Collecting typing-inspection>=0.4.2 (from pydantic<3,>=1.9.0->openai)
|
||||
Downloading typing_inspection-0.4.2-py3-none-any.whl.metadata (2.6 kB)
|
||||
Requirement already satisfied: colorama in c:\users\linha\appdata\local\packages\pythonsoftwarefoundation.python.3.13_qbz5n2kfra8p0\localcache\local-packages\python313\site-packages (from tqdm>4->openai) (0.4.6)
|
||||
Downloading openai-2.14.0-py3-none-any.whl (1.1 MB)
|
||||
---------------------------------------- 1.1/1.1 MB 2.6 MB/s 0:00:00
|
||||
Downloading anyio-4.12.0-py3-none-any.whl (113 kB)
|
||||
Downloading distro-1.9.0-py3-none-any.whl (20 kB)
|
||||
Downloading httpx-0.28.1-py3-none-any.whl (73 kB)
|
||||
Downloading httpcore-1.0.9-py3-none-any.whl (78 kB)
|
||||
Downloading jiter-0.12.0-cp313-cp313-win_amd64.whl (204 kB)
|
||||
Downloading pydantic-2.12.5-py3-none-any.whl (463 kB)
|
||||
Downloading pydantic_core-2.41.5-cp313-cp313-win_amd64.whl (2.0 MB)
|
||||
---------------------------------------- 2.0/2.0 MB 6.3 MB/s 0:00:00
|
||||
Downloading annotated_types-0.7.0-py3-none-any.whl (13 kB)
|
||||
Downloading h11-0.16.0-py3-none-any.whl (37 kB)
|
||||
Downloading tqdm-4.67.1-py3-none-any.whl (78 kB)
|
||||
Downloading typing_inspection-0.4.2-py3-none-any.whl (14 kB)
|
||||
Downloading sniffio-1.3.1-py3-none-any.whl (10 kB)
|
||||
Installing collected packages: typing-inspection, tqdm, sniffio, pydantic-core, jiter, h11, distro, anyio, annotated-types, pydantic, httpcore, httpx, openai
|
||||
|
||||
Successfully installed annotated-types-0.7.0 anyio-4.12.0 distro-1.9.0 h11-0.16.0 httpcore-1.0.9 httpx-0.28.1 jiter-0.12.0 openai-2.14.0 pydantic-2.12.5 pydantic-core-2.41.5 sniffio-1.3.1 tqdm-4.67.1 typing-inspection-0.4.2
|
||||
506
app.py
@@ -1,12 +1,13 @@
|
||||
import os
|
||||
from flask import Flask, render_template, redirect, url_for, request, flash, jsonify
|
||||
from flask_login import LoginManager, login_user, logout_user, login_required, current_user
|
||||
from flask_admin import Admin, AdminIndexView
|
||||
from flask_admin import Admin, AdminIndexView, expose
|
||||
from flask_admin.contrib.sqla import ModelView
|
||||
from datetime import datetime
|
||||
from config import config
|
||||
from models import db, Site, Tag, Admin as AdminModel
|
||||
from models import db, Site, Tag, Admin as AdminModel, News
|
||||
from utils.website_fetcher import WebsiteFetcher
|
||||
from utils.tag_generator import TagGenerator
|
||||
|
||||
def create_app(config_name='default'):
|
||||
"""应用工厂函数"""
|
||||
@@ -35,37 +36,77 @@ def create_app(config_name='default'):
|
||||
# 获取所有启用的标签
|
||||
tags = Tag.query.order_by(Tag.sort_order.desc(), Tag.id).all()
|
||||
|
||||
# 获取筛选的标签
|
||||
# 获取筛选参数
|
||||
tag_slug = request.args.get('tag')
|
||||
search_query = request.args.get('q', '').strip()
|
||||
page = request.args.get('page', 1, type=int)
|
||||
per_page = 100 # 每页显示100个站点
|
||||
|
||||
selected_tag = None
|
||||
|
||||
# 构建基础查询
|
||||
query = Site.query.filter_by(is_active=True)
|
||||
|
||||
# 标签筛选
|
||||
if tag_slug:
|
||||
selected_tag = Tag.query.filter_by(slug=tag_slug).first()
|
||||
if selected_tag:
|
||||
sites = Site.query.filter(
|
||||
Site.is_active == True,
|
||||
Site.tags.contains(selected_tag)
|
||||
).order_by(Site.sort_order.desc(), Site.id.desc()).all()
|
||||
query = query.filter(Site.tags.contains(selected_tag))
|
||||
else:
|
||||
sites = []
|
||||
else:
|
||||
# 获取所有启用的网站
|
||||
sites = Site.query.filter_by(is_active=True).order_by(
|
||||
Site.sort_order.desc(), Site.id.desc()
|
||||
).all()
|
||||
pagination = None
|
||||
return render_template('index_new.html', sites=sites, tags=tags,
|
||||
selected_tag=selected_tag, search_query=search_query,
|
||||
pagination=pagination)
|
||||
|
||||
return render_template('index.html', sites=sites, tags=tags, selected_tag=selected_tag)
|
||||
# 搜索功能
|
||||
if search_query:
|
||||
# 使用OR条件搜索:网站名称、URL、描述
|
||||
search_pattern = f'%{search_query}%'
|
||||
query = query.filter(
|
||||
db.or_(
|
||||
Site.name.like(search_pattern),
|
||||
Site.url.like(search_pattern),
|
||||
Site.description.like(search_pattern),
|
||||
Site.short_desc.like(search_pattern)
|
||||
)
|
||||
)
|
||||
|
||||
@app.route('/site/<slug>')
|
||||
def site_detail(slug):
|
||||
# 排序并分页
|
||||
query = query.order_by(Site.sort_order.desc(), Site.id.desc())
|
||||
pagination = query.paginate(page=page, per_page=per_page, error_out=False)
|
||||
sites = pagination.items
|
||||
|
||||
return render_template('index_new.html', sites=sites, tags=tags,
|
||||
selected_tag=selected_tag, search_query=search_query,
|
||||
pagination=pagination)
|
||||
|
||||
@app.route('/site/<code>')
|
||||
def site_detail(code):
|
||||
"""网站详情页"""
|
||||
site = Site.query.filter_by(slug=slug, is_active=True).first_or_404()
|
||||
site = Site.query.filter_by(code=code, is_active=True).first_or_404()
|
||||
|
||||
# 增加浏览次数
|
||||
site.view_count += 1
|
||||
db.session.commit()
|
||||
|
||||
return render_template('detail.html', site=site)
|
||||
# 获取该网站的相关新闻(最多显示5条)
|
||||
news_list = News.query.filter_by(
|
||||
site_id=site.id,
|
||||
is_active=True
|
||||
).order_by(News.published_at.desc()).limit(5).all()
|
||||
|
||||
# 获取同类工具推荐(通过标签匹配,最多显示4个)
|
||||
recommended_sites = []
|
||||
if site.tags:
|
||||
# 获取有相同标签的其他网站
|
||||
recommended_sites = Site.query.filter(
|
||||
Site.id != site.id,
|
||||
Site.is_active == True,
|
||||
Site.tags.any(Tag.id.in_([tag.id for tag in site.tags]))
|
||||
).order_by(Site.view_count.desc()).limit(4).all()
|
||||
|
||||
return render_template('detail_new.html', site=site, news_list=news_list, recommended_sites=recommended_sites)
|
||||
|
||||
# ========== 后台登录路由 ==========
|
||||
@app.route('/admin/login', methods=['GET', 'POST'])
|
||||
@@ -144,9 +185,289 @@ def create_app(config_name='default'):
|
||||
'message': f'抓取失败: {str(e)}'
|
||||
}), 500
|
||||
|
||||
@app.route('/api/generate-tags', methods=['POST'])
|
||||
@login_required
|
||||
def generate_tags():
|
||||
"""使用DeepSeek自动生成标签"""
|
||||
try:
|
||||
data = request.get_json()
|
||||
name = data.get('name', '').strip()
|
||||
description = data.get('description', '').strip()
|
||||
|
||||
if not name or not description:
|
||||
return jsonify({
|
||||
'success': False,
|
||||
'message': '请提供网站名称和描述'
|
||||
}), 400
|
||||
|
||||
# 获取现有标签作为参考
|
||||
existing_tags = [tag.name for tag in Tag.query.all()]
|
||||
|
||||
# 生成标签
|
||||
generator = TagGenerator()
|
||||
suggested_tags = generator.generate_tags(name, description, existing_tags)
|
||||
|
||||
if not suggested_tags:
|
||||
return jsonify({
|
||||
'success': False,
|
||||
'message': 'DeepSeek标签生成失败,请检查API配置'
|
||||
}), 500
|
||||
|
||||
return jsonify({
|
||||
'success': True,
|
||||
'tags': suggested_tags
|
||||
})
|
||||
|
||||
except ValueError as e:
|
||||
return jsonify({
|
||||
'success': False,
|
||||
'message': str(e)
|
||||
}), 400
|
||||
except Exception as e:
|
||||
return jsonify({
|
||||
'success': False,
|
||||
'message': f'生成失败: {str(e)}'
|
||||
}), 500
|
||||
|
||||
# ========== 批量导入路由 ==========
|
||||
@app.route('/admin/batch-import', methods=['GET', 'POST'])
|
||||
@login_required
|
||||
def batch_import():
|
||||
"""批量导入网站"""
|
||||
from utils.bookmark_parser import BookmarkParser
|
||||
from utils.website_fetcher import WebsiteFetcher
|
||||
|
||||
results = None
|
||||
|
||||
if request.method == 'POST':
|
||||
import_type = request.form.get('import_type')
|
||||
auto_activate = request.form.get('auto_activate') == 'on'
|
||||
|
||||
parser = BookmarkParser()
|
||||
fetcher = WebsiteFetcher(timeout=15)
|
||||
|
||||
urls_to_import = []
|
||||
|
||||
try:
|
||||
# 解析输入
|
||||
if import_type == 'url_list':
|
||||
url_list_text = request.form.get('url_list', '')
|
||||
urls_to_import = parser.parse_url_list(url_list_text)
|
||||
|
||||
elif import_type == 'bookmark_file':
|
||||
bookmark_file = request.files.get('bookmark_file')
|
||||
if not bookmark_file:
|
||||
flash('请选择书签文件', 'error')
|
||||
return render_template('admin/batch_import.html')
|
||||
|
||||
html_content = bookmark_file.read().decode('utf-8', errors='ignore')
|
||||
all_bookmarks = parser.parse_html_file(html_content)
|
||||
|
||||
# 筛选文件夹(如果指定)
|
||||
folder_filter = request.form.get('folder_filter', '').strip()
|
||||
if folder_filter:
|
||||
urls_to_import = [
|
||||
b for b in all_bookmarks
|
||||
if folder_filter.lower() in b.get('folder', '').lower()
|
||||
]
|
||||
else:
|
||||
urls_to_import = all_bookmarks
|
||||
|
||||
# 批量导入
|
||||
success_list = []
|
||||
failed_list = []
|
||||
|
||||
for idx, item in enumerate(urls_to_import, 1):
|
||||
url = item['url']
|
||||
name = item.get('name', '')
|
||||
|
||||
# 为每个URL创建独立的事务
|
||||
try:
|
||||
# 1. 检查URL是否已存在
|
||||
try:
|
||||
existing = Site.query.filter_by(url=url).first()
|
||||
if existing:
|
||||
failed_list.append({
|
||||
'url': url,
|
||||
'name': name or existing.name,
|
||||
'error': f'该URL已存在(网站名称:{existing.name})'
|
||||
})
|
||||
continue
|
||||
except Exception as e:
|
||||
failed_list.append({
|
||||
'url': url,
|
||||
'name': name,
|
||||
'error': f'检查URL时出错: {str(e)}'
|
||||
})
|
||||
continue
|
||||
|
||||
# 2. 抓取网站信息(带超时和错误处理)
|
||||
info = None
|
||||
try:
|
||||
info = fetcher.fetch_website_info(url)
|
||||
except Exception as e:
|
||||
print(f"抓取 {url} 失败: {str(e)}")
|
||||
# 抓取失败不是致命错误,继续尝试使用书签名称
|
||||
|
||||
# 3. 处理网站信息
|
||||
if not info or not info.get('name'):
|
||||
# 如果有书签名称,使用书签名称
|
||||
if name:
|
||||
info = {
|
||||
'name': name,
|
||||
'description': '',
|
||||
'logo_url': ''
|
||||
}
|
||||
else:
|
||||
# 尝试从URL提取域名作为名称
|
||||
from urllib.parse import urlparse
|
||||
try:
|
||||
parsed = urlparse(url)
|
||||
domain = parsed.netloc or parsed.path
|
||||
if domain:
|
||||
info = {
|
||||
'name': domain,
|
||||
'description': '',
|
||||
'logo_url': ''
|
||||
}
|
||||
else:
|
||||
failed_list.append({
|
||||
'url': url,
|
||||
'name': name,
|
||||
'error': '无法获取网站信息且没有备用名称'
|
||||
})
|
||||
continue
|
||||
except Exception:
|
||||
failed_list.append({
|
||||
'url': url,
|
||||
'name': name,
|
||||
'error': '无法获取网站信息且URL解析失败'
|
||||
})
|
||||
continue
|
||||
|
||||
# 4. 下载Logo(失败不影响导入)
|
||||
logo_path = None
|
||||
if info.get('logo_url'):
|
||||
try:
|
||||
logo_path = fetcher.download_logo(info['logo_url'])
|
||||
except Exception as e:
|
||||
print(f"下载Logo失败 ({url}): {str(e)}")
|
||||
# Logo下载失败不影响网站导入
|
||||
|
||||
# 5. 生成code和slug
|
||||
try:
|
||||
import random
|
||||
from pypinyin import lazy_pinyin
|
||||
import re
|
||||
|
||||
# 生成唯一的code
|
||||
site_code = None
|
||||
max_attempts = 10
|
||||
for _ in range(max_attempts):
|
||||
code = str(random.randint(10000000, 99999999))
|
||||
if not Site.query.filter_by(code=code).first():
|
||||
site_code = code
|
||||
break
|
||||
|
||||
if not site_code:
|
||||
# 如果10次都失败,使用时间戳
|
||||
import time
|
||||
site_code = str(int(time.time() * 1000))[-8:]
|
||||
|
||||
# 生成slug
|
||||
site_name = info.get('name', name or 'Unknown')[:100]
|
||||
slug = ''.join(lazy_pinyin(site_name))
|
||||
slug = slug.lower()
|
||||
slug = re.sub(r'[^\w\s-]', '', slug)
|
||||
slug = re.sub(r'[-\s]+', '-', slug).strip('-')
|
||||
if not slug:
|
||||
slug = f"site-{site_code}"
|
||||
|
||||
# 确保slug唯一
|
||||
base_slug = slug[:50] # 限制长度
|
||||
counter = 1
|
||||
final_slug = slug
|
||||
while Site.query.filter_by(slug=final_slug).first():
|
||||
final_slug = f"{base_slug}-{counter}"
|
||||
counter += 1
|
||||
if counter > 100: # 防止无限循环
|
||||
final_slug = f"{base_slug}-{site_code}"
|
||||
break
|
||||
|
||||
# 6. 创建网站记录(带code和slug)
|
||||
site = Site(
|
||||
code=site_code,
|
||||
slug=final_slug,
|
||||
name=site_name,
|
||||
url=url[:500], # 限制URL长度
|
||||
logo=logo_path or info.get('logo_url', '')[:500] if info.get('logo_url') else '',
|
||||
short_desc=info.get('description', '')[:200] if info.get('description') else '',
|
||||
description=info.get('description', '')[:2000] if info.get('description') else '',
|
||||
is_active=auto_activate
|
||||
)
|
||||
|
||||
# 添加到数据库并提交
|
||||
db.session.add(site)
|
||||
db.session.commit()
|
||||
|
||||
success_list.append({
|
||||
'name': site.name,
|
||||
'url': site.url
|
||||
})
|
||||
|
||||
print(f"成功导入 [{idx}/{len(urls_to_import)}]: {site.name}")
|
||||
|
||||
except Exception as e:
|
||||
db.session.rollback()
|
||||
failed_list.append({
|
||||
'url': url,
|
||||
'name': name or info.get('name', 'Unknown'),
|
||||
'error': f'数据库保存失败: {str(e)}'
|
||||
})
|
||||
continue
|
||||
|
||||
except Exception as e:
|
||||
# 捕获所有未预期的错误
|
||||
db.session.rollback()
|
||||
failed_list.append({
|
||||
'url': url,
|
||||
'name': name,
|
||||
'error': f'未知错误: {str(e)}'
|
||||
})
|
||||
print(f"导入 {url} 时发生未知错误: {str(e)}")
|
||||
continue
|
||||
|
||||
results = {
|
||||
'total_count': len(urls_to_import),
|
||||
'success_count': len(success_list),
|
||||
'failed_count': len(failed_list),
|
||||
'success_list': success_list,
|
||||
'failed_list': failed_list
|
||||
}
|
||||
|
||||
if success_list:
|
||||
flash(f'成功导入 {len(success_list)} 个网站!', 'success')
|
||||
|
||||
except Exception as e:
|
||||
flash(f'导入失败: {str(e)}', 'error')
|
||||
|
||||
return render_template('admin/batch_import.html', results=results)
|
||||
|
||||
# ========== Flask-Admin 配置 ==========
|
||||
class SecureModelView(ModelView):
|
||||
"""需要登录的模型视图"""
|
||||
# 中文化配置
|
||||
can_set_page_size = True
|
||||
page_size = 20
|
||||
|
||||
# 自定义文本
|
||||
list_template = 'admin/model/list.html'
|
||||
create_template = 'admin/model/create.html'
|
||||
edit_template = 'admin/model/edit.html'
|
||||
|
||||
# 覆盖英文文本
|
||||
named_filter_urls = True
|
||||
|
||||
def is_accessible(self):
|
||||
return current_user.is_authenticated
|
||||
|
||||
@@ -161,17 +482,44 @@ def create_app(config_name='default'):
|
||||
def inaccessible_callback(self, name, **kwargs):
|
||||
return redirect(url_for('admin_login'))
|
||||
|
||||
@expose('/')
|
||||
def index(self):
|
||||
"""控制台首页,显示统计信息"""
|
||||
# 统计数据
|
||||
stats = {
|
||||
'sites_count': Site.query.filter_by(is_active=True).count(),
|
||||
'tags_count': Tag.query.count(),
|
||||
'news_count': News.query.filter_by(is_active=True).count(),
|
||||
'total_views': db.session.query(db.func.sum(Site.view_count)).scalar() or 0
|
||||
}
|
||||
|
||||
# 最近添加的工具(最多5个)
|
||||
recent_sites = Site.query.order_by(Site.created_at.desc()).limit(5).all()
|
||||
|
||||
return self.render('admin/index.html', stats=stats, recent_sites=recent_sites)
|
||||
|
||||
# 网站管理视图
|
||||
class SiteAdmin(SecureModelView):
|
||||
# 自定义模板
|
||||
create_template = 'admin/site/create.html'
|
||||
edit_template = 'admin/site/edit.html'
|
||||
|
||||
column_list = ['id', 'name', 'url', 'slug', 'is_active', 'view_count', 'created_at']
|
||||
column_searchable_list = ['name', 'url', 'description']
|
||||
# 启用编辑和删除
|
||||
can_edit = True
|
||||
can_delete = True
|
||||
can_create = True
|
||||
can_view_details = False # 禁用查看详情,点击名称即可查看
|
||||
|
||||
# 显示操作列
|
||||
column_display_actions = True
|
||||
action_disallowed_list = []
|
||||
|
||||
column_list = ['id', 'code', 'name', 'url', 'slug', 'is_active', 'view_count', 'created_at']
|
||||
column_searchable_list = ['code', 'name', 'url', 'description']
|
||||
column_filters = ['is_active', 'tags']
|
||||
column_labels = {
|
||||
'id': 'ID',
|
||||
'code': '网站编码',
|
||||
'name': '网站名称',
|
||||
'url': 'URL',
|
||||
'slug': 'URL别名',
|
||||
@@ -188,8 +536,76 @@ def create_app(config_name='default'):
|
||||
}
|
||||
form_columns = ['name', 'url', 'slug', 'logo', 'short_desc', 'description', 'features', 'tags', 'is_active', 'sort_order']
|
||||
|
||||
# 自定义编辑/删除文字
|
||||
column_extra_row_actions = None
|
||||
|
||||
def on_model_change(self, form, model, is_created):
|
||||
"""保存前自动生成code和slug(如果为空)"""
|
||||
import re
|
||||
import random
|
||||
from pypinyin import lazy_pinyin
|
||||
|
||||
# 使用no_autoflush防止在查询时触发提前flush
|
||||
with db.session.no_autoflush:
|
||||
# 如果code为空,自动生成唯一的8位数字编码
|
||||
if not model.code or model.code.strip() == '':
|
||||
max_attempts = 10
|
||||
for attempt in range(max_attempts):
|
||||
# 生成10000000-99999999之间的随机数
|
||||
code = str(random.randint(10000000, 99999999))
|
||||
# 检查是否已存在
|
||||
existing = Site.query.filter(Site.code == code).first()
|
||||
if not existing or existing.id == model.id:
|
||||
model.code = code
|
||||
break
|
||||
|
||||
# 如果10次都失败,使用时间戳
|
||||
if not model.code:
|
||||
import time
|
||||
model.code = str(int(time.time() * 1000))[-8:]
|
||||
|
||||
# 如果slug为空,从name自动生成
|
||||
if not model.slug or model.slug.strip() == '':
|
||||
# 将中文转换为拼音
|
||||
slug = ''.join(lazy_pinyin(model.name))
|
||||
# 转换为小写,移除特殊字符
|
||||
slug = slug.lower()
|
||||
slug = re.sub(r'[^\w\s-]', '', slug)
|
||||
slug = re.sub(r'[-\s]+', '-', slug).strip('-')
|
||||
|
||||
# 如果转换后为空,使用code
|
||||
if not slug:
|
||||
slug = f"site-{model.code}"
|
||||
|
||||
# 确保slug唯一(限制长度和重试次数)
|
||||
base_slug = slug[:50]
|
||||
counter = 1
|
||||
final_slug = slug
|
||||
max_slug_attempts = 100
|
||||
|
||||
while counter < max_slug_attempts:
|
||||
existing = Site.query.filter(Site.slug == final_slug).first()
|
||||
if not existing or existing.id == model.id:
|
||||
break
|
||||
final_slug = f"{base_slug}-{counter}"
|
||||
counter += 1
|
||||
|
||||
# 如果100次都失败,使用code确保唯一
|
||||
if counter >= max_slug_attempts:
|
||||
final_slug = f"{base_slug}-{model.code}"
|
||||
|
||||
model.slug = final_slug
|
||||
|
||||
# 标签管理视图
|
||||
class TagAdmin(SecureModelView):
|
||||
can_edit = True
|
||||
can_delete = True
|
||||
can_create = True
|
||||
can_view_details = False
|
||||
|
||||
# 显示操作列
|
||||
column_display_actions = True
|
||||
|
||||
column_list = ['id', 'name', 'slug', 'description', 'sort_order']
|
||||
column_searchable_list = ['name', 'description']
|
||||
column_labels = {
|
||||
@@ -205,6 +621,14 @@ def create_app(config_name='default'):
|
||||
|
||||
# 管理员视图
|
||||
class AdminAdmin(SecureModelView):
|
||||
can_edit = True
|
||||
can_delete = True
|
||||
can_create = True
|
||||
can_view_details = False
|
||||
|
||||
# 显示操作列
|
||||
column_display_actions = True
|
||||
|
||||
column_list = ['id', 'username', 'email', 'is_active', 'last_login', 'created_at']
|
||||
column_searchable_list = ['username', 'email']
|
||||
column_filters = ['is_active']
|
||||
@@ -223,17 +647,55 @@ def create_app(config_name='default'):
|
||||
if is_created:
|
||||
model.set_password('admin123') # 默认密码
|
||||
|
||||
# 新闻管理视图
|
||||
class NewsAdmin(SecureModelView):
|
||||
can_edit = True
|
||||
can_delete = True
|
||||
can_create = True
|
||||
can_view_details = False
|
||||
|
||||
# 显示操作列
|
||||
column_display_actions = True
|
||||
|
||||
column_list = ['id', 'site', 'title', 'news_type', 'published_at', 'is_active']
|
||||
column_searchable_list = ['title', 'content']
|
||||
column_filters = ['site', 'news_type', 'is_active', 'published_at']
|
||||
column_labels = {
|
||||
'id': 'ID',
|
||||
'site': '关联网站',
|
||||
'title': '新闻标题',
|
||||
'content': '新闻内容',
|
||||
'news_type': '新闻类型',
|
||||
'url': '新闻链接',
|
||||
'published_at': '发布时间',
|
||||
'is_active': '是否启用',
|
||||
'created_at': '创建时间',
|
||||
'updated_at': '更新时间'
|
||||
}
|
||||
form_columns = ['site', 'title', 'content', 'news_type', 'url', 'published_at', 'is_active']
|
||||
|
||||
# 可选的新闻类型
|
||||
form_choices = {
|
||||
'news_type': [
|
||||
('Product Update', 'Product Update'),
|
||||
('Industry News', 'Industry News'),
|
||||
('Company News', 'Company News'),
|
||||
('Other', 'Other')
|
||||
]
|
||||
}
|
||||
|
||||
# 初始化 Flask-Admin
|
||||
admin = Admin(
|
||||
app,
|
||||
name='ZJPB 焦提示词 - 后台管理',
|
||||
name='ZJPB 焦提示词',
|
||||
template_mode='bootstrap4',
|
||||
index_view=SecureAdminIndexView(),
|
||||
base_template='admin/custom_base.html'
|
||||
index_view=SecureAdminIndexView(name='控制台', url='/admin'),
|
||||
base_template='admin/master.html'
|
||||
)
|
||||
|
||||
admin.add_view(SiteAdmin(Site, db.session, name='网站管理'))
|
||||
admin.add_view(TagAdmin(Tag, db.session, name='标签管理'))
|
||||
admin.add_view(NewsAdmin(News, db.session, name='新闻管理'))
|
||||
admin.add_view(AdminAdmin(AdminModel, db.session, name='管理员', endpoint='admin_users'))
|
||||
|
||||
return app
|
||||
|
||||
14
config.py
@@ -20,6 +20,20 @@ class Config:
|
||||
SQLALCHEMY_TRACK_MODIFICATIONS = False
|
||||
SQLALCHEMY_ECHO = False
|
||||
|
||||
# 数据库连接池配置
|
||||
SQLALCHEMY_ENGINE_OPTIONS = {
|
||||
'pool_size': 10, # 连接池大小
|
||||
'pool_recycle': 3600, # 连接回收时间(秒)
|
||||
'pool_pre_ping': True, # 每次取连接前先ping,确保连接有效
|
||||
'pool_timeout': 30, # 连接池超时时间
|
||||
'max_overflow': 20, # 超过pool_size后最多创建的连接数
|
||||
'connect_args': {
|
||||
'connect_timeout': 10, # 连接超时(秒)
|
||||
'read_timeout': 30, # 读取超时(秒)
|
||||
'write_timeout': 30, # 写入超时(秒)
|
||||
}
|
||||
}
|
||||
|
||||
# 分页配置
|
||||
SITES_PER_PAGE = 20
|
||||
|
||||
|
||||
62
migrations/add_code_field.py
Normal file
@@ -0,0 +1,62 @@
|
||||
"""添加code字段的迁移脚本"""
|
||||
import sys
|
||||
import os
|
||||
import random
|
||||
|
||||
# 添加项目根目录到系统路径
|
||||
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||
|
||||
from app import create_app
|
||||
from models import db, Site
|
||||
from sqlalchemy import text
|
||||
|
||||
# 创建应用上下文
|
||||
app = create_app()
|
||||
|
||||
def generate_unique_code():
|
||||
"""生成唯一的8位数字编码"""
|
||||
while True:
|
||||
# 生成10000000-99999999之间的随机数
|
||||
code = str(random.randint(10000000, 99999999))
|
||||
# 检查是否已存在
|
||||
if not Site.query.filter_by(code=code).first():
|
||||
return code
|
||||
|
||||
with app.app_context():
|
||||
try:
|
||||
print("Step 1: Adding code column to sites table...")
|
||||
|
||||
# 添加code列(先允许为空)
|
||||
with db.engine.connect() as connection:
|
||||
connection.execute(text('ALTER TABLE sites ADD COLUMN code VARCHAR(8) NULL COMMENT "8位数字编码"'))
|
||||
connection.commit()
|
||||
|
||||
print(" - Column added successfully!")
|
||||
|
||||
print("\nStep 2: Generating codes for existing sites...")
|
||||
|
||||
# 为现有网站生成code
|
||||
sites = Site.query.all()
|
||||
for site in sites:
|
||||
site.code = generate_unique_code()
|
||||
print(f" - Site #{site.id} '{site.name}': code = {site.code}")
|
||||
|
||||
db.session.commit()
|
||||
print(f" - Generated codes for {len(sites)} sites!")
|
||||
|
||||
print("\nStep 3: Making code column NOT NULL and UNIQUE...")
|
||||
|
||||
# 现在修改列为NOT NULL和UNIQUE
|
||||
with db.engine.connect() as connection:
|
||||
connection.execute(text('ALTER TABLE sites MODIFY COLUMN code VARCHAR(8) NOT NULL'))
|
||||
connection.execute(text('ALTER TABLE sites ADD UNIQUE INDEX idx_site_code (code)'))
|
||||
connection.commit()
|
||||
|
||||
print(" - Code column constraints added!")
|
||||
print("\n✓ Migration completed successfully!")
|
||||
|
||||
except Exception as e:
|
||||
print(f"\n✗ Migration failed: {str(e)}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
db.session.rollback()
|
||||
17
migrations/add_news_table.py
Normal file
@@ -0,0 +1,17 @@
|
||||
"""添加news表的迁移脚本"""
|
||||
import sys
|
||||
import os
|
||||
|
||||
# 添加项目根目录到系统路径
|
||||
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||
|
||||
from app import create_app
|
||||
from models import db, News
|
||||
|
||||
# 创建应用上下文
|
||||
app = create_app()
|
||||
|
||||
with app.app_context():
|
||||
# 创建news表
|
||||
db.create_all()
|
||||
print("✓ News表创建成功!")
|
||||
73
migrations/fix_empty_slugs.py
Normal file
@@ -0,0 +1,73 @@
|
||||
"""修复空slug的迁移脚本"""
|
||||
import sys
|
||||
import os
|
||||
import re
|
||||
|
||||
# 添加项目根目录到系统路径
|
||||
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||
|
||||
from app import create_app
|
||||
from models import db, Site
|
||||
from datetime import datetime
|
||||
from pypinyin import lazy_pinyin
|
||||
|
||||
# 创建应用上下文
|
||||
app = create_app()
|
||||
|
||||
def generate_slug(name, site_id=None):
|
||||
"""从名称生成slug"""
|
||||
# 将中文转换为拼音
|
||||
slug = ''.join(lazy_pinyin(name))
|
||||
# 转换为小写,移除特殊字符
|
||||
slug = slug.lower()
|
||||
slug = re.sub(r'[^\w\s-]', '', slug)
|
||||
slug = re.sub(r'[-\s]+', '-', slug).strip('-')
|
||||
|
||||
# 如果转换后为空,使用ID或时间戳
|
||||
if not slug:
|
||||
if site_id:
|
||||
slug = f"site-{site_id}"
|
||||
else:
|
||||
slug = f"site-{datetime.now().strftime('%Y%m%d%H%M%S')}"
|
||||
|
||||
# 确保slug唯一
|
||||
base_slug = slug
|
||||
counter = 1
|
||||
while Site.query.filter(Site.slug == slug, Site.id != site_id).first():
|
||||
slug = f"{base_slug}-{counter}"
|
||||
counter += 1
|
||||
|
||||
return slug
|
||||
|
||||
with app.app_context():
|
||||
try:
|
||||
# 查找所有slug为空或包含乱码的网站
|
||||
all_sites = Site.query.all()
|
||||
sites_to_fix = []
|
||||
|
||||
for site in all_sites:
|
||||
# 检查slug是否为空、None、或包含乱码
|
||||
if (not site.slug or
|
||||
site.slug == 'None' or
|
||||
site.slug.strip() == '' or
|
||||
any(ord(char) > 127 and not char.isalpha() for char in site.slug)):
|
||||
sites_to_fix.append(site)
|
||||
|
||||
if not sites_to_fix:
|
||||
print("No sites to fix. All good!")
|
||||
else:
|
||||
print(f"Found {len(sites_to_fix)} sites to fix...")
|
||||
|
||||
for site in sites_to_fix:
|
||||
old_slug = site.slug
|
||||
site.slug = generate_slug(site.name, site.id)
|
||||
print(f" - Site #{site.id} '{site.name}': '{old_slug}' -> '{site.slug}'")
|
||||
|
||||
db.session.commit()
|
||||
print(f"Successfully fixed {len(sites_to_fix)} sites!")
|
||||
|
||||
except Exception as e:
|
||||
print(f"Migration failed: {str(e)}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
db.session.rollback()
|
||||
25
migrations/make_slug_nullable.py
Normal file
@@ -0,0 +1,25 @@
|
||||
"""修改slug字段为可空的迁移脚本"""
|
||||
import sys
|
||||
import os
|
||||
|
||||
# 添加项目根目录到系统路径
|
||||
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||
|
||||
from app import create_app
|
||||
from models import db
|
||||
from sqlalchemy import text
|
||||
|
||||
# 创建应用上下文
|
||||
app = create_app()
|
||||
|
||||
with app.app_context():
|
||||
# 使用原生SQL修改列属性
|
||||
try:
|
||||
# MySQL语法:修改slug列为可空
|
||||
with db.engine.connect() as connection:
|
||||
connection.execute(text('ALTER TABLE sites MODIFY COLUMN slug VARCHAR(100) NULL'))
|
||||
connection.commit()
|
||||
print("slug field successfully updated to nullable!")
|
||||
except Exception as e:
|
||||
print(f"Migration failed: {str(e)}")
|
||||
print("Note: If the field is already nullable, you can ignore this error.")
|
||||
38
models.py
@@ -16,9 +16,10 @@ class Site(db.Model):
|
||||
__tablename__ = 'sites'
|
||||
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
code = db.Column(db.String(8), unique=True, nullable=False, comment='8位数字编码')
|
||||
name = db.Column(db.String(100), nullable=False, comment='网站名称')
|
||||
url = db.Column(db.String(500), nullable=False, comment='网站URL')
|
||||
slug = db.Column(db.String(100), unique=True, nullable=False, comment='URL别名')
|
||||
slug = db.Column(db.String(100), unique=True, nullable=True, comment='URL别名(SEO用)')
|
||||
logo = db.Column(db.String(500), comment='Logo图片路径')
|
||||
short_desc = db.Column(db.String(200), comment='简短描述')
|
||||
description = db.Column(db.Text, comment='详细介绍')
|
||||
@@ -40,6 +41,7 @@ class Site(db.Model):
|
||||
"""转换为字典"""
|
||||
return {
|
||||
'id': self.id,
|
||||
'code': self.code,
|
||||
'name': self.name,
|
||||
'url': self.url,
|
||||
'slug': self.slug,
|
||||
@@ -78,6 +80,40 @@ class Tag(db.Model):
|
||||
'icon': self.icon
|
||||
}
|
||||
|
||||
class News(db.Model):
|
||||
"""新闻模型"""
|
||||
__tablename__ = 'news'
|
||||
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
site_id = db.Column(db.Integer, db.ForeignKey('sites.id'), nullable=False, comment='关联网站ID')
|
||||
title = db.Column(db.String(200), nullable=False, comment='新闻标题')
|
||||
content = db.Column(db.Text, comment='新闻内容')
|
||||
news_type = db.Column(db.String(50), default='Industry News', comment='新闻类型')
|
||||
url = db.Column(db.String(500), comment='新闻链接')
|
||||
published_at = db.Column(db.DateTime, default=datetime.now, comment='发布时间')
|
||||
is_active = db.Column(db.Boolean, default=True, comment='是否启用')
|
||||
created_at = db.Column(db.DateTime, default=datetime.now, comment='创建时间')
|
||||
updated_at = db.Column(db.DateTime, default=datetime.now, onupdate=datetime.now, comment='更新时间')
|
||||
|
||||
# 关联网站
|
||||
site = db.relationship('Site', backref=db.backref('news', lazy='dynamic', order_by='News.published_at.desc()'))
|
||||
|
||||
def __repr__(self):
|
||||
return f'<News {self.title}>'
|
||||
|
||||
def to_dict(self):
|
||||
"""转换为字典"""
|
||||
return {
|
||||
'id': self.id,
|
||||
'site_id': self.site_id,
|
||||
'title': self.title,
|
||||
'content': self.content,
|
||||
'news_type': self.news_type,
|
||||
'url': self.url,
|
||||
'published_at': self.published_at.strftime('%Y-%m-%d') if self.published_at else None,
|
||||
'created_at': self.created_at.strftime('%Y-%m-%d %H:%M:%S') if self.created_at else None
|
||||
}
|
||||
|
||||
class Admin(UserMixin, db.Model):
|
||||
"""管理员模型"""
|
||||
__tablename__ = 'admins'
|
||||
|
||||
@@ -10,3 +10,4 @@ WTForms==2.3.3
|
||||
requests==2.31.0
|
||||
beautifulsoup4==4.12.2
|
||||
Pillow>=10.2.0
|
||||
openai>=1.0.0
|
||||
|
||||
97
static/css/admin-actions.css
Normal file
@@ -0,0 +1,97 @@
|
||||
/* 管理后台操作按钮样式 - 添加中文文字 */
|
||||
|
||||
/* 表格操作列 */
|
||||
td.list-buttons-column,
|
||||
th.list-buttons-column {
|
||||
white-space: nowrap;
|
||||
min-width: 140px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
/* 隐藏所有图标 */
|
||||
td.list-buttons-column a.icon .glyphicon,
|
||||
td.list-buttons-column a.icon .fa,
|
||||
td.list-buttons-column a.icon span.glyphicon,
|
||||
td.list-buttons-column a.icon i,
|
||||
td.list-buttons-column form .glyphicon,
|
||||
td.list-buttons-column form .fa,
|
||||
td.list-buttons-column form span.glyphicon,
|
||||
td.list-buttons-column form i {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
/* 通用操作按钮样式(链接) */
|
||||
td.list-buttons-column a.icon {
|
||||
display: inline-flex !important;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-width: 60px;
|
||||
height: 32px;
|
||||
padding: 0 12px;
|
||||
margin: 0 4px;
|
||||
border-radius: 4px;
|
||||
font-size: 13px;
|
||||
font-weight: 500;
|
||||
text-decoration: none;
|
||||
transition: all 0.2s;
|
||||
font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
|
||||
}
|
||||
|
||||
/* 删除表单样式 */
|
||||
td.list-buttons-column form {
|
||||
display: inline-block;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
/* 删除表单的提交按钮 */
|
||||
td.list-buttons-column form button,
|
||||
td.list-buttons-column form input[type="submit"] {
|
||||
display: inline-flex !important;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-width: 60px;
|
||||
height: 32px;
|
||||
padding: 0 12px;
|
||||
margin: 0 4px;
|
||||
border-radius: 4px;
|
||||
font-size: 13px;
|
||||
font-weight: 500;
|
||||
text-decoration: none;
|
||||
transition: all 0.2s;
|
||||
font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
|
||||
background: #E34D59 !important;
|
||||
color: white !important;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
td.list-buttons-column form button:hover,
|
||||
td.list-buttons-column form input[type="submit"]:hover {
|
||||
background: #C9353F !important;
|
||||
}
|
||||
|
||||
/* 使用::before添加删除文字 */
|
||||
td.list-buttons-column form button::before,
|
||||
td.list-buttons-column form input[type="submit"]::before {
|
||||
content: "删除";
|
||||
}
|
||||
|
||||
/* 编辑按钮 - 通过href匹配 */
|
||||
td.list-buttons-column a[href*="/edit/"] {
|
||||
background: #0052D9 !important;
|
||||
color: white !important;
|
||||
}
|
||||
|
||||
td.list-buttons-column a[href*="/edit/"]:hover {
|
||||
background: #0041A8 !important;
|
||||
}
|
||||
|
||||
td.list-buttons-column a[href*="/edit/"]::after {
|
||||
content: "编辑";
|
||||
}
|
||||
|
||||
/* 查看按钮 - 完全隐藏 */
|
||||
td.list-buttons-column a[href*="/details/"] {
|
||||
display: none !important;
|
||||
}
|
||||
83
static/css/admin-i18n.css
Normal file
@@ -0,0 +1,83 @@
|
||||
/* Flask-Admin 界面中文化样式 */
|
||||
|
||||
/* ========== 顶部导航栏中文化 ========== */
|
||||
|
||||
/* List (4) 标签页 - 只针对第一个li */
|
||||
.nav-tabs li:first-child a {
|
||||
font-size: 0 !important;
|
||||
}
|
||||
|
||||
.nav-tabs li:first-child a::after {
|
||||
font-size: 14px;
|
||||
content: "列表";
|
||||
}
|
||||
|
||||
/* 保留数字显示 */
|
||||
.nav-tabs li a .badge {
|
||||
font-size: 12px !important;
|
||||
}
|
||||
|
||||
/* Create 按钮 - 通过href精确匹配 */
|
||||
.nav.nav-tabs ~ .btn-group a.btn-primary,
|
||||
a.btn.btn-primary[href$="/new/"] {
|
||||
font-size: 0 !important;
|
||||
}
|
||||
|
||||
.nav.nav-tabs ~ .btn-group a.btn-primary::before,
|
||||
a.btn.btn-primary[href$="/new/"]::before {
|
||||
font-size: 14px;
|
||||
content: "创建";
|
||||
font-family: inherit;
|
||||
}
|
||||
|
||||
.nav.nav-tabs ~ .btn-group a.btn-primary .fa,
|
||||
a.btn.btn-primary[href$="/new/"] .fa {
|
||||
font-size: 14px !important;
|
||||
margin-right: 4px;
|
||||
}
|
||||
|
||||
/* Add Filter 下拉按钮 */
|
||||
.btn-group .dropdown-toggle {
|
||||
font-size: 0 !important;
|
||||
}
|
||||
|
||||
.btn-group .dropdown-toggle::before {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
/* 根据位置区分不同的dropdown */
|
||||
/* Add Filter - 第一个dropdown */
|
||||
.btn-group:nth-of-type(1) .dropdown-toggle::before {
|
||||
content: "添加筛选";
|
||||
}
|
||||
|
||||
/* With selected - 第二个dropdown (通常在表格上方) */
|
||||
form[id^="action_confirmation"] ~ .btn-group .dropdown-toggle::before,
|
||||
.actions-nav .dropdown-toggle::before {
|
||||
content: "批量操作";
|
||||
}
|
||||
|
||||
/* 保留下拉箭头 */
|
||||
.btn-group .dropdown-toggle .caret {
|
||||
font-size: 0;
|
||||
margin-left: 4px;
|
||||
display: inline-block;
|
||||
width: 0;
|
||||
height: 0;
|
||||
border-left: 4px solid transparent;
|
||||
border-right: 4px solid transparent;
|
||||
border-top: 4px solid currentColor;
|
||||
}
|
||||
|
||||
/* Delete 批量删除选项 */
|
||||
.dropdown-menu li a {
|
||||
font-size: 14px !important;
|
||||
}
|
||||
|
||||
/* ========== 修正:避免影响其他链接 ========== */
|
||||
/* 重置可能被误伤的元素 */
|
||||
.admin-sidebar a,
|
||||
.nav-item a,
|
||||
table a {
|
||||
font-size: 14px !important;
|
||||
}
|
||||
636
static/css/admin-sidebar.css
Normal file
@@ -0,0 +1,636 @@
|
||||
/* ========== Flask-Admin 左侧菜单布局 - ZJPB焦提示词 ========== */
|
||||
|
||||
/* TDesign 色彩系统 */
|
||||
:root {
|
||||
--td-brand-color: #0052D9;
|
||||
--td-brand-color-hover: #266FE8;
|
||||
--td-brand-color-active: #0034B5;
|
||||
--td-brand-color-light: #ECF2FE;
|
||||
--td-success-color: #00A870;
|
||||
--td-warning-color: #E37318;
|
||||
--td-error-color: #D54941;
|
||||
--td-bg-color-page: #F5F7FA;
|
||||
--td-bg-color-container: #FFFFFF;
|
||||
--td-bg-color-container-hover: #F5F5F5;
|
||||
--td-text-color-primary: #000000;
|
||||
--td-text-color-secondary: #606266;
|
||||
--td-text-color-placeholder: #C0C4CC;
|
||||
--td-border-color: #DCDFE6;
|
||||
--td-border-color-light: #E4E7ED;
|
||||
--td-border-radius: 3px;
|
||||
--td-border-radius-medium: 6px;
|
||||
--td-shadow-1: 0 1px 4px rgba(0, 0, 0, .05);
|
||||
--td-shadow-2: 0 2px 12px rgba(0, 0, 0, .08);
|
||||
--sidebar-width: 240px;
|
||||
}
|
||||
|
||||
/* 全局样式重置 */
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body.admin-sidebar-layout {
|
||||
background: var(--td-bg-color-page);
|
||||
color: var(--td-text-color-primary);
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, 'Noto Sans', sans-serif;
|
||||
font-size: 14px;
|
||||
line-height: 1.5;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
/* ========== 左侧边栏 ========== */
|
||||
.admin-sidebar {
|
||||
position: fixed;
|
||||
left: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
width: var(--sidebar-width);
|
||||
background: var(--td-bg-color-container);
|
||||
border-right: 1px solid var(--td-border-color);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
/* Logo */
|
||||
.sidebar-logo {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
padding: 20px 24px;
|
||||
border-bottom: 1px solid var(--td-border-color);
|
||||
}
|
||||
|
||||
.sidebar-logo .logo-icon {
|
||||
font-size: 32px;
|
||||
color: var(--td-brand-color);
|
||||
}
|
||||
|
||||
.sidebar-logo .logo-text {
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
color: var(--td-text-color-primary);
|
||||
font-family: 'Space Grotesk', sans-serif;
|
||||
}
|
||||
|
||||
/* 导航区域 */
|
||||
.sidebar-nav {
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
padding: 16px 0;
|
||||
}
|
||||
|
||||
.nav-section {
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
.nav-section-title {
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
color: var(--td-text-color-secondary);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.5px;
|
||||
padding: 8px 24px;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.nav-menu {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.nav-item {
|
||||
margin: 2px 12px;
|
||||
}
|
||||
|
||||
.nav-link {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
padding: 10px 12px;
|
||||
color: var(--td-text-color-secondary);
|
||||
text-decoration: none;
|
||||
border-radius: var(--td-border-radius-medium);
|
||||
transition: all 0.2s cubic-bezier(0.38, 0, 0.24, 1);
|
||||
}
|
||||
|
||||
.nav-link:hover {
|
||||
background: var(--td-bg-color-container-hover);
|
||||
color: var(--td-text-color-primary);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.nav-item.active .nav-link {
|
||||
background: var(--td-brand-color-light);
|
||||
color: var(--td-brand-color);
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.nav-icon {
|
||||
font-size: 20px;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.nav-text {
|
||||
flex: 1;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
/* 用户信息 */
|
||||
.sidebar-user {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
padding: 16px 24px;
|
||||
border-top: 1px solid var(--td-border-color);
|
||||
background: var(--td-bg-color-page);
|
||||
}
|
||||
|
||||
.user-avatar {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
border-radius: 50%;
|
||||
background: var(--td-brand-color-light);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: var(--td-brand-color);
|
||||
}
|
||||
|
||||
.user-avatar .material-symbols-outlined {
|
||||
font-size: 24px;
|
||||
}
|
||||
|
||||
.user-info {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.user-name {
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
color: var(--td-text-color-primary);
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.user-email {
|
||||
font-size: 12px;
|
||||
color: var(--td-text-color-secondary);
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
/* ========== 主内容区域 ========== */
|
||||
.admin-main {
|
||||
margin-left: var(--sidebar-width);
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
/* 顶部导航 */
|
||||
.admin-header {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 100;
|
||||
background: var(--td-bg-color-container);
|
||||
border-bottom: 1px solid var(--td-border-color);
|
||||
padding: 16px 32px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
box-shadow: var(--td-shadow-1);
|
||||
}
|
||||
|
||||
.header-breadcrumb {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.breadcrumb-link {
|
||||
color: var(--td-text-color-secondary);
|
||||
text-decoration: none;
|
||||
transition: color 0.2s;
|
||||
}
|
||||
|
||||
.breadcrumb-link:hover {
|
||||
color: var(--td-brand-color);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.breadcrumb-separator {
|
||||
color: var(--td-text-color-placeholder);
|
||||
}
|
||||
|
||||
.breadcrumb-current {
|
||||
color: var(--td-text-color-primary);
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.header-actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.search-box {
|
||||
position: relative;
|
||||
width: 300px;
|
||||
}
|
||||
|
||||
.search-icon {
|
||||
position: absolute;
|
||||
left: 12px;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
color: var(--td-text-color-placeholder);
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
.search-input {
|
||||
width: 100%;
|
||||
padding: 8px 12px 8px 40px;
|
||||
border: 1px solid var(--td-border-color);
|
||||
border-radius: var(--td-border-radius);
|
||||
font-size: 14px;
|
||||
background: var(--td-bg-color-page);
|
||||
transition: all 0.2s;
|
||||
}
|
||||
|
||||
.search-input:focus {
|
||||
outline: none;
|
||||
border-color: var(--td-brand-color);
|
||||
box-shadow: 0 0 0 2px rgba(0, 82, 217, 0.1);
|
||||
}
|
||||
|
||||
.header-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: transparent;
|
||||
border: none;
|
||||
border-radius: var(--td-border-radius);
|
||||
color: var(--td-text-color-secondary);
|
||||
cursor: pointer;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
|
||||
.header-btn:hover {
|
||||
background: var(--td-bg-color-container-hover);
|
||||
color: var(--td-text-color-primary);
|
||||
}
|
||||
|
||||
.header-btn .material-symbols-outlined {
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
/* 页面内容 */
|
||||
.admin-content {
|
||||
flex: 1;
|
||||
padding: 32px;
|
||||
}
|
||||
|
||||
.page-header {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
.page-title {
|
||||
font-size: 28px;
|
||||
font-weight: 600;
|
||||
color: var(--td-text-color-primary);
|
||||
margin: 0 0 8px 0;
|
||||
font-family: 'Space Grotesk', sans-serif;
|
||||
}
|
||||
|
||||
.page-description {
|
||||
font-size: 14px;
|
||||
color: var(--td-text-color-secondary);
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* ========== 表格样式 ========== */
|
||||
.table {
|
||||
background: var(--td-bg-color-container);
|
||||
border-radius: var(--td-border-radius-medium);
|
||||
overflow: hidden;
|
||||
box-shadow: var(--td-shadow-1);
|
||||
border: 1px solid var(--td-border-color);
|
||||
}
|
||||
|
||||
.table thead th {
|
||||
background: var(--td-bg-color-page);
|
||||
border: none;
|
||||
color: var(--td-text-color-secondary);
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.5px;
|
||||
padding: 16px;
|
||||
border-bottom: 1px solid var(--td-border-color);
|
||||
}
|
||||
|
||||
.table tbody tr {
|
||||
border-bottom: 1px solid var(--td-border-color-light);
|
||||
transition: background-color 0.2s;
|
||||
}
|
||||
|
||||
.table tbody tr:hover {
|
||||
background: var(--td-bg-color-page);
|
||||
}
|
||||
|
||||
.table tbody tr:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.table td {
|
||||
padding: 16px;
|
||||
vertical-align: middle;
|
||||
color: var(--td-text-color-primary);
|
||||
border: none;
|
||||
}
|
||||
|
||||
/* ========== 表单样式 ========== */
|
||||
.form-control {
|
||||
background: var(--td-bg-color-container);
|
||||
border: 1px solid var(--td-border-color);
|
||||
border-radius: var(--td-border-radius);
|
||||
padding: 8px 12px;
|
||||
font-size: 14px;
|
||||
color: var(--td-text-color-primary);
|
||||
transition: all 0.2s cubic-bezier(0.38, 0, 0.24, 1);
|
||||
}
|
||||
|
||||
.form-control:focus {
|
||||
border-color: var(--td-brand-color);
|
||||
box-shadow: 0 0 0 2px rgba(0, 82, 217, 0.1);
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.form-control::placeholder {
|
||||
color: var(--td-text-color-placeholder);
|
||||
}
|
||||
|
||||
label, .form-label {
|
||||
color: var(--td-text-color-primary);
|
||||
font-weight: 500;
|
||||
font-size: 14px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
/* ========== 按钮样式 ========== */
|
||||
.btn {
|
||||
padding: 8px 16px;
|
||||
border-radius: var(--td-border-radius);
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s cubic-bezier(0.38, 0, 0.24, 1);
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
background: var(--td-brand-color);
|
||||
color: #FFFFFF;
|
||||
}
|
||||
|
||||
.btn-primary:hover {
|
||||
background: var(--td-brand-color-hover);
|
||||
transform: translateY(-1px);
|
||||
box-shadow: 0 4px 8px rgba(0, 82, 217, 0.2);
|
||||
}
|
||||
|
||||
.btn-success {
|
||||
background: var(--td-success-color);
|
||||
color: #FFFFFF;
|
||||
}
|
||||
|
||||
.btn-warning {
|
||||
background: var(--td-warning-color);
|
||||
color: #FFFFFF;
|
||||
}
|
||||
|
||||
.btn-danger {
|
||||
background: var(--td-error-color);
|
||||
color: #FFFFFF;
|
||||
}
|
||||
|
||||
.btn-secondary, .btn-default {
|
||||
background: var(--td-bg-color-container);
|
||||
border: 1px solid var(--td-border-color);
|
||||
color: var(--td-text-color-primary);
|
||||
}
|
||||
|
||||
.btn-secondary:hover, .btn-default:hover {
|
||||
background: var(--td-bg-color-container-hover);
|
||||
}
|
||||
|
||||
/* ========== 卡片样式 ========== */
|
||||
.card, .panel {
|
||||
background: var(--td-bg-color-container);
|
||||
border: 1px solid var(--td-border-color);
|
||||
border-radius: var(--td-border-radius-medium);
|
||||
box-shadow: var(--td-shadow-1);
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.card-header, .panel-heading {
|
||||
background: transparent;
|
||||
border-bottom: 1px solid var(--td-border-color);
|
||||
padding: 16px 20px;
|
||||
font-weight: 600;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.card-body, .panel-body {
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
/* ========== 警告框 ========== */
|
||||
.alert {
|
||||
border-radius: var(--td-border-radius-medium);
|
||||
border: none;
|
||||
padding: 12px 16px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.alert-success {
|
||||
background: rgba(0, 168, 112, 0.1);
|
||||
color: var(--td-success-color);
|
||||
}
|
||||
|
||||
.alert-danger {
|
||||
background: rgba(213, 73, 65, 0.1);
|
||||
color: var(--td-error-color);
|
||||
}
|
||||
|
||||
.alert-info {
|
||||
background: rgba(0, 82, 217, 0.1);
|
||||
color: var(--td-brand-color);
|
||||
}
|
||||
|
||||
.alert-warning {
|
||||
background: rgba(227, 115, 24, 0.1);
|
||||
color: var(--td-warning-color);
|
||||
}
|
||||
|
||||
/* ========== 分页 ========== */
|
||||
.pagination {
|
||||
display: flex;
|
||||
gap: 4px;
|
||||
margin: 20px 0;
|
||||
}
|
||||
|
||||
.pagination .page-link {
|
||||
background: var(--td-bg-color-container);
|
||||
border: 1px solid var(--td-border-color);
|
||||
color: var(--td-text-color-primary);
|
||||
border-radius: var(--td-border-radius);
|
||||
padding: 6px 12px;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
|
||||
.pagination .page-link:hover {
|
||||
background: var(--td-brand-color-light);
|
||||
border-color: var(--td-brand-color);
|
||||
color: var(--td-brand-color);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.pagination .page-item.active .page-link {
|
||||
background: var(--td-brand-color);
|
||||
border-color: var(--td-brand-color);
|
||||
color: #FFFFFF;
|
||||
}
|
||||
|
||||
.pagination .page-item.disabled .page-link {
|
||||
background: var(--td-bg-color-page);
|
||||
border-color: var(--td-border-color);
|
||||
color: var(--td-text-color-placeholder);
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
/* ========== 模态框 ========== */
|
||||
.modal-content {
|
||||
background: var(--td-bg-color-container);
|
||||
border: 1px solid var(--td-border-color);
|
||||
border-radius: var(--td-border-radius-medium);
|
||||
box-shadow: var(--td-shadow-2);
|
||||
}
|
||||
|
||||
.modal-header {
|
||||
border-bottom: 1px solid var(--td-border-color);
|
||||
padding: 20px 24px;
|
||||
}
|
||||
|
||||
.modal-title {
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
color: var(--td-text-color-primary);
|
||||
}
|
||||
|
||||
.modal-body {
|
||||
padding: 24px;
|
||||
}
|
||||
|
||||
.modal-footer {
|
||||
border-top: 1px solid var(--td-border-color);
|
||||
padding: 16px 24px;
|
||||
}
|
||||
|
||||
/* ========== 下拉菜单 ========== */
|
||||
.dropdown-menu {
|
||||
background: var(--td-bg-color-container);
|
||||
border: 1px solid var(--td-border-color);
|
||||
border-radius: var(--td-border-radius-medium);
|
||||
box-shadow: var(--td-shadow-2);
|
||||
}
|
||||
|
||||
.dropdown-item {
|
||||
color: var(--td-text-color-primary);
|
||||
padding: 8px 16px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.dropdown-item:hover {
|
||||
background: var(--td-brand-color-light);
|
||||
color: var(--td-brand-color);
|
||||
}
|
||||
|
||||
/* ========== Select2 ========== */
|
||||
.select2-container--bootstrap4 .select2-selection {
|
||||
background: var(--td-bg-color-container);
|
||||
border: 1px solid var(--td-border-color);
|
||||
border-radius: var(--td-border-radius);
|
||||
}
|
||||
|
||||
.select2-dropdown {
|
||||
background: var(--td-bg-color-container);
|
||||
border: 1px solid var(--td-border-color);
|
||||
border-radius: var(--td-border-radius-medium);
|
||||
box-shadow: var(--td-shadow-2);
|
||||
}
|
||||
|
||||
.select2-results__option {
|
||||
color: var(--td-text-color-primary);
|
||||
padding: 8px 12px;
|
||||
}
|
||||
|
||||
.select2-results__option--highlighted {
|
||||
background: var(--td-brand-color-light);
|
||||
color: var(--td-brand-color);
|
||||
}
|
||||
|
||||
/* ========== 滚动条 ========== */
|
||||
::-webkit-scrollbar {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-track {
|
||||
background: var(--td-bg-color-page);
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb {
|
||||
background: var(--td-border-color);
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb:hover {
|
||||
background: var(--td-text-color-placeholder);
|
||||
}
|
||||
|
||||
/* ========== 响应式 ========== */
|
||||
@media (max-width: 768px) {
|
||||
.admin-sidebar {
|
||||
transform: translateX(-100%);
|
||||
transition: transform 0.3s;
|
||||
}
|
||||
|
||||
.admin-main {
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
.search-box {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
@@ -1,295 +1,459 @@
|
||||
/* ========== Flask-Admin 后台科技感主题 - ZJPB焦提示词 ========== */
|
||||
/* ========== Flask-Admin TDesign主题 - ZJPB焦提示词 (亮色主题) ========== */
|
||||
|
||||
/* 深色主题覆盖 */
|
||||
body.admin-theme {
|
||||
background: #111618 !important;
|
||||
background-image:
|
||||
radial-gradient(at 20% 20%, rgba(37, 192, 244, 0.08) 0px, transparent 50%),
|
||||
radial-gradient(at 80% 80%, rgba(124, 58, 237, 0.08) 0px, transparent 50%);
|
||||
color: #ffffff !important;
|
||||
font-family: 'Space Grotesk', 'Noto Sans', sans-serif !important;
|
||||
/* TDesign 色彩系统 */
|
||||
:root {
|
||||
--td-brand-color: #0052D9;
|
||||
--td-brand-color-hover: #266FE8;
|
||||
--td-brand-color-active: #0034B5;
|
||||
--td-brand-color-light: #ECF2FE;
|
||||
--td-success-color: #00A870;
|
||||
--td-warning-color: #E37318;
|
||||
--td-error-color: #D54941;
|
||||
--td-bg-color-page: #F3F3F3;
|
||||
--td-bg-color-container: #FFFFFF;
|
||||
--td-bg-color-container-hover: #F5F5F5;
|
||||
--td-text-color-primary: #000000;
|
||||
--td-text-color-secondary: #606266;
|
||||
--td-text-color-placeholder: #C0C4CC;
|
||||
--td-border-color: #DCDFE6;
|
||||
--td-border-color-light: #E4E7ED;
|
||||
--td-border-radius: 3px;
|
||||
--td-border-radius-medium: 6px;
|
||||
--td-shadow-1: 0 1px 10px rgba(0, 0, 0, .05);
|
||||
--td-shadow-2: 0 2px 20px rgba(0, 0, 0, .08);
|
||||
}
|
||||
|
||||
/* 亮色模式 - 全局覆盖 */
|
||||
body, body.admin-theme, .admin-theme {
|
||||
background: var(--td-bg-color-page) !important;
|
||||
color: var(--td-text-color-primary) !important;
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, 'Noto Sans', sans-serif !important;
|
||||
}
|
||||
|
||||
/* 主容器 */
|
||||
.container-fluid, .container {
|
||||
background: transparent !important;
|
||||
}
|
||||
|
||||
/* 导航栏 */
|
||||
.navbar-admin {
|
||||
background: rgba(27, 36, 39, 0.95) !important;
|
||||
backdrop-filter: blur(20px);
|
||||
border-bottom: 1px solid #283539;
|
||||
box-shadow: 0 4px 30px rgba(0, 0, 0, 0.3);
|
||||
.navbar, .navbar-default, .navbar-inverse, .navbar-admin {
|
||||
background: #FFFFFF !important;
|
||||
border-bottom: 1px solid var(--td-border-color) !important;
|
||||
box-shadow: var(--td-shadow-1) !important;
|
||||
border: none !important;
|
||||
min-height: 64px !important;
|
||||
}
|
||||
|
||||
.navbar-admin .navbar-brand {
|
||||
background: linear-gradient(to right, #25c0f4, #c084fc);
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
background-clip: text;
|
||||
font-weight: 700;
|
||||
font-family: 'Space Grotesk', sans-serif !important;
|
||||
.navbar-brand, .navbar .navbar-brand {
|
||||
color: var(--td-brand-color) !important;
|
||||
font-weight: 600 !important;
|
||||
font-size: 18px !important;
|
||||
}
|
||||
|
||||
.navbar-nav .nav-link, .navbar-nav > li > a {
|
||||
color: var(--td-text-color-secondary) !important;
|
||||
font-size: 14px !important;
|
||||
padding: 8px 16px !important;
|
||||
border-radius: var(--td-border-radius) !important;
|
||||
transition: all 0.2s ease !important;
|
||||
}
|
||||
|
||||
.navbar-nav .nav-link:hover, .navbar-nav > li > a:hover,
|
||||
.navbar-nav .nav-link:focus, .navbar-nav > li > a:focus {
|
||||
color: var(--td-text-color-primary) !important;
|
||||
background: var(--td-bg-color-container-hover) !important;
|
||||
}
|
||||
|
||||
/* 侧边栏 */
|
||||
.nav-sidebar {
|
||||
background: rgba(27, 36, 39, 0.8) !important;
|
||||
.nav-sidebar, .nav.nav-pills {
|
||||
background: #FFFFFF !important;
|
||||
border-right: 1px solid var(--td-border-color) !important;
|
||||
}
|
||||
|
||||
.nav-sidebar .nav-link {
|
||||
color: #9cb2ba !important;
|
||||
transition: all 0.3s ease;
|
||||
.nav-sidebar .nav-link, .nav-sidebar > li > a,
|
||||
.nav-pills > li > a {
|
||||
color: var(--td-text-color-secondary) !important;
|
||||
padding: 12px 16px !important;
|
||||
margin: 4px 8px !important;
|
||||
border-radius: var(--td-border-radius-medium) !important;
|
||||
font-size: 14px !important;
|
||||
transition: all 0.2s cubic-bezier(0.38, 0, 0.24, 1) !important;
|
||||
background: transparent !important;
|
||||
}
|
||||
|
||||
.nav-sidebar .nav-link:hover,
|
||||
.nav-sidebar .nav-link.active {
|
||||
color: #ffffff !important;
|
||||
background: rgba(37, 192, 244, 0.15) !important;
|
||||
border-left: 3px solid #25c0f4;
|
||||
.nav-sidebar .nav-link:hover, .nav-sidebar > li > a:hover,
|
||||
.nav-pills > li > a:hover {
|
||||
color: var(--td-text-color-primary) !important;
|
||||
background: var(--td-brand-color-light) !important;
|
||||
}
|
||||
|
||||
.nav-sidebar .nav-link.active, .nav-sidebar > li.active > a,
|
||||
.nav-pills > li.active > a {
|
||||
color: var(--td-brand-color) !important;
|
||||
background: var(--td-brand-color-light) !important;
|
||||
font-weight: 500 !important;
|
||||
}
|
||||
|
||||
/* 卡片和面板 */
|
||||
.card, .panel {
|
||||
background: rgba(27, 36, 39, 0.6) !important;
|
||||
backdrop-filter: blur(20px);
|
||||
border: 1px solid #283539 !important;
|
||||
border-radius: 12px !important;
|
||||
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
|
||||
background: var(--td-bg-color-container) !important;
|
||||
border: 1px solid var(--td-border-color) !important;
|
||||
border-radius: var(--td-border-radius-medium) !important;
|
||||
box-shadow: var(--td-shadow-1);
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.card-header, .panel-heading {
|
||||
background: rgba(37, 192, 244, 0.08) !important;
|
||||
border-bottom: 1px solid #283539 !important;
|
||||
color: #ffffff !important;
|
||||
background: transparent !important;
|
||||
border-bottom: 1px solid var(--td-border-color) !important;
|
||||
color: var(--td-text-color-primary) !important;
|
||||
font-weight: 600;
|
||||
font-size: 16px;
|
||||
padding: 16px !important;
|
||||
}
|
||||
|
||||
/* 表格 */
|
||||
.table {
|
||||
color: #ffffff !important;
|
||||
color: var(--td-text-color-primary) !important;
|
||||
font-size: 14px;
|
||||
border-collapse: separate;
|
||||
border-spacing: 0;
|
||||
}
|
||||
|
||||
.table thead th {
|
||||
background: rgba(30, 39, 44, 0.8) !important;
|
||||
border-color: #283539 !important;
|
||||
color: #9cb2ba !important;
|
||||
font-size: 11px;
|
||||
background: var(--td-bg-color-container-hover) !important;
|
||||
border: none !important;
|
||||
color: var(--td-text-color-secondary) !important;
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.05em;
|
||||
letter-spacing: 0.5px;
|
||||
padding: 12px 16px !important;
|
||||
height: 48px;
|
||||
}
|
||||
|
||||
.table thead th:first-child {
|
||||
border-top-left-radius: var(--td-border-radius);
|
||||
}
|
||||
|
||||
.table thead th:last-child {
|
||||
border-top-right-radius: var(--td-border-radius);
|
||||
}
|
||||
|
||||
.table tbody tr {
|
||||
background: transparent !important;
|
||||
border-color: #283539 !important;
|
||||
transition: all 0.2s ease;
|
||||
border-bottom: 1px solid var(--td-border-color-light) !important;
|
||||
transition: background-color 0.2s ease;
|
||||
}
|
||||
|
||||
.table tbody tr:hover {
|
||||
background: rgba(30, 39, 44, 0.5) !important;
|
||||
background: var(--td-brand-color-light) !important;
|
||||
}
|
||||
|
||||
.table td, .table th {
|
||||
border-color: #283539 !important;
|
||||
color: #ffffff !important;
|
||||
.table tbody tr:last-child {
|
||||
border-bottom: none !important;
|
||||
}
|
||||
|
||||
.table td {
|
||||
border: none !important;
|
||||
color: var(--td-text-color-primary) !important;
|
||||
padding: 16px !important;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.table th {
|
||||
border: none !important;
|
||||
}
|
||||
|
||||
/* 表单 */
|
||||
.form-control {
|
||||
background: #111618 !important;
|
||||
border: 1px solid #283539 !important;
|
||||
color: #ffffff !important;
|
||||
border-radius: 8px !important;
|
||||
transition: all 0.3s ease;
|
||||
background: var(--td-bg-color-container) !important;
|
||||
border: 1px solid var(--td-border-color) !important;
|
||||
color: var(--td-text-color-primary) !important;
|
||||
border-radius: var(--td-border-radius) !important;
|
||||
padding: 8px 12px !important;
|
||||
font-size: 14px;
|
||||
line-height: 22px;
|
||||
transition: all 0.2s cubic-bezier(0.38, 0, 0.24, 1);
|
||||
}
|
||||
|
||||
.form-control:focus {
|
||||
background: rgba(27, 36, 39, 0.8) !important;
|
||||
border-color: #25c0f4 !important;
|
||||
box-shadow: 0 0 0 3px rgba(37, 192, 244, 0.1) !important;
|
||||
background: var(--td-bg-color-container) !important;
|
||||
border-color: var(--td-brand-color) !important;
|
||||
box-shadow: 0 0 0 2px rgba(0, 82, 217, 0.1) !important;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.form-control::placeholder {
|
||||
color: #9cb2ba !important;
|
||||
color: var(--td-text-color-placeholder) !important;
|
||||
}
|
||||
|
||||
.form-label, label {
|
||||
color: #9cb2ba !important;
|
||||
color: var(--td-text-color-primary) !important;
|
||||
font-weight: 500;
|
||||
font-size: 13px;
|
||||
font-size: 14px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.form-text {
|
||||
color: var(--td-text-color-secondary) !important;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
/* 按钮 */
|
||||
.btn {
|
||||
border-radius: var(--td-border-radius) !important;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
padding: 8px 16px !important;
|
||||
line-height: 22px;
|
||||
transition: all 0.2s cubic-bezier(0.38, 0, 0.24, 1);
|
||||
border: none;
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
background: #25c0f4 !important;
|
||||
border: none !important;
|
||||
color: #111618 !important;
|
||||
font-weight: 600;
|
||||
box-shadow: 0 0 20px rgba(37, 192, 244, 0.3);
|
||||
transition: all 0.3s ease;
|
||||
background: var(--td-brand-color) !important;
|
||||
color: #FFFFFF !important;
|
||||
box-shadow: 0 2px 4px rgba(0, 82, 217, 0.2);
|
||||
}
|
||||
|
||||
.btn-primary:hover {
|
||||
background: #1fa8d8 !important;
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 0 30px rgba(37, 192, 244, 0.5);
|
||||
background: var(--td-brand-color-hover) !important;
|
||||
box-shadow: 0 4px 8px rgba(0, 82, 217, 0.3);
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
.btn-primary:active {
|
||||
background: var(--td-brand-color-active) !important;
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
.btn-success {
|
||||
background: var(--td-success-color) !important;
|
||||
color: #FFFFFF !important;
|
||||
}
|
||||
|
||||
.btn-warning {
|
||||
background: var(--td-warning-color) !important;
|
||||
color: #FFFFFF !important;
|
||||
}
|
||||
|
||||
.btn-danger {
|
||||
background: var(--td-error-color) !important;
|
||||
color: #FFFFFF !important;
|
||||
}
|
||||
|
||||
.btn-info {
|
||||
background: linear-gradient(135deg, #25c0f4 0%, #00f2fe 100%) !important;
|
||||
border: none !important;
|
||||
color: #111618 !important;
|
||||
font-weight: 600;
|
||||
background: #029CD4 !important;
|
||||
color: #FFFFFF !important;
|
||||
}
|
||||
|
||||
.btn-secondary, .btn-default {
|
||||
background: rgba(40, 53, 57, 0.6) !important;
|
||||
border: 1px solid #283539 !important;
|
||||
color: #ffffff !important;
|
||||
transition: all 0.3s ease;
|
||||
background: #FFFFFF !important;
|
||||
border: 1px solid var(--td-border-color) !important;
|
||||
color: var(--td-text-color-primary) !important;
|
||||
}
|
||||
|
||||
.btn-secondary:hover, .btn-default:hover {
|
||||
background: rgba(52, 66, 71, 0.8) !important;
|
||||
border-color: #4a5a60 !important;
|
||||
background: var(--td-bg-color-container-hover) !important;
|
||||
border-color: var(--td-border-color) !important;
|
||||
}
|
||||
|
||||
/* 模态框 */
|
||||
.modal-content {
|
||||
background: rgba(27, 36, 39, 0.95) !important;
|
||||
backdrop-filter: blur(20px);
|
||||
border: 1px solid #283539 !important;
|
||||
border-radius: 12px !important;
|
||||
background: var(--td-bg-color-container) !important;
|
||||
border: 1px solid var(--td-border-color) !important;
|
||||
border-radius: var(--td-border-radius-medium) !important;
|
||||
box-shadow: var(--td-shadow-2);
|
||||
}
|
||||
|
||||
.modal-header {
|
||||
border-bottom-color: #283539 !important;
|
||||
background: rgba(37, 192, 244, 0.05);
|
||||
border-bottom: 1px solid var(--td-border-color) !important;
|
||||
padding: 20px 24px;
|
||||
}
|
||||
|
||||
.modal-title {
|
||||
color: var(--td-text-color-primary) !important;
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.modal-body {
|
||||
padding: 24px;
|
||||
}
|
||||
|
||||
.modal-footer {
|
||||
border-top-color: #283539 !important;
|
||||
border-top: 1px solid var(--td-border-color) !important;
|
||||
padding: 16px 24px;
|
||||
}
|
||||
|
||||
/* 分页 */
|
||||
.pagination {
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.pagination .page-link {
|
||||
background: rgba(27, 36, 39, 0.6) !important;
|
||||
border-color: #283539 !important;
|
||||
color: #9cb2ba !important;
|
||||
background: var(--td-bg-color-container) !important;
|
||||
border: 1px solid var(--td-border-color) !important;
|
||||
color: var(--td-text-color-primary) !important;
|
||||
border-radius: var(--td-border-radius) !important;
|
||||
padding: 6px 12px;
|
||||
margin: 0;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.pagination .page-link:hover {
|
||||
background: rgba(37, 192, 244, 0.1) !important;
|
||||
border-color: #25c0f4 !important;
|
||||
color: #ffffff !important;
|
||||
background: var(--td-brand-color-light) !important;
|
||||
border-color: var(--td-brand-color) !important;
|
||||
color: var(--td-brand-color) !important;
|
||||
}
|
||||
|
||||
.pagination .page-item.active .page-link {
|
||||
background: #25c0f4 !important;
|
||||
border-color: #25c0f4 !important;
|
||||
color: #111618 !important;
|
||||
background: var(--td-brand-color) !important;
|
||||
border-color: var(--td-brand-color) !important;
|
||||
color: #FFFFFF !important;
|
||||
}
|
||||
|
||||
.pagination .page-item.disabled .page-link {
|
||||
background: var(--td-bg-color-container) !important;
|
||||
border-color: var(--td-border-color) !important;
|
||||
color: var(--td-text-color-placeholder) !important;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
/* 警告框 */
|
||||
.alert {
|
||||
background: rgba(27, 36, 39, 0.8) !important;
|
||||
border: 1px solid #283539 !important;
|
||||
color: #ffffff !important;
|
||||
border-radius: 8px !important;
|
||||
border-radius: var(--td-border-radius-medium) !important;
|
||||
border: none !important;
|
||||
padding: 12px 16px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.alert-success {
|
||||
background: rgba(34, 197, 94, 0.1) !important;
|
||||
border-color: rgba(34, 197, 94, 0.3) !important;
|
||||
color: #4ade80 !important;
|
||||
background: rgba(0, 168, 112, 0.1) !important;
|
||||
color: #00A870 !important;
|
||||
}
|
||||
|
||||
.alert-danger {
|
||||
background: rgba(239, 68, 68, 0.1) !important;
|
||||
border-color: rgba(239, 68, 68, 0.3) !important;
|
||||
color: #f87171 !important;
|
||||
background: rgba(213, 73, 65, 0.1) !important;
|
||||
color: #D54941 !important;
|
||||
}
|
||||
|
||||
.alert-info {
|
||||
background: rgba(37, 192, 244, 0.1) !important;
|
||||
border-color: rgba(37, 192, 244, 0.3) !important;
|
||||
color: #25c0f4 !important;
|
||||
background: rgba(2, 156, 212, 0.1) !important;
|
||||
color: #029CD4 !important;
|
||||
}
|
||||
|
||||
.alert-warning {
|
||||
background: rgba(251, 191, 36, 0.1) !important;
|
||||
border-color: rgba(251, 191, 36, 0.3) !important;
|
||||
color: #fbbf24 !important;
|
||||
background: rgba(227, 115, 24, 0.1) !important;
|
||||
color: #E37318 !important;
|
||||
}
|
||||
|
||||
/* 链接 */
|
||||
a {
|
||||
color: #25c0f4 !important;
|
||||
color: var(--td-brand-color) !important;
|
||||
text-decoration: none;
|
||||
transition: color 0.2s ease;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
color: #1fa8d8 !important;
|
||||
color: var(--td-brand-color-hover) !important;
|
||||
}
|
||||
|
||||
/* 文本颜色 */
|
||||
.text-muted {
|
||||
color: #9cb2ba !important;
|
||||
color: var(--td-text-color-secondary) !important;
|
||||
}
|
||||
|
||||
/* 输入组 */
|
||||
.input-group-text {
|
||||
background: rgba(27, 36, 39, 0.6) !important;
|
||||
border-color: #283539 !important;
|
||||
color: #9cb2ba !important;
|
||||
background: var(--td-bg-color-container-hover) !important;
|
||||
border: 1px solid var(--td-border-color) !important;
|
||||
color: var(--td-text-color-secondary) !important;
|
||||
}
|
||||
|
||||
/* Select2 下拉框 */
|
||||
.select2-container--bootstrap4 .select2-selection {
|
||||
background: #111618 !important;
|
||||
border-color: #283539 !important;
|
||||
color: #ffffff !important;
|
||||
background: var(--td-bg-color-container) !important;
|
||||
border: 1px solid var(--td-border-color) !important;
|
||||
color: var(--td-text-color-primary) !important;
|
||||
border-radius: var(--td-border-radius) !important;
|
||||
}
|
||||
|
||||
.select2-dropdown {
|
||||
background: rgba(27, 36, 39, 0.95) !important;
|
||||
border-color: #283539 !important;
|
||||
backdrop-filter: blur(10px);
|
||||
background: var(--td-bg-color-container) !important;
|
||||
border: 1px solid var(--td-border-color) !important;
|
||||
border-radius: var(--td-border-radius-medium) !important;
|
||||
box-shadow: var(--td-shadow-2);
|
||||
}
|
||||
|
||||
.select2-results__option {
|
||||
color: #ffffff !important;
|
||||
color: var(--td-text-color-primary) !important;
|
||||
padding: 8px 12px;
|
||||
}
|
||||
|
||||
.select2-results__option--highlighted {
|
||||
background: rgba(37, 192, 244, 0.2) !important;
|
||||
background: var(--td-brand-color-light) !important;
|
||||
color: var(--td-brand-color) !important;
|
||||
}
|
||||
|
||||
/* 徽章 */
|
||||
.badge {
|
||||
border-radius: 2px !important;
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
padding: 2px 8px;
|
||||
}
|
||||
|
||||
.badge-primary {
|
||||
background: #25c0f4 !important;
|
||||
color: #111618 !important;
|
||||
background: var(--td-brand-color) !important;
|
||||
color: #FFFFFF !important;
|
||||
}
|
||||
|
||||
.badge-success {
|
||||
background: var(--td-success-color) !important;
|
||||
color: #FFFFFF !important;
|
||||
}
|
||||
|
||||
.badge-warning {
|
||||
background: var(--td-warning-color) !important;
|
||||
color: #FFFFFF !important;
|
||||
}
|
||||
|
||||
.badge-danger {
|
||||
background: var(--td-error-color) !important;
|
||||
color: #FFFFFF !important;
|
||||
}
|
||||
|
||||
.badge-secondary {
|
||||
background: #283539 !important;
|
||||
color: #9cb2ba !important;
|
||||
background: var(--td-bg-color-container-hover) !important;
|
||||
color: var(--td-text-color-secondary) !important;
|
||||
}
|
||||
|
||||
/* 进度条 */
|
||||
.progress {
|
||||
background: rgba(27, 36, 39, 0.6) !important;
|
||||
background: var(--td-bg-color-container-hover) !important;
|
||||
border-radius: 2px !important;
|
||||
height: 8px;
|
||||
}
|
||||
|
||||
.progress-bar {
|
||||
background: #25c0f4 !important;
|
||||
background: var(--td-brand-color) !important;
|
||||
}
|
||||
|
||||
/* 额外优化 */
|
||||
.navbar-nav .nav-link {
|
||||
color: #9cb2ba !important;
|
||||
/* 复选框和单选框 */
|
||||
.form-check-input {
|
||||
background-color: var(--td-bg-color-container) !important;
|
||||
border: 1px solid var(--td-border-color) !important;
|
||||
}
|
||||
|
||||
.navbar-nav .nav-link:hover {
|
||||
color: #ffffff !important;
|
||||
.form-check-input:checked {
|
||||
background-color: var(--td-brand-color) !important;
|
||||
border-color: var(--td-brand-color) !important;
|
||||
}
|
||||
|
||||
.form-check-input:focus {
|
||||
box-shadow: 0 0 0 2px rgba(0, 82, 217, 0.1) !important;
|
||||
}
|
||||
|
||||
/* 自定义滚动条 */
|
||||
@@ -299,15 +463,123 @@ a:hover {
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-track {
|
||||
background: #111618;
|
||||
background: var(--td-bg-color-page);
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb {
|
||||
background: #283539;
|
||||
background: var(--td-border-color);
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb:hover {
|
||||
background: #3a4b50;
|
||||
background: var(--td-text-color-placeholder);
|
||||
}
|
||||
|
||||
/* 工具提示 */
|
||||
.tooltip-inner {
|
||||
background: var(--td-text-color-primary) !important;
|
||||
color: #FFFFFF !important;
|
||||
border-radius: var(--td-border-radius) !important;
|
||||
padding: 6px 12px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
/* 面包屑 */
|
||||
.breadcrumb {
|
||||
background: transparent !important;
|
||||
padding: 0;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.breadcrumb-item {
|
||||
color: var(--td-text-color-secondary) !important;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.breadcrumb-item.active {
|
||||
color: var(--td-text-color-primary) !important;
|
||||
}
|
||||
|
||||
.breadcrumb-item + .breadcrumb-item::before {
|
||||
color: var(--td-text-color-placeholder) !important;
|
||||
}
|
||||
|
||||
/* 标签页 */
|
||||
.nav-tabs {
|
||||
border-bottom: 1px solid var(--td-border-color) !important;
|
||||
}
|
||||
|
||||
.nav-tabs .nav-link {
|
||||
border: none !important;
|
||||
color: var(--td-text-color-secondary) !important;
|
||||
padding: 12px 24px;
|
||||
margin-bottom: -1px;
|
||||
border-bottom: 2px solid transparent;
|
||||
}
|
||||
|
||||
.nav-tabs .nav-link:hover {
|
||||
color: var(--td-text-color-primary) !important;
|
||||
border-bottom-color: var(--td-border-color) !important;
|
||||
}
|
||||
|
||||
.nav-tabs .nav-link.active {
|
||||
color: var(--td-brand-color) !important;
|
||||
background: transparent !important;
|
||||
border-bottom-color: var(--td-brand-color) !important;
|
||||
}
|
||||
|
||||
/* 下拉菜单 */
|
||||
.dropdown-menu {
|
||||
background: var(--td-bg-color-container) !important;
|
||||
border: 1px solid var(--td-border-color) !important;
|
||||
border-radius: var(--td-border-radius-medium) !important;
|
||||
box-shadow: var(--td-shadow-2);
|
||||
}
|
||||
|
||||
.dropdown-item {
|
||||
color: var(--td-text-color-primary) !important;
|
||||
padding: 8px 16px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.dropdown-item:hover {
|
||||
background: var(--td-brand-color-light) !important;
|
||||
color: var(--td-brand-color) !important;
|
||||
}
|
||||
|
||||
/* 表格操作按钮 */
|
||||
.table .btn-sm {
|
||||
padding: 4px 12px !important;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
/* 空状态 */
|
||||
.empty-state {
|
||||
text-align: center;
|
||||
padding: 48px 24px;
|
||||
color: var(--td-text-color-secondary);
|
||||
}
|
||||
|
||||
.empty-state-icon {
|
||||
font-size: 48px;
|
||||
color: var(--td-text-color-placeholder);
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
/* 加载状态 */
|
||||
.spinner-border {
|
||||
border-color: var(--td-brand-color);
|
||||
border-right-color: transparent;
|
||||
}
|
||||
|
||||
/* 响应式优化 */
|
||||
@media (max-width: 768px) {
|
||||
.table {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.table td, .table th {
|
||||
padding: 8px !important;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
108
stitch_ai_tool_detail_page/admin_login_page/code.html
Normal file
@@ -0,0 +1,108 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en"><head>
|
||||
<meta charset="utf-8"/>
|
||||
<meta content="width=device-width, initial-scale=1.0" name="viewport"/>
|
||||
<title>Admin Login - AI Discovery</title>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@300;400;500;600;700&display=swap" rel="stylesheet"/>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Noto+Sans:wght@300;400;500;600;700&display=swap" rel="stylesheet"/>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:wght,FILL@100..700,0..1&display=swap" rel="stylesheet"/>
|
||||
<script src="https://cdn.tailwindcss.com?plugins=forms,container-queries"></script>
|
||||
<script>
|
||||
tailwind.config = {
|
||||
theme: {
|
||||
extend: {
|
||||
colors: {
|
||||
"primary": "#0ea5e9", // Sky 500 - Clearer on light
|
||||
"primary-dark": "#0284c7", // Sky 600 - Hover state
|
||||
"background": "#f8fafc", // Slate 50 - Light, clean
|
||||
"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"],
|
||||
},
|
||||
backgroundImage: {
|
||||
// Very subtle grid for light mode
|
||||
'tech-grid': "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)",
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
<style>
|
||||
.glass-panel {
|
||||
background: rgba(255, 255, 255, 0.75);
|
||||
backdrop-filter: blur(20px);
|
||||
-webkit-backdrop-filter: blur(20px);
|
||||
}
|
||||
.tech-bg-size {
|
||||
background-size: 100% 100%, 40px 40px, 40px 40px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body class="bg-background font-display text-text-main min-h-screen flex flex-col items-center justify-center relative overflow-hidden selection:bg-primary/20 selection:text-primary-dark">
|
||||
<div class="absolute inset-0 z-0 bg-tech-grid tech-bg-size 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>
|
||||
<div class="w-full max-w-[480px] px-4 z-10 flex flex-col gap-6">
|
||||
<a class="group flex items-center gap-2 text-text-muted hover:text-text-main transition-colors w-fit" href="#">
|
||||
<div class="flex items-center justify-center w-8 h-8 rounded-full border border-input-border bg-white group-hover:border-primary/50 group-hover:bg-primary/5 transition-all shadow-sm">
|
||||
<span class="material-symbols-outlined text-sm">arrow_back</span>
|
||||
</div>
|
||||
<span class="text-sm font-medium">Back to Home</span>
|
||||
</a>
|
||||
<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">
|
||||
<div class="absolute top-0 left-0 w-full h-[2px] bg-gradient-to-r from-transparent via-primary/50 to-transparent"></div>
|
||||
<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">System Access</span>
|
||||
</div>
|
||||
<h1 class="text-3xl font-black tracking-tight text-text-main leading-tight">管理员登录</h1>
|
||||
<p class="text-text-muted text-base font-normal">Enter your credentials to access the AI Control Panel.</p>
|
||||
</div>
|
||||
<form class="flex flex-col gap-5">
|
||||
<div class="space-y-2">
|
||||
<label class="text-text-secondary text-sm font-semibold leading-normal" for="username">Username</label>
|
||||
<div class="relative group">
|
||||
<input class="form-input flex w-full h-12 pl-11 pr-4 rounded-lg text-text-main focus:outline-0 focus:ring-2 focus:ring-primary/20 border border-input-border bg-input-bg focus:border-primary placeholder:text-slate-400 text-base font-normal leading-normal transition-all shadow-sm" id="username" placeholder="Enter your username" type="text"/>
|
||||
<div class="absolute left-3 top-1/2 -translate-y-1/2 text-slate-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>
|
||||
<div class="space-y-2">
|
||||
<div class="flex justify-between items-end">
|
||||
<label class="text-text-secondary text-sm font-semibold leading-normal" for="password">Password</label>
|
||||
<a class="text-xs font-medium text-primary hover:text-primary-dark transition-colors mb-1" href="#">Forgot password?</a>
|
||||
</div>
|
||||
<div class="flex w-full items-stretch rounded-lg group relative">
|
||||
<input class="form-input flex w-full h-12 pl-11 pr-11 rounded-lg text-text-main focus:outline-0 focus:ring-2 focus:ring-primary/20 border border-input-border bg-input-bg focus:border-primary placeholder:text-slate-400 text-base font-normal leading-normal transition-all shadow-sm z-10" id="password" placeholder="Enter your password" type="password"/>
|
||||
<div class="absolute left-3 top-1/2 -translate-y-1/2 text-slate-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>
|
||||
<button class="absolute right-0 top-0 h-full px-3 text-slate-400 hover:text-text-secondary transition-colors flex items-center justify-center z-20 focus:outline-none" type="button">
|
||||
<span class="material-symbols-outlined text-[20px]">visibility_off</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<button class="mt-4 flex w-full h-12 cursor-pointer items-center justify-center overflow-hidden rounded-lg bg-primary text-white text-base font-bold leading-normal tracking-wide hover:bg-primary-dark transition-all active:scale-[0.98] shadow-lg shadow-primary/25 hover:shadow-primary/40" type="button">
|
||||
<span class="truncate">Login</span>
|
||||
</button>
|
||||
</form>
|
||||
<div class="mt-8 flex justify-center gap-6 border-t border-slate-100 pt-6">
|
||||
<p class="text-text-muted text-xs text-center font-medium">
|
||||
Protected by reCAPTCHA. <a class="underline hover:text-primary decoration-slate-300 hover:decoration-primary" href="#">Privacy</a> & <a class="underline hover:text-primary decoration-slate-300 hover:decoration-primary" href="#">Terms</a>.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</body></html>
|
||||
BIN
stitch_ai_tool_detail_page/admin_login_page/screen.png
Normal file
|
After Width: | Height: | Size: 604 KiB |
365
stitch_ai_tool_detail_page/admin_management_interface/code.html
Normal file
@@ -0,0 +1,365 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en"><head>
|
||||
<meta charset="utf-8"/>
|
||||
<meta content="width=device-width, initial-scale=1.0" name="viewport"/>
|
||||
<title>Admin Management Interface</title>
|
||||
<link href="https://fonts.googleapis.com" rel="preconnect"/>
|
||||
<link crossorigin="" href="https://fonts.gstatic.com" rel="preconnect"/>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@300;400;500;700&family=Noto+Sans:wght@400;500;700&display=swap" rel="stylesheet"/>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:wght,FILL@100..700,0..1&display=swap" rel="stylesheet"/>
|
||||
<script src="https://cdn.tailwindcss.com?plugins=forms,container-queries"></script>
|
||||
<script id="tailwind-config">
|
||||
tailwind.config = {
|
||||
theme: {
|
||||
extend: {
|
||||
colors: {
|
||||
"primary": "#0ea5e9", // Sky 500
|
||||
"primary-hover": "#0284c7", // Sky 600
|
||||
"primary-light": "#e0f2fe", // Sky 100
|
||||
"surface": "#ffffff",
|
||||
"surface-highlight": "#f1f5f9", // Slate 100
|
||||
"border-subtle": "#e2e8f0", // Slate 200
|
||||
"text-main": "#0f172a", // Slate 900
|
||||
"text-secondary": "#64748b", // Slate 500
|
||||
"bg-main": "#f8fafc", // Slate 50
|
||||
},
|
||||
fontFamily: {
|
||||
"display": ["Space Grotesk", "sans-serif"],
|
||||
"body": ["Noto Sans", "sans-serif"],
|
||||
},
|
||||
borderRadius: {"DEFAULT": "0.25rem", "lg": "0.5rem", "xl": "0.75rem", "2xl": "1rem", "full": "9999px"},
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
<style>
|
||||
body {
|
||||
font-family: "Noto Sans", sans-serif;
|
||||
}
|
||||
h1, h2, h3, h4, h5, h6, .font-display {
|
||||
font-family: "Space Grotesk", sans-serif;
|
||||
}::-webkit-scrollbar {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
}
|
||||
::-webkit-scrollbar-track {
|
||||
background: #f1f5f9;
|
||||
}
|
||||
::-webkit-scrollbar-thumb {
|
||||
background: #cbd5e1;
|
||||
border-radius: 4px;
|
||||
}
|
||||
::-webkit-scrollbar-thumb:hover {
|
||||
background: #94a3b8;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body class="bg-bg-main text-text-main overflow-hidden flex h-screen w-full">
|
||||
<aside class="flex flex-col w-64 h-full border-r border-border-subtle bg-surface flex-shrink-0 transition-all duration-300">
|
||||
<div class="p-6 flex items-center gap-3">
|
||||
<div class="size-8 text-primary">
|
||||
<svg fill="none" viewBox="0 0 48 48" xmlns="http://www.w3.org/2000/svg">
|
||||
<path clip-rule="evenodd" d="M24 18.4228L42 11.475V34.3663C42 34.7796 41.7457 35.1504 41.3601 35.2992L24 42V18.4228Z" fill="currentColor" fill-rule="evenodd"></path>
|
||||
<path clip-rule="evenodd" d="M24 8.18819L33.4123 11.574L24 15.2071L14.5877 11.574L24 8.18819ZM9 15.8487L21 20.4805V37.6263L9 32.9945V15.8487ZM27 37.6263V20.4805L39 15.8487V32.9945L27 37.6263ZM25.354 2.29885C24.4788 1.98402 23.5212 1.98402 22.646 2.29885L4.98454 8.65208C3.7939 9.08038 3 10.2097 3 11.475V34.3663C3 36.0196 4.01719 37.5026 5.55962 38.098L22.9197 44.7987C23.6149 45.0671 24.3851 45.0671 25.0803 44.7987L42.4404 38.098C43.9828 37.5026 45 36.0196 45 34.3663V11.475C45 10.2097 44.2061 9.08038 43.0155 8.65208L25.354 2.29885Z" fill="currentColor" fill-rule="evenodd"></path>
|
||||
</svg>
|
||||
</div>
|
||||
<h1 class="text-text-main text-xl font-bold tracking-tight">AI Discovery</h1>
|
||||
</div>
|
||||
<nav class="flex-1 px-4 flex flex-col gap-2 mt-4">
|
||||
<div class="px-3 py-2">
|
||||
<p class="text-text-secondary text-xs font-bold uppercase tracking-wider mb-2">Main Menu</p>
|
||||
<a class="flex items-center gap-3 px-3 py-2.5 rounded-lg bg-primary-light/50 text-primary border border-primary/10 group transition-colors" href="#">
|
||||
<span class="material-symbols-outlined text-primary transition-colors">language</span>
|
||||
<span class="text-sm font-medium">Website Management</span>
|
||||
</a>
|
||||
<a class="flex items-center gap-3 px-3 py-2.5 rounded-lg text-text-secondary hover:bg-surface-highlight hover:text-text-main transition-colors mt-1" href="#">
|
||||
<span class="material-symbols-outlined">label</span>
|
||||
<span class="text-sm font-medium">Tag Management</span>
|
||||
</a>
|
||||
<a class="flex items-center gap-3 px-3 py-2.5 rounded-lg text-text-secondary hover:bg-surface-highlight hover:text-text-main transition-colors mt-1" href="#">
|
||||
<span class="material-symbols-outlined">group</span>
|
||||
<span class="text-sm font-medium">Administrators</span>
|
||||
</a>
|
||||
</div>
|
||||
<div class="px-3 py-2 mt-auto">
|
||||
<p class="text-text-secondary text-xs font-bold uppercase tracking-wider mb-2">System</p>
|
||||
<a class="flex items-center gap-3 px-3 py-2.5 rounded-lg text-text-secondary hover:bg-surface-highlight hover:text-text-main transition-colors" href="#">
|
||||
<span class="material-symbols-outlined">settings</span>
|
||||
<span class="text-sm font-medium">Settings</span>
|
||||
</a>
|
||||
</div>
|
||||
</nav>
|
||||
<div class="p-4 border-t border-border-subtle">
|
||||
<div class="flex items-center gap-3">
|
||||
<div class="bg-center bg-no-repeat bg-cover rounded-full size-10 border border-border-subtle" data-alt="User profile picture showing a minimal avatar" style='background-image: url("https://lh3.googleusercontent.com/aida-public/AB6AXuDlD4gGno1M8YZQOQ6QyLdUZtaU7klHxTjeH2Kf3aH39GkQ56ld-iSPZ6RCbkxzCeXJzMq6_29atBkPks_wP-Q7pvpyNAAZW2FJKxOFTSrcQZp2O30dVhn-DPYELqG1fdqM-hPfluduBN-I0k_Q8cJrJ3LKVUUgnxsE8bWD4CSo8CoYvXmtK03j86TehszyrfWfKpoBcu8XaHpjh10EGoDHFYeSJ6AKJQDsp_0iz5LC4bOXvyg0_Nef5mQOBtQ9zKuKSbCYhRuu7Wyo");'></div>
|
||||
<div class="flex flex-col overflow-hidden">
|
||||
<h2 class="text-text-main text-sm font-medium truncate">Admin User</h2>
|
||||
<p class="text-text-secondary text-xs truncate">admin@ai-discovery.com</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</aside>
|
||||
<main class="flex-1 flex flex-col h-full overflow-hidden bg-bg-main relative">
|
||||
<header class="h-16 border-b border-border-subtle bg-surface/90 backdrop-blur px-8 flex items-center justify-between shrink-0 z-20">
|
||||
<div class="flex items-center gap-2 text-sm">
|
||||
<a class="text-text-secondary hover:text-text-main transition-colors" href="#">Dashboard</a>
|
||||
<span class="text-text-secondary">/</span>
|
||||
<span class="text-text-main font-medium">Website Management</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-6">
|
||||
<div class="relative w-64 hidden md:block">
|
||||
<span class="absolute inset-y-0 left-0 flex items-center pl-3 text-text-secondary">
|
||||
<span class="material-symbols-outlined text-[20px]">search</span>
|
||||
</span>
|
||||
<input class="w-full bg-surface-highlight border-none rounded-lg py-2 pl-10 pr-4 text-sm text-text-main placeholder-text-secondary focus:ring-1 focus:ring-primary focus:bg-white transition-all shadow-sm" placeholder="Global search..." type="text"/>
|
||||
</div>
|
||||
<button class="relative text-text-secondary hover:text-text-main transition-colors">
|
||||
<span class="material-symbols-outlined">notifications</span>
|
||||
<span class="absolute top-0 right-0 size-2 bg-primary rounded-full ring-2 ring-white"></span>
|
||||
</button>
|
||||
<button class="text-text-secondary hover:text-text-main transition-colors">
|
||||
<span class="material-symbols-outlined">logout</span>
|
||||
</button>
|
||||
</div>
|
||||
</header>
|
||||
<div class="flex-1 overflow-y-auto overflow-x-hidden p-8 scroll-smooth">
|
||||
<div class="max-w-[1400px] mx-auto flex flex-col gap-8 pb-10">
|
||||
<div class="flex flex-col md:flex-row md:items-end justify-between gap-4">
|
||||
<div>
|
||||
<h1 class="text-3xl md:text-4xl font-bold text-text-main mb-2">Website Management</h1>
|
||||
<p class="text-text-secondary text-base">Manage and curate AI tools for the discovery platform.</p>
|
||||
</div>
|
||||
<div class="flex gap-3">
|
||||
<button class="flex items-center gap-2 h-10 px-4 rounded-lg bg-surface border border-border-subtle hover:bg-surface-highlight text-text-main text-sm font-medium transition-colors shadow-sm">
|
||||
<span class="material-symbols-outlined text-[20px]">filter_list</span>
|
||||
<span>Filter</span>
|
||||
</button>
|
||||
<button class="flex items-center gap-2 h-10 px-4 rounded-lg bg-surface border border-border-subtle hover:bg-surface-highlight text-text-main text-sm font-medium transition-colors shadow-sm">
|
||||
<span class="material-symbols-outlined text-[20px]">download</span>
|
||||
<span>Export</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="grid grid-cols-1 xl:grid-cols-3 gap-8 items-start">
|
||||
<div class="xl:col-span-2 flex flex-col gap-4">
|
||||
<div class="bg-surface border border-border-subtle rounded-t-xl p-4 flex flex-col sm:flex-row justify-between items-center gap-4 shadow-sm">
|
||||
<div class="relative w-full sm:w-72">
|
||||
<span class="absolute inset-y-0 left-0 flex items-center pl-3 text-text-secondary">
|
||||
<span class="material-symbols-outlined text-[20px]">search</span>
|
||||
</span>
|
||||
<input class="w-full bg-surface border border-border-subtle rounded-lg py-2 pl-10 pr-4 text-sm text-text-main placeholder-text-secondary focus:ring-1 focus:ring-primary focus:border-primary" placeholder="Search websites..." type="text"/>
|
||||
</div>
|
||||
<div class="text-sm text-text-secondary">
|
||||
Showing <span class="text-text-main font-medium">1-10</span> of <span class="text-text-main font-medium">128</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="bg-surface border border-border-subtle rounded-b-xl overflow-hidden shadow-sm">
|
||||
<div class="overflow-x-auto">
|
||||
<table class="w-full text-left border-collapse">
|
||||
<thead>
|
||||
<tr class="bg-surface-highlight border-b border-border-subtle">
|
||||
<th class="p-4 text-xs font-semibold uppercase tracking-wider text-text-secondary w-10">
|
||||
<input class="rounded bg-surface border-gray-300 text-primary focus:ring-primary focus:ring-offset-white" type="checkbox"/>
|
||||
</th>
|
||||
<th class="p-4 text-xs font-semibold uppercase tracking-wider text-text-secondary">Website / Tool</th>
|
||||
<th class="p-4 text-xs font-semibold uppercase tracking-wider text-text-secondary">Tags</th>
|
||||
<th class="p-4 text-xs font-semibold uppercase tracking-wider text-text-secondary">Status</th>
|
||||
<th class="p-4 text-xs font-semibold uppercase tracking-wider text-text-secondary text-right">Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="divide-y divide-border-subtle">
|
||||
<tr class="group hover:bg-surface-highlight/50 transition-colors">
|
||||
<td class="p-4">
|
||||
<input class="rounded bg-surface border-gray-300 text-primary focus:ring-primary focus:ring-offset-white" type="checkbox"/>
|
||||
</td>
|
||||
<td class="p-4">
|
||||
<div class="flex items-center gap-3">
|
||||
<div class="size-10 rounded-lg bg-cover bg-center shrink-0 border border-border-subtle" data-alt="Icon of Chat AI Tool" style='background-image: url("https://lh3.googleusercontent.com/aida-public/AB6AXuB_TTlnbkP1ygA1GNesXOhx_H9yup1gOKwumL9Qnh8VcPEUlwDRA89LRKhO8tyDtOtvtfrGh7CRSnP5xMOyb_GvJgQ-FQToY20cgHuTpVeI97COVlA4e2wOp04SH5ogk98S2SFF4jtSoS7RldXDdaafrysnsulA7_euKFIDf42NTPk_YwR4aglKFOeiQCQT-ITD6hCZjE8kqEfam8E6onBbJ14KZt2sE8gn0VP-pzzgAgPlr3htq5j6T26Tijk8V7SIFQGlB20GjSsb");'></div>
|
||||
<div>
|
||||
<h3 class="text-sm font-bold text-text-main group-hover:text-primary transition-colors">ChatGPT</h3>
|
||||
<p class="text-xs text-text-secondary truncate max-w-[200px]">openai.com/chatgpt</p>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td class="p-4">
|
||||
<div class="flex flex-wrap gap-1">
|
||||
<span class="px-2 py-0.5 rounded text-[10px] font-medium bg-surface-highlight text-text-secondary border border-border-subtle">LLM</span>
|
||||
<span class="px-2 py-0.5 rounded text-[10px] font-medium bg-surface-highlight text-text-secondary border border-border-subtle">Chatbot</span>
|
||||
</div>
|
||||
</td>
|
||||
<td class="p-4">
|
||||
<span class="inline-flex items-center px-2 py-1 rounded-full text-xs font-medium bg-green-50 text-green-700 border border-green-200">
|
||||
Active
|
||||
</span>
|
||||
</td>
|
||||
<td class="p-4 text-right">
|
||||
<div class="flex items-center justify-end gap-2 opacity-0 group-hover:opacity-100 transition-opacity">
|
||||
<button class="p-1.5 rounded-md hover:bg-primary-light hover:text-primary text-text-secondary transition-colors">
|
||||
<span class="material-symbols-outlined text-[18px]">edit</span>
|
||||
</button>
|
||||
<button class="p-1.5 rounded-md hover:bg-red-50 hover:text-red-600 text-text-secondary transition-colors">
|
||||
<span class="material-symbols-outlined text-[18px]">delete</span>
|
||||
</button>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="group hover:bg-surface-highlight/50 transition-colors">
|
||||
<td class="p-4">
|
||||
<input class="rounded bg-surface border-gray-300 text-primary focus:ring-primary focus:ring-offset-white" type="checkbox"/>
|
||||
</td>
|
||||
<td class="p-4">
|
||||
<div class="flex items-center gap-3">
|
||||
<div class="size-10 rounded-lg bg-cover bg-center shrink-0 border border-border-subtle" data-alt="Icon of Midjourney AI" style='background-image: url("https://lh3.googleusercontent.com/aida-public/AB6AXuA-IWUA_5FInPrqeTtZDcb85HR0X3jABxlDeG811Bd-_2x4ISEl8huiNp-kAG75fBmopT3dd5nSp1E0wCtOxXMFoP6HR9nrg4PfNDxjLao4ulneOOrJMgL4LdtuXgKxrQHm1WDoG4TviJ-lIYXzLVI9Yw-9mjDFihMurEOdJlarrMZekyEVrogy_jliQPo3oXvcemTUiJAZz9UCwq8LOEr09EqzU25kXCXx3Z3B0XvdM6VDUrQN8mKtKU1eU9CVwsLJfOKZ09CpJIwx");'></div>
|
||||
<div>
|
||||
<h3 class="text-sm font-bold text-text-main group-hover:text-primary transition-colors">Midjourney</h3>
|
||||
<p class="text-xs text-text-secondary truncate max-w-[200px]">midjourney.com</p>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td class="p-4">
|
||||
<div class="flex flex-wrap gap-1">
|
||||
<span class="px-2 py-0.5 rounded text-[10px] font-medium bg-surface-highlight text-text-secondary border border-border-subtle">Image Gen</span>
|
||||
<span class="px-2 py-0.5 rounded text-[10px] font-medium bg-surface-highlight text-text-secondary border border-border-subtle">Art</span>
|
||||
</div>
|
||||
</td>
|
||||
<td class="p-4">
|
||||
<span class="inline-flex items-center px-2 py-1 rounded-full text-xs font-medium bg-green-50 text-green-700 border border-green-200">
|
||||
Active
|
||||
</span>
|
||||
</td>
|
||||
<td class="p-4 text-right">
|
||||
<div class="flex items-center justify-end gap-2 opacity-0 group-hover:opacity-100 transition-opacity">
|
||||
<button class="p-1.5 rounded-md hover:bg-primary-light hover:text-primary text-text-secondary transition-colors">
|
||||
<span class="material-symbols-outlined text-[18px]">edit</span>
|
||||
</button>
|
||||
<button class="p-1.5 rounded-md hover:bg-red-50 hover:text-red-600 text-text-secondary transition-colors">
|
||||
<span class="material-symbols-outlined text-[18px]">delete</span>
|
||||
</button>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="group hover:bg-surface-highlight/50 transition-colors">
|
||||
<td class="p-4">
|
||||
<input class="rounded bg-surface border-gray-300 text-primary focus:ring-primary focus:ring-offset-white" type="checkbox"/>
|
||||
</td>
|
||||
<td class="p-4">
|
||||
<div class="flex items-center gap-3">
|
||||
<div class="size-10 rounded-lg bg-cover bg-center shrink-0 border border-border-subtle" data-alt="Icon of Copy AI Tool" style='background-image: url("https://lh3.googleusercontent.com/aida-public/AB6AXuB6XdW_8pAKAs4YPwwYM4bcub_ilJ2i6L7baANaKdNNmDP5k1TRngBOsHE0aTy6sBuBJG49alX-4PRkz55qTcxsrRy6_S8rqD5gCTWLquEDcjqfSai-s7GEdZcBCNJ6G5hEFJM7JFli5r5ETcPq2QBz_NQT0W94XYhzaYpZQVEX-su5ywWxAcrfOFrWrhwC5RCiZL0B7AinM4c1pRSfeDtDpVTzxTvx0MrDi6gBH-duuCUmbr0YjkqhyaZqpRpX-ZhW-JiJQ3sKPUCA");'></div>
|
||||
<div>
|
||||
<h3 class="text-sm font-bold text-text-main group-hover:text-primary transition-colors">Copy.ai</h3>
|
||||
<p class="text-xs text-text-secondary truncate max-w-[200px]">copy.ai</p>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td class="p-4">
|
||||
<div class="flex flex-wrap gap-1">
|
||||
<span class="px-2 py-0.5 rounded text-[10px] font-medium bg-surface-highlight text-text-secondary border border-border-subtle">Writing</span>
|
||||
<span class="px-2 py-0.5 rounded text-[10px] font-medium bg-surface-highlight text-text-secondary border border-border-subtle">Marketing</span>
|
||||
</div>
|
||||
</td>
|
||||
<td class="p-4">
|
||||
<span class="inline-flex items-center px-2 py-1 rounded-full text-xs font-medium bg-yellow-50 text-yellow-700 border border-yellow-200">
|
||||
Pending
|
||||
</span>
|
||||
</td>
|
||||
<td class="p-4 text-right">
|
||||
<div class="flex items-center justify-end gap-2 opacity-0 group-hover:opacity-100 transition-opacity">
|
||||
<button class="p-1.5 rounded-md hover:bg-primary-light hover:text-primary text-text-secondary transition-colors">
|
||||
<span class="material-symbols-outlined text-[18px]">edit</span>
|
||||
</button>
|
||||
<button class="p-1.5 rounded-md hover:bg-red-50 hover:text-red-600 text-text-secondary transition-colors">
|
||||
<span class="material-symbols-outlined text-[18px]">delete</span>
|
||||
</button>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="flex items-center justify-between px-4 py-3 border-t border-border-subtle bg-surface">
|
||||
<div class="flex flex-1 justify-between sm:hidden">
|
||||
<a class="relative inline-flex items-center rounded-md border border-border-subtle bg-surface px-4 py-2 text-sm font-medium text-text-secondary hover:bg-surface-highlight hover:text-text-main" href="#">Previous</a>
|
||||
<a class="relative ml-3 inline-flex items-center rounded-md border border-border-subtle bg-surface px-4 py-2 text-sm font-medium text-text-secondary hover:bg-surface-highlight hover:text-text-main" href="#">Next</a>
|
||||
</div>
|
||||
<div class="hidden sm:flex sm:flex-1 sm:items-center sm:justify-end">
|
||||
<nav aria-label="Pagination" class="isolate inline-flex -space-x-px rounded-md shadow-sm">
|
||||
<a class="relative inline-flex items-center rounded-l-md px-2 py-2 text-text-secondary ring-1 ring-inset ring-border-subtle hover:bg-surface-highlight focus:z-20 focus:outline-offset-0" href="#">
|
||||
<span class="material-symbols-outlined text-[20px]">chevron_left</span>
|
||||
</a>
|
||||
<a aria-current="page" class="relative z-10 inline-flex items-center bg-primary px-4 py-2 text-sm font-semibold text-white focus:z-20 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary" href="#">1</a>
|
||||
<a class="relative inline-flex items-center px-4 py-2 text-sm font-semibold text-text-secondary ring-1 ring-inset ring-border-subtle hover:bg-surface-highlight focus:z-20 focus:outline-offset-0" href="#">2</a>
|
||||
<a class="relative inline-flex items-center px-4 py-2 text-sm font-semibold text-text-secondary ring-1 ring-inset ring-border-subtle hover:bg-surface-highlight focus:z-20 focus:outline-offset-0" href="#">3</a>
|
||||
<a class="relative inline-flex items-center rounded-r-md px-2 py-2 text-text-secondary ring-1 ring-inset ring-border-subtle hover:bg-surface-highlight focus:z-20 focus:outline-offset-0" href="#">
|
||||
<span class="material-symbols-outlined text-[20px]">chevron_right</span>
|
||||
</a>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="xl:col-span-1">
|
||||
<div class="bg-surface border border-border-subtle rounded-xl shadow-lg shadow-slate-200/50 sticky top-8">
|
||||
<div class="px-6 py-4 border-b border-border-subtle flex justify-between items-center bg-surface-highlight/30 rounded-t-xl">
|
||||
<h2 class="text-lg font-bold text-text-main">Add New Website</h2>
|
||||
<span class="material-symbols-outlined text-text-secondary cursor-pointer hover:text-text-main">close</span>
|
||||
</div>
|
||||
<div class="p-6 flex flex-col gap-6">
|
||||
<div class="flex flex-col gap-3">
|
||||
<label class="text-sm font-medium text-text-main">Website URL <span class="text-primary">*</span></label>
|
||||
<div class="flex flex-col gap-2">
|
||||
<div class="relative">
|
||||
<input class="w-full bg-surface border border-border-subtle rounded-lg py-2.5 px-3 text-sm text-text-main placeholder-text-secondary focus:ring-1 focus:ring-primary focus:border-primary transition-all shadow-sm" placeholder="https://example.com" type="url"/>
|
||||
<div class="absolute inset-y-0 right-3 flex items-center">
|
||||
<span class="material-symbols-outlined text-green-500 text-[18px]" style="display:none;">check_circle</span>
|
||||
</div>
|
||||
</div>
|
||||
<button class="flex items-center justify-center gap-2 w-full py-2 px-3 rounded-lg border border-primary/20 bg-primary-light/50 hover:bg-primary-light text-primary text-sm font-medium transition-colors group" type="button">
|
||||
<span class="material-symbols-outlined text-[18px] group-hover:animate-spin">autorenew</span>
|
||||
Auto-fetch Website Info
|
||||
</button>
|
||||
<p class="text-xs text-text-secondary mt-1">
|
||||
Our AI will attempt to scrape metadata and descriptions automatically.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex flex-col gap-3">
|
||||
<label class="text-sm font-medium text-text-main">Tool Name</label>
|
||||
<input class="w-full bg-surface border border-border-subtle rounded-lg py-2.5 px-3 text-sm text-text-main placeholder-text-secondary focus:ring-1 focus:ring-primary focus:border-primary shadow-sm" placeholder="e.g. ChatGPT" type="text"/>
|
||||
</div>
|
||||
<div class="flex flex-col gap-3">
|
||||
<label class="text-sm font-medium text-text-main">Short Description</label>
|
||||
<textarea class="w-full bg-surface border border-border-subtle rounded-lg py-2.5 px-3 text-sm text-text-main placeholder-text-secondary focus:ring-1 focus:ring-primary focus:border-primary resize-none shadow-sm" placeholder="Enter a brief description of the AI tool..." rows="4"></textarea>
|
||||
</div>
|
||||
<div class="flex flex-col gap-3">
|
||||
<label class="text-sm font-medium text-text-main">Tags</label>
|
||||
<div class="p-3 bg-surface border border-border-subtle rounded-lg min-h-[44px] flex flex-wrap gap-2 shadow-sm">
|
||||
<span class="inline-flex items-center gap-1 px-2 py-1 rounded bg-surface-highlight text-xs text-text-main border border-border-subtle">
|
||||
Productivity
|
||||
<span class="material-symbols-outlined text-[14px] cursor-pointer hover:text-red-500 text-text-secondary">close</span>
|
||||
</span>
|
||||
<input class="bg-transparent border-none p-0 text-sm text-text-main placeholder-text-secondary focus:ring-0 w-24" placeholder="+ Add tag" type="text"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-center justify-between py-2">
|
||||
<label class="text-sm font-medium text-text-main">Active Status</label>
|
||||
<button class="w-11 h-6 bg-primary rounded-full relative cursor-pointer shadow-inner">
|
||||
<span class="absolute top-1 left-6 size-4 bg-white rounded-full shadow-sm transition-all"></span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="px-6 py-4 border-t border-border-subtle flex gap-3 bg-surface-highlight/30 rounded-b-xl">
|
||||
<button class="flex-1 py-2.5 rounded-lg bg-primary hover:bg-primary-hover text-white text-sm font-bold shadow-lg shadow-primary/20 transition-all">
|
||||
Save Website
|
||||
</button>
|
||||
<button class="px-4 py-2.5 rounded-lg border border-border-subtle bg-surface hover:bg-surface-highlight text-text-main text-sm font-medium transition-colors shadow-sm">
|
||||
Cancel
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
</body></html>
|
||||
BIN
stitch_ai_tool_detail_page/admin_management_interface/screen.png
Normal file
|
After Width: | Height: | Size: 221 KiB |
351
stitch_ai_tool_detail_page/ai_tool_detail_page/code.html
Normal file
@@ -0,0 +1,351 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en"><head>
|
||||
<meta charset="utf-8"/>
|
||||
<meta content="width=device-width, initial-scale=1.0" name="viewport"/>
|
||||
<title>AI Tool Detail Page (Light Theme)</title>
|
||||
<link href="https://fonts.googleapis.com" rel="preconnect"/>
|
||||
<link crossorigin="" href="https://fonts.gstatic.com" rel="preconnect"/>
|
||||
<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"/>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:wght,FILL@100..700,0..1&display=swap" rel="stylesheet"/>
|
||||
<script src="https://cdn.tailwindcss.com?plugins=forms,container-queries"></script>
|
||||
<script id="tailwind-config">
|
||||
tailwind.config = {
|
||||
theme: {
|
||||
extend: {
|
||||
colors: {
|
||||
"primary": "#0ea5e9", // Updated to sky-500 for better visibility on light
|
||||
"primary-dark": "#0284c7", // Sky-600
|
||||
"secondary": "#8b5cf6", // Violet-500
|
||||
"background": "#f8fafc", // Slate-50 - Light background
|
||||
"surface": "#ffffff", // White cards
|
||||
"surface-highlight": "#f1f5f9", // Slate-100 for inputs/tags
|
||||
"border": "#e2e8f0", // Slate-200
|
||||
"text-main": "#0f172a", // Slate-900
|
||||
"text-muted": "#64748b", // Slate-500
|
||||
"text-on-dark": "#ffffff",
|
||||
},
|
||||
fontFamily: {
|
||||
"display": ["Space Grotesk", "sans-serif"],
|
||||
"body": ["Noto Sans", "sans-serif"]
|
||||
},
|
||||
borderRadius: {"DEFAULT": "0.25rem", "lg": "0.5rem", "xl": "0.75rem", "2xl": "1rem", "full": "9999px"},
|
||||
boxShadow: {
|
||||
"soft": "0 4px 6px -1px rgba(0, 0, 0, 0.05), 0 2px 4px -1px rgba(0, 0, 0, 0.03)",
|
||||
"card": "0 0 0 1px rgba(226, 232, 240, 1), 0 2px 8px rgba(0, 0, 0, 0.05)",
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
<style>
|
||||
body {
|
||||
font-family: 'Space Grotesk', sans-serif;
|
||||
}
|
||||
.glass-card {
|
||||
background: rgba(255, 255, 255, 0.8);
|
||||
backdrop-filter: blur(12px);
|
||||
border: 1px solid rgba(226, 232, 240, 0.6);
|
||||
box-shadow: 0 10px 30px -5px rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
.glass-header {
|
||||
background: rgba(255, 255, 255, 0.85);
|
||||
backdrop-filter: blur(8px);
|
||||
border-bottom: 1px solid rgba(226, 232, 240, 1);
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body class="bg-background text-text-main min-h-screen flex flex-col selection:bg-primary/20 selection:text-primary-dark">
|
||||
<div class="w-full glass-header sticky top-0 z-50">
|
||||
<div class="max-w-[1280px] mx-auto px-4 sm:px-6 lg:px-8">
|
||||
<header class="flex items-center justify-between h-16">
|
||||
<div class="flex items-center gap-8">
|
||||
<div class="flex items-center gap-3">
|
||||
<div class="size-8 text-primary">
|
||||
<span class="material-symbols-outlined text-3xl">token</span>
|
||||
</div>
|
||||
<h2 class="text-text-main text-xl font-bold tracking-tight">AI Discovery</h2>
|
||||
</div>
|
||||
<div class="hidden md:flex items-center gap-6">
|
||||
<a class="text-text-muted hover:text-primary text-sm font-medium transition-colors" href="#">Categories</a>
|
||||
<a class="text-text-muted hover:text-primary text-sm font-medium transition-colors" href="#">Submit Tool</a>
|
||||
<a class="text-text-muted hover:text-primary text-sm font-medium transition-colors" href="#">Blog</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-center gap-4">
|
||||
<div class="hidden sm:flex relative group">
|
||||
<div class="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
|
||||
<span class="material-symbols-outlined text-text-muted text-[20px]">search</span>
|
||||
</div>
|
||||
<input class="bg-surface-highlight hover:bg-white focus:bg-white text-text-main text-sm rounded-lg pl-10 pr-4 py-2 w-48 focus:w-64 transition-all outline-none ring-0 border border-transparent focus:border-primary/50 placeholder:text-text-muted/70 shadow-sm" placeholder="Search tools..."/>
|
||||
</div>
|
||||
<button class="flex items-center justify-center rounded-lg h-9 px-4 bg-primary hover:bg-primary-dark text-white text-sm font-bold transition-colors shadow-sm hover:shadow-md">
|
||||
Log In
|
||||
</button>
|
||||
</div>
|
||||
</header>
|
||||
</div>
|
||||
</div>
|
||||
<main class="flex-grow w-full max-w-[1280px] mx-auto px-4 sm:px-6 lg:px-8 py-8">
|
||||
<div class="mb-8">
|
||||
<button class="group flex items-center gap-2 text-text-muted 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>Back to Home</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="grid grid-cols-1 lg:grid-cols-12 gap-8 lg:gap-12">
|
||||
<div class="lg:col-span-8 flex flex-col gap-8">
|
||||
<div class="flex flex-col sm:flex-row gap-6 items-start">
|
||||
<div class="relative shrink-0">
|
||||
<div class="absolute -inset-2 bg-gradient-to-br from-primary/30 via-purple-300/30 to-secondary/30 rounded-3xl opacity-60 blur-xl"></div>
|
||||
<div class="relative size-[120px] rounded-xl bg-surface flex items-center justify-center overflow-hidden border border-border bg-cover bg-center shadow-soft" data-alt="Abstract neural network pattern in deep blue and purple" style="background-image: url('https://lh3.googleusercontent.com/aida-public/AB6AXuAFm-_1orioA8YwgKaNGCzxomQeon7z13YmJCs2VpIoXP6J2a5kLgu_mC9IFA-cRA5s6yNaDPiWRxhF8tTX4FFOTbngaHydfCmAMNxU2EuZ2burR-hLOggJYl24v1Ry7GaNu6ETcYui4SIfvWqNs43vwA4vEmoogV3TxVzuDRDHsy5nj9ab4TnW4c5mVgqHPttLIF1NKTm6rtEcPnWW1jtCihIAAqZVnst9o0Z-IyAYGJoPjNajRdJZGR5NDfiBf-tdr79Bo42KY_DX');">
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex flex-col gap-3 flex-1">
|
||||
<div>
|
||||
<h1 class="text-4xl font-bold text-text-main tracking-tight font-display mb-1">NeuroGen AI</h1>
|
||||
<a class="text-primary hover:text-primary-dark hover:underline text-base font-medium flex items-center gap-1" href="#">
|
||||
www.neurogen.ai
|
||||
<span class="material-symbols-outlined text-[16px]">open_in_new</span>
|
||||
</a>
|
||||
</div>
|
||||
<div class="flex flex-wrap gap-2">
|
||||
<span class="inline-flex items-center px-3 py-1 rounded-full text-xs font-medium bg-surface-highlight text-text-muted border border-border hover:border-primary/30 transition-colors cursor-default">Generative AI</span>
|
||||
<span class="inline-flex items-center px-3 py-1 rounded-full text-xs font-medium bg-surface-highlight text-text-muted border border-border hover:border-primary/30 transition-colors cursor-default">Copywriting</span>
|
||||
<span class="inline-flex items-center px-3 py-1 rounded-full text-xs font-medium bg-surface-highlight text-text-muted border border-border hover:border-primary/30 transition-colors cursor-default">Productivity</span>
|
||||
<span class="inline-flex items-center px-3 py-1 rounded-full text-xs font-medium bg-purple-50 text-secondary border border-purple-100">Free Trial</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-6 mt-1 text-text-muted text-sm">
|
||||
<div class="flex items-center gap-2" title="Total Views">
|
||||
<span class="material-symbols-outlined text-[18px]">visibility</span>
|
||||
<span>12,450 Views</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-2" title="Date Added">
|
||||
<span class="material-symbols-outlined text-[18px]">calendar_today</span>
|
||||
<span>Added Oct 24, 2023</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<hr class="border-border"/>
|
||||
<div class="space-y-10">
|
||||
<section>
|
||||
<h3 class="text-xl font-bold text-text-main mb-4 flex items-center gap-2">
|
||||
<span class="material-symbols-outlined text-primary">info</span>
|
||||
Product Overview
|
||||
</h3>
|
||||
<p class="text-text-muted leading-relaxed text-lg font-body">
|
||||
NeuroGen AI is an advanced copywriting assistant designed to help marketers, writers, and businesses generate high-converting content in seconds. By leveraging state-of-the-art natural language processing models, it understands context, tone, and brand voice to deliver tailored outputs.
|
||||
</p>
|
||||
</section>
|
||||
<div class="rounded-xl overflow-hidden border border-border relative group aspect-video w-full bg-surface shadow-md">
|
||||
<div class="absolute inset-0 bg-cover bg-center" data-alt="Dashboard interface of NeuroGen AI showing analytics and text editor" style="background-image: url('https://lh3.googleusercontent.com/aida-public/AB6AXuCtwM-4OkGIPG4sQqABpRcLvOgKvLzuyVlQJUVcU8SqFsVod_jck_vFm-q7UX70vUaaF7UPEa--rkf18dMi5_qYcXYsjTzd5ehM3ga_OoIr1r6wnRTnRxKcrUUC5USP72vdsFJkRFeKmH6epH1Avi5tglJ732VTvoNiuys499jctwZ67d4Yu8Slfn0EO4Ny8zIjzPRGLer8lMaEeZPMagNbgn4asMLvmDtTep3mgPzwTscfzoOFVChGqNak0ZmLyOiVY7vKIapdHyoz');">
|
||||
</div>
|
||||
<div class="absolute inset-0 bg-gradient-to-t from-black/80 via-transparent to-transparent opacity-60"></div>
|
||||
<div class="absolute bottom-4 left-4">
|
||||
<p class="text-white font-medium text-sm bg-black/40 backdrop-blur-md px-3 py-1 rounded-lg border border-white/10">Dashboard Interface</p>
|
||||
</div>
|
||||
</div>
|
||||
<section>
|
||||
<h3 class="text-xl font-bold text-text-main mb-4">Detailed Description</h3>
|
||||
<div class="text-text-muted space-y-4 leading-relaxed font-body">
|
||||
<p>
|
||||
Writing high-quality content consistently is a challenge for modern businesses. NeuroGen AI bridges the gap between human creativity and machine speed. Unlike generic text generators, NeuroGen allows users to fine-tune specific parameters such as emotional resonance, sentence structure complexity, and SEO keyword density.
|
||||
</p>
|
||||
<p>
|
||||
The platform includes specialized templates for:
|
||||
</p>
|
||||
<ul class="list-disc pl-5 space-y-2 text-text-muted marker:text-primary">
|
||||
<li>Social media captions (Instagram, LinkedIn, Twitter)</li>
|
||||
<li>Long-form blog posts with automatic formatting</li>
|
||||
<li>Email marketing sequences</li>
|
||||
<li>Ad copy variants for A/B testing</li>
|
||||
</ul>
|
||||
<p>
|
||||
Security is paramount; NeuroGen ensures that your proprietary data is never used to train public models. Enterprise-grade encryption and team collaboration features make it a suitable choice for large organizations looking to scale their content operations.
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
<section>
|
||||
<h3 class="text-xl font-bold text-text-main mb-4 flex items-center gap-2">
|
||||
<span class="material-symbols-outlined text-secondary">newspaper</span>
|
||||
Related News
|
||||
</h3>
|
||||
<div class="grid gap-4">
|
||||
<a class="group block p-5 rounded-xl border border-border bg-surface hover:shadow-soft hover:border-primary/30 transition-all" href="#">
|
||||
<div class="flex justify-between items-start mb-2">
|
||||
<span class="inline-flex items-center px-2 py-0.5 rounded text-[10px] font-bold uppercase tracking-wider bg-primary/10 text-primary-dark">Product Update</span>
|
||||
<span class="text-xs text-text-muted">2 days ago</span>
|
||||
</div>
|
||||
<h4 class="text-text-main font-bold text-base group-hover:text-primary transition-colors mb-2">NeuroGen 2.0 Released: Now with Multimodal capabilities</h4>
|
||||
<p class="text-sm text-text-muted line-clamp-2">The latest version introduces image analysis and generation alongside text, marking a significant step forward for content creators looking for all-in-one solutions.</p>
|
||||
</a>
|
||||
<a class="group block p-5 rounded-xl border border-border bg-surface hover:shadow-soft hover:border-primary/30 transition-all" href="#">
|
||||
<div class="flex justify-between items-start mb-2">
|
||||
<span class="inline-flex items-center px-2 py-0.5 rounded text-[10px] font-bold uppercase tracking-wider bg-purple-50 text-secondary">Industry News</span>
|
||||
<span class="text-xs text-text-muted">Oct 12, 2023</span>
|
||||
</div>
|
||||
<h4 class="text-text-main font-bold text-base group-hover:text-primary transition-colors mb-2">AI Copywriting Market Analysis Q3 2023</h4>
|
||||
<p class="text-sm text-text-muted line-clamp-2">NeuroGen AI has been named a "High Performer" in the latest G2 Grid Report for AI Writing Assistants, recognized for its ease of use and ROI.</p>
|
||||
</a>
|
||||
</div>
|
||||
</section>
|
||||
<section>
|
||||
<h3 class="text-xl font-bold text-text-main mb-4 flex items-center gap-2">
|
||||
<span class="material-symbols-outlined text-primary">auto_awesome</span>
|
||||
Similar Recommendations
|
||||
</h3>
|
||||
<div class="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
||||
<a class="flex flex-col p-4 rounded-xl border border-border bg-surface hover:border-primary/50 hover:shadow-soft transition-all group h-full" href="#">
|
||||
<div class="flex items-start justify-between mb-3">
|
||||
<div class="size-10 rounded-lg bg-gradient-to-br from-blue-500 to-indigo-500 flex items-center justify-center shadow-lg shadow-blue-500/20">
|
||||
<span class="text-white font-bold text-lg">J</span>
|
||||
</div>
|
||||
<div class="size-8 rounded-full bg-surface-highlight flex items-center justify-center text-text-muted group-hover:text-white group-hover:bg-primary group-hover:scale-110 transition-all">
|
||||
<span class="material-symbols-outlined text-[18px]">arrow_forward</span>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<h4 class="text-text-main font-bold mb-1">Jasper AI</h4>
|
||||
<p class="text-xs text-text-muted line-clamp-2 mb-3">Enterprise-grade AI content platform for marketing teams and creators.</p>
|
||||
<div class="flex gap-2">
|
||||
<span class="px-2 py-1 rounded bg-surface-highlight text-[10px] text-text-muted font-medium border border-border">Writing</span>
|
||||
<span class="px-2 py-1 rounded bg-surface-highlight text-[10px] text-text-muted font-medium border border-border">Marketing</span>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
<a class="flex flex-col p-4 rounded-xl border border-border bg-surface hover:border-primary/50 hover:shadow-soft transition-all group h-full" href="#">
|
||||
<div class="flex items-start justify-between mb-3">
|
||||
<div class="size-10 rounded-lg bg-gradient-to-br from-emerald-400 to-teal-500 flex items-center justify-center shadow-lg shadow-emerald-500/20">
|
||||
<span class="text-white font-bold text-lg">C</span>
|
||||
</div>
|
||||
<div class="size-8 rounded-full bg-surface-highlight flex items-center justify-center text-text-muted group-hover:text-white group-hover:bg-primary group-hover:scale-110 transition-all">
|
||||
<span class="material-symbols-outlined text-[18px]">arrow_forward</span>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<h4 class="text-text-main font-bold mb-1">Copy.ai</h4>
|
||||
<p class="text-xs text-text-muted line-clamp-2 mb-3">Generates high-quality copy for your business in seconds.</p>
|
||||
<div class="flex gap-2">
|
||||
<span class="px-2 py-1 rounded bg-surface-highlight text-[10px] text-text-muted font-medium border border-border">Free Plan</span>
|
||||
<span class="px-2 py-1 rounded bg-surface-highlight text-[10px] text-text-muted font-medium border border-border">Social</span>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
<a class="flex flex-col p-4 rounded-xl border border-border bg-surface hover:border-primary/50 hover:shadow-soft transition-all group h-full" href="#">
|
||||
<div class="flex items-start justify-between mb-3">
|
||||
<div class="size-10 rounded-lg bg-gradient-to-br from-orange-400 to-red-500 flex items-center justify-center shadow-lg shadow-orange-500/20">
|
||||
<span class="text-white font-bold text-lg">W</span>
|
||||
</div>
|
||||
<div class="size-8 rounded-full bg-surface-highlight flex items-center justify-center text-text-muted group-hover:text-white group-hover:bg-primary group-hover:scale-110 transition-all">
|
||||
<span class="material-symbols-outlined text-[18px]">arrow_forward</span>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<h4 class="text-text-main font-bold mb-1">Writesonic</h4>
|
||||
<p class="text-xs text-text-muted line-clamp-2 mb-3">AI writer that creates SEO-friendly content for blogs and websites.</p>
|
||||
<div class="flex gap-2">
|
||||
<span class="px-2 py-1 rounded bg-surface-highlight text-[10px] text-text-muted font-medium border border-border">SEO</span>
|
||||
<span class="px-2 py-1 rounded bg-surface-highlight text-[10px] text-text-muted font-medium border border-border">Blogs</span>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
<a class="flex flex-col p-4 rounded-xl border border-border bg-surface hover:border-primary/50 hover:shadow-soft transition-all group h-full" href="#">
|
||||
<div class="flex items-start justify-between mb-3">
|
||||
<div class="size-10 rounded-lg bg-gradient-to-br from-purple-400 to-fuchsia-500 flex items-center justify-center shadow-lg shadow-purple-500/20">
|
||||
<span class="text-white font-bold text-lg">R</span>
|
||||
</div>
|
||||
<div class="size-8 rounded-full bg-surface-highlight flex items-center justify-center text-text-muted group-hover:text-white group-hover:bg-primary group-hover:scale-110 transition-all">
|
||||
<span class="material-symbols-outlined text-[18px]">arrow_forward</span>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<h4 class="text-text-main font-bold mb-1">Rytr</h4>
|
||||
<p class="text-xs text-text-muted line-clamp-2 mb-3">A better, faster way to write profile bios, facebook ads and more.</p>
|
||||
<div class="flex gap-2">
|
||||
<span class="px-2 py-1 rounded bg-surface-highlight text-[10px] text-text-muted font-medium border border-border">Emails</span>
|
||||
<span class="px-2 py-1 rounded bg-surface-highlight text-[10px] text-text-muted font-medium border border-border">Ads</span>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
<div class="lg:col-span-4 space-y-6">
|
||||
<div class="glass-card rounded-2xl p-6 flex flex-col gap-4 sticky top-24">
|
||||
<div class="flex items-center justify-between mb-2">
|
||||
<span class="text-text-main font-bold text-lg">Try it now</span>
|
||||
<span class="px-2 py-1 rounded bg-green-50 text-green-600 text-xs font-bold border border-green-200">ONLINE</span>
|
||||
</div>
|
||||
<a class="group relative flex items-center justify-center w-full overflow-hidden rounded-xl bg-gradient-to-r from-primary via-primary-dark to-secondary p-[1px] focus:outline-none focus:ring-2 focus:ring-primary focus:ring-offset-2 transition-all hover:scale-[1.02] shadow-sm hover:shadow-md" href="#">
|
||||
<div class="relative flex h-12 w-full items-center justify-center rounded-xl bg-surface group-hover:bg-transparent px-8 py-1 transition-colors">
|
||||
<span class="flex items-center gap-2 text-primary group-hover:text-white font-bold text-base uppercase tracking-wide transition-colors">
|
||||
Visit Website
|
||||
<span class="material-symbols-outlined text-[20px] font-bold">arrow_outward</span>
|
||||
</span>
|
||||
</div>
|
||||
</a>
|
||||
<p class="text-xs text-center text-text-muted">
|
||||
Opens in a new tab • <span class="text-text-main font-medium">neurogen.ai</span>
|
||||
</p>
|
||||
</div>
|
||||
<div class="rounded-2xl border border-border bg-surface p-6 shadow-card">
|
||||
<h3 class="text-lg font-bold text-text-main mb-5 flex items-center gap-2">
|
||||
<span class="material-symbols-outlined text-secondary">verified</span>
|
||||
Main Features
|
||||
</h3>
|
||||
<ul class="space-y-4">
|
||||
<li class="flex items-start gap-3">
|
||||
<div class="mt-0.5 rounded-full bg-primary/10 p-1">
|
||||
<span class="material-symbols-outlined text-primary text-[16px] block">check</span>
|
||||
</div>
|
||||
<div>
|
||||
<p class="text-sm font-bold text-text-main">Contextual Awareness</p>
|
||||
<p class="text-xs text-text-muted mt-0.5">Understands previous inputs to maintain thread continuity.</p>
|
||||
</div>
|
||||
</li>
|
||||
<li class="flex items-start gap-3">
|
||||
<div class="mt-0.5 rounded-full bg-primary/10 p-1">
|
||||
<span class="material-symbols-outlined text-primary text-[16px] block">check</span>
|
||||
</div>
|
||||
<div>
|
||||
<p class="text-sm font-bold text-text-main">Multi-language Support</p>
|
||||
<p class="text-xs text-text-muted mt-0.5">Generate content in over 25 languages natively.</p>
|
||||
</div>
|
||||
</li>
|
||||
<li class="flex items-start gap-3">
|
||||
<div class="mt-0.5 rounded-full bg-primary/10 p-1">
|
||||
<span class="material-symbols-outlined text-primary text-[16px] block">check</span>
|
||||
</div>
|
||||
<div>
|
||||
<p class="text-sm font-bold text-text-main">SEO Optimization</p>
|
||||
<p class="text-xs text-text-muted mt-0.5">Built-in keyword analyzer and suggestion tool.</p>
|
||||
</div>
|
||||
</li>
|
||||
<li class="flex items-start gap-3">
|
||||
<div class="mt-0.5 rounded-full bg-primary/10 p-1">
|
||||
<span class="material-symbols-outlined text-primary text-[16px] block">check</span>
|
||||
</div>
|
||||
<div>
|
||||
<p class="text-sm font-bold text-text-main">Export to CMS</p>
|
||||
<p class="text-xs text-text-muted mt-0.5">Direct integration with WordPress and Ghost.</p>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="flex gap-4">
|
||||
<button class="flex-1 py-3 rounded-lg border border-border bg-surface text-text-muted hover:text-text-main hover:bg-surface-highlight hover:border-gray-300 transition-colors text-sm font-medium flex items-center justify-center gap-2 shadow-sm">
|
||||
<span class="material-symbols-outlined text-[18px]">share</span>
|
||||
Share
|
||||
</button>
|
||||
<button class="flex-1 py-3 rounded-lg border border-border bg-surface text-text-muted hover:text-red-600 hover:border-red-200 hover:bg-red-50 transition-colors text-sm font-medium flex items-center justify-center gap-2 shadow-sm">
|
||||
<span class="material-symbols-outlined text-[18px]">flag</span>
|
||||
Report
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="h-20"></div>
|
||||
</main>
|
||||
|
||||
</body></html>
|
||||
BIN
stitch_ai_tool_detail_page/ai_tool_detail_page/screen.png
Normal file
|
After Width: | Height: | Size: 389 KiB |
357
stitch_ai_tool_detail_page/ai_tool_home_page/code.html
Normal file
@@ -0,0 +1,357 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en"><head>
|
||||
<meta charset="utf-8"/>
|
||||
<meta content="width=device-width, initial-scale=1.0" name="viewport"/>
|
||||
<title>ZJPB - AI Tool Discovery</title>
|
||||
<link href="https://fonts.googleapis.com" rel="preconnect"/>
|
||||
<link crossorigin="" href="https://fonts.gstatic.com" rel="preconnect"/>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@300;400;500;600;700&display=swap" rel="stylesheet"/>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Noto+Sans:wght@400;500;700&display=swap" rel="stylesheet"/>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:wght,FILL@100..700,0..1&display=swap" rel="stylesheet"/>
|
||||
<script src="https://cdn.tailwindcss.com?plugins=forms,container-queries"></script>
|
||||
<script id="tailwind-config">
|
||||
tailwind.config = {
|
||||
darkMode: "class",
|
||||
theme: {
|
||||
extend: {
|
||||
colors: {
|
||||
primary: "#0ea5e9", // Sky Blue - Fresh and Techy
|
||||
secondary: "#8b5cf6", // Violet - Softened purple
|
||||
"page-bg": "#f8fafc", // Slate 50 - Very light grey/white
|
||||
"surface": "#ffffff", // Pure white
|
||||
"border-color": "#e2e8f0", // Slate 200 - Subtle borders
|
||||
"text-main": "#0f172a", // Slate 900 - High contrast text
|
||||
"text-muted": "#64748b", // Slate 500 - Secondary text
|
||||
},
|
||||
fontFamily: {
|
||||
"display": ["Space Grotesk", "sans-serif"],
|
||||
"body": ["Noto Sans", "sans-serif"],
|
||||
},
|
||||
backgroundImage: {
|
||||
'gradient-tech': 'linear-gradient(135deg, #0ea5e9 0%, #8b5cf6 100%)',
|
||||
'gradient-text': 'linear-gradient(to right, #0ea5e9, #8b5cf6)',
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
<style>
|
||||
body {
|
||||
font-family: 'Space Grotesk', sans-serif;
|
||||
}
|
||||
.text-gradient {
|
||||
background: linear-gradient(to right, #0ea5e9, #a855f7);
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
}
|
||||
.card-hover:hover {
|
||||
transform: translateY(-4px);
|
||||
box-shadow: 0 10px 30px -10px rgba(14, 165, 233, 0.15);
|
||||
border-color: #0ea5e9;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body class="bg-page-bg text-text-main antialiased selection:bg-primary selection:text-white">
|
||||
<div class="relative flex min-h-screen w-full flex-col overflow-x-hidden">
|
||||
<div class="fixed top-0 left-0 w-full h-96 bg-primary/10 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>
|
||||
<header class="sticky top-0 z-50 w-full border-b border-solid border-border-color bg-surface/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">
|
||||
<a class="flex items-center gap-3 text-text-main hover:opacity-80 transition-opacity" href="#">
|
||||
<div class="flex items-center justify-center size-8 rounded-lg bg-gradient-tech text-white">
|
||||
<span class="material-symbols-outlined" style="font-size: 20px; font-weight: 700;">bolt</span>
|
||||
</div>
|
||||
<h2 class="text-text-main text-lg font-bold leading-tight tracking-tight">ZJPB</h2>
|
||||
</a>
|
||||
<nav class="hidden md:flex items-center gap-8">
|
||||
<a class="text-text-muted hover:text-primary text-sm font-medium transition-colors" href="#">Home</a>
|
||||
<a class="text-text-muted hover:text-primary text-sm font-medium transition-colors" href="#">Categories</a>
|
||||
<a class="text-text-muted hover:text-primary text-sm font-medium transition-colors" href="#">Community</a>
|
||||
<a class="text-text-muted hover:text-primary text-sm font-medium transition-colors" href="#">About</a>
|
||||
</nav>
|
||||
</div>
|
||||
<div class="flex items-center gap-4">
|
||||
<div class="hidden lg:flex items-center bg-slate-100 border border-transparent rounded-full h-10 px-4 w-64 focus-within:border-primary focus-within:bg-white transition-all">
|
||||
<span class="material-symbols-outlined text-slate-400" style="font-size: 20px;">search</span>
|
||||
<input class="bg-transparent border-none text-text-main text-sm placeholder-slate-400 focus:ring-0 w-full ml-2" placeholder="Search AI tools..." type="text"/>
|
||||
</div>
|
||||
<button class="hidden sm:flex h-9 px-4 items-center justify-center rounded-lg text-sm font-bold text-text-muted hover:bg-slate-100 transition-colors">
|
||||
Log In
|
||||
</button>
|
||||
<button class="flex h-9 px-5 items-center justify-center rounded-lg bg-primary hover:bg-primary/90 text-white text-sm font-bold transition-colors shadow-[0_4px_14px_rgba(14,165,233,0.3)]">
|
||||
Sign Up
|
||||
</button>
|
||||
<button class="md:hidden text-text-main">
|
||||
<span class="material-symbols-outlined">menu</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
<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">
|
||||
<div class="flex flex-col md:flex-row items-start md:items-end justify-between gap-6 pb-6 border-b border-border-color">
|
||||
<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-text-main mb-2">
|
||||
ZJPB - <span class="text-gradient">焦提示词</span>
|
||||
</h1>
|
||||
<p class="text-text-muted text-lg md:text-xl font-light">
|
||||
发现最新最好用的AI工具和产品. Discover the best AI tools tailored for your workflow.
|
||||
</p>
|
||||
</div>
|
||||
<button class="flex items-center gap-2 bg-surface border border-border-color hover:border-primary text-text-main px-5 py-2.5 rounded-lg transition-all group whitespace-nowrap shadow-sm hover:shadow-md">
|
||||
<span class="material-symbols-outlined text-primary group-hover:scale-110 transition-transform">add_circle</span>
|
||||
<span class="font-bold text-sm">Submit a Tool</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="w-full overflow-x-auto pb-2 scrollbar-hide">
|
||||
<div class="flex gap-3 min-w-max">
|
||||
<button class="flex items-center h-9 px-5 rounded-full bg-primary text-white font-bold text-sm shadow-[0_2px_10px_rgba(14,165,233,0.3)]">
|
||||
All Tools
|
||||
</button>
|
||||
<button class="flex items-center gap-2 h-9 px-5 rounded-full bg-surface border border-border-color text-text-muted hover:text-primary hover:border-primary font-medium text-sm transition-all shadow-sm">
|
||||
<span class="material-symbols-outlined text-sm">chat</span> AI Chat
|
||||
</button>
|
||||
<button class="flex items-center gap-2 h-9 px-5 rounded-full bg-surface border border-border-color text-text-muted hover:text-primary hover:border-primary font-medium text-sm transition-all shadow-sm">
|
||||
<span class="material-symbols-outlined text-sm">image</span> Image Gen
|
||||
</button>
|
||||
<button class="flex items-center gap-2 h-9 px-5 rounded-full bg-surface border border-border-color text-text-muted hover:text-primary hover:border-primary font-medium text-sm transition-all shadow-sm">
|
||||
<span class="material-symbols-outlined text-sm">movie</span> Video
|
||||
</button>
|
||||
<button class="flex items-center gap-2 h-9 px-5 rounded-full bg-surface border border-border-color text-text-muted hover:text-primary hover:border-primary font-medium text-sm transition-all shadow-sm">
|
||||
<span class="material-symbols-outlined text-sm">edit_note</span> Writing
|
||||
</button>
|
||||
<button class="flex items-center gap-2 h-9 px-5 rounded-full bg-surface border border-border-color text-text-muted hover:text-primary hover:border-primary font-medium text-sm transition-all shadow-sm">
|
||||
<span class="material-symbols-outlined text-sm">code</span> Coding
|
||||
</button>
|
||||
<button class="flex items-center gap-2 h-9 px-5 rounded-full bg-surface border border-border-color text-text-muted hover:text-primary hover:border-primary font-medium text-sm transition-all shadow-sm">
|
||||
<span class="material-symbols-outlined text-sm">graphic_eq</span> Audio
|
||||
</button>
|
||||
<button class="flex items-center gap-2 h-9 px-5 rounded-full bg-surface border border-border-color text-text-muted hover:text-primary hover:border-primary font-medium text-sm transition-all shadow-sm">
|
||||
<span class="material-symbols-outlined text-sm">view_in_ar</span> 3D Assets
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-6">
|
||||
<div class="group relative flex flex-col gap-4 rounded-xl border border-border-color bg-surface p-5 card-hover transition-all duration-300 cursor-pointer overflow-hidden shadow-sm">
|
||||
<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">
|
||||
<div class="size-12 rounded-lg bg-cover bg-center shadow-md ring-1 ring-black/5" data-alt="ChatGPT logo icon green background" style="background-image: url('https://lh3.googleusercontent.com/aida-public/AB6AXuBStb1HsLpCc4Hlu8FzN0ecAT6pMnnu_pzF4cUUQDG6FdYLA65ua60D-3NxKDOHqkuVsgsy9ku-vUZ0_Jyc5O9Xi1NlwR5L7oC9gt_jretgJqsEeOk1dm2yo4GvB26KgmMWzpUKeCtIg1NyZDWRNJF3gfIVxF95sT29iy6tKSXsUdfVsOo-o_5kEpB3qCFKZdMo4fhOe8DVh4JnmmdZuS8z3PflupzZpru6F0QK3xL407vfIULuQ3L5NRtFdTfZs7O-elYl2FrwjJ_x');"></div>
|
||||
<div class="flex items-center justify-center size-8 rounded-full bg-slate-100 text-slate-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-text-main text-lg font-bold leading-tight group-hover:text-primary transition-colors">ChatGPT</h3>
|
||||
<p class="text-text-muted text-sm mt-2 line-clamp-2">OpenAI's advanced conversational model capable of understanding and generating human-like text.</p>
|
||||
</div>
|
||||
<div class="mt-auto flex items-center justify-between pt-4 border-t border-border-color">
|
||||
<div class="flex gap-2">
|
||||
<span class="px-2 py-0.5 rounded text-[10px] font-bold uppercase tracking-wider bg-slate-100 text-slate-600 border border-slate-200">Chat</span>
|
||||
<span class="px-2 py-0.5 rounded text-[10px] font-bold uppercase tracking-wider bg-slate-100 text-slate-600 border border-slate-200">NLP</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-1 text-slate-400 text-xs">
|
||||
<span class="material-symbols-outlined text-[14px]">visibility</span>
|
||||
<span>1.2M</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="group relative flex flex-col gap-4 rounded-xl border border-border-color bg-surface p-5 card-hover transition-all duration-300 cursor-pointer overflow-hidden shadow-sm">
|
||||
<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">
|
||||
<div class="size-12 rounded-lg bg-cover bg-center shadow-md ring-1 ring-black/5" data-alt="Midjourney logo icon blue background" style="background-image: url('https://lh3.googleusercontent.com/aida-public/AB6AXuBW8nuPBGShgvw9aCK5p0mKlki3EMfhi8ZZKvLTaM-QupUQDzsBx5wkIvN_Unyt30H2WHXBw5GB4Kz8gL68peWqK99Cpo6gRM-Bk3f94xWdYji9osPrreu5UMVtu2W1Rn8IyBCfPDUu3LJnbDd_viZz4nb04tliq9O_ezo8OCYMede7GZYxIxHTrEkhNBADzC2Z7KjeF-DR7fRBf4dxHYz5rJUGakf1qyEouaAlEDWMnBymOKFkQGK19UvizQiQIUeDFMGMxgL0ng_x');"></div>
|
||||
<div class="flex items-center justify-center size-8 rounded-full bg-slate-100 text-slate-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-text-main text-lg font-bold leading-tight group-hover:text-primary transition-colors">Midjourney</h3>
|
||||
<p class="text-text-muted text-sm mt-2 line-clamp-2">Hyper-realistic AI image generator that creates stunning visuals from text prompts.</p>
|
||||
</div>
|
||||
<div class="mt-auto flex items-center justify-between pt-4 border-t border-border-color">
|
||||
<div class="flex gap-2">
|
||||
<span class="px-2 py-0.5 rounded text-[10px] font-bold uppercase tracking-wider bg-slate-100 text-slate-600 border border-slate-200">Image</span>
|
||||
<span class="px-2 py-0.5 rounded text-[10px] font-bold uppercase tracking-wider bg-slate-100 text-slate-600 border border-slate-200">Art</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-1 text-slate-400 text-xs">
|
||||
<span class="material-symbols-outlined text-[14px]">visibility</span>
|
||||
<span>850k</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="group relative flex flex-col gap-4 rounded-xl border border-border-color bg-surface p-5 card-hover transition-all duration-300 cursor-pointer overflow-hidden shadow-sm">
|
||||
<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">
|
||||
<div class="size-12 rounded-lg bg-cover bg-center shadow-md ring-1 ring-black/5" data-alt="Jasper logo icon yellow background" style="background-image: url('https://lh3.googleusercontent.com/aida-public/AB6AXuAQv96KYBZ3uV5NpDJyuDzc5J-mGo3fTWcBzVv00wC062ApfHezk6XkvKI6lsad5hFopxB7Qqhbqifto971lBo_7ASfOFUvtSU-Z3omU0q4PGcmea1YR_Va7vKPBtpA1DS_uq779QNTY5oF_kjapJrp7-Nfi5tI0NfQ3kU5KOd-dwBrN2T1Md6nT1jUEeOKG3zEgmAfEYan4mQXQXpz7Ywh2ZaoFCOY0OTiJP9RAnzIYL0Tv-ywt3_r66CSTY5pyzfqqP7sUk4vegAv');"></div>
|
||||
<div class="flex items-center justify-center size-8 rounded-full bg-slate-100 text-slate-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-text-main text-lg font-bold leading-tight group-hover:text-primary transition-colors">Jasper AI</h3>
|
||||
<p class="text-text-muted text-sm mt-2 line-clamp-2">AI copywriter for marketing content, blog posts, and social media captions.</p>
|
||||
</div>
|
||||
<div class="mt-auto flex items-center justify-between pt-4 border-t border-border-color">
|
||||
<div class="flex gap-2">
|
||||
<span class="px-2 py-0.5 rounded text-[10px] font-bold uppercase tracking-wider bg-slate-100 text-slate-600 border border-slate-200">Writing</span>
|
||||
<span class="px-2 py-0.5 rounded text-[10px] font-bold uppercase tracking-wider bg-slate-100 text-slate-600 border border-slate-200">Marketing</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-1 text-slate-400 text-xs">
|
||||
<span class="material-symbols-outlined text-[14px]">visibility</span>
|
||||
<span>300k</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="group relative flex flex-col gap-4 rounded-xl border border-border-color bg-surface p-5 card-hover transition-all duration-300 cursor-pointer overflow-hidden shadow-sm">
|
||||
<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">
|
||||
<div class="size-12 rounded-lg bg-cover bg-center shadow-md ring-1 ring-black/5" data-alt="Runway logo icon pink background" style="background-image: url('https://lh3.googleusercontent.com/aida-public/AB6AXuAkmxk1Ar1KxgHHM5pKMer4rOra0KUieDAXhm9nE4sQp5WjsnGYENKmbM-EN-hRjhV7OC2ha7Go-Cl90HgfNpbgt--grIpJYKoUjgvsp0juLIDgX_fN4rKrjdkezxRU8YVJBuTaM3lWZJCptm3I6isDO10xidrChOc5kWV9SQny79KEVKENXxOJXEVT3c0m16M3JnGRTRmu-6EY8XU68-On78-7SXxLyP5TESi-ooZ2wHaOwxqJUwFb-oQyQoKAkXnzXAqGwwITBiLS');"></div>
|
||||
<div class="flex items-center justify-center size-8 rounded-full bg-slate-100 text-slate-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-text-main text-lg font-bold leading-tight group-hover:text-primary transition-colors">Runway Gen-2</h3>
|
||||
<p class="text-text-muted text-sm mt-2 line-clamp-2">Next-generation video creation tool that turns text into high-quality video clips.</p>
|
||||
</div>
|
||||
<div class="mt-auto flex items-center justify-between pt-4 border-t border-border-color">
|
||||
<div class="flex gap-2">
|
||||
<span class="px-2 py-0.5 rounded text-[10px] font-bold uppercase tracking-wider bg-slate-100 text-slate-600 border border-slate-200">Video</span>
|
||||
<span class="px-2 py-0.5 rounded text-[10px] font-bold uppercase tracking-wider bg-slate-100 text-slate-600 border border-slate-200">Gen-AI</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-1 text-slate-400 text-xs">
|
||||
<span class="material-symbols-outlined text-[14px]">visibility</span>
|
||||
<span>420k</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="group relative flex flex-col gap-4 rounded-xl border border-border-color bg-surface p-5 card-hover transition-all duration-300 cursor-pointer overflow-hidden shadow-sm">
|
||||
<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">
|
||||
<div class="size-12 rounded-lg bg-cover bg-center shadow-md ring-1 ring-black/5" data-alt="Copilot logo icon purple background" style="background-image: url('https://lh3.googleusercontent.com/aida-public/AB6AXuBki2piUPdnWstozizUU63ouakjjA3B4xN5uQs7rA84yX4NsYFIaNybtfVRy0bXUKfUBYdixo22jb3bbN6TlKindW5iQU0sJc3FF2GXS_CRpgLoNMLziOyf4rLArNmM2VoycHJ3DyrIsI8847xlO3BHrK8kyemNc9mBfaXyPnWsJ0hnF2lXfljCOcYNQTtY1qgzw5RfOHwA0NUnLwuRG53tU-mnJPPejbDyzW7YWdBnh9yTj3dnJJDOfzYiEx252wgf7Q6GBjTGrQnG');"></div>
|
||||
<div class="flex items-center justify-center size-8 rounded-full bg-slate-100 text-slate-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-text-main text-lg font-bold leading-tight group-hover:text-primary transition-colors">GitHub Copilot</h3>
|
||||
<p class="text-text-muted text-sm mt-2 line-clamp-2">Your AI pair programmer that helps you write better code faster.</p>
|
||||
</div>
|
||||
<div class="mt-auto flex items-center justify-between pt-4 border-t border-border-color">
|
||||
<div class="flex gap-2">
|
||||
<span class="px-2 py-0.5 rounded text-[10px] font-bold uppercase tracking-wider bg-slate-100 text-slate-600 border border-slate-200">Dev</span>
|
||||
<span class="px-2 py-0.5 rounded text-[10px] font-bold uppercase tracking-wider bg-slate-100 text-slate-600 border border-slate-200">Coding</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-1 text-slate-400 text-xs">
|
||||
<span class="material-symbols-outlined text-[14px]">visibility</span>
|
||||
<span>1.5M</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="group relative flex flex-col gap-4 rounded-xl border border-border-color bg-surface p-5 card-hover transition-all duration-300 cursor-pointer overflow-hidden shadow-sm">
|
||||
<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">
|
||||
<div class="size-12 rounded-lg bg-cover bg-center shadow-md ring-1 ring-black/5" data-alt="ElevenLabs logo icon red background" style="background-image: url('https://lh3.googleusercontent.com/aida-public/AB6AXuBVqan6FEM01JlSNs0aKYTXdgzFAXn1xT_Yx0S3VszDGf5x0hsptwFAxMNtCEtJZOW65aZMncYKyPpzdAoIHLaBDk_ORHur89Redtuublqbd51Cr6sKVdMJ2VAAihNuVPsvpNBdIiLGuf40kXbe3HJrdYVNAKi27xcxBICheI4OzkFF7uJvChUyDbumqLRFUDMssCuxZIQYTeDKbTT628ZBFUN2H8u0RfVSPEyEhJVSE2NCAD4mH-dWGmsQzMWjmeZkwXJKpw1TGHFp');"></div>
|
||||
<div class="flex items-center justify-center size-8 rounded-full bg-slate-100 text-slate-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-text-main text-lg font-bold leading-tight group-hover:text-primary transition-colors">ElevenLabs</h3>
|
||||
<p class="text-text-muted text-sm mt-2 line-clamp-2">The most realistic AI voice generator and text-to-speech software.</p>
|
||||
</div>
|
||||
<div class="mt-auto flex items-center justify-between pt-4 border-t border-border-color">
|
||||
<div class="flex gap-2">
|
||||
<span class="px-2 py-0.5 rounded text-[10px] font-bold uppercase tracking-wider bg-slate-100 text-slate-600 border border-slate-200">Audio</span>
|
||||
<span class="px-2 py-0.5 rounded text-[10px] font-bold uppercase tracking-wider bg-slate-100 text-slate-600 border border-slate-200">TTS</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-1 text-slate-400 text-xs">
|
||||
<span class="material-symbols-outlined text-[14px]">visibility</span>
|
||||
<span>210k</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="group relative flex flex-col gap-4 rounded-xl border border-border-color bg-surface p-5 card-hover transition-all duration-300 cursor-pointer overflow-hidden shadow-sm">
|
||||
<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">
|
||||
<div class="size-12 rounded-lg bg-cover bg-center shadow-md ring-1 ring-black/5" data-alt="Stable Diffusion logo icon teal background" style="background-image: url('https://lh3.googleusercontent.com/aida-public/AB6AXuB4zzlwlZK6dJVjV4vvFGvRmtsvd7dvQMCC1mAzFfEqA2sYnJnEdTDAF76lfVYkaKzibwuppHRIcEgbT8xaoWOBF1qdMiV3Qs3P9fEOLJ2jApPPqJyBnPXsvxFsI6FTlZ0SS_Rpblvjln_n_YLh8Wi_VWh8lCJirMM5vMCuyrNGK9Crxv3bnJzlbH34i9vmxSxSQ5uCmoAE5GPxkD23vzun9BBOKjRK-Ln7DhQ_bGhnVxkxEtM8Z-DVuGg2dNE7WnD7BEBXTOBh5XQa');"></div>
|
||||
<div class="flex items-center justify-center size-8 rounded-full bg-slate-100 text-slate-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-text-main text-lg font-bold leading-tight group-hover:text-primary transition-colors">Stable Diffusion</h3>
|
||||
<p class="text-text-muted text-sm mt-2 line-clamp-2">Open source latent text-to-image diffusion model for image generation.</p>
|
||||
</div>
|
||||
<div class="mt-auto flex items-center justify-between pt-4 border-t border-border-color">
|
||||
<div class="flex gap-2">
|
||||
<span class="px-2 py-0.5 rounded text-[10px] font-bold uppercase tracking-wider bg-slate-100 text-slate-600 border border-slate-200">Open Source</span>
|
||||
<span class="px-2 py-0.5 rounded text-[10px] font-bold uppercase tracking-wider bg-slate-100 text-slate-600 border border-slate-200">Image</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-1 text-slate-400 text-xs">
|
||||
<span class="material-symbols-outlined text-[14px]">visibility</span>
|
||||
<span>950k</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="group relative flex flex-col gap-4 rounded-xl border border-border-color bg-surface p-5 card-hover transition-all duration-300 cursor-pointer overflow-hidden shadow-sm">
|
||||
<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">
|
||||
<div class="size-12 rounded-lg bg-cover bg-center shadow-md ring-1 ring-black/5" data-alt="Notion AI logo icon indigo background" style="background-image: url('https://lh3.googleusercontent.com/aida-public/AB6AXuAgnpQqxiQhRd6xj43Q0hY2EQTna5khOzvl_NxO3OJN_7_nsfC0wP1x7ZmfBEvEEOoGi7NHVx6pvOpJb7Zk7dvUQWD64Y7Nrhc0mi0wsVIB1U8OfG1wioEusBqkI6zJQQh8W0om8gi2ubnJn2McHrvANulIAVnRxn1zPcy-SAnW1rLynAdVFKurBX5qK3BFBi_knAMoL8bTL_1LTxIdhdCggw6WeCGsFgUu6vqwjq9prGC-j94Fr8GibbjDSABY36P1OoZg59Aiv1M4');"></div>
|
||||
<div class="flex items-center justify-center size-8 rounded-full bg-slate-100 text-slate-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-text-main text-lg font-bold leading-tight group-hover:text-primary transition-colors">Notion AI</h3>
|
||||
<p class="text-text-muted text-sm mt-2 line-clamp-2">Access the limitless power of AI, right inside your Notion workspace.</p>
|
||||
</div>
|
||||
<div class="mt-auto flex items-center justify-between pt-4 border-t border-border-color">
|
||||
<div class="flex gap-2">
|
||||
<span class="px-2 py-0.5 rounded text-[10px] font-bold uppercase tracking-wider bg-slate-100 text-slate-600 border border-slate-200">Productivity</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-1 text-slate-400 text-xs">
|
||||
<span class="material-symbols-outlined text-[14px]">visibility</span>
|
||||
<span>600k</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-center justify-center py-4">
|
||||
<nav class="flex items-center gap-2">
|
||||
<a class="flex size-10 items-center justify-center rounded-full border border-border-color text-text-muted hover:text-primary hover:border-primary transition-colors bg-surface" href="#">
|
||||
<span class="material-symbols-outlined text-base">chevron_left</span>
|
||||
</a>
|
||||
<a class="flex size-10 items-center justify-center rounded-full bg-primary text-white text-sm font-bold shadow-[0_4px_10px_rgba(14,165,233,0.3)]" href="#">1</a>
|
||||
<a class="flex size-10 items-center justify-center rounded-full border border-transparent hover:bg-slate-200 text-text-muted hover:text-text-main text-sm font-medium transition-colors" href="#">2</a>
|
||||
<a class="flex size-10 items-center justify-center rounded-full border border-transparent hover:bg-slate-200 text-text-muted hover:text-text-main text-sm font-medium transition-colors" href="#">3</a>
|
||||
<span class="flex size-10 items-center justify-center text-slate-400">...</span>
|
||||
<a class="flex size-10 items-center justify-center rounded-full border border-transparent hover:bg-slate-200 text-text-muted hover:text-text-main text-sm font-medium transition-colors" href="#">12</a>
|
||||
<a class="flex size-10 items-center justify-center rounded-full border border-border-color text-text-muted hover:text-primary hover:border-primary transition-colors bg-surface" href="#">
|
||||
<span class="material-symbols-outlined text-base">chevron_right</span>
|
||||
</a>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
<footer class="w-full border-t border-border-color bg-surface 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-text-muted text-sm">
|
||||
<span>© 2023 ZJPB AI Directory. All rights reserved.</span>
|
||||
</div>
|
||||
<div class="flex gap-6">
|
||||
<a class="text-text-muted hover:text-primary transition-colors" href="#">
|
||||
<span class="text-sm font-medium">Twitter</span>
|
||||
</a>
|
||||
<a class="text-text-muted hover:text-primary transition-colors" href="#">
|
||||
<span class="text-sm font-medium">Discord</span>
|
||||
</a>
|
||||
<a class="text-text-muted hover:text-primary transition-colors" href="#">
|
||||
<span class="text-sm font-medium">Privacy Policy</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
</div>
|
||||
|
||||
</body></html>
|
||||
BIN
stitch_ai_tool_detail_page/ai_tool_home_page/screen.png
Normal file
|
After Width: | Height: | Size: 341 KiB |
@@ -1,126 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
|
||||
<html class="dark" lang="en"><head>
|
||||
<meta charset="utf-8"/>
|
||||
<meta content="width=device-width, initial-scale=1.0" name="viewport"/>
|
||||
<title>Admin Login - AI Discovery</title>
|
||||
<!-- Fonts -->
|
||||
<link href="https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@300;400;500;600;700&family=Noto+Sans:wght@300;400;500;600;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"/>
|
||||
<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 id="tailwind-config">
|
||||
tailwind.config = {
|
||||
darkMode: "class",
|
||||
theme: {
|
||||
extend: {
|
||||
colors: {
|
||||
"primary": "#25c0f4",
|
||||
"background-light": "#f5f8f8",
|
||||
"background-dark": "#101e22",
|
||||
"surface-dark": "#162125",
|
||||
"input-bg": "#1b2427",
|
||||
"input-border": "#3b4e54",
|
||||
"text-muted": "#9cb2ba",
|
||||
},
|
||||
fontFamily: {
|
||||
"display": ["Space Grotesk", "sans-serif"],
|
||||
"body": ["Noto Sans", "sans-serif"],
|
||||
},
|
||||
borderRadius: {"DEFAULT": "0.25rem", "lg": "0.5rem", "xl": "0.75rem", "full": "9999px"},
|
||||
backgroundImage: {
|
||||
'tech-grid': "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)",
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
<style>
|
||||
.glass-panel {
|
||||
background: rgba(22, 33, 37, 0.7);
|
||||
backdrop-filter: blur(12px);
|
||||
-webkit-backdrop-filter: blur(12px);
|
||||
}
|
||||
.tech-bg-size {
|
||||
background-size: 100% 100%, 40px 40px, 40px 40px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body class="bg-background-light dark:bg-background-dark font-display text-slate-900 dark: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 bg-tech-grid tech-bg-size pointer-events-none" data-alt="Abstract subtle grid pattern background representing technology"></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-text-muted hover:text-white transition-colors w-fit" href="#">
|
||||
<div class="flex items-center justify-center w-8 h-8 rounded-full border border-input-border 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">Back to Home</span>
|
||||
</a>
|
||||
<!-- Login Card -->
|
||||
<div class="glass-panel border border-white/5 dark:border-input-border/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-text-muted text-base font-normal">Enter your credentials to access the AI Control Panel.</p>
|
||||
</div>
|
||||
<!-- Error Message Area (Static Example) -->
|
||||
<!-- <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">Invalid credentials provided.</p>
|
||||
</div> -->
|
||||
<!-- Form -->
|
||||
<form 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">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-input-border bg-input-bg focus:border-primary placeholder:text-text-muted text-base font-normal leading-normal transition-colors" id="username" placeholder="Enter your username or email" type="text"/>
|
||||
<div class="absolute left-4 top-1/2 -translate-y-1/2 text-text-muted 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">
|
||||
<div class="flex justify-between items-end">
|
||||
<label class="text-white text-sm font-medium leading-normal" for="password">Password</label>
|
||||
<a class="text-xs text-primary hover:text-white transition-colors mb-1" href="#">Forgot password?</a>
|
||||
</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-input-border bg-input-bg focus:border-primary placeholder:text-text-muted text-base font-normal leading-normal transition-colors z-10" id="password" placeholder="Enter your password" type="password"/>
|
||||
<div class="absolute left-4 top-1/2 -translate-y-1/2 text-text-muted 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>
|
||||
<button class="absolute right-0 top-0 h-full px-4 text-text-muted hover:text-white transition-colors flex items-center justify-center z-20 focus:outline-none" type="button">
|
||||
<span class="material-symbols-outlined text-[20px]">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-background-dark 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="button">
|
||||
<span class="truncate">Login</span>
|
||||
</button>
|
||||
</form>
|
||||
<!-- Footer Meta -->
|
||||
<div class="mt-8 flex justify-center gap-6 border-t border-input-border/30 pt-6">
|
||||
<p class="text-text-muted text-xs text-center">
|
||||
Protected by reCAPTCHA. <a class="underline hover:text-primary" href="#">Privacy</a> & <a class="underline hover:text-primary" href="#">Terms</a>.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body></html>
|
||||
|
Before Width: | Height: | Size: 518 KiB |
@@ -1,394 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
|
||||
<html class="dark" lang="en"><head>
|
||||
<meta charset="utf-8"/>
|
||||
<meta content="width=device-width, initial-scale=1.0" name="viewport"/>
|
||||
<title>Admin Management Interface</title>
|
||||
<!-- Fonts -->
|
||||
<link href="https://fonts.googleapis.com" rel="preconnect"/>
|
||||
<link crossorigin="" href="https://fonts.gstatic.com" rel="preconnect"/>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@300;400;500;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"/>
|
||||
<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>
|
||||
<!-- Theme Configuration -->
|
||||
<script id="tailwind-config">
|
||||
tailwind.config = {
|
||||
darkMode: "class",
|
||||
theme: {
|
||||
extend: {
|
||||
colors: {
|
||||
"primary": "#25c0f4",
|
||||
"primary-hover": "#0ea5e9",
|
||||
"background-light": "#f5f8f8",
|
||||
"background-dark": "#111618", /* Matched to snippets */
|
||||
"surface-dark": "#192226",
|
||||
"surface-highlight": "#283539",
|
||||
"text-secondary": "#9cb2ba",
|
||||
},
|
||||
fontFamily: {
|
||||
"display": ["Space Grotesk", "sans-serif"],
|
||||
"body": ["Noto Sans", "sans-serif"],
|
||||
},
|
||||
borderRadius: {"DEFAULT": "0.25rem", "lg": "0.5rem", "xl": "0.75rem", "2xl": "1rem", "full": "9999px"},
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
<style>
|
||||
body {
|
||||
font-family: "Noto Sans", sans-serif;
|
||||
}
|
||||
h1, h2, h3, h4, h5, h6, .font-display {
|
||||
font-family: "Space Grotesk", sans-serif;
|
||||
}
|
||||
/* Custom Scrollbar for dark theme */
|
||||
::-webkit-scrollbar {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
}
|
||||
::-webkit-scrollbar-track {
|
||||
background: #111618;
|
||||
}
|
||||
::-webkit-scrollbar-thumb {
|
||||
background: #283539;
|
||||
border-radius: 4px;
|
||||
}
|
||||
::-webkit-scrollbar-thumb:hover {
|
||||
background: #3a4b50;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body class="bg-background-light dark:bg-background-dark text-slate-900 dark:text-white overflow-hidden flex h-screen w-full">
|
||||
<!-- Sidebar -->
|
||||
<aside class="flex flex-col w-64 h-full border-r border-[#283539] bg-background-dark flex-shrink-0 transition-all duration-300">
|
||||
<div class="p-6 flex items-center gap-3">
|
||||
<div class="size-8 text-primary">
|
||||
<svg fill="none" viewbox="0 0 48 48" xmlns="http://www.w3.org/2000/svg">
|
||||
<path clip-rule="evenodd" d="M24 18.4228L42 11.475V34.3663C42 34.7796 41.7457 35.1504 41.3601 35.2992L24 42V18.4228Z" fill="currentColor" fill-rule="evenodd"></path>
|
||||
<path clip-rule="evenodd" d="M24 8.18819L33.4123 11.574L24 15.2071L14.5877 11.574L24 8.18819ZM9 15.8487L21 20.4805V37.6263L9 32.9945V15.8487ZM27 37.6263V20.4805L39 15.8487V32.9945L27 37.6263ZM25.354 2.29885C24.4788 1.98402 23.5212 1.98402 22.646 2.29885L4.98454 8.65208C3.7939 9.08038 3 10.2097 3 11.475V34.3663C3 36.0196 4.01719 37.5026 5.55962 38.098L22.9197 44.7987C23.6149 45.0671 24.3851 45.0671 25.0803 44.7987L42.4404 38.098C43.9828 37.5026 45 36.0196 45 34.3663V11.475C45 10.2097 44.2061 9.08038 43.0155 8.65208L25.354 2.29885Z" fill="currentColor" fill-rule="evenodd"></path>
|
||||
</svg>
|
||||
</div>
|
||||
<h1 class="text-white text-xl font-bold tracking-tight">AI Discovery</h1>
|
||||
</div>
|
||||
<nav class="flex-1 px-4 flex flex-col gap-2 mt-4">
|
||||
<div class="px-3 py-2">
|
||||
<p class="text-text-secondary text-xs font-bold uppercase tracking-wider mb-2">Main Menu</p>
|
||||
<a class="flex items-center gap-3 px-3 py-2.5 rounded-lg bg-surface-highlight text-white group transition-colors" href="#">
|
||||
<span class="material-symbols-outlined text-primary group-hover:text-white transition-colors">language</span>
|
||||
<span class="text-sm font-medium">Website Management</span>
|
||||
</a>
|
||||
<a class="flex items-center gap-3 px-3 py-2.5 rounded-lg text-text-secondary hover:bg-surface-highlight hover:text-white transition-colors mt-1" href="#">
|
||||
<span class="material-symbols-outlined">label</span>
|
||||
<span class="text-sm font-medium">Tag Management</span>
|
||||
</a>
|
||||
<a class="flex items-center gap-3 px-3 py-2.5 rounded-lg text-text-secondary hover:bg-surface-highlight hover:text-white transition-colors mt-1" href="#">
|
||||
<span class="material-symbols-outlined">group</span>
|
||||
<span class="text-sm font-medium">Administrators</span>
|
||||
</a>
|
||||
</div>
|
||||
<div class="px-3 py-2 mt-auto">
|
||||
<p class="text-text-secondary text-xs font-bold uppercase tracking-wider mb-2">System</p>
|
||||
<a class="flex items-center gap-3 px-3 py-2.5 rounded-lg text-text-secondary hover:bg-surface-highlight hover:text-white transition-colors" href="#">
|
||||
<span class="material-symbols-outlined">settings</span>
|
||||
<span class="text-sm font-medium">Settings</span>
|
||||
</a>
|
||||
</div>
|
||||
</nav>
|
||||
<div class="p-4 border-t border-[#283539]">
|
||||
<div class="flex items-center gap-3">
|
||||
<div class="bg-center bg-no-repeat bg-cover rounded-full size-10 border border-[#283539]" data-alt="User profile picture showing a minimal avatar" style='background-image: url("https://lh3.googleusercontent.com/aida-public/AB6AXuDlD4gGno1M8YZQOQ6QyLdUZtaU7klHxTjeH2Kf3aH39GkQ56ld-iSPZ6RCbkxzCeXJzMq6_29atBkPks_wP-Q7pvpyNAAZW2FJKxOFTSrcQZp2O30dVhn-DPYELqG1fdqM-hPfluduBN-I0k_Q8cJrJ3LKVUUgnxsE8bWD4CSo8CoYvXmtK03j86TehszyrfWfKpoBcu8XaHpjh10EGoDHFYeSJ6AKJQDsp_0iz5LC4bOXvyg0_Nef5mQOBtQ9zKuKSbCYhRuu7Wyo");'></div>
|
||||
<div class="flex flex-col overflow-hidden">
|
||||
<h2 class="text-white text-sm font-medium truncate">Admin User</h2>
|
||||
<p class="text-text-secondary text-xs truncate">admin@ai-discovery.com</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</aside>
|
||||
<!-- Main Content -->
|
||||
<main class="flex-1 flex flex-col h-full overflow-hidden bg-background-dark relative">
|
||||
<!-- Top Header -->
|
||||
<header class="h-16 border-b border-[#283539] bg-background-dark/95 backdrop-blur px-8 flex items-center justify-between shrink-0 z-20">
|
||||
<!-- Breadcrumbs -->
|
||||
<div class="flex items-center gap-2 text-sm">
|
||||
<a class="text-text-secondary hover:text-white transition-colors" href="#">Dashboard</a>
|
||||
<span class="text-text-secondary">/</span>
|
||||
<span class="text-white font-medium">Website Management</span>
|
||||
</div>
|
||||
<!-- Global Actions -->
|
||||
<div class="flex items-center gap-6">
|
||||
<!-- Search -->
|
||||
<div class="relative w-64 hidden md:block">
|
||||
<span class="absolute inset-y-0 left-0 flex items-center pl-3 text-text-secondary">
|
||||
<span class="material-symbols-outlined text-[20px]">search</span>
|
||||
</span>
|
||||
<input class="w-full bg-surface-highlight border-none rounded-lg py-2 pl-10 pr-4 text-sm text-white placeholder-text-secondary focus:ring-1 focus:ring-primary focus:bg-[#1f292d] transition-all" placeholder="Global search..." type="text"/>
|
||||
</div>
|
||||
<!-- Notifications -->
|
||||
<button class="relative text-text-secondary hover:text-white transition-colors">
|
||||
<span class="material-symbols-outlined">notifications</span>
|
||||
<span class="absolute top-0 right-0 size-2 bg-primary rounded-full"></span>
|
||||
</button>
|
||||
<!-- Logout -->
|
||||
<button class="text-text-secondary hover:text-white transition-colors">
|
||||
<span class="material-symbols-outlined">logout</span>
|
||||
</button>
|
||||
</div>
|
||||
</header>
|
||||
<!-- Scrollable Content Area -->
|
||||
<div class="flex-1 overflow-y-auto overflow-x-hidden p-8 scroll-smooth">
|
||||
<div class="max-w-[1400px] mx-auto flex flex-col gap-8 pb-10">
|
||||
<!-- Page Title & Toolbar -->
|
||||
<div class="flex flex-col md:flex-row md:items-end justify-between gap-4">
|
||||
<div>
|
||||
<h1 class="text-3xl md:text-4xl font-bold text-white mb-2">Website Management</h1>
|
||||
<p class="text-text-secondary text-base">Manage and curate AI tools for the discovery platform.</p>
|
||||
</div>
|
||||
<div class="flex gap-3">
|
||||
<button class="flex items-center gap-2 h-10 px-4 rounded-lg bg-surface-highlight hover:bg-[#344247] text-white text-sm font-medium transition-colors border border-transparent hover:border-[#4a5a60]">
|
||||
<span class="material-symbols-outlined text-[20px]">filter_list</span>
|
||||
<span>Filter</span>
|
||||
</button>
|
||||
<button class="flex items-center gap-2 h-10 px-4 rounded-lg bg-surface-highlight hover:bg-[#344247] text-white text-sm font-medium transition-colors border border-transparent hover:border-[#4a5a60]">
|
||||
<span class="material-symbols-outlined text-[20px]">download</span>
|
||||
<span>Export</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Content Grid: Table & Quick Add Form -->
|
||||
<div class="grid grid-cols-1 xl:grid-cols-3 gap-8 items-start">
|
||||
<!-- Left Column: Data Table -->
|
||||
<div class="xl:col-span-2 flex flex-col gap-4">
|
||||
<!-- Table Toolbar -->
|
||||
<div class="bg-surface-dark border border-[#283539] rounded-t-xl p-4 flex flex-col sm:flex-row justify-between items-center gap-4">
|
||||
<div class="relative w-full sm:w-72">
|
||||
<span class="absolute inset-y-0 left-0 flex items-center pl-3 text-text-secondary">
|
||||
<span class="material-symbols-outlined text-[20px]">search</span>
|
||||
</span>
|
||||
<input class="w-full bg-[#111618] border border-[#283539] rounded-lg py-2 pl-10 pr-4 text-sm text-white placeholder-text-secondary focus:ring-1 focus:ring-primary" placeholder="Search websites..." type="text"/>
|
||||
</div>
|
||||
<div class="text-sm text-text-secondary">
|
||||
Showing <span class="text-white font-medium">1-10</span> of <span class="text-white font-medium">128</span>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Table -->
|
||||
<div class="bg-surface-dark border border-[#283539] rounded-b-xl overflow-hidden shadow-xl">
|
||||
<div class="overflow-x-auto">
|
||||
<table class="w-full text-left border-collapse">
|
||||
<thead>
|
||||
<tr class="bg-[#1e272c] border-b border-[#283539]">
|
||||
<th class="p-4 text-xs font-semibold uppercase tracking-wider text-text-secondary w-10">
|
||||
<input class="rounded bg-[#111618] border-[#283539] text-primary focus:ring-offset-[#111618]" type="checkbox"/>
|
||||
</th>
|
||||
<th class="p-4 text-xs font-semibold uppercase tracking-wider text-text-secondary">Website / Tool</th>
|
||||
<th class="p-4 text-xs font-semibold uppercase tracking-wider text-text-secondary">Tags</th>
|
||||
<th class="p-4 text-xs font-semibold uppercase tracking-wider text-text-secondary">Status</th>
|
||||
<th class="p-4 text-xs font-semibold uppercase tracking-wider text-text-secondary text-right">Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="divide-y divide-[#283539]">
|
||||
<!-- Row 1 -->
|
||||
<tr class="group hover:bg-[#1e272c] transition-colors">
|
||||
<td class="p-4">
|
||||
<input class="rounded bg-[#111618] border-[#283539] text-primary focus:ring-offset-[#111618]" type="checkbox"/>
|
||||
</td>
|
||||
<td class="p-4">
|
||||
<div class="flex items-center gap-3">
|
||||
<div class="size-10 rounded-lg bg-cover bg-center shrink-0 border border-[#283539]" data-alt="Icon of Chat AI Tool" style='background-image: url("https://lh3.googleusercontent.com/aida-public/AB6AXuB_TTlnbkP1ygA1GNesXOhx_H9yup1gOKwumL9Qnh8VcPEUlwDRA89LRKhO8tyDtOtvtfrGh7CRSnP5xMOyb_GvJgQ-FQToY20cgHuTpVeI97COVlA4e2wOp04SH5ogk98S2SFF4jtSoS7RldXDdaafrysnsulA7_euKFIDf42NTPk_YwR4aglKFOeiQCQT-ITD6hCZjE8kqEfam8E6onBbJ14KZt2sE8gn0VP-pzzgAgPlr3htq5j6T26Tijk8V7SIFQGlB20GjSsb");'></div>
|
||||
<div>
|
||||
<h3 class="text-sm font-bold text-white group-hover:text-primary transition-colors">ChatGPT</h3>
|
||||
<p class="text-xs text-text-secondary truncate max-w-[200px]">openai.com/chatgpt</p>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td class="p-4">
|
||||
<div class="flex flex-wrap gap-1">
|
||||
<span class="px-2 py-0.5 rounded text-[10px] font-medium bg-[#283539] text-white border border-[#3a4b50]">LLM</span>
|
||||
<span class="px-2 py-0.5 rounded text-[10px] font-medium bg-[#283539] text-white border border-[#3a4b50]">Chatbot</span>
|
||||
</div>
|
||||
</td>
|
||||
<td class="p-4">
|
||||
<span class="inline-flex items-center px-2 py-1 rounded-full text-xs font-medium bg-green-500/10 text-green-400 border border-green-500/20">
|
||||
Active
|
||||
</span>
|
||||
</td>
|
||||
<td class="p-4 text-right">
|
||||
<div class="flex items-center justify-end gap-2 opacity-0 group-hover:opacity-100 transition-opacity">
|
||||
<button class="p-1.5 rounded-md hover:bg-primary/20 hover:text-primary text-text-secondary transition-colors">
|
||||
<span class="material-symbols-outlined text-[18px]">edit</span>
|
||||
</button>
|
||||
<button class="p-1.5 rounded-md hover:bg-red-500/20 hover:text-red-400 text-text-secondary transition-colors">
|
||||
<span class="material-symbols-outlined text-[18px]">delete</span>
|
||||
</button>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<!-- Row 2 -->
|
||||
<tr class="group hover:bg-[#1e272c] transition-colors">
|
||||
<td class="p-4">
|
||||
<input class="rounded bg-[#111618] border-[#283539] text-primary focus:ring-offset-[#111618]" type="checkbox"/>
|
||||
</td>
|
||||
<td class="p-4">
|
||||
<div class="flex items-center gap-3">
|
||||
<div class="size-10 rounded-lg bg-cover bg-center shrink-0 border border-[#283539]" data-alt="Icon of Midjourney AI" style='background-image: url("https://lh3.googleusercontent.com/aida-public/AB6AXuA-IWUA_5FInPrqeTtZDcb85HR0X3jABxlDeG811Bd-_2x4ISEl8huiNp-kAG75fBmopT3dd5nSp1E0wCtOxXMFoP6HR9nrg4PfNDxjLao4ulneOOrJMgL4LdtuXgKxrQHm1WDoG4TviJ-lIYXzLVI9Yw-9mjDFihMurEOdJlarrMZekyEVrogy_jliQPo3oXvcemTUiJAZz9UCwq8LOEr09EqzU25kXCXx3Z3B0XvdM6VDUrQN8mKtKU1eU9CVwsLJfOKZ09CpJIwx");'></div>
|
||||
<div>
|
||||
<h3 class="text-sm font-bold text-white group-hover:text-primary transition-colors">Midjourney</h3>
|
||||
<p class="text-xs text-text-secondary truncate max-w-[200px]">midjourney.com</p>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td class="p-4">
|
||||
<div class="flex flex-wrap gap-1">
|
||||
<span class="px-2 py-0.5 rounded text-[10px] font-medium bg-[#283539] text-white border border-[#3a4b50]">Image Gen</span>
|
||||
<span class="px-2 py-0.5 rounded text-[10px] font-medium bg-[#283539] text-white border border-[#3a4b50]">Art</span>
|
||||
</div>
|
||||
</td>
|
||||
<td class="p-4">
|
||||
<span class="inline-flex items-center px-2 py-1 rounded-full text-xs font-medium bg-green-500/10 text-green-400 border border-green-500/20">
|
||||
Active
|
||||
</span>
|
||||
</td>
|
||||
<td class="p-4 text-right">
|
||||
<div class="flex items-center justify-end gap-2 opacity-0 group-hover:opacity-100 transition-opacity">
|
||||
<button class="p-1.5 rounded-md hover:bg-primary/20 hover:text-primary text-text-secondary transition-colors">
|
||||
<span class="material-symbols-outlined text-[18px]">edit</span>
|
||||
</button>
|
||||
<button class="p-1.5 rounded-md hover:bg-red-500/20 hover:text-red-400 text-text-secondary transition-colors">
|
||||
<span class="material-symbols-outlined text-[18px]">delete</span>
|
||||
</button>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<!-- Row 3 -->
|
||||
<tr class="group hover:bg-[#1e272c] transition-colors">
|
||||
<td class="p-4">
|
||||
<input class="rounded bg-[#111618] border-[#283539] text-primary focus:ring-offset-[#111618]" type="checkbox"/>
|
||||
</td>
|
||||
<td class="p-4">
|
||||
<div class="flex items-center gap-3">
|
||||
<div class="size-10 rounded-lg bg-cover bg-center shrink-0 border border-[#283539]" data-alt="Icon of Copy AI Tool" style='background-image: url("https://lh3.googleusercontent.com/aida-public/AB6AXuB6XdW_8pAKAs4YPwwYM4bcub_ilJ2i6L7baANaKdNNmDP5k1TRngBOsHE0aTy6sBuBJG49alX-4PRkz55qTcxsrRy6_S8rqD5gCTWLquEDcjqfSai-s7GEdZcBCNJ6G5hEFJM7JFli5r5ETcPq2QBz_NQT0W94XYhzaYpZQVEX-su5ywWxAcrfOFrWrhwC5RCiZL0B7AinM4c1pRSfeDtDpVTzxTvx0MrDi6gBH-duuCUmbr0YjkqhyaZqpRpX-ZhW-JiJQ3sKPUCA");'></div>
|
||||
<div>
|
||||
<h3 class="text-sm font-bold text-white group-hover:text-primary transition-colors">Copy.ai</h3>
|
||||
<p class="text-xs text-text-secondary truncate max-w-[200px]">copy.ai</p>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td class="p-4">
|
||||
<div class="flex flex-wrap gap-1">
|
||||
<span class="px-2 py-0.5 rounded text-[10px] font-medium bg-[#283539] text-white border border-[#3a4b50]">Writing</span>
|
||||
<span class="px-2 py-0.5 rounded text-[10px] font-medium bg-[#283539] text-white border border-[#3a4b50]">Marketing</span>
|
||||
</div>
|
||||
</td>
|
||||
<td class="p-4">
|
||||
<span class="inline-flex items-center px-2 py-1 rounded-full text-xs font-medium bg-yellow-500/10 text-yellow-400 border border-yellow-500/20">
|
||||
Pending
|
||||
</span>
|
||||
</td>
|
||||
<td class="p-4 text-right">
|
||||
<div class="flex items-center justify-end gap-2 opacity-0 group-hover:opacity-100 transition-opacity">
|
||||
<button class="p-1.5 rounded-md hover:bg-primary/20 hover:text-primary text-text-secondary transition-colors">
|
||||
<span class="material-symbols-outlined text-[18px]">edit</span>
|
||||
</button>
|
||||
<button class="p-1.5 rounded-md hover:bg-red-500/20 hover:text-red-400 text-text-secondary transition-colors">
|
||||
<span class="material-symbols-outlined text-[18px]">delete</span>
|
||||
</button>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<!-- Pagination -->
|
||||
<div class="flex items-center justify-between px-4 py-3 border-t border-[#283539]">
|
||||
<div class="flex flex-1 justify-between sm:hidden">
|
||||
<a class="relative inline-flex items-center rounded-md border border-[#283539] bg-[#111618] px-4 py-2 text-sm font-medium text-text-secondary hover:bg-[#1e272c] hover:text-white" href="#">Previous</a>
|
||||
<a class="relative ml-3 inline-flex items-center rounded-md border border-[#283539] bg-[#111618] px-4 py-2 text-sm font-medium text-text-secondary hover:bg-[#1e272c] hover:text-white" href="#">Next</a>
|
||||
</div>
|
||||
<div class="hidden sm:flex sm:flex-1 sm:items-center sm:justify-end">
|
||||
<nav aria-label="Pagination" class="isolate inline-flex -space-x-px rounded-md shadow-sm">
|
||||
<a class="relative inline-flex items-center rounded-l-md px-2 py-2 text-text-secondary ring-1 ring-inset ring-[#283539] hover:bg-[#1e272c] focus:z-20 focus:outline-offset-0" href="#">
|
||||
<span class="material-symbols-outlined text-[20px]">chevron_left</span>
|
||||
</a>
|
||||
<a aria-current="page" class="relative z-10 inline-flex items-center bg-primary px-4 py-2 text-sm font-semibold text-[#111618] focus:z-20 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary" href="#">1</a>
|
||||
<a class="relative inline-flex items-center px-4 py-2 text-sm font-semibold text-text-secondary ring-1 ring-inset ring-[#283539] hover:bg-[#1e272c] focus:z-20 focus:outline-offset-0" href="#">2</a>
|
||||
<a class="relative inline-flex items-center px-4 py-2 text-sm font-semibold text-text-secondary ring-1 ring-inset ring-[#283539] hover:bg-[#1e272c] focus:z-20 focus:outline-offset-0" href="#">3</a>
|
||||
<a class="relative inline-flex items-center rounded-r-md px-2 py-2 text-text-secondary ring-1 ring-inset ring-[#283539] hover:bg-[#1e272c] focus:z-20 focus:outline-offset-0" href="#">
|
||||
<span class="material-symbols-outlined text-[20px]">chevron_right</span>
|
||||
</a>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Right Column: Add/Edit Form -->
|
||||
<div class="xl:col-span-1">
|
||||
<div class="bg-surface-dark border border-[#283539] rounded-xl shadow-xl sticky top-8">
|
||||
<div class="px-6 py-4 border-b border-[#283539] flex justify-between items-center bg-[#1e272c] rounded-t-xl">
|
||||
<h2 class="text-lg font-bold text-white">Add New Website</h2>
|
||||
<span class="material-symbols-outlined text-text-secondary cursor-pointer hover:text-white">close</span>
|
||||
</div>
|
||||
<div class="p-6 flex flex-col gap-6">
|
||||
<!-- URL Input + Auto Fetch -->
|
||||
<div class="flex flex-col gap-3">
|
||||
<label class="text-sm font-medium text-white">Website URL <span class="text-primary">*</span></label>
|
||||
<div class="flex flex-col gap-2">
|
||||
<div class="relative">
|
||||
<input class="w-full bg-[#111618] border border-[#283539] rounded-lg py-2.5 px-3 text-sm text-white placeholder-text-secondary focus:ring-1 focus:ring-primary focus:border-primary transition-all" placeholder="https://example.com" type="url"/>
|
||||
<div class="absolute inset-y-0 right-3 flex items-center">
|
||||
<span class="material-symbols-outlined text-green-500 text-[18px]" style="display:none;">check_circle</span>
|
||||
</div>
|
||||
</div>
|
||||
<!-- CRITICAL: Auto-fetch Button -->
|
||||
<button class="flex items-center justify-center gap-2 w-full py-2 px-3 rounded-lg border border-primary/30 bg-primary/5 hover:bg-primary/10 text-primary text-sm font-medium transition-colors group" type="button">
|
||||
<span class="material-symbols-outlined text-[18px] group-hover:animate-spin">autorenew</span>
|
||||
Auto-fetch Website Info
|
||||
</button>
|
||||
<p class="text-xs text-text-secondary mt-1">
|
||||
Our AI will attempt to scrape metadata and descriptions automatically.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Other Fields -->
|
||||
<div class="flex flex-col gap-3">
|
||||
<label class="text-sm font-medium text-white">Tool Name</label>
|
||||
<input class="w-full bg-[#111618] border border-[#283539] rounded-lg py-2.5 px-3 text-sm text-white placeholder-text-secondary focus:ring-1 focus:ring-primary focus:border-primary" placeholder="e.g. ChatGPT" type="text"/>
|
||||
</div>
|
||||
<div class="flex flex-col gap-3">
|
||||
<label class="text-sm font-medium text-white">Short Description</label>
|
||||
<textarea class="w-full bg-[#111618] border border-[#283539] rounded-lg py-2.5 px-3 text-sm text-white placeholder-text-secondary focus:ring-1 focus:ring-primary focus:border-primary resize-none" placeholder="Enter a brief description of the AI tool..." rows="4"></textarea>
|
||||
</div>
|
||||
<div class="flex flex-col gap-3">
|
||||
<label class="text-sm font-medium text-white">Tags</label>
|
||||
<div class="p-3 bg-[#111618] border border-[#283539] rounded-lg min-h-[44px] flex flex-wrap gap-2">
|
||||
<span class="inline-flex items-center gap-1 px-2 py-1 rounded bg-[#283539] text-xs text-white">
|
||||
Productivity
|
||||
<span class="material-symbols-outlined text-[14px] cursor-pointer hover:text-red-400">close</span>
|
||||
</span>
|
||||
<input class="bg-transparent border-none p-0 text-sm text-white placeholder-text-secondary focus:ring-0 w-24" placeholder="+ Add tag" type="text"/>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Status Toggle -->
|
||||
<div class="flex items-center justify-between py-2">
|
||||
<label class="text-sm font-medium text-white">Active Status</label>
|
||||
<button class="w-11 h-6 bg-primary rounded-full relative cursor-pointer">
|
||||
<span class="absolute top-1 left-6 size-4 bg-white rounded-full shadow-sm transition-all"></span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="px-6 py-4 border-t border-[#283539] flex gap-3 bg-[#1e272c] rounded-b-xl">
|
||||
<button class="flex-1 py-2.5 rounded-lg bg-primary hover:bg-primary-hover text-[#111618] text-sm font-bold shadow-lg shadow-primary/20 transition-all">
|
||||
Save Website
|
||||
</button>
|
||||
<button class="px-4 py-2.5 rounded-lg border border-[#283539] bg-transparent hover:bg-[#283539] text-white text-sm font-medium transition-colors">
|
||||
Cancel
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</body></html>
|
||||
|
Before Width: | Height: | Size: 249 KiB |
@@ -1,270 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
|
||||
<html class="dark" lang="en"><head>
|
||||
<meta charset="utf-8"/>
|
||||
<meta content="width=device-width, initial-scale=1.0" name="viewport"/>
|
||||
<title>AI Tool Detail Page</title>
|
||||
<!-- Google Fonts -->
|
||||
<link href="https://fonts.googleapis.com" rel="preconnect"/>
|
||||
<link crossorigin="" href="https://fonts.gstatic.com" rel="preconnect"/>
|
||||
<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"/>
|
||||
<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>
|
||||
<!-- Theme Config -->
|
||||
<script id="tailwind-config">
|
||||
tailwind.config = {
|
||||
darkMode: "class",
|
||||
theme: {
|
||||
extend: {
|
||||
colors: {
|
||||
"primary": "#25c0f4",
|
||||
"primary-dark": "#0ea5e9",
|
||||
"secondary": "#a855f7", // Adding purple for the gradient request
|
||||
"background-light": "#f5f8f8",
|
||||
"background-dark": "#101e22",
|
||||
"surface-dark": "#1a262b",
|
||||
"surface-border": "#283539",
|
||||
"text-secondary": "#9cb2ba"
|
||||
},
|
||||
fontFamily: {
|
||||
"display": ["Space Grotesk", "sans-serif"],
|
||||
"body": ["Noto Sans", "sans-serif"]
|
||||
},
|
||||
borderRadius: {"DEFAULT": "0.25rem", "lg": "0.5rem", "xl": "0.75rem", "2xl": "1rem", "full": "9999px"},
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
<style>
|
||||
body {
|
||||
font-family: 'Space Grotesk', sans-serif;
|
||||
}
|
||||
.glass-card {
|
||||
background: rgba(26, 38, 43, 0.6);
|
||||
backdrop-filter: blur(12px);
|
||||
border: 1px solid rgba(40, 53, 57, 0.5);
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body class="bg-background-light dark:bg-background-dark text-slate-900 dark:text-white min-h-screen flex flex-col selection:bg-primary selection:text-background-dark">
|
||||
<!-- Top Navigation -->
|
||||
<div class="w-full border-b border-surface-border bg-background-dark/95 backdrop-blur sticky top-0 z-50">
|
||||
<div class="max-w-[1280px] mx-auto px-4 sm:px-6 lg:px-8">
|
||||
<header class="flex items-center justify-between h-16">
|
||||
<!-- Logo Area -->
|
||||
<div class="flex items-center gap-8">
|
||||
<div class="flex items-center gap-3 text-white">
|
||||
<div class="size-8 text-primary">
|
||||
<span class="material-symbols-outlined text-3xl">token</span>
|
||||
</div>
|
||||
<h2 class="text-white text-xl font-bold tracking-tight">AI Discovery</h2>
|
||||
</div>
|
||||
<!-- Desktop Nav -->
|
||||
<div class="hidden md:flex items-center gap-6">
|
||||
<a class="text-text-secondary hover:text-white text-sm font-medium transition-colors" href="#">Categories</a>
|
||||
<a class="text-text-secondary hover:text-white text-sm font-medium transition-colors" href="#">Submit Tool</a>
|
||||
<a class="text-text-secondary hover:text-white text-sm font-medium transition-colors" href="#">Blog</a>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Right Actions -->
|
||||
<div class="flex items-center gap-4">
|
||||
<div class="hidden sm:flex relative group">
|
||||
<div class="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
|
||||
<span class="material-symbols-outlined text-text-secondary text-[20px]">search</span>
|
||||
</div>
|
||||
<input class="bg-surface-border/50 hover:bg-surface-border focus:bg-surface-border text-white text-sm rounded-lg pl-10 pr-4 py-2 w-48 focus:w-64 transition-all outline-none ring-0 border border-transparent focus:border-primary/50 placeholder:text-text-secondary/70" placeholder="Search tools..."/>
|
||||
</div>
|
||||
<button class="flex items-center justify-center rounded-lg h-9 px-4 bg-primary hover:bg-primary-dark text-background-dark text-sm font-bold transition-colors">
|
||||
Log In
|
||||
</button>
|
||||
</div>
|
||||
</header>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Main Layout -->
|
||||
<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">
|
||||
<button class="group flex items-center gap-2 text-text-secondary 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>Back to Home</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="grid grid-cols-1 lg:grid-cols-12 gap-8 lg:gap-12">
|
||||
<!-- Left Column: Header Info + 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>
|
||||
<div class="relative size-[120px] rounded-xl bg-surface-border flex items-center justify-center overflow-hidden border border-surface-border bg-cover bg-center" data-alt="Abstract neural network pattern in deep blue and purple" style="background-image: url('https://lh3.googleusercontent.com/aida-public/AB6AXuAFm-_1orioA8YwgKaNGCzxomQeon7z13YmJCs2VpIoXP6J2a5kLgu_mC9IFA-cRA5s6yNaDPiWRxhF8tTX4FFOTbngaHydfCmAMNxU2EuZ2burR-hLOggJYl24v1Ry7GaNu6ETcYui4SIfvWqNs43vwA4vEmoogV3TxVzuDRDHsy5nj9ab4TnW4c5mVgqHPttLIF1NKTm6rtEcPnWW1jtCihIAAqZVnst9o0Z-IyAYGJoPjNajRdJZGR5NDfiBf-tdr79Bo42KY_DX');">
|
||||
</div>
|
||||
</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">NeuroGen AI</h1>
|
||||
<a class="text-primary hover:text-primary-dark hover:underline text-base font-medium flex items-center gap-1" href="#">
|
||||
www.neurogen.ai
|
||||
<span class="material-symbols-outlined text-[16px]">open_in_new</span>
|
||||
</a>
|
||||
</div>
|
||||
<!-- Tags -->
|
||||
<div class="flex flex-wrap gap-2">
|
||||
<span class="inline-flex items-center px-3 py-1 rounded-full text-xs font-medium bg-surface-border text-text-secondary border border-transparent hover:border-primary/30 transition-colors cursor-default">Generative AI</span>
|
||||
<span class="inline-flex items-center px-3 py-1 rounded-full text-xs font-medium bg-surface-border text-text-secondary border border-transparent hover:border-primary/30 transition-colors cursor-default">Copywriting</span>
|
||||
<span class="inline-flex items-center px-3 py-1 rounded-full text-xs font-medium bg-surface-border text-text-secondary border border-transparent hover:border-primary/30 transition-colors cursor-default">Productivity</span>
|
||||
<span class="inline-flex items-center px-3 py-1 rounded-full text-xs font-medium bg-purple-500/10 text-purple-400 border border-purple-500/20">Free Trial</span>
|
||||
</div>
|
||||
<!-- Stats Row -->
|
||||
<div class="flex items-center gap-6 mt-1 text-text-secondary text-sm">
|
||||
<div class="flex items-center gap-2" title="Total Views">
|
||||
<span class="material-symbols-outlined text-[18px]">visibility</span>
|
||||
<span>12,450 Views</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-2" title="Date Added">
|
||||
<span class="material-symbols-outlined text-[18px]">calendar_today</span>
|
||||
<span>Added Oct 24, 2023</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<hr class="border-surface-border/60"/>
|
||||
<!-- Main Content Body -->
|
||||
<div class="space-y-8">
|
||||
<!-- Overview -->
|
||||
<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>
|
||||
Product Overview
|
||||
</h3>
|
||||
<p class="text-text-secondary leading-relaxed text-lg">
|
||||
NeuroGen AI is an advanced copywriting assistant designed to help marketers, writers, and businesses generate high-converting content in seconds. By leveraging state-of-the-art natural language processing models, it understands context, tone, and brand voice to deliver tailored outputs.
|
||||
</p>
|
||||
</section>
|
||||
<!-- Image Gallery / Preview (Optional visual break) -->
|
||||
<div class="rounded-xl overflow-hidden border border-surface-border relative group aspect-video w-full bg-surface-dark">
|
||||
<div class="absolute inset-0 bg-cover bg-center" data-alt="Dashboard interface of NeuroGen AI showing analytics and text editor" style="background-image: url('https://lh3.googleusercontent.com/aida-public/AB6AXuCtwM-4OkGIPG4sQqABpRcLvOgKvLzuyVlQJUVcU8SqFsVod_jck_vFm-q7UX70vUaaF7UPEa--rkf18dMi5_qYcXYsjTzd5ehM3ga_OoIr1r6wnRTnRxKcrUUC5USP72vdsFJkRFeKmH6epH1Avi5tglJ732VTvoNiuys499jctwZ67d4Yu8Slfn0EO4Ny8zIjzPRGLer8lMaEeZPMagNbgn4asMLvmDtTep3mgPzwTscfzoOFVChGqNak0ZmLyOiVY7vKIapdHyoz');">
|
||||
</div>
|
||||
<div class="absolute inset-0 bg-gradient-to-t from-background-dark via-transparent to-transparent opacity-60"></div>
|
||||
<div class="absolute bottom-4 left-4">
|
||||
<p class="text-white font-medium text-sm bg-black/50 backdrop-blur px-3 py-1 rounded-lg">Dashboard Interface</p>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Detailed Description -->
|
||||
<section>
|
||||
<h3 class="text-xl font-bold text-white mb-4">Detailed Description</h3>
|
||||
<div class="text-text-secondary space-y-4 leading-relaxed">
|
||||
<p>
|
||||
Writing high-quality content consistently is a challenge for modern businesses. NeuroGen AI bridges the gap between human creativity and machine speed. Unlike generic text generators, NeuroGen allows users to fine-tune specific parameters such as emotional resonance, sentence structure complexity, and SEO keyword density.
|
||||
</p>
|
||||
<p>
|
||||
The platform includes specialized templates for:
|
||||
</p>
|
||||
<ul class="list-disc pl-5 space-y-2 text-gray-400 marker:text-primary">
|
||||
<li>Social media captions (Instagram, LinkedIn, Twitter)</li>
|
||||
<li>Long-form blog posts with automatic formatting</li>
|
||||
<li>Email marketing sequences</li>
|
||||
<li>Ad copy variants for A/B testing</li>
|
||||
</ul>
|
||||
<p>
|
||||
Security is paramount; NeuroGen ensures that your proprietary data is never used to train public models. Enterprise-grade encryption and team collaboration features make it a suitable choice for large organizations looking to scale their content operations.
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Right Column: Sidebar -->
|
||||
<div class="lg:col-span-4 space-y-6">
|
||||
<!-- CTA Card -->
|
||||
<div class="glass-card rounded-2xl p-6 flex flex-col gap-4 shadow-xl shadow-black/20 sticky top-24">
|
||||
<div class="flex items-center justify-between mb-2">
|
||||
<span class="text-white font-bold text-lg">Try it now</span>
|
||||
<span class="px-2 py-1 rounded bg-green-500/20 text-green-400 text-xs font-bold border border-green-500/20">ONLINE</span>
|
||||
</div>
|
||||
<a class="group relative flex items-center justify-center w-full overflow-hidden rounded-xl bg-gradient-to-r from-primary via-primary to-purple-500 p-[1px] focus:outline-none focus:ring-2 focus:ring-slate-400 focus:ring-offset-2 focus:ring-offset-slate-50 transition-all hover:scale-[1.02]" href="#">
|
||||
<span class="absolute inset-[-1000%] animate-[spin_2s_linear_infinite] bg-[conic-gradient(from_90deg_at_50%_50%,#E2CBFF_0%,#393BB2_50%,#E2CBFF_100%)] opacity-0 group-hover:opacity-100 transition-opacity"></span>
|
||||
<div class="relative flex h-12 w-full items-center justify-center rounded-xl bg-background-dark/10 group-hover:bg-transparent px-8 py-1 backdrop-blur-3xl transition-colors">
|
||||
<span class="flex items-center gap-2 text-background-dark font-bold text-base uppercase tracking-wide">
|
||||
Visit Website
|
||||
<span class="material-symbols-outlined text-[20px] font-bold">arrow_outward</span>
|
||||
</span>
|
||||
</div>
|
||||
<!-- Overlay for text color correction on hover/default since bg changes -->
|
||||
<div class="absolute inset-0 flex items-center justify-center pointer-events-none">
|
||||
<span class="flex items-center gap-2 text-white font-bold text-base uppercase tracking-wide group-hover:text-white transition-colors">
|
||||
Visit Website
|
||||
<span class="material-symbols-outlined text-[20px] font-bold">arrow_outward</span>
|
||||
</span>
|
||||
</div>
|
||||
</a>
|
||||
<p class="text-xs text-center text-text-secondary">
|
||||
Opens in a new tab • <span class="text-white">neurogen.ai</span>
|
||||
</p>
|
||||
</div>
|
||||
<!-- Features Box -->
|
||||
<div class="rounded-2xl border border-surface-border bg-surface-dark/50 p-6">
|
||||
<h3 class="text-lg font-bold text-white mb-5 flex items-center gap-2">
|
||||
<span class="material-symbols-outlined text-secondary">verified</span>
|
||||
Main Features
|
||||
</h3>
|
||||
<ul class="space-y-4">
|
||||
<li class="flex items-start gap-3">
|
||||
<div class="mt-0.5 rounded-full bg-primary/20 p-1">
|
||||
<span class="material-symbols-outlined text-primary text-[16px] block">check</span>
|
||||
</div>
|
||||
<div>
|
||||
<p class="text-sm font-bold text-white">Contextual Awareness</p>
|
||||
<p class="text-xs text-text-secondary mt-0.5">Understands previous inputs to maintain thread continuity.</p>
|
||||
</div>
|
||||
</li>
|
||||
<li class="flex items-start gap-3">
|
||||
<div class="mt-0.5 rounded-full bg-primary/20 p-1">
|
||||
<span class="material-symbols-outlined text-primary text-[16px] block">check</span>
|
||||
</div>
|
||||
<div>
|
||||
<p class="text-sm font-bold text-white">Multi-language Support</p>
|
||||
<p class="text-xs text-text-secondary mt-0.5">Generate content in over 25 languages natively.</p>
|
||||
</div>
|
||||
</li>
|
||||
<li class="flex items-start gap-3">
|
||||
<div class="mt-0.5 rounded-full bg-primary/20 p-1">
|
||||
<span class="material-symbols-outlined text-primary text-[16px] block">check</span>
|
||||
</div>
|
||||
<div>
|
||||
<p class="text-sm font-bold text-white">SEO Optimization</p>
|
||||
<p class="text-xs text-text-secondary mt-0.5">Built-in keyword analyzer and suggestion tool.</p>
|
||||
</div>
|
||||
</li>
|
||||
<li class="flex items-start gap-3">
|
||||
<div class="mt-0.5 rounded-full bg-primary/20 p-1">
|
||||
<span class="material-symbols-outlined text-primary text-[16px] block">check</span>
|
||||
</div>
|
||||
<div>
|
||||
<p class="text-sm font-bold text-white">Export to CMS</p>
|
||||
<p class="text-xs text-text-secondary mt-0.5">Direct integration with WordPress and Ghost.</p>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<!-- Share / Report -->
|
||||
<div class="flex gap-4">
|
||||
<button class="flex-1 py-3 rounded-lg border border-surface-border bg-transparent text-text-secondary hover:text-white hover:bg-surface-border transition-colors text-sm font-medium flex items-center justify-center gap-2">
|
||||
<span class="material-symbols-outlined text-[18px]">share</span>
|
||||
Share
|
||||
</button>
|
||||
<button class="flex-1 py-3 rounded-lg border border-surface-border bg-transparent text-text-secondary hover:text-red-400 hover:border-red-400/30 hover:bg-red-400/10 transition-colors text-sm font-medium flex items-center justify-center gap-2">
|
||||
<span class="material-symbols-outlined text-[18px]">flag</span>
|
||||
Report
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Footer Spacer -->
|
||||
<div class="h-20"></div>
|
||||
</main>
|
||||
</body></html>
|
||||
|
Before Width: | Height: | Size: 654 KiB |
@@ -1,383 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
|
||||
<html class="dark" lang="en"><head>
|
||||
<meta charset="utf-8"/>
|
||||
<meta content="width=device-width, initial-scale=1.0" name="viewport"/>
|
||||
<title>ZJPB - AI Tool Discovery</title>
|
||||
<!-- Google Fonts -->
|
||||
<link href="https://fonts.googleapis.com" rel="preconnect"/>
|
||||
<link crossorigin="" href="https://fonts.gstatic.com" rel="preconnect"/>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@300;400;500;600;700&display=swap" rel="stylesheet"/>
|
||||
<link href="https://fonts.googleapis.com/css2?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"/>
|
||||
<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 id="tailwind-config">
|
||||
tailwind.config = {
|
||||
darkMode: "class",
|
||||
theme: {
|
||||
extend: {
|
||||
colors: {
|
||||
primary: "#25c0f4",
|
||||
secondary: "#7c3aed", // Adding purple for the gradient mix
|
||||
"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>
|
||||
</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 for Tech Vibe -->
|
||||
<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="#">
|
||||
<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="#">Home</a>
|
||||
<a class="text-gray-300 hover:text-primary text-sm font-medium transition-colors" href="#">Categories</a>
|
||||
<a class="text-gray-300 hover:text-primary text-sm font-medium transition-colors" href="#">Community</a>
|
||||
<a class="text-gray-300 hover:text-primary text-sm font-medium transition-colors" href="#">About</a>
|
||||
</nav>
|
||||
</div>
|
||||
<!-- Actions -->
|
||||
<div class="flex items-center gap-4">
|
||||
<!-- Search -->
|
||||
<div class="hidden lg:flex items-center bg-surface-dark border border-border-dark rounded-full h-10 px-4 w-64 focus-within:border-primary transition-colors">
|
||||
<span class="material-symbols-outlined text-gray-400" style="font-size: 20px;">search</span>
|
||||
<input class="bg-transparent border-none text-white text-sm placeholder-gray-500 focus:ring-0 w-full ml-2" placeholder="Search AI tools..." type="text"/>
|
||||
</div>
|
||||
<!-- Auth Buttons -->
|
||||
<button 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">
|
||||
Log In
|
||||
</button>
|
||||
<button class="flex h-9 px-5 items-center justify-center rounded-lg bg-primary hover:bg-primary/90 text-background-dark text-sm font-bold transition-colors shadow-[0_0_15px_rgba(37,192,244,0.3)]">
|
||||
Sign Up
|
||||
</button>
|
||||
<!-- Mobile Menu Toggle -->
|
||||
<button class="md:hidden text-white">
|
||||
<span class="material-symbols-outlined">menu</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
<!-- Main 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工具和产品. Discover the best AI tools tailored for your workflow.
|
||||
</p>
|
||||
</div>
|
||||
<button class="flex items-center gap-2 bg-surface-dark border border-border-dark hover:border-primary text-white px-5 py-2.5 rounded-lg transition-all group whitespace-nowrap">
|
||||
<span class="material-symbols-outlined text-primary group-hover:scale-110 transition-transform">add_circle</span>
|
||||
<span class="font-bold text-sm">Submit a Tool</span>
|
||||
</button>
|
||||
</div>
|
||||
<!-- Filter Chips -->
|
||||
<div class="w-full overflow-x-auto pb-2 scrollbar-hide">
|
||||
<div class="flex gap-3 min-w-max">
|
||||
<button class="flex items-center h-9 px-5 rounded-full bg-primary text-background-dark font-bold text-sm shadow-[0_0_10px_rgba(37,192,244,0.4)]">
|
||||
All Tools
|
||||
</button>
|
||||
<button class="flex items-center gap-2 h-9 px-5 rounded-full bg-surface-dark border border-border-dark text-gray-300 hover:text-white hover:border-gray-500 font-medium text-sm transition-all">
|
||||
<span class="material-symbols-outlined text-sm">chat</span> AI Chat
|
||||
</button>
|
||||
<button class="flex items-center gap-2 h-9 px-5 rounded-full bg-surface-dark border border-border-dark text-gray-300 hover:text-white hover:border-gray-500 font-medium text-sm transition-all">
|
||||
<span class="material-symbols-outlined text-sm">image</span> Image Gen
|
||||
</button>
|
||||
<button class="flex items-center gap-2 h-9 px-5 rounded-full bg-surface-dark border border-border-dark text-gray-300 hover:text-white hover:border-gray-500 font-medium text-sm transition-all">
|
||||
<span class="material-symbols-outlined text-sm">movie</span> Video
|
||||
</button>
|
||||
<button class="flex items-center gap-2 h-9 px-5 rounded-full bg-surface-dark border border-border-dark text-gray-300 hover:text-white hover:border-gray-500 font-medium text-sm transition-all">
|
||||
<span class="material-symbols-outlined text-sm">edit_note</span> Writing
|
||||
</button>
|
||||
<button class="flex items-center gap-2 h-9 px-5 rounded-full bg-surface-dark border border-border-dark text-gray-300 hover:text-white hover:border-gray-500 font-medium text-sm transition-all">
|
||||
<span class="material-symbols-outlined text-sm">code</span> Coding
|
||||
</button>
|
||||
<button class="flex items-center gap-2 h-9 px-5 rounded-full bg-surface-dark border border-border-dark text-gray-300 hover:text-white hover:border-gray-500 font-medium text-sm transition-all">
|
||||
<span class="material-symbols-outlined text-sm">graphic_eq</span> Audio
|
||||
</button>
|
||||
<button class="flex items-center gap-2 h-9 px-5 rounded-full bg-surface-dark border border-border-dark text-gray-300 hover:text-white hover:border-gray-500 font-medium text-sm transition-all">
|
||||
<span class="material-symbols-outlined text-sm">view_in_ar</span> 3D Assets
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Tool Grid -->
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-6">
|
||||
<!-- Card 1 -->
|
||||
<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">
|
||||
<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">
|
||||
<div class="size-12 rounded-lg bg-cover bg-center shadow-lg" data-alt="ChatGPT logo icon green background" style="background-image: url('https://lh3.googleusercontent.com/aida-public/AB6AXuBStb1HsLpCc4Hlu8FzN0ecAT6pMnnu_pzF4cUUQDG6FdYLA65ua60D-3NxKDOHqkuVsgsy9ku-vUZ0_Jyc5O9Xi1NlwR5L7oC9gt_jretgJqsEeOk1dm2yo4GvB26KgmMWzpUKeCtIg1NyZDWRNJF3gfIVxF95sT29iy6tKSXsUdfVsOo-o_5kEpB3qCFKZdMo4fhOe8DVh4JnmmdZuS8z3PflupzZpru6F0QK3xL407vfIULuQ3L5NRtFdTfZs7O-elYl2FrwjJ_x');"></div>
|
||||
<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">ChatGPT</h3>
|
||||
<p class="text-gray-400 text-sm mt-2 line-clamp-2">OpenAI's advanced conversational model capable of understanding and generating human-like text.</p>
|
||||
</div>
|
||||
<div class="mt-auto flex items-center justify-between pt-4 border-t border-border-dark">
|
||||
<div class="flex gap-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">Chat</span>
|
||||
<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">NLP</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-1 text-gray-500 text-xs">
|
||||
<span class="material-symbols-outlined text-[14px]">visibility</span>
|
||||
<span>1.2M</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Card 2 -->
|
||||
<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">
|
||||
<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">
|
||||
<div class="size-12 rounded-lg bg-cover bg-center shadow-lg" data-alt="Midjourney logo icon blue background" style="background-image: url('https://lh3.googleusercontent.com/aida-public/AB6AXuBW8nuPBGShgvw9aCK5p0mKlki3EMfhi8ZZKvLTaM-QupUQDzsBx5wkIvN_Unyt30H2WHXBw5GB4Kz8gL68peWqK99Cpo6gRM-Bk3f94xWdYji9osPrreu5UMVtu2W1Rn8IyBCfPDUu3LJnbDd_viZz4nb04tliq9O_ezo8OCYMede7GZYxIxHTrEkhNBADzC2Z7KjeF-DR7fRBf4dxHYz5rJUGakf1qyEouaAlEDWMnBymOKFkQGK19UvizQiQIUeDFMGMxgL0ng_x');"></div>
|
||||
<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">Midjourney</h3>
|
||||
<p class="text-gray-400 text-sm mt-2 line-clamp-2">Hyper-realistic AI image generator that creates stunning visuals from text prompts.</p>
|
||||
</div>
|
||||
<div class="mt-auto flex items-center justify-between pt-4 border-t border-border-dark">
|
||||
<div class="flex gap-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">Image</span>
|
||||
<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">Art</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-1 text-gray-500 text-xs">
|
||||
<span class="material-symbols-outlined text-[14px]">visibility</span>
|
||||
<span>850k</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Card 3 -->
|
||||
<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">
|
||||
<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">
|
||||
<div class="size-12 rounded-lg bg-cover bg-center shadow-lg" data-alt="Jasper logo icon yellow background" style="background-image: url('https://lh3.googleusercontent.com/aida-public/AB6AXuAQv96KYBZ3uV5NpDJyuDzc5J-mGo3fTWcBzVv00wC062ApfHezk6XkvKI6lsad5hFopxB7Qqhbqifto971lBo_7ASfOFUvtSU-Z3omU0q4PGcmea1YR_Va7vKPBtpA1DS_uq779QNTY5oF_kjapJrp7-Nfi5tI0NfQ3kU5KOd-dwBrN2T1Md6nT1jUEeOKG3zEgmAfEYan4mQXQXpz7Ywh2ZaoFCOY0OTiJP9RAnzIYL0Tv-ywt3_r66CSTY5pyzfqqP7sUk4vegAv');"></div>
|
||||
<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">Jasper AI</h3>
|
||||
<p class="text-gray-400 text-sm mt-2 line-clamp-2">AI copywriter for marketing content, blog posts, and social media captions.</p>
|
||||
</div>
|
||||
<div class="mt-auto flex items-center justify-between pt-4 border-t border-border-dark">
|
||||
<div class="flex gap-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">Writing</span>
|
||||
<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">Marketing</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-1 text-gray-500 text-xs">
|
||||
<span class="material-symbols-outlined text-[14px]">visibility</span>
|
||||
<span>300k</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Card 4 -->
|
||||
<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">
|
||||
<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">
|
||||
<div class="size-12 rounded-lg bg-cover bg-center shadow-lg" data-alt="Runway logo icon pink background" style="background-image: url('https://lh3.googleusercontent.com/aida-public/AB6AXuAkmxk1Ar1KxgHHM5pKMer4rOra0KUieDAXhm9nE4sQp5WjsnGYENKmbM-EN-hRjhV7OC2ha7Go-Cl90HgfNpbgt--grIpJYKoUjgvsp0juLIDgX_fN4rKrjdkezxRU8YVJBuTaM3lWZJCptm3I6isDO10xidrChOc5kWV9SQny79KEVKENXxOJXEVT3c0m16M3JnGRTRmu-6EY8XU68-On78-7SXxLyP5TESi-ooZ2wHaOwxqJUwFb-oQyQoKAkXnzXAqGwwITBiLS');"></div>
|
||||
<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">Runway Gen-2</h3>
|
||||
<p class="text-gray-400 text-sm mt-2 line-clamp-2">Next-generation video creation tool that turns text into high-quality video clips.</p>
|
||||
</div>
|
||||
<div class="mt-auto flex items-center justify-between pt-4 border-t border-border-dark">
|
||||
<div class="flex gap-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">Video</span>
|
||||
<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">Gen-AI</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-1 text-gray-500 text-xs">
|
||||
<span class="material-symbols-outlined text-[14px]">visibility</span>
|
||||
<span>420k</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Card 5 -->
|
||||
<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">
|
||||
<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">
|
||||
<div class="size-12 rounded-lg bg-cover bg-center shadow-lg" data-alt="Copilot logo icon purple background" style="background-image: url('https://lh3.googleusercontent.com/aida-public/AB6AXuBki2piUPdnWstozizUU63ouakjjA3B4xN5uQs7rA84yX4NsYFIaNybtfVRy0bXUKfUBYdixo22jb3bbN6TlKindW5iQU0sJc3FF2GXS_CRpgLoNMLziOyf4rLArNmM2VoycHJ3DyrIsI8847xlO3BHrK8kyemNc9mBfaXyPnWsJ0hnF2lXfljCOcYNQTtY1qgzw5RfOHwA0NUnLwuRG53tU-mnJPPejbDyzW7YWdBnh9yTj3dnJJDOfzYiEx252wgf7Q6GBjTGrQnG');"></div>
|
||||
<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">GitHub Copilot</h3>
|
||||
<p class="text-gray-400 text-sm mt-2 line-clamp-2">Your AI pair programmer that helps you write better code faster.</p>
|
||||
</div>
|
||||
<div class="mt-auto flex items-center justify-between pt-4 border-t border-border-dark">
|
||||
<div class="flex gap-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">Dev</span>
|
||||
<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">Coding</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-1 text-gray-500 text-xs">
|
||||
<span class="material-symbols-outlined text-[14px]">visibility</span>
|
||||
<span>1.5M</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Card 6 -->
|
||||
<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">
|
||||
<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">
|
||||
<div class="size-12 rounded-lg bg-cover bg-center shadow-lg" data-alt="ElevenLabs logo icon red background" style="background-image: url('https://lh3.googleusercontent.com/aida-public/AB6AXuBVqan6FEM01JlSNs0aKYTXdgzFAXn1xT_Yx0S3VszDGf5x0hsptwFAxMNtCEtJZOW65aZMncYKyPpzdAoIHLaBDk_ORHur89Redtuublqbd51Cr6sKVdMJ2VAAihNuVPsvpNBdIiLGuf40kXbe3HJrdYVNAKi27xcxBICheI4OzkFF7uJvChUyDbumqLRFUDMssCuxZIQYTeDKbTT628ZBFUN2H8u0RfVSPEyEhJVSE2NCAD4mH-dWGmsQzMWjmeZkwXJKpw1TGHFp');"></div>
|
||||
<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">ElevenLabs</h3>
|
||||
<p class="text-gray-400 text-sm mt-2 line-clamp-2">The most realistic AI voice generator and text-to-speech software.</p>
|
||||
</div>
|
||||
<div class="mt-auto flex items-center justify-between pt-4 border-t border-border-dark">
|
||||
<div class="flex gap-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">Audio</span>
|
||||
<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">TTS</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-1 text-gray-500 text-xs">
|
||||
<span class="material-symbols-outlined text-[14px]">visibility</span>
|
||||
<span>210k</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Card 7 -->
|
||||
<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">
|
||||
<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">
|
||||
<div class="size-12 rounded-lg bg-cover bg-center shadow-lg" data-alt="Stable Diffusion logo icon teal background" style="background-image: url('https://lh3.googleusercontent.com/aida-public/AB6AXuB4zzlwlZK6dJVjV4vvFGvRmtsvd7dvQMCC1mAzFfEqA2sYnJnEdTDAF76lfVYkaKzibwuppHRIcEgbT8xaoWOBF1qdMiV3Qs3P9fEOLJ2jApPPqJyBnPXsvxFsI6FTlZ0SS_Rpblvjln_n_YLh8Wi_VWh8lCJirMM5vMCuyrNGK9Crxv3bnJzlbH34i9vmxSxSQ5uCmoAE5GPxkD23vzun9BBOKjRK-Ln7DhQ_bGhnVxkxEtM8Z-DVuGg2dNE7WnD7BEBXTOBh5XQa');"></div>
|
||||
<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">Stable Diffusion</h3>
|
||||
<p class="text-gray-400 text-sm mt-2 line-clamp-2">Open source latent text-to-image diffusion model for image generation.</p>
|
||||
</div>
|
||||
<div class="mt-auto flex items-center justify-between pt-4 border-t border-border-dark">
|
||||
<div class="flex gap-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">Open Source</span>
|
||||
<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">Image</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-1 text-gray-500 text-xs">
|
||||
<span class="material-symbols-outlined text-[14px]">visibility</span>
|
||||
<span>950k</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Card 8 -->
|
||||
<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">
|
||||
<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">
|
||||
<div class="size-12 rounded-lg bg-cover bg-center shadow-lg" data-alt="Notion AI logo icon indigo background" style="background-image: url('https://lh3.googleusercontent.com/aida-public/AB6AXuAgnpQqxiQhRd6xj43Q0hY2EQTna5khOzvl_NxO3OJN_7_nsfC0wP1x7ZmfBEvEEOoGi7NHVx6pvOpJb7Zk7dvUQWD64Y7Nrhc0mi0wsVIB1U8OfG1wioEusBqkI6zJQQh8W0om8gi2ubnJn2McHrvANulIAVnRxn1zPcy-SAnW1rLynAdVFKurBX5qK3BFBi_knAMoL8bTL_1LTxIdhdCggw6WeCGsFgUu6vqwjq9prGC-j94Fr8GibbjDSABY36P1OoZg59Aiv1M4');"></div>
|
||||
<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">Notion AI</h3>
|
||||
<p class="text-gray-400 text-sm mt-2 line-clamp-2">Access the limitless power of AI, right inside your Notion workspace.</p>
|
||||
</div>
|
||||
<div class="mt-auto flex items-center justify-between pt-4 border-t border-border-dark">
|
||||
<div class="flex gap-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">Productivity</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-1 text-gray-500 text-xs">
|
||||
<span class="material-symbols-outlined text-[14px]">visibility</span>
|
||||
<span>600k</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Pagination -->
|
||||
<div class="flex items-center justify-center py-4">
|
||||
<nav class="flex items-center gap-2">
|
||||
<a class="flex size-10 items-center justify-center rounded-full border border-border-dark text-gray-400 hover:text-white hover:border-primary transition-colors" href="#">
|
||||
<span class="material-symbols-outlined text-base">chevron_left</span>
|
||||
</a>
|
||||
<a class="flex size-10 items-center justify-center rounded-full bg-primary text-background-dark text-sm font-bold shadow-[0_0_10px_rgba(37,192,244,0.3)]" href="#">1</a>
|
||||
<a class="flex size-10 items-center justify-center rounded-full border border-transparent hover:bg-surface-dark text-gray-400 hover:text-white text-sm font-medium transition-colors" href="#">2</a>
|
||||
<a class="flex size-10 items-center justify-center rounded-full border border-transparent hover:bg-surface-dark text-gray-400 hover:text-white text-sm font-medium transition-colors" href="#">3</a>
|
||||
<span class="flex size-10 items-center justify-center text-gray-600">...</span>
|
||||
<a class="flex size-10 items-center justify-center rounded-full border border-transparent hover:bg-surface-dark text-gray-400 hover:text-white text-sm font-medium transition-colors" href="#">12</a>
|
||||
<a class="flex size-10 items-center justify-center rounded-full border border-border-dark text-gray-400 hover:text-white hover:border-primary transition-colors" href="#">
|
||||
<span class="material-symbols-outlined text-base">chevron_right</span>
|
||||
</a>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
<!-- Simple 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>© 2023 ZJPB AI Directory. All rights reserved.</span>
|
||||
</div>
|
||||
<div class="flex gap-6">
|
||||
<a class="text-gray-500 hover:text-primary transition-colors" href="#">
|
||||
<i class="fab fa-twitter text-lg"></i> <!-- Placeholder for social icon -->
|
||||
<span class="text-sm">Twitter</span>
|
||||
</a>
|
||||
<a class="text-gray-500 hover:text-primary transition-colors" href="#">
|
||||
<span class="text-sm">Discord</span>
|
||||
</a>
|
||||
<a class="text-gray-500 hover:text-primary transition-colors" href="#">
|
||||
<span class="text-sm">Privacy Policy</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
</div>
|
||||
</body></html>
|
||||
|
Before Width: | Height: | Size: 354 KiB |
363
templates/admin/batch_import.html
Normal file
@@ -0,0 +1,363 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>批量导入 - ZJPB 焦提示词</title>
|
||||
|
||||
<!-- 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">
|
||||
|
||||
<!-- Google Material Symbols -->
|
||||
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,GRAD@20..48,100..700,0..1,-50..200" />
|
||||
|
||||
<!-- Bootstrap CSS -->
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@4.6.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
|
||||
<!-- Custom Admin Theme -->
|
||||
<link href="{{ url_for('static', filename='css/admin-sidebar.css') }}" rel="stylesheet">
|
||||
<link href="{{ url_for('static', filename='css/admin-actions.css') }}" rel="stylesheet">
|
||||
</head>
|
||||
<body class="admin-sidebar-layout">
|
||||
<!-- 左侧菜单栏 -->
|
||||
<aside class="admin-sidebar">
|
||||
<!-- Logo -->
|
||||
<div class="sidebar-logo">
|
||||
<span class="material-symbols-outlined logo-icon">blur_on</span>
|
||||
<span class="logo-text">ZJPB 焦提示词</span>
|
||||
</div>
|
||||
|
||||
<!-- 主菜单 -->
|
||||
<nav class="sidebar-nav">
|
||||
<div class="nav-section">
|
||||
<div class="nav-section-title">主菜单</div>
|
||||
<ul class="nav-menu">
|
||||
<li class="nav-item">
|
||||
<a href="{{ url_for('admin.index') }}" class="nav-link">
|
||||
<span class="material-symbols-outlined nav-icon">dashboard</span>
|
||||
<span class="nav-text">控制台</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a href="{{ url_for('site.index_view') }}" class="nav-link">
|
||||
<span class="material-symbols-outlined nav-icon">public</span>
|
||||
<span class="nav-text">网站管理</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a href="{{ url_for('tag.index_view') }}" class="nav-link">
|
||||
<span class="material-symbols-outlined nav-icon">label</span>
|
||||
<span class="nav-text">标签管理</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a href="{{ url_for('news.index_view') }}" class="nav-link">
|
||||
<span class="material-symbols-outlined nav-icon">newspaper</span>
|
||||
<span class="nav-text">新闻管理</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a href="{{ url_for('admin_users.index_view') }}" class="nav-link">
|
||||
<span class="material-symbols-outlined nav-icon">admin_panel_settings</span>
|
||||
<span class="nav-text">管理员</span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<!-- 系统菜单 -->
|
||||
<div class="nav-section">
|
||||
<div class="nav-section-title">系统</div>
|
||||
<ul class="nav-menu">
|
||||
<li class="nav-item active">
|
||||
<a href="{{ url_for('batch_import') }}" class="nav-link">
|
||||
<span class="material-symbols-outlined nav-icon">upload_file</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>
|
||||
<span class="nav-text">查看网站</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a href="{{ url_for('admin_logout') }}" class="nav-link">
|
||||
<span class="material-symbols-outlined nav-icon">logout</span>
|
||||
<span class="nav-text">退出登录</span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<!-- 用户信息 -->
|
||||
<div class="sidebar-user">
|
||||
<div class="user-avatar">
|
||||
<span class="material-symbols-outlined">account_circle</span>
|
||||
</div>
|
||||
<div class="user-info">
|
||||
<div class="user-name">{{ current_user.username }}</div>
|
||||
<div class="user-email">{{ current_user.email or 'admin@zjpb.com' }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</aside>
|
||||
|
||||
<!-- 右侧主内容区 -->
|
||||
<div class="admin-main">
|
||||
<!-- 顶部导航栏 -->
|
||||
<header class="admin-header">
|
||||
<div class="header-breadcrumb">
|
||||
<a href="{{ url_for('admin.index') }}" class="breadcrumb-link">控制台</a>
|
||||
<span class="breadcrumb-separator">/</span>
|
||||
<span class="breadcrumb-current">批量导入</span>
|
||||
</div>
|
||||
<div class="header-actions">
|
||||
<div class="search-box">
|
||||
<span class="material-symbols-outlined search-icon">search</span>
|
||||
<input type="text" placeholder="全局搜索..." class="search-input">
|
||||
</div>
|
||||
<button class="header-btn">
|
||||
<span class="material-symbols-outlined">notifications</span>
|
||||
</button>
|
||||
<button class="header-btn">
|
||||
<span class="material-symbols-outlined">settings</span>
|
||||
</button>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<!-- 页面内容 -->
|
||||
<main class="admin-content">
|
||||
<div class="page-header">
|
||||
<div>
|
||||
<h1 class="page-title">批量导入网站</h1>
|
||||
<p class="page-description">支持通过URL列表或Chrome书签文件批量导入网站</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% with messages = get_flashed_messages(with_categories=true) %}
|
||||
{% if messages %}
|
||||
{% for category, message in messages %}
|
||||
<div class="alert alert-{{ 'success' if category == 'success' else 'danger' }} alert-dismissible fade show" role="alert">
|
||||
{{ message }}
|
||||
<button type="button" class="close" data-dismiss="alert">
|
||||
<span>×</span>
|
||||
</button>
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% endwith %}
|
||||
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<ul class="nav nav-tabs mb-4" role="tablist">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link active" data-toggle="tab" href="#url-list">URL列表导入</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" data-toggle="tab" href="#bookmark-file">Chrome书签导入</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<div class="tab-content">
|
||||
<!-- URL列表导入 -->
|
||||
<div class="tab-pane fade show active" id="url-list">
|
||||
<form method="POST" action="{{ url_for('batch_import') }}">
|
||||
<input type="hidden" name="import_type" value="url_list">
|
||||
|
||||
<div class="form-group">
|
||||
<label for="url-textarea">网站URL列表</label>
|
||||
<textarea class="form-control" id="url-textarea" name="url_list" rows="10"
|
||||
placeholder="每行一个URL,例如: https://www.example.com https://www.google.com https://github.com"></textarea>
|
||||
<small class="form-text text-muted">
|
||||
每行输入一个网站URL,系统将自动抓取网站名称、描述和Logo
|
||||
</small>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<div class="custom-control custom-checkbox">
|
||||
<input type="checkbox" class="custom-control-input" id="auto-activate" name="auto_activate" checked>
|
||||
<label class="custom-control-label" for="auto-activate">自动启用导入的网站</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button type="submit" class="btn btn-primary">
|
||||
<span class="material-symbols-outlined" style="font-size: 18px; vertical-align: middle;">upload_file</span>
|
||||
开始导入
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<!-- Chrome书签导入 -->
|
||||
<div class="tab-pane fade" id="bookmark-file">
|
||||
<form method="POST" action="{{ url_for('batch_import') }}" enctype="multipart/form-data">
|
||||
<input type="hidden" name="import_type" value="bookmark_file">
|
||||
|
||||
<div class="alert alert-info">
|
||||
<strong>如何导出Chrome书签?</strong>
|
||||
<ol class="mb-0 mt-2">
|
||||
<li>打开Chrome浏览器</li>
|
||||
<li>按 <kbd>Ctrl + Shift + O</kbd> 打开书签管理器</li>
|
||||
<li>点击右上角的 <strong>⋮</strong> 菜单</li>
|
||||
<li>选择 <strong>导出书签</strong></li>
|
||||
<li>保存为HTML文件</li>
|
||||
</ol>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="bookmark-file-input">选择书签文件</label>
|
||||
<div class="custom-file">
|
||||
<input type="file" class="custom-file-input" id="bookmark-file-input"
|
||||
name="bookmark_file" accept=".html,.htm" required>
|
||||
<label class="custom-file-label" for="bookmark-file-input">选择文件...</label>
|
||||
</div>
|
||||
<small class="form-text text-muted">
|
||||
仅支持HTML格式的Chrome书签导出文件
|
||||
</small>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="folder-filter">筛选文件夹(可选)</label>
|
||||
<input type="text" class="form-control" id="folder-filter" name="folder_filter"
|
||||
placeholder="例如:AI工具">
|
||||
<small class="form-text text-muted">
|
||||
留空则导入所有书签,填写文件夹名称则只导入该文件夹下的书签
|
||||
</small>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<div class="custom-control custom-checkbox">
|
||||
<input type="checkbox" class="custom-control-input" id="auto-activate-2" name="auto_activate" checked>
|
||||
<label class="custom-control-label" for="auto-activate-2">自动启用导入的网站</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button type="submit" class="btn btn-primary">
|
||||
<span class="material-symbols-outlined" style="font-size: 18px; vertical-align: middle;">upload_file</span>
|
||||
开始导入
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% if results %}
|
||||
<div class="card mt-4">
|
||||
<div class="card-header">
|
||||
<h5 class="mb-0">导入结果</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="alert alert-success">
|
||||
<strong>导入完成!</strong>
|
||||
成功: {{ results.success_count }},
|
||||
失败: {{ results.failed_count }},
|
||||
总计: {{ results.total_count }}
|
||||
</div>
|
||||
|
||||
{% if results.success_list %}
|
||||
<h6>成功导入 ({{ results.success_count }})</h6>
|
||||
<ul class="list-group mb-3">
|
||||
{% for item in results.success_list %}
|
||||
<li class="list-group-item">
|
||||
<span class="material-symbols-outlined text-success" style="font-size: 18px; vertical-align: middle;">check_circle</span>
|
||||
<strong>{{ item.name }}</strong> - {{ item.url }}
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endif %}
|
||||
|
||||
{% if results.failed_list %}
|
||||
<h6 class="text-danger">导入失败 ({{ results.failed_count }})</h6>
|
||||
<div class="alert alert-warning">
|
||||
<small><strong>提示:</strong>失败的URL不会影响其他URL的导入,您可以稍后手动添加这些网站。</small>
|
||||
</div>
|
||||
<div class="table-responsive">
|
||||
<table class="table table-sm table-hover">
|
||||
<thead class="thead-light">
|
||||
<tr>
|
||||
<th style="width: 40px;">#</th>
|
||||
<th style="width: 30%;">网站名称</th>
|
||||
<th style="width: 35%;">URL</th>
|
||||
<th style="width: 35%;">失败原因</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for item in results.failed_list %}
|
||||
<tr>
|
||||
<td>
|
||||
<span class="material-symbols-outlined text-danger" style="font-size: 20px;">cancel</span>
|
||||
</td>
|
||||
<td>
|
||||
<strong>{{ item.name or '未知' }}</strong>
|
||||
</td>
|
||||
<td>
|
||||
<small class="text-muted" style="word-break: break-all;">{{ item.url }}</small>
|
||||
</td>
|
||||
<td>
|
||||
<span class="badge badge-danger">{{ item.error }}</span>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
</main>
|
||||
</div>
|
||||
|
||||
<!-- Bootstrap JS -->
|
||||
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@4.6.0/dist/js/bootstrap.bundle.min.js"></script>
|
||||
|
||||
<style>
|
||||
.page-title {
|
||||
font-size: 24px;
|
||||
font-weight: 600;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.page-description {
|
||||
color: #666;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
.card {
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
|
||||
border: 1px solid #e0e0e0;
|
||||
}
|
||||
|
||||
.card-header {
|
||||
background: #f8f9fa;
|
||||
border-bottom: 1px solid #dee2e6;
|
||||
}
|
||||
|
||||
kbd {
|
||||
background-color: #f7f7f7;
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 3px;
|
||||
box-shadow: 0 1px 0 rgba(0,0,0,0.2);
|
||||
color: #333;
|
||||
display: inline-block;
|
||||
font-family: monospace;
|
||||
font-size: 12px;
|
||||
padding: 2px 6px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
// 文件选择器显示文件名
|
||||
document.querySelector('.custom-file-input').addEventListener('change', function(e) {
|
||||
var fileName = e.target.files[0].name;
|
||||
var label = e.target.nextElementSibling;
|
||||
label.textContent = fileName;
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -8,10 +8,14 @@
|
||||
<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">
|
||||
<style>
|
||||
/* 强制应用亮色主题到body */
|
||||
body {
|
||||
background: #F3F3F3 !important;
|
||||
color: #000000 !important;
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, 'Noto Sans', sans-serif !important;
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
||||
|
||||
{% block body %}
|
||||
<body class="admin-theme">
|
||||
{{ super() }}
|
||||
</body>
|
||||
{% endblock %}
|
||||
{% block body_class %}admin-theme{% endblock %}
|
||||
|
||||
239
templates/admin/index.html
Normal file
@@ -0,0 +1,239 @@
|
||||
{% extends 'admin/master.html' %}
|
||||
|
||||
{% block body %}
|
||||
<div class="dashboard-container">
|
||||
<div class="row">
|
||||
<!-- 统计卡片 -->
|
||||
<div class="col-md-3 mb-4">
|
||||
<div class="stat-card">
|
||||
<div class="stat-icon" style="background: rgba(0, 82, 217, 0.1); color: #0052D9;">
|
||||
<span class="material-symbols-outlined">public</span>
|
||||
</div>
|
||||
<div class="stat-info">
|
||||
<div class="stat-value">{{ stats.sites_count or 0 }}</div>
|
||||
<div class="stat-label">AI工具总数</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-3 mb-4">
|
||||
<div class="stat-card">
|
||||
<div class="stat-icon" style="background: rgba(0, 168, 112, 0.1); color: #00A870;">
|
||||
<span class="material-symbols-outlined">label</span>
|
||||
</div>
|
||||
<div class="stat-info">
|
||||
<div class="stat-value">{{ stats.tags_count or 0 }}</div>
|
||||
<div class="stat-label">标签分类</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-3 mb-4">
|
||||
<div class="stat-card">
|
||||
<div class="stat-icon" style="background: rgba(227, 115, 24, 0.1); color: #E37318;">
|
||||
<span class="material-symbols-outlined">newspaper</span>
|
||||
</div>
|
||||
<div class="stat-info">
|
||||
<div class="stat-value">{{ stats.news_count or 0 }}</div>
|
||||
<div class="stat-label">新闻动态</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-3 mb-4">
|
||||
<div class="stat-card">
|
||||
<div class="stat-icon" style="background: rgba(213, 73, 65, 0.1); color: #D54941;">
|
||||
<span class="material-symbols-outlined">visibility</span>
|
||||
</div>
|
||||
<div class="stat-info">
|
||||
<div class="stat-value">{{ stats.total_views or 0 }}</div>
|
||||
<div class="stat-label">总浏览量</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 快捷操作 -->
|
||||
<div class="row">
|
||||
<div class="col-md-12 mb-4">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h5 class="mb-0">快捷操作</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="quick-actions">
|
||||
<a href="{{ url_for('site.create_view') }}" class="quick-action-btn">
|
||||
<span class="material-symbols-outlined">add_circle</span>
|
||||
<span>添加新工具</span>
|
||||
</a>
|
||||
<a href="{{ url_for('tag.index_view') }}" class="quick-action-btn">
|
||||
<span class="material-symbols-outlined">label</span>
|
||||
<span>管理标签</span>
|
||||
</a>
|
||||
<a href="{{ url_for('news.create_view') }}" class="quick-action-btn">
|
||||
<span class="material-symbols-outlined">post_add</span>
|
||||
<span>发布新闻</span>
|
||||
</a>
|
||||
<a href="{{ url_for('index') }}" class="quick-action-btn" target="_blank">
|
||||
<span class="material-symbols-outlined">open_in_new</span>
|
||||
<span>查看网站</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 最近添加的工具 -->
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h5 class="mb-0">最近添加的工具</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
{% if recent_sites %}
|
||||
<div class="table-responsive">
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>名称</th>
|
||||
<th>URL</th>
|
||||
<th>浏览量</th>
|
||||
<th>状态</th>
|
||||
<th>添加时间</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for site in recent_sites %}
|
||||
<tr>
|
||||
<td>
|
||||
<div class="d-flex align-items-center">
|
||||
{% if site.logo %}
|
||||
<img src="{{ site.logo }}" alt="{{ site.name }}" style="width: 32px; height: 32px; border-radius: 4px; margin-right: 12px; object-fit: cover;">
|
||||
{% endif %}
|
||||
<strong>{{ site.name }}</strong>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<a href="{{ site.url }}" target="_blank" style="color: #0052D9; text-decoration: none;">
|
||||
{{ site.url[:50] }}...
|
||||
</a>
|
||||
</td>
|
||||
<td>{{ site.view_count }}</td>
|
||||
<td>
|
||||
{% if site.is_active %}
|
||||
<span class="badge badge-success">已启用</span>
|
||||
{% else %}
|
||||
<span class="badge badge-secondary">已禁用</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>{{ site.created_at.strftime('%Y-%m-%d %H:%M') }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{% else %}
|
||||
<p class="text-muted text-center mb-0">暂无数据</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.dashboard-container {
|
||||
max-width: 1400px;
|
||||
}
|
||||
|
||||
.stat-card {
|
||||
background: white;
|
||||
border: 1px solid #DCDFE6;
|
||||
border-radius: 6px;
|
||||
padding: 20px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 16px;
|
||||
box-shadow: 0 1px 4px rgba(0, 0, 0, .05);
|
||||
}
|
||||
|
||||
.stat-icon {
|
||||
width: 56px;
|
||||
height: 56px;
|
||||
border-radius: 8px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.stat-icon .material-symbols-outlined {
|
||||
font-size: 28px;
|
||||
}
|
||||
|
||||
.stat-info {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.stat-value {
|
||||
font-size: 28px;
|
||||
font-weight: 600;
|
||||
color: #000000;
|
||||
line-height: 1;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.stat-label {
|
||||
font-size: 14px;
|
||||
color: #606266;
|
||||
}
|
||||
|
||||
.quick-actions {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.quick-action-btn {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
padding: 16px;
|
||||
background: #F5F7FA;
|
||||
border: 1px solid #DCDFE6;
|
||||
border-radius: 6px;
|
||||
color: #000000;
|
||||
text-decoration: none;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
|
||||
.quick-action-btn:hover {
|
||||
background: #ECF2FE;
|
||||
border-color: #0052D9;
|
||||
color: #0052D9;
|
||||
text-decoration: none;
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
.quick-action-btn .material-symbols-outlined {
|
||||
font-size: 24px;
|
||||
}
|
||||
|
||||
.badge {
|
||||
padding: 4px 8px;
|
||||
font-size: 12px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.badge-success {
|
||||
background: rgba(0, 168, 112, 0.1);
|
||||
color: #00A870;
|
||||
}
|
||||
|
||||
.badge-secondary {
|
||||
background: #F5F5F5;
|
||||
color: #606266;
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
||||
176
templates/admin/master.html
Normal file
@@ -0,0 +1,176 @@
|
||||
{% import 'admin/layout.html' as layout with context -%}
|
||||
{% import 'admin/static.html' as admin_static with context %}
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>{% block title %}{% if admin_view.category %}{{ admin_view.category }} - {% endif %}{{ admin_view.name }} - {{ admin_view.admin.name }}{% endblock %}</title>
|
||||
|
||||
<!-- 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">
|
||||
|
||||
<!-- Google Material Symbols -->
|
||||
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,GRAD@20..48,100..700,0..1,-50..200" />
|
||||
|
||||
<!-- Bootstrap CSS -->
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@4.6.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
|
||||
<!-- Custom Admin Theme -->
|
||||
<link href="{{ url_for('static', filename='css/admin-sidebar.css') }}" rel="stylesheet">
|
||||
<link href="{{ url_for('static', filename='css/admin-actions.css') }}" rel="stylesheet">
|
||||
<!-- 暂时禁用中文化CSS,等待精确调整 -->
|
||||
<!-- <link href="{{ url_for('static', filename='css/admin-i18n.css') }}" rel="stylesheet"> -->
|
||||
|
||||
{% block head_css %}{% endblock %}
|
||||
</head>
|
||||
<body class="admin-sidebar-layout">
|
||||
<!-- 左侧菜单栏 -->
|
||||
<aside class="admin-sidebar">
|
||||
<!-- Logo -->
|
||||
<div class="sidebar-logo">
|
||||
<span class="material-symbols-outlined logo-icon">blur_on</span>
|
||||
<span class="logo-text">ZJPB 焦提示词</span>
|
||||
</div>
|
||||
|
||||
<!-- 主菜单 -->
|
||||
<nav class="sidebar-nav">
|
||||
<div class="nav-section">
|
||||
<div class="nav-section-title">主菜单</div>
|
||||
<ul class="nav-menu">
|
||||
{% set active_category = admin_view.category if admin_view.category else '' %}
|
||||
{% set active_name = admin_view.name if admin_view.name else '' %}
|
||||
|
||||
{% for item in admin_view.admin._menu %}
|
||||
{% if item.is_category() %}
|
||||
{# 分类菜单 #}
|
||||
{% for child in item.get_children() %}
|
||||
{% set is_active = (child.name == active_name) %}
|
||||
<li class="nav-item {% if is_active %}active{% endif %}">
|
||||
<a href="{{ child.get_url() }}" class="nav-link">
|
||||
<span class="material-symbols-outlined nav-icon">
|
||||
{% if '网站' in child.name %}public
|
||||
{% elif '标签' in child.name %}label
|
||||
{% elif '新闻' in child.name %}newspaper
|
||||
{% elif '管理员' in child.name %}admin_panel_settings
|
||||
{% else %}circle{% endif %}
|
||||
</span>
|
||||
<span class="nav-text">{{ child.name }}</span>
|
||||
</a>
|
||||
</li>
|
||||
{% endfor %}
|
||||
{% else %}
|
||||
{# 单独菜单项 #}
|
||||
{% set is_active = (item.name == active_name) %}
|
||||
<li class="nav-item {% if is_active %}active{% endif %}">
|
||||
<a href="{{ item.get_url() }}" class="nav-link">
|
||||
<span class="material-symbols-outlined nav-icon">dashboard</span>
|
||||
<span class="nav-text">{{ item.name }}</span>
|
||||
</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<!-- 系统菜单 -->
|
||||
<div class="nav-section">
|
||||
<div class="nav-section-title">系统</div>
|
||||
<ul class="nav-menu">
|
||||
<li class="nav-item">
|
||||
<a href="{{ url_for('batch_import') }}" class="nav-link">
|
||||
<span class="material-symbols-outlined nav-icon">upload_file</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>
|
||||
<span class="nav-text">查看网站</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a href="{{ url_for('admin_logout') }}" class="nav-link">
|
||||
<span class="material-symbols-outlined nav-icon">logout</span>
|
||||
<span class="nav-text">退出登录</span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<!-- 用户信息 -->
|
||||
<div class="sidebar-user">
|
||||
<div class="user-avatar">
|
||||
<span class="material-symbols-outlined">account_circle</span>
|
||||
</div>
|
||||
<div class="user-info">
|
||||
<div class="user-name">{{ current_user.username }}</div>
|
||||
<div class="user-email">{{ current_user.email or 'admin@zjpb.com' }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</aside>
|
||||
|
||||
<!-- 右侧主内容区 -->
|
||||
<div class="admin-main">
|
||||
<!-- 顶部导航栏 -->
|
||||
<header class="admin-header">
|
||||
<div class="header-breadcrumb">
|
||||
<a href="{{ url_for('admin.index') }}" class="breadcrumb-link">控制台</a>
|
||||
<span class="breadcrumb-separator">/</span>
|
||||
<span class="breadcrumb-current">{{ admin_view.name }}</span>
|
||||
</div>
|
||||
<div class="header-actions">
|
||||
<div class="search-box">
|
||||
<span class="material-symbols-outlined search-icon">search</span>
|
||||
<input type="text" placeholder="全局搜索..." class="search-input">
|
||||
</div>
|
||||
<button class="header-btn">
|
||||
<span class="material-symbols-outlined">notifications</span>
|
||||
</button>
|
||||
<button class="header-btn">
|
||||
<span class="material-symbols-outlined">settings</span>
|
||||
</button>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<!-- 页面内容 -->
|
||||
<main class="admin-content">
|
||||
{% block page_body %}
|
||||
<div class="page-header">
|
||||
<div>
|
||||
<h1 class="page-title">{% block brand %}{{ admin_view.name }}{% endblock %}</h1>
|
||||
{% if admin_view.name == '网站管理' %}
|
||||
<p class="page-description">管理和维护AI工具导航平台的所有工具。</p>
|
||||
{% elif admin_view.name == '标签管理' %}
|
||||
<p class="page-description">管理工具分类标签,优化内容组织。</p>
|
||||
{% elif admin_view.name == '新闻管理' %}
|
||||
<p class="page-description">管理工具相关新闻和更新动态。</p>
|
||||
{% elif admin_view.name == '管理员' %}
|
||||
<p class="page-description">管理后台管理员账号和权限。</p>
|
||||
{% else %}
|
||||
<p class="page-description">{{ admin_view.category or '' }}</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% block page_actions %}{% endblock %}
|
||||
</div>
|
||||
|
||||
{% block messages %}
|
||||
{{ layout.messages() }}
|
||||
{% endblock %}
|
||||
|
||||
{% block body %}{% endblock %}
|
||||
{% endblock %}
|
||||
</main>
|
||||
</div>
|
||||
|
||||
<!-- Bootstrap JS -->
|
||||
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@4.6.0/dist/js/bootstrap.bundle.min.js"></script>
|
||||
|
||||
{% block tail_js %}{% endblock %}
|
||||
{% block tail %}{% endblock %}
|
||||
</body>
|
||||
</html>
|
||||
@@ -7,30 +7,40 @@
|
||||
margin-top: 10px;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
.generate-tags-btn {
|
||||
margin-top: 10px;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
.fetch-status {
|
||||
margin-top: 10px;
|
||||
padding: 10px;
|
||||
border-radius: 8px;
|
||||
display: none;
|
||||
}
|
||||
.fetch-status.success {
|
||||
.tags-status {
|
||||
margin-top: 10px;
|
||||
padding: 10px;
|
||||
border-radius: 8px;
|
||||
display: none;
|
||||
}
|
||||
.fetch-status.success, .tags-status.success {
|
||||
background-color: rgba(34, 197, 94, 0.1);
|
||||
border: 1px solid rgba(34, 197, 94, 0.3);
|
||||
color: #4ade80;
|
||||
}
|
||||
.fetch-status.error {
|
||||
.fetch-status.error, .tags-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 {
|
||||
.auto-fetch-btn .loading-icon, .generate-tags-btn .loading-icon {
|
||||
display: none;
|
||||
}
|
||||
.auto-fetch-btn.loading .loading-icon {
|
||||
.auto-fetch-btn.loading .loading-icon, .generate-tags-btn.loading .loading-icon {
|
||||
display: inline-block;
|
||||
animation: spin 1s linear infinite;
|
||||
}
|
||||
.auto-fetch-btn.loading .normal-icon {
|
||||
.auto-fetch-btn.loading .normal-icon, .generate-tags-btn.loading .normal-icon {
|
||||
display: none;
|
||||
}
|
||||
@keyframes spin {
|
||||
@@ -119,6 +129,75 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
statusDiv.style.display = 'block';
|
||||
}
|
||||
}
|
||||
|
||||
// 在标签字段后添加"AI生成标签"按钮
|
||||
const tagsField = document.querySelector('select[name="tags"]');
|
||||
if (tagsField) {
|
||||
const generateBtn = document.createElement('button');
|
||||
generateBtn.type = 'button';
|
||||
generateBtn.className = 'btn btn-success generate-tags-btn';
|
||||
generateBtn.innerHTML = '<span class="normal-icon">✨</span><span class="loading-icon">↻</span> AI生成标签';
|
||||
|
||||
const tagsStatusDiv = document.createElement('div');
|
||||
tagsStatusDiv.className = 'tags-status';
|
||||
|
||||
tagsField.parentNode.appendChild(generateBtn);
|
||||
tagsField.parentNode.appendChild(tagsStatusDiv);
|
||||
|
||||
generateBtn.addEventListener('click', function() {
|
||||
const nameField = document.querySelector('input[name="name"]');
|
||||
const descriptionField = document.querySelector('textarea[name="description"]');
|
||||
|
||||
const name = nameField ? nameField.value.trim() : '';
|
||||
const description = descriptionField ? descriptionField.value.trim() : '';
|
||||
|
||||
if (!name || !description) {
|
||||
showTagsStatus('请先填写网站名称和描述', 'error');
|
||||
return;
|
||||
}
|
||||
|
||||
// 显示加载状态
|
||||
generateBtn.disabled = true;
|
||||
generateBtn.classList.add('loading');
|
||||
tagsStatusDiv.style.display = 'none';
|
||||
|
||||
// 调用API生成标签
|
||||
fetch('/api/generate-tags', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
name: name,
|
||||
description: description
|
||||
})
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data.success && data.tags && data.tags.length > 0) {
|
||||
// 显示生成的标签
|
||||
const tagsText = data.tags.join(', ');
|
||||
showTagsStatus('✓ AI生成的标签建议: ' + tagsText + '\n(请在标签字段中手动选择或创建这些标签)', 'success');
|
||||
} else {
|
||||
showTagsStatus('✗ ' + (data.message || '标签生成失败'), 'error');
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error:', error);
|
||||
showTagsStatus('✗ 网络请求失败,请重试', 'error');
|
||||
})
|
||||
.finally(() => {
|
||||
generateBtn.disabled = false;
|
||||
generateBtn.classList.remove('loading');
|
||||
});
|
||||
});
|
||||
|
||||
function showTagsStatus(message, type) {
|
||||
tagsStatusDiv.textContent = message;
|
||||
tagsStatusDiv.className = 'tags-status ' + type;
|
||||
tagsStatusDiv.style.display = 'block';
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
||||
@@ -7,30 +7,40 @@
|
||||
margin-top: 10px;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
.generate-tags-btn {
|
||||
margin-top: 10px;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
.fetch-status {
|
||||
margin-top: 10px;
|
||||
padding: 10px;
|
||||
border-radius: 8px;
|
||||
display: none;
|
||||
}
|
||||
.fetch-status.success {
|
||||
.tags-status {
|
||||
margin-top: 10px;
|
||||
padding: 10px;
|
||||
border-radius: 8px;
|
||||
display: none;
|
||||
}
|
||||
.fetch-status.success, .tags-status.success {
|
||||
background-color: rgba(34, 197, 94, 0.1);
|
||||
border: 1px solid rgba(34, 197, 94, 0.3);
|
||||
color: #4ade80;
|
||||
}
|
||||
.fetch-status.error {
|
||||
.fetch-status.error, .tags-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 {
|
||||
.auto-fetch-btn .loading-icon, .generate-tags-btn .loading-icon {
|
||||
display: none;
|
||||
}
|
||||
.auto-fetch-btn.loading .loading-icon {
|
||||
.auto-fetch-btn.loading .loading-icon, .generate-tags-btn.loading .loading-icon {
|
||||
display: inline-block;
|
||||
animation: spin 1s linear infinite;
|
||||
}
|
||||
.auto-fetch-btn.loading .normal-icon {
|
||||
.auto-fetch-btn.loading .normal-icon, .generate-tags-btn.loading .normal-icon {
|
||||
display: none;
|
||||
}
|
||||
@keyframes spin {
|
||||
@@ -119,6 +129,75 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
statusDiv.style.display = 'block';
|
||||
}
|
||||
}
|
||||
|
||||
// 在标签字段后添加"AI生成标签"按钮
|
||||
const tagsField = document.querySelector('select[name="tags"]');
|
||||
if (tagsField) {
|
||||
const generateBtn = document.createElement('button');
|
||||
generateBtn.type = 'button';
|
||||
generateBtn.className = 'btn btn-success generate-tags-btn';
|
||||
generateBtn.innerHTML = '<span class="normal-icon">✨</span><span class="loading-icon">↻</span> AI生成标签';
|
||||
|
||||
const tagsStatusDiv = document.createElement('div');
|
||||
tagsStatusDiv.className = 'tags-status';
|
||||
|
||||
tagsField.parentNode.appendChild(generateBtn);
|
||||
tagsField.parentNode.appendChild(tagsStatusDiv);
|
||||
|
||||
generateBtn.addEventListener('click', function() {
|
||||
const nameField = document.querySelector('input[name="name"]');
|
||||
const descriptionField = document.querySelector('textarea[name="description"]');
|
||||
|
||||
const name = nameField ? nameField.value.trim() : '';
|
||||
const description = descriptionField ? descriptionField.value.trim() : '';
|
||||
|
||||
if (!name || !description) {
|
||||
showTagsStatus('请先填写网站名称和描述', 'error');
|
||||
return;
|
||||
}
|
||||
|
||||
// 显示加载状态
|
||||
generateBtn.disabled = true;
|
||||
generateBtn.classList.add('loading');
|
||||
tagsStatusDiv.style.display = 'none';
|
||||
|
||||
// 调用API生成标签
|
||||
fetch('/api/generate-tags', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
name: name,
|
||||
description: description
|
||||
})
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data.success && data.tags && data.tags.length > 0) {
|
||||
// 显示生成的标签
|
||||
const tagsText = data.tags.join(', ');
|
||||
showTagsStatus('✓ AI生成的标签建议: ' + tagsText + '\n(请在标签字段中手动选择或创建这些标签)', 'success');
|
||||
} else {
|
||||
showTagsStatus('✗ ' + (data.message || '标签生成失败'), 'error');
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error:', error);
|
||||
showTagsStatus('✗ 网络请求失败,请重试', 'error');
|
||||
})
|
||||
.finally(() => {
|
||||
generateBtn.disabled = false;
|
||||
generateBtn.classList.remove('loading');
|
||||
});
|
||||
});
|
||||
|
||||
function showTagsStatus(message, type) {
|
||||
tagsStatusDiv.textContent = message;
|
||||
tagsStatusDiv.className = 'tags-status ' + type;
|
||||
tagsStatusDiv.style.display = 'block';
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
||||
@@ -56,6 +56,12 @@
|
||||
box-shadow: 0 10px 30px -10px rgba(37, 192, 244, 0.15);
|
||||
border-color: #25c0f4;
|
||||
}
|
||||
.line-clamp-2 {
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 2;
|
||||
-webkit-box-orient: vertical;
|
||||
overflow: hidden;
|
||||
}
|
||||
</style>
|
||||
{% block extra_css %}{% endblock %}
|
||||
</head>
|
||||
|
||||
278
templates/base_new.html
Normal file
@@ -0,0 +1,278 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>{% block title %}ZJPB - 焦提示词 | AI工具导航{% endblock %}</title>
|
||||
|
||||
<!-- 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=Inter:wght@300;400;500;600;700;800&display=swap" rel="stylesheet">
|
||||
|
||||
<!-- Material Symbols -->
|
||||
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,GRAD@20..48,100..700,0..1,-50..200" />
|
||||
|
||||
<style>
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
:root {
|
||||
--primary-blue: #0ea5e9;
|
||||
--primary-dark: #0284c7;
|
||||
--text-primary: #1e293b;
|
||||
--text-secondary: #64748b;
|
||||
--text-muted: #94a3b8;
|
||||
--bg-page: #f8fafc;
|
||||
--bg-white: #ffffff;
|
||||
--border-color: #e2e8f0;
|
||||
--shadow-sm: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
|
||||
--shadow-md: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
|
||||
--shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, 0.1);
|
||||
--radius-sm: 6px;
|
||||
--radius-md: 8px;
|
||||
--radius-lg: 12px;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
|
||||
background: var(--bg-page);
|
||||
color: var(--text-primary);
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
/* 导航栏 */
|
||||
.navbar {
|
||||
background: var(--bg-white);
|
||||
border-bottom: 1px solid var(--border-color);
|
||||
padding: 0;
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 1000;
|
||||
box-shadow: var(--shadow-sm);
|
||||
}
|
||||
|
||||
.nav-container {
|
||||
max-width: 1280px;
|
||||
margin: 0 auto;
|
||||
padding: 0 24px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
height: 64px;
|
||||
}
|
||||
|
||||
.nav-left {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 40px;
|
||||
}
|
||||
|
||||
.nav-logo {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
text-decoration: none;
|
||||
color: var(--text-primary);
|
||||
font-weight: 700;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.nav-logo-icon {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
background: linear-gradient(135deg, #0ea5e9 0%, #0284c7 100%);
|
||||
border-radius: 8px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.nav-links {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 32px;
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
.nav-links a {
|
||||
color: var(--text-secondary);
|
||||
text-decoration: none;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
transition: color 0.2s;
|
||||
}
|
||||
|
||||
.nav-links a:hover {
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
.nav-right {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.search-box {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.search-box input {
|
||||
width: 280px;
|
||||
padding: 8px 12px 8px 36px;
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: var(--radius-sm);
|
||||
font-size: 14px;
|
||||
background: var(--bg-page);
|
||||
transition: all 0.2s;
|
||||
}
|
||||
|
||||
.search-box input:focus {
|
||||
outline: none;
|
||||
border-color: var(--primary-blue);
|
||||
background: var(--bg-white);
|
||||
}
|
||||
|
||||
.search-box .material-symbols-outlined {
|
||||
position: absolute;
|
||||
left: 10px;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
font-size: 20px;
|
||||
color: var(--text-muted);
|
||||
}
|
||||
|
||||
.btn {
|
||||
padding: 8px 16px;
|
||||
border-radius: var(--radius-sm);
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
text-decoration: none;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s;
|
||||
border: none;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.btn-secondary {
|
||||
background: transparent;
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
.btn-secondary:hover {
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
background: var(--primary-blue);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.btn-primary:hover {
|
||||
background: var(--primary-dark);
|
||||
}
|
||||
|
||||
/* 主内容区 */
|
||||
.main-content {
|
||||
max-width: 1280px;
|
||||
margin: 0 auto;
|
||||
padding: 0 24px;
|
||||
}
|
||||
|
||||
/* 页脚 */
|
||||
.footer {
|
||||
background: var(--bg-white);
|
||||
border-top: 1px solid var(--border-color);
|
||||
margin-top: 80px;
|
||||
padding: 32px 0;
|
||||
}
|
||||
|
||||
.footer-container {
|
||||
max-width: 1280px;
|
||||
margin: 0 auto;
|
||||
padding: 0 24px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.footer-text {
|
||||
color: var(--text-secondary);
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.footer-links {
|
||||
display: flex;
|
||||
gap: 24px;
|
||||
}
|
||||
|
||||
.footer-links a {
|
||||
color: var(--text-secondary);
|
||||
text-decoration: none;
|
||||
font-size: 14px;
|
||||
transition: color 0.2s;
|
||||
}
|
||||
|
||||
.footer-links a:hover {
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
{% block extra_css %}{% endblock %}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<!-- 导航栏 -->
|
||||
<nav class="navbar">
|
||||
<div class="nav-container">
|
||||
<div class="nav-left">
|
||||
<a href="/" class="nav-logo">
|
||||
<div class="nav-logo-icon">
|
||||
<span class="material-symbols-outlined" style="font-size: 20px;">blur_on</span>
|
||||
</div>
|
||||
<span>ZJPB</span>
|
||||
</a>
|
||||
<ul class="nav-links">
|
||||
<li><a href="/">Home</a></li>
|
||||
<li><a href="/#categories">Categories</a></li>
|
||||
<li><a href="/admin/login">Admin</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="nav-right">
|
||||
<form action="/" method="get" class="search-box">
|
||||
<span class="material-symbols-outlined">search</span>
|
||||
<input type="text" name="q" placeholder="搜索 AI 工具..." value="{{ search_query or '' }}">
|
||||
</form>
|
||||
<a href="/admin/login" class="btn btn-secondary">登录</a>
|
||||
<a href="/admin/login" class="btn btn-primary">注册</a>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<!-- 主内容 -->
|
||||
{% block content %}{% endblock %}
|
||||
|
||||
<!-- 页脚 -->
|
||||
<footer class="footer">
|
||||
<div class="footer-container">
|
||||
<div class="footer-text">
|
||||
© 2023 ZJPB AI Directory. All rights reserved.
|
||||
</div>
|
||||
<div class="footer-links">
|
||||
<a href="#">Twitter</a>
|
||||
<a href="#">Discord</a>
|
||||
<a href="#">Privacy Policy</a>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
{% block extra_js %}{% endblock %}
|
||||
</body>
|
||||
</html>
|
||||
@@ -106,6 +106,82 @@
|
||||
</div>
|
||||
</section>
|
||||
{% endif %}
|
||||
|
||||
<!-- Related News -->
|
||||
{% if news_list %}
|
||||
<section>
|
||||
<h3 class="text-xl font-bold text-white mb-4 flex items-center gap-2">
|
||||
<span class="material-symbols-outlined text-secondary\">newspaper</span>
|
||||
相关新闻
|
||||
</h3>
|
||||
<div class="grid gap-4">
|
||||
{% for news in news_list %}
|
||||
<a class="group block p-5 rounded-xl border border-border-dark bg-surface-dark/40 hover:bg-border-dark/60 hover:border-primary/30 transition-all" href="{{ news.url if news.url else '#' }}" {{ 'target=\"_blank\"' if news.url else '' }}>
|
||||
<div class="flex justify-between items-start mb-2">
|
||||
{% if news.news_type == 'Product Update' %}
|
||||
<span class="inline-flex items-center px-2 py-0.5 rounded text-[10px] font-bold uppercase tracking-wider bg-primary/10 text-primary">{{ news.news_type }}</span>
|
||||
{% elif news.news_type == 'Industry News' %}
|
||||
<span class="inline-flex items-center px-2 py-0.5 rounded text-[10px] font-bold uppercase tracking-wider bg-purple-500/10 text-purple-400">{{ news.news_type }}</span>
|
||||
{% else %}
|
||||
<span class="inline-flex items-center px-2 py-0.5 rounded text-[10px] font-bold uppercase tracking-wider bg-gray-500/10 text-gray-400">{{ news.news_type }}</span>
|
||||
{% endif %}
|
||||
<span class="text-xs text-gray-400">
|
||||
{% set days_ago = (now() - news.published_at).days %}
|
||||
{% if days_ago == 0 %}
|
||||
今天
|
||||
{% elif days_ago == 1 %}
|
||||
1天前
|
||||
{% elif days_ago < 7 %}
|
||||
{{ days_ago }}天前
|
||||
{% else %}
|
||||
{{ news.published_at.strftime('%Y年%m月%d日') }}
|
||||
{% endif %}
|
||||
</span>
|
||||
</div>
|
||||
<h4 class="text-white font-bold text-base group-hover:text-primary transition-colors mb-2">{{ news.title }}</h4>
|
||||
<p class="text-sm text-gray-400 line-clamp-2">{{ news.content }}</p>
|
||||
</a>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</section>
|
||||
{% endif %}
|
||||
|
||||
<!-- Similar Recommendations -->
|
||||
{% if recommended_sites %}
|
||||
<section>
|
||||
<h3 class="text-xl font-bold text-white mb-4 flex items-center gap-2">
|
||||
<span class="material-symbols-outlined text-primary">auto_awesome</span>
|
||||
同类工具推荐
|
||||
</h3>
|
||||
<div class="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
||||
{% for rec_site in recommended_sites %}
|
||||
<a class="flex flex-col p-4 rounded-xl border border-border-dark bg-surface-dark hover:border-primary/50 hover:shadow-[0_0_20px_-10px_rgba(37,192,244,0.3)] transition-all group h-full" href="{{ url_for('site_detail', slug=rec_site.slug) }}">
|
||||
<div class="flex items-start justify-between mb-3">
|
||||
{% if rec_site.logo %}
|
||||
<div class="size-10 rounded-lg bg-cover bg-center shadow-lg" style="background-image: url('{{ rec_site.logo }}');"></div>
|
||||
{% else %}
|
||||
<div class="size-10 rounded-lg bg-gradient-to-br from-primary to-secondary flex items-center justify-center shadow-lg">
|
||||
<span class="text-white font-bold text-lg">{{ rec_site.name[0] }}</span>
|
||||
</div>
|
||||
{% endif %}
|
||||
<div class="size-8 rounded-full bg-border-dark/50 flex items-center justify-center text-gray-400 group-hover:text-white group-hover:bg-primary group-hover:scale-110 transition-all">
|
||||
<span class="material-symbols-outlined text-[18px]">arrow_forward</span>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<h4 class="text-white font-bold mb-1">{{ rec_site.name }}</h4>
|
||||
<p class="text-xs text-gray-400 line-clamp-2 mb-3">{{ rec_site.short_desc or '暂无描述' }}</p>
|
||||
<div class="flex gap-2 flex-wrap">
|
||||
{% for tag in rec_site.tags[:2] %}
|
||||
<span class="px-2 py-1 rounded bg-border-dark text-[10px] text-gray-400 font-medium">{{ tag.name }}</span>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</section>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
577
templates/detail_new.html
Normal file
@@ -0,0 +1,577 @@
|
||||
{% extends 'base_new.html' %}
|
||||
|
||||
{% block title %}{{ site.name }} - ZJPB AI工具导航{% endblock %}
|
||||
|
||||
{% block extra_css %}
|
||||
<style>
|
||||
/* 返回链接 */
|
||||
.back-link {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
color: var(--text-secondary);
|
||||
text-decoration: none;
|
||||
font-size: 24px;
|
||||
font-weight: 500;
|
||||
transition: color 0.2s;
|
||||
}
|
||||
|
||||
.back-link:hover {
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
.back-link .material-symbols-outlined {
|
||||
font-size: 24px;
|
||||
line-height: 1;
|
||||
vertical-align: middle;
|
||||
margin-top: -2px;
|
||||
}
|
||||
|
||||
/* 产品头部区域 */
|
||||
.product-header-wrapper {
|
||||
display: flex;
|
||||
gap: 32px;
|
||||
margin-bottom: 32px;
|
||||
align-items: flex-start;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.product-main-section {
|
||||
flex: 1;
|
||||
background: var(--bg-white);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: var(--radius-lg);
|
||||
padding: 32px;
|
||||
}
|
||||
|
||||
.product-header {
|
||||
display: flex;
|
||||
gap: 20px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.product-logo-large {
|
||||
width: 88px;
|
||||
height: 88px;
|
||||
border-radius: var(--radius-lg);
|
||||
object-fit: cover;
|
||||
flex-shrink: 0;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
|
||||
}
|
||||
|
||||
.product-main-info {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.product-main-info h1 {
|
||||
font-size: 32px;
|
||||
font-weight: 700;
|
||||
margin-bottom: 8px;
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
.product-link {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
color: var(--primary-blue);
|
||||
text-decoration: none;
|
||||
font-size: 14px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.product-link:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.product-link .material-symbols-outlined {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.product-meta {
|
||||
display: flex;
|
||||
gap: 20px;
|
||||
margin-bottom: 16px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.meta-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
color: var(--text-secondary);
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.meta-item .material-symbols-outlined {
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.product-tags-list {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.product-tag {
|
||||
padding: 6px 12px;
|
||||
background: #f1f5f9;
|
||||
color: #64748b;
|
||||
border-radius: 6px;
|
||||
font-size: 13px;
|
||||
font-weight: 500;
|
||||
text-decoration: none;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
|
||||
.product-tag:hover {
|
||||
background: rgba(14, 165, 233, 0.1);
|
||||
color: var(--primary-blue);
|
||||
}
|
||||
|
||||
.product-tag.free {
|
||||
background: rgba(139, 92, 246, 0.1);
|
||||
color: #8b5cf6;
|
||||
}
|
||||
|
||||
/* Try It Now卡片 - 独立定位 */
|
||||
.try-now-card {
|
||||
position: sticky;
|
||||
top: 88px;
|
||||
width: 300px;
|
||||
background: var(--bg-white);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: var(--radius-lg);
|
||||
padding: 24px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.try-now-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.try-now-header h3 {
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.status-badge {
|
||||
padding: 4px 10px;
|
||||
background: rgba(34, 197, 94, 0.1);
|
||||
color: #059669;
|
||||
border-radius: 4px;
|
||||
font-size: 11px;
|
||||
font-weight: 700;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.5px;
|
||||
}
|
||||
|
||||
.visit-btn {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 8px;
|
||||
width: 100%;
|
||||
padding: 12px;
|
||||
background: var(--primary-blue);
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: var(--radius-md);
|
||||
text-decoration: none;
|
||||
font-weight: 600;
|
||||
font-size: 14px;
|
||||
transition: all 0.2s;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.3px;
|
||||
}
|
||||
|
||||
.visit-btn:hover {
|
||||
background: var(--primary-dark);
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 4px 12px rgba(14, 165, 233, 0.3);
|
||||
}
|
||||
|
||||
.visit-btn .material-symbols-outlined {
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.visit-hint {
|
||||
text-align: center;
|
||||
margin-top: 12px;
|
||||
font-size: 12px;
|
||||
color: var(--text-muted);
|
||||
}
|
||||
|
||||
/* 主内容布局 */
|
||||
.content-layout {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 300px;
|
||||
gap: 32px;
|
||||
margin-bottom: 48px;
|
||||
}
|
||||
|
||||
.main-column {
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.sidebar-column {
|
||||
position: sticky;
|
||||
top: 88px;
|
||||
align-self: flex-start;
|
||||
}
|
||||
|
||||
/* 内容块 */
|
||||
.content-block {
|
||||
background: var(--bg-white);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: var(--radius-lg);
|
||||
padding: 32px;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
.content-block h2 {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
font-size: 20px;
|
||||
font-weight: 700;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.content-block h2 .material-symbols-outlined {
|
||||
font-size: 24px;
|
||||
color: var(--primary-blue);
|
||||
}
|
||||
|
||||
.content-block p {
|
||||
color: var(--text-secondary);
|
||||
line-height: 1.7;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.content-block ul,
|
||||
.content-block ol {
|
||||
margin-left: 20px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.content-block li {
|
||||
color: var(--text-secondary);
|
||||
line-height: 1.7;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
/* 新闻卡片 */
|
||||
.news-item {
|
||||
padding: 20px;
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: var(--radius-md);
|
||||
margin-bottom: 16px;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
|
||||
.news-item:hover {
|
||||
border-color: var(--primary-blue);
|
||||
box-shadow: var(--shadow-md);
|
||||
}
|
||||
|
||||
.news-item:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.news-badge {
|
||||
display: inline-block;
|
||||
padding: 4px 10px;
|
||||
background: rgba(139, 92, 246, 0.1);
|
||||
color: #8b5cf6;
|
||||
border-radius: 4px;
|
||||
font-size: 11px;
|
||||
font-weight: 600;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.5px;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.news-item h4 {
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
margin-bottom: 8px;
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
.news-item p {
|
||||
font-size: 14px;
|
||||
color: var(--text-secondary);
|
||||
margin-bottom: 8px;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.news-date {
|
||||
font-size: 12px;
|
||||
color: var(--text-muted);
|
||||
}
|
||||
|
||||
/* 推荐卡片 */
|
||||
.recommendations-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.recommendation-card {
|
||||
padding: 20px;
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: var(--radius-md);
|
||||
text-decoration: none;
|
||||
color: var(--text-primary);
|
||||
transition: all 0.2s;
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.recommendation-card:hover {
|
||||
border-color: var(--primary-blue);
|
||||
box-shadow: var(--shadow-md);
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
.recommendation-card .arrow-icon {
|
||||
position: absolute;
|
||||
top: 20px;
|
||||
right: 20px;
|
||||
color: #cbd5e1;
|
||||
font-size: 20px;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
|
||||
.recommendation-card:hover .arrow-icon {
|
||||
color: var(--primary-blue);
|
||||
transform: translate(2px, -2px);
|
||||
}
|
||||
|
||||
.rec-logo {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
border-radius: var(--radius-md);
|
||||
object-fit: cover;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.rec-info {
|
||||
flex: 1;
|
||||
padding-right: 24px;
|
||||
}
|
||||
|
||||
.rec-info h4 {
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.rec-info p {
|
||||
font-size: 12px;
|
||||
color: var(--text-secondary);
|
||||
margin-bottom: 8px;
|
||||
line-height: 1.4;
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 2;
|
||||
-webkit-box-orient: vertical;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.rec-tags {
|
||||
display: flex;
|
||||
gap: 4px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.rec-tag {
|
||||
padding: 2px 8px;
|
||||
background: #f1f5f9;
|
||||
color: #64748b;
|
||||
border-radius: 4px;
|
||||
font-size: 11px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
/* 响应式 */
|
||||
@media (max-width: 968px) {
|
||||
.product-header-wrapper {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.try-now-card {
|
||||
width: 100%;
|
||||
position: static;
|
||||
}
|
||||
|
||||
.content-layout {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.sidebar-column {
|
||||
position: static;
|
||||
}
|
||||
|
||||
.recommendations-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="main-content">
|
||||
<!-- 顶部空白 -->
|
||||
<div style="height: 40px;"></div>
|
||||
|
||||
<!-- 返回链接 -->
|
||||
<a href="/" class="back-link">
|
||||
<span class="material-symbols-outlined">arrow_back</span>
|
||||
返回首页
|
||||
</a>
|
||||
|
||||
<!-- 底部空白 -->
|
||||
<div style="height: 20px;"></div>
|
||||
|
||||
<!-- 产品头部区域 -->
|
||||
<div class="product-header-wrapper">
|
||||
<!-- 左侧主内容 -->
|
||||
<div class="product-main-section">
|
||||
<div class="product-header">
|
||||
<!-- Logo -->
|
||||
{% if site.logo %}
|
||||
<img src="{{ site.logo }}" alt="{{ site.name }}" class="product-logo-large">
|
||||
{% else %}
|
||||
<div class="product-logo-large" style="background: linear-gradient(135deg, #0ea5e9 0%, #8b5cf6 100%);"></div>
|
||||
{% endif %}
|
||||
|
||||
<!-- 产品信息 -->
|
||||
<div class="product-main-info">
|
||||
<h1>{{ site.name }}</h1>
|
||||
<a href="{{ site.url }}" target="_blank" class="product-link">
|
||||
{{ site.url }}
|
||||
<span class="material-symbols-outlined">open_in_new</span>
|
||||
</a>
|
||||
|
||||
<div class="product-meta">
|
||||
<div class="meta-item">
|
||||
<span class="material-symbols-outlined">visibility</span>
|
||||
<span>{{ site.view_count | default(0) }} 次浏览</span>
|
||||
</div>
|
||||
<div class="meta-item">
|
||||
<span class="material-symbols-outlined">calendar_today</span>
|
||||
<span>添加于 {{ site.created_at.strftime('%Y年%m月%d日') }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="product-tags-list">
|
||||
{% for tag in site.tags %}
|
||||
<a href="/?tag={{ tag.slug }}" class="product-tag">{{ tag.name }}</a>
|
||||
{% endfor %}
|
||||
<span class="product-tag free">免费试用</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Try It Now卡片 -->
|
||||
<div class="try-now-card">
|
||||
<div class="try-now-header">
|
||||
<h3>立即访问</h3>
|
||||
<span class="status-badge">在线</span>
|
||||
</div>
|
||||
<a href="{{ site.url }}" target="_blank" class="visit-btn">
|
||||
访问网站
|
||||
<span class="material-symbols-outlined">north_east</span>
|
||||
</a>
|
||||
<p class="visit-hint">在新标签页打开 • {{ site.url.split('/')[2] if site.url else '' }}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 内容布局 -->
|
||||
<div class="content-layout">
|
||||
<!-- 主列 -->
|
||||
<div class="main-column">
|
||||
<!-- Product Overview -->
|
||||
<div class="content-block">
|
||||
<h2>
|
||||
<span class="material-symbols-outlined">info</span>
|
||||
产品概述
|
||||
</h2>
|
||||
<p>{{ site.description }}</p>
|
||||
</div>
|
||||
|
||||
<!-- Detailed Description -->
|
||||
{% if site.features %}
|
||||
<div class="content-block">
|
||||
<h2>
|
||||
<span class="material-symbols-outlined">description</span>
|
||||
详细描述
|
||||
</h2>
|
||||
<div>{{ site.features | safe }}</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<!-- Related News -->
|
||||
{% if news_list %}
|
||||
<div class="content-block">
|
||||
<h2>
|
||||
<span class="material-symbols-outlined">newspaper</span>
|
||||
相关新闻
|
||||
</h2>
|
||||
{% for news in news_list %}
|
||||
<div class="news-item">
|
||||
<span class="news-badge">{{ news.news_type }}</span>
|
||||
<h4>{{ news.title }}</h4>
|
||||
<p>{{ news.content[:200] }}...</p>
|
||||
<div class="news-date">{{ news.published_at.strftime('%b %d, %Y') }}</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<!-- Similar Recommendations -->
|
||||
{% if recommended_sites %}
|
||||
<div class="content-block">
|
||||
<h2>
|
||||
<span class="material-symbols-outlined">auto_awesome</span>
|
||||
相似推荐
|
||||
</h2>
|
||||
<div class="recommendations-grid">
|
||||
{% for rec_site in recommended_sites %}
|
||||
<a href="/site/{{ rec_site.code }}" class="recommendation-card">
|
||||
{% if rec_site.logo %}
|
||||
<img src="{{ rec_site.logo }}" alt="{{ rec_site.name }}" class="rec-logo">
|
||||
{% else %}
|
||||
<div class="rec-logo" style="background: linear-gradient(135deg, #0ea5e9 0%, #8b5cf6 100%);"></div>
|
||||
{% endif %}
|
||||
<div class="rec-info">
|
||||
<h4>{{ rec_site.name }}</h4>
|
||||
<p>{{ rec_site.short_desc or rec_site.description }}</p>
|
||||
<div class="rec-tags">
|
||||
{% for tag in rec_site.tags[:2] %}
|
||||
<span class="rec-tag">{{ tag.name }}</span>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
<span class="material-symbols-outlined arrow-icon">north_east</span>
|
||||
</a>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<!-- 侧边栏 -->
|
||||
<div class="sidebar-column">
|
||||
<!-- 预留侧边栏位置,可以后续添加其他模块 -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
412
templates/index_new.html
Normal file
@@ -0,0 +1,412 @@
|
||||
{% extends 'base_new.html' %}
|
||||
|
||||
{% block title %}ZJPB - 焦提示词 | 发现最新最好用的AI工具和产品{% endblock %}
|
||||
|
||||
{% block extra_css %}
|
||||
<style>
|
||||
/* Hero区域 */
|
||||
.hero {
|
||||
padding-bottom: 40px;
|
||||
background: var(--bg-page);
|
||||
}
|
||||
|
||||
.hero-content {
|
||||
max-width: 1280px;
|
||||
margin: 0 auto;
|
||||
padding: 0 24px;
|
||||
}
|
||||
|
||||
.hero-subtitle {
|
||||
font-size: 24px;
|
||||
font-weight: 400;
|
||||
color: #475569;
|
||||
line-height: 1.5;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.hero-subtitle-en {
|
||||
font-size: 16px;
|
||||
font-weight: 400;
|
||||
color: #64748b;
|
||||
line-height: 1.6;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
/* 分类过滤 */
|
||||
.categories {
|
||||
padding: 32px 0;
|
||||
}
|
||||
|
||||
.category-tabs {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.category-tab {
|
||||
padding: 10px 20px;
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 50px;
|
||||
background: var(--bg-white);
|
||||
color: var(--text-secondary);
|
||||
text-decoration: none;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
transition: all 0.2s;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.category-tab:hover {
|
||||
border-color: var(--primary-blue);
|
||||
color: var(--primary-blue);
|
||||
}
|
||||
|
||||
.category-tab.active {
|
||||
background: var(--primary-blue);
|
||||
border-color: var(--primary-blue);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.category-tab .material-symbols-outlined {
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
/* 工具网格 */
|
||||
.tools-section {
|
||||
padding: 32px 0 48px;
|
||||
}
|
||||
|
||||
.tools-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
|
||||
gap: 20px;
|
||||
margin-bottom: 48px;
|
||||
}
|
||||
|
||||
.tool-card {
|
||||
background: var(--bg-white);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 12px;
|
||||
padding: 24px;
|
||||
text-decoration: none;
|
||||
color: var(--text-primary);
|
||||
transition: all 0.2s;
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.tool-card:hover {
|
||||
border-color: var(--primary-blue);
|
||||
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.08);
|
||||
transform: translateY(-4px);
|
||||
}
|
||||
|
||||
.tool-card-header {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.tool-logo {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
border-radius: 10px;
|
||||
object-fit: cover;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.tool-link-icon {
|
||||
color: #cbd5e1;
|
||||
transition: all 0.2s;
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
.tool-card:hover .tool-link-icon {
|
||||
color: var(--primary-blue);
|
||||
transform: translate(2px, -2px);
|
||||
}
|
||||
|
||||
.tool-name {
|
||||
font-size: 18px;
|
||||
font-weight: 700;
|
||||
margin-bottom: 8px;
|
||||
margin-top: 0;
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
.tool-description {
|
||||
font-size: 14px;
|
||||
color: var(--text-secondary);
|
||||
line-height: 1.6;
|
||||
margin-bottom: 16px;
|
||||
flex: 1;
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 2;
|
||||
-webkit-box-orient: vertical;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.tool-footer {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 12px;
|
||||
padding-top: 16px;
|
||||
border-top: 1px solid var(--border-color);
|
||||
}
|
||||
|
||||
.tool-tags {
|
||||
display: flex;
|
||||
gap: 6px;
|
||||
flex-wrap: wrap;
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.tool-tag {
|
||||
padding: 4px 10px;
|
||||
background: #f1f5f9;
|
||||
color: #64748b;
|
||||
border-radius: 4px;
|
||||
font-size: 11px;
|
||||
font-weight: 600;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.3px;
|
||||
}
|
||||
|
||||
.tool-views {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
color: #94a3b8;
|
||||
font-size: 13px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.tool-views .material-symbols-outlined {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
/* 分页 */
|
||||
.pagination {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.pagination a,
|
||||
.pagination span {
|
||||
min-width: 40px;
|
||||
height: 40px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: var(--radius-sm);
|
||||
background: var(--bg-white);
|
||||
color: var(--text-secondary);
|
||||
text-decoration: none;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
|
||||
.pagination a:hover {
|
||||
border-color: var(--primary-blue);
|
||||
color: var(--primary-blue);
|
||||
}
|
||||
|
||||
.pagination .active {
|
||||
background: var(--primary-blue);
|
||||
border-color: var(--primary-blue);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.pagination .disabled {
|
||||
opacity: 0.5;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.pagination .disabled:hover {
|
||||
border-color: var(--border-color);
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
/* 空状态 */
|
||||
.empty-state {
|
||||
text-align: center;
|
||||
padding: 80px 20px;
|
||||
}
|
||||
|
||||
.empty-state .material-symbols-outlined {
|
||||
font-size: 64px;
|
||||
color: var(--text-muted);
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.empty-state h3 {
|
||||
font-size: 20px;
|
||||
color: var(--text-primary);
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.empty-state p {
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
/* 响应式 */
|
||||
@media (max-width: 768px) {
|
||||
.hero-title {
|
||||
font-size: 40px;
|
||||
}
|
||||
|
||||
.tools-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<!-- Hero区域 -->
|
||||
<div class="hero">
|
||||
<div style="height: 40px;"></div>
|
||||
<div class="hero-content">
|
||||
<p class="hero-subtitle">发现最新最好用的AI工具和产品</p>
|
||||
<p class="hero-subtitle-en">Discover the best AI tools tailored for your workflow</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="main-content">
|
||||
|
||||
<!-- 分类过滤 -->
|
||||
<div class="categories" id="categories">
|
||||
<div class="category-tabs">
|
||||
<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 %}
|
||||
</span>
|
||||
{{ tag.name }}
|
||||
</a>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 工具网格 -->
|
||||
<div class="tools-section">
|
||||
{% 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>
|
||||
搜索 "{{ search_query }}" 的结果:找到 {{ pagination.total if pagination else sites|length }} 个工具
|
||||
<a href="/" style="margin-left: 12px; color: #0ea5e9; text-decoration: none;">清除搜索</a>
|
||||
</p>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if sites %}
|
||||
<div class="tools-grid">
|
||||
{% for site in sites %}
|
||||
<a href="/site/{{ site.code }}" class="tool-card">
|
||||
<div class="tool-card-header">
|
||||
{% if site.logo %}
|
||||
<img src="{{ site.logo }}" alt="{{ site.name }}" class="tool-logo">
|
||||
{% 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>
|
||||
</div>
|
||||
<h3 class="tool-name">{{ site.name }}</h3>
|
||||
<p class="tool-description">{{ site.short_desc or site.description }}</p>
|
||||
<div class="tool-footer">
|
||||
<div class="tool-tags">
|
||||
{% for tag in site.tags[:2] %}
|
||||
<span class="tool-tag">{{ tag.name }}</span>
|
||||
{% endfor %}
|
||||
</div>
|
||||
<div class="tool-views">
|
||||
<span class="material-symbols-outlined">visibility</span>
|
||||
<span>{% if site.view_count >= 1000 %}{{ (site.view_count / 1000) | round(1) }}k{% else %}{{ site.view_count | default(0) }}{% endif %}</span>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
||||
<!-- 分页 -->
|
||||
{% if pagination and pagination.pages > 1 %}
|
||||
<div class="pagination">
|
||||
<!-- 上一页 -->
|
||||
{% 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>
|
||||
</a>
|
||||
{% else %}
|
||||
<a href="#" class="disabled">
|
||||
<span class="material-symbols-outlined">chevron_left</span>
|
||||
</a>
|
||||
{% endif %}
|
||||
|
||||
<!-- 页码显示 -->
|
||||
{% set start_page = [1, pagination.page - 2]|max %}
|
||||
{% set end_page = [pagination.pages, pagination.page + 2]|min %}
|
||||
|
||||
{% if start_page > 1 %}
|
||||
<a href="?page=1{% if selected_tag %}&tag={{ selected_tag.slug }}{% endif %}{% if search_query %}&q={{ search_query }}{% endif %}">1</a>
|
||||
{% if start_page > 2 %}
|
||||
<span>...</span>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
||||
{% for page_num in range(start_page, end_page + 1) %}
|
||||
{% if page_num == pagination.page %}
|
||||
<span class="active">{{ page_num }}</span>
|
||||
{% else %}
|
||||
<a href="?page={{ page_num }}{% if selected_tag %}&tag={{ selected_tag.slug }}{% endif %}{% if search_query %}&q={{ search_query }}{% endif %}">{{ page_num }}</a>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
|
||||
{% if end_page < pagination.pages %}
|
||||
{% if end_page < pagination.pages - 1 %}
|
||||
<span>...</span>
|
||||
{% endif %}
|
||||
<a href="?page={{ pagination.pages }}{% if selected_tag %}&tag={{ selected_tag.slug }}{% endif %}{% if search_query %}&q={{ search_query }}{% endif %}">{{ pagination.pages }}</a>
|
||||
{% endif %}
|
||||
|
||||
<!-- 下一页 -->
|
||||
{% 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>
|
||||
</a>
|
||||
{% else %}
|
||||
<a href="#" class="disabled">
|
||||
<span class="material-symbols-outlined">chevron_right</span>
|
||||
</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
<div class="empty-state">
|
||||
<span class="material-symbols-outlined">search_off</span>
|
||||
<h3>暂无工具</h3>
|
||||
<p>{% if selected_tag %}该分类下还没有工具{% else %}还没有添加任何工具{% endif %}</p>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
125
temt/OneNav_Export_2025.12.28.html
Normal file
@@ -0,0 +1,125 @@
|
||||
<!DOCTYPE NETSCAPE-Bookmark-file-1>
|
||||
<!-- This is an automatically generated file.
|
||||
It will be read and overwritten.
|
||||
DO NOT EDIT! -->
|
||||
<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=UTF-8">
|
||||
<TITLE>Bookmarks</TITLE>
|
||||
<H1>Bookmarks</H1>
|
||||
<DL><p>
|
||||
<DT><H3 ADD_DATE="1766918493" LAST_MODIFIED="1766918493" PERSONAL_TOOLBAR_FOLDER="true">OneNav</H3>
|
||||
<DL><p>
|
||||
<DT><H3 ADD_DATE="1766918493" LAST_MODIFIED="1766918493">OneNav默认分类</H3>
|
||||
<DL><p>
|
||||
<DT><A HREF="https://stitch.withgoogle.com/" ADD_DATE="1764662976">Stitch</A>
|
||||
<DT><A HREF="http://news.aipmclub.com/" ADD_DATE="1762877013">NewsNow</A>
|
||||
<DT><A HREF="https://www.boxshub.com" ADD_DATE="1762876972">BoxsHub</A>
|
||||
<DT><A HREF="https://www.aipmclub.com/" ADD_DATE="1762876927">AIPMClub</A>
|
||||
<DT><A HREF="https://www.imgurl.org/" ADD_DATE="1608042525">ImgURL免费图床</A>
|
||||
</DL><p>
|
||||
<DT><H3 ADD_DATE="1766918493" LAST_MODIFIED="1766918493">AGI大模型</H3>
|
||||
<DL><p>
|
||||
<DT><A HREF="https://www.perplexity.ai/" ADD_DATE="1762878118">Perplexity</A>
|
||||
<DT><A HREF="https://www.siliconflow.cn/" ADD_DATE="1762878079">硅基流动 SiliconFlow - 致力于成为全球领先的 AI 能力提供商</A>
|
||||
<DT><A HREF="https://www.deepseek.com/" ADD_DATE="1762878065">DeepSeek | 深度求索</A>
|
||||
<DT><A HREF="https://grok.com/" ADD_DATE="1762878029">Grok</A>
|
||||
<DT><A HREF="https://chatgpt.com/" ADD_DATE="1762877965">ChatGPT</A>
|
||||
<DT><A HREF="https://claude.ai/" ADD_DATE="1762876402">Claude</A>
|
||||
<DT><A HREF="https://www.kimi.com/" ADD_DATE="1762876179">Kimi</A>
|
||||
</DL><p>
|
||||
<DT><H3 ADD_DATE="1766918493" LAST_MODIFIED="1766918493">API资源</H3>
|
||||
<DL><p>
|
||||
<DT><A HREF="https://www.pingxx.com/" ADD_DATE="1762964204">「简米|Ping++」聚合支付系统-支付分账接口-B2B 支付解决方案-二清支付公司</A>
|
||||
<DT><A HREF="https://openrouter.ai/" ADD_DATE="1762961868">OpenRouter</A>
|
||||
<DT><A HREF="https://aihubmix.com/" ADD_DATE="1762961796">AIHubMix - One Interface, Router All LLMs</A>
|
||||
<DT><A HREF="https://newsapi.org/" ADD_DATE="1762877264">NewsAPI</A>
|
||||
<DT><A HREF="https://yunwu.zeabur.app/" ADD_DATE="1762877221">云雾API</A>
|
||||
<DT><A HREF="https://serpapi.com/" ADD_DATE="1762877191">SerpAPI</A>
|
||||
</DL><p>
|
||||
<DT><H3 ADD_DATE="1766918493" LAST_MODIFIED="1766918493">服务器管理</H3>
|
||||
<DL><p>
|
||||
<DT><A HREF="https://www.cloudflare.com/" ADD_DATE="1763042359">Connect, protect, and build everywhere | Cloudflare</A>
|
||||
<DT><A HREF="https://www.hostinger.com/" ADD_DATE="1762964107">Hostinger - Bring Your Idea Online With a Website</A>
|
||||
<DT><A HREF="https://zh.semrush.com/" ADD_DATE="1762963724">Semrush:数据驱动的营销工具,助您拓展业务</A>
|
||||
<DT><A HREF="https://www.pexels.com/" ADD_DATE="1762879063">Pexels</A>
|
||||
<DT><A HREF="https://zeabur.com/" ADD_DATE="1762879008">Zeabur - Your AI DevOps Engineer</A>
|
||||
<DT><A HREF="https://railway.com/" ADD_DATE="1762878979">Railway</A>
|
||||
<DT><A HREF="https://github.com/" ADD_DATE="1762878961">GitHub · Change is constant. GitHub keeps you ahead. · GitHub</A>
|
||||
<DT><A HREF="https://www.bt.cn/new/index.html" ADD_DATE="1762878924">宝塔面板(bt.cn) 简单好用的Linux/Windows服务器运维管理面板</A>
|
||||
<DT><A HREF="https://1panel.cn/" ADD_DATE="1762878906">1Panel - 现代化、开源的Linux服务器运维管理面板</A>
|
||||
</DL><p>
|
||||
<DT><H3 ADD_DATE="1766918493" LAST_MODIFIED="1766918493">AI工具</H3>
|
||||
<DL><p>
|
||||
<DT><A HREF="https://gamma.app/" ADD_DATE="1762962993">Gamma</A>
|
||||
<DT><A HREF="https://www.jetbrains.com/zh-cn/" ADD_DATE="1762961836">JetBrains: 软件开发者和团队的必备工具</A>
|
||||
<DT><A HREF="https://www.shodan.io/" ADD_DATE="1762961720">shodan</A>
|
||||
<DT><A HREF="https://www.similarweb.com/" ADD_DATE="1762961518">Similarweb: AI-Powered Digital Data Intelligence Solutions</A>
|
||||
<DT><A HREF="https://www.tradingview.com/" ADD_DATE="1762960567">Trading View</A>
|
||||
<DT><A HREF="https://sierra.ai/" ADD_DATE="1762960476">Sierra | Better customer experiences</A>
|
||||
<DT><A HREF="https://www.producthunt.com/" ADD_DATE="1762960045">Product Hunt</A>
|
||||
<DT><A HREF="https://quillbot.com/" ADD_DATE="1762959999">QuillBot: Your complete writing solution</A>
|
||||
<DT><A HREF="https://www.newsminimalist.com/" ADD_DATE="1762878734">News Minimalist</A>
|
||||
<DT><A HREF="https://www.piccopilot.com/" ADD_DATE="1762878663">Fashion AI Model &amp; AI Generated Product Images for eCcommerce | Pic Copilot</A>
|
||||
<DT><A HREF="https://www.canva.com/" ADD_DATE="1762878393">Canva</A>
|
||||
<DT><A HREF="https://buildingai.cc/" ADD_DATE="1762878234">BuildingAI | 开源AI框架 | FastBuildAI</A>
|
||||
<DT><A HREF="https://notebooklm.google/" ADD_DATE="1762878157">NotebookLLM</A>
|
||||
<DT><A HREF="https://ip.rss.ink/" ADD_DATE="1669700402">IPRSS</A>
|
||||
</DL><p>
|
||||
<DT><H3 ADD_DATE="1766918493" LAST_MODIFIED="1766918493">Agent平台</H3>
|
||||
<DL><p>
|
||||
<DT><A HREF="https://www.lightlycode.com/" ADD_DATE="1764040904">轻灵</A>
|
||||
<DT><A HREF="https://dify.ai/" ADD_DATE="1762961611">Dify: Leading Agentic Workflow Builder</A>
|
||||
<DT><A HREF="https://flowith.io/" ADD_DATE="1762960422">flowith</A>
|
||||
<DT><A HREF="https://fastgpt.io/zh" ADD_DATE="1762960391">FastGPT - 企业级 AI Agent 搭建平台</A>
|
||||
<DT><A HREF="https://n8n.io/" ADD_DATE="1762878751">AI Workflow Automation Platform &amp; Tools - n8n</A>
|
||||
</DL><p>
|
||||
<DT><H3 ADD_DATE="1766918493" LAST_MODIFIED="1766918493">AI视频</H3>
|
||||
<DL><p>
|
||||
<DT><A HREF="https://fogsight.ai/" ADD_DATE="1762963149">Fogsight 雾象 | AI Animation Engine - Transform Concepts into Cinematic Visuals</A>
|
||||
<DT><A HREF="https://www.mooliv.com/" ADD_DATE="1762961767">模力视频 - AIGC视频制作平台 | AI剪辑 | 云剪辑 | 海量模板</A>
|
||||
<DT><A HREF="https://medio.cool/" ADD_DATE="1762960443">米壳AI - Medio.cool 助力企业海外视频推广</A>
|
||||
<DT><A HREF="https://www.relume.io/" ADD_DATE="1762878776">Relume — Websites designed &amp; built faster with AI | AI website builder</A>
|
||||
<DT><A HREF="https://www.fliki.ai/" ADD_DATE="1762878700">fliki</A>
|
||||
<DT><A HREF="https://vidiq.com/" ADD_DATE="1762878646">vidiq</A>
|
||||
<DT><A HREF="https://app.creatify.ai/" ADD_DATE="1762878619">Creatify </A>
|
||||
</DL><p>
|
||||
<DT><H3 ADD_DATE="1766918493" LAST_MODIFIED="1766918493">AI图像</H3>
|
||||
<DL><p>
|
||||
<DT><A HREF="https://www.aiwind.org/" ADD_DATE="1764389764">AI Wind</A>
|
||||
<DT><A HREF="https://uizard.io/" ADD_DATE="1762961665">UI Design Made Easy, Powered By AI | Uizard</A>
|
||||
<DT><A HREF="https://www.uxbot.cn/" ADD_DATE="1762960498">AI设计生成代码:快速构建网站与App|UXbot</A>
|
||||
<DT><A HREF="https://logodiffusion.com/" ADD_DATE="1762960460">AI Logo Maker | Logo Maker | Logo Diffusion</A>
|
||||
</DL><p>
|
||||
<DT><H3 ADD_DATE="1766918493" LAST_MODIFIED="1766918493">AI建站</H3>
|
||||
<DL><p>
|
||||
<DT><A HREF="https://stocksentinel.ai/" ADD_DATE="1762960515">Stock Sentinel</A>
|
||||
<DT><A HREF="https://www.appwrite.io/" ADD_DATE="1762879030">Appwrite - The developers&#039; cloud</A>
|
||||
<DT><A HREF="https://gitcode.com/" ADD_DATE="1762878942">GitCode - 全球开发者的开源社区,开源代码托管平台</A>
|
||||
<DT><A HREF="https://lovable.dev/" ADD_DATE="1762878420">Lovable</A>
|
||||
<DT><A HREF="https://base44.com/" ADD_DATE="1762876528">Base44</A>
|
||||
</DL><p>
|
||||
<DT><H3 ADD_DATE="1766918493" LAST_MODIFIED="1766918493">Youtube大神</H3>
|
||||
<DL><p>
|
||||
<DT><A HREF="https://www.youtube.com/@tech-shrimp" ADD_DATE="1762960930">技术爬爬虾</A>
|
||||
</DL><p>
|
||||
<DT><H3 ADD_DATE="1766918493" LAST_MODIFIED="1766918493">GitHub大神</H3>
|
||||
<DL><p>
|
||||
<DT><A HREF="https://github.com/wayfind" ADD_DATE="1762961434">Ramond</A>
|
||||
<DT><A HREF="https://github.com/Selei1983" ADD_DATE="1762961372">Selei1983</A>
|
||||
</DL><p>
|
||||
<DT><H3 ADD_DATE="1766918493" LAST_MODIFIED="1766918493">神奇必推</H3>
|
||||
<DL><p>
|
||||
<DT><A HREF="https://lmarena.ai" ADD_DATE="1764656354">LMArena</A>
|
||||
<DT><A HREF="https://www.playphrase.me/" ADD_DATE="1762963518">PlayPhrase.me: Site for cinema archaeologists.</A>
|
||||
</DL><p>
|
||||
<DT><H3 ADD_DATE="1766918493" LAST_MODIFIED="1766918493">其他收藏</H3>
|
||||
<DL><p>
|
||||
<DT><A HREF="https://www.ycombinator.com/" ADD_DATE="1764041514">Y Combinator</A>
|
||||
<DT><A HREF="https://www.trademap.org/" ADD_DATE="1762964077">Trade Map - Trade statistics for international business development</A>
|
||||
</DL><p>
|
||||
<DT><H3 ADD_DATE="1766918493" LAST_MODIFIED="1766918493">本地服务</H3>
|
||||
<DL><p>
|
||||
<DT><A HREF="https://www.asterdex.com/en" ADD_DATE="1763223053">Aster - The next-gen perp DEX for all traders</A>
|
||||
<DT><A HREF="http://192.168.210.166:5666/login" ADD_DATE="1763046519">210飞牛</A>
|
||||
</DL><p>
|
||||
</DL><p>
|
||||
</DL><p>
|
||||
145
test_batch_import.py
Normal file
@@ -0,0 +1,145 @@
|
||||
"""测试批量导入功能的错误处理"""
|
||||
import sys
|
||||
import os
|
||||
|
||||
# 设置UTF-8编码
|
||||
if sys.platform == 'win32':
|
||||
import io
|
||||
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8')
|
||||
|
||||
from utils.bookmark_parser import BookmarkParser
|
||||
from utils.website_fetcher import WebsiteFetcher
|
||||
|
||||
def test_url_parsing():
|
||||
"""测试URL列表解析"""
|
||||
print("=" * 50)
|
||||
print("测试1: URL列表解析")
|
||||
print("=" * 50)
|
||||
|
||||
parser = BookmarkParser()
|
||||
test_urls = """
|
||||
https://www.google.com
|
||||
https://github.com
|
||||
https://invalid-url-that-does-not-exist-12345.com
|
||||
http://example.com
|
||||
# 这是注释,应该被忽略
|
||||
https://www.python.org
|
||||
"""
|
||||
|
||||
urls = parser.parse_url_list(test_urls)
|
||||
print(f"成功解析 {len(urls)} 个URL:")
|
||||
for idx, url_info in enumerate(urls, 1):
|
||||
print(f" {idx}. {url_info['url']}")
|
||||
print()
|
||||
|
||||
|
||||
def test_website_fetching():
|
||||
"""测试网站信息抓取"""
|
||||
print("=" * 50)
|
||||
print("测试2: 网站信息抓取(含错误处理)")
|
||||
print("=" * 50)
|
||||
|
||||
fetcher = WebsiteFetcher(timeout=10)
|
||||
|
||||
test_cases = [
|
||||
"https://www.google.com",
|
||||
"https://github.com",
|
||||
"https://invalid-url-that-does-not-exist-12345.com", # 这个应该失败
|
||||
"https://httpbin.org",
|
||||
]
|
||||
|
||||
for idx, url in enumerate(test_cases, 1):
|
||||
print(f"\n[{idx}/{len(test_cases)}] 正在抓取: {url}")
|
||||
try:
|
||||
info = fetcher.fetch_website_info(url)
|
||||
if info:
|
||||
print(f" [OK] 成功:")
|
||||
print(f" 名称: {info.get('name', 'N/A')[:50]}")
|
||||
print(f" 描述: {info.get('description', 'N/A')[:80]}")
|
||||
print(f" Logo: {info.get('logo_url', 'N/A')[:60]}")
|
||||
else:
|
||||
print(f" [FAIL] 失败: 无法获取网站信息")
|
||||
except Exception as e:
|
||||
print(f" [ERROR] 异常: {str(e)[:100]}")
|
||||
print()
|
||||
|
||||
|
||||
def test_error_isolation():
|
||||
"""测试错误隔离 - 确保一个失败不影响其他"""
|
||||
print("=" * 50)
|
||||
print("测试3: 错误隔离验证")
|
||||
print("=" * 50)
|
||||
|
||||
fetcher = WebsiteFetcher(timeout=5)
|
||||
|
||||
urls = [
|
||||
{"url": "https://www.google.com", "name": "Google"},
|
||||
{"url": "https://invalid-url-12345.com", "name": "Invalid"}, # 这个会失败
|
||||
{"url": "https://github.com", "name": "GitHub"},
|
||||
{"url": "https://another-invalid-url.xyz", "name": "Invalid2"}, # 这个也会失败
|
||||
{"url": "https://www.python.org", "name": "Python"},
|
||||
]
|
||||
|
||||
success_count = 0
|
||||
failed_count = 0
|
||||
|
||||
for idx, item in enumerate(urls, 1):
|
||||
url = item['url']
|
||||
name = item['name']
|
||||
|
||||
print(f"\n[{idx}/{len(urls)}] 处理: {name} ({url})")
|
||||
|
||||
try:
|
||||
# 模拟批量导入的错误处理逻辑
|
||||
info = None
|
||||
try:
|
||||
info = fetcher.fetch_website_info(url)
|
||||
except Exception as e:
|
||||
print(f" ⚠ 抓取失败: {str(e)[:50]}")
|
||||
|
||||
if not info or not info.get('name'):
|
||||
if name:
|
||||
info = {'name': name, 'description': '', 'logo_url': ''}
|
||||
print(f" [FALLBACK] 使用备用名称: {name}")
|
||||
else:
|
||||
failed_count += 1
|
||||
print(f" [SKIP] 跳过: 无法获取信息且无备用名称")
|
||||
continue
|
||||
|
||||
success_count += 1
|
||||
print(f" [SUCCESS] 成功: {info.get('name', 'N/A')[:50]}")
|
||||
|
||||
except Exception as e:
|
||||
failed_count += 1
|
||||
print(f" [ERROR] 未知错误: {str(e)[:50]}")
|
||||
continue # 继续处理下一个
|
||||
|
||||
print(f"\n{'=' * 50}")
|
||||
print(f"测试完成:")
|
||||
print(f" 总计: {len(urls)}")
|
||||
print(f" 成功: {success_count}")
|
||||
print(f" 失败: {failed_count}")
|
||||
print(f" 成功率: {success_count / len(urls) * 100:.1f}%")
|
||||
print(f"{'=' * 50}\n")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
try:
|
||||
test_url_parsing()
|
||||
test_website_fetching()
|
||||
test_error_isolation()
|
||||
|
||||
print("\n" + "=" * 50)
|
||||
print("所有测试完成!")
|
||||
print("=" * 50)
|
||||
print("\n关键验证点:")
|
||||
print("1. [OK] URL解析正常工作")
|
||||
print("2. [OK] 网站信息抓取有错误处理")
|
||||
print("3. [OK] 单个失败不影响其他URL处理")
|
||||
print("4. [OK] 提供了备用方案(使用书签名称)")
|
||||
|
||||
except Exception as e:
|
||||
print(f"\n测试过程中出现错误: {str(e)}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
sys.exit(1)
|
||||
184
test_site_creation.py
Normal file
@@ -0,0 +1,184 @@
|
||||
"""测试网站创建功能 - 验证code和slug自动生成"""
|
||||
import sys
|
||||
import os
|
||||
|
||||
# 设置UTF-8编码
|
||||
if sys.platform == 'win32':
|
||||
import io
|
||||
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8')
|
||||
|
||||
from app import create_app
|
||||
from models import db, Site
|
||||
|
||||
def test_site_creation_with_autoflush():
|
||||
"""测试通过Flask-Admin添加网站时的autoflush问题"""
|
||||
print("=" * 60)
|
||||
print("测试:验证on_model_change中的no_autoflush修复")
|
||||
print("=" * 60)
|
||||
|
||||
app = create_app()
|
||||
|
||||
with app.app_context():
|
||||
try:
|
||||
# 模拟Flask-Admin创建网站的过程
|
||||
print("\n1. 创建新网站对象(code和slug为空)...")
|
||||
site = Site(
|
||||
name='测试网站WaytoAGI',
|
||||
url='https://test.waytoagi.com',
|
||||
short_desc='这是一个测试网站',
|
||||
description='用于测试code和slug自动生成功能',
|
||||
is_active=True
|
||||
)
|
||||
|
||||
print(f" 初始状态: code={site.code}, slug={site.slug}")
|
||||
|
||||
# 添加到session
|
||||
print("\n2. 添加到数据库session...")
|
||||
db.session.add(site)
|
||||
|
||||
# 模拟on_model_change的调用
|
||||
print("\n3. 执行on_model_change逻辑(生成code和slug)...")
|
||||
import re
|
||||
import random
|
||||
from pypinyin import lazy_pinyin
|
||||
|
||||
# 使用no_autoflush(这是修复的关键)
|
||||
with db.session.no_autoflush:
|
||||
# 生成code
|
||||
if not site.code:
|
||||
for _ in range(10):
|
||||
code = str(random.randint(10000000, 99999999))
|
||||
existing = Site.query.filter(Site.code == code).first()
|
||||
if not existing:
|
||||
site.code = code
|
||||
print(f" 生成code: {code}")
|
||||
break
|
||||
|
||||
# 生成slug
|
||||
if not site.slug:
|
||||
slug = ''.join(lazy_pinyin(site.name))
|
||||
slug = slug.lower()
|
||||
slug = re.sub(r'[^\w\s-]', '', slug)
|
||||
slug = re.sub(r'[-\s]+', '-', slug).strip('-')
|
||||
|
||||
if not slug:
|
||||
slug = f"site-{site.code}"
|
||||
|
||||
base_slug = slug[:50]
|
||||
counter = 1
|
||||
final_slug = slug
|
||||
|
||||
while counter < 100:
|
||||
existing = Site.query.filter(Site.slug == final_slug).first()
|
||||
if not existing:
|
||||
break
|
||||
final_slug = f"{base_slug}-{counter}"
|
||||
counter += 1
|
||||
|
||||
site.slug = final_slug
|
||||
print(f" 生成slug: {final_slug}")
|
||||
|
||||
print(f"\n4. 提交到数据库...")
|
||||
print(f" 最终状态: code={site.code}, slug={site.slug}")
|
||||
|
||||
# 提交事务
|
||||
db.session.commit()
|
||||
|
||||
print("\n[SUCCESS] 网站创建成功!")
|
||||
print(f" ID: {site.id}")
|
||||
print(f" 名称: {site.name}")
|
||||
print(f" Code: {site.code}")
|
||||
print(f" Slug: {site.slug}")
|
||||
|
||||
# 验证数据
|
||||
print("\n5. 验证数据完整性...")
|
||||
assert site.code is not None, "code不能为空"
|
||||
assert site.slug is not None, "slug不能为空"
|
||||
assert len(site.code) == 8, "code必须是8位数字"
|
||||
assert site.code.isdigit(), "code必须是纯数字"
|
||||
print(" [OK] 所有验证通过")
|
||||
|
||||
# 清理测试数据
|
||||
print("\n6. 清理测试数据...")
|
||||
db.session.delete(site)
|
||||
db.session.commit()
|
||||
print(" [OK] 测试数据已清理")
|
||||
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
db.session.rollback()
|
||||
print(f"\n[FAILED] 测试失败:{str(e)}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
return False
|
||||
|
||||
def test_unique_code_generation():
|
||||
"""测试code唯一性生成"""
|
||||
print("\n" + "=" * 60)
|
||||
print("测试:验证code唯一性检查")
|
||||
print("=" * 60)
|
||||
|
||||
app = create_app()
|
||||
|
||||
with app.app_context():
|
||||
try:
|
||||
import random
|
||||
|
||||
print("\n1. 生成测试code...")
|
||||
test_code = str(random.randint(10000000, 99999999))
|
||||
print(f" 测试code: {test_code}")
|
||||
|
||||
print("\n2. 创建第一个网站...")
|
||||
site1 = Site(
|
||||
code=test_code,
|
||||
name='测试网站1',
|
||||
url='https://test1.com',
|
||||
slug='test-site-1',
|
||||
is_active=True
|
||||
)
|
||||
db.session.add(site1)
|
||||
db.session.commit()
|
||||
print(f" [OK] 网站1创建成功,code={site1.code}")
|
||||
|
||||
print("\n3. 尝试在no_autoflush中查询该code...")
|
||||
with db.session.no_autoflush:
|
||||
existing = Site.query.filter(Site.code == test_code).first()
|
||||
if existing:
|
||||
print(f" [OK] 成功查询到已存在的code: {existing.code}")
|
||||
else:
|
||||
print(f" [ERROR] 未能查询到已存在的code")
|
||||
return False
|
||||
|
||||
print("\n4. 清理测试数据...")
|
||||
db.session.delete(site1)
|
||||
db.session.commit()
|
||||
print(" [OK] 测试数据已清理")
|
||||
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
db.session.rollback()
|
||||
print(f"\n[FAILED] 测试失败:{str(e)}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
return False
|
||||
|
||||
if __name__ == "__main__":
|
||||
print("\n开始测试网站创建功能...\n")
|
||||
|
||||
test1_passed = test_site_creation_with_autoflush()
|
||||
test2_passed = test_unique_code_generation()
|
||||
|
||||
print("\n" + "=" * 60)
|
||||
print("测试结果汇总")
|
||||
print("=" * 60)
|
||||
print(f"测试1 - autoflush修复验证: {'[PASSED]' if test1_passed else '[FAILED]'}")
|
||||
print(f"测试2 - code唯一性验证: {'[PASSED]' if test2_passed else '[FAILED]'}")
|
||||
|
||||
if test1_passed and test2_passed:
|
||||
print("\n[SUCCESS] 所有测试通过!on_model_change修复有效。")
|
||||
sys.exit(0)
|
||||
else:
|
||||
print("\n[FAILED] 部分测试失败,需要进一步检查。")
|
||||
sys.exit(1)
|
||||
204
utils/bookmark_parser.py
Normal file
@@ -0,0 +1,204 @@
|
||||
"""OneNav/Chrome书签HTML文件解析工具"""
|
||||
from bs4 import BeautifulSoup
|
||||
from typing import List, Dict
|
||||
import re
|
||||
|
||||
|
||||
class BookmarkParser:
|
||||
"""解析OneNav/Chrome导出的书签HTML文件"""
|
||||
|
||||
def parse_html_file(self, html_content: str, debug=False) -> Dict[str, any]:
|
||||
"""
|
||||
解析OneNav/Chrome书签HTML文件
|
||||
|
||||
Args:
|
||||
html_content: HTML文件内容
|
||||
debug: 是否打印调试信息
|
||||
|
||||
Returns:
|
||||
Dict: 包含 categories(标签列表) 和 sites(网站列表)
|
||||
"""
|
||||
soup = BeautifulSoup(html_content, 'html.parser')
|
||||
categories = set() # 使用set去重
|
||||
sites = []
|
||||
|
||||
# 找到第一个DL标签作为起点
|
||||
first_dl = soup.find('dl')
|
||||
if first_dl:
|
||||
# 递归解析书签,收集分类和网站
|
||||
self._parse_dl_tag(first_dl, categories, sites, current_category=None, debug=debug)
|
||||
|
||||
return {
|
||||
'categories': sorted(list(categories)), # 转为排序的列表
|
||||
'sites': sites
|
||||
}
|
||||
|
||||
def parse_html_file_legacy(self, html_content: str) -> List[Dict[str, str]]:
|
||||
"""
|
||||
解析Chrome书签HTML文件(旧版格式)
|
||||
|
||||
Args:
|
||||
html_content: HTML文件内容
|
||||
|
||||
Returns:
|
||||
List[Dict]: 书签列表,每个书签包含 name, url, folder
|
||||
"""
|
||||
soup = BeautifulSoup(html_content, 'html.parser')
|
||||
bookmarks = []
|
||||
|
||||
# 递归解析书签
|
||||
self._parse_dl_tag_legacy(soup, bookmarks, folder_path="")
|
||||
|
||||
return bookmarks
|
||||
|
||||
def _parse_dl_tag(self, element, categories: set, sites: List[Dict], current_category: str, debug=False):
|
||||
"""递归解析DL标签(OneNav格式)"""
|
||||
# 查找所有DT标签(不限制为直接子元素,因为可能在p标签内)
|
||||
dt_list = element.find_all('dt')
|
||||
if debug and dt_list:
|
||||
print(f"Found {len(dt_list)} DT tags total")
|
||||
|
||||
for dt in dt_list:
|
||||
# 检查是否是文件夹/分类
|
||||
h3 = dt.find('h3', recursive=False)
|
||||
if h3:
|
||||
category_name = h3.get_text(strip=True)
|
||||
|
||||
# 跳过根节点和默认分类
|
||||
if category_name not in ['OneNav', 'OneNav默认分类']:
|
||||
categories.add(category_name)
|
||||
if debug:
|
||||
print(f" Category: {category_name}")
|
||||
|
||||
# 检查是否是书签链接(并且不在子分类的DL中)
|
||||
a = dt.find('a', recursive=False)
|
||||
if a and a.get('href'):
|
||||
# 找到这个DT所属的最近的H3分类
|
||||
parent_category = None
|
||||
# 向上查找同级或父级的H3
|
||||
prev = dt.find_previous('h3')
|
||||
if prev:
|
||||
parent_category = prev.get_text(strip=True)
|
||||
# 跳过根节点和默认分类
|
||||
if parent_category in ['OneNav', 'OneNav默认分类']:
|
||||
parent_category = None
|
||||
|
||||
if parent_category:
|
||||
url = a['href']
|
||||
title = a.get_text(strip=True)
|
||||
|
||||
sites.append({
|
||||
'title': title,
|
||||
'url': url,
|
||||
'category': parent_category,
|
||||
'add_date': a.get('add_date', '')
|
||||
})
|
||||
if debug:
|
||||
print(f" Site: {title} -> {parent_category}")
|
||||
|
||||
def _parse_dl_tag_legacy(self, element, bookmarks: List[Dict], folder_path: str):
|
||||
"""递归解析DL标签(Chrome旧格式)"""
|
||||
# 查找所有DT标签(书签项)
|
||||
for dt in element.find_all('dt', recursive=False):
|
||||
# 检查是否是文件夹
|
||||
h3 = dt.find('h3', recursive=False)
|
||||
if h3:
|
||||
folder_name = h3.get_text(strip=True)
|
||||
new_folder_path = f"{folder_path}/{folder_name}" if folder_path else folder_name
|
||||
|
||||
# 递归解析子文件夹
|
||||
dl = dt.find('dl', recursive=False)
|
||||
if dl:
|
||||
self._parse_dl_tag_legacy(dl, bookmarks, new_folder_path)
|
||||
|
||||
# 检查是否是书签链接
|
||||
a = dt.find('a', recursive=False)
|
||||
if a and a.get('href'):
|
||||
url = a['href']
|
||||
name = a.get_text(strip=True)
|
||||
|
||||
bookmarks.append({
|
||||
'name': name,
|
||||
'url': url,
|
||||
'folder': folder_path
|
||||
})
|
||||
|
||||
def parse_url_list(self, text: str) -> List[Dict[str, str]]:
|
||||
"""
|
||||
解析纯文本URL列表
|
||||
|
||||
Args:
|
||||
text: 文本内容,每行一个URL
|
||||
|
||||
Returns:
|
||||
List[Dict]: URL列表
|
||||
"""
|
||||
urls = []
|
||||
lines = text.strip().split('\n')
|
||||
|
||||
for line in lines:
|
||||
line = line.strip()
|
||||
if not line or line.startswith('#'):
|
||||
continue
|
||||
|
||||
# 简单的URL验证
|
||||
if re.match(r'^https?://', line):
|
||||
urls.append({
|
||||
'url': line,
|
||||
'name': '', # 名称留空,后续自动获取
|
||||
'folder': ''
|
||||
})
|
||||
|
||||
return urls
|
||||
|
||||
@staticmethod
|
||||
def clean_title(title: str) -> str:
|
||||
"""清理网站标题,提取网站名称"""
|
||||
if not title:
|
||||
return ''
|
||||
|
||||
# 去除HTML实体
|
||||
title = re.sub(r'&', '&', title)
|
||||
title = re.sub(r'<', '<', title)
|
||||
title = re.sub(r'>', '>', title)
|
||||
title = re.sub(r'"', '"', title)
|
||||
|
||||
# 常见的分隔符
|
||||
separators = [' - ', ' | ', ' · ', '·', '|', ' — ']
|
||||
for sep in separators:
|
||||
if sep in title:
|
||||
parts = title.split(sep)
|
||||
# 过滤掉常见的无用部分
|
||||
filtered_parts = []
|
||||
skip_keywords = ['官网', '首页', 'official', 'home', 'page', 'website']
|
||||
|
||||
for part in parts:
|
||||
part = part.strip()
|
||||
if part and not any(kw in part.lower() for kw in skip_keywords):
|
||||
filtered_parts.append(part)
|
||||
|
||||
if filtered_parts:
|
||||
# 返回最短的部分(通常是网站名)
|
||||
return min(filtered_parts, key=len)
|
||||
|
||||
# 去除一些常见的后缀词
|
||||
suffixes = [
|
||||
r'\s*官网\s*$', r'\s*首页\s*$',
|
||||
r'\s*Official Site\s*$', r'\s*Home Page\s*$',
|
||||
r'\s*Homepage\s*$', r'\s*Website\s*$'
|
||||
]
|
||||
for suffix in suffixes:
|
||||
title = re.sub(suffix, '', title, flags=re.IGNORECASE)
|
||||
|
||||
return title.strip()
|
||||
|
||||
@staticmethod
|
||||
def extract_domain(url: str) -> str:
|
||||
"""从URL提取域名"""
|
||||
match = re.search(r'https?://([^/]+)', url)
|
||||
if match:
|
||||
domain = match.group(1)
|
||||
# 去除www前缀
|
||||
domain = re.sub(r'^www\.', '', domain)
|
||||
return domain
|
||||
return url
|
||||
98
utils/tag_generator.py
Normal file
@@ -0,0 +1,98 @@
|
||||
"""DeepSeek AI 标签生成工具"""
|
||||
import os
|
||||
from openai import OpenAI
|
||||
|
||||
|
||||
class TagGenerator:
|
||||
"""使用DeepSeek生成标签"""
|
||||
|
||||
def __init__(self):
|
||||
self.api_key = os.environ.get('DEEPSEEK_API_KEY')
|
||||
self.base_url = os.environ.get('DEEPSEEK_BASE_URL', 'https://api.deepseek.com')
|
||||
self.client = None
|
||||
|
||||
# 如果有API key,初始化客户端
|
||||
if self.api_key:
|
||||
self.client = OpenAI(
|
||||
api_key=self.api_key,
|
||||
base_url=self.base_url
|
||||
)
|
||||
|
||||
def generate_tags(self, name, description, existing_tags=None):
|
||||
"""
|
||||
根据产品名称和描述生成标签
|
||||
|
||||
Args:
|
||||
name: 产品名称
|
||||
description: 产品描述
|
||||
existing_tags: 现有标签列表(用于参考)
|
||||
|
||||
Returns:
|
||||
list: 生成的标签列表
|
||||
"""
|
||||
# 检查是否配置了API key
|
||||
if not self.client:
|
||||
raise ValueError("DEEPSEEK_API_KEY未配置,请在.env文件中添加")
|
||||
|
||||
try:
|
||||
# 构建提示词
|
||||
existing_tags_str = ""
|
||||
if existing_tags:
|
||||
existing_tags_str = f"\n\n系统中已有的标签参考:\n{', '.join(existing_tags)}\n尽量使用已有标签,如果合适的话。"
|
||||
|
||||
prompt = f"""你是一个AI工具导航网站的标签生成助手。根据以下产品信息,生成3-5个最合适的标签。
|
||||
|
||||
产品名称: {name}
|
||||
|
||||
产品描述: {description}
|
||||
{existing_tags_str}
|
||||
|
||||
要求:
|
||||
1. 标签应该准确描述产品的功能、类型或应用场景
|
||||
2. 每个标签2-4个汉字
|
||||
3. 标签要具体且有区分度
|
||||
4. 如果是AI工具,可以标注具体的AI类型(如"GPT"、"图像生成"等)
|
||||
5. 只返回标签,用逗号分隔,不要其他说明
|
||||
|
||||
示例输出格式:写作助手,营销,GPT,内容生成
|
||||
|
||||
请生成标签:"""
|
||||
|
||||
# 调用DeepSeek API
|
||||
response = self.client.chat.completions.create(
|
||||
model="deepseek-chat",
|
||||
messages=[
|
||||
{"role": "system", "content": "你是一个专业的AI工具分类专家,擅长为各类AI产品生成准确的标签。"},
|
||||
{"role": "user", "content": prompt}
|
||||
],
|
||||
temperature=0.7,
|
||||
max_tokens=100
|
||||
)
|
||||
|
||||
# 解析返回的标签
|
||||
tags_text = response.choices[0].message.content.strip()
|
||||
tags = [tag.strip() for tag in tags_text.split(',') if tag.strip()]
|
||||
|
||||
# 限制标签数量为3-5个
|
||||
if len(tags) > 5:
|
||||
tags = tags[:5]
|
||||
|
||||
return tags
|
||||
|
||||
except Exception as e:
|
||||
print(f"DeepSeek标签生成失败: {str(e)}")
|
||||
return []
|
||||
|
||||
def generate_news_summary(self, url, content):
|
||||
"""
|
||||
生成新闻摘要(未来功能)
|
||||
|
||||
Args:
|
||||
url: 新闻链接
|
||||
content: 新闻内容
|
||||
|
||||
Returns:
|
||||
str: 新闻摘要
|
||||
"""
|
||||
# TODO: 实现新闻摘要生成
|
||||
pass
|
||||
25
zjpb_homepage.html
Normal file
@@ -0,0 +1,25 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta content="AI网址收藏" name="keywords">
|
||||
<meta content="0焦提示盒(ZJPB.NET),不费吹灰之力获取AI平台最新信息" name="description">
|
||||
<meta content="IE=edge" http-equiv="X-UA-Compatible">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
|
||||
<title>0焦提示盒 - 0焦提示盒,不费吹灰之力获取AI平台最新信息</title>
|
||||
<script type="module" crossorigin src="/templates/default2/assets/index.js?v=1.3.5"></script>
|
||||
<link href="/templates/default2/assets/index.css?v=1.3.5" rel="stylesheet">
|
||||
<script type="text/javascript">
|
||||
(function(c,l,a,r,i,t,y){
|
||||
c[a]=c[a]||function(){(c[a].q=c[a].q||[]).push(arguments)};
|
||||
t=l.createElement(r);t.async=1;t.src="https://www.clarity.ms/tag/"+i;
|
||||
y=l.getElementsByTagName(r)[0];y.parentNode.insertBefore(t,y);
|
||||
})(window, document, "clarity", "script", "uoa2j40sf0");
|
||||
</script> <link rel="manifest" href="/templates/default2/manifest.json" />
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
</body>
|
||||
|
||||
</html>
|
||||