chenlei 11 months ago
parent
commit
fe87efb8aa

+ 8 - 1
src/api/publications.ts

@@ -7,7 +7,7 @@ import {
 export interface PublishListParams extends PaginationParams {
   type?: "Magazines" | "Exhibition";
   searchKey?: string;
-  year?: number;
+  year?: string;
 }
 
 /**
@@ -48,3 +48,10 @@ export const getPublishDetailApi = async (id: string | number) => {
     }
   );
 };
+
+/**
+ * 获取有数据的年份列表
+ */
+export const getMagazineYearListApi = () => {
+  return requestByGet<string[]>("/api/show/publish/getYear");
+};

+ 12 - 1
src/assets/css/base.css

@@ -83,7 +83,6 @@ video {
   padding: 0;
   border: 0;
   font-size: 100%;
-  font: inherit;
   vertical-align: baseline;
   box-sizing: border-box;
 }
@@ -150,6 +149,11 @@ h5 {
   font-weight: bold;
 }
 
+p {
+  font-family: Regular;
+  text-align: justify;
+}
+
 .limit-line {
   display: -webkit-box;
   overflow: hidden;
@@ -167,3 +171,10 @@ h5 {
 .line-4 {
   -webkit-line-clamp: 4;
 }
+
+.media-wrap {
+  text-align: center;
+}
+.media-wrap > * {
+  max-width: 100%;
+}

+ 1 - 0
src/components/ImgSwiper.vue

@@ -14,6 +14,7 @@
   </div>
 
   <VanImagePreview
+    closeable
     v-model:show="showPreview"
     :images="list"
     :start-position="current"

+ 2 - 1
src/components/Layout/components/Search/index.vue

@@ -14,6 +14,7 @@
               });
               show = false;
               keyword = '';
+              emits('confirm');
             }
           "
         />
@@ -32,7 +33,7 @@ import { computed, ref } from "vue";
 const props = defineProps<{
   visible: boolean;
 }>();
