chenlei před 7 měsíci
rodič
revize
df52fb6f5e

binární
packages/mobile/src/assets/images/search-min.png


binární
packages/mobile/src/assets/images/title_3-min.png


binární
packages/mobile/src/assets/images/top-min.png


+ 74 - 0
packages/mobile/src/components/Sidebar.vue

@@ -0,0 +1,74 @@
+<template>
+  <VanPopup v-model:show="show" class="sidebar" position="left">
+    <div
+      v-for="item in TABBAR"
+      :key="item.label"
+      class="sidebar-item"
+      @click="
+        () => {
+          $router.push({ name: item.name[0] });
+          show = false;
+        }
+      "
+    >
+      {{ item.label }}
+    </div>
+  </VanPopup>
+</template>
+
+<script setup>
+import { computed } from "vue";
+
+const props = defineProps(["visible"]);
+const emits = defineEmits(["update:visible"]);
+const show = computed({
+  get() {
+    return props.visible;
+  },
+  set(v) {
+    emits("update:visible", v);
+  },
+});
+
+const TABBAR = [
+  {
+    label: "首页",
+    name: ["home"],
+  },
+  {
+    label: "文物赏析",
+    name: ["antiquity", "antiquityDetail"],
+  },
+  {
+    label: "数字展厅",
+    name: ["exhibition"],
+  },
+  {
+    label: "视频资料",
+    name: ["media", "mediaDetail"],
+  },
+];
+</script>
+
+<style lang="scss" scoped>
+.sidebar {
+  display: flex;
+  flex-direction: column;
+  gap: 100px;
+  padding: 157px 40px 0;
+  width: 375px;
+  height: 100%;
+  background: white;
+
+  &-item {
+    width: 100%;
+    height: 82px;
+    line-height: 82px;
+    text-align: center;
+    font-size: 42px;
+    border-radius: 10px;
+    font-family: "SourceHanSerifSC-Regular";
+    border: 2px solid #f3f3f3;
+  }
+}
+</style>

+ 94 - 0
packages/mobile/src/views/Antiquity/Detail/index.scss

@@ -0,0 +1,94 @@
+.antiquity-detail {
+  padding: calc(80px + 50px) 30px 60px;
+  display: flex;
+  flex-direction: column;
+  height: 100vh;
+  background: url("@/assets/images/bg_1-min.png") no-repeat center top / cover;
+
+  &-head {
+    position: fixed;
+    top: 0;
+    left: 0;
+    right: 0;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    padding: 40px 0;
+    z-index: 1;
+
+    .logo {
+      width: 262px;
+      height: 50px;
+    }
+  }
+  &__back {
+    position: absolute;
+    top: 50%;
+    left: 30px;
+    width: 60px;
+    height: 60px;
+    transform: translateY(-50%);
+  }
+  &-img {
+    padding: 48px;
+  }
+  &-tools {
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    gap: 57px;
+
+    img {
+      margin: 30px 0;
+      width: 60px;
+      height: 60px;
+    }
+  }
+  &-wrap {
+    flex: 1;
+    padding: 60px 40px;
+    height: 0;
+    overflow: auto;
+    background: rgba(136, 113, 82, 0.1);
+
+    h3 {
+      margin-bottom: 40px;
+      color: #ce1a28;
+      text-align: center;
+      font-size: 36px;
+    }
+    p {
+      color: #644e36;
+      font-size: 30px;
+      line-height: 50px;
+    }
+  }
+  &-three {
+    display: flex;
+    flex-direction: column;
+    padding: 80px 0;
+    gap: 40px;
+    height: 100%;
+
+    h3 {
+      color: #ce1a28;
+      text-align: center;
+      font-size: 36px;
+    }
+    iframe {
+      flex: 1;
+      height: 0;
+    }
+    &-tools {
+      display: flex;
+      align-items: center;
+      justify-content: center;
+      gap: 60px;
+
+      img {
+        width: 60px;
+        height: 60px;
+      }
+    }
+  }
+}

+ 115 - 0
packages/mobile/src/views/Antiquity/Detail/index.vue

