info.vue 10.9 KB
<!--
 * @Date: 2024-09-15 22:08:49
 * @LastEditors: hookehuyr hookehuyr@gmail.com
 * @LastEditTime: 2024-09-23 15:43:14
 * @FilePath: /map-demo/src/views/bieyuan/info.vue
 * @Description: 文件描述
-->
<template>
  <div class="info-page">
    <div class="info-header-wrapper">
      <div v-if="showBack && page_details.banner?.length" style="position: absolute; top: 1rem; left: 0.5rem; z-index: 9;">
        <van-icon name="arrow-left" color="white" size="1.75rem" @click="goBack()" />
      </div>
      <van-config-provider :theme-vars="themeVars">
        <van-swipe class="my-swipe" indicator-color="#DD7850" lazy-render :autoplay="5000">
          <van-swipe-item v-for="image in page_details.banner" :key="image">
            <van-image fit="fill" width="100%" height="15rem" :src="image" />
          </van-swipe-item>
        </van-swipe>
      </van-config-provider>
      <div class="header-z"></div>
    </div>
    <div class="info-content-wrapper">
      <div class="info-header">
        <div style="display: flex; justify-content: space-between;">
          <p class="info-title">{{ page_details.name }}</p>
          <div @click="goTo()" class="info-btn">前往</div>
        </div>
        <div class="info-sub-title">{{ page_details.note }}</div>
      </div>
      <div style="margin-top: 0.5rem;">
        <van-config-provider :theme-vars="themeVars">
          <van-tabs ref="tabsRef" v-model:active="active" @click-tab="clickTab" color="#DD7850" title-active-color="#DD7850" title-inactive-color="#DD7850" sticky animated>
            <van-tab title="介 绍">
              <div class="info-content">
                <div v-html="page_details.introduction" style="padding: 0 1rem;"></div>
              </div>
            </van-tab>
            <van-tab title="故 事">
              <div class="info-content">
                <div v-html="page_details.story" style="padding: 0 1rem;"></div>
              </div>
            </van-tab>
            <van-tab title="体 验" id="tab-3">
              <div class="info-content">
                <div v-html="page_details.experience" style="padding: 0 1rem;"></div>
              </div>
              <div v-if="page_details.experience_audio.length" class="audio-wrapper">
                <div :class="['audio-item', play_audio_index === index ? 'click' : '']" v-for="(item, index) in page_details.experience_audio" :key="index">
                  <div>{{ item.description }}</div>
                  <!-- <div :class="['audio-icon', play_audio_index === index ? 'click' : '']"></div> -->
                  <van-icon @click="stopAudio(item, index)" v-if="item.play" size="2rem" name="stop-circle-o" color="#DD7850" />
                  <van-icon v-else @click="playAudio(item, index)" size="2rem" name="https://cdn.ipadbiz.cn/bieyuan/map/icon/audio_icon.png" />
                </div>
              </div>
            </van-tab>
          </van-tabs>
        </van-config-provider>
      </div>
    </div>
    <div class="info-logo">
      <van-image width="3rem" height="3rem" fit="contain" src="https://cdn.ipadbiz.cn/bieyuan/map/icon/scan_logo.png" />
    </div>

    <van-toast v-model:show="show_toast" style="padding: 0">
      <template #message>
        <p style="padding: 0.5rem 1rem;">{{ toast_text }}</p>
      </template>
    </van-toast>
    </div>
</template>

<script setup>
import { ref, watch } from 'vue'
import { useRoute, useRouter } from 'vue-router'

import { storeToRefs } from 'pinia'
import { mainStore } from '@/store';

import $ from 'jquery';

import { mapAPI } from '@/api/map.js'

const store = mainStore();
const { audio_status, audio_entity } = storeToRefs(store);

const $route = useRoute();
const $router = useRouter();

const themeVars = ref({
  swipeIndicatorInactiveBackground: '#fff',
  swipeIndicatorMargin: '1.5rem',
  tabFontSize: '0.95rem',
});

const props = defineProps({
  info: Object,
  height: Number
});

const page_details = ref({});

watch(
  () => props.info,
  (v) => {
    if (v.details.length) {
      page_details.value = { ...v.details[0], position: v.position, path: v.path };
      // 获取浏览器可视范围的高度
      $('.info-page').height(props.height + 'px');
    }
  }
)

const images = ref([
  'https://cdn.ipadbiz.cn/bieyuan/map/swiper_img.png',
  'https://cdn.ipadbiz.cn/bieyuan/map/Mix_20230612_201951.png',
  'https://cdn.ipadbiz.cn/bieyuan/map/Mix_20240815_211927.png',
]);

const active = ref(0);
const play_audio_index = ref(null);

const audioList = ref([{
  text: '5分钟观呼吸',
  src: 'https://cdn.ipadbiz.cn/bieyuan/map/audio/%E6%AD%A3%E5%BF%B5%E5%91%BC%E5%90%B8%EF%BC%8810%E5%88%86%E9%92%9F%EF%BC%89.mp3',
  play: false,
}, {
  text: '10分钟正念静坐',
  src: 'https://cdn.ipadbiz.cn/bieyuan/map/audio/%E5%8D%81%E5%88%86%E9%92%9F%E6%AD%A3%E5%BF%B5%E9%9D%99%E5%9D%9020210510.mp3',
  play: false,
}, {
  text: '15分钟正念静坐',
  src: 'https://cdn.ipadbiz.cn/bieyuan/map/audio/%E5%8D%81%E5%88%86%E9%92%9F%E6%AD%A3%E5%BF%B5%E9%9D%99%E5%9D%9020210510.mp3',
  play: false,
}])

const playAudio = (item, index) => {
  audioList.value.forEach(item => item.play = false);
  audio.value.src = item.src;
  // 后台有播放器运行时,先暂停
  if (audio_status.value === 'play'){
    audio_entity.value.pause();
  }
  play_audio_index.value = index;
  let play_status = audio.value.play() // 播放
  if (play_status) {
    console.warn('start');
    play_status.then(() => {
      console.warn('success');
      item.play = true;
      // 存放到pinia里面控制
      store.changeAudio(audio.value);
      store.changeAudioSrc(audio.value.src);
      store.changeAudioStatus('play');
    }).catch((e) => {
      // 失败
      console.log('Operation is too fast, audio play fails')
    })
  }
}

const stopAudio = (item, index) => {
  item.play = false;
  audio.value.pause();
}

const audio = ref(new Audio());

onMounted(async () => {
  // 通过ID查询到标记点详情
  if (!props.info) {
    let id = $route.query.id;
    const { data } = await mapAPI({ i: id });
    const raw_list = data.list[0].list; // 获取标记点列表
    const marker_id = $route.query.marker_id;
    const current_marker = raw_list.filter(item => item.id == marker_id)[0];
    //
    page_details.value = { ...current_marker.details[0], position: current_marker.position, path: current_marker.path };
    // 富文本转义, 分割线样式转换
    page_details.value.introduction = page_details.value.introduction?.replace(/\<hr\>/g, '<div class="van-hairline--bottom" style="margin: 1rem 0;"></div>')
    page_details.value.story = page_details.value.story?.replace(/\<hr\>/g, '<div class="van-hairline--bottom" style="margin: 1rem 0;"></div>')
    page_details.value.experience = page_details.value.experience?.replace(/\<hr\>/g, '<div class="van-hairline--bottom" style="margin: 1rem 0;"></div>')
    console.log("🚀 ~ file: info.vue:177 ~ onMounted ~ page_details.value:", page_details.value);
  }
});

onUnmounted(() => { // 离开页面时关闭音频播放
  audio.value.pause();
  store.changeAudioStatus('pause');
})

const audio_play = (src, index) => {
  audio.value.src = src;
}

const outerStopAudio = () => {
  audio.value.pause();
}

const emit = defineEmits(["closeFloat", 'route']);

const show_toast = ref(false);
const toast_text = ref('');

const goTo = () => { // 打开标记地图显示
  // 没有关联导航提示
  if (page_details.value.path.length <= 1) {
    show_toast.value = true;
    toast_text.value = '该标记点没有关联导航';
    return;
  }
  //
  if ($router.currentRoute.value.path === '/bieyuan/info') { // 详情页
    $router.push({
      path: '/bieyuan/map',
      query: {
        id: $route.query.id,
        marker_id: $route.query.marker_id
      }
    })
  } else { // 地图页
    //
    emit("closeFloat", false);
    //
    emit("route", {name: '参观路径', path: page_details.value.path});
  }
}

const goBack = () => {
  $router.go(-1);
}

const showBack = computed(() => $router.currentRoute.value.path === '/bieyuan/info');

const voicePause = () => {
  audio.value.pause();
  store.changeAudioStatus('pause');
}

const tabsRef = ref(null);
const clickTab = (evt) => {
  tabsRef.value.resize();
}

watch(
  () => audio_status.value,
  (v) => {
    if (v === 'pause') {
      voicePause();
      audioList.value.forEach(item => item.play = false);
    }
  },
  { immediate: true }
);

defineExpose({
  outerStopAudio
})
</script>

<style lang="less">
.info-page {
  background-color: #EBEBEB;
  height: 100vh;
  overflow: scroll;
  position: relative;
  .info-header-wrapper {
    position: relative;
    min-height: 2rem;
    .header-z {
      position: absolute;
      bottom: 0;
      left: 0;
      right: 0;
      height: 1rem;
      box-shadow: rgba(241, 242, 248, 0.3) 0px -3px 20px 12px;
      background-color: #f7f7f7;
      margin: 0 1rem;
      border-top-left-radius: 0.5rem;
      border-top-right-radius: 0.5rem;
    }
  }
  .info-content-wrapper {
    box-shadow: 0px -3px 6px 0 rgba(241, 242, 248, 0.8);
    margin: 1rem;
    margin-top: 0;
    // padding: 1rem;
    border-bottom-left-radius: 0.5rem;
    border-bottom-right-radius: 0.5rem;
    background-color: white;
    .info-header {
      padding: 1rem 2rem 0;
      // display: flex;
      // justify-content: space-between;
      // align-items: center;
      .info-title {
        font-size: 1.25rem;
        margin-bottom: 0.5rem;
      }
      .info-sub-title {
        font-size: 0.85rem;
        color: #A0A8B1;
        line-height: 1.75;
      }
      .info-btn {
        width: 3rem;
        height: 1.5rem;
        border: 1px solid #DD7850;
        color: #DD7850;
        border-radius: 0.8rem;
        font-size: 0.85rem;
        text-align: center;
        line-height: 1.5rem;
      }
    }
    .info-content {
      color: #47525F;
      padding: 1rem;
      line-height: 1.75;
      p {
        line-height: 1.75;
        // padding: 0 0.85rem;
        text-align: justify;
        img {
          width: 100%;
        }
      }
    }
    .audio-wrapper {
      padding: 1rem;
      .audio-item {
        color: #47525F;
        display: flex;
        justify-content: space-between;
        align-items: center;
        padding: 1rem;
        background-color: #FFF;
        border-radius: 0.25rem;
        margin: 1rem;
        box-shadow: 0 0 0.5rem rgba(0, 0, 0, 0.1);
        &.click {
          border: 1px solid #DD7850;
        }
        .audio-icon {
          width: 2rem;
          height: 2rem;
          background-image: url('https://cdn.ipadbiz.cn/bieyuan/map/icon/audio_icon.png'); /* 使用上传的图标 */
          background-size: cover;
          &.click {
            animation: pulse 1.5s infinite;
          }
        }

        @keyframes pulse {
          0% {
            transform: scale(1);
          }
          50% {
            transform: scale(1.2);
          }
          100% {
            transform: scale(1);
          }
        }
      }
    }
  }
  .info-logo {
    display: flex;
    justify-content: center;
    margin: 3rem;
  }

  .van-tabs__wrap {
    border-bottom: 1px solid #F3F3F3;
  }
}
</style>