参考资料:
【Vue.js】基于vue的实时搜索,在结果中高亮显示关键词 https://www.cnblogs.com/pengshengguang/p/8059190.html
JS不区分大小写匹配字符串高亮模拟浏览器Ctrl+F https://www.jianshu.com/p/936029d3b9a6
js转义和反转义html https://www.cnblogs.com/daysme/p/7100553.html
用Javascript(js)进行HTML转义工具(处理特殊字符显示)https://blog.csdn.net/hj7jay/article/details/51280405
axios取消接口请求 https://www.jianshu.com/p/22b49e6ad819
<template>
<div class="awc-search">
<div class="awcs-wrap">
<div class="awcs-shade">div>
<div class="awcs-inner">
<input
type="text"
placeholder="作品名称/艺术院校"
class="awcsi-input"
v-model="searchVal"
@keyup.enter="searchSkip()"
@keyup.delete="searchDel()"
@input="searchInput()"
@click="searchInput()"
@keyup.up="upKey()"
@keyup.down="downKey()"
@blur="searchBlur()"
/>
<div class="awcsi-icon-wrap" @click="searchSkip()">
<i class="awcsi-icon">i>
div>
<div class="search-list" v-if="sugShow">
<div
class="sl-item"
v-for="(val,index) in sugList"
:key="index"
:checkVal="val.value"
v-html="val.span"
@click="itemClick(val.value)"
@mouseover="sugOver($event,index)"
@mouseout="sugOut($event,index)"
>div>
div>
div>
div>
div>
template>
<script>
//这里可以导入其他文件(比如:组件,工具js,第三方插件js,json文件,图片文件等等)
//例如:import 《组件名称》 from '《组件路径》';
// 项目api axios请求
import { Api } from "@/config/api";
import axios from "@/config/axios.js";
import {
encodeHtml,
decodeHtml,
html_encode,
html_decode
} from "@/config/js-string";
import { setTimeout } from "timers";
export default {
//import引入的组件需要注入到对象中才能使用
components: {},
data() {
//这里存放数据
return {
searchVal: "",
// 搜索建议列表
sugShow: false,
sugList: [],
sugIndex: -1,
sugTime: null,
srfFlag: true, // 输入法打开关闭状态
// cancel:null, // 取消上一次的网络请求
blurOrclick: true, // 解决点击列表选值和离开事件冲突
isIeAxios:true, // 解决ie通过js改变input val值触发oninput方法造成分类页面补全建议显示的问题
};
},
// 传入的类型
props: ["searchType"],
//监听属性 类似于data概念
computed: {},
//监控data中的数据变化
watch: {
searchVal() {
const that = this;
// console.log(encodeHtml('沉睡<一>'),'encodeHtml'); // 沉睡<一> encodeHtml
// console.log(decodeHtml('沉睡<一>'),'decodeHtml'); // 沉睡<一> decodeHtml
// console.log(html_encode('沉睡<一>'),'html_encode'); // 沉睡<一> html_encode
// console.log(html_decode('沉睡<一>'),'html_decode'); // 沉睡<一> html_decode
// 如果存在转义符 就进行转义
that.searchVal = html_decode(that.searchVal);
// if( that.searchVal == '' ){
// if (that.searchType == "artworkclassify") {
// console.log('我要提交了',that.searchType,that.searchVal)
// that.$emit("searchBackVal",'');
// that.sugShow = false;
// }
// }
}
},
//方法集合
methods: {
// 搜索请求
searchSkip() {
const that = this;
console.log(that.searchVal, that.searchType);
if (!that.searchVal) {
this.$message.closeAll();
that.$message.error("您尚未输入任何搜索条件");
return false;
}
that.clearData();
// 如果已经是分类页面 则不刷新路由 传值出去
if (that.searchType == "artworkclassify") {
that.$emit("searchBackVal", that.searchVal);
} else {
that.$emit("searchBackVal", that.searchVal);
that.$router.push({
name: "artworkclassify",
params: {
searchType: that.searchType,
searchVal: that.searchVal
}
});
that.searchVal = "";
}
},
// 清空状态
clearData() {
const that = this;
that.sugList = [];
that.sugShow = false;
that.sugIndex = -1;
},
// 删除按钮
searchDel() {
const that = this;
console.log("我删除了", that.searchType, that.searchVal);
setTimeout(() => {
if (that.searchVal == "") {
if (that.searchType == "artworkclassify") {
console.log("我要提交了", that.searchType, that.searchVal);
that.$emit("searchBackVal", "-asjsaljkfjsla");
that.sugShow = false;
}
}
}, 100);
},
// 鼠标离开输入框
searchBlur() {
const that = this;
setTimeout(() => {
if (that.blurOrclick) {
setTimeout(() => {
that.$emit("searchBackVal", that.searchVal);
that.sugShow = false;
}, 300);
}
}, 0);
},
// 鼠标进入输入框 搜索建议
searchInput: function(e) {
var that = this;
// 如果是ie进入分类页面 直接打断第一次操作
if( !that.isIeAxios ){
that.isIeAxios = true;
return;
}
that.itemClassDel();
if (that.searchVal.trim() == "") {
that.clearData();
return;
}
// 设置搜索取消请求动画
let searchApiConfig = {
headers: {
loadingFalse: true
}
};
clearTimeout(that.sugTime);
that.sugTime = setTimeout(() => {
if (that.srfFlag) {
if (that.cancel) {
// 取消上一次的异步请求
that.cancel("canceled by user");
}
let CancelToken = axios.CancelToken;
that.axios
.get(
`/artwork/search_suggest/?keywords=${encodeURI(that.searchVal)}`,
searchApiConfig,
{
cancelToken: new CancelToken(function executor(c) {
self.cancel = c;
// 这个参数 c 就是CancelToken构造函数里面自带的取消请求的函数,这里把该函数当参数用
})
}
)
.then(res => {
if (res.data.code == 2001) {
that.sugList = [];
that.sugIndex = -1;
// that.sugList = res.data.data.data_list;
that.sugList = that.spanColor(
that.searchVal,
res.data.data.data_list
);
that.sugShow = true;
} else {
that.clearData();
}
})
.catch(err => {
that.clearData();
});
}
}, 0);
},
// 清除item上面的样式
itemClassDel() {
const that = this;
let objList = document.querySelectorAll(".sl-item");
for (let i = 0; i < objList.length; i++) {
objList[i].classList.remove("sl-active");
objList[i].classList.remove("overitem");
}
},
// 搜索值点击
itemClick(val) {
const that = this;
that.searchVal = val;
that.blurOrclick = false;
setTimeout(() => {
that.searchSkip();
that.blurOrclick = true;
}, 200);
},
// 去除标签
spanDel(str) {
let newstr = str.replace(/<[^>]+>/g, "");
return newstr;
},
// 搜索建议变色
spanColor(searchValue, data) {
let comData = [];
searchValue = html_decode(searchValue);
// 过滤所有特殊字符
let YesStrReg = new RegExp(
/[(\@)(\#)(\$)(\^)(\&)(\*)(\[)(\])(\{)(\})(\|)(\\)(\')(\/)(\<)(\>)(\《)(\》)(\)]+/
);
let searchValueYesStr = searchValue.replace(
/[(\@)(\#)(\$)(\^)(\&)(\*)(\[)(\])(\{)(\})(\|)(\\)(\')(\/)(\<)(\>)(\《)(\》)(\)]+/g,
""
);
let zimuReg = new RegExp(/[A-Za-z]/);
for (let val of data) {
if (!val) {
return "";
}
val = html_decode(val);
let obj = {
span: "",
value: val
};
if (searchValue && searchValue.length > 0) {
console.log(YesStrReg.test(searchValue),'dsakjjkasdjkasjk')
// 如果搜索条件有字母
if (zimuReg.test(searchValue)) {
console.log(val, searchValue, "搜索条件有字母");
// 去除首尾空格
let placeVal = searchValue.replace(/(^\s*)|(\s*$)/g, "");
let zimuPlace = new RegExp(placeVal, "gi");
var replaceString = str => `${str}`;
obj.span = val.replace(zimuPlace, function(num) {
return replaceString(num);
});
} else {
// 如果搜索返回值有特殊字符 直接匹配变蓝
if (YesStrReg.test(val)) {
// console.log(val, searchValue, "返回值有特殊字符");
// 匹配关键字正则
let replaceReg = new RegExp(searchValue, "g");
// 高亮替换v-html值
let replaceString =
'' +
searchValue +
"";
// 开始替换
obj.span = val.replace(replaceReg, replaceString);
} else {
// console.log(val, searchValue, "返回值没有特殊字符");
// 如果没有 则只将文字变蓝色
// 匹配关键字正则
let replaceReg = new RegExp(searchValueYesStr, "g");
// 高亮替换v-html值
let replaceString =
'' +
searchValueYesStr +
"";
// 开始替换
obj.span = val.replace(replaceReg, replaceString);
}
}
}
// 先将所有字符串转义 然后 在匹配span标签 解决<>造成标签闭合出错的bug
obj.span = html_encode(obj.span);
obj.span = obj.span.replace(/<span class="search-text" style="color:#5890EB;">/gi,``);
obj.span = obj.span.replace(/<\/span>/gi,``);
comData.push(obj);
}
return comData;
},
// 键盘下箭头
downKey: function() {
const that = this;
if (that.sugList.length > 0) {
if (that.sugIndex < that.sugList.length - 1) {
that.sugIndex += 1;
}
}
that.itemClassDel();
let objList = document.querySelectorAll(".sl-item");
if (objList.length > 0) {
objList[that.sugIndex].classList.add("sl-active");
that.searchVal = objList[that.sugIndex].getAttribute("checkVal");
}
},
// 键盘上箭头
upKey: function() {
const that = this;
if (that.sugList.length > 0) {
if (that.sugIndex > 0) {
that.sugIndex -= 1;
}
}
that.itemClassDel();
let objList = document.querySelectorAll(".sl-item");
if (objList.length > 0) {
objList[that.sugIndex].classList.add("sl-active");
that.searchVal = objList[that.sugIndex].getAttribute("checkVal");
}
},
sugOver(event, i) {
const that = this;
let obj = event.currentTarget;
that.itemClassDel();
obj.classList.add("overitem");
that.sugIndex = i;
},
sugOut(event, i) {
const that = this;
let obj = event.currentTarget;
that.itemClassDel();
obj.classList.remove("overitem");
that.sugIndex = -1;
}
},
//生命周期 - 创建完成(可以访问当前this实例)
created() {},
//生命周期 - 挂载完成(可以访问DOM元素)
mounted() {
const that = this;
//监听拼音输入法确定输入 ,compositionstart 、 compositionend 、 input都存在时的解决办法
let srkInputObj = document.getElementsByClassName("awcsi-input")[0];
srkInputObj.addEventListener("compositionstart", function(data) {
// 输入框候选出现
that.srfFlag = false;
});
srkInputObj.addEventListener("compositionend", function(data) {
// 输入框候选结束
that.srfFlag = true;
});
// 赋值搜索内容
if (that.$route.params.searchVal) {
that.searchVal = that.$route.params.searchVal;
if(!!window.ActiveXObject || "ActiveXObject" in window){
that.isIeAxios = false;
console.log('是ie浏览器不包括edeg');
// return true;
}else{
console.log('不是ie的其它浏览器');
// return false;
}
}
},
beforeCreate() {}, //生命周期 - 创建之前
beforeMount() {}, //生命周期 - 挂载之前
beforeUpdate() {}, //生命周期 - 更新之前
updated() {}, //生命周期 - 更新之后
beforeDestroy() {}, //生命周期 - 销毁之前
destroyed() {}, //生命周期 - 销毁完成
activated() {} //如果页面有keep-alive缓存功能,这个函数会触发
};
script>
<style lang='less' scoped>
//@import url(); 引入公共css类
.visibility {
visibility: hidden;
}
.awcs-wrap {
height: 216px;
background: url("./images/asearch-bg.png") no-repeat;
background-size: cover;
position: relative;
}
.awcs-shade {
position: absolute;
left: 0;
right: 0;
top: 0;
bottom: 0;
background: rgba(37, 79, 113, 0.2);
}
.awcs-inner {
position: absolute;
left: 50%;
margin-left: -374px;
top: 50%;
margin-top: -29px;
z-index: 100;
.awcsi-input {
width: 748px;
height: 58px;
padding: 18px;
padding-right: 70px;
border: none;
letter-spacing: 1px;
box-sizing: border-box;
font-size: 16px;
color: #222222;
}
.awcsi-icon-wrap {
width: 30px;
height: 30px;
padding: 14px 20px;
position: absolute;
top: 0;
right: 0;
cursor: pointer;
.awcsi-icon {
width: 100%;
height: 100%;
display: block;
background: url("./images/asearch-icon.png") no-repeat;
background-size: cover;
}
}
}
.search-list {
border-top: 1px solid #f2f2f2;
padding: 10px 0;
background: #fff;
z-index: 10;
}
.sl-item {
text-align: left;
line-height: 30px;
color: #888888;
font-size: 16px;
cursor: pointer;
padding: 0 20px;
}
.overitem:hover {
background: #fafafa;
background: rgba(34, 34, 34, 0.2);
}
.sl-item.sl-active {
background: #fafafa;
background: rgba(34, 34, 34, 0.2);
}
style>
// js-string.js
// ---- 用于搜索转义 ---
// https://www.cnblogs.com/daysme/p/7100553.html
// console.log(encodeHtml('沉睡<一>'),'encodeHtml'); // 沉睡<一> encodeHtml
// console.log(decodeHtml('沉睡<一>'),'decodeHtml'); // 沉睡<一> decodeHtml
// console.log(html_encode('沉睡<一>'),'html_encode'); // 沉睡<一> html_encode
// console.log(html_decode('沉睡<一>'),'html_decode'); // 沉睡<一> html_decode
export const html_encode = (str) =>
{
var s = "";
if (str.length == 0) return "";
s = str.replace(/&/g, "&");
s = s.replace(/, "<");
s = s.replace(/>/g, ">");
s = s.replace(/ /g, " ");
s = s.replace(/\'/g, "'");
s = s.replace(/\"/g, """);
s = s.replace(/\n/g, "
");
return s;
}
export const html_decode = (str) =>
{
var s = "";
if (str.length == 0) return "";
s = str.replace(/&/g, "&");
s = s.replace(/</g, "<");
s = s.replace(/>/g, ">");
s = s.replace(/ /g, " ");
s = s.replace(/'/g, "\'");
s = s.replace(/"/g, "\"");
s = s.replace(/
/g, "\n");
return s;
}
// ---- 用于搜索转义End ---
const REGX_HTML_ENCODE = /"|&|'|<|>|[\x00-\x20]|[\x7F-\xFF]|[\u0100-\u2700]/g;
const REGX_HTML_DECODE = /&\w+;|(\d+);/g;
const REGX_TRIM = /(^\s*)|(\s*$)/g;
const HTML_DECODE = {
"<": "<",
">": ">",
"&": "&",
" ": " ",
'"': "\"",
"©": ""
// Add more
};
export const encodeHtml = (s) => {
s = (s != undefined) ? s : this.toString();
return (typeof s != "string") ? s :
s.replace(REGX_HTML_ENCODE,
function ($0) {
var c = $0.charCodeAt(0), r = [""];
c = (c == 0x20) ? 0xA0 : c;
r.push(c); r.push(";");
return r.join("");
});
};
export const decodeHtml = (s) => {
s = (s != undefined) ? s : this.toString();
return (typeof s != "string") ? s :
s.replace(REGX_HTML_DECODE,
function ($0, $1) {
var c = HTML_DECODE[$0];
if (c == undefined) {
// Maybe is Entity Number
if (!isNaN($1)) {
c = String.fromCharCode(($1 == 160) ? 32 : $1);
} else {
c = $0;
}
}
return c;
});
};
export const trim = (s) => {
s = (s != undefined) ? s : this.toString();
return (typeof s != "string") ? s :
s.replace(REGX_TRIM, "");
};
export const hashCode = () => {
var hash = this.__hash__, _char;
if (hash == undefined || hash == 0) {
hash = 0;
for (var i = 0, len = this.length; i < len; i++) {
_char = this.charCodeAt(i);
hash = 31 * hash + _char;
hash = hash & hash; // Convert to 32bit integer
}
hash = hash & 0x7fffffff;
}
this.__hash__ = hash;
return this.__hash__;
};