vueベースのリアルタイム検索で、結果にキーワードをハイライト表示


参考資料:【Vue.js】vueのリアルタイム検索に基づき、結果にキーワードをハイライト表示https://www.cnblogs.com/pengshengguang/p/8059190.htmlJS大文字と小文字を区別しない文字列ハイライトシミュレーションブラウザCtrl+Fhttps://www.jianshu.com/p/936029d3b9a6jsエスケープと逆エスケープhtmlhttps://www.cnblogs.com/daysme/p/7100553.htmlJavascript(js)によるHTMLエスケープツール(特殊文字表示の処理)https://blog.csdn.net/hj7jay/article/details/51280405axiosインタフェースリクエストのキャンセル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(/
/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
, "
"
); 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__; };