221 lines
5.6 KiB
Vue
221 lines
5.6 KiB
Vue
![]() |
<template>
|
||
|
<div class="search-main py-3">
|
||
|
<form
|
||
|
class="entity-search-filter"
|
||
|
role="search"
|
||
|
@submit.prevent="sendQuery"
|
||
|
>
|
||
|
<div class="mb-3">
|
||
|
<div style="border-bottom: 1px solid var(--primary-color) !important">
|
||
|
<div
|
||
|
class="d-flex justify-content-between align-items-center mb-2 header_div"
|
||
|
>
|
||
|
<h6 style="font-family: sahel-semi-bold" class="m-0">
|
||
|
جستجوی در متن
|
||
|
</h6>
|
||
|
<button @click.prevent="closeList" class="btn" type="button">
|
||
|
<svg class="icon icon-Component-21--1">
|
||
|
<use xlink:href="#icon-Component-21--1"></use>
|
||
|
</svg>
|
||
|
</button>
|
||
|
</div>
|
||
|
</div>
|
||
|
<div class="input-group mt-2">
|
||
|
<div class="input-group-append">
|
||
|
<span class="tavasi tavasi-Component-198--1"></span>
|
||
|
</div>
|
||
|
|
||
|
<input
|
||
|
ref="search-input"
|
||
|
dir="rtl"
|
||
|
v-model.trim="searchText"
|
||
|
type="search"
|
||
|
required
|
||
|
class="form-control"
|
||
|
id="search-query"
|
||
|
placeholder="جستجو..."
|
||
|
name="search-query"
|
||
|
aria-label="جستجو در اسناد، عناوین و واژگان"
|
||
|
aria-describedby="basic-addon1"
|
||
|
size="50"
|
||
|
@keyup="sendQuery()"
|
||
|
@keydown="onKeyDown()"
|
||
|
/>
|
||
|
<button
|
||
|
v-if="searchText.length"
|
||
|
@click="clearSearchAndGetList"
|
||
|
type="button"
|
||
|
class="btn clear-search p-0"
|
||
|
>
|
||
|
<svg class="icon icon-search_off">
|
||
|
<use xlink:href="#icon-search_off"></use>
|
||
|
</svg>
|
||
|
</button>
|
||
|
|
||
|
<div class="input-group-prepend">
|
||
|
<span> </span>
|
||
|
</div>
|
||
|
</div>
|
||
|
|
||
|
<!-- <button
|
||
|
@click.prevent="isSearchModes()"
|
||
|
type="button"
|
||
|
class="btn close-search p-0 pe-1"
|
||
|
>
|
||
|
<svg class="icon icon-Component-71--1">
|
||
|
<use xlink:href="#icon-Component-71--1"></use>
|
||
|
</svg>
|
||
|
</button> -->
|
||
|
<small v-if="searchInCurrentGroup && listGetter">
|
||
|
جستجو در :
|
||
|
{{ listGetter?.title }}
|
||
|
</small>
|
||
|
</div>
|
||
|
</form>
|
||
|
|
||
|
<ul class="search-list firefox-scrollbar">
|
||
|
<li v-for="(item, index) in searchResult" :key="index">
|
||
|
<button
|
||
|
@click.prevent="jumpTo(item)"
|
||
|
class="btn text-end text__13"
|
||
|
type="button"
|
||
|
v-html="highLight(item)"
|
||
|
></button>
|
||
|
</li>
|
||
|
</ul>
|
||
|
</div>
|
||
|
</template>
|
||
|
<script>
|
||
|
// import entityViewMixin from "~/entity/mixins/entityViewMixin.js";
|
||
|
import searchApi from "@apis/searchApi";
|
||
|
|
||
|
export default {
|
||
|
props: {
|
||
|
item: {
|
||
|
default() {
|
||
|
return {};
|
||
|
},
|
||
|
},
|
||
|
},
|
||
|
|
||
|
// mixins: [entityViewMixin],
|
||
|
|
||
|
data() {
|
||
|
return {
|
||
|
typingTimer: 0,
|
||
|
doneTypingInterval: 800,
|
||
|
searchText: "",
|
||
|
searchInCurrentGroup: false,
|
||
|
searchResult: [],
|
||
|
};
|
||
|
},
|
||
|
methods: {
|
||
|
closeList() {
|
||
|
this.$emit("showListHeadline", {
|
||
|
isSearchMode: false,
|
||
|
isHeadingMode: false,
|
||
|
});
|
||
|
},
|
||
|
highLight(item, key = "content") {
|
||
|
let text = "";
|
||
|
|
||
|
if (item.highlight) {
|
||
|
if (item.highlight[key])
|
||
|
if (Array.isArray(item.highlight[key])) text = item.highlight[key][0];
|
||
|
else text = item.highlight[key];
|
||
|
}
|
||
|
|
||
|
if (text == "") {
|
||
|
let qText = this.searchText.trim();
|
||
|
if (qText.length >= 2) {
|
||
|
if (item._source[key])
|
||
|
text = item._source[key].replaceAll(
|
||
|
this.searchText,
|
||
|
`<span class="high-lighted">${this.searchText}</span>`
|
||
|
);
|
||
|
}
|
||
|
}
|
||
|
return (
|
||
|
`<span class="text__bold">${item._source?.other_info?.full_path} : </span> ` +
|
||
|
text
|
||
|
);
|
||
|
},
|
||
|
// highLightSearch(text) {
|
||
|
// return text.replace(
|
||
|
// this.searchText,
|
||
|
// `<span class="highlight">${this.searchText}</span>`
|
||
|
// );
|
||
|
// },
|
||
|
jumpTo(item) {
|
||
|
const { $eventBus } = useNuxtApp();
|
||
|
$eventBus.emit("jump-to-item", item);
|
||
|
},
|
||
|
clearSearchAndGetList() {
|
||
|
this.searchText = "";
|
||
|
this.searchResult = [];
|
||
|
},
|
||
|
sendQuery() {
|
||
|
clearTimeout(this.typingTimer);
|
||
|
|
||
|
this.typingTimer = setTimeout(() => {
|
||
|
this.searchInRules();
|
||
|
}, this.doneTypingInterval);
|
||
|
},
|
||
|
onKeyDown() {
|
||
|
clearTimeout(this.typingTimer);
|
||
|
},
|
||
|
async searchInRules() {
|
||
|
let qText = this.searchText.trim();
|
||
|
if (qText.length < 2) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
let url = searchApi.search.textSearch;
|
||
|
|
||
|
url = url.replace("{{appname}}", import.meta.env.VITE_BUILD_NAME);
|
||
|
url = url.replace("{{index_key}}", this.$route.params.key);
|
||
|
url = url.replace("{{offset}}", 0);
|
||
|
url = url.replace("{{limit}}", 100);
|
||
|
|
||
|
url = url.replace("{{field}}", "content");
|
||
|
let filter = `qanon_id=${this.$route.params.id}&q=${qText}`;
|
||
|
url = url.replace("{{filter}}", filter);
|
||
|
|
||
|
try {
|
||
|
const { $api } = useNuxtApp();
|
||
|
const response = await $api(url, {
|
||
|
baseURL: repoUrl(),
|
||
|
});
|
||
|
|
||
|
this.searchResult = response.hits.hits;
|
||
|
} catch (err) {}
|
||
|
},
|
||
|
},
|
||
|
};
|
||
|
</script>
|
||
|
<style lang="scss">
|
||
|
.search-main {
|
||
|
.search-list {
|
||
|
height: calc(100dvh - 16em);
|
||
|
overflow-y: auto;
|
||
|
}
|
||
|
.high-lighted {
|
||
|
background-color: yellow;
|
||
|
padding: 0 5px;
|
||
|
font-weight: bold;
|
||
|
}
|
||
|
}
|
||
|
.header_div {
|
||
|
button {
|
||
|
&:hover {
|
||
|
color: #ef4444;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
.clear-search {
|
||
|
&:hover {
|
||
|
color: #ef4444;
|
||
|
}
|
||
|
}
|
||
|
</style>
|