Parcourir la source

feat: 湖南省博物馆 - 遇见庞贝展

chenlei il y a 14 heures
Parent
commit
c4d1838fd2
84 fichiers modifiés avec 1039 ajouts et 45 suppressions
  1. 2 0
      components.d.ts
  2. BIN
      public/images/pb/1000/1.尤利娅·克劳狄娅公主铜像 拷贝.jpg
  3. BIN
      public/images/pb/1000/10.神圣景观湿壁画 拷贝.jpg
  4. BIN
      public/images/pb/1000/11.半球链式金手镯 拷贝.jpg
  5. BIN
      public/images/pb/1000/12.圣杯形铜调酒器 拷贝.jpg
  6. BIN
      public/images/pb/1000/13.贝壳形铜杯 拷贝.jpg
  7. BIN
      public/images/pb/1000/14.铜罐 拷贝.jpg
  8. BIN
      public/images/pb/1000/15.铜壶 拷贝.jpg
  9. BIN
      public/images/pb/1000/16.托食盘人物铜像 拷贝.png
  10. BIN
      public/images/pb/1000/17.铁保险箱 拷贝.jpg
  11. BIN
      public/images/pb/1000/18.大理石面具 拷贝.jpg
  12. BIN
      public/images/pb/1000/19.大理石喷泉装饰 拷贝.jpg
  13. BIN
      public/images/pb/1000/2.着托加袍的阿努比斯大理石雕像 拷贝.jpg
  14. BIN
      public/images/pb/1000/20.海豚形大理石喷泉装饰 拷贝.jpg
  15. BIN
      public/images/pb/1000/21.维纳斯女神大理石雕塑 拷贝.jpg
  16. BIN
      public/images/pb/1000/22.庞贝遇难者遗骸石膏铸型 拷贝.jpg
  17. BIN
      public/images/pb/1000/3.杜萨雷斯大理石祭坛 拷贝.jpg
  18. BIN
      public/images/pb/1000/4.女性雕像 拷贝.png
  19. BIN
      public/images/pb/1000/5.塞勒诺大理石头像 拷贝.jpg
  20. BIN
      public/images/pb/1000/6.科林斯式大理石柱头 拷贝.jpg
  21. BIN
      public/images/pb/1000/7.描绘角斗士盔甲的湿壁画 拷贝.jpg
  22. BIN
      public/images/pb/1000/8.鱼篮湿壁画 拷贝.jpg
  23. BIN
      public/images/pb/1000/9.野味湿壁画 拷贝.jpg
  24. BIN
      public/images/pb/2000/1.尤利娅·克劳狄娅公主铜像 拷贝.jpg
  25. BIN
      public/images/pb/2000/10.神圣景观湿壁画 拷贝.jpg
  26. BIN
      public/images/pb/2000/11.半球链式金手镯 拷贝.jpg
  27. BIN
      public/images/pb/2000/12.圣杯形铜调酒器 拷贝.jpg
  28. BIN
      public/images/pb/2000/13.贝壳形铜杯 拷贝.jpg
  29. BIN
      public/images/pb/2000/14.铜罐 拷贝.jpg
  30. BIN
      public/images/pb/2000/15.铜壶 拷贝.jpg
  31. BIN
      public/images/pb/2000/16.托食盘人物铜像 拷贝.png
  32. BIN
      public/images/pb/2000/17.铁保险箱 拷贝.jpg
  33. BIN
      public/images/pb/2000/18.大理石面具 拷贝.jpg
  34. BIN
      public/images/pb/2000/19.大理石喷泉装饰 拷贝.jpg
  35. BIN
      public/images/pb/2000/2.着托加袍的阿努比斯大理石雕像 拷贝.jpg
  36. BIN
      public/images/pb/2000/20.海豚形大理石喷泉装饰 拷贝.jpg
  37. BIN
      public/images/pb/2000/21.维纳斯女神大理石雕塑 拷贝.jpg
  38. BIN
      public/images/pb/2000/22.庞贝遇难者遗骸石膏铸型 拷贝.jpg
  39. BIN
      public/images/pb/2000/3.杜萨雷斯大理石祭坛 拷贝.jpg
  40. BIN
      public/images/pb/2000/4.女性雕像 拷贝.png
  41. BIN
      public/images/pb/2000/5.塞勒诺大理石头像 拷贝.jpg
  42. BIN
      public/images/pb/2000/6.科林斯式大理石柱头 拷贝.jpg
  43. BIN
      public/images/pb/2000/7.描绘角斗士盔甲的湿壁画 拷贝.jpg
  44. BIN
      public/images/pb/2000/8.鱼篮湿壁画 拷贝.jpg
  45. BIN
      public/images/pb/2000/9.野味湿壁画 拷贝.jpg
  46. BIN
      public/images/pb/500/1.尤利娅·克劳狄娅公主铜像 拷贝.jpg
  47. BIN
      public/images/pb/500/10.神圣景观湿壁画 拷贝.jpg
  48. BIN
      public/images/pb/500/11.半球链式金手镯 拷贝.jpg
  49. BIN
      public/images/pb/500/12.圣杯形铜调酒器 拷贝.jpg
  50. BIN
      public/images/pb/500/13.贝壳形铜杯 拷贝.jpg
  51. BIN
      public/images/pb/500/14.铜罐 拷贝.jpg
  52. BIN
      public/images/pb/500/15.铜壶 拷贝.jpg
  53. BIN
      public/images/pb/500/16.托食盘人物铜像 拷贝.png
  54. BIN
      public/images/pb/500/17.铁保险箱 拷贝.jpg
  55. BIN
      public/images/pb/500/18.大理石面具 拷贝.jpg
  56. BIN
      public/images/pb/500/19.大理石喷泉装饰 拷贝.jpg
  57. BIN
      public/images/pb/500/2.着托加袍的阿努比斯大理石雕像 拷贝.jpg
  58. BIN
      public/images/pb/500/20.海豚形大理石喷泉装饰 拷贝.jpg
  59. BIN
      public/images/pb/500/21.维纳斯女神大理石雕塑 拷贝.jpg
  60. BIN
      public/images/pb/500/22.庞贝遇难者遗骸石膏铸型 拷贝.jpg
  61. BIN
      public/images/pb/500/3.杜萨雷斯大理石祭坛 拷贝.jpg
  62. BIN
      public/images/pb/500/4.女性雕像 拷贝.png
  63. BIN
      public/images/pb/500/5.塞勒诺大理石头像 拷贝.jpg
  64. BIN
      public/images/pb/500/6.科林斯式大理石柱头 拷贝.jpg
  65. BIN
      public/images/pb/500/7.描绘角斗士盔甲的湿壁画 拷贝.jpg
  66. BIN
      public/images/pb/500/8.鱼篮湿壁画 拷贝.jpg
  67. BIN
      public/images/pb/500/9.野味湿壁画 拷贝.jpg
  68. 3 22
      src/index/app.scss
  69. BIN
      src/index/assets/images/close.png
  70. BIN
      src/index/components/info-popup/images/1.jpg
  71. 145 0
      src/index/components/info-popup/index.vue
  72. 200 0
      src/index/views/home/components/ant-popup/constants.ts
  73. 151 0
      src/index/views/home/components/ant-popup/detail.vue
  74. BIN
      src/index/views/home/components/ant-popup/images/bd.png
  75. BIN
      src/index/views/home/components/ant-popup/images/icon.png
  76. 272 0
      src/index/views/home/components/ant-popup/index.vue
  77. BIN
      src/index/views/home/components/chapter-popup/images/1.jpg
  78. BIN
      src/index/views/home/components/chapter-popup/images/2.jpg
  79. BIN
      src/index/views/home/components/chapter-popup/images/3.jpg
  80. BIN
      src/index/views/home/components/chapter-popup/images/4.jpg
  81. 198 0
      src/index/views/home/components/chapter-popup/index.vue
  82. 3 4
      src/index/views/home/components/menu/index.pb.scss
  83. 47 13
      src/index/views/home/components/menu/index.pb.tsx
  84. 18 6
      src/index/views/home/index.pb.tsx

+ 2 - 0
components.d.ts

@@ -9,6 +9,8 @@ declare module 'vue' {
   export interface GlobalComponents {
     ElDialog: typeof import('element-plus/es')['ElDialog']
     ElImage: typeof import('element-plus/es')['ElImage']
+    ElInput: typeof import('element-plus/es')['ElInput']
+    ElScrollbar: typeof import('element-plus/es')['ElScrollbar']
     ElTooltip: typeof import('element-plus/es')['ElTooltip']
     RouterLink: typeof import('vue-router')['RouterLink']
     RouterView: typeof import('vue-router')['RouterView']

BIN
public/images/pb/1000/1.尤利娅·克劳狄娅公主铜像 拷贝.jpg


BIN
public/images/pb/1000/10.神圣景观湿壁画 拷贝.jpg


BIN
public/images/pb/1000/11.半球链式金手镯 拷贝.jpg


BIN
public/images/pb/1000/12.圣杯形铜调酒器 拷贝.jpg


BIN
public/images/pb/1000/13.贝壳形铜杯 拷贝.jpg


BIN
public/images/pb/1000/14.铜罐 拷贝.jpg


BIN
public/images/pb/1000/15.铜壶 拷贝.jpg


BIN
public/images/pb/1000/16.托食盘人物铜像 拷贝.png


BIN
public/images/pb/1000/17.铁保险箱 拷贝.jpg


BIN
public/images/pb/1000/18.大理石面具 拷贝.jpg


BIN
public/images/pb/1000/19.大理石喷泉装饰 拷贝.jpg


BIN
public/images/pb/1000/2.着托加袍的阿努比斯大理石雕像 拷贝.jpg


BIN
public/images/pb/1000/20.海豚形大理石喷泉装饰 拷贝.jpg


BIN
public/images/pb/1000/21.维纳斯女神大理石雕塑 拷贝.jpg


BIN
public/images/pb/1000/22.庞贝遇难者遗骸石膏铸型 拷贝.jpg


BIN
public/images/pb/1000/3.杜萨雷斯大理石祭坛 拷贝.jpg


BIN
public/images/pb/1000/4.女性雕像 拷贝.png


BIN
public/images/pb/1000/5.塞勒诺大理石头像 拷贝.jpg


BIN
public/images/pb/1000/6.科林斯式大理石柱头 拷贝.jpg


BIN
public/images/pb/1000/7.描绘角斗士盔甲的湿壁画 拷贝.jpg


BIN
public/images/pb/1000/8.鱼篮湿壁画 拷贝.jpg


BIN
public/images/pb/1000/9.野味湿壁画 拷贝.jpg


BIN
public/images/pb/2000/1.尤利娅·克劳狄娅公主铜像 拷贝.jpg


BIN
public/images/pb/2000/10.神圣景观湿壁画 拷贝.jpg


BIN
public/images/pb/2000/11.半球链式金手镯 拷贝.jpg


BIN
public/images/pb/2000/12.圣杯形铜调酒器 拷贝.jpg


BIN
public/images/pb/2000/13.贝壳形铜杯 拷贝.jpg


BIN
public/images/pb/2000/14.铜罐 拷贝.jpg


BIN
public/images/pb/2000/15.铜壶 拷贝.jpg


BIN
public/images/pb/2000/16.托食盘人物铜像 拷贝.png


BIN
public/images/pb/2000/17.铁保险箱 拷贝.jpg


BIN
public/images/pb/2000/18.大理石面具 拷贝.jpg


BIN
public/images/pb/2000/19.大理石喷泉装饰 拷贝.jpg


BIN
public/images/pb/2000/2.着托加袍的阿努比斯大理石雕像 拷贝.jpg


BIN
public/images/pb/2000/20.海豚形大理石喷泉装饰 拷贝.jpg


BIN
public/images/pb/2000/21.维纳斯女神大理石雕塑 拷贝.jpg


BIN
public/images/pb/2000/22.庞贝遇难者遗骸石膏铸型 拷贝.jpg


BIN
public/images/pb/2000/3.杜萨雷斯大理石祭坛 拷贝.jpg


BIN
public/images/pb/2000/4.女性雕像 拷贝.png


BIN
public/images/pb/2000/5.塞勒诺大理石头像 拷贝.jpg


BIN
public/images/pb/2000/6.科林斯式大理石柱头 拷贝.jpg


BIN
public/images/pb/2000/7.描绘角斗士盔甲的湿壁画 拷贝.jpg


BIN
public/images/pb/2000/8.鱼篮湿壁画 拷贝.jpg


BIN
public/images/pb/2000/9.野味湿壁画 拷贝.jpg


BIN
public/images/pb/500/1.尤利娅·克劳狄娅公主铜像 拷贝.jpg


BIN
public/images/pb/500/10.神圣景观湿壁画 拷贝.jpg


BIN
public/images/pb/500/11.半球链式金手镯 拷贝.jpg


BIN
public/images/pb/500/12.圣杯形铜调酒器 拷贝.jpg


BIN
public/images/pb/500/13.贝壳形铜杯 拷贝.jpg


BIN
public/images/pb/500/14.铜罐 拷贝.jpg


BIN
public/images/pb/500/15.铜壶 拷贝.jpg


BIN
public/images/pb/500/16.托食盘人物铜像 拷贝.png


BIN
public/images/pb/500/17.铁保险箱 拷贝.jpg


BIN
public/images/pb/500/18.大理石面具 拷贝.jpg


BIN
public/images/pb/500/19.大理石喷泉装饰 拷贝.jpg


BIN
public/images/pb/500/2.着托加袍的阿努比斯大理石雕像 拷贝.jpg


BIN
public/images/pb/500/20.海豚形大理石喷泉装饰 拷贝.jpg


BIN
public/images/pb/500/21.维纳斯女神大理石雕塑 拷贝.jpg


BIN
public/images/pb/500/22.庞贝遇难者遗骸石膏铸型 拷贝.jpg


BIN
public/images/pb/500/3.杜萨雷斯大理石祭坛 拷贝.jpg


BIN
public/images/pb/500/4.女性雕像 拷贝.png


BIN
public/images/pb/500/5.塞勒诺大理石头像 拷贝.jpg


BIN
public/images/pb/500/6.科林斯式大理石柱头 拷贝.jpg


BIN
public/images/pb/500/7.描绘角斗士盔甲的湿壁画 拷贝.jpg


BIN
public/images/pb/500/8.鱼篮湿壁画 拷贝.jpg


BIN
public/images/pb/500/9.野味湿壁画 拷贝.jpg


+ 3 - 22
src/index/app.scss

@@ -7,6 +7,7 @@
   --design-height: 1000;
   --swiper-theme-color: white;
   --swiper-pagination-bullet-inactive-color: white;
+  --el-overlay-color-lighter: rgba(0, 0, 0, 0.88);
 }
 
 body,
@@ -77,28 +78,8 @@ iframe {
 }
 
 @font-face {
-  font-family: 'Source Han Sans CN-Regular';
-  src: url('/fonts/SourceHanSansCN-Regular.otf');
-}
-@font-face {
-  font-family: 'Source Han Sans CN-Bold';
-  src: url('/fonts/SourceHanSansCN-Bold.otf');
-}
-@font-face {
-  font-family: 'Source Han Sans CN-Medium';
-  src: url('/fonts/SourceHanSansCN-Medium.otf');
-}
-@font-face {
-  font-family: 'SourceHanSerifCN-Bold';
-  src: url('/fonts/SOURCEHANSERIFCN-BOLD.OTF');
-}
-@font-face {
-  font-family: 'SourceHanSerifCN-Regular';
-  src: url('/fonts/SOURCEHANSERIFCN-REGULAR.OTF');
-}
-@font-face {
   font-family: 'fzq';
-  src: url('/font/FZQKBYSJW.TTF');
+  src: url('/fonts/FZQKBYSJW.TTF');
 }
 
 .limit-line {
@@ -142,6 +123,6 @@ iframe {
 @media only screen and (max-width: 600px) {
   :root {
     --design-width: 750;
-    --design-height: 1333;
+    --design-height: 1624;
   }
 }

BIN
src/index/assets/images/close.png


BIN
src/index/components/info-popup/images/1.jpg


+ 145 - 0
src/index/components/info-popup/index.vue

@@ -0,0 +1,145 @@
+<template>
+  <el-dialog
+    class="info-popup"
+    v-model="show"
+    append-to-body
+    destroy-on-close
+    :close-on-click-modal="false"
+    :show-close="false"
+  >
+    <img class="info-popup__close" src="@/assets/images/close.png" @click="show = false" />
+
+    <div class="info-popup-container">
+      <h3>遇见庞贝——永恒之城</h3>
+      <div class="info-popup-contact">
+        <p>展览时间:2025.07.08-2025.11.02</p>
+        <p>展览城市:湖南-长沙</p>
+        <p>展览地点:湖南博物院一层特展一厅</p>
+        <p>主办单位:湖南博物院</p>
+      </div>
+      <div class="info-popup-info">
+        <p>
+          在遗落的古代文明中,有一处倏然消失了的鲜活城市,仿佛被时空所冻结,那就是位于今天意大利那不勒斯维苏威地区的庞贝古城遗址。这座庞贝古城曾经是地中海文明精髓熔于一炉的商贸明珠,从公元前8世纪奥斯坎人在此播撒下文明的种子,后与古希腊城邦组建联盟,再发展到古罗马帝国耀眼的“骄傲”,并最终定格在公元79年。
+        </p>
+        <p>
+          这一年维苏威火山大爆发,一场前所未有的灾难致使庞贝古城仿佛一切都停止了。烟炎张天,喷涌而出的火山物质很快掩盖了整座城市,两万多名居民从最初的惊恐到慌张逃亡,时间短暂却又漫长得如同瞬息和永远。
+        </p>
+        <p>
+          古罗马百科全书式学者老普林尼是这场灾难的亲历者,他的记述流传至今,与大约1700年后考古工作者不断发掘出的“地下材料”互为佐证,为我们全面揭开了庞贝古城那凝固了千年的“时空胶囊”。
+        </p>
+        <p>
+          从博物馆推开世界之窗,超越时空之限,邀您一同徜徉异国他乡的文化之旅。我们摆渡历史长河,遇见庞贝,用心触摸和感受那伤痛与悲悯之间、敬畏与大爱里的生命智慧和永恒之境!
+        </p>
+      </div>
+    </div>
+
+    <img class="info-popup-img" src="./images/1.jpg" />
+  </el-dialog>
+</template>
+
+<script setup lang="ts">
+  import { computed } from 'vue';
+
+  const emits = defineEmits(['update:visible']);
+  const props = defineProps<{
+    visible: boolean;
+  }>();
+
+  const show = computed({
+    get() {
+      return props.visible;
+    },
+    set(v) {
+      emits('update:visible', v);
+    },
+  });
+</script>
+
+<style lang="scss">
+  @use '@/assets/utils.scss';
+
+  .info-popup {
+    --el-dialog-bg-color: transparent;
+    --el-dialog-box-shadow: none;
+    --el-dialog-width: 100%;
+    --el-dialog-padding-primary: 0;
+    margin: 0;
+    height: 100%;
+
+    &__close {
+      position: absolute;
+      top: utils.vh-calc(80);
+      right: utils.vw-calc(80);
+      width: utils.vh-calc(24);
+      height: utils.vh-calc(24);
+      cursor: pointer;
+    }
+    .el-dialog__body {
+      padding: 0 utils.vw-calc(140);
+      display: flex;
+      align-items: center;
+      gap: utils.vw-calc(75);
+      height: 100%;
+    }
+    &-container {
+      flex: 1;
+      color: #e0b387;
+      font-size: utils.vh-calc(18);
+
+      h3 {
+        font-size: utils.vh-calc(30);
+        text-align: center;
+      }
+    }
+    &-contact {
+      margin: utils.vh-calc(40) 0;
+      line-height: utils.vh-calc(30);
+    }
+    &-info {
+      line-height: utils.vh-calc(28);
+    }
+    &-img {
+      flex-shrink: 0;
+      width: utils.vw-calc(776);
+    }
+  }
+
+  @media only screen and (max-width: 600px) {
+    .info-popup {
+      &__close {
+        top: unset;
+        right: unset;
+        left: 50%;
+        bottom: utils.vh-calc(115);
+        width: utils.vh-calc(36);
+        height: utils.vh-calc(36);
+        transform: translateX(-50%);
+      }
+      .el-dialog__body {
+        padding: utils.vh-calc(129) utils.vw-calc(30) utils.vh-calc(194);
+        flex-direction: column-reverse;
+        gap: utils.vw-calc(58);
+      }
+      &-container {
+        font-size: utils.vh-calc(28);
+
+        h3 {
+          font-size: utils.vh-calc(36);
+        }
+      }
+      &-contact {
+        padding: 0 utils.vw-calc(10);
+        line-height: utils.vh-calc(42);
+      }
+      &-info {
+        padding: 0 utils.vw-calc(10);
+        line-height: utils.vh-calc(42);
+        height: utils.vh-calc(574);
+        overflow-y: auto;
+      }
+      &-img {
+        width: 100%;
+      }
+    }
+  }
+</style>

Fichier diff supprimé car celui-ci est trop grand
+ 200 - 0
src/index/views/home/components/ant-popup/constants.ts


+ 151 - 0
src/index/views/home/components/ant-popup/detail.vue

@@ -0,0 +1,151 @@
+<template>
+  <el-dialog
+    class="detail-popup"
+    v-model="show"
+    append-to-body
+    destroy-on-close
+    :close-on-click-modal="false"
+    :show-close="false"
+  >
+    <img class="detail-popup__close" src="@/assets/images/close.png" @click="show = false" />
+
+    <template v-if="detail">
+      <el-image
+        fit="contain"
+        :src="`images/pb/1000/${detail.imgName}`"
+        :preview-src-list="[`images/pb/2000/${detail.imgName}`]"
+      />
+      <el-scrollbar class="detail-popup-container">
+        <p>{{ detail.name }}</p>
+        <p>{{ detail.age }}</p>
+        <p>{{ detail.unearthed }}</p>
+        <p>{{ detail.from }}</p>
+        <p>{{ detail.desc }}</p>
+      </el-scrollbar>
+    </template>
+  </el-dialog>
+</template>
+
+<script setup lang="ts">
+  import { computed } from 'vue';
+  import { ANT_LIST } from './constants';
+
+  const emits = defineEmits(['update:visible']);
+  const props = defineProps<{
+    visible: boolean;
+    checkedIndex: number;
+  }>();
+  const detail = computed(() => {
+    return props.checkedIndex !== -1 ? ANT_LIST[props.checkedIndex] : null;
+  });
+
+  const show = computed({
+    get() {
+      return props.visible;
+    },
+    set(v) {
+      emits('update:visible', v);
+    },
+  });
+</script>
+
+<style lang="scss">
+  @use '@/assets/utils.scss';
+
+  .detail-popup {
+    --el-dialog-bg-color: transparent;
+    --el-dialog-box-shadow: none;
+    --el-dialog-width: 100%;
+    --el-dialog-padding-primary: 0;
+    margin: 0;
+    height: 100%;
+
+    &__close {
+      position: absolute;
+      top: utils.vh-calc(80);
+      right: utils.vw-calc(80);
+      width: utils.vw-calc(24);
+      cursor: pointer;
+      z-index: 1;
+    }
+    .el-image {
+      max-width: utils.vw-calc(800);
+      height: utils.vh-calc(620);
+    }
+    .el-dialog__body {
+      height: 100%;
+      display: flex;
+      align-items: center;
+      justify-content: center;
+      gap: utils.vw-calc(200);
+    }
+    &-container {
+      flex-shrink: 0;
+      width: utils.vw-calc(638);
+      height: auto;
+      color: #e0b387;
+      font-size: utils.vh-calc(18);
+      line-height: utils.vh-calc(30);
+
+      p {
+        margin-bottom: utils.vh-calc(30);
+
+        &:first-child {
+          position: relative;
+          margin-bottom: utils.vh-calc(55);
+          font-size: utils.vh-calc(30);
+          text-align: center;
+
+          &::after {
+            content: '';
+            position: absolute;
+            bottom: utils.vh-calc(-22);
+            left: 50%;
+            transform: translateX(-50%);
+            width: utils.vw-calc(304);
+            height: utils.vw-calc(10);
+            background: url('./images/bd.png') no-repeat center / contain;
+          }
+        }
+      }
+    }
+  }
+
+  @media only screen and (max-width: 600px) {
+    .detail-popup {
+      .el-dialog__body {
+        flex-direction: column;
+        gap: utils.vh-calc(60);
+      }
+      .el-image {
+        margin-top: utils.vh-calc(120);
+        padding: 0 utils.vw-calc(45);
+        max-width: 100%;
+        height: utils.vh-calc(674);
+      }
+      &-container {
+        flex: 1;
+        height: 0;
+        width: 100%;
+        padding: 0 utils.vw-calc(45) utils.vh-calc(158);
+
+        p {
+          font-size: utils.vh-calc(28);
+          line-height: utils.vh-calc(42);
+
+          &:first-child {
+            font-size: utils.vh-calc(36);
+          }
+        }
+      }
+      &__close {
+        top: unset;
+        left: 50%;
+        right: unset;
+        bottom: utils.vh-calc(66);
+        width: utils.vw-calc(36);
+        transform: translateX(-50%);
+      }
+    }
+  }
+</style>

BIN
src/index/views/home/components/ant-popup/images/bd.png


BIN
src/index/views/home/components/ant-popup/images/icon.png


+ 272 - 0
src/index/views/home/components/ant-popup/index.vue

@@ -0,0 +1,272 @@
+<template>
+  <el-dialog
+    class="ant-popup"
+    v-model="show"
+    append-to-body
+    destroy-on-close
+    :close-on-click-modal="false"
+    :show-close="false"
+  >
+    <div class="ant-popup-head">
+      <p>展品说明</p>
+      <img class="close" src="@/assets/images/close.png" @click="show = false" />
+    </div>
+
+    <div class="ant-popup-search">
+      <el-input
+        v-model="query"
+        placeholder="请输入展品名称"
+        @keydown.stop
+        @keyup.stop
+        @keyup.enter="search"
+      />
+
+      <div class="search btn" @click="search">搜索</div>
+      <div class="reset btn" @click="reset">重置</div>
+    </div>
+
+    <el-scrollbar class="ant-popup-scrollbar">
+      <ul>
+        <li v-for="item in filteredList" :key="item.id" @click="handleChecked(item.id)">
+          <el-image :src="`images/pb/500/${item.imgName}`" />
+          <p>{{ item.name }}</p>
+        </li>
+      </ul>
+    </el-scrollbar>
+  </el-dialog>
+
+  <DetailPopup v-model:visible="detailVisible" :checkedIndex="checkedIndex" />
+</template>
+
+<script setup lang="ts">
+  import { computed, ref } from 'vue';
+  import { ANT_LIST } from './constants';
+  import DetailPopup from './detail.vue';
+
+  const emits = defineEmits(['update:visible']);
+  const props = defineProps<{
+    visible: boolean;
+  }>();
+
+  const show = computed({
+    get() {
+      return props.visible;
+    },
+    set(v) {
+      emits('update:visible', v);
+    },
+  });
+
+  const query = ref('');
+  const checkedIndex = ref(-1);
+  const detailVisible = ref(false);
+
+  const originalList = ANT_LIST;
+
+  const filteredList = computed(() => {
+    const q = query.value.trim().toLowerCase();
+    if (!q) return originalList;
+    return originalList.filter((item) => item.name.toLowerCase().includes(q));
+  });
+
+  const search = () => {
+    // computed `filteredList` reacts to `query`, so no extra logic required
+  };
+
+  const reset = () => {
+    query.value = '';
+  };
+
+  const handleChecked = (index: number) => {
+    checkedIndex.value = index;
+    detailVisible.value = true;
+  };
+</script>
+
+<style lang="scss">
+  @use '@/assets/utils.scss';
+
+  .ant-popup {
+    --el-dialog-bg-color: transparent;
+    --el-dialog-box-shadow: none;
+    --el-dialog-padding-primary: 0;
+    margin: 0 auto;
+    padding-top: utils.vh-calc(100);
+    width: utils.vw-calc(1060);
+    height: 100%;
+
+    .el-dialog__body {
+      height: 100%;
+      display: flex;
+      flex-direction: column;
+    }
+    &-scrollbar {
+      flex: 1;
+      height: 0;
+
+      ul {
+        list-style: none;
+        padding: 0;
+        margin: 0;
+        column-count: 5;
+        column-gap: utils.vw-calc(40);
+      }
+
+      li {
+        display: inline-block;
+        width: 100%;
+        -webkit-column-break-inside: avoid;
+        -moz-column-break-inside: avoid;
+        break-inside: avoid;
+        margin: 0 0 utils.vh-calc(30) 0;
+        min-height: utils.vh-calc(80);
+        cursor: pointer;
+
+        .el-image {
+          min-height: utils.vw-calc(100);
+        }
+        p {
+          margin: utils.vh-calc(12) 0 0;
+          font-size: utils.vh-calc(18);
+          color: #e0b387;
+          text-align: center;
+        }
+      }
+
+      li > .el-image,
+      li .el-image {
+        display: block;
+        width: 100%;
+      }
+    }
+    &-head {
+      position: relative;
+      display: flex;
+      align-items: center;
+      justify-content: space-between;
+      padding: 0 0 utils.vh-calc(10) utils.vw-calc(23);
+      font-size: utils.vh-calc(22);
+      color: #e0b387;
+      border-bottom: 1px solid #e0b387;
+
+      .close {
+        width: utils.vh-calc(24);
+        height: utils.vh-calc(24);
+        cursor: pointer;
+      }
+      &::before {
+        content: '';
+        position: absolute;
+        top: utils.vh-calc(-5);
+        left: 0;
+        width: utils.vh-calc(13);
+        height: utils.vh-calc(30);
+        background: url('./images/icon.png') no-repeat center / contain;
+      }
+      &::after {
+        content: '';
+        position: absolute;
+        left: 0;
+        bottom: 0;
+        width: utils.vh-calc(114);
+        height: 2px;
+        background: #e0b387;
+        z-index: 1;
+      }
+    }
+    &-search {
+      margin: utils.vh-calc(30) 0;
+      display: flex;
+      justify-content: space-between;
+      height: utils.vh-calc(43);
+      gap: utils.vw-calc(30);
+
+      .el-input {
+        --el-input-bg-color: transparent;
+        --el-input-border-radius: 0;
+        --el-border-color: #e0b387;
+        --el-input-text-color: #e0b387;
+        --el-input-placeholder-color: rgba(224, 179, 135, 0.7);
+        flex: 1;
+        font-size: utils.vh-calc(18);
+
+        .el-input__inner {
+          height: 100%;
+          line-height: 100%;
+        }
+      }
+      .btn {
+        flex-shrink: 0;
+        width: utils.vw-calc(90);
+        height: 100%;
+        text-align: center;
+        font-size: utils.vh-calc(18);
+        line-height: utils.vh-calc(45);
+        cursor: pointer;
+
+        &.search {
+          background: #e0b387;
+          color: #7d3400;
+        }
+        &.reset {
+          background: rgba(224, 179, 135, 0.3);
+          color: #e0b387;
+          border: 1px solid #e0b387;
+        }
+      }
+    }
+  }
+
+  @media only screen and (max-width: 600px) {
+    .ant-popup {
+      width: 100%;
+
+      &-head {
+        margin: 0 utils.vw-calc(30);
+        padding: 0 0 utils.vh-calc(16) utils.vh-calc(40);
+        font-size: utils.vh-calc(36);
+
+        .close {
+          width: utils.vh-calc(36);
+          height: utils.vh-calc(36);
+        }
+        &::before {
+          width: utils.vh-calc(22);
+          height: utils.vh-calc(50);
+        }
+        &::after {
+          width: utils.vh-calc(200);
+          height: 1px;
+        }
+      }
+      &-scrollbar {
+        padding: 0 utils.vw-calc(30);
+
+        ul {
+          column-count: 2;
+          column-gap: utils.vw-calc(30);
+        }
+        li {
+          p {
+            margin: utils.vh-calc(15) 0 0;
+            font-size: utils.vh-calc(28);
+          }
+        }
+      }
+      &-search {
+        margin: utils.vh-calc(40) utils.vw-calc(30);
+        height: utils.vh-calc(72);
+
+        .el-input {
+          font-size: utils.vh-calc(30);
+        }
+        .btn {
+          width: utils.vw-calc(115);
+          text-align: center;
+          font-size: utils.vh-calc(30);
+          line-height: utils.vh-calc(80);
+        }
+      }
+    }
+  }
+</style>

BIN
src/index/views/home/components/chapter-popup/images/1.jpg


BIN
src/index/views/home/components/chapter-popup/images/2.jpg


BIN
src/index/views/home/components/chapter-popup/images/3.jpg


BIN
src/index/views/home/components/chapter-popup/images/4.jpg


+ 198 - 0
src/index/views/home/components/chapter-popup/index.vue

@@ -0,0 +1,198 @@
+<template>
+  <el-dialog
+    class="chapter-popup"
+    v-model="show"
+    append-to-body
+    destroy-on-close
+    :close-on-click-modal="false"
+    :show-close="false"
+  >
+    <ul v-if="!isMobile">
+      <li>
+        <img src="./images/1.jpg" />
+      </li>
+      <li>
+        <img src="./images/2.jpg" />
+      </li>
+      <li>
+        <img src="./images/3.jpg" />
+      </li>
+      <li>
+        <img src="./images/4.jpg" />
+      </li>
+
+      <img class="chapter-popup__close" src="@/assets/images/close.png" @click="show = false" />
+    </ul>
+
+    <template v-else>
+      <Swiper
+        class="chapter-popup-swiper"
+        :modules="[Pagination]"
+        :pagination="paginationOptions"
+        :centeredSlides="true"
+        :slidesPerView="1.2"
+        :spaceBetween="15"
+        :loop="true"
+        @swiper="onSwiper"
+      >
+        <SwiperSlide>
+          <img src="./images/1.jpg" />
+        </SwiperSlide>
+        <SwiperSlide>
+          <img src="./images/2.jpg" />
+        </SwiperSlide>
+        <SwiperSlide>
+          <img src="./images/3.jpg" />
+        </SwiperSlide>
+        <SwiperSlide>
+          <img src="./images/4.jpg" />
+        </SwiperSlide>
+      </Swiper>
+
+      <div class="chapter-popup-swiper__pagination" />
+
+      <img class="chapter-popup__close" src="@/assets/images/close.png" @click="show = false" />
+    </template>
+  </el-dialog>
+</template>
+
+<script setup lang="ts">
+  import { Swiper, SwiperSlide } from 'swiper/vue';
+  import { Pagination } from 'swiper/modules';
+  import useBaseStore from '@/store/module/base';
+  import { storeToRefs } from 'pinia';
+  import { computed, ref, watch } from 'vue';
+  import 'swiper/css';
+  import 'swiper/css/pagination';
+
+  const emits = defineEmits(['update:visible']);
+  const baseStore = useBaseStore();
+  const { manageJsLoaded } = storeToRefs(baseStore);
+  const isMobile = ref(false);
+  const props = defineProps<{
+    visible: boolean;
+  }>();
+
+  const show = computed({
+    get() {
+      return props.visible;
+    },
+    set(v) {
+      emits('update:visible', v);
+    },
+  });
+
+  const swiperRef = ref<any>(null);
+
+  const paginationOptions: any = {
+    el: '.chapter-popup-swiper__pagination',
+    clickable: true,
+    renderBullet(index: number, className: string) {
+      return `<span class="${className}"></span>`;
+    },
+  };
+
+  function onSwiper(swiper: any) {
+    swiperRef.value = swiper;
+  }
+
+  watch(
+    manageJsLoaded,
+    (v) => {
+      if (v) {
+        isMobile.value = window.browser.isMobile();
+      }
+    },
+    {
+      immediate: true,
+    }
+  );
+</script>
+
+<style lang="scss">
+  @use '@/assets/utils.scss';
+
+  .chapter-popup {
+    --el-dialog-bg-color: transparent;
+    --el-dialog-box-shadow: none;
+    --el-dialog-width: 100%;
+    --el-dialog-padding-primary: 0;
+    margin: 0;
+    height: 100%;
+
+    .el-dialog__header {
+      display: none;
+    }
+    .el-dialog__body {
+      display: flex;
+      align-items: center;
+      justify-content: center;
+      width: 100%;
+      height: 100%;
+    }
+    ul {
+      position: relative;
+      display: flex;
+      align-items: center;
+      justify-content: center;
+      gap: utils.vw-calc(40);
+
+      img {
+        width: utils.vw-calc(420);
+      }
+    }
+    &-swiper {
+      .swiper-slide img {
+        width: 100%;
+      }
+      &__pagination.swiper-pagination-bullets {
+        --swiper-pagination-bullet-border-radius: 5px;
+
+        position: absolute;
+        display: flex;
+        justify-content: center;
+        top: unset;
+        left: 0;
+        right: 0;
+        bottom: utils.vh-calc(311);
+
+        .swiper-pagination-bullet {
+          width: utils.vw-calc(10);
+          height: utils.vw-calc(10);
+          background: #d6ba99;
+          opacity: 1;
+          transition: width 0.2s;
+
+          &-active {
+            width: utils.vw-calc(40);
+          }
+        }
+      }
+    }
+    &__close {
+      position: absolute;
+      top: utils.vw-calc(-54);
+      right: 0;
+      width: utils.vw-calc(24) !important;
+      height: utils.vw-calc(24);
+      cursor: pointer;
+    }
+  }
+
+  @media only screen and (max-width: 600px) {
+    .chapter-popup {
+      .el-dialog__body {
+        padding-bottom: utils.vh-calc(80);
+      }
+      &__close {
+        top: unset;
+        right: unset;
+        left: 50%;
+        bottom: utils.vh-calc(195);
+        width: utils.vw-calc(36) !important;
+        height: utils.vw-calc(36);
+        transform: translateX(-50%);
+      }
+    }
+  }
+</style>

+ 3 - 4
src/index/views/home/components/menu/index.pb.scss

@@ -46,6 +46,7 @@
     }
   }
   &.right {
+    display: none;
     right: 20px;
     bottom: 0;
 
@@ -114,6 +115,7 @@
 .el-popper.is-dark {
   --el-text-color-primary: rgba(0, 0, 0, 0.3);
   --el-bg-color: #e0b387;
+  border: none;
 }
 
 @media only screen and (max-width: 600px) {
@@ -148,10 +150,6 @@
         margin-right: 0;
       }
     }
-    &.right {
-      right: utils.vw-calc(20);
-      bottom: utils.vh-calc(100);
-    }
     > div {
       width: utils.vw-calc(50);
       height: utils.vw-calc(50);
@@ -163,6 +161,7 @@
 .home-viewer,
 .home_img {
   transition:
+    bottom 0.5s,
     opacity 0.38s ease,
     transform 0.38s ease,
     visibility 0.38s;

+ 47 - 13
src/index/views/home/components/menu/index.pb.tsx

@@ -1,6 +1,8 @@
-import { defineComponent, ref } from 'vue';
+import { defineComponent, ref, watch } from 'vue';
 import { homeApi } from '@/api';
 import SharePopup from '../share-popup/index.vue';
+import ChapterPopup from '../chapter-popup/index.vue';
+import AntPopup from '../ant-popup/index.vue';
 import './index.pb.scss';
 
 export default defineComponent({
@@ -14,11 +16,18 @@ export default defineComponent({
       type: Function,
       required: true,
     },
+    manageJsLoaded: {
+      type: Boolean,
+      required: true,
+    },
   },
   setup(props) {
     const isSimpleMode = ref(false);
     const shareVisible = ref(false);
+    const chapterVisible = ref(false);
+    const antVisible = ref(false);
     const animationThumb = ref(false);
+    const isMobile = ref(false);
 
     const toggleSimpleMode = (on?: boolean) => {
       const next = typeof on === 'boolean' ? on : !isSimpleMode.value;
@@ -35,17 +44,32 @@ export default defineComponent({
 
       animationThumb.value = true;
       homeApi.saveStar(window.number);
-      props.handleLikeCounter(props.likeCount + 1);
+      props.handleLikeCounter();
 
       setTimeout(() => {
         animationThumb.value = false;
       }, 200);
     };
 
+    watch(
+      () => props.manageJsLoaded,
+      (v) => {
+        if (v) {
+          isMobile.value = window.browser.isMobile();
+        }
+      },
+      {
+        immediate: true,
+      }
+    );
+
     return {
+      isMobile,
       isSimpleMode,
       shareVisible,
+      antVisible,
       animationThumb,
+      chapterVisible,
       handleThumb,
       toggleSimpleMode,
     };
@@ -60,7 +84,7 @@ export default defineComponent({
                 <img src="images/pb/play.png" width="24" height="24" data-original-title="播放" />
               </a>
             </div>
-            <el-tooltip content="播放" show-arrow={false} offset={5}>
+            <el-tooltip content="播放" show-arrow={false} offset={5} disabled={this.isMobile}>
               <div id="play" class="ui-icon">
                 <a>
                   <img src="images/pb/play.png" width="24" height="24" />
@@ -77,7 +101,7 @@ export default defineComponent({
                 <i title="" class="icon icon-dpad-right" data-original-title="下一个"></i>
               </a>
             </div>
-            <el-tooltip content="导览" show-arrow={false} offset={5}>
+            <el-tooltip content="导览" show-arrow={false} offset={5} disabled={this.isMobile}>
               <div id="pullTab" title="">
                 <img
                   class="icon icon-inside"
@@ -89,7 +113,7 @@ export default defineComponent({
               </div>
             </el-tooltip>
             <div id="hotList" title="" style="display: none">
-              <el-tooltip content="热点列表" show-arrow={false} offset={5}>
+              <el-tooltip content="热点列表" show-arrow={false} offset={5} disabled={this.isMobile}>
                 <img class="icon icon-inside" src="images/pb/hotlist.png" title="热点列表" />
               </el-tooltip>
             </div>
@@ -102,7 +126,7 @@ export default defineComponent({
             >
               <img class="icon icon-inside" src="images/inside.png" title="全景漫游" />
             </div>
-            <el-tooltip content="迷你模型" show-arrow={false} offset={5}>
+            <el-tooltip content="迷你模型" show-arrow={false} offset={5} disabled={this.isMobile}>
               <div id="gui-modes-dollhouse" title="" class="">
                 <img class="icon icon-inside" src="images/pb/model.png" title="迷你模型" />
               </div>
@@ -115,13 +139,18 @@ export default defineComponent({
             >
               <img class="icon icon-inside" src="images/floor.png" title="俯视图" />
             </div>
-            <el-tooltip content="展品列表" show-arrow={false} offset={5}>
-              <div id="gui-modes-antlist" title="" class="">
+            <el-tooltip content="展品列表" show-arrow={false} offset={5} disabled={this.isMobile}>
+              <div
+                id="gui-modes-antlist"
+                title=""
+                class=""
+                onClick={() => (this.antVisible = true)}
+              >
                 <img class="icon icon-inside" src="images/pb/antlist.png" title="展品列表" />
               </div>
             </el-tooltip>
-            <el-tooltip content="章节列表" show-arrow={false} offset={5}>
-              <div id="gui-modes-chapter" title="" class="">
+            <el-tooltip content="章节列表" show-arrow={false} offset={5} disabled={this.isMobile}>
+              <div id="gui-modes-chapter" onClick={() => (this.chapterVisible = true)}>
                 <img class="icon icon-inside" src="images/pb/chapter.png" title="章节列表" />
               </div>
             </el-tooltip>
@@ -135,12 +164,12 @@ export default defineComponent({
                 <div class="icon icon-slot" title="点赞" />
               </div>
             </el-tooltip>
-            <el-tooltip content="分享" show-arrow={false} offset={5}>
+            <el-tooltip content="分享" show-arrow={false} offset={5} disabled={this.isMobile}>
               <div id="sharing" onClick={() => (this.shareVisible = true)}>
                 <img class="icon icon-inside" src="images/pb/share.png" title="分享" />
               </div>
             </el-tooltip>
-            <el-tooltip content="全屏" show-arrow={false} offset={5}>
+            <el-tooltip content="全屏" show-arrow={false} offset={5} disabled={this.isMobile}>
               <div
                 id="gui-fullscreen"
                 class="ui-icon wide"
@@ -163,7 +192,7 @@ export default defineComponent({
                 <img class="icon icon-fullscreen-exit" src="images/pb/narrow_off.png" />
               </a>
             </div>
-            <el-tooltip content="清屏" show-arrow={false} offset={5}>
+            <el-tooltip content="清屏" show-arrow={false} offset={5} disabled={this.isMobile}>
               <div onClick={() => this.toggleSimpleMode(true)}>
                 <img class="icon icon-inside" src="images/pb/simple.png" title="清屏" />
               </div>
@@ -208,6 +237,11 @@ export default defineComponent({
           退出清屏
         </div>
 
+        <AntPopup visible={this.antVisible} onUpdate:visible={(v) => (this.antVisible = v)} />
+        <ChapterPopup
+          visible={this.chapterVisible}
+          onUpdate:visible={(v) => (this.chapterVisible = v)}
+        />
         <SharePopup visible={this.shareVisible} onUpdate:visible={(v) => (this.shareVisible = v)} />
       </div>
     );

+ 18 - 6
src/index/views/home/index.pb.tsx

@@ -10,9 +10,12 @@ import GuiLoading from './components/gui-loading';
 import Popup from './components/popup';
 import HotSpotList from './components/hot-spot-list';
 import Img from './images/home.png';
-import './index.pb.scss';
 import { useRouter } from 'vue-router';
 import { homeApi } from '@/api';
+import useBaseStore from '@/store/module/base';
+import { storeToRefs } from 'pinia';
+import InfoPopup from '@/components/info-popup/index.vue';
+import './index.pb.scss';
 
 // 自定义热点图标
 // @ts-ignore
@@ -33,11 +36,13 @@ export default defineComponent({
     Popup,
   },
   setup() {
+    const baseStore = useBaseStore();
+    const { manageJsLoaded } = storeToRefs(baseStore);
     const router = useRouter();
-    const manageJsLoaded = ref(false);
     const hotJsLoaded = ref(false);
     const visitCount = ref(0);
     const likeCount = ref(0);
+    const infoVisible = ref(false);
 
     const getSceneDetail = async () => {
       const { data } = await homeApi.getSceneDetail(window.number);
@@ -46,8 +51,8 @@ export default defineComponent({
       likeCount.value = data.starSum;
     };
 
-    const handleLikeCounter = (num: number) => {
-      likeCount.value += num;
+    const handleLikeCounter = () => {
+      likeCount.value += 1;
     };
 
     onMounted(() => {
@@ -58,6 +63,7 @@ export default defineComponent({
 
     return {
       router,
+      infoVisible,
       manageJsLoaded,
       hotJsLoaded,
       visitCount,
@@ -75,7 +81,7 @@ export default defineComponent({
           <span class="limit-line">{this.visitCount}</span>
         </div>
 
-        <img class="home_img" src={Img} onClick={() => this.router.replace({ name: 'cover' })} />
+        <img class="home_img" src={Img} onClick={() => (this.infoVisible = true)} />
 
         {/* 加载初始页面 */}
         <div id="gui-thumb" />
@@ -99,7 +105,11 @@ export default defineComponent({
             <HotSpotList />
 
             {/* 底部菜单 */}
-            <Menu likeCount={this.likeCount} handleLikeCounter={this.handleLikeCounter} />
+            <Menu
+              manageJsLoaded={this.manageJsLoaded}
+              likeCount={this.likeCount}
+              handleLikeCounter={this.handleLikeCounter}
+            />
 
             {/* 导览 */}
             <Guide />
@@ -115,6 +125,8 @@ export default defineComponent({
           <Other />
         </div>
 
+        <InfoPopup visible={this.infoVisible} onUpdate:visible={() => (this.infoVisible = false)} />
+
         {/* TODO: 没有控制权,耦合严重;放在此处为了防止元素未渲染导致报错 */}
         <JsScript src="./js/manage.js" onLoad={() => (this.manageJsLoaded = true)} />
         {this.manageJsLoaded && (