最近在做法院的案件检索系统,需求中有一个需要做搜索下拉框可以提供历史高频搜索记录,并且可以根据输入自动匹配,就是像浏览器的搜索栏一样的效果。
如图所示,根据输入的关键字自动匹配高频搜索记录,并按照搜索次数进行排序。
1.前段Vue部分:
<el-row style="padding-top: 10px;">
<el-col style="width: 85%;">
<el-autocomplete
popper-class="my-autocomplete"
class="inline-input"
v-model="keywords"
:fetch-suggestions="querySearch"
placeholder="请输入内容"
@select="handleSelect"
style="width: 100%;margin: 0px;padding: 0px;"
id="1">
<template slot-scope="{ item }">
<div>
<div class="val">{{ item.value }}div>
<div class="addr">{{ item.num }}div>
div>
template>
el-autocomplete>
el-col>
<el-col style="width: 15%; padding-left: 5px">
<el-button type="primary" icon="el-icon-search" @click="searchAl">搜索el-button>
el-col>
el-row>
采用element-ui中的el-autocomplete组件,这个组件就是带搜索建议的输入框, v-model="keywords"用来接收输入的值, :fetch-suggestions="querySearch"用来提供搜索建议,template用来自定义下拉框的模板。
对应的css代码:
.val {
width: 95%;
/*background-color: red ;*/
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
float: left;
}
.addr {
font-size: 12px;
color: #b4b4b4;
float: right;
}
这是template中的样式,其他的样式都用style写在html中了。
对应的js代码:
export default{
data() {
return {
restaurants: [],
keywords: '',
};
},
methods:{
querySearch(queryString, cb) {
var restaurants = this.restaurants;
var results = queryString ? restaurants.filter(this.createFilter(queryString)) : restaurants;
// 调用 callback 返回建议列表的数据
cb(results);
},
createFilter(queryString) {
return (restaurant) => {
return (restaurant.value.toLowerCase().indexOf(queryString.toLowerCase()) === 0);
};
},
handleSelect(item) {
console.log(item);
},
loadAll() {
this.$axios.get('/bd/read').then(res => {
console.log(res.data)
this.restaurants = res.data
})
},
},
mounted() {
this.loadAll();
},
}
上面代码初始化的时候会返回restaurants和keywords,都是为空的,restaurants需要通过loadAll函数从后端获取数据,并将值赋值给restaurants,mounted()函数会在页面加载时就会执行,把loadAll放在这个函数中,就可以打开页面时restaurants就可以获取到数据了。当点击输入框时会触发querySearch函数,通过过滤后调用 callback 返回建议列表的数据。
那么词频是怎么计算的呢?词频的数据格式如下:
[{"value":"土地","num":7},{"value":"行政","num":4},{"value":"案件","num":3}]
简单来说就是当点击搜索按钮时,对应的num增加,然后再更新到后台持久保存。
还有一点就是如何对数据根据num排序,代码如下:
//搜索高词频统计
searchSuggest() {
if (this.keywords != '' && this.keywords != null) {
var l = this.restaurants.length
if (l > 0) {
var b = false;
for (let a = 0; a < l; a++) {
var i = this.restaurants[a].value == this.keywords;
if (i) {
this.restaurants[a].num += 1;
b = true;
}
}
if (!b) {
console.log(this.keywords)
this.restaurants.push({"value": this.keywords, "num": 1});
}
var str = JSON.stringify(this.restaurants);
// str = "%5B"+str.substring(1,str.length-1)+"%5D"
console.log(str);
var parm = qs.stringify({
data:str,
});
this.$axios.post("/bd/save",parm).then(res=>{
if(res.data==1){
console.log("成功");
}
})
// console.log(str);
var objectArraySort = function (keyName) {
return function (objectN, objectM) {
var valueN = objectN[keyName]
var valueM = objectM[keyName]
if (valueN < valueM) return 1
else if (valueN > valueM) return -1
else return 0
}
}
this.restaurants.sort(objectArraySort("num"));
}
}
},
上面代码的逻辑如下:当搜索输入栏不为空,并且已经获得高频词数据(l>0),这时如果搜索栏的词在高频词库中,那么将它的num加一,如果不在高频词库中,将该词和num=1加入到高频词库中。通过push方法加入到列表的末尾。
var str = JSON.stringify(this.restaurants);
console.log(str);
var parm = qs.stringify({
data:str,
});
this.$axios.post("/bd/save",parm).then(res=>{
if(res.data==1){
console.log("成功");
}
})
上面这段代码就是通过post方法将数据传送到后端服务器保存起来。注意post传参数的方法。尤其是传送列表数据。
var objectArraySort = function (keyName) {
return function (objectN, objectM) {
var valueN = objectN[keyName]
var valueM = objectM[keyName]
if (valueN < valueM) return 1
else if (valueN > valueM) return -1
else return 0
}
}
this.restaurants.sort(objectArraySort("num"));
上面这段代码用来根据列表中每个对象字典中的num数值进行从大到小的排序,这样搜索建议栏就可以按照词频进行显示了。
当点击搜索按钮时,不止是实现搜索功能,还要调用searchSuggest方法,把搜索词保存。
searchAl() {
var q = this.keywords
if (q == '') {
alert("搜索内容不能为空")
this.tableLoading = false;
return;
}
this.searchSuggest();
this.$router.push({path: '/anli', query: {}})//实现页面跳转到搜索结果页面的。
},
2.后端springboot代码:
package com.nju.software.search.Controller;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;
import java.io.*;
import java.util.List;
@Controller
public class SearchController {
@ResponseBody
@RequestMapping("/save")
public String saveSearch( String data){
BufferedWriter writer = null;
File file = new File("./data.json");
//如果文件不存在,则新建一个
if(!file.exists()){
try {
file.createNewFile();
}catch (IOException e) {
e.printStackTrace();
}
}
//写入
try {
writer = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file,false), "UTF-8"));
writer.write(String.valueOf(data));
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
if(writer != null){
writer.close();
System.out.println("文件写入成功!");
return "1";
}
} catch (IOException e) {
e.printStackTrace();
}
}
System.out.println("文件写入失败!");
return "0";
}
@ResponseBody
@RequestMapping("/read")
public String read(){
try {
BufferedReader in = new BufferedReader(new FileReader("./data.json"));
String str;
while ((str = in.readLine()) != null) {
// System.out.println(str);
return str;
}
} catch (IOException e) {
}
return "";
}
}
后端代码为了简单,没有使用数据库,而是将数据以json文件存储在本地,主要两个方法,一个保存数据,一个读取数据,过于简单,不做详细解释了。