You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
unilife/Front/vue-unilife/src/views/SearchResult.vue

396 lines
8.4 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<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>