|
|
@@ -1,17 +1,19 @@
|
|
|
<script setup lang="ts">
|
|
|
-import { onUnmounted, ref, type Ref, onMounted } from "vue";
|
|
|
-import { useRoute, useRouter } from "vue-router";
|
|
|
+import { onUnmounted, ref, computed, type Ref, onMounted, reactive } from "vue";
|
|
|
+import { useRoute } from "vue-router";
|
|
|
import { useSideBarStore } from "@/stores/side-bar";
|
|
|
-import * as searchService from "@/services/search.service";
|
|
|
-import type { SearchResult, AggItem } from "@/types/search.types";
|
|
|
import SearchBox from "./components/SearchBox.vue";
|
|
|
import SearchResultToolbar from "./components/SearchResultToolbar.vue";
|
|
|
import SearchResultList from "./components/SearchResultList.vue";
|
|
|
import type { SearchRequest } from "@/types/request.types";
|
|
|
+import { useSearchStore } from "@/stores/search.store";
|
|
|
+import { routeToSearch, type SearchRouteParam } from "./search.helper";
|
|
|
+import type { Sort } from "./search-component.type";
|
|
|
|
|
|
const sideBarStore = useSideBarStore();
|
|
|
-// 路由管理器
|
|
|
-const router = useRouter();
|
|
|
+
|
|
|
+const searchStore = useSearchStore();
|
|
|
+
|
|
|
// 当前路由对象
|
|
|
const route = useRoute();
|
|
|
|
|
|
@@ -20,206 +22,225 @@ const keyword = ref("");
|
|
|
// 字段
|
|
|
const field = ref("TP");
|
|
|
// 排序
|
|
|
-const sort = ref("Relevance");
|
|
|
+const sort: Ref<Sort> = ref("Relevance");
|
|
|
|
|
|
-const page = ref(1);
|
|
|
+const pagination = reactive({
|
|
|
+ page: 1,
|
|
|
+ size: 10,
|
|
|
+})
|
|
|
|
|
|
-const size = ref(10);
|
|
|
+let authorChecked: Ref<string[]> = ref([]);
|
|
|
+let yearChecked: Ref<string[]> = ref([]);
|
|
|
|
|
|
-let authorChecked = ref([]);
|
|
|
-let yearChecked = ref([]);
|
|
|
+const searchResult = computed(() => searchStore.result);
|
|
|
+const yearAggResult = computed(() => searchStore.yearAggResult);
|
|
|
+const authorAggResult = computed(() => searchStore.authorAggResult);
|
|
|
|
|
|
-let yearValueArr: string[] = []
|
|
|
-let authorValueArr: string[] = []
|
|
|
+const searchType: Ref<'simple' | 'advanced'> = ref('simple');
|
|
|
|
|
|
-// 搜索结果
|
|
|
-const searchResult: Ref<SearchResult | null> = ref(null);
|
|
|
+const query = ref('');
|
|
|
+
|
|
|
+interface SearchParams {
|
|
|
+ query: string;
|
|
|
+ yearSelected?: string[];
|
|
|
+ authorSelected?: string[];
|
|
|
+ sort?: Sort;
|
|
|
+ page?: number;
|
|
|
+ size?: number;
|
|
|
+}
|
|
|
|
|
|
-// 搜索结果
|
|
|
-const aggYearResult: Ref<AggItem[] | null> = ref(null);
|
|
|
-// 搜索结果
|
|
|
-const aggAuthorResult: Ref<AggItem[] | null> = ref(null);
|
|
|
+function performSearch(searchParams: SearchParams) {
|
|
|
+ let sortParam: Object = { "Relevance": "Relevance" };
|
|
|
+ if (searchParams.sort) {
|
|
|
+ const sort = searchParams.sort;
|
|
|
+ if (sort === 'DateDesc') {
|
|
|
+ sortParam = { "year": "DESC" }
|
|
|
+ } else if (sort === 'DateAsc') {
|
|
|
+ sortParam = { "year": "ASC" }
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
-// 根据年份聚合
|
|
|
-function aggsYearSearch(query: string) {
|
|
|
- const aggYear = {
|
|
|
- name: 'year',
|
|
|
- field: 'year'
|
|
|
+ let filters: string[] = []
|
|
|
+ if (searchParams.yearSelected && searchParams.yearSelected.length > 0) {
|
|
|
+ filters.push(`PY=(${searchParams.yearSelected.join(" ")})`);
|
|
|
}
|
|
|
- const aggs: { [key: string]: any } = {}
|
|
|
- aggs.year = aggYear;
|
|
|
- const param = {
|
|
|
- query: query,
|
|
|
- aggs: aggs
|
|
|
- };
|
|
|
- searchService.search(param).then((resp) => {
|
|
|
- aggYearResult.value = resp.data.data.aggs.year ? resp.data.data.aggs.year : []
|
|
|
- }).catch((err) => {
|
|
|
- console.log(err);
|
|
|
- })
|
|
|
-}
|
|
|
+ if (searchParams.authorSelected && searchParams.authorSelected.length > 0) {
|
|
|
+ filters.push(`AU=(${searchParams.authorSelected.join(" OR ")})`);
|
|
|
+ }
|
|
|
+ const filter = filters.join(" AND ");
|
|
|
|
|
|
-// 根据作者聚合
|
|
|
-function aggsAuthorsSearch(query: string) {
|
|
|
+ // size
|
|
|
+ const size = Math.max(searchParams.size || 10, 10);
|
|
|
+ // from
|
|
|
+ const from = (Math.max(searchParams.page || 1, 1) - 1) * size;
|
|
|
|
|
|
- const aggAuthors = {
|
|
|
- name: 'authors',
|
|
|
- field: 'authors.keyword'
|
|
|
+ const commonParams = {
|
|
|
+ query: searchParams.query,
|
|
|
+ filter,
|
|
|
}
|
|
|
- const aggs: { [key: string]: any } = {}
|
|
|
- aggs.authors = aggAuthors;
|
|
|
- const param = {
|
|
|
- query: query,
|
|
|
- aggs: aggs
|
|
|
+
|
|
|
+ const param: SearchRequest = {
|
|
|
+ ...commonParams,
|
|
|
+ from,
|
|
|
+ size,
|
|
|
+ sort: sortParam,
|
|
|
};
|
|
|
- searchService.search(param).then((resp) => {
|
|
|
- aggAuthorResult.value = resp.data.data.aggs.authors ? resp.data.data.aggs.authors : []
|
|
|
- }).catch((err) => {
|
|
|
- console.log(err);
|
|
|
- })
|
|
|
+
|
|
|
+ searchStore.search(param);
|
|
|
+ searchStore.aggYear({
|
|
|
+ ...commonParams,
|
|
|
+ aggs: { year: {} },
|
|
|
+ size: 0,
|
|
|
+ });
|
|
|
+ searchStore.aggAuthor({
|
|
|
+ ...commonParams,
|
|
|
+ aggs: { AU: {} },
|
|
|
+ size: 0,
|
|
|
+ });
|
|
|
}
|
|
|
|
|
|
-function doSearch(field: string, keyword: string) {
|
|
|
- if (keyword && keyword.length == 0) {
|
|
|
- router.push({ name: "SearchIndex" });
|
|
|
- return;
|
|
|
+/**
|
|
|
+ * 执行搜索,同时修改url
|
|
|
+ */
|
|
|
+function doSearch() {
|
|
|
+ let params: SearchRouteParam = {
|
|
|
+ type: searchType.value,
|
|
|
}
|
|
|
- router.push({ name: "SearchResult", query: { kw: keyword } });
|
|
|
- const query = `${field}=(${keyword})`;
|
|
|
- let sortParam = null;
|
|
|
- if (sort.value === 'Relevance') {
|
|
|
- sortParam = { "Relevance": "Relevance" }
|
|
|
- } else if (sort.value === 'DateDesc') {
|
|
|
- sortParam = { "year": "DESC" }
|
|
|
- } else if (sort.value === 'DateAsc') {
|
|
|
- sortParam = { "year": "ASC" }
|
|
|
- } else if (sort.value === 'CitedDesc') {
|
|
|
- sortParam = { "cited": "ASC" }
|
|
|
+ if (searchType.value == 'simple') {
|
|
|
+ params['field'] = field.value;
|
|
|
+ params['kw'] = keyword.value;
|
|
|
+ } else {
|
|
|
+ params['query'] = query.value;
|
|
|
}
|
|
|
|
|
|
- const param: SearchRequest = {
|
|
|
- query: query,
|
|
|
- from: page.value,
|
|
|
- size: size.value,
|
|
|
- sort: sortParam
|
|
|
+ const extras = {
|
|
|
+ page: pagination.page,
|
|
|
+ size: pagination.size,
|
|
|
+ yearSelected: yearChecked.value,
|
|
|
+ authorSelected: authorChecked.value,
|
|
|
+ sort: sort.value,
|
|
|
+ }
|
|
|
+ routeToSearch(params, extras);
|
|
|
+
|
|
|
+ const searchParams: SearchParams = {
|
|
|
+ query: query.value,
|
|
|
+ yearSelected: yearChecked.value,
|
|
|
+ authorSelected: authorChecked.value,
|
|
|
+ sort: sort.value,
|
|
|
};
|
|
|
- const aggs: { [key: string]: any } = {}
|
|
|
- if (yearValueArr && yearValueArr.length > 0) {
|
|
|
- var yearQuery: string = "PY=";
|
|
|
- var pyKw = '('
|
|
|
- for (let i = 0; i < yearValueArr.length; i++) {
|
|
|
- if (i == 0) {
|
|
|
- pyKw += yearValueArr[i]
|
|
|
- } else {
|
|
|
- pyKw += " " + yearValueArr[i]
|
|
|
- }
|
|
|
-
|
|
|
- console.log(pyKw);
|
|
|
- if (i == yearValueArr.length - 1) {
|
|
|
- pyKw += ')'
|
|
|
- }
|
|
|
+ performSearch(searchParams);
|
|
|
+}
|
|
|
+
|
|
|
+function onSimpleSearch() {
|
|
|
+ routeToSearch({type: 'simple', field: field.value, kw: keyword.value});
|
|
|
+ doSearch();
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * 根据url包含的信息,初始化状态信息
|
|
|
+ */
|
|
|
+function setupState() {
|
|
|
+ const queryParam = route.query;
|
|
|
+ if (queryParam.type == 'advanced') {
|
|
|
+ searchType.value = 'advanced';
|
|
|
+ query.value = queryParam.query as string;
|
|
|
+ } else {
|
|
|
+ searchType.value = 'simple';
|
|
|
+ // 初始化关键词
|
|
|
+ if (route.query.kw) {
|
|
|
+ keyword.value = (route.query.kw as string).trim();
|
|
|
+ }
|
|
|
+ if (route.query.field) {
|
|
|
+ field.value = (route.query.field as string).trim();
|
|
|
}
|
|
|
- param.query += ' AND ' + yearQuery + pyKw
|
|
|
+ query.value = `${field.value}=(${keyword.value})`
|
|
|
}
|
|
|
|
|
|
- if (authorValueArr && authorValueArr.length > 0) {
|
|
|
- var auQuery: string = "AU=";
|
|
|
- var pyKw = '('
|
|
|
- for (let i = 0; i < authorValueArr.length; i++) {
|
|
|
- if (i == 0) {
|
|
|
- pyKw += authorValueArr[i]
|
|
|
- } else {
|
|
|
- pyKw += " " + authorValueArr[i]
|
|
|
- }
|
|
|
-
|
|
|
- console.log(pyKw);
|
|
|
- if (i == authorValueArr.length - 1) {
|
|
|
- pyKw += ')'
|
|
|
- }
|
|
|
+ if (queryParam.yearSelected) {
|
|
|
+ let yearSelected: string[];
|
|
|
+ if (Array.isArray(queryParam.yearSelected)) {
|
|
|
+ yearSelected = queryParam.yearSelected as string[];
|
|
|
+ } else {
|
|
|
+ yearSelected = [queryParam.yearSelected];
|
|
|
}
|
|
|
- param.query += ' AND ' + auQuery + pyKw
|
|
|
+ yearChecked.value = yearSelected;
|
|
|
}
|
|
|
- searchResult.value = null
|
|
|
- searchService.search(param).then((resp) => {
|
|
|
- searchResult.value = resp.data.data;
|
|
|
- if (yearValueArr && yearValueArr.length > 0) {
|
|
|
- aggsAuthorsSearch(param.query)
|
|
|
+
|
|
|
+ if (queryParam.authorSelected) {
|
|
|
+ let authorSelected: string[];
|
|
|
+ if (Array.isArray(queryParam.authorSelected)) {
|
|
|
+ authorSelected = queryParam.authorSelected as string[];
|
|
|
} else {
|
|
|
- aggsYearSearch(param.query)
|
|
|
+ authorSelected = [queryParam.authorSelected];
|
|
|
}
|
|
|
- }).catch((err) => {
|
|
|
- console.log(err);
|
|
|
- })
|
|
|
+ authorChecked.value = authorSelected;
|
|
|
+ }
|
|
|
+ if (queryParam.sort) {
|
|
|
+ sort.value = queryParam.sort as Sort;
|
|
|
+ }
|
|
|
+ if (queryParam.page) {
|
|
|
+ pagination.page = parseInt(queryParam.page as string);
|
|
|
+ }
|
|
|
+ if (queryParam.size) {
|
|
|
+ pagination.size = parseInt(queryParam.size as string);
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
onMounted(() => {
|
|
|
// 收起左侧边栏
|
|
|
sideBarStore.setCollapse(true);
|
|
|
- // 初始化关键词
|
|
|
- if (route.query.kw) {
|
|
|
- keyword.value = (route.query.kw as string).trim();
|
|
|
- }
|
|
|
- if (route.query.field) {
|
|
|
- field.value = (route.query.field as string).trim();
|
|
|
- }
|
|
|
+ // 初始化状态
|
|
|
+ setupState();
|
|
|
// 搜索
|
|
|
- doSearch(field.value, keyword.value);
|
|
|
- const query = `${field.value}=(${keyword.value})`
|
|
|
- aggsYearSearch(query);
|
|
|
- aggsAuthorsSearch(query);
|
|
|
+ doSearch();
|
|
|
});
|
|
|
|
|
|
-function changeFile(val: string) {
|
|
|
- // 搜索
|
|
|
- field.value = val
|
|
|
- doSearch(field.value, keyword.value);
|
|
|
+function changeField(value: string) {
|
|
|
+ field.value = value;
|
|
|
+ query.value = `${field.value}=(${keyword.value})`;
|
|
|
}
|
|
|
|
|
|
-function changKeyword(val: string) {
|
|
|
- keyword.value = val
|
|
|
- doSearch(field.value, keyword.value);
|
|
|
+function changKeyword(value: string) {
|
|
|
+ keyword.value = value;
|
|
|
+ query.value = `${field.value}=(${keyword.value})`;
|
|
|
}
|
|
|
|
|
|
function changeSort(val: any) {
|
|
|
- sort.value = val
|
|
|
- doSearch(field.value, keyword.value);
|
|
|
+ sort.value = val;
|
|
|
+ doSearch();
|
|
|
}
|
|
|
|
|
|
-function changePagination(p: any, s: any) {
|
|
|
- page.value = p
|
|
|
- size.value = s
|
|
|
- doSearch(field.value, keyword.value);
|
|
|
+function onPaginationChange(page: any, pageSize: any) {
|
|
|
+ pagination.page = page;
|
|
|
+ pagination.size = pageSize;
|
|
|
+ doSearch();
|
|
|
}
|
|
|
|
|
|
function changeYearChecked() {
|
|
|
- yearValueArr = []
|
|
|
- authorValueArr = []
|
|
|
- for (let valueKey in yearChecked.value) {
|
|
|
- yearValueArr.push(yearChecked.value[valueKey])
|
|
|
- }
|
|
|
- doSearch(field.value, keyword.value);
|
|
|
+ pagination.page = 1;
|
|
|
+ doSearch();
|
|
|
}
|
|
|
|
|
|
function changeAuthorChecked() {
|
|
|
- authorValueArr = []
|
|
|
- yearValueArr = []
|
|
|
- for (let valueKey in authorChecked.value) {
|
|
|
- authorValueArr.push(authorChecked.value[valueKey])
|
|
|
- }
|
|
|
- doSearch(field.value, keyword.value);
|
|
|
+ pagination.page = 1;
|
|
|
+ doSearch();
|
|
|
}
|
|
|
|
|
|
onUnmounted(() => {
|
|
|
sideBarStore.setCollapse(false);
|
|
|
});
|
|
|
-
|
|
|
</script>
|
|
|
|
|
|
<template>
|
|
|
<div class="search-result-wrap">
|
|
|
<div class="search-box-wrap">
|
|
|
- <SearchBox v-model:keyword="keyword" v-model:field="field" @search="doSearch" @update:field="changeFile"
|
|
|
- @update:keyword="changKeyword" />
|
|
|
+ <SearchBox
|
|
|
+ v-model:keyword="keyword"
|
|
|
+ v-model:field="field"
|
|
|
+ @search="onSimpleSearch"
|
|
|
+ @update:field="changeField"
|
|
|
+ @update:keyword="changKeyword"
|
|
|
+ />
|
|
|
</div>
|
|
|
<a-row type="flex" class="search-result-main" :gutter="10">
|
|
|
<a-col :span="5">
|
|
|
@@ -229,8 +250,8 @@ onUnmounted(() => {
|
|
|
<a-space direction="vertical" style="width: 100%" v-if="searchResult">
|
|
|
<a-card title="年份">
|
|
|
<a-checkbox-group v-model:value="yearChecked" style="width: 100%" @change="changeYearChecked"
|
|
|
- v-if="aggYearResult && aggYearResult.length > 0">
|
|
|
- <a-row v-for="(item, index) in aggYearResult" :key="index">
|
|
|
+ v-if="yearAggResult && yearAggResult.length > 0">
|
|
|
+ <a-row v-for="(item, index) in yearAggResult" :key="index">
|
|
|
<a-col :span="20">
|
|
|
<a-checkbox :value="item.key">{{ item.key }}</a-checkbox>
|
|
|
</a-col>
|
|
|
@@ -240,7 +261,7 @@ onUnmounted(() => {
|
|
|
</a-card>
|
|
|
<a-card title="作者">
|
|
|
<a-checkbox-group v-model:value="authorChecked" style="width: 100%" @change="changeAuthorChecked">
|
|
|
- <a-row v-for="(item, index) in aggAuthorResult" :key="index">
|
|
|
+ <a-row v-for="(item, index) in authorAggResult" :key="index">
|
|
|
<a-col :span="20">
|
|
|
<a-checkbox :value="item.key">{{ item.key }}</a-checkbox>
|
|
|
</a-col>
|
|
|
@@ -251,8 +272,14 @@ onUnmounted(() => {
|
|
|
</a-space>
|
|
|
</a-col>
|
|
|
<a-col :span="19">
|
|
|
- <SearchResultToolbar :data="searchResult" v-model:sort="sort" v-if="searchResult" @update:sort="changeSort"
|
|
|
- @update:pagination="changePagination" />
|
|
|
+ <SearchResultToolbar
|
|
|
+ v-if="searchResult"
|
|
|
+ @update:sort="changeSort"
|
|
|
+ :data="searchResult"
|
|
|
+ v-model:sort="sort"
|
|
|
+ v-model:pagination="pagination"
|
|
|
+ @paginationChange="onPaginationChange"
|
|
|
+ />
|
|
|
<SearchResultList :data="searchResult" v-if="searchResult" />
|
|
|
</a-col>
|
|
|
</a-row>
|