@@ -0,0 +1,115 @@
+<template>
+  <div class="antiquity-detail">
+    <div class="antiquity-detail-head">
+      <img
+        class="antiquity-detail__back"
+        src="@/assets/images/back-min.png"
+        @click="$router.back"
+      />
+      <img class="logo" src="@/assets/images/logo-min.png" />
+    </div>
+
+    <template v-if="!isThree">
+      <div class="antiquity-detail-img">
+        <VanImage fit="contain" :src="getEnvImagePath(detail?.minImg)" />
+      </div>
+
+      <div class="antiquity-detail-tools">
+        <img src="@/assets/images/icon_2-min.png" @click="preview = true" />
+      </div>
+
+      <div class="antiquity-detail-wrap">
+        <h3>{{ detail?.name }}</h3>
+        <div>
+          <p>文物等级:{{ detail?.level || "--" }}</p>
+          <p>尺寸:{{ detail?.size || "--" }}</p>
+          <p>时代:{{ detail?.sd || "--" }}</p>
+          <p>质地:{{ detail?.zd || "--" }}</p>
+          <p>来源:{{ detail?.from || "--" }}</p>
+          <p>类别:{{ detail?.type || "--" }}</p>
+          <p>存放位置:{{ detail?.address || "--" }}</p>
+          <p v-html="detail?.content" />
+        </div>
+      </div>
+    </template>
+
+    <div v-else class="antiquity-detail-three">
+      <h3>{{ detail?.name }}</h3>
+      <iframe ref="iframe" :src="iframeUrl" />
+      <div class="antiquity-detail-three-tools">
+        <img
+          draggable="false"
+          src="@/assets/images/icon_5-min.png"
+          @click="handleZoom"
+        />
+        <img
+          draggable="false"
+          :src="autoPlay ? PauseImg : PlayImg"
+          @click="handleAutoPlay"
+        />
+        <img
+          draggable="false"
+          src="@/assets/images/icon_7-min.png"
+          @click="handleResetView"
+        />
+      </div>
+    </div>
+  </div>
+
+  <van-image-preview v-model:show="preview" :images="imgs" />
+</template>
+
+<script setup>
+import { computed, ref } from "vue";
+import { antiquityData, getEnvImagePath, isDevelopment } from "@lbc/base";
+import { useRoute } from "vue-router";
+import PlayImg from "@/assets/images/icon_6-min.png";
+import PauseImg from "@/assets/images/icon_8-min.png";
+
+const route = useRoute();
+const detail = computed(() =>
+  antiquityData.find((i) => i.id === Number(route.params.id))
+);
+const imgs = computed(() =>
+  detail.value?.img ? detail.value.img.map((i) => getEnvImagePath(i)) : []
+);
+const preview = ref(false);
+// 是否三维文物
+const isThree = computed(() => Boolean(detail.value?.link));
+const iframe = ref(null);
+const iframeUrl = computed(
+  () =>
+    isThree &&
+    `${
+      isDevelopment
+        ? "/model.html"
+        : `${location.origin}${location.pathname.replace(
+            /index\.html$/,
+            "model.html"
+          )}`
+    }?m=${detail.value?.link}`
+);
+// 模型自动旋转
+const autoPlay = ref(true);
+
+const handleZoom = () => {
+  iframe.value.contentWindow.webview.zoomIn(); // 放大
+};
+
+const handleAutoPlay = () => {
+  if (!iframe.value.contentWindow.modelLoding) return;
+
+  iframe.value.contentWindow.webview.stopRotate();
+  autoPlay.value = !autoPlay.value;
+};
+
+const handleResetView = () => {
+  iframe.value.contentWindow.webview.resetView();
+
+  if (!autoPlay.value) handleAutoPlay();
+};
+</script>
+
+<style lang="scss" scoped>
+@use "./index.scss";
+</style>

+ 63 - 0
packages/mobile/src/views/Antiquity/components/List.vue

