index.html 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596
  1. <!DOCTYPE html>
  2. <html lang="zh">
  3. <head>
  4. <meta charset="UTF-8">
  5. <title>宝可梦卡片搜索</title>
  6. <style>
  7. body { font-family: sans-serif; max-width: 1200px; margin: 0 auto; padding: 20px; }
  8. .container { display: flex; gap: 20px; }
  9. .upload-section { flex: 1; border-right: 1px solid #ccc; padding-right: 20px; }
  10. .results-section { flex: 2; }
  11. .card { border: 1px solid #ddd; padding: 10px; margin-bottom: 10px; display: flex; align-items: center; border-radius: 8px; }
  12. .card img { max-width: 100px; max-height: 140px; margin-right: 20px; }
  13. .card-info { font-size: 14px; }
  14. .similarity { color: green; font-weight: bold; }
  15. </style>
  16. </head>
  17. <body>
  18. <h1>🔍 Pokemon Card Search</h1>
  19. <div class="container">
  20. <div class="upload-section">
  21. <h3>图片搜索</h3>
  22. <input type="file" id="imgInput" accept="image/*">
  23. <br><br>
  24. <label>Top N: <input type="number" id="topN" value="5" style="width: 50px;"></label>
  25. <br><br>
  26. <button onclick="searchImage()">搜索</button>
  27. <br><hr><br>
  28. <h3>文字过滤</h3>
  29. <input type="text" id="nameFilter" placeholder="卡名 (如 Swadloon)">
  30. <button onclick="filterData()">查询</button>
  31. </div>
  32. <div class="results-section">
  33. <h3>结果</h3>
  34. <div id="results"></div>
  35. </div>
  36. </div>
  37. <script>
  38. async function searchImage() {
  39. const input = document.getElementById('imgInput');
  40. const topN = document.getElementById('topN').value;
  41. if (!input.files[0]) return alert("请选择图片");
  42. const formData = new FormData();
  43. formData.append('file', input.files[0]);
  44. const res = await fetch(`/search/image?top_k=${topN}`, {
  45. method: 'POST',
  46. body: formData
  47. });
  48. const data = await res.json();
  49. renderResults(data.results);
  50. }
  51. async function filterData() {
  52. const name = document.getElementById('nameFilter').value;
  53. const res = await fetch(`/search/filter?card_name=${name}&limit=10`, {
  54. method: 'POST'
  55. });
  56. const data = await res.json();
  57. renderResults(data.data, false);
  58. }
  59. function renderResults(items, isSearch=true) {
  60. const container = document.getElementById('results');
  61. container.innerHTML = '';
  62. if (!items || items.length === 0) {
  63. container.innerHTML = '<p>无结果</p>';
  64. return;
  65. }
  66. items.forEach(item => {
  67. const div = document.createElement('div');
  68. div.className = 'card';
  69. // 计算显示内容
  70. let scoreHtml = isSearch ? `<p class="similarity">相似度: ${(item.score).toFixed(4)}</p>` : '';
  71. div.innerHTML = `
  72. <img src="${item.img_url}" alt="${item.card_name}" onerror="this.src=''">
  73. <div class="card-info">
  74. <h3>${item.card_name} #${item.card_num}</h3>
  75. <p>语言: ${item.lang} | Source ID: ${item.source_id}</p>
  76. ${scoreHtml}
  77. <p style="color:gray; font-size:12px;">ID: ${item.id}</p>
  78. </div>
  79. `;
  80. container.appendChild(div);
  81. });
  82. }
  83. </script>
  84. </body>
  85. </html>