搜索文章
前面 Django 开发的部分已经实现了搜索接口,本章就基于此将其对应的搜索功能补充完整。
准备工作
为了让用户在博客的所有页面都能找到搜索框,将它放到页眉里是个不错的注意。
修改 BlogHeader.vue ,将搜索框的外观放进去:
<!-- frontend/src/components/BlogHeader.vue -->
<template>
<div id="header">
<div class="grid">
<div></div>
<h1>My Drf-Vue Blog</h1>
<div class="search">
<form>
<input type="text" placeholder="输入搜索内容...">
<button></button>
</form>
</div>
</div>
<hr>
</div>
</template>
<script>
export default {
name: 'BlogHeader'
}
</script>
<style scoped>
#header {
text-align: center;
margin-top: 20px;
}
.grid {
display: grid;
grid-template-columns: 1fr 4fr 1fr;
}
.search {
padding-top: 22px;
}
/* css 来源:https://blog.csdn.net/qq_39198420/article/details/77973339*/
* {
box-sizing: border-box;
}
form {
position: relative;
width: 200px;
margin: 0 auto;
}
input, button {
border: none;
outline: none;
}
input {
width: 100%;
height: 35px;
padding-left: 13px;
padding-right: 46px;
}
button {
height: 35px;
width: 35px;
cursor: pointer;
position: absolute;
}
.search input {
border: 2px solid gray;
border-radius: 5px;
background: transparent;
top: 0;
right: 0;
}
.search button {
background: gray;
border-radius: 0 5px 5px 0;
width: 45px;
top: 0;
right: 0;
}
.search button:before {
content: "搜索";
font-size: 13px;
color: white;
}
</style>
代码大部分是在定义搜索框的外观,有兴趣的同学自行研究。
现在你的博客标题的右边应该就出现一个像模像样的搜索框了(无功能)。
接下来开始正式编写搜索的逻辑。
编程式导航
为了让搜索框发挥功能,继续修改 BlogHeader.vue:
<!-- frontend/src/components/BlogHeader.vue -->
<template>
...
<div class="search">
<form>
<input v-model="searchText" type="text" placeholder="输入搜索内容...">
<button v-on:click.prevent="searchArticles"></button>
</form>
</div>
...
</template>
<script>
export default {
name: 'BlogHeader',
data: function () {
return {
searchText: ''
}
},
methods: {
searchArticles() {
const text = this.searchText.trim();
if (text.charAt(0) !== '') {
this.$router.push({name: 'Home', query: { search: text }})
}
}
}
}
</script>
...
前面章节我们用 <router-link> 标签实现了路由跳转。在必要时候路由跳转也可以通过脚本来动态实现 ,也就是上面代码的 this.$router.push(...) 了。注意 this.$route 和 this.$router ,前者代表路径对象,后者代表路由器对象。
总之,点击按钮触发 searchArticles() ,然后此方法将 searchText 作为参数跳转到新的路径。
正餐
页面跳转实现了,但是因为前面章节把 get_article_data() 方法中的 url 写死为 '/api/article' 了,所以跳转之后还不能够根据路径的中 search 参数展示筛选后的数据。因此要换个战场 ,在 ArticleList.vue 里进行修改(主要是 Javascript 部分)。
旧的翻页 <router-link> 仅考虑了路径参数中的 page 值。为了在翻页后取得包括 page 和 search 的正确路径,新写一个方法 get_path():
<!-- frontend/src/components/ArticleList.vue -->
...
<script>
...
export default {
...
methods: {
...
get_path: function (direction) {
let url = '';
try {
switch (direction) {
case 'next':
if (this.info.next !== undefined) {
url += (new URL(this.info.next)).search
}
break;
case 'previous':
if (this.info.previous !== undefined) {
url += (new URL(this.info.previous)).search
}
break;
}
}
catch { return url }
return url
}
},
...
}
</script>
...
如果下一页的路径存在,那么则返回其带参数的路径,否则就返回无任何参数的首页路径。
有了 get_path() 获取到路径后,还需要将路径用到请求数据的接口里。
修改 get_article_data() 方法,如下面这样:
<!-- frontend/src/components/ArticleList.vue -->
...
<script>
...
export default {
...
methods: {
...
get_article_data: function () {
let url = '/api/article';
let params = new URLSearchParams();
// 注意 appendIfExists 方法是原生没有的
// 原生只有 append 方法,但此方法不能判断值是否存在
params.appendIfExists('page', this.$route.query.page);
params.appendIfExists('search', this.$route.query.search);
const paramsString = params.toString();
if (paramsString.charAt(0) !== '') {
url += '/?' + paramsString
}
axios
.get(url)
.then(response => (this.info = response.data))
}
},
...
}
</script>
...
这里的代码抛弃了之前用的字符串拼接的方式,改为专门用于处理路径参数的 URLSearchParams() 对象。为了将路径中已有的参数添加到 URLSearchParams() 中,可以用其本身的 append() 方法,但此方法不能判断值是否存在 ,从而获得类似 http://localhost:8080/?page=undefined 这种错误的路径。
解决方法可以在 methods 里写一个 appendIfExists() 方法,调用它来排除错误路径。还有一种方法是由于 JavaScript 是基于原型链的语言,因此可以通过原型链将此方法添加到已有对象中(包括内置原生对象),以扩展此对象的功能。
具体实施方法就是在 main.js 中写入:
// frontend/src/main.js
import ...
URLSearchParams.prototype.appendIfExists = function (key, value) {
if (value !== null && value !== undefined) {
this.append(key, value)
}
};
createApp(App)...;
因为 main.js 在 Vue 初始化时必然会执行,如此一来URLSearchParams 对象就有了这个 appendIfExists() 了。
把这些脚本写好后,就可以修改路由的模板了:
<!-- frontend/src/components/ArticleList.vue -->
<template>
...
<div id="paginator">
<span v-if="...">
<router-link :to="get_path('previous')">
Prev
</router-link>
</span>
<span class="...e">
...
</span>
<span v-if="...">
<router-link :to="get_path('next')">
Next
</router-link>
</span>
</div>
</template>
...
:to 是 v-bind:to 的简写,意思是“将 to 属性和 get_path(...) 的返回值保持一致”。如果不需要这种响应式行为,也可以 to="/abc" 这样直接赋值给属性。
**这就搞定了。**随便搜索点东西看看效果:

翻页试试,看看路径和文章数据的变化,应该都是正常工作的。
Last modified: 06 January 2025