@@ -0,0 +1,63 @@
+<template>
+  <ul v-if="list.length" class="ant-list">
+    <li
+      v-for="item in list"
+      :key="item.id"
+      class="ant-item"
+      @click="
+        $router.push({ name: 'antiquityDetail', params: { id: item.id } })
+      "
+    >
+      <div class="ant-item-img">
+        <VanImage lazy-load :src="getEnvImagePath(item.minImg)" fit="contain" />
+      </div>
+      <p class="limit-line">{{ item.name }}</p>
+    </li>
+  </ul>
+
+  <VanEmpty v-else image="search" description="暂无内容" />
+</template>
+
+<script setup>
+import { getEnvImagePath } from "@lbc/base";
+
+defineProps({
+  list: {
+    type: Array,
+    default: [],
+  },
+});
+</script>
+
+<style lang="scss" scoped>
+.ant-list {
+  display: flex;
+  flex-wrap: wrap;
+  gap: 30px;
+  padding: 0 30px 60px;
+  height: calc(100vh - 135px - 180px);
+  overflow: auto;
+}
+.ant-item {
+  width: calc(50% - 15px);
+
+  &-img {
+    width: 330px;
+    height: 280px;
+    border-radius: 15px;
+    overflow: hidden;
+    background: #d7d7d9;
+
+    .van-image {
+      width: 100%;
+      height: 100%;
+    }
+  }
+  p {
+    margin-top: 20px;
+    color: #3b3b3b;
+    font-size: 24px;
+    text-align: center;
+  }
+}
+</style>

+ 58 - 0
packages/mobile/src/views/Antiquity/index.scss

@@ -0,0 +1,58 @@
+.antiquity {
+  padding-top: 135px;
+  height: 100vh;
+  background: url("@/assets/images/bg_1-min.png") no-repeat center top / cover;
+
+  &-tabs {
+    --van-tabs-line-height: 160px;
+    --van-tab-font-size: 36px;
+    --van-tab-active-text-color: #ce1a28;
+    --van-tab-text-color: #393939;
+    --van-padding-xs: 30px;
+    --van-tabs-bottom-bar-width: 120px;
+    --van-tabs-bottom-bar-height: 60px;
+    --van-tabs-bottom-bar-color: rgba(201, 1, 16, 0.2);
+    --van-tabs-nav-background: transparent;
+
+    font-family: "SourceHanSerifSC-Regular";
+
+    :deep(.van-tabs__nav) {
+      margin: 0 -30px;
+    }
+    :deep(.van-tabs__line) {
+      bottom: 79px;
+    }
+  }
+  &-search {
+    position: absolute;
+    top: 185px;
+    right: 30px;
+    padding: 0 30px;
+    display: flex;
+    align-items: center;
+    gap: 10px;
+    width: 300px;
+    height: 60px;
+    background: #c90110;
+    border-radius: 30px;
+    z-index: 999;
+
+    input {
+      flex: 1;
+      width: 0;
+      border: 0;
+      font-size: 24px;
+      color: rgba(255, 255, 255, 0.5);
+      background: transparent;
+
+      &::placeholder {
+        color: inherit;
+      }
+    }
+    img {
+      flex-shrink: 0;
+      width: 25px;
+      height: 25px;
+    }
+  }
+}

+ 45 - 0
packages/mobile/src/views/Antiquity/index.vue

@@ -0,0 +1,45 @@
+<template>
+  <div class="antiquity">
+    <div class="antiquity-search">
+      <input v-model="keyword" type="search" placeholder="请输入文物名称" />
+      <img src="@/assets/images/search-min.png" />
+    </div>
+
+    <van-tabs swipeable shrink class="antiquity-tabs">
+      <van-tab v-for="item in tabs" :key="item.label" :title="item.label">
+        <List :list="item.list" />
+      </van-tab>
+    </van-tabs>
+  </div>
+</template>
+
+<script setup>
+import { ref, computed } from "vue";
+import { antiquityData } from "@lbc/base";
+import List from "./components/List.vue";
+
+const keyword = ref("");
+
+const tabs = computed(() => {
+  const regex = new RegExp(keyword.value, "i");
+
+  return [
+    {
+      label: "全部",
+      list: antiquityData.filter((item) => regex.test(item.name)),
+    },
+    {
+      label: "二维",
+      list: antiquityData.filter((i) => !i.link && regex.test(i.name)),
+    },
+    {
+      label: "三维",
+      list: antiquityData.filter((i) => !!i.link && regex.test(i.name)),
+    },
+  ];
+});
+</script>
+
+<style lang="scss" scoped>
+@use "./index.scss";
+</style>

