|
@@ -1,62 +1,495 @@
|
|
|
<template>
|
|
|
- <div class="txtcon">
|
|
|
+ <div class="imagetxtcon">
|
|
|
<div class="title">
|
|
|
- <i class="iconfont icon-material_text"/>
|
|
|
- {{currentTag.hotspotTitle}}
|
|
|
+ <i class="iconfont icon-material_image title-icon" />
|
|
|
+ {{ currentTag.hotspotTitle }}
|
|
|
</div>
|
|
|
|
|
|
- <div class="txtbody" v-html="currentTag.articleInfo.html">
|
|
|
+ <div class="main-text-container">
|
|
|
+ <div class="image-left">
|
|
|
+ <div
|
|
|
+ class="realimgcon"
|
|
|
+ :class="{ fullimgcon: objectFit === 'contain' }"
|
|
|
+ >
|
|
|
+ <img
|
|
|
+ class="image"
|
|
|
+ :src="currentTag.imageTextInfo.imageList[currentIndex].ossPath"
|
|
|
+ :style="imageStyle"
|
|
|
+ @wheel.prevent="onImageWheel"
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+ <div class="toolbar-container">
|
|
|
+ <div class="toolbar">
|
|
|
+ <div class="left">
|
|
|
+ <i
|
|
|
+ v-if="currentTag.imageTextInfo.imageList.length > 1"
|
|
|
+ class="iconfont icon-material_preview_previous hover-tips"
|
|
|
+ :class="{ disabled: currentIndex === 0 }"
|
|
|
+ @click="onClickPrevious()"
|
|
|
+ >
|
|
|
+ <div>
|
|
|
+ <div class="remark">{{ $t("common.prev") }}</div>
|
|
|
+ </div>
|
|
|
+ </i>
|
|
|
+ <i
|
|
|
+ v-if="currentTag.imageTextInfo.imageList.length > 1"
|
|
|
+ class="iconfont icon-material_preview_next1 hover-tips append-splitter"
|
|
|
+ :class="{
|
|
|
+ disabled: currentIndex === currentTag.image.length - 1,
|
|
|
+ }"
|
|
|
+ @click="onClickNext()"
|
|
|
+ >
|
|
|
+ <div>
|
|
|
+ <div class="remark">{{ $t("common.next") }}</div>
|
|
|
+ </div>
|
|
|
+ </i>
|
|
|
+ </div>
|
|
|
+ <div class="right">
|
|
|
+ <i
|
|
|
+ :class="{ disabled: scaleRate >= 2 }"
|
|
|
+ class="iconfont icon-material_preview_enlarge hover-tips"
|
|
|
+ @click="onClickZoomIn()"
|
|
|
+ >
|
|
|
+ <div>
|
|
|
+ <div class="remark">{{ $t("common.zoomIn") }}</div>
|
|
|
+ </div>
|
|
|
+ </i>
|
|
|
+
|
|
|
+ <i
|
|
|
+ :class="{ disabled: scaleRate <= 0.5 }"
|
|
|
+ class="iconfont icon-material_preview_narrow hover-tips"
|
|
|
+ @click="onClickZoomOut()"
|
|
|
+ >
|
|
|
+ <div>
|
|
|
+ <div class="remark">{{ $t("common.zoomOut") }}</div>
|
|
|
+ </div>
|
|
|
+ </i>
|
|
|
+
|
|
|
+ <i
|
|
|
+ v-if="canFullScreen && objectFit === 'scale-down'"
|
|
|
+ class="iconfont icon-material_preview_full_screen hover-tips"
|
|
|
+ @click="onClickFullScreen()"
|
|
|
+ >
|
|
|
+ <div>
|
|
|
+ <div class="remark">{{ $t("common.fullScene") }}</div>
|
|
|
+ </div>
|
|
|
+ </i>
|
|
|
+
|
|
|
+ <i
|
|
|
+ v-if="canFullScreen && objectFit === 'contain'"
|
|
|
+ class="iconfont icon-material_preview_drop_out hover-tips"
|
|
|
+ @click="onClickCancelFullScreen()"
|
|
|
+ >
|
|
|
+ <div>
|
|
|
+ <div class="remark">{{ $t("common.exitFullScene") }}</div>
|
|
|
+ </div>
|
|
|
+ </i>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div class="audio-control">
|
|
|
+ <ui-icon @click="pauseAudio" type="player_pause" v-if="isPlaying"></ui-icon>
|
|
|
+ <ui-icon @click="playAudio" type="player_playback" v-else></ui-icon>
|
|
|
+ <span class="timeline">{{ time }} / {{ audioAllTime }}</span>
|
|
|
+ </div>
|
|
|
+ <audio
|
|
|
+ id="audioTag"
|
|
|
+ class="noshow"
|
|
|
+ autoplay
|
|
|
+ :src="currentTag.imageTextInfo.audio.ossPath"
|
|
|
+ ></audio>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <!-- 文字 -->
|
|
|
+ <div class="text-right" v-if="currentTag.imageTextInfo.text.length > 0">
|
|
|
+ <div class="txtbody" v-html="currentTag.imageTextInfo.text"></div>
|
|
|
+ </div>
|
|
|
</div>
|
|
|
+ <div class="btnmask"></div>
|
|
|
</div>
|
|
|
</template>
|
|
|
|
|
|
<script setup>
|
|
|
-import { reactive, defineEmits, onBeforeMount, onMounted, ref, watchEffect, computed, watch, nextTick } from "vue";
|
|
|
+import { nextTick } from "process";
|
|
|
+import { ref, computed, unref, onMounted, onUnmounted } from "vue";
|
|
|
import { useStore } from "vuex";
|
|
|
const store = useStore();
|
|
|
+const hasAudio = computed(
|
|
|
+ () => currentTag.value.imageTextInfo.audio.ossPath.length > 0
|
|
|
+);
|
|
|
+
|
|
|
+const scaleRate = ref(1);
|
|
|
+
|
|
|
+const objectFit = ref("scale-down");
|
|
|
+
|
|
|
+const canFullScreen = ref(true);
|
|
|
+
|
|
|
+const imageStyle = computed(() => {
|
|
|
+ return {
|
|
|
+ transform: `scale(${scaleRate.value})`,
|
|
|
+ objectFit: objectFit.value,
|
|
|
+ };
|
|
|
+});
|
|
|
+
|
|
|
+const currentTag = computed(() => store.getters["tags/currentTag"]);
|
|
|
+
|
|
|
+const currentIndex = ref(0);
|
|
|
+
|
|
|
+const onImageWheel = (e) => {
|
|
|
+ if (e.deltaY < 0) {
|
|
|
+ let scle = scaleRate.value * 1.1;
|
|
|
+ if (scle > 2) {
|
|
|
+ scaleRate.value = 2;
|
|
|
+ } else {
|
|
|
+ scaleRate.value = scle;
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ let scle = scaleRate.value * 0.9;
|
|
|
+ if (scle < 0.5) {
|
|
|
+ scaleRate.value = 0.5;
|
|
|
+ } else {
|
|
|
+ scaleRate.value = scle;
|
|
|
+ }
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+const onClickZoomIn = () => {
|
|
|
+ scaleRate.value = Math.min(scaleRate.value * 1.1, 2);
|
|
|
+};
|
|
|
+const onClickZoomOut = () => {
|
|
|
+ scaleRate.value = Math.max(scaleRate.value * 0.9, 0.5);
|
|
|
+};
|
|
|
+
|
|
|
+const onClickPrevious = () => {
|
|
|
+ if (currentIndex.value > 0) {
|
|
|
+ currentIndex.value--;
|
|
|
+ }
|
|
|
+};
|
|
|
+const onClickNext = () => {
|
|
|
+ if (
|
|
|
+ currentIndex.value <
|
|
|
+ unref(currentTag).imageTextInfo.imageList.length - 1
|
|
|
+ ) {
|
|
|
+ currentIndex.value++;
|
|
|
+ console.log("currentIndex", unref(currentIndex));
|
|
|
+ }
|
|
|
+};
|
|
|
|
|
|
-const currentTag = computed(() => store.getters['tags/currentTag'])
|
|
|
-
|
|
|
+const onClickFullScreen = () => {
|
|
|
+ scaleRate.value = 1;
|
|
|
+ objectFit.value = "contain";
|
|
|
+};
|
|
|
+const onClickCancelFullScreen = () => {
|
|
|
+ scaleRate.value = 1;
|
|
|
+ objectFit.value = "scale-down";
|
|
|
+};
|
|
|
+function transTime(time) {
|
|
|
+ var duration = parseInt(time);
|
|
|
+ var minute = parseInt(duration / 60);
|
|
|
+ var sec = (duration % 60) + "";
|
|
|
+ var isM0 = ":";
|
|
|
+ if (minute == 0) {
|
|
|
+ minute = "00";
|
|
|
+ } else if (minute < 10) {
|
|
|
+ minute = "0" + minute;
|
|
|
+ }
|
|
|
+ if (sec.length == 1) {
|
|
|
+ sec = "0" + sec;
|
|
|
+ }
|
|
|
+ return minute + isM0 + sec;
|
|
|
+}
|
|
|
+const time = ref("00:00");
|
|
|
+const audioAllTime = ref("");
|
|
|
+const isPlaying = ref(false);
|
|
|
+onMounted(() => {
|
|
|
+ const audio = document.querySelector("#audioTag");
|
|
|
+ nextTick(() => {
|
|
|
+ if (unref(hasAudio)) {
|
|
|
+ audio.addEventListener("loadedmetadata", (e) => {
|
|
|
+ audioAllTime.value = transTime(e.currentTarget.duration);
|
|
|
+ console.log("e", unref(audioAllTime));
|
|
|
+ isPlaying.value = true;
|
|
|
+ });
|
|
|
+
|
|
|
+ console.log("audio", audio);
|
|
|
+ audio.addEventListener(
|
|
|
+ "playing",
|
|
|
+ function (e) {
|
|
|
+ isPlaying.value = true;
|
|
|
+ },
|
|
|
+ false
|
|
|
+ );
|
|
|
+ audio.addEventListener(
|
|
|
+ "pause",
|
|
|
+ function (e) {
|
|
|
+ isPlaying.value = false;
|
|
|
+ },
|
|
|
+ false
|
|
|
+ );
|
|
|
+ audio.addEventListener(
|
|
|
+ "timeupdate",
|
|
|
+ function (event) {
|
|
|
+ time.value = transTime(event.target.currentTime);
|
|
|
+ },
|
|
|
+ false
|
|
|
+ );
|
|
|
+ }
|
|
|
+ });
|
|
|
+});
|
|
|
+
|
|
|
+const playAudio = () => {
|
|
|
+ const audio = document.querySelector("#audioTag");
|
|
|
+ audio && audio.play();
|
|
|
+};
|
|
|
+const pauseAudio = () => {
|
|
|
+ const audio = document.querySelector("#audioTag");
|
|
|
+ audio && audio.pause();
|
|
|
+};
|
|
|
+onUnmounted(()=>{
|
|
|
+ pauseAudio();
|
|
|
+})
|
|
|
</script>
|
|
|
|
|
|
<style lang="scss" scoped>
|
|
|
-.txtcon{
|
|
|
+.imagetxtcon {
|
|
|
display: flex;
|
|
|
- align-items: flex-start;
|
|
|
+ align-items: center;
|
|
|
justify-content: center;
|
|
|
height: 100%;
|
|
|
width: 100%;
|
|
|
- padding-top: 70px;
|
|
|
-
|
|
|
- .txtbody{
|
|
|
- width: 744px;
|
|
|
- background: rgba(0,0,0,0.6);
|
|
|
- border-radius: 10px;
|
|
|
- font-size: 14px;
|
|
|
- color: #fff;
|
|
|
- padding: 30px;
|
|
|
- max-height: calc(100vh - 100px);
|
|
|
- overflow-y: auto;
|
|
|
- line-height: 1.5;
|
|
|
- :deep(p) {
|
|
|
- line-height: 24px;
|
|
|
+
|
|
|
+ > video {
|
|
|
+ max-width: 960px;
|
|
|
+ height: 540px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .main-text-container {
|
|
|
+ width: 100%;
|
|
|
+ height: 550px;
|
|
|
+ // max-width: 1920px;
|
|
|
+ max-width: 998px;
|
|
|
+ display: flex;
|
|
|
+ background: rgba(0, 0, 0, 0.8);
|
|
|
+ .image-left {
|
|
|
+ flex: 1 1 58%;
|
|
|
+ overflow: hidden;
|
|
|
+ position: relative;
|
|
|
+ }
|
|
|
+ .text-right {
|
|
|
+ flex: 0 0 32%;
|
|
|
+ overflow: hidden;
|
|
|
+ color: white;
|
|
|
+ .txtbody {
|
|
|
+ display: block;
|
|
|
+ word-wrap: break-word;
|
|
|
+ margin: 30px;
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
- .title{
|
|
|
+ .title {
|
|
|
position: absolute;
|
|
|
left: 20px;
|
|
|
top: 20px;
|
|
|
height: 36px;
|
|
|
line-height: 36px;
|
|
|
padding: 0 16px;
|
|
|
- background: rgba(0,0,0,0.6);
|
|
|
+ background: rgba(0, 0, 0, 0.6);
|
|
|
border-radius: 20px;
|
|
|
color: #fff;
|
|
|
font-size: 14px;
|
|
|
z-index: 999;
|
|
|
- >i{
|
|
|
+
|
|
|
+ > i {
|
|
|
margin-right: 4px;
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
+ .toolbar-container {
|
|
|
+ position: absolute;
|
|
|
+ bottom: 14px;
|
|
|
+ left: 50%;
|
|
|
+ transform: translateX(-50%);
|
|
|
+ display: flex;
|
|
|
+ flex-direction: row;
|
|
|
+ }
|
|
|
+ .iconfont {
|
|
|
+ cursor: pointer;
|
|
|
+ color: white;
|
|
|
+ margin: 0 12px;
|
|
|
+ font-size: 22px;
|
|
|
+ // width: 24px;
|
|
|
+ // height: 24px;
|
|
|
+
|
|
|
+ &.disabled {
|
|
|
+ opacity: 0.5;
|
|
|
+ pointer-events: none;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ .toolbar {
|
|
|
+ height: 60px;
|
|
|
+ display: flex;
|
|
|
+ justify-content: center;
|
|
|
+ align-items: center;
|
|
|
+ width: 292px;
|
|
|
+ height: 60px;
|
|
|
+ border-radius: 8px;
|
|
|
+ background: rgba(0, 0, 0, 0.6);
|
|
|
+
|
|
|
+ z-index: 2;
|
|
|
+ .left {
|
|
|
+ width: 122px;
|
|
|
+ height: 100%;
|
|
|
+ display: flex;
|
|
|
+ justify-content: center;
|
|
|
+ align-items: center;
|
|
|
+ position: relative;
|
|
|
+ &::after {
|
|
|
+ pointer-events: none;
|
|
|
+ content: "";
|
|
|
+ position: absolute;
|
|
|
+ height: 20px;
|
|
|
+ width: 2px;
|
|
|
+ right: -1px;
|
|
|
+ top: 50%;
|
|
|
+ transform: translateY(-50%);
|
|
|
+ // right: -18px;
|
|
|
+ // top: -4px;
|
|
|
+ // font-size: 20px;
|
|
|
+ background: rgba(255, 255, 255, 0.5);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ .right {
|
|
|
+ width: 169px;
|
|
|
+ height: 100%;
|
|
|
+ display: flex;
|
|
|
+ justify-content: center;
|
|
|
+ align-items: center;
|
|
|
+ }
|
|
|
+
|
|
|
+ > span {
|
|
|
+ color: #fff;
|
|
|
+ text-align: center;
|
|
|
+ display: inline-block;
|
|
|
+ min-width: 50px;
|
|
|
+ }
|
|
|
+
|
|
|
+ // .append-splitter {
|
|
|
+ // &::after {
|
|
|
+ // pointer-events: none;
|
|
|
+ // content: "|";
|
|
|
+ // position: absolute;
|
|
|
+ // right: -18px;
|
|
|
+ // top: -4px;
|
|
|
+ // font-size: 20px;
|
|
|
+ // color: rgba(255, 255, 255, 0.5);
|
|
|
+ // }
|
|
|
+ // }
|
|
|
+ }
|
|
|
+ .audio-control {
|
|
|
+ height: 60px;
|
|
|
+ border-radius: 8px;
|
|
|
+ background: rgba(0, 0, 0, 0.6);
|
|
|
+ width: 181px;
|
|
|
+ margin-left: 20px;
|
|
|
+ display: flex;
|
|
|
+ justify-content: center;
|
|
|
+ align-items: center;
|
|
|
+ .iconfont {
|
|
|
+ margin-left: 0;
|
|
|
+ }
|
|
|
+ .timeline {
|
|
|
+ color: #fff;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .realimgcon {
|
|
|
+ width: 100%;
|
|
|
+ height: 100%;
|
|
|
+ z-index: 1;
|
|
|
+
|
|
|
+ .image {
|
|
|
+ width: 100%;
|
|
|
+ height: 100%;
|
|
|
+ // top: 0;
|
|
|
+ // left: 0;
|
|
|
+ // width: 100%;
|
|
|
+ // height: 100%;
|
|
|
+ // position: absolute;
|
|
|
+ // z-index: 1;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .fullimgcon {
|
|
|
+ top: 0;
|
|
|
+ height: 100%;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+.hover-tips,
|
|
|
+.hover-tips-warn {
|
|
|
+ position: relative;
|
|
|
+ font-size: 18px;
|
|
|
+
|
|
|
+ &:hover {
|
|
|
+ > div {
|
|
|
+ display: block;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // tip的方框
|
|
|
+ > div {
|
|
|
+ background: rgba(0, 0, 0, 0.6);
|
|
|
+ border: none;
|
|
|
+ cursor: default;
|
|
|
+ display: none;
|
|
|
+ z-index: 10000;
|
|
|
+ position: absolute;
|
|
|
+ left: 50%;
|
|
|
+ transform: translateX(-50%);
|
|
|
+ top: -50px;
|
|
|
+ color: #fff;
|
|
|
+ pointer-events: none;
|
|
|
+ text-align: center;
|
|
|
+ word-break: keep-all;
|
|
|
+ padding: 0 8px;
|
|
|
+ font-size: 12px;
|
|
|
+ border-radius: 3px;
|
|
|
+
|
|
|
+ // tip的箭头
|
|
|
+ &::before {
|
|
|
+ border: 7px solid transparent;
|
|
|
+ border-top: 7px solid rgba(0, 0, 0, 0.6);
|
|
|
+ width: 0;
|
|
|
+ height: 0px;
|
|
|
+ content: "";
|
|
|
+ display: inline-block;
|
|
|
+ position: absolute;
|
|
|
+ bottom: -14px;
|
|
|
+ left: 50%;
|
|
|
+ transform: translateX(-50%);
|
|
|
+ }
|
|
|
+
|
|
|
+ // tip的文字
|
|
|
+ .remark {
|
|
|
+ line-height: 2.5;
|
|
|
+ color: #fff;
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+.btnmask {
|
|
|
+ width: 100%;
|
|
|
+ position: absolute;
|
|
|
+ bottom: 0;
|
|
|
+ left: 0;
|
|
|
+ right: 0;
|
|
|
+ pointer-events: none;
|
|
|
+ opacity: 0.5;
|
|
|
+ z-index: 1;
|
|
|
+ height: 60px;
|
|
|
+ background: linear-gradient(
|
|
|
+ 180deg,
|
|
|
+ rgba(0, 0, 0, 0) 0%,
|
|
|
+ rgba(0, 0, 0, 0.7) 52%,
|
|
|
+ #000000 100%
|
|
|
+ );
|
|
|
}
|
|
|
-</style>
|
|
|
+</style>
|