-const emits = defineEmits(["update:visible"]);
+const emits = defineEmits(["update:visible", "confirm"]);
 
 const show = computed({
   get() {

+ 20 - 1
src/components/Layout/components/Sidebar/index.scss

@@ -36,7 +36,7 @@
     --van-cell-horizontal-padding: 0;
     --van-collapse-item-content-font-size: 32px;
     --van-collapse-item-content-text-color: #c1aa7b;
-    --van-collapse-item-content-padding: 0 40px;
+    --van-collapse-item-content-padding: 0 60px;
 
     :deep(.van-collapse-item__title) {
       color: #c1aa7b;
@@ -56,8 +56,27 @@
     :deep(.van-cell__right-icon) {
       padding-left: 10px;
     }
+    &__title.active {
+      color: var(--van-primary-color);
+    }
     &__link {
+      position: relative;
       padding: 10px 0;
+
+      &.active {
+        color: var(--van-primary-color);
+
+        &::before {
+          content: "";
+          position: absolute;
+          top: 50%;
+          left: -54px;
+          width: 44px;
+          height: 36px;
+          background: url("@/assets/images/chosen.png") no-repeat center / cover;
+          transform: translateY(-50%);
+        }
+      }
     }
   }
 

+ 34 - 6
src/components/Layout/components/Sidebar/index.vue

@@ -27,10 +27,16 @@
           v-for="item in topData"
           :key="item.id"
           :title="item.name"
-          :name="item.id"
+          :name="`${item.id}`"
         >
           <template #title>
-            <div class="sidebar-nav__title" @click.stop="handleClick(item)">
+            <div
+              class="sidebar-nav__title"
+              :class="{
+                active: item.id === realActiveSubCollapse,
+              }"
+              @click.stop="handleClick(item)"
+            >
               {{ item.name }}
             </div>
           </template>
@@ -39,6 +45,9 @@
             v-for="sub in item.children"
             :key="sub.id"
             class="sidebar-nav__link"
+            :class="{
+              active: sub.id === activeSubCollapse,
+            }"
             @click="handleClick(sub)"
           >
             {{ sub.name }}
@@ -55,17 +64,20 @@
 </template>
 
 <script setup lang="ts">
-import { computed, ref } from "vue";
-import { topData, type TOP_DATA } from "../../data";
-import { useRouter } from "vue-router";
+import { computed, ref, watch } from "vue";
+import { findIndexes, topData, type TOP_DATA } from "../../data";
+import { useRoute, useRouter } from "vue-router";
 
 const props = defineProps<{
   visible: boolean;
 }>();
 const emits = defineEmits(["update:visible", "goToCNWeb", "openSearch"]);
 
+const route = useRoute();
 const router = useRouter();
-const activeCollapse = ref();
+const activeCollapse = ref("");
+const realActiveSubCollapse = ref<number | undefined>();
+const activeSubCollapse = ref<number | undefined>();
 
 const show = computed({
   get() {
@@ -80,6 +92,22 @@ const handleClick = (item: TOP_DATA) => {
   router.push(item.routeParams!);
   show.value = false;
 };
+
+watch(
+  route,
+  (v) => {
+    const indexes = findIndexes(topData, v.name as string, v.query, v.params);
+
+    if (indexes.length) {
+      activeCollapse.value = `${indexes[0][0]}`;
+      realActiveSubCollapse.value = indexes[0][0];
+      activeSubCollapse.value = indexes[0][1];
+    }
+  },
+  {
+    immediate: true,
+  }
+);
 </script>
 
 <style lang="scss" scoped>

+ 120 - 43
src/components/Layout/data.ts

@@ -1,4 +1,4 @@
-import type { RouteLocationRaw } from "vue-router";
+import type { RouteLocationNamedRaw, RouteLocationRaw } from "vue-router";
 
 export interface TOP_DATA {
   id: number;
@@ -11,20 +11,48 @@ export const topData: TOP_DATA[] = [
   {
     id: 2,
     name: "Visit",
-    routeParams: { name: "Visit", params: { id: 8 } },
+    routeParams: { name: "Visit", params: { id: "8" } },
     children: [
-      { routeParams: "/Layout/Visit/8", id: 2.8, name: "Calendar" },
       {
-        routeParams: "/Layout/Visit/1",
+        routeParams: { name: "Visit", params: { id: "8" } },
+        id: 2.8,
+        name: "Calendar",
+      },
+      {
+        routeParams: { name: "Visit", params: { id: "1" } },
         id: 2.1,
         name: "Hours, Direction & Admission",
       },
-      { routeParams: "/Layout/Visit/2", id: 2.2, name: "Reservation" },
-      { routeParams: "/Layout/Visit/3", id: 2.3, name: "Floor Plans" },
-      { routeParams: "/Layout/Visit/4", id: 2.4, name: "Audio Guide & Tour" },
-      { routeParams: "/Layout/Visit/5", id: 2.5, name: "Accessibility" },
-      { routeParams: "/Layout/Visit/6", id: 2.6, name: "Café & Shop" },
-      { routeParams: "/Layout/Visit/7", id: 2.7, name: "Visitor Guidelines" },
+      {
+        routeParams: { name: "Visit", params: { id: "2" } },
+        id: 2.2,
+        name: "Reservation",
+      },
+      {
+        routeParams: { name: "Visit", params: { id: "3" } },
+        id: 2.3,
+        name: "Floor Plans",
+      },
+      {
+        routeParams: { name: "Visit", params: { id: "4" } },
+        id: 2.4,
+        name: "Audio Guide & Tour",
+      },
+      {
+        routeParams: { name: "Visit", params: { id: "5" } },
+        id: 2.5,
+        name: "Accessibility",
+      },
+      {
+        routeParams: { name: "Visit", params: { id: "6" } },
+        id: 2.6,
+        name: "Café & Shop",
+      },
+      {
+        routeParams: { name: "Visit", params: { id: "7" } },
+        id: 2.7,
+        name: "Visitor Guidelines",
+      },
     ],
   },
   {
@@ -33,22 +61,22 @@ export const topData: TOP_DATA[] = [
     routeParams: { name: "Exhibitions" },
     children: [
       {
-        routeParams: "/Layout/Exhibitions/Current",
+        routeParams: { name: "ExhibitionsCurrent" },
         id: 3.1,
         name: "Current Exhibitions",
       },
       {
-        routeParams: "/Layout/Exhibitions/Permanent",
+        routeParams: { name: "ExhibitionsPermanent" },
         id: 3.2,
         name: "Permanent Exhibitions",
       },
       {
-        routeParams: "/Layout/Exhibitions/Past",
+        routeParams: { name: "ExhibitionsPast" },
         id: 3.3,
         name: "Past Exhibitions",
       },
       {
-        routeParams: "/Layout/Exhibitions/Overseas",
+        routeParams: { name: "ExhibitionsOverseas" },
         id: 3.4,
         name: "Overseas Exhibitions",
       },
@@ -57,56 +85,66 @@ export const topData: TOP_DATA[] = [
   {
     id: 4,
     name: "Collections",
-    routeParams: { name: "Collections", params: { type: "Bronzes" } },
+    routeParams: { name: "Collections" },
     children: [
-      { routeParams: "/Layout/Collections/Bronzes", id: 4.1, name: "Bronzes" },
       {
-        routeParams: "/Layout/Collections/Ceramics",
-        id: 4.2,
+        routeParams: { name: "CollectionsList", params: { name: "Bronzes" } },
+        id: 4.01,
+        name: "Bronzes",
+      },
+      {
+        routeParams: { name: "CollectionsList", params: { name: "Ceramics" } },
+        id: 4.02,
         name: "Ceramics",
       },
       {
-        routeParams: "/Layout/Collections/Buddhist",
-        id: 4.3,
+        routeParams: { name: "CollectionsList", params: { name: "Buddhist" } },
+        id: 4.03,
         name: "Buddhist Statues",
       },
       {
-        routeParams: "/Layout/Collections/Jadewares",
-        id: 4.4,
+        routeParams: { name: "CollectionsList", params: { name: "Jadewares" } },
+        id: 4.04,
         name: "Jadewares",
       },
       {
-        routeParams: "/Layout/Collections/Calligraphies",
-        id: 4.5,
+        routeParams: {
+          name: "CollectionsList",
+          params: { name: "Calligraphies" },
+        },
+        id: 4.05,
         name: "Calligraphies",
       },
       {
-        routeParams: "/Layout/Collections/Paintings",
-        id: 4.6,
+        routeParams: { name: "CollectionsList", params: { name: "Paintings" } },
+        id: 4.06,
         name: "Paintings",
       },
       {
-        routeParams: "/Layout/Collections/Gold",
-        id: 4.7,
+        routeParams: { name: "CollectionsList", params: { name: "Gold" } },
+        id: 4.07,
         name: "Gold & Silverwares",
       },
       {
-        routeParams: "/Layout/Collections/Coins",
-        id: 4.8,
+        routeParams: { name: "CollectionsList", params: { name: "Coins" } },
+        id: 4.08,
         name: "Coins & Banknotes",
       },
       {
-        routeParams: "/Layout/Collections/Brocades",
-        id: 4.9,
+        routeParams: { name: "CollectionsList", params: { name: "Brocades" } },
+        id: 4.09,
         name: "Brocades & Embroideries",
       },
       {
-        routeParams: "/Layout/Collections/Cultural",
+        routeParams: { name: "CollectionsList", params: { name: "Cultural" } },
         id: 4.1,
         name: "Cultural Supplies",
       },
       {
-        routeParams: "/Layout/Collections/Miscellaneous",
+        routeParams: {
+          name: "CollectionsList",
+          params: { name: "Miscellaneous" },
+        },
         id: 4.11,
         name: "Miscellaneous",
       },
@@ -140,12 +178,12 @@ export const topData: TOP_DATA[] = [
     routeParams: { name: "Publications" },
     children: [
       {
-        routeParams: "/Layout/Publications/Magazines",
+        routeParams: { name: "PuMagazines" },
         id: 6.1,
         name: "Magazines",
       },
       {
-        routeParams: "/Layout/Publications/Catalogues",
+        routeParams: { name: "PuCatalogues" },
         id: 6.2,
         name: "Exhibition Catalogues",
       },
@@ -184,12 +222,12 @@ export const topData: TOP_DATA[] = [
         name: "Ways to Give",
         children: [
           {
-            routeParams: "/Layout/Join/GiveInfo?id=4",
+            routeParams: { name: "JoinGi" },
             id: 7.21,
             name: "Individuals",
           },
           {
-            routeParams: "/Layout/Join/GiveInfo?id=5",
+            routeParams: { name: "JoinGi" },
             id: 7.22,
             name: "Corporations Institutions",
           },
@@ -200,28 +238,67 @@ export const topData: TOP_DATA[] = [
   {
     id: 8,
     name: "About",
-    routeParams: { name: "About" },
+    routeParams: { name: "AboutIndex" },
     children: [
       {
-        routeParams: { name: "About", query: { scroll: 0 } },
+        routeParams: { name: "AboutIndex", query: { scroll: "0" } },
         id: 8.1,
         name: "From the Director",
       },
       {
-        routeParams: { name: "About", query: { scroll: 1 } },
+        routeParams: { name: "AboutIndex", query: { scroll: "1" } },
         id: 8.2,
         name: "History",
       },
       {
-        routeParams: { name: "About", query: { scroll: 2 } },
+        routeParams: { name: "AboutIndex", query: { scroll: "2" } },
         id: 8.3,
         name: "Partners & Connections",
       },
       {
-        routeParams: { name: "About", query: { scroll: 3 } },
+        routeParams: { name: "AboutIndex", query: { scroll: "3" } },
         id: 8.4,
         name: "Contact",
       },
     ],
   },
 ];
+
+export const findIndexes = (
+  data: TOP_DATA[],
+  targetName: string,
+  query: Record<string, any>,
+  params: Record<string, any>,
+  parentIndex: number[] = []
+) => {
+  const result: number[][] = [];
+
+  for (let i = 0; i < data.length; i++) {
+    const item = data[i];
+    const currentIndex = [...parentIndex, item.id];
+    const routeParams = item.routeParams as RouteLocationNamedRaw | undefined;
+
+    if (
+      routeParams?.name === targetName &&
+      (routeParams?.query
+        ? JSON.stringify(routeParams.query) === JSON.stringify(query)
+        : true) &&
+      (routeParams?.params
+        ? JSON.stringify(routeParams.params) === JSON.stringify(params)
+        : true)
+    ) {
+      if (currentIndex.length === 2) {
+        result.push(currentIndex);
+        return result;
+      }
+    }
+
+    if (item.children) {
+      result.push(
+        ...findIndexes(item.children, targetName, query, params, currentIndex)
+      );
+    }
+  }
+
+  return result;
+};

+ 1 - 1
src/components/Layout/index.vue

@@ -76,7 +76,7 @@
     @goToCNWeb="goToCNWeb"
     @openSearch="searchVisible = true"
   />
-  <Search v-model:visible="searchVisible" />
+  <Search v-model:visible="searchVisible" @confirm="sidebarVisible = false" />
 </template>
 
 <script lang="ts" setup>

+ 15 - 0
src/views/Collections/Detail/index.vue

@@ -8,6 +8,12 @@
           fit="contain"
           :src="url"
           style="display: block"
+          @click="
+            () => {
+              curImgIndex = idx;
+              showPreview = true;
+            }
+          "
         />
       </VanSwipeItem>
     </VanSwipe>
@@ -28,6 +34,13 @@
       <div v-for="item in rtf" :key="item.id" v-html="item.txt" />
     </div>
   </div>
+
+  <VanImagePreview
+    closeable
+    v-model:show="showPreview"
+    :images="imgList"
+    :start-position="curPreviewIdx"
+  />
 </template>
 
 <script setup lang="ts">
@@ -45,6 +58,8 @@ const curImgIndex = ref(0);
 const imgList = computed(() =>
   detail.value ? detail.value.files.map((item) => baseUrl + item.filePath) : []
 );
+const showPreview = ref(false);
+const curPreviewIdx = ref(0);
 
 const getDetail = async () => {
   try {

+ 39 - 24
src/views/Collections/List/index.vue

@@ -27,7 +27,7 @@
               })
             "
           >
-            <LazyImg :url="url" />
+            <VanImage :src="url" width="100%" :height="item.imgHeight + 'px'" />
             <p class="limit-line line-2">{{ item.name }}</p>
           </div>
         </template>
@@ -37,8 +37,8 @@
 </template>
 
 <script lang="ts" setup>
-import { computed, onMounted, ref, watch } from "vue";
-import { LazyImg, Waterfall } from "vue-waterfall-plugin-next";
+import { computed, nextTick, ref, watch } from "vue";
+import { Waterfall } from "vue-waterfall-plugin-next";
 import { getBaseURL } from "@dage/service";
 import { getCollectionListApi, type CollectionListItem } from "@/api";
 import PageBanner from "@/components/PageBanner.vue";
@@ -46,6 +46,7 @@ import { PaginationType, usePagination } from "@/utils/usePagination";
 import { useCollectionStore } from "@/stores/collection";
 import "vue-waterfall-plugin-next/dist/style.css";
 import { useRoute } from "vue-router";
+import { NAV_LIST } from "../constants";
 
 const baseUrl = getBaseURL();
 const collectionStore = useCollectionStore();
@@ -53,10 +54,13 @@ const route = useRoute();
 // 当前页图片数据是否全部加载完成
 const rendering = ref(false);
 const list = ref<any[]>([]);
+const realType = computed(
+  () => NAV_LIST.find((i) => i.type === (route.params.name as string))?.name
+);
 const bgImg = computed(
   () =>
     baseUrl +
-    collectionStore.thumbList.find((i) => i.type === route.params.name)?.thumb
+    collectionStore.thumbList.find((i) => i.type === realType.value)?.thumb
 );
 
 const {
@@ -70,7 +74,7 @@ const {
     rendering.value = true;
 
     return getCollectionListApi({
-      type: route.params.name as string,
+      type: realType.value,
       ...params,
     });
   },
@@ -82,35 +86,46 @@ const loading = computed(() => rendering.value || fetchLoading.value);
 
 const onLoad = () => {
   // 检查用户是否滚动到页面底部
-  if (!loading.value && !noMore.value) {
+  if (!loading.value && !noMore.value && !rendering.value) {
     pageNum.value++;
     getList();
   }
 };
 
-onMounted(() => {
-  getList();
-});
+watch(
+  route,
+  () => {
+    pageNum.value = 1;
+    list.value = [];
+    getList();
+  },
+  {
+    immediate: true,
+  }
+);
 
-watch(sourceList, (v) => {
+const ITEM_WIDTH = window.innerWidth / 2 - 60;
+watch(sourceList, async (v) => {
   if (!v.length) return;
 
-  const promises = v.map(async (i) => {
-    const url = baseUrl + i.thumb;
-    const ratio = await getImgRatio(url);
+  rendering.value = true;
 
-    list.value.push({
-      ...i,
-      imgHeight: 303 * ratio,
-      src: url,
-    });
-
-    return i;
-  });
+  try {
+    for (const i of v) {
+      const url = baseUrl + i.thumb;
+      const ratio = await getImgRatio(url);
 
-  Promise.allSettled(promises).then(() => {
-    rendering.value = false;
-  });
+      list.value.push({
+        ...i,
+        imgHeight: ITEM_WIDTH * ratio,
+        src: url,
+      });
+    }
+  } finally {
+    nextTick(() => {
+      rendering.value = false;
+    });
+  }
 });
 
 const getImgRatio: (url: string) => Promise<number> = (url: string) => {

+ 16 - 0
src/views/Collections/constants.ts

@@ -0,0 +1,16 @@
+export const NAV_LIST = [
+  {
+    name: "Bronzes",
+    type: "Bronzes",
+  },
+  { name: "Ceramics", type: "Ceramics" },
+  { name: "Buddhist Statues", type: "Buddhist" },
+  { name: "Jadewares", type: "Jadewares" },
+  { name: "Calligraphies", type: "Calligraphies" },
+  { name: "Paintings", type: "Paintings" },
+  { name: "Gold & Silverwares", type: "Gold" },
+  { name: "Coins & Banknotes", type: "Coins" },
+  { name: "Brocades & Embroideries", type: "Brocades" },
+  { name: "Cultural Supplies", type: "Cultural" },
+  { name: "Miscellaneous", type: "Miscellaneous" },
+];

+ 1 - 16
src/views/Collections/index.vue

@@ -38,6 +38,7 @@ import { useCollectionStore } from "@/stores/collection";
 import BannerImg from "./images/bannerC.png";
 import { useBaseStore } from "@/stores/base";
 import { computed } from "vue";
+import { NAV_LIST } from "./constants";
 
 const baseUrl = getBaseURL();
 const baseStore = useBaseStore();
@@ -48,22 +49,6 @@ const bannerUrl = computed(() => {
   );
 });
 const collectionStore = useCollectionStore();
-const NAV_LIST = [
-  {
-    name: "Bronzes",
-    type: "Bronzes",
-  },
-  { name: "Ceramics", type: "Ceramics" },
-  { name: "Buddhist Statues", type: "Buddhist" },
-  { name: "Jadewares", type: "Jadewares" },
-  { name: "Calligraphies", type: "Calligraphies" },
-  { name: "Paintings", type: "Paintings" },
-  { name: "Gold & Silverwares", type: "Gold" },
-  { name: "Coins & Banknotes", type: "Coins" },
-  { name: "Brocades & Embroideries", type: "Brocades" },
-  { name: "Cultural Supplies", type: "Cultural" },
-  { name: "Miscellaneous", type: "Miscellaneous" },
-];
 </script>
 
 <style lang="scss" scoped>

+ 2 - 2
src/views/Events/Detail/index.vue

@@ -10,7 +10,7 @@
         </p>
       </div>
 
-      <div class="events-detail-directory">
+      <div v-if="detail?.titleArr.length" class="events-detail-directory">
         <p
           v-for="(title, idx) in detail?.titleArr"
           :key="title"
@@ -72,7 +72,7 @@ const getDetail = async () => {
 
     detail.value = {
       ...data,
-      titleArr: JSON.parse(data.rtfTitle),
+      titleArr: JSON.parse(data.rtfTitle).filter((i: string) => Boolean(i)),
       txtArr: data.rtf ? JSON.parse(data.rtf).txtArr : [],
     };
 

+ 7 - 15
src/views/Events/index.scss

@@ -5,6 +5,7 @@
   }
   &-item {
     margin-bottom: 30px;
+    padding: 0 10px;
     display: flex;
     flex-direction: column;
     align-items: center;
@@ -17,24 +18,15 @@
     background-position: center;
 
     h3 {
-      position: relative;
-      margin-bottom: 30px;
-      padding-bottom: 16px;
       font-weight: bold;
       font-size: 32px;
       text-align: center;
-
-      &::after {
-        content: "";
-        position: absolute;
-        display: inline-block;
-        width: 60%;
-        bottom: 0;
-        left: 50%;
-        transform: translateX(-50%);
-        height: 4px;
-        background-color: #fff;
-      }
+    }
+    &__driver {
+      margin: 16px auto 30px;
+      width: 60%;
+      height: 4px;
+      background-color: #fff;
     }
     i {
       font-style: italic;

+ 4 - 3
src/views/Events/index.vue

@@ -19,7 +19,8 @@
         }"
         @click="$router.push({ name: 'EventsDetail', params: { id: item.id } })"
       >
-        <h3>{{ item.name }}</h3>
+        <h3 class="limit-line line-2">{{ item.name }}</h3>
+        <div class="events-item__driver" />
         <i>{{ item.date }}</i>
       </div>
     </VanList>
@@ -30,7 +31,7 @@
 import PageBanner from "@/components/PageBanner.vue";
 import BannerImg from "./images/banner.png";
 import { getBaseURL } from "@dage/service";
-import { usePagination } from "@/utils/usePagination";
+import { PaginationType, usePagination } from "@/utils/usePagination";
 import { getEventListApi, type EventListItem } from "@/api";
 import { computed, onMounted } from "vue";
 import { getFormatDate } from "@/utils/date";
@@ -46,7 +47,7 @@ const {
   return getEventListApi({
     ...params,
   });
-});
+}, PaginationType.CONCAT);
 
 const list = computed(() =>
   _list.value.map((i) => ({

+ 7 - 4
src/views/Exhibitions/Detail/index.vue

@@ -29,11 +29,10 @@
         </Card>
       </div>
 
-      <div class="detail-objects">
+      <div v-if="detail && detail.exhibitsFile.length" class="detail-objects">
         <page-label>Exhibition Objects</page-label>
 
         <ImgSwiper
-          v-if="detail"
           :list="
             detail.exhibitsFile
               .slice(0, 9)
@@ -51,10 +50,13 @@
         </div>
       </div>
 
-      <div class="detail-galleries">
+      <div
+        v-if="detail && detail.exhibitionFile.length"
+        class="detail-galleries"
+      >
         <page-label>Exhibition Galleries</page-label>
 
-        <van-swipe v-if="detail" class="detail-galleries-swipe" :loop="false">
+        <van-swipe class="detail-galleries-swipe" :loop="false">
           <van-swipe-item
             v-for="(item, idx) in detail.exhibitionFile.slice(0, 5)"
             :key="item.id"
@@ -92,6 +94,7 @@
   </div>
 
   <VanImagePreview
+    closeable
     v-model:show="showPreview"
     :images="previewList"
     :start-position="curPreviewIdx"

+ 1 - 0
src/views/Exhibitions/Galleries/index.vue

@@ -18,6 +18,7 @@
   </div>
 
   <VanImagePreview
+    closeable
     v-model:show="showPreview"
     :images="previewList"
     :start-position="curPreviewIdx"

+ 1 - 0
src/views/Exhibitions/Objects/index.vue

@@ -18,6 +18,7 @@
   </div>
 
   <VanImagePreview
+    closeable
     v-model:show="showPreview"
     :images="previewList"
     :start-position="curPreviewIdx"

+ 3 - 1
src/views/Home/components/Connections.vue

@@ -10,7 +10,9 @@
       </li>
     </ul>
 
-    <div class="more">See More</div>
+    <div class="more" @click="$router.push({ name: 'AboutLink' })">
+      See More
+    </div>
   </div>
 </template>
 

+ 12 - 1
src/views/Home/components/VisitInfo.vue

@@ -8,7 +8,11 @@
     </div>
 
     <ul>
-      <li v-for="(item, idx) in LIST2" :key="idx">
+      <li
+        v-for="(item, idx) in LIST2"
+        :key="idx"
+        @click="$router.push({ name: 'Visit', params: { id: item.idx } })"
+      >
         <img :src="item.icon" />
         <p>{{ item.label }}</p>
       </li>
@@ -46,26 +50,32 @@ const LIST2 = [
   {
     icon: ICON1,
     label: "Hours, Direction & Admission",
+    idx: 1,
   },
   {
     icon: ICON2,
     label: "Floor Plans",
+    idx: 3,
   },
   {
     icon: ICON3,
     label: "Audio Guide & Tour",
+    idx: 4,
   },
   {
     icon: ICON4,
     label: "Accessibility",
+    idx: 5,
   },
   {
     icon: ICON5,
     label: "Café & Shop",
+    idx: 6,
   },
   {
     icon: ICON6,
     label: "Visitor Guidelines",
+    idx: 7,
   },
 ];
 </script>
@@ -110,6 +120,7 @@ const LIST2 = [
         font-size: 24px;
         line-height: 24px;
         color: #c1aa7b;
+        text-align: center;
       }
     }
   }

+ 19 - 12
src/views/Publications/Magazines/components/TimeLinePanel.vue

@@ -43,22 +43,19 @@
 </template>
 
 <script lang="ts" setup>
-import { getPublishListApi, type PubicationItem } from "@/api";
+import {
+  getMagazineYearListApi,
+  getPublishListApi,
+  type PubicationItem,
+} from "@/api";
 import { PaginationType, usePagination } from "@/utils/usePagination";
 import { getBaseURL } from "@dage/service";
 import { debounce } from "lodash-unified";
 import { onMounted, ref } from "vue";
 
-const MIN_YEAR = 2017;
-const date = new Date();
-const year = date.getFullYear();
-const years = Array.from(
-  { length: year - MIN_YEAR + 1 },
-  (_, index) => year - index
-);
-
 const baseUrl = getBaseURL();
-const activeYear = ref(years[0]);
+const years = ref<string[]>([]);
+const activeYear = ref("");
 const infoInd = ref(0);
 const info = ref<any>({});
 const opacity = ref(1);
@@ -85,15 +82,25 @@ const { pageNum, noMore, noData, loading, list, getList } =
   );
 
 onMounted(() => {
-  getList();
+  getMagazineYearList();
 });
 
+const getMagazineYearList = async () => {
+  const data = await getMagazineYearListApi();
+  years.value = data;
+
+  if (data.length) {
+    activeYear.value = data[0];
+    getList();
+  }
+};
+
 const debounceSearch = debounce(() => {
   pageNum.value = 1;
   getList();
 }, 500);
 
-const handleYear = (year: number) => {
+const handleYear = (year: string) => {
   activeYear.value = year;
   debounceSearch();
 };

+ 48 - 37
src/views/Search/index.vue

@@ -24,7 +24,7 @@
         <ul>
           <li
             v-for="item in list"
-            :key="item.id"
+            :key="item.module + item.id"
             class="search-item"
             @click="handleClick(item)"
           >
@@ -52,7 +52,7 @@
 </template>
 
 <script lang="ts" setup>
-import { onMounted, ref, watch } from "vue";
+import { ref, watch } from "vue";
 import PageBanner from "@/components/PageBanner.vue";
 import PageNav from "@/components/PageNav.vue";
 import BannerImg from "./images/bannerRes.png";
@@ -119,44 +119,48 @@ const getList = async () => {
     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[] = [];
+    let stack: any[] = list.value.length ? cloneDeep(list.value) : [];
 
-    stack = FRONT_DATA.filter((i) => {
-      // 判断是否满足查询条件
-      const res = reg.test(i.rtf);
-      const res2 = reg.test(i.name);
-      if (!res && !res2) return false;
+    if (!stack.length) {
+      stack = cloneDeep(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;
-      }
+          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;
+          // 如果非查询全部,先判断类型是否一致
+          if (
+            activeTabbar.value !== 0 &&
+            tabbar.value[activeTabbar.value]?.module !== i.module
+          )
+            return false;
 
-      return true;
-    }).map((i) => ({
-      ...i,
-      rtf: getAbstract(i.rtf),
-    }));
+          return true;
+        })
+        .map((i) => ({
+          ...i,
+          rtf: getAbstract(i.rtf),
+        }));
+    }
 
     // 需要请求接口的tab索引
     const BACKEND_MODULE_INDEX = [0, 2, 3, 4, 5, 8];
@@ -180,7 +184,7 @@ const getList = async () => {
     });
 
     if (BACKEND_MODULE_INDEX.includes(activeTabbar.value)) {
-      stack.unshift(
+      stack.push(
         ...data.list.records.map((i: SearchItem) => ({
           ...i,
           thumb: baseUrl + i.thumb,
@@ -206,6 +210,12 @@ const getList = async () => {
   }
 };
 
+const reset = () => {
+  pageNum.value = 1;
+  noMore.value = false;
+  list.value = [];
+};
+
 const onLoad = () => {
   if (noMore.value) return;
 
@@ -284,6 +294,7 @@ const handleClick = (item: SearchItem) => {
 watch(
   route,
   () => {
+    reset();
     getList();
   },
   {