299 lines
6.1 KiB
Cheetah
299 lines
6.1 KiB
Cheetah
{{ define "debug" }}
|
||
<!DOCTYPE html>
|
||
<html lang="zh">
|
||
<head>
|
||
<meta charset="UTF-8" />
|
||
<title>Search</title>
|
||
<style>
|
||
body {
|
||
font-family: Arial;
|
||
background: #222;
|
||
color: #fff;
|
||
}
|
||
|
||
.container {
|
||
width: 800px;
|
||
margin: 40px auto;
|
||
background: #444;
|
||
padding: 20px;
|
||
border-radius: 10px;
|
||
}
|
||
|
||
/* 整体搜索栏 */
|
||
.search-bar {
|
||
display: flex;
|
||
border: 1px solid #ccc;
|
||
border-radius: 8px;
|
||
overflow: hidden;
|
||
}
|
||
|
||
/* 左侧下拉 */
|
||
.search-bar select {
|
||
border: none;
|
||
padding: 10px;
|
||
background: #eee;
|
||
}
|
||
|
||
/* 中间输入区域 */
|
||
.input-area {
|
||
flex: 1;
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
gap: 5px;
|
||
padding: 5px;
|
||
}
|
||
|
||
/* 输入框 */
|
||
.input-area input {
|
||
border: none;
|
||
outline: none;
|
||
flex: 1;
|
||
}
|
||
|
||
/* 右侧按钮 */
|
||
.search-bar button {
|
||
border: none;
|
||
padding: 0 20px;
|
||
background: #007bff;
|
||
color: white;
|
||
cursor: pointer;
|
||
}
|
||
|
||
/* tag chip */
|
||
.tag-chip {
|
||
background: #007bff;
|
||
color: white;
|
||
padding: 5px 10px;
|
||
border-radius: 20px;
|
||
display: flex;
|
||
align-items: center;
|
||
}
|
||
|
||
.tag-chip span {
|
||
margin-left: 5px;
|
||
cursor: pointer;
|
||
}
|
||
|
||
/* 下拉建议 */
|
||
.suggestions {
|
||
border: 1px solid #ccc;
|
||
border-top: none;
|
||
max-height: 150px;
|
||
overflow-y: auto;
|
||
}
|
||
|
||
.suggestion {
|
||
padding: 5px;
|
||
cursor: pointer;
|
||
}
|
||
|
||
.suggestion:hover {
|
||
background: #eee;
|
||
}
|
||
|
||
.item {
|
||
border-bottom: 1px solid #eee;
|
||
padding: 10px 0;
|
||
}
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<div class="container">
|
||
|
||
<!-- 搜索栏 -->
|
||
<div class="search-bar">
|
||
<select id="modeSelect" onchange="setMode(this.value)">
|
||
<option value="posts">Posts</option>
|
||
<option value="tags">Tags</option>
|
||
</select>
|
||
|
||
<div id="inputArea" class="input-area">
|
||
<input id="searchInput" placeholder="输入..." />
|
||
</div>
|
||
|
||
<button onclick="search()">搜索</button>
|
||
</div>
|
||
|
||
<div id="suggestions" class="suggestions"></div>
|
||
|
||
<div id="results"></div>
|
||
|
||
</div>
|
||
|
||
<script>
|
||
let mode = 'posts';
|
||
let selectedTags = [];
|
||
let debounceTimer = null;
|
||
|
||
/* =========================
|
||
切换模式
|
||
========================= */
|
||
function setMode(m) {
|
||
mode = m;
|
||
document.getElementById('suggestions').innerHTML = '';
|
||
document.getElementById('results').innerHTML = '';
|
||
}
|
||
|
||
/* =========================
|
||
输入监听(tag 模式)
|
||
========================= */
|
||
function bindInputEvent(input) {
|
||
input.addEventListener('input', (e) => {
|
||
if (mode !== 'tags') return;
|
||
|
||
const text = e.target.value.trim();
|
||
if (!text) {
|
||
document.getElementById('suggestions').innerHTML = '';
|
||
return;
|
||
}
|
||
|
||
clearTimeout(debounceTimer);
|
||
debounceTimer = setTimeout(() => {
|
||
fetch(`/api/search/tags/${text}`)
|
||
.then(res => res.json())
|
||
.then(data => {
|
||
// ⭐ 关键:过滤已选标签
|
||
const filtered = (data.posts || []).filter(tag =>
|
||
!selectedTags.some(t => t.id === tag.id)
|
||
);
|
||
renderSuggestions(filtered);
|
||
});
|
||
}, 300);
|
||
});
|
||
}
|
||
|
||
bindInputEvent(document.getElementById('searchInput'));
|
||
|
||
/* =========================
|
||
渲染建议
|
||
========================= */
|
||
function renderSuggestions(tags) {
|
||
const container = document.getElementById('suggestions');
|
||
container.innerHTML = '';
|
||
|
||
tags.forEach(tag => {
|
||
const div = document.createElement('div');
|
||
div.className = 'suggestion';
|
||
div.textContent = `${tag.tag} (${tag.count})`;
|
||
|
||
div.onclick = () => addTag(tag);
|
||
|
||
container.appendChild(div);
|
||
});
|
||
}
|
||
|
||
/* =========================
|
||
添加 tag
|
||
========================= */
|
||
function addTag(tag) {
|
||
selectedTags.push(tag);
|
||
renderTagChips();
|
||
|
||
document.getElementById('suggestions').innerHTML = '';
|
||
}
|
||
|
||
/* =========================
|
||
删除 tag
|
||
========================= */
|
||
function removeTag(id) {
|
||
selectedTags = selectedTags.filter(t => t.id !== id);
|
||
renderTagChips();
|
||
}
|
||
|
||
/* =========================
|
||
渲染 tag chips
|
||
========================= */
|
||
function renderTagChips() {
|
||
const container = document.getElementById('inputArea');
|
||
container.innerHTML = '';
|
||
|
||
selectedTags.forEach(tag => {
|
||
const chip = document.createElement('div');
|
||
chip.className = 'tag-chip';
|
||
chip.innerHTML = `
|
||
${tag.tag}
|
||
<span onclick="removeTag(${tag.id})">×</span>
|
||
`;
|
||
container.appendChild(chip);
|
||
});
|
||
|
||
const input = document.createElement('input');
|
||
input.id = 'searchInput';
|
||
input.placeholder = '输入...';
|
||
|
||
container.appendChild(input);
|
||
|
||
bindInputEvent(input);
|
||
}
|
||
|
||
/* =========================
|
||
搜索
|
||
========================= */
|
||
function search() {
|
||
const resultDiv = document.getElementById('results');
|
||
resultDiv.innerHTML = '加载中...';
|
||
|
||
if (mode === 'posts') {
|
||
const text = document.getElementById('searchInput').value;
|
||
|
||
fetch(`/api/search/posts/${text}`)
|
||
.then(r => r.json())
|
||
.then(data => renderPosts(data.posts || []));
|
||
|
||
} else {
|
||
if (!selectedTags.length) {
|
||
resultDiv.innerHTML = '请选择 tag';
|
||
return;
|
||
}
|
||
|
||
const ids = selectedTags.map(t => t.id).join(',');
|
||
|
||
fetch(`/api/tags/${ids}/artworks`)
|
||
.then(r => r.json())
|
||
.then(data => renderArtworks(data.artworks || []));
|
||
}
|
||
}
|
||
|
||
/* =========================
|
||
渲染 posts
|
||
========================= */
|
||
function renderPosts(posts) {
|
||
const div = document.getElementById('results');
|
||
div.innerHTML = '';
|
||
|
||
posts.forEach(p => {
|
||
div.innerHTML += `
|
||
<div class="item">
|
||
<strong>${p.title || ''}</strong><br/>
|
||
${p.text || ''}
|
||
</div>
|
||
`;
|
||
});
|
||
}
|
||
|
||
/* =========================
|
||
渲染 artworks
|
||
========================= */
|
||
function renderArtworks(list) {
|
||
const div = document.getElementById('results');
|
||
div.innerHTML = '';
|
||
|
||
list
|
||
.filter(a => !a.private)
|
||
.forEach(a => {
|
||
const tags = a.edges.tags.map(t => t.text).join(', ');
|
||
|
||
div.innerHTML += `
|
||
<div class="item">
|
||
<strong>ID: ${a.id}</strong><br/>
|
||
Tags: ${tags}
|
||
</div>
|
||
`;
|
||
});
|
||
}
|
||
</script>
|
||
|
||
</body>
|
||
</html>
|
||
{{ end }}
|