diff --git a/app.py b/app.py index cd32590..df966eb 100644 --- a/app.py +++ b/app.py @@ -43,6 +43,11 @@ def index(): def search_page(): return render_template('search.html') +# 内容库页面 +@app.route('/library') +def library_page(): + return render_template('library.html') + # API首页 @app.route('/api') def api_index(): diff --git a/static/css/library.css b/static/css/library.css new file mode 100644 index 0000000..aa3cdb1 --- /dev/null +++ b/static/css/library.css @@ -0,0 +1,327 @@ +/* 内容库页面专用样式 */ + +.library-container { + max-width: 1400px; + margin: 0 auto; + padding: 20px; +} + +/* 头部 */ +.library-header { + background: white; + padding: 20px 30px; + border-radius: 12px; + box-shadow: 0 4px 6px rgba(0,0,0,0.1); + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 20px; +} + +.header-left { + display: flex; + align-items: center; + gap: 20px; +} + +.back-link { + color: #667eea; + text-decoration: none; + display: flex; + align-items: center; + gap: 5px; + font-size: 14px; +} + +.back-link:hover { + text-decoration: underline; +} + +.library-header h1 { + color: #333; + font-size: 24px; + display: flex; + align-items: center; + gap: 10px; +} + +.stats-info { + font-size: 14px; + color: #666; +} + +/* 工具栏 */ +.toolbar { + background: white; + padding: 15px 20px; + border-radius: 12px; + box-shadow: 0 2px 4px rgba(0,0,0,0.05); + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 20px; +} + +.toolbar-left { + display: flex; + gap: 15px; + align-items: center; +} + +.search-box { + position: relative; +} + +.search-box i { + position: absolute; + left: 12px; + top: 50%; + transform: translateY(-50%); + color: #999; +} + +.search-box input { + padding: 10px 15px 10px 35px; + border: 1px solid #ddd; + border-radius: 6px; + font-size: 14px; + width: 300px; +} + +.search-box input:focus { + outline: none; + border-color: #667eea; +} + +.category-select { + padding: 10px 15px; + border: 1px solid #ddd; + border-radius: 6px; + font-size: 14px; + background: white; + cursor: pointer; +} + +.toolbar-right { + display: flex; + gap: 10px; +} + +/* 文章列表区域 */ +.articles-section { + background: white; + border-radius: 12px; + box-shadow: 0 2px 4px rgba(0,0,0,0.05); +} + +.articles-header { + padding: 15px 20px; + background: #f8f9fa; + border-bottom: 1px solid #e9ecef; + display: flex; + justify-content: space-between; + align-items: center; +} + +.checkbox-wrapper { + display: flex; + align-items: center; + gap: 8px; + cursor: pointer; + font-size: 14px; +} + +.checkbox-wrapper input { + width: 18px; + height: 18px; + cursor: pointer; +} + +.batch-actions { + display: flex; + align-items: center; + gap: 15px; +} + +#selected-count { + font-size: 14px; + color: #666; +} + +.articles-body { + padding: 20px; +} + +.articles-list { + display: flex; + flex-direction: column; + gap: 15px; +} + +/* 文章卡片 */ +.article-card { + border: 1px solid #e9ecef; + border-radius: 8px; + padding: 20px; + transition: all 0.2s; +} + +.article-card:hover { + border-color: #667eea; + background: #f8f9fa; +} + +.article-card.selected { + border-color: #667eea; + background: #f0f4ff; +} + +.article-card-header { + display: flex; + justify-content: space-between; + align-items: flex-start; + margin-bottom: 10px; +} + +.article-select { + width: 18px; + height: 18px; + cursor: pointer; +} + +.article-title { + font-size: 16px; + font-weight: 500; + color: #333; + cursor: pointer; + flex: 1; +} + +.article-title:hover { + color: #667eea; +} + +.article-actions { + display: flex; + gap: 8px; +} + +.article-meta { + display: flex; + gap: 15px; + font-size: 13px; + color: #666; + margin-bottom: 10px; +} + +.article-meta span { + display: flex; + align-items: center; + gap: 4px; +} + +.article-category { + background: #e0e7ff; + color: #3730a3; + padding: 2px 8px; + border-radius: 4px; + font-size: 12px; +} + +.article-summary { + font-size: 14px; + color: #666; + line-height: 1.6; + display: -webkit-box; + -webkit-line-clamp: 2; + -webkit-box-orient: vertical; + overflow: hidden; +} + +.article-tags { + display: flex; + gap: 6px; + margin-top: 10px; +} + +.article-tag { + background: #f3f4f6; + color: #6b7280; + padding: 2px 8px; + border-radius: 4px; + font-size: 12px; +} + +/* 分页 */ +.pagination { + display: flex; + justify-content: center; + align-items: center; + gap: 20px; + margin-top: 20px; +} + +#page-info { + font-size: 14px; + color: #666; +} + +/* 空状态 */ +.empty-text { + text-align: center; + color: #999; + padding: 40px; +} + +.loading-text { + text-align: center; + color: #666; + padding: 40px; +} + +/* 详情模态框 */ +#detail-body .detail-section { + margin-bottom: 20px; +} + +#detail-body .detail-section h4 { + color: #667eea; + margin-bottom: 10px; + font-size: 16px; +} + +#detail-body .detail-content { + white-space: pre-wrap; + background: #f8f9fa; + padding: 15px; + border-radius: 8px; + max-height: 400px; + overflow-y: auto; +} + +/* 响应式 */ +@media (max-width: 768px) { + .toolbar { + flex-direction: column; + gap: 15px; + } + + .toolbar-left, .toolbar-right { + width: 100%; + flex-wrap: wrap; + } + + .search-box input { + width: 100%; + } + + .article-card-header { + flex-direction: column; + gap: 10px; + } + + .article-actions { + width: 100%; + justify-content: flex-end; + } +} \ No newline at end of file diff --git a/static/css/style.css b/static/css/style.css index 9a247a4..a504740 100644 --- a/static/css/style.css +++ b/static/css/style.css @@ -768,4 +768,37 @@ body { .search-entrance-link small { font-size: 13px; opacity: 0.8; +} + +/* 内容库入口链接 */ +.library-entrance-link { + display: inline-flex; + flex-direction: column; + align-items: center; + gap: 15px; + padding: 40px 80px; + background: linear-gradient(135deg, #10b981 0%, #059669 100%); + color: white; + text-decoration: none; + border-radius: 12px; + transition: all 0.3s; +} + +.library-entrance-link:hover { + transform: translateY(-3px); + box-shadow: 0 10px 30px rgba(16, 185, 129, 0.4); +} + +.library-entrance-link i { + font-size: 48px; +} + +.library-entrance-link span { + font-size: 18px; + font-weight: 500; +} + +.library-entrance-link small { + font-size: 13px; + opacity: 0.8; } \ No newline at end of file diff --git a/static/js/library.js b/static/js/library.js new file mode 100644 index 0000000..0a42130 --- /dev/null +++ b/static/js/library.js @@ -0,0 +1,482 @@ +// API基础地址 +const API_BASE = ''; + +// 状态 +let articles = []; +let currentPage = 1; +let pageSize = 20; +let totalCount = 0; +let selectedIds = new Set(); +let currentArticleId = null; + +// 页面加载 +document.addEventListener('DOMContentLoaded', () => { + loadArticles(); + loadCategories(); + + // 搜索防抖 + let searchTimeout; + document.getElementById('search-input').addEventListener('input', (e) => { + clearTimeout(searchTimeout); + searchTimeout = setTimeout(() => { + currentPage = 1; + loadArticles(); + }, 300); + }); + + // 分类筛选 + document.getElementById('category-filter').addEventListener('change', () => { + currentPage = 1; + loadArticles(); + }); +}); + +// 加载文章列表 +async function loadArticles() { + const keyword = document.getElementById('search-input').value.trim(); + const category = document.getElementById('category-filter').value; + + const params = new URLSearchParams({ + limit: pageSize, + offset: (currentPage - 1) * pageSize + }); + + if (keyword) { + const response = await fetch(`${API_BASE}/api/articles/search?q=${encodeURIComponent(keyword)}${category ? '&category=' + encodeURIComponent(category) : ''}`); + const data = await response.json(); + + if (data.success) { + articles = data.articles; + totalCount = data.count; + } + } else { + const response = await fetch(`${API_BASE}/api/articles?${params.toString()}`); + const data = await response.json(); + + if (data.success) { + articles = data.articles; + totalCount = data.count; + } + } + + displayArticles(); + updatePagination(); + updateStats(); +} + +// 加载分类列表 +async function loadCategories() { + try { + const response = await fetch(`${API_BASE}/api/articles?limit=1000`); + const data = await response.json(); + + if (data.success) { + const categories = new Set(); + data.articles.forEach(a => { + if (a.category) categories.add(a.category); + }); + + const select = document.getElementById('category-filter'); + select.innerHTML = '' + + Array.from(categories).map(c => ``).join(''); + } + } catch (error) { + console.error('加载分类失败:', error); + } +} + +// 显示文章列表 +function displayArticles() { + const container = document.getElementById('articles-list'); + + if (articles.length === 0) { + container.innerHTML = '
${escapeHtml(productNames.join(', '))}
+${escapeHtml(keywords.join(', '))}
${escapeHtml(article.summary || '无')}
+