mirror of
https://github.com/Usagi-org/ai-goofish-monitor.git
synced 2025-11-25 19:37:37 +08:00
update 结果查看页面支持排序
This commit is contained in:
@@ -25,6 +25,15 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
<input type="checkbox" id="recommended-only-checkbox">
|
<input type="checkbox" id="recommended-only-checkbox">
|
||||||
仅看AI推荐
|
仅看AI推荐
|
||||||
</label>
|
</label>
|
||||||
|
<select id="sort-by-selector">
|
||||||
|
<option value="crawl_time">按爬取时间</option>
|
||||||
|
<option value="publish_time">按发布时间</option>
|
||||||
|
<option value="price">按价格</option>
|
||||||
|
</select>
|
||||||
|
<select id="sort-order-selector">
|
||||||
|
<option value="desc">降序</option>
|
||||||
|
<option value="asc">升序</option>
|
||||||
|
</select>
|
||||||
<button id="refresh-results-btn" class="control-button">🔄 刷新</button>
|
<button id="refresh-results-btn" class="control-button">🔄 刷新</button>
|
||||||
</div>
|
</div>
|
||||||
<div id="results-grid-container">
|
<div id="results-grid-container">
|
||||||
@@ -191,12 +200,14 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function fetchResultContent(filename, recommendedOnly) {
|
async function fetchResultContent(filename, recommendedOnly, sortBy, sortOrder) {
|
||||||
try {
|
try {
|
||||||
const params = new URLSearchParams({
|
const params = new URLSearchParams({
|
||||||
page: 1,
|
page: 1,
|
||||||
limit: 100, // Fetch a decent number of items
|
limit: 100, // Fetch a decent number of items
|
||||||
recommended_only: recommendedOnly
|
recommended_only: recommendedOnly,
|
||||||
|
sort_by: sortBy,
|
||||||
|
sort_order: sortOrder
|
||||||
});
|
});
|
||||||
const response = await fetch(`/api/results/${filename}?${params}`);
|
const response = await fetch(`/api/results/${filename}?${params}`);
|
||||||
if (!response.ok) throw new Error(`无法获取文件 ${filename} 的内容`);
|
if (!response.ok) throw new Error(`无法获取文件 ${filename} 的内容`);
|
||||||
@@ -288,6 +299,8 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
const recommendationText = isRecommended ? '推荐' : (ai.is_recommended === false ? '不推荐' : '待定');
|
const recommendationText = isRecommended ? '推荐' : (ai.is_recommended === false ? '不推荐' : '待定');
|
||||||
|
|
||||||
const imageUrl = (info.商品图片列表 && info.商品图片列表[0]) ? info.商品图片列表[0] : 'data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs=';
|
const imageUrl = (info.商品图片列表 && info.商品图片列表[0]) ? info.商品图片列表[0] : 'data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs=';
|
||||||
|
const crawlTime = item.爬取时间 ? new Date(item.爬取时间).toLocaleString('sv-SE').slice(0, 16) : '未知';
|
||||||
|
const publishTime = info.发布时间 || '未知';
|
||||||
|
|
||||||
return `
|
return `
|
||||||
<div class="result-card" data-item='${JSON.stringify(item)}'>
|
<div class="result-card" data-item='${JSON.stringify(item)}'>
|
||||||
@@ -303,6 +316,10 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
</div>
|
</div>
|
||||||
<div class="card-footer">
|
<div class="card-footer">
|
||||||
<span class="seller-info">卖家: ${info.卖家昵称 || seller.卖家昵称 || '未知'}</span>
|
<span class="seller-info">卖家: ${info.卖家昵称 || seller.卖家昵称 || '未知'}</span>
|
||||||
|
<div class="time-info">
|
||||||
|
<p>发布于: ${publishTime}</p>
|
||||||
|
<p>抓取于: ${crawlTime}</p>
|
||||||
|
</div>
|
||||||
<span>
|
<span>
|
||||||
<a href="${info.商品链接 || '#'}" target="_blank" class="action-btn">查看详情</a>
|
<a href="${info.商品链接 || '#'}" target="_blank" class="action-btn">查看详情</a>
|
||||||
</span>
|
</span>
|
||||||
@@ -400,12 +417,16 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
async function fetchAndRenderResults() {
|
async function fetchAndRenderResults() {
|
||||||
const selector = document.getElementById('result-file-selector');
|
const selector = document.getElementById('result-file-selector');
|
||||||
const checkbox = document.getElementById('recommended-only-checkbox');
|
const checkbox = document.getElementById('recommended-only-checkbox');
|
||||||
|
const sortBySelector = document.getElementById('sort-by-selector');
|
||||||
|
const sortOrderSelector = document.getElementById('sort-order-selector');
|
||||||
const container = document.getElementById('results-grid-container');
|
const container = document.getElementById('results-grid-container');
|
||||||
|
|
||||||
if (!selector || !checkbox || !container) return;
|
if (!selector || !checkbox || !container || !sortBySelector || !sortOrderSelector) return;
|
||||||
|
|
||||||
const selectedFile = selector.value;
|
const selectedFile = selector.value;
|
||||||
const recommendedOnly = checkbox.checked;
|
const recommendedOnly = checkbox.checked;
|
||||||
|
const sortBy = sortBySelector.value;
|
||||||
|
const sortOrder = sortOrderSelector.value;
|
||||||
|
|
||||||
if (!selectedFile) {
|
if (!selectedFile) {
|
||||||
container.innerHTML = '<p>请先选择一个结果文件。</p>';
|
container.innerHTML = '<p>请先选择一个结果文件。</p>';
|
||||||
@@ -413,7 +434,7 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
container.innerHTML = '<p>正在加载结果...</p>';
|
container.innerHTML = '<p>正在加载结果...</p>';
|
||||||
const data = await fetchResultContent(selectedFile, recommendedOnly);
|
const data = await fetchResultContent(selectedFile, recommendedOnly, sortBy, sortOrder);
|
||||||
container.innerHTML = renderResultsGrid(data);
|
container.innerHTML = renderResultsGrid(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -421,12 +442,16 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
const selector = document.getElementById('result-file-selector');
|
const selector = document.getElementById('result-file-selector');
|
||||||
const checkbox = document.getElementById('recommended-only-checkbox');
|
const checkbox = document.getElementById('recommended-only-checkbox');
|
||||||
const refreshBtn = document.getElementById('refresh-results-btn');
|
const refreshBtn = document.getElementById('refresh-results-btn');
|
||||||
|
const sortBySelector = document.getElementById('sort-by-selector');
|
||||||
|
const sortOrderSelector = document.getElementById('sort-order-selector');
|
||||||
|
|
||||||
const fileData = await fetchResultFiles();
|
const fileData = await fetchResultFiles();
|
||||||
if (fileData && fileData.files && fileData.files.length > 0) {
|
if (fileData && fileData.files && fileData.files.length > 0) {
|
||||||
selector.innerHTML = fileData.files.map(f => `<option value="${f}">${f}</option>`).join('');
|
selector.innerHTML = fileData.files.map(f => `<option value="${f}">${f}</option>`).join('');
|
||||||
selector.addEventListener('change', fetchAndRenderResults);
|
selector.addEventListener('change', fetchAndRenderResults);
|
||||||
checkbox.addEventListener('change', fetchAndRenderResults);
|
checkbox.addEventListener('change', fetchAndRenderResults);
|
||||||
|
sortBySelector.addEventListener('change', fetchAndRenderResults);
|
||||||
|
sortOrderSelector.addEventListener('change', fetchAndRenderResults);
|
||||||
refreshBtn.addEventListener('click', fetchAndRenderResults);
|
refreshBtn.addEventListener('click', fetchAndRenderResults);
|
||||||
// Initial load
|
// Initial load
|
||||||
await fetchAndRenderResults();
|
await fetchAndRenderResults();
|
||||||
|
|||||||
@@ -327,9 +327,9 @@ async def list_result_files():
|
|||||||
|
|
||||||
|
|
||||||
@app.get("/api/results/{filename}")
|
@app.get("/api/results/{filename}")
|
||||||
async def get_result_file_content(filename: str, page: int = 1, limit: int = 20, recommended_only: bool = False):
|
async def get_result_file_content(filename: str, page: int = 1, limit: int = 20, recommended_only: bool = False, sort_by: str = "crawl_time", sort_order: str = "desc"):
|
||||||
"""
|
"""
|
||||||
读取指定的 .jsonl 文件内容,支持分页和筛选。
|
读取指定的 .jsonl 文件内容,支持分页、筛选和排序。
|
||||||
"""
|
"""
|
||||||
if not filename.endswith(".jsonl") or "/" in filename or ".." in filename:
|
if not filename.endswith(".jsonl") or "/" in filename or ".." in filename:
|
||||||
raise HTTPException(status_code=400, detail="无效的文件名。")
|
raise HTTPException(status_code=400, detail="无效的文件名。")
|
||||||
@@ -353,7 +353,23 @@ async def get_result_file_content(filename: str, page: int = 1, limit: int = 20,
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
raise HTTPException(status_code=500, detail=f"读取结果文件时出错: {e}")
|
raise HTTPException(status_code=500, detail=f"读取结果文件时出错: {e}")
|
||||||
|
|
||||||
results.reverse()
|
# --- Sorting logic ---
|
||||||
|
def get_sort_key(item):
|
||||||
|
info = item.get("商品信息", {})
|
||||||
|
if sort_by == "publish_time":
|
||||||
|
# Handles "未知时间" by placing it at the end/start depending on order
|
||||||
|
return info.get("发布时间", "0000-00-00 00:00")
|
||||||
|
elif sort_by == "price":
|
||||||
|
price_str = str(info.get("当前售价", "0")).replace("¥", "").replace(",", "").strip()
|
||||||
|
try:
|
||||||
|
return float(price_str)
|
||||||
|
except (ValueError, TypeError):
|
||||||
|
return 0.0 # Default for unparsable prices
|
||||||
|
else: # default to crawl_time
|
||||||
|
return item.get("爬取时间", "")
|
||||||
|
|
||||||
|
is_reverse = (sort_order == "desc")
|
||||||
|
results.sort(key=get_sort_key, reverse=is_reverse)
|
||||||
|
|
||||||
total_items = len(results)
|
total_items = len(results)
|
||||||
start = (page - 1) * limit
|
start = (page - 1) * limit
|
||||||
|
|||||||
Reference in New Issue
Block a user