Files
91fans_h5/src/views/acgModule/detail-video/index.vue
T
2026-09-15 15:25:05 +07:00

469 lines
12 KiB
Vue

<template>
<div ref="rootRef" class="video-detail-wrap">
<SvgIcon name="back_white_round" class="back" @click="$router.go(-1)" />
<template v-if="state.info.id">
<div class="movieBox">
<div class="videoAdv" v-if="state.advsList?.length && state.isShowAdv">
<div class="advVideoBox">
<AdvSwiper :list="state.advsList" :autoplay="2000" v-if="state.advsList.length" class="adv" />
<div class="timer_close" v-if="isVip">
<div v-if="state.timer > 0">
{{ state.timer }}s |
<span @click="state.isShowAdv = false">关闭广告</span>
</div>
<div v-else><span @click="state.isShowAdv = false">关闭</span></div>
</div>
<div class="timer_close" v-else>
<div v-if="state.timer > 0">
{{ state.timer }}s |
<span @click="vipJump">VIP可关闭广告</span>
</div>
<div v-else><span @click="state.isShowAdv = false">关闭</span></div>
</div>
</div>
</div>
<template v-else>
<AnimationHorizontalVideoInfo
:videoInfo="currentItem"
:mediaInfo="state.info"
v-if="currentItem?.videoUrl && showVideo"
:key="currentItem?.episodeNumber"
@buySuccess="beforeBuySuccess"
/>
</template>
</div>
<CountdownBanner
@openMemberPay="showPaymentStatusPopup = true"
/>
<div class="changeLineBox flex-vcenter" :class="!isVip ? 'top258' : ''">
<div class="flex-vcenter" @click.stop="openChangeRoadLine">
<SvgIcon name="changeLineIcon" />
<span>线路切换</span>
</div>
</div>
<van-tabs
v-model:active="tabActive"
:animated="true"
@rendered="onTabRendered"
class="tabs"
:class="{ newUser: !isVip }"
>
<van-tab :title="item.label" v-for="item in itemList" :key="item.value" :name="prefixKey + item.value">
<template v-slot:title>
{{ item.label }}
<Teleport to=".van-tabs__nav">
<span v-if="item.value === 2" class="countComment" :class="{ active: tabActive === prefixKey + 2 }">
{{ watchCount(state.info.countComment) }}
</span>
</Teleport>
</template>
<!--
@selectEpisode="selectEpisode" -->
<Introduction
v-if="tabIsInit(prefixKey + item.value) && item.value == 1 && state.info.id"
ref="introductionRef"
:info="state.info"
@updateCollected="updateCollected"
@updateLiked="updateLiked"
@updateItem="updateItem"
></Introduction>
<CommentBox
v-else-if="tabIsInit(prefixKey + item.value) && item.value == 2 && state.info.id"
:id="prefixKey + item.value"
ref="dvComment"
:isOpen="true"
renderType="normal"
objType="cartoon"
theme="black"
class="comment-comp"
@addComment="state.info.countComment += 1"
:showCollected="false"
></CommentBox>
</van-tab>
</van-tabs>
</template>
<Loading v-else />
</div>
<!-- 切换路线框 -->
<SelectionLinePopup :is-open="showChangeRoadLine" @closeSelectLine="closeSelectLine" @close="closeChangeRoadLine" />
<!-- 开通会员 -->
<PaymentStatusPopup
:show="showPaymentStatusPopup"
:video-id="String(state.info?.id || '')"
@close="closePaymentStatusPopup"
/>
</template>
<script lang="ts">
export default {
name: 'AcgVideoDetail',
};
</script>
<script lang="ts" setup>
import { ref, onUnmounted, nextTick, provide, computed, toRaw, watch, Ref, onMounted, defineAsyncComponent } from 'vue';
import { HistoryVideoType, AdType } from '@/enums/app';
import mediaInfo from '../utils/mediaInfo';
import { useUserStore } from '@/stores/user';
import MineApi from '@/service/mine';
import { watchCount } from '@/utils/index';
// import { Pay_emit } from '../utils/pay-mitt';
import type { PayInfo } from '../utils/pay-mitt';
import Introduction from './components/introduction.vue';
import tabAction from '@/utils/tabAction';
import { useRoute, useRouter } from 'vue-router';
import { getAdItem } from '@/utils/getConfig';
import CountdownBanner from '@/components/CountdownBanner/index.vue';
const PaymentStatusPopup = defineAsyncComponent(() => import('@/components/paymentStatusPopup/index.vue'));
const route = useRoute();
const router = useRouter();
const currentItem = ref({}) as Ref<Types.Acg.MediaContentListItem>;
const itemList = [
{
label: '简介',
value: 1,
},
{
label: '评论',
value: 2,
},
];
const showPaymentStatusPopup = ref(false);
const closePaymentStatusPopup = () => {
showPaymentStatusPopup.value = false;
};
const prefixKey = 'acg_video_';
const { tabActive, tabIsInit, onTabRendered } = tabAction(true);
const { isVip } = useUserStore();
const showVideo = ref(true);
const { state, historyData, addHistory } = mediaInfo({ dbType: HistoryVideoType.AcgVideo });
provide('historyData', historyData);
const showChangeRoadLine = ref(false);
// ------------------- 广告相关逻辑完善 -------------------
const vipJump = () => {
router.push('/member');
};
// 获取广告
const getRandomAdv = () => {
state.advsList = <any>[];
let advs = getAdItem(AdType.NEW_FREE_VIDEO).filter((item: any) => item.watchTime > 0);
if (advs.length > 0) {
state.advsList = advs;
if (!(isVip || historyData.value?.mediaStatus?.hasPaid)) {
state.isShowAdv = true;
return;
}
}
state.isShowAdv = false;
};
// 倒计时
const countdown = () => {
let advs = getAdItem(AdType.NEW_FREE_VIDEO);
if (advs && advs.length) {
state.timer = getAdItem(AdType.NEW_FREE_VIDEO)[0].watchTime;
} else {
state.timer = 0;
}
if (state.countdowner) {
clearInterval(state.countdowner);
}
state.countdowner = setInterval(() => {
state.timer--;
if (state.timer <= 0) {
clearInterval(state.countdowner);
state.countdowner = null;
}
}, 1000);
};
onUnmounted(() => {
state.countdowner && clearInterval(state.countdowner);
});
// 只在动漫数据获取到后再处理广告
watch(
() => currentItem.value,
(val) => {
if (val) {
getRandomAdv();
if (state.advsList.length > 0) {
countdown();
}
}
},
{ immediate: true },
);
const openChangeRoadLine = () => {
showChangeRoadLine.value = true;
};
const closeChangeRoadLine = () => {
showChangeRoadLine.value = false;
};
// 关闭线路选择
const closeSelectLine = () => {
showVideo.value = false;
showChangeRoadLine.value = false;
nextTick(() => {
showVideo.value = true;
});
};
const dvComment = ref();
const initComment = () => {
dvComment.value[0].state.videoInfo = toRaw(state.info);
dvComment.value[0].getCommentList();
};
watch(
dvComment,
(el) => {
if (!el) return;
initComment();
},
{
once: true,
},
);
const updateCollected = (collected: boolean) => {
state.info.mediaStatus.hasCollected = collected;
state.info.countCollect = state.info.countCollect + (collected ? +1 : -1);
};
const updateLiked = (liked: boolean) => {
state.info.mediaStatus.hasLiked = liked;
state.info.countLike = state.info.countLike + (liked ? +1 : -1);
};
const updateItem = (item: any) => {
currentItem.value = item;
if (route.query.tabActive) {
tabActive.value = (prefixKey + route.query.tabActive) as string;
}
};
const introductionRef = ref();
const beforeBuySuccess = (obj: PayInfo) => {
if (obj.type === 'all') {
state.info.mediaStatus.hasPaid = true;
historyData.value.mediaStatus.hasPaid = true;
addHistory(Number(route.query.episodeNumber || 1));
}
introductionRef.value[0].buySuccess(obj.type);
};
</script>
<style lang="scss" scoped>
.video-detail-wrap {
height: 100%;
overflow: hidden;
.back {
width: 24px;
height: 24px;
flex-shrink: 0;
position: fixed;
top: 16px;
left: 16px;
z-index: 800;
}
.movieBox {
width: 375px;
height: 212px;
position: sticky;
top: 0;
left: 0;
//margin-left: -6px;
z-index: 2;
.videoAdv {
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
z-index: 99999;
width: 100%;
height: 212px;
background: rgba(0, 0, 0, 1);
.advVideoBox {
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
z-index: 10;
.timer_close {
font-size: 12px;
position: absolute;
top: 10px;
right: 10px;
z-index: 10;
background: rgba(0, 0, 0, 0.4);
padding: 4px 12px;
border-radius: 40px;
color: white;
// span {
// color: #ff7f0f;
// }
}
.adv {
height: 211px;
width: 375px;
overflow: hidden;
:deep(.van-swipe-item) {
height: 211px !important;
width: 375px !important;
}
}
}
}
}
.newUserBg {
height: 46px;
background: url('@/assets/images/png/newUserBg.png') no-repeat;
background-size: 100% 100%;
font-size: 12px;
font-weight: 500;
padding-left: 98px;
padding-right: 9px;
color: #fff;
&.noNewUser {
// background-image: url('@/assets/images/png/newUserBg2.png');
background-image: url('@/assets/images/png/newUserBg.png');
}
.nowOpenBtn {
color: #000;
background: linear-gradient(270deg, #e3b284 0%, #feeede 100%);
background-clip: text;
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
font-size: 10px;
display: flex;
align-items: center;
svg {
width: 14px;
height: 14px;
margin-left: 4px;
}
}
}
.changeLineBox {
position: absolute;
top: 212px;
right: 16px;
z-index: 2;
height: 44px;
width: 100px;
justify-content: flex-end;
color: #989898;
font-size: 12px;
font-style: normal;
font-weight: 400;
svg {
width: 16px;
height: 16px;
margin-right: 4px;
}
}
.countComment {
position: absolute;
top: 12px;
left: 112px;
color: rgba(255, 255, 255, 0.45);
font-size: 12px;
font-style: normal;
font-weight: 400;
line-height: 18px; /* 150% */
&.active {
color: rgba(255, 255, 255, 0.9);
}
}
.tabs {
--van-tab-active-text-color: rgba(255, 255, 255, 0.9);
--van-tab-text-color: rgba(255, 255, 255, 0.45);
height: calc(100% - 269px + 11px);
:deep(> .van-tabs__wrap) {
background-color: unset;
border-bottom: 1px solid rgba(255, 255, 255, 0.05);
.van-tabs__nav {
background: unset;
.van-tab {
font-size: 14px;
padding: unset;
width: 60px;
flex: unset;
}
.van-tabs__line {
background-color: $themeColor;
height: 4px;
width: 16px;
border-radius: 2px 2px 0 0;
}
}
}
:deep(> .van-tabs__content) {
height: calc(100% - 44px);
overflow-x: hidden;
.van-tab__panel {
height: 100%;
max-width: 100%;
box-sizing: border-box;
}
}
&.newUser {
height: calc(100% - 269px + 11px);
}
}
.comment-comp {
height: 100%;
:deep(.comment-wrap) {
height: 100%;
.wrapper {
height: 100%;
}
.headerBox {
display: none;
}
.slider {
display: none;
}
.main {
background: transparent;
// border-top: 1px solid #e0e0e0;
height: 100%;
}
.bottomBox {
position: sticky;
left: 0;
bottom: 0;
.commentBtnBox {
.commentInput {
padding-left: 0;
}
}
}
}
}
}
.top258 {
top: 258px !important;
}
:deep(.adv_swiper) {
border-radius: unset !important;
.adv-container {
.adv-swiper-slide {
border-radius: unset !important;
.cover {
border-radius: unset !important;
}
}
}
}
</style>