基于vue的实时搜索,在结果中高亮显示关键词

参考资料:
【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'); // 沉睡&lt;一&gt; encodeHtml
      // console.log(decodeHtml('沉睡<一>'),'decodeHtml'); // 沉睡<一> decodeHtml
      // console.log(html_encode('沉睡<一>'),'html_encode'); // 沉睡&lt;一&gt; 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'); // 沉睡&lt;一&gt; encodeHtml
// console.log(decodeHtml('沉睡<一>'),'decodeHtml'); // 沉睡<一> decodeHtml
// console.log(html_encode('沉睡<一>'),'html_encode'); // 沉睡&lt;一&gt; 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__; };

你可能感兴趣的:(笔记,Vue,原创)