|
|
@ -0,0 +1,268 @@
|
|
|
|
|
|
|
|
<template>
|
|
|
|
|
|
|
|
<div>
|
|
|
|
|
|
|
|
<!-- 搜索区域 -->
|
|
|
|
|
|
|
|
<div v-if="!showResults" class="search-component">
|
|
|
|
|
|
|
|
<input type="text" v-model="searchTerm" placeholder="请输入搜索词" />
|
|
|
|
|
|
|
|
<button @click="performSearch">搜索</button>
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 搜索结果展示区域 -->
|
|
|
|
|
|
|
|
<div v-if="showResults" class="search-results-page">
|
|
|
|
|
|
|
|
<!-- 搜索词展示 -->
|
|
|
|
|
|
|
|
<div class="search-term-display">
|
|
|
|
|
|
|
|
<h2>搜索结果:“{{ searchTerm }}”</h2>
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 排序与筛选区域 -->
|
|
|
|
|
|
|
|
<div class="sort-filter-area">
|
|
|
|
|
|
|
|
<!-- 排序方式选择 -->
|
|
|
|
|
|
|
|
<div class="sort-by">
|
|
|
|
|
|
|
|
<label for="sort-select">排序方式:</label>
|
|
|
|
|
|
|
|
<select id="sort-select" v-model="sortBy">
|
|
|
|
|
|
|
|
<option value="relevance">相关性</option>
|
|
|
|
|
|
|
|
<option value="publishDate-desc">发布日期(最新)</option>
|
|
|
|
|
|
|
|
<option value="publishDate-asc">发布日期(最旧)</option>
|
|
|
|
|
|
|
|
<option value="popularity">热度</option>
|
|
|
|
|
|
|
|
</select>
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 筛选条件选择 -->
|
|
|
|
|
|
|
|
<div class="filter-by">
|
|
|
|
|
|
|
|
<label for="category-select">文章类别:</label>
|
|
|
|
|
|
|
|
<select id="category-select" v-model="filterConditions.category">
|
|
|
|
|
|
|
|
<option value="">全部</option>
|
|
|
|
|
|
|
|
<option value="food">美食</option>
|
|
|
|
|
|
|
|
<option value="travel">旅游</option>
|
|
|
|
|
|
|
|
<option value="technology">科技</option>
|
|
|
|
|
|
|
|
</select>
|
|
|
|
|
|
|
|
<label for="time-range-select">时间范围:</label>
|
|
|
|
|
|
|
|
<select id="time-range-select" v-model="filterConditions.timeRange">
|
|
|
|
|
|
|
|
<option value="">全部</option>
|
|
|
|
|
|
|
|
<option value="this-week">本周</option>
|
|
|
|
|
|
|
|
<option value="this-month">本月</option>
|
|
|
|
|
|
|
|
<option value="this-year">本年内</option>
|
|
|
|
|
|
|
|
</select>
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 应用筛选与排序按钮 -->
|
|
|
|
|
|
|
|
<button @click="applyFilter">应用筛选</button>
|
|
|
|
|
|
|
|
<button @click="sortResults">排序</button>
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 搜索结果列表展示 -->
|
|
|
|
|
|
|
|
<div class="result-list-area">
|
|
|
|
|
|
|
|
<ul>
|
|
|
|
|
|
|
|
<li v-for="(result, index) in searchResults" :key="index">
|
|
|
|
|
|
|
|
<!-- 突出显示搜索词 -->
|
|
|
|
|
|
|
|
<h3 v-html="result.title | highlight(searchTerm)"></h3>
|
|
|
|
|
|
|
|
<p v-html="result.summary | highlight(searchTerm)"></p>
|
|
|
|
|
|
|
|
<p>发布日期:{{ result.publishDate }}</p>
|
|
|
|
|
|
|
|
</li>
|
|
|
|
|
|
|
|
</ul>
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 最下面一排的导航按钮 -->
|
|
|
|
|
|
|
|
<div class="bottom-navigation">
|
|
|
|
|
|
|
|
<button @click="goToPreviousPage" :disabled="currentPage === 1">上一页</button>
|
|
|
|
|
|
|
|
<span>第{{ currentPage }}页 / 共{{ totalPages }}页</span>
|
|
|
|
|
|
|
|
<button @click="goToNextPage" :disabled="currentPage === totalPages">下一页</button>
|
|
|
|
|
|
|
|
<button @click="goBackToSearch">返回搜索</button>
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<script>
|
|
|
|
|
|
|
|
import axios from 'axios';
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
export default {
|
|
|
|
|
|
|
|
name: 'BlogSearch',
|
|
|
|
|
|
|
|
data() {
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
|
|
|
searchTerm: '',
|
|
|
|
|
|
|
|
showResults: false,
|
|
|
|
|
|
|
|
searchResults: [],
|
|
|
|
|
|
|
|
sortBy: 'relevance',
|
|
|
|
|
|
|
|
filterConditions: {
|
|
|
|
|
|
|
|
category: '',
|
|
|
|
|
|
|
|
timeRange: ''
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
currentPage: 1,
|
|
|
|
|
|
|
|
totalPages: 1,
|
|
|
|
|
|
|
|
resultsPerPage: 10
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
filters: {
|
|
|
|
|
|
|
|
highlight: function (text, searchTerm) {
|
|
|
|
|
|
|
|
if (!searchTerm) return text;
|
|
|
|
|
|
|
|
const re = new RegExp(searchTerm, 'gi');
|
|
|
|
|
|
|
|
return text.replace(re, '<span style="color:red;">$&</span>');
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
methods: {
|
|
|
|
|
|
|
|
performSearch() {
|
|
|
|
|
|
|
|
// 简单验证搜索词是否为空
|
|
|
|
|
|
|
|
if (!this.searchTerm.trim()) {
|
|
|
|
|
|
|
|
alert('请输入有效的搜索词');
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 设置显示结果为true,以便展示搜索结果区域
|
|
|
|
|
|
|
|
this.showResults = true;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 获取搜索结果
|
|
|
|
|
|
|
|
this.getSearchResults();
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
async getSearchResults() {
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
|
|
// 根据当前页码、排序方式和筛选条件发送请求获取搜索结果
|
|
|
|
|
|
|
|
const response = await axios.get('/api/search', {
|
|
|
|
|
|
|
|
params: {
|
|
|
|
|
|
|
|
page: this.currentPage,
|
|
|
|
|
|
|
|
sortBy: this.sortBy,
|
|
|
|
|
|
|
|
category: this.filterConditions.category,
|
|
|
|
|
|
|
|
timeRange: this.filterConditions.timeRange,
|
|
|
|
|
|
|
|
searchTerm: this.searchTerm
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
this.searchResults = response.data.results;
|
|
|
|
|
|
|
|
this.totalPages = Math.ceil(response.data.totalCount / this.resultsPerPage);
|
|
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
|
|
console.error('获取搜索结果出错:', error);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
sortResults() {
|
|
|
|
|
|
|
|
// 根据选择的排序方式对搜索结果进行排序
|
|
|
|
|
|
|
|
if (this.sortBy === 'publishDate-desc') {
|
|
|
|
|
|
|
|
this.searchResults.sort((a, b) => new Date(b.publishDate) - new Date(a.publishDate));
|
|
|
|
|
|
|
|
} else if (this.sortBy === 'publishDate-asc') {
|
|
|
|
|
|
|
|
this.searchResults.sort((a, b) => new Date(a.publishDate) - new Date(b.publishDate));
|
|
|
|
|
|
|
|
} else if (this.sortBy === 'popularity') {
|
|
|
|
|
|
|
|
// 假设文章对象有一个popularity属性用于表示热度
|
|
|
|
|
|
|
|
this.searchResults.sort((a, b) => b.popularity - a.popularity);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
applyFilter() {
|
|
|
|
|
|
|
|
// 应用筛选条件后重新获取搜索结果并重置当前页码为1
|
|
|
|
|
|
|
|
this.currentPage = 1;
|
|
|
|
|
|
|
|
this.getSearchResults();
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
goToPreviousPage() {
|
|
|
|
|
|
|
|
if (this.currentPage > 1) {
|
|
|
|
|
|
|
|
this.currentPage--;
|
|
|
|
|
|
|
|
this.getSearchResults();
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
goToNextPage() {
|
|
|
|
|
|
|
|
if (this.currentPage < this.totalPages) {
|
|
|
|
|
|
|
|
this.currentPage++;
|
|
|
|
|
|
|
|
this.getSearchResults();
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
goBackToSearch() {
|
|
|
|
|
|
|
|
// 重置相关数据并隐藏搜索结果区域,返回搜索状态
|
|
|
|
|
|
|
|
this.searchTerm = '';
|
|
|
|
|
|
|
|
this.showResults = false;
|
|
|
|
|
|
|
|
this.sortBy = 'relevance';
|
|
|
|
|
|
|
|
this.filterConditions = {
|
|
|
|
|
|
|
|
category: '',
|
|
|
|
|
|
|
|
timeRange: ''
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
this.currentPage = 1;
|
|
|
|
|
|
|
|
this.totalPages = 1;
|
|
|
|
|
|
|
|
this.searchResults = [];
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
mounted() {
|
|
|
|
|
|
|
|
// 如果是通过路由参数进入此页面且有搜索词参数,自动执行搜索操作
|
|
|
|
|
|
|
|
if (this.$route.params.searchTerm) {
|
|
|
|
|
|
|
|
this.searchTerm = this.$route.params.searchTerm;
|
|
|
|
|
|
|
|
this.showResults = true;
|
|
|
|
|
|
|
|
this.getSearchResults();
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<style>
|
|
|
|
|
|
|
|
.search-component {
|
|
|
|
|
|
|
|
margin: 20px;
|
|
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
|
|
justify-content: center;
|
|
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
input[type="text"] {
|
|
|
|
|
|
|
|
padding: 10px;
|
|
|
|
|
|
|
|
width: 300px;
|
|
|
|
|
|
|
|
border: 1px solid #ccc;
|
|
|
|
|
|
|
|
border-radius: 5px;
|
|
|
|
|
|
|
|
margin-right: 10px;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
button {
|
|
|
|
|
|
|
|
padding: 10px 20px;
|
|
|
|
|
|
|
|
background-color: #007BFF;
|
|
|
|
|
|
|
|
color: white;
|
|
|
|
|
|
|
|
border: none;
|
|
|
|
|
|
|
|
border-radius: 5px;
|
|
|
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
.search-results-page {
|
|
|
|
|
|
|
|
font-family: Arial, sans-serif;
|
|
|
|
|
|
|
|
padding: 20px;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
.search-term-display {
|
|
|
|
|
|
|
|
margin-bottom: 10px;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
.sort-filter-area {
|
|
|
|
|
|
|
|
margin-bottom: 20px;
|
|
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
|
|
flex-wrap: wrap;
|
|
|
|
|
|
|
|
justify-content: space-between;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
.sort-by,
|
|
|
|
|
|
|
|
.filter-by {
|
|
|
|
|
|
|
|
margin-bottom: 10px;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
.sort-by label,
|
|
|
|
|
|
|
|
.filter-by label {
|
|
|
|
|
|
|
|
margin-right: 10px;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
.result-list-area ul {
|
|
|
|
|
|
|
|
list-style: none;
|
|
|
|
|
|
|
|
padding: 0;
|
|
|
|
|
|
|
|
margin: 0;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
.result-list-area li {
|
|
|
|
|
|
|
|
border-bottom: 1px solid #ccc;
|
|
|
|
|
|
|
|
padding-bottom: 10px;
|
|
|
|
|
|
|
|
margin-bottom: 10px;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
.pagination-area {
|
|
|
|
|
|
|
|
margin-top: 20px;
|
|
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
|
|
justify-content: space-between;
|
|
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
.back-button-area {
|
|
|
|
|
|
|
|
margin-top: 20px;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
.bottom-navigation {
|
|
|
|
|
|
|
|
margin-top: 20px;
|
|
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
|
|
justify-content: space-between;
|
|
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
|
|
border-top: 1px solid #ccc;
|
|
|
|
|
|
|
|
padding-top: 20px;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
</style>
|