+ 27 - 0
packages/mobile/src/views/Exhibition/index.scss

@@ -0,0 +1,27 @@
+.exhibition {
+  &-cover {
+    width: 100%;
+  }
+  &-nav {
+    display: flex;
+    flex-direction: column;
+    align-items: center;
+    justify-content: center;
+    margin-bottom: 68px;
+
+    > img {
+      margin: 85px 0 49px;
+      width: 310px;
+      height: 70px;
+    }
+    &-swiper {
+      width: 100%;
+
+      .swiper-slide {
+        img {
+          width: 100%;
+        }
+      }
+    }
+  }
+}

+ 64 - 0
packages/mobile/src/views/Exhibition/index.vue

@@ -0,0 +1,64 @@
+<template>
+  <div class="exhibition">
+    <img class="exhibition-cover" src="@/assets/images/banner_2-min.png" />
+
+    <div class="exhibition-nav">
+      <img src="@/assets/images/title_2-min.png" />
+
+      <Swiper
+        class="exhibition-nav-swiper"
+        :modules="[EffectCoverflow]"
+        :effect="'coverflow'"
+        :slides-per-view="2"
+        :centered-slides="true"
+        :initial-slide="1"
+        :coverflow-effect="{
+          rotate: 0,
+          stretch: 109 /* 控制两边的偏移距离 */,
+          depth: 0,
+          scale: 0.8 /* 两侧 slide 缩放比例 */,
+          modifier: 1,
+          slideShadows: false,
+        }"
+      >
+        <SwiperSlide v-for="item in LIST" :key="item.link">
+          <img :src="item.img" @click="goToDetail(item)" />
+        </SwiperSlide>
+      </Swiper>
+    </div>
+  </div>
+</template>
+
+<script setup>
+import { Swiper, SwiperSlide } from "swiper/vue";
+import { EffectCoverflow } from "swiper/modules";
+import "swiper/swiper-bundle.css";
+import { exhibitionData } from "@lbc/base";
+
+import Img1 from "@/assets/images/pic_8-min.png";
+import Img2 from "@/assets/images/pic_6-min.png";
+import Img3 from "@/assets/images/pic_7-min.png";
+
+const LIST = [
+  {
+    img: Img1,
+    link: exhibitionData[0].link,
+  },
+  {
+    img: Img2,
+    link: exhibitionData[1].link,
+  },
+  {
+    img: Img3,
+    link: exhibitionData[2].link,
+  },
+];
+
+const goToDetail = (item) => {
+  window.location = item.link;
+};
+</script>
+
+<style lang="scss" scoped>
+@use "./index.scss";
+</style>

+ 107 - 0
packages/mobile/src/views/Home/index.scss

