mirror of
https://github.com/saveweb/saveweb-search-backend.git
synced 2024-09-19 11:25:28 -07:00
270 lines
10 KiB
HTML
270 lines
10 KiB
HTML
|
<!DOCTYPE html>
|
|||
|
<html>
|
|||
|
<head>
|
|||
|
<meta charset="UTF-8">
|
|||
|
<title>丑搜</title>
|
|||
|
<style>
|
|||
|
#searchBar {
|
|||
|
width: 500px;
|
|||
|
height: 30px;
|
|||
|
margin-top: 20px;
|
|||
|
margin-bottom: 20px;
|
|||
|
padding: 5px;
|
|||
|
border: solid 1px #ccc;
|
|||
|
box-shadow: 0px 0px 5px #ccc;
|
|||
|
font-size: 16px;
|
|||
|
}
|
|||
|
.resultItem {
|
|||
|
margin-top: 10px;
|
|||
|
/* padding: 5px; */
|
|||
|
/* 居中 */
|
|||
|
margin-left: auto;
|
|||
|
margin-right: auto;
|
|||
|
border: solid 1px #ccc;
|
|||
|
box-shadow: 0px 0px 5px #ccc;
|
|||
|
background-color: #eeeeeecb;
|
|||
|
max-width: 1000px;
|
|||
|
}
|
|||
|
.resultTitle {
|
|||
|
font-size: 18px;
|
|||
|
font-weight: bold;
|
|||
|
}
|
|||
|
.resultInfo {
|
|||
|
font-size: 14px;
|
|||
|
}
|
|||
|
.resultContent {
|
|||
|
font-size: 14px;
|
|||
|
}
|
|||
|
#prevPage, #nextPage, #fullText {
|
|||
|
width: 100px;
|
|||
|
height: 30px;
|
|||
|
margin-top: 20px;
|
|||
|
margin-bottom: 20px;
|
|||
|
margin-right: auto;
|
|||
|
border: solid 1px #ccc;
|
|||
|
box-shadow: 0px 0px 5px #ccc;
|
|||
|
font-size: 16px;
|
|||
|
}
|
|||
|
#estimatedTotalHits, #page, #fullText, #prevPage, #nextPage {
|
|||
|
display: inline-block;
|
|||
|
/* font-size: 16px; */
|
|||
|
}
|
|||
|
#fullText {
|
|||
|
color: red;
|
|||
|
}
|
|||
|
#buttonGroup_ {
|
|||
|
margin-top: 20px;
|
|||
|
margin-bottom: 20px;
|
|||
|
margin-left: auto;
|
|||
|
margin-right: auto;
|
|||
|
max-width: 220px;
|
|||
|
}
|
|||
|
#prevPage_, #nextPage_ {
|
|||
|
width: 100px;
|
|||
|
height: 30px;
|
|||
|
margin-top: 20px;
|
|||
|
margin-bottom: 20px;
|
|||
|
margin-right: auto;
|
|||
|
border: solid 1px #ccc;
|
|||
|
box-shadow: 0px 0px 5px #ccc;
|
|||
|
font-size: 16px;
|
|||
|
}
|
|||
|
.uglyHighlight {
|
|||
|
color: rgb(255, 0, 153);
|
|||
|
}
|
|||
|
</style>
|
|||
|
</head>
|
|||
|
<body>
|
|||
|
<input type="text" id="searchBar" placeholder="请输入关键字">
|
|||
|
|
|||
|
<div id="estimatedTotalHits"></div> 第 <div id="page"></div> 页
|
|||
|
<button id="prevPage">上一页</button> <button id="nextPage">下一页</button>
|
|||
|
<button id="fullText">展开全文</button>
|
|||
|
|
|||
|
<div id="searchResults">随便打点字呗</div>
|
|||
|
<div id="buttonGroup_">
|
|||
|
<button id="prevPage_">上一页</button> <button id="nextPage_">下一页</button>
|
|||
|
</div>
|
|||
|
|
|||
|
<li>多么优雅的搜索界面!全文搜索,模糊搜索,简繁同搜,拼音,同音字。</li>
|
|||
|
<li>有近 13 万篇中文博客文章(包含少量播客),共收录有 1.5K+ 博客。</li>
|
|||
|
<p><strong>搜索结果以匹配度排序,没有时间权重,这样更容易找到真正有价值的文章</strong>。如果你需要更精准的搜索结果,请发动你的小脑瓜。可以用 ";作者" 来筛选同作者的文章。数据库月度更新,如果你需要实时信息,请使用其他优美的搜索引擎。希望你能在这十几万篇文章里找到有用的东西。</p>
|
|||
|
<br>
|
|||
|
<li>输入文字后如果没反应说明数据库炸了。</li>
|
|||
|
<li>什么,左右键能同时移动光标和翻页,是的,这是 feature 。翻页翻过了就啥都没有了,是的,这也是 feature !</li>
|
|||
|
<li>为什么下面的翻页按钮没用?这是 fea... 好吧,是 BUG,修了。</li>
|
|||
|
<li>为什么你认为本站需要搜索按钮?那太优雅了,你只管在框框里打字,剩下的浏览器来想办法。</li>
|
|||
|
<li>什么,你说本站真的太优雅了?请把您写好的 uGly CsS 直接发给我!</li>
|
|||
|
<p><del>展开全文还不太优雅,我看能不能塞个 MarkDown 渲染器。</del>不加了不加了,人脑不就是最好的 MarkDown 渲染器吗?</p>
|
|||
|
<p>如需添加收录,给我发消息 TG: @yzqzss / Email: yzqzss@othing.xyz </p>
|
|||
|
<script>
|
|||
|
// 获取搜索框、搜索结果、总量估计 元素
|
|||
|
const searchBar = document.getElementById('searchBar');
|
|||
|
const searchResults = document.getElementById('searchResults');
|
|||
|
const estimatedTotalHits = document.getElementById('estimatedTotalHits');
|
|||
|
const prevPage = document.getElementById('prevPage');
|
|||
|
const nextPage = document.getElementById('nextPage');
|
|||
|
const prevPage_ = document.getElementById('prevPage_');
|
|||
|
const nextPage_ = document.getElementById('nextPage_');
|
|||
|
const fullText = document.getElementById('fullText');
|
|||
|
|
|||
|
// 流控
|
|||
|
let dosearchCount = 0;
|
|||
|
setInterval(() => {
|
|||
|
dosearchCount = 0;
|
|||
|
}, 10 * 1000);
|
|||
|
|
|||
|
// 默认页码
|
|||
|
let page = 0;
|
|||
|
|
|||
|
// 监听上一页
|
|||
|
prevPage_.addEventListener('click', () => {
|
|||
|
if (page > 0) {
|
|||
|
page--;
|
|||
|
searchBar.dispatchEvent(new Event('dosearch'));
|
|||
|
}
|
|||
|
});
|
|||
|
prevPage.addEventListener('click', () => {
|
|||
|
if (page > 0) {
|
|||
|
page--;
|
|||
|
searchBar.dispatchEvent(new Event('dosearch'));
|
|||
|
}
|
|||
|
});
|
|||
|
// 监听左箭头
|
|||
|
document.addEventListener('keydown', (event) => {
|
|||
|
if (event.keyCode == 37) {
|
|||
|
if (page > 0) {
|
|||
|
page--;
|
|||
|
searchBar.dispatchEvent(new Event('dosearch'));
|
|||
|
}
|
|||
|
}
|
|||
|
});
|
|||
|
|
|||
|
// 监听下一页
|
|||
|
nextPage_.addEventListener('click', () => {
|
|||
|
page++;
|
|||
|
searchBar.dispatchEvent(new Event('dosearch'));
|
|||
|
});
|
|||
|
nextPage.addEventListener('click', () => {
|
|||
|
page++;
|
|||
|
searchBar.dispatchEvent(new Event('dosearch'));
|
|||
|
});
|
|||
|
// 监听右箭头
|
|||
|
document.addEventListener('keydown', (event) => {
|
|||
|
if (event.keyCode == 39) {
|
|||
|
page++;
|
|||
|
searchBar.dispatchEvent(new Event('dosearch'));
|
|||
|
}
|
|||
|
});
|
|||
|
|
|||
|
let fullTextFlag = false;
|
|||
|
|
|||
|
// 监听展开全文
|
|||
|
fullText.addEventListener('click', () => {
|
|||
|
if (fullTextFlag) {
|
|||
|
fullTextFlag = false;
|
|||
|
fullText.innerHTML = '展开全文';
|
|||
|
searchResults.innerHTML = '';
|
|||
|
searchBar.dispatchEvent(new Event('dosearch'));
|
|||
|
} else {
|
|||
|
fullTextFlag = true;
|
|||
|
fullText.innerHTML = '收起全文';
|
|||
|
searchResults.innerHTML = '';
|
|||
|
searchBar.dispatchEvent(new Event('dosearch'));
|
|||
|
}
|
|||
|
});
|
|||
|
|
|||
|
|
|||
|
// 监听搜索框的输入事件
|
|||
|
searchBar.addEventListener('input', () => {
|
|||
|
// 重置页码
|
|||
|
page = 0;
|
|||
|
// 重置全文
|
|||
|
fullTextFlag = false;
|
|||
|
fullText.innerHTML = '展开全文';
|
|||
|
// 更新页码
|
|||
|
document.getElementById('page').innerHTML = page + 1;
|
|||
|
|
|||
|
// 等待用户输入完毕后再搜索
|
|||
|
clearTimeout(window.searchTimer);
|
|||
|
window.searchTimer = setTimeout(() => {
|
|||
|
searchBar.dispatchEvent(new Event('dosearch'));
|
|||
|
}, 200);
|
|||
|
});
|
|||
|
// 监听搜索框的搜索事件
|
|||
|
searchBar.addEventListener('dosearch', () => {
|
|||
|
// 获取搜索关键字
|
|||
|
const query = searchBar.value.trim();
|
|||
|
|
|||
|
// 如果搜索关键字为空,则清空搜索结果并返回
|
|||
|
if (!query) {
|
|||
|
searchResults.innerHTML = '';
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
if (dosearchCount > 20) {
|
|||
|
searchResults.innerHTML = '搜索太频繁了,休息一下吧。';
|
|||
|
return;
|
|||
|
}
|
|||
|
dosearchCount++;
|
|||
|
|
|||
|
// 发送搜索请求
|
|||
|
// p 从 0 开始,h 代表是否返回高亮
|
|||
|
fetch('/api/search?q=' + encodeURIComponent(query) + '&p=' + page + '&f=' + fullTextFlag + '&h=' + true)
|
|||
|
.then(response => response.json())
|
|||
|
.then(data => {
|
|||
|
|
|||
|
// results.update({
|
|||
|
// 'hits': _results['hits'],
|
|||
|
// 'estimatedTotalHits': _results['estimatedTotalHits'],
|
|||
|
// })
|
|||
|
|
|||
|
|
|||
|
// 清空搜索结果
|
|||
|
searchResults.innerHTML = '';
|
|||
|
|
|||
|
// 添加估计的总命中数
|
|||
|
if (data.estimatedTotalHits == 1000) {
|
|||
|
estimatedTotalHits.innerHTML = `约 999+ 条结果`;
|
|||
|
} else {
|
|||
|
estimatedTotalHits.innerHTML = `约 ${data.estimatedTotalHits} 条结果`;
|
|||
|
}
|
|||
|
|
|||
|
// 更新页码
|
|||
|
document.getElementById('page').innerHTML = page + 1;
|
|||
|
|
|||
|
// 显示搜索结果
|
|||
|
data.hits.forEach(hit => {
|
|||
|
const resultItem = document.createElement('div');
|
|||
|
resultItem.classList.add('resultItem');
|
|||
|
const resultTitle = document.createElement('a');
|
|||
|
resultTitle.classList.add('resultTitle');
|
|||
|
resultTitle.innerHTML = hit.title.replace(/;/g, '');
|
|||
|
resultTitle.href = hit.link;
|
|||
|
|
|||
|
const resultInfo = document.createElement('div');
|
|||
|
resultInfo.classList.add('resultInfo');
|
|||
|
resultInfo.innerHTML = "by " + hit.author + ' at ' + new Date(hit.date*1000).toLocaleString() + '. 大概字数: ' + hit.content.length;
|
|||
|
|
|||
|
const resultContent = document.createElement('div');
|
|||
|
resultContent.classList.add('resultContent');
|
|||
|
if (fullTextFlag) {
|
|||
|
// 去掉连续两个以上的换行符(最多保留两个)
|
|||
|
hit.content = hit.content.replace(/\n{3,}/g, '\n\n');
|
|||
|
// 将 \n 替换为 <br>
|
|||
|
resultContent.innerHTML = hit.content.replace(/\n/g, '<br>');
|
|||
|
} else {
|
|||
|
resultContent.innerHTML = hit.content + '...';
|
|||
|
}
|
|||
|
|
|||
|
resultItem.appendChild(resultTitle);
|
|||
|
resultItem.appendChild(resultInfo);
|
|||
|
resultItem.appendChild(resultContent);
|
|||
|
searchResults.appendChild(resultItem);
|
|||
|
});
|
|||
|
})
|
|||
|
.catch(error => console.error(error));
|
|||
|
});
|
|||
|
</script>
|
|||
|
</body>
|
|||
|
</html>
|