|
|
|
@ -0,0 +1,395 @@
|
|
|
|
|
<template>
|
|
|
|
|
<div class="search-container">
|
|
|
|
|
<!-- 左侧栏 -->
|
|
|
|
|
<aside class="sidebar">
|
|
|
|
|
<section class="search-standards">
|
|
|
|
|
<ul>
|
|
|
|
|
<li
|
|
|
|
|
v-for="standard in standards"
|
|
|
|
|
:key="standard"
|
|
|
|
|
:class="{active: selectedStandard === standard}"
|
|
|
|
|
@click="selectStandard(standard)"
|
|
|
|
|
>{{ standard }}</li>
|
|
|
|
|
</ul>
|
|
|
|
|
</section>
|
|
|
|
|
|
|
|
|
|
<hr />
|
|
|
|
|
|
|
|
|
|
<section class="categories">
|
|
|
|
|
<h3>分类</h3>
|
|
|
|
|
|
|
|
|
|
<div class="category-group">
|
|
|
|
|
<div class="category-title">作业/资料:</div>
|
|
|
|
|
<ul>
|
|
|
|
|
<li
|
|
|
|
|
v-for="college in colleges"
|
|
|
|
|
:key="college"
|
|
|
|
|
@click="selectCategory(college)"
|
|
|
|
|
:class="{active: selectedCategory === college}"
|
|
|
|
|
>
|
|
|
|
|
{{ college }}
|
|
|
|
|
</li>
|
|
|
|
|
<li>(以下省略)</li>
|
|
|
|
|
</ul>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<hr />
|
|
|
|
|
|
|
|
|
|
<div class="category-group">
|
|
|
|
|
<div class="category-title">帖子:</div>
|
|
|
|
|
<ul>
|
|
|
|
|
<li
|
|
|
|
|
v-for="postType in postTypes"
|
|
|
|
|
:key="postType"
|
|
|
|
|
@click="selectCategory(postType)"
|
|
|
|
|
:class="{active: selectedCategory === postType}"
|
|
|
|
|
>
|
|
|
|
|
{{ postType }}
|
|
|
|
|
</li>
|
|
|
|
|
<li>(以下省略)</li>
|
|
|
|
|
</ul>
|
|
|
|
|
</div>
|
|
|
|
|
</section>
|
|
|
|
|
</aside>
|
|
|
|
|
|
|
|
|
|
<!-- 右侧栏 -->
|
|
|
|
|
<main class="search-results">
|
|
|
|
|
<div class="search-box">
|
|
|
|
|
<input
|
|
|
|
|
type="text"
|
|
|
|
|
v-model="searchQuery"
|
|
|
|
|
placeholder="请输入搜索关键字"
|
|
|
|
|
@keypress.enter="doSearch"
|
|
|
|
|
aria-label="搜索输入"
|
|
|
|
|
/>
|
|
|
|
|
<button @click="doSearch">🔍 搜索</button>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div class="results-list" v-if="pagedResults.length">
|
|
|
|
|
<div v-for="item in pagedResults" :key="item.id" class="result-item">
|
|
|
|
|
<div class="content">
|
|
|
|
|
<h4>{{ item.title }}</h4>
|
|
|
|
|
<p>{{ item.content }}</p>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="preview-box">
|
|
|
|
|
<!-- 这里你可用图片或其它预览,示例文本 -->
|
|
|
|
|
预览图/内容
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
<div v-else class="no-results">
|
|
|
|
|
暂无搜索结果
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div class="pagination" v-if="totalPages > 1">
|
|
|
|
|
<button @click="prevPage" :disabled="page === 1">上一页</button>
|
|
|
|
|
<span>第 {{ page }} 页 / 共 {{ totalPages }} 页</span>
|
|
|
|
|
<button @click="nextPage" :disabled="page === totalPages">下一页</button>
|
|
|
|
|
</div>
|
|
|
|
|
</main>
|
|
|
|
|
</div>
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
<script lang="ts" setup>
|
|
|
|
|
import { ref, computed, onMounted } from 'vue'
|
|
|
|
|
import { useRoute } from 'vue-router'
|
|
|
|
|
|
|
|
|
|
// 路由参数
|
|
|
|
|
const route = useRoute()
|
|
|
|
|
|
|
|
|
|
// 搜索标准
|
|
|
|
|
const standards = ['综合', '最新', '热度最高', '用户']
|
|
|
|
|
const selectedStandard = ref('综合')
|
|
|
|
|
|
|
|
|
|
// 分类选项
|
|
|
|
|
const colleges = [
|
|
|
|
|
'计算机学院',
|
|
|
|
|
'遥感学院',
|
|
|
|
|
'电子信息学院',
|
|
|
|
|
// 省略更多...
|
|
|
|
|
]
|
|
|
|
|
const postTypes = [
|
|
|
|
|
'二手集市',
|
|
|
|
|
'吐槽树洞',
|
|
|
|
|
'校内组局',
|
|
|
|
|
// 省略更多...
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
const selectedCategory = ref<string | null>(null)
|
|
|
|
|
|
|
|
|
|
// 搜索相关状态
|
|
|
|
|
const searchQuery = ref('')
|
|
|
|
|
const results = ref([
|
|
|
|
|
// 固定假数据示例(真实项目你要通过API替换)
|
|
|
|
|
{ id: 1, title: '帖子标题1', content: '帖子内容A' },
|
|
|
|
|
{ id: 2, title: '帖子标题2', content: '帖子内容B' },
|
|
|
|
|
{ id: 3, title: '帖子标题3', content: '帖子内容C' },
|
|
|
|
|
{ id: 4, title: '帖子标题4', content: '帖子内容D' },
|
|
|
|
|
{ id: 5, title: '帖子标题5', content: '帖子内容E' },
|
|
|
|
|
{ id: 6, title: '帖子标题6', content: '帖子内容F' },
|
|
|
|
|
])
|
|
|
|
|
|
|
|
|
|
// 分页
|
|
|
|
|
const page = ref(1)
|
|
|
|
|
const pageSize = 3
|
|
|
|
|
|
|
|
|
|
// 初始化搜索词,如果路由query带了query参数,则自动填充并搜索
|
|
|
|
|
onMounted(() => {
|
|
|
|
|
const queryParam = route.query.query
|
|
|
|
|
if (typeof queryParam === 'string' && queryParam.trim()) {
|
|
|
|
|
searchQuery.value = queryParam.trim()
|
|
|
|
|
doSearch()
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
// 计算过滤结果(这里简单模拟:按selectedCategory和searchQuery筛选)
|
|
|
|
|
const filteredResults = computed(() => {
|
|
|
|
|
let filtered = results.value
|
|
|
|
|
|
|
|
|
|
// 1. 分类过滤
|
|
|
|
|
if (selectedCategory.value) {
|
|
|
|
|
filtered = filtered.filter(item => item.title.includes(selectedCategory.value!))
|
|
|
|
|
}
|
|
|
|
|
// 2. 搜索关键词过滤(标题/content)
|
|
|
|
|
if (searchQuery.value) {
|
|
|
|
|
const q = searchQuery.value.toLowerCase()
|
|
|
|
|
filtered = filtered.filter(item =>
|
|
|
|
|
item.title.toLowerCase().includes(q) || item.content.toLowerCase().includes(q)
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
// 3. TODO: 按 selectedStandard 排序(综合,最新,热度最高,用户 等)
|
|
|
|
|
// 这里留空,可根据业务需求实现排序逻辑
|
|
|
|
|
|
|
|
|
|
return filtered
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
const totalPages = computed(() => Math.max(1, Math.ceil(filteredResults.value.length / pageSize)))
|
|
|
|
|
|
|
|
|
|
const pagedResults = computed(() => {
|
|
|
|
|
const start = (page.value - 1) * pageSize
|
|
|
|
|
return filteredResults.value.slice(start, start + pageSize)
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
// 行为方法
|
|
|
|
|
|
|
|
|
|
function selectStandard(std: string) {
|
|
|
|
|
selectedStandard.value = std
|
|
|
|
|
page.value = 1
|
|
|
|
|
doSearch()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function selectCategory(cat: string) {
|
|
|
|
|
selectedCategory.value = cat
|
|
|
|
|
page.value = 1
|
|
|
|
|
doSearch()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function doSearch() {
|
|
|
|
|
page.value = 1
|
|
|
|
|
// 实际项目里会去调用接口获取数据,这里用的是本地过滤
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function prevPage() {
|
|
|
|
|
if (page.value > 1) page.value--
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function nextPage() {
|
|
|
|
|
if (page.value < totalPages.value) page.value++
|
|
|
|
|
}
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
<style scoped>
|
|
|
|
|
.search-container {
|
|
|
|
|
width: 1800px;
|
|
|
|
|
height: 1000px;
|
|
|
|
|
margin: 20px auto;
|
|
|
|
|
display: flex;
|
|
|
|
|
gap: 24px;
|
|
|
|
|
padding: 24px;
|
|
|
|
|
background: linear-gradient(90deg, #f6e7f7, #e2e0f9);
|
|
|
|
|
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
|
|
|
|
font-size: 24px;
|
|
|
|
|
box-sizing: border-box;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* 左侧栏 */
|
|
|
|
|
.sidebar {
|
|
|
|
|
flex: 0 0 280px;
|
|
|
|
|
background: #fff;
|
|
|
|
|
padding: 24px;
|
|
|
|
|
border-radius: 8px;
|
|
|
|
|
box-shadow: 0 0 12px #ddd;
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
gap: 16px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.sidebar ul {
|
|
|
|
|
list-style: none;
|
|
|
|
|
padding: 0;
|
|
|
|
|
margin: 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.sidebar ul li {
|
|
|
|
|
margin: 10px 0;
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
user-select: none;
|
|
|
|
|
transition: color 0.2s;
|
|
|
|
|
font-size: 1.1em;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.sidebar ul li:hover {
|
|
|
|
|
color: #7a42f4;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.sidebar ul li.active {
|
|
|
|
|
font-weight: 700;
|
|
|
|
|
color: #581ce0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.categories h3 {
|
|
|
|
|
margin-bottom: 12px;
|
|
|
|
|
font-size: 1.3em;
|
|
|
|
|
font-weight: 700;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.category-group {
|
|
|
|
|
margin-bottom: 16px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.category-title {
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
margin-bottom: 8px;
|
|
|
|
|
font-size: 1.1em;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* 右侧栏 */
|
|
|
|
|
.search-results {
|
|
|
|
|
flex: 1;
|
|
|
|
|
background: #faf8ff;
|
|
|
|
|
padding: 32px;
|
|
|
|
|
border-radius: 8px;
|
|
|
|
|
box-shadow: 0 0 12px #ccc;
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
gap: 24px;
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.search-box {
|
|
|
|
|
display: flex;
|
|
|
|
|
gap: 12px;
|
|
|
|
|
align-items: center;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.search-box input {
|
|
|
|
|
flex: 1;
|
|
|
|
|
padding: 12px 16px;
|
|
|
|
|
font-size: 1.2em;
|
|
|
|
|
border: 1px solid #ccc;
|
|
|
|
|
border-radius: 6px;
|
|
|
|
|
outline: none;
|
|
|
|
|
transition: border-color 0.2s;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.search-box input:focus {
|
|
|
|
|
border-color: #765ce6;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.search-box button {
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
padding: 10px 18px;
|
|
|
|
|
font-size: 1.1em;
|
|
|
|
|
border: none;
|
|
|
|
|
background-color: #765ce6;
|
|
|
|
|
color: white;
|
|
|
|
|
border-radius: 6px;
|
|
|
|
|
user-select: none;
|
|
|
|
|
transition: background-color 0.2s;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.search-box button:hover {
|
|
|
|
|
background-color: #5a42b8;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.results-list {
|
|
|
|
|
flex-grow: 1;
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
gap: 16px;
|
|
|
|
|
overflow-y: auto;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.result-item {
|
|
|
|
|
display: flex;
|
|
|
|
|
justify-content: space-between;
|
|
|
|
|
padding: 16px;
|
|
|
|
|
background: white;
|
|
|
|
|
border: 1px solid #ccc;
|
|
|
|
|
border-radius: 8px;
|
|
|
|
|
box-sizing: border-box;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.content {
|
|
|
|
|
flex: 1;
|
|
|
|
|
margin-right: 16px;
|
|
|
|
|
line-height: 1.5em;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.content h4 {
|
|
|
|
|
margin: 0 0 8px 0;
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
font-size: 1.3em;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.preview-box {
|
|
|
|
|
width: 150px;
|
|
|
|
|
background: #ccc;
|
|
|
|
|
color: #444;
|
|
|
|
|
display: flex;
|
|
|
|
|
justify-content: center;
|
|
|
|
|
align-items: center;
|
|
|
|
|
border-radius: 6px;
|
|
|
|
|
user-select: none;
|
|
|
|
|
font-size: 1em;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.no-results {
|
|
|
|
|
font-size: 1.2em;
|
|
|
|
|
color: #999;
|
|
|
|
|
margin-top: 30px;
|
|
|
|
|
text-align: center;
|
|
|
|
|
flex-grow: 1;
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
justify-content: center;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.pagination {
|
|
|
|
|
display: flex;
|
|
|
|
|
justify-content: center;
|
|
|
|
|
align-items: center;
|
|
|
|
|
gap: 24px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.pagination button {
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
border: none;
|
|
|
|
|
background: #765ce6;
|
|
|
|
|
color: white;
|
|
|
|
|
padding: 10px 18px;
|
|
|
|
|
border-radius: 6px;
|
|
|
|
|
user-select: none;
|
|
|
|
|
font-size: 1.1em;
|
|
|
|
|
transition: background-color 0.2s;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.pagination button:disabled {
|
|
|
|
|
opacity: 0.5;
|
|
|
|
|
cursor: not-allowed;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.pagination button:hover:not(:disabled) {
|
|
|
|
|
background-color: #5a42b8;
|
|
|
|
|
}
|
|
|
|
|
</style>
|