@@ -0,0 +1,107 @@
+.home {
+  &-swiper {
+    img {
+      width: 100%;
+    }
+  }
+  &-nav {
+    display: flex;
+    flex-direction: column;
+    align-items: center;
+    justify-content: center;
+    height: 770px;
+    background: url("@/assets/images/bg-min.png") no-repeat center / cover;
+
+    > img {
+      margin: 85px 0 49px;
+      width: 310px;
+      height: 70px;
+    }
+    &-swiper {
+      width: 100%;
+
+      .swiper-slide {
+        img {
+          width: 100%;
+        }
+      }
+    }
+  }
+  &-list {
+    display: flex;
+    flex-wrap: wrap;
+    background: #f5ecd5;
+
+    > div {
+      flex: 0 0 50%;
+      display: flex;
+      align-items: center;
+      justify-content: center;
+      padding: 30px;
+      height: 350px;
+
+      &:first-child {
+        flex-direction: column;
+        gap: 88px;
+        color: #c90110;
+        font-family: "SourceHanSerifSC-Bold";
+        background: url("@/assets/images/bg-min.png") no-repeat center / contain;
+
+        h3 {
+          font-size: 48px;
+        }
+        p {
+          display: flex;
+          align-items: center;
+          justify-content: center;
+          width: 274px;
+          height: 74px;
+          font-size: 42px;
+          background: url("@/assets/images/pic-min.png") no-repeat center /
+            contain;
+        }
+      }
+      &:nth-child(4),
+      &:nth-child(5) {
+        background: #dbd0b3;
+      }
+      img {
+        width: 100%;
+        height: 100%;
+        object-fit: contain;
+      }
+    }
+  }
+  // &-footer {
+  //   padding: 57px 36px;
+  //   color: #efdba1;
+  //   background: #c90110;
+
+  //   &__title {
+  //     position: relative;
+  //     margin: 0 auto 36px;
+  //     width: 271px;
+  //     height: 51px;
+  //     border: 1px solid #efdba1;
+
+  //     span {
+  //       position: absolute;
+  //       top: 50%;
+  //       left: 50%;
+  //       font-size: 36px;
+  //       text-align: center;
+  //       font-weight: bold;
+  //       transform: translate(-50%, -50%);
+  //       width: 251px;
+  //       height: 71px;
+  //       line-height: 71px;
+  //       background: #c90110;
+  //       border: 1px solid #efdba1;
+  //     }
+  //   }
+  //   &__inner {
+  //     font-size: 30px;
+  //     line-height: 48px;
+  //   }
+  // }
+}

+ 41 - 0
packages/mobile/src/views/Media/Detail/index.scss

@@ -0,0 +1,41 @@
+.media-detail {
+  padding: calc(80px + 50px) 30px 0;
+
+  &-head {
+    position: fixed;
+    top: 0;
+    left: 0;
+    right: 0;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    padding: 40px 0;
+    background: white;
+    z-index: 1;
+
+    .logo {
+      width: 262px;
+      height: 50px;
+    }
+  }
+  &__back {
+    position: absolute;
+    top: 50%;
+    left: 30px;
+    width: 60px;
+    height: 60px;
+    transform: translateY(-50%);
+  }
+  video {
+    margin-top: 20px;
+    width: 100%;
+    height: 420px;
+    object-fit: contain;
+  }
+  h3 {
+    margin: 50px 0 40px;
+    font-size: 36px;
+    font-weight: bold;
+    color: #494949;
+  }
+}

+ 46 - 0
packages/mobile/src/views/Media/Detail/index.vue

@@ -0,0 +1,46 @@
+<template>
+  <div class="media-detail">
+    <div class="media-detail-head">
+      <img
+        class="media-detail__back"
+        src="@/assets/images/back-min.png"
+        @click="$router.back"
+      />
+      <img class="logo" src="@/assets/images/logo-min.png" />
+    </div>
+
+    <video
+      :src="getEnvImagePath(detail?.url)"
+      controls
+      controlsList="nodownload noplaybackrate"
+    />
+
+    <h3>{{ detail?.title }}</h3>
+
+    <div class="media-detail__content"></div>
+  </div>
+</template>
+
+<script setup>
+import { onMounted, computed } from "vue";
+import { mediaData, getEnvImagePath } from "@lbc/base";
+import { useRoute } from "vue-router";
+
+const route = useRoute();
+const detail = computed(() => mediaData[route.params.index]);
+
+onMounted(() => {
+  const list = document.getElementsByTagName("video");
+
+  for (let i = 0; i < list.length; i++) {
+    const item = list[i];
+    item.addEventListener("contextmenu", (e) => {
+      e.preventDefault();
+    });
+  }
+});
+</script>
+
+<style lang="scss" scoped>
+@use "./index.scss";
+</style>

+ 57 - 0
packages/mobile/src/views/Media/index.scss

@@ -0,0 +1,57 @@
+.media {
+  padding-bottom: 50px;
+
+  &-cover {
+    width: 100%;
+  }
+  &-title {
+    display: block;
+    margin: 50px auto 0;
+    width: 320px;
+    height: 80px;
+  }
+  &-tags {
+    display: flex;
+    align-items: center;
+    gap: 50px;
+    margin: 55px 40px 45px;
+
+    li {
+      width: 130px;
+      height: 60px;
+      line-height: 60px;
+      text-align: center;
+      font-size: 30px;
+      color: #adadad;
+      border-radius: 8px;
+      background: #f4f4f4;
+      cursor: pointer;
+
+      &.active {
+        color: white;
+        font-weight: bold;
+        background: #c90110;
+      }
+    }
+  }
+  &-list {
+    display: flex;
+    flex-wrap: wrap;
+    gap: 30px;
+    padding: 0 30px;
+  }
+  &-item {
+    flex: 0 0 calc(50% - 15px);
+
+    &__cover {
+      height: 280px;
+      border-radius: 10px;
+      overflow: hidden;
+    }
+    p {
+      color: #494949;
+      font-size: 30px;
+      text-align: center;
+    }
+  }
+}

+ 57 - 0
packages/mobile/src/views/Media/index.vue

@@ -0,0 +1,57 @@
+<template>
+  <div class="media">
+    <img class="media-cover" src="@/assets/images/banner_1-min.png" />
+
+    <img class="media-title" src="@/assets/images/title_3-min.png" />
+
+    <ul class="media-tags">
+      <li
+        v-for="(tab, index) in TABS"
+        :key="tab.id"
+        :class="{ active: activeTab === index }"
+        @click="activeTab = index"
+      >
+        {{ tab.label }}
+      </li>
+    </ul>
+
+    <ul class="media-list">
+      <li
+        v-for="(item, index) in mediaData.filter(
+          (i) => i.type === TABS[activeTab].id
+        )"
+        :key="index"
+        class="media-item"
+        @click="$router.push({ name: 'mediaDetail', params: { index } })"
+      >
+        <VanImage
+          class="media-item__cover"
+          :src="getEnvImagePath(item.img)"
+          fit="cover"
+        />
+        <p class="limit-line">{{ item.title }}</p>
+      </li>
+    </ul>
+  </div>
+</template>
+
+<script setup>
+import { ref } from "vue";
+import { mediaData, getEnvImagePath } from "@lbc/base";
+
+const activeTab = ref(0);
+const TABS = [
+  {
+    label: "专辑",
+    id: 0,
+  },
+  {
+    label: "宣传",
+    id: 1,
+  },
+];
+</script>
+
+<style lang="scss" scoped>
+@use "./index.scss";
+</style>

Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 6515 - 0
packages/pc/public/4dage.js


binární
packages/pc/public/img/bg.jpg


+ 51 - 0
packages/pc/public/model.html

@@ -0,0 +1,51 @@
+<!DOCTYPE html>
+<html lang="en">
+
+<head>
+  <meta charset="UTF-8">
+  <meta http-equiv="X-UA-Compatible" content="IE=edge">
+  <meta name="viewport" content="width=device-width, initial-scale=1.0">
+  <script src="./4dage.js"></script>
+  <title>Document</title>
+  <style>
+    html {
+      overflow: hidden;
+    }
+
+    .bacBox {
+      opacity: 1;
+      pointer-events: auto;
+      position: absolute;
+      z-index: 998;
+      top: 0;
+      left: 0;
+      width: 100%;
+      height: 100%;
+      background-color: #cfcfd0;
+      transition: all 1s;
+    }
+  </style>
+</head>
+
+<body>
+  <div id="ui"></div>
+  <div class="outerImg">
+    <img src="./img/bg.jpg" alt="">
+  </div>
+  <script>
+    let number = getQueryVariable("m");
+
+    console.log('ppppppppp',number);
+
+    window.autoRotate = true; // 是否自动旋转
+    fdage.embed('https://4dscene.4dage.com/culturalrelics/LBCJNG/4dage/' + number + '.4dage', {
+        width: 800,
+        height: 600,
+        autoStart: true,
+        fullFrame: true,
+        pagePreset: false
+    });
+  </script>
+</body>
+
+</html>

binární
packages/pc/src/assets/images/bg_1-min.png