|
@@ -0,0 +1,304 @@
|
|
|
+<template>
|
|
|
+ <div class="search">
|
|
|
+ <PageBanner title="Result" :img="BannerImg" />
|
|
|
+
|
|
|
+ <PageNav v-model="activeTabbar">
|
|
|
+ <van-tab v-for="item in tabbar" :key="item.module" :title="item.name" />
|
|
|
+ </PageNav>
|
|
|
+
|
|
|
+ <div class="search-main">
|
|
|
+ <div class="search-label">
|
|
|
+ <p>
|
|
|
+ <span>{{ tabbar[activeTabbar].total }}</span
|
|
|
+ >results
|
|
|
+ </p>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <VanList
|
|
|
+ v-model:loading="loading"
|
|
|
+ :finished="noMore"
|
|
|
+ finished-text="no more"
|
|
|
+ :immediate-check="false"
|
|
|
+ @load="onLoad"
|
|
|
+ >
|
|
|
+ <ul>
|
|
|
+ <li
|
|
|
+ v-for="item in list"
|
|
|
+ :key="item.id"
|
|
|
+ class="search-item"
|
|
|
+ @click="handleClick(item)"
|
|
|
+ >
|
|
|
+ <VanImage
|
|
|
+ v-if="item.thumb"
|
|
|
+ lazy-load
|
|
|
+ :width="120"
|
|
|
+ :height="120"
|
|
|
+ :src="item.thumb"
|
|
|
+ />
|
|
|
+
|
|
|
+ <div class="search-item-inner">
|
|
|
+ <p class="search-item__title limit-line">
|
|
|
+ {{ item.name }}
|
|
|
+ </p>
|
|
|
+ <p class="search-item__content limit-line line-4">
|
|
|
+ {{ item.rtf }}
|
|
|
+ </p>
|
|
|
+ </div>
|
|
|
+ </li>
|
|
|
+ </ul>
|
|
|
+ </VanList>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script lang="ts" setup>
|
|
|
+import { onMounted, ref, watch } from "vue";
|
|
|
+import PageBanner from "@/components/PageBanner.vue";
|
|
|
+import PageNav from "@/components/PageNav.vue";
|
|
|
+import BannerImg from "./images/bannerRes.png";
|
|
|
+import { useRoute, useRouter } from "vue-router";
|
|
|
+import { cloneDeep, isUndefined } from "lodash-unified";
|
|
|
+import { JoinSupport, About, TermsOfUse, Employment, Visit } from "@/data";
|
|
|
+import { searchApi, type SearchItem } from "@/api";
|
|
|
+import { getBaseURL } from "@dage/service";
|
|
|
+
|
|
|
+const _VISIT = cloneDeep(Visit);
|
|
|
+Object.values(_VISIT.Reservation.children).forEach((rtf) => {
|
|
|
+ _VISIT.Reservation.rtf += rtf;
|
|
|
+});
|
|
|
+
|
|
|
+const PAGE_SIZE = 20;
|
|
|
+const FRONT_DATA = [
|
|
|
+ ...Object.values(JoinSupport).flat(),
|
|
|
+ ...Object.values(_VISIT).flat(),
|
|
|
+ About.Director,
|
|
|
+ TermsOfUse,
|
|
|
+ Employment,
|
|
|
+];
|
|
|
+
|
|
|
+const baseUrl = getBaseURL();
|
|
|
+const router = useRouter();
|
|
|
+const route = useRoute();
|
|
|
+const activeTabbar = ref(
|
|
|
+ isUndefined(route.query.activeTabbar) ? 0 : Number(route.query.activeTabbar)
|
|
|
+);
|
|
|
+const tabbar = ref([
|
|
|
+ { name: "All Results", module: "", total: 0 },
|
|
|
+ { name: "Visit", module: "visit", total: 0 },
|
|
|
+ { name: "Exhibitions", module: "exhibition", total: 0 },
|
|
|
+ { name: "Collections", module: "collection", total: 0 },
|
|
|
+ { name: "Learn & Engage", module: "learn", total: 0 },
|
|
|
+ {
|
|
|
+ name: "Research & Publications",
|
|
|
+ module: "publish",
|
|
|
+ total: 0,
|
|
|
+ },
|
|
|
+ { name: "Join & Support", module: "join", total: 0 },
|
|
|
+ { name: "About", module: "about", total: 0 },
|
|
|
+ { name: "Events", module: "event", total: 0 },
|
|
|
+ { name: "Terms of Use", module: "terms", total: 0 },
|
|
|
+ { name: "Employment", module: "employment", total: 0 },
|
|
|
+]);
|
|
|
+
|
|
|
+/**
|
|
|
+ * 处理富文本标签,返回限制后的文字
|
|
|
+ */
|
|
|
+const getAbstract = (str: string) => {
|
|
|
+ const txt = str.replace(/<[^>]*>/g, " ");
|
|
|
+ return `${txt.slice(0, 200)}${txt.length > 200 ? "..." : ""}`;
|
|
|
+};
|
|
|
+
|
|
|
+const loading = ref(false);
|
|
|
+const pageNum = ref(1);
|
|
|
+const noMore = ref(false);
|
|
|
+const list = ref<SearchItem[]>([]);
|
|
|
+
|
|
|
+const getList = async () => {
|
|
|
+ try {
|
|
|
+ loading.value = true;
|
|
|
+ const cloneTabbar = [...tabbar.value].map((i) => ({ ...i, total: 0 }));
|
|
|
+ const k = decodeURIComponent(route.query.keyword as string);
|
|
|
+ const reg = new RegExp(k, "i");
|
|
|
+ let stack: any[] = [];
|
|
|
+
|
|
|
+ stack = FRONT_DATA.filter((i) => {
|
|
|
+ // 判断是否满足查询条件
|
|
|
+ const res = reg.test(i.rtf);
|
|
|
+ const res2 = reg.test(i.name);
|
|
|
+ if (!res && !res2) return false;
|
|
|
+
|
|
|
+ switch (i.module) {
|
|
|
+ case "visit":
|
|
|
+ cloneTabbar[1].total += 1;
|
|
|
+ break;
|
|
|
+ case "join":
|
|
|
+ cloneTabbar[6].total += 1;
|
|
|
+ break;
|
|
|
+ case "about":
|
|
|
+ cloneTabbar[7].total += 1;
|
|
|
+ break;
|
|
|
+ case "terms":
|
|
|
+ cloneTabbar[9].total += 1;
|
|
|
+ break;
|
|
|
+ case "employment":
|
|
|
+ cloneTabbar[10].total += 1;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 如果非查询全部,先判断类型是否一致
|
|
|
+ if (
|
|
|
+ activeTabbar.value !== -1 &&
|
|
|
+ tabbar.value[activeTabbar.value]?.module !== i.module
|
|
|
+ )
|
|
|
+ return false;
|
|
|
+
|
|
|
+ return true;
|
|
|
+ }).map((i) => ({
|
|
|
+ ...i,
|
|
|
+ rtf: getAbstract(i.rtf),
|
|
|
+ }));
|
|
|
+
|
|
|
+ // 需要请求接口的tab索引
|
|
|
+ const BACKEND_MODULE_INDEX = [0, 2, 3, 4, 5, 8];
|
|
|
+ const data = await searchApi({
|
|
|
+ pageNum: pageNum.value,
|
|
|
+ pageSize: PAGE_SIZE,
|
|
|
+ searchKey: k,
|
|
|
+ // @ts-ignore
|
|
|
+ module:
|
|
|
+ activeTabbar.value === 0 ||
|
|
|
+ !BACKEND_MODULE_INDEX.includes(activeTabbar.value)
|
|
|
+ ? undefined
|
|
|
+ : tabbar.value[activeTabbar.value].module,
|
|
|
+ });
|
|
|
+
|
|
|
+ cloneTabbar[0].total = 0;
|
|
|
+ Object.keys(data.count).forEach((key) => {
|
|
|
+ const target = cloneTabbar.find((i) => i.module === key);
|
|
|
+ if (target) target.total = data.count[key];
|
|
|
+ cloneTabbar[0].total += data.count[key];
|
|
|
+ });
|
|
|
+
|
|
|
+ if (BACKEND_MODULE_INDEX.includes(activeTabbar.value)) {
|
|
|
+ stack.unshift(
|
|
|
+ ...data.list.records.map((i: SearchItem) => ({
|
|
|
+ ...i,
|
|
|
+ thumb: baseUrl + i.thumb,
|
|
|
+ rtf: i.rtf
|
|
|
+ ? getAbstract(
|
|
|
+ JSON.parse(i.rtf).txtArr.reduce(
|
|
|
+ (arr: string, item: Record<string, string>) =>
|
|
|
+ (arr += item.txt),
|
|
|
+ ""
|
|
|
+ )
|
|
|
+ )
|
|
|
+ : "",
|
|
|
+ }))
|
|
|
+ );
|
|
|
+ }
|
|
|
+
|
|
|
+ tabbar.value = cloneTabbar;
|
|
|
+ list.value = stack;
|
|
|
+
|
|
|
+ noMore.value = stack.length < PAGE_SIZE;
|
|
|
+ } finally {
|
|
|
+ loading.value = false;
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+const onLoad = () => {
|
|
|
+ if (noMore.value) return;
|
|
|
+
|
|
|
+ pageNum.value++;
|
|
|
+ getList();
|
|
|
+};
|
|
|
+
|
|
|
+const VISIT_MAP: Record<string, Function> = {
|
|
|
+ reservation: () => {
|
|
|
+ router.push({ name: "VisitInfo" });
|
|
|
+ },
|
|
|
+ guide: () => {
|
|
|
+ router.push({ name: "Visit", params: { id: 4 } });
|
|
|
+ },
|
|
|
+ accessibility: () => {
|
|
|
+ router.push({ name: "Visit", params: { id: 5 } });
|
|
|
+ },
|
|
|
+ shop: () => {
|
|
|
+ router.push({ name: "Visit", params: { id: 6 } });
|
|
|
+ },
|
|
|
+};
|
|
|
+const handleClick = (item: SearchItem) => {
|
|
|
+ switch (item.module as string) {
|
|
|
+ case "visit":
|
|
|
+ VISIT_MAP[item.type]();
|
|
|
+ break;
|
|
|
+ case "learn":
|
|
|
+ router.push({ name: "LearnInfo", query: { id: item.id } });
|
|
|
+ break;
|
|
|
+ case "exhibition":
|
|
|
+ router.push({ name: "ExDetail", query: { id: item.id } });
|
|
|
+ break;
|
|
|
+ case "event":
|
|
|
+ router.push({ name: "EventsDetail", params: { id: item.id } });
|
|
|
+ break;
|
|
|
+ case "collection":
|
|
|
+ router.push({ name: "CollectionsDetail", query: { id: item.id } });
|
|
|
+ break;
|
|
|
+ case "publish":
|
|
|
+ if (item.type === "Magazines") {
|
|
|
+ router.push({
|
|
|
+ name: "PublicationDetail",
|
|
|
+ query: { id: item.id },
|
|
|
+ });
|
|
|
+ } else {
|
|
|
+ router.push({
|
|
|
+ name: "Pdf",
|
|
|
+ query: {
|
|
|
+ url: encodeURIComponent(baseUrl + item.filePath),
|
|
|
+ title: item.name,
|
|
|
+ },
|
|
|
+ });
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ case "join":
|
|
|
+ if (item.type === "volunteer") {
|
|
|
+ router.push({ name: "JoinInfo", query: { id: item.id } });
|
|
|
+ } else {
|
|
|
+ router.push({ name: "JoinGi" });
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ case "about":
|
|
|
+ if (item.type === "director") {
|
|
|
+ router.push({ name: "AboutDirector" });
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ case "terms":
|
|
|
+ router.push({ name: "Use" });
|
|
|
+ break;
|
|
|
+ case "employment":
|
|
|
+ router.push({ name: "Employment" });
|
|
|
+ break;
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+watch(
|
|
|
+ route,
|
|
|
+ () => {
|
|
|
+ getList();
|
|
|
+ },
|
|
|
+ {
|
|
|
+ immediate: true,
|
|
|
+ }
|
|
|
+);
|
|
|
+
|
|
|
+watch(activeTabbar, () => {
|
|
|
+ router.replace({
|
|
|
+ name: "Search",
|
|
|
+ query: { ...route.query, activeTabbar: activeTabbar.value },
|
|
|
+ });
|
|
|
+});
|
|
|
+</script>
|
|
|
+
|
|
|
+<style lang="scss" scoped>
|
|
|
+@import "./index.scss";
|
|
|
+</style>
|