2025-02-15 17:49:35 +08:00

551 lines
13 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div
class="video-play video-wrap h100"
v-if="isPlay && ![5, 6].includes(detail.status)"
>
<!-- <svg-icon
class="arrow-left"
icon-class="arrow-left"
@click="onClickLeft"
></svg-icon> -->
<div class="h100">
<div class="videoRef"></div>
</div>
<div class="footer">
<div class="flex intro">
<div class="left-content">
<CarItem
v-if="detail.cartList && detail.cartList.length"
:item="detail.cartList[0]"
></CarItem>
<div class="live-stream" v-if="detail.upcomingLive">
<div class="title">
<h3>{{ detail.upcomingLive.title }}</h3>
<p>{{ detail.upcomingLive.startTime }}直播</p>
</div>
<button
@click="setSubLiveVideo"
:disabled="disableSub || detail.upcomingLive.isSubscribe !== 2"
>
{{ detail.upcomingLive.isSubscribe === 1 ? "已预约" : "预约" }}
</button>
</div>
<p class="text">
{{ detail.viewPoint }}
</p>
</div>
<ul>
<li class="photo">
<img class="avatar" :src="detail.advisor?.avatar" />
<img
src="@/assets/images/chat/living.gif"
class="svg-icon"
alt=""
srcset=""
v-if="detail.isLiving === 1"
/>
</li>
<li @click="showChat">
<svg-icon icon-class="chat"></svg-icon>
<p>{{ detail.commentCount ? detail.commentCount : "评论" }}</p>
</li>
<li @click="setVideoFavor">
<svg-icon
:icon-class="isFavor === 1 ? 'live-active' : 'live'"
></svg-icon>
<p>{{ favorNum ? favorNum : "点赞" }}</p>
</li>
<li @click="toShare">
<svg-icon icon-class="share1"></svg-icon>
<p>转发</p>
</li>
<li
@click="showCarList"
v-if="detail.cartList && detail.cartList.length > 1"
class="car"
>
<svg-icon icon-class="car"></svg-icon>
<span>{{ detail.cartList.length }}</span>
</li>
</ul>
</div>
<div
class="flex compilations"
@click="showSerialList"
v-if="detail.serial"
>
<svg-icon class="hj" icon-class="hj"></svg-icon>
<p>合集- {{ detail.serial?.name }}</p>
<svg-icon class="arrow-right" icon-class="arrow-right"></svg-icon>
</div>
</div>
</div>
<NoLookTip v-if="[5, 6].includes(detail.status)" text="该视频已下架" />
<NoLookTip v-else-if="noAuth" text="暂无服务权限,请联系小助理开通" />
<Share ref="shareRef" :detail="detail" />
<van-overlay :show="showGuide" @click="showGuide = false">
<img src="@/assets/images/guide1.png" />
<p>点击右上角进行分享点击屏幕关闭分享指引</p>
</van-overlay>
<SerialList
ref="serialListRef"
:id="detail.serial?.id"
:title="detail.serial?.name"
:remark="detail.serial?.remark"
/>
<ChatFrame
ref="chatFrameRef"
:detail="detail"
:commentCount="detail.commentCount"
/>
<CarList
v-if="detail.cartList && detail.cartList.length > 1"
ref="cartListRef"
:list="detail.cartList || []"
/>
<van-dialog
v-model:show="showCodeDialog"
title="观看权限提示"
show-cancel-button
@confirm="getLiveLimit"
@cancel="toBack"
>
<div class="code-content">
<p class="tip-text">当前短视频需输入邀请码才可观看</p>
<van-field v-model.trim="code" placeholder="请输入邀请码" />
</div>
</van-dialog>
</template>
<script setup>
import { ref, computed, watch, nextTick } from "vue";
import { useRouter, useRoute } from "vue-router";
import { showToast, showDialog } from "vant";
import {
queryVideoDetail,
videoFavor,
queryLimitCheck,
videoRecord,
shareSave,
} from "@/api/shortVideo";
import { subLiveVideo } from "@/api/video";
import useShortVideoPlay from "@/hooks/useShortVideoPlay";
import Share from "@/components/Share";
import SerialList from "./components/SerialList";
import ChatFrame from "./components/ChatFrame";
import CarList from "./components/CarList";
import CarItem from "./components/CarItem";
import { useStore } from "vuex";
import NoLookTip from "@/components/NoLookTip.vue";
const router = useRouter();
const route = useRoute();
const store = useStore();
// const onClickLeft = () => {
// router.back();
// };
const serialListRef = ref();
const showSerialList = () => {
serialListRef.value.show = true;
};
const detail = ref({});
const favorNum = ref(0);
const isFavor = ref(2);
const isPlay = ref(false); // 当直播限制需要验证码登录的时候,验证码校验通过才能播放
const noAuth = ref(false);
const { playVideo } = useShortVideoPlay();
// 之前是否已经进行邀请码校验且通过校验
const codeVerify = localStorage.getItem(
`${store.state.userInfo.userId}-shotVideoPlay-${route.query.id}-code`
);
const showCodeDialog = computed(() => {
if (detail.value.limitType === 3 && !isPlay.value && !codeVerify) {
// 邀请码
return true;
} else {
return false;
}
});
watch(
() => isPlay.value,
() => {
if (detail.value.status !== 5) {
nextTick(() => {
playVideo({
detail: detail.value,
videoEl: document.querySelector(".videoRef"),
});
});
}
}
);
const getVideoDetail = async () => {
let ret = await queryVideoDetail({
id: route.query.id,
saleUserId: route.query.saleUserId,
});
if (ret.code === 0) {
document.title = ret.data.title;
detail.value = ret.data;
favorNum.value = detail.value.favorCount;
isFavor.value = detail.value.isFavor;
// limitType 直播限制 1不限制 2手机号 3邀请码 4微信授权 5产品专属直播 6 权限号
if (
[1].includes(ret.data.limitType) ||
(ret.data.limitType === 3 && codeVerify) ||
(ret.data.limitType === 2 && store.state.token)
) {
isPlay.value = true;
} else if (ret.data.limitType === 6) {
const authResultVo = ret.data.authResultVo || {};
isPlay.value = !!authResultVo.auth;
noAuth.value = !authResultVo.auth;
if (authResultVo.auth) {
if (authResultVo.riskUrl) {
showDialog({
title: "提示",
message: `您当前订单尚未完成风险测评,无法查看产品服务内容,请点击完成风险测评`,
confirmButtonText: "去测评",
})
.then(() => {
location.href =
authResultVo.riskUrl +
`&callbackUrl=${encodeURIComponent(location.href)}`;
})
.catch(() => {
router.go(-1);
});
} else if (authResultVo.contractUrl) {
showDialog({
title: "提示",
message: `您当前订单尚未完成合同签署,无法查看产品服务内容,请点击完成合同签署`,
confirmButtonText: "去签署",
})
.then(() => {
location.href =
authResultVo.contractUrl +
`&contractCallbackUrl=${encodeURIComponent(location.href)}`;
})
.catch(() => {
router.go(-1);
});
}
}
}
}
};
getVideoDetail();
const code = ref("");
const getLiveLimit = async () => {
let ret = await queryLimitCheck({
videoId: route.query.id,
code: code.value,
});
if (ret.code === 0) {
if (ret.data.result) {
// 检验通过
localStorage.setItem(
`${store.state.userInfo.userId}-shotVideoPlay-${route.query.id}-code`,
true
);
isPlay.value = true;
} else {
showToast("邀请码输入错误!");
}
}
};
const setVideoFavor = async () => {
// 点赞
let ret = await videoFavor({
option: isFavor.value === 1 ? 2 : 1,
videoId: route.query.id,
});
if (ret.code === 0) {
favorNum.value = ret.data.count;
isFavor.value = isFavor.value === 1 ? 2 : 1;
}
};
const shareRef = ref();
const showGuide = ref(false);
const toShare = () => {
shareSave({ id: route.query.id });
if (
typeof WeixinJSBridge === "object" &&
typeof window.WeixinJSBridge.invoke === "function"
) {
showGuide.value = true;
} else {
shareRef.value.showPopup = true;
}
};
const cartListRef = ref();
const showCarList = () => {
cartListRef.value.show = true;
};
const chatFrameRef = ref();
const showChat = () => {
chatFrameRef.value.show = true;
};
const setVideoRecord = () => {
videoRecord({
list: [
{
videoId: route.query.id,
},
],
});
};
setVideoRecord();
const disableSub = ref(false);
const setSubLiveVideo = async () => {
if (disableSub.value) return;
disableSub.value = true;
let ret = await subLiveVideo({
id: detail.value.upcomingLive.videoId,
option: detail.value.upcomingLive.isSubscribe === 1 ? 2 : 1,
saleUserId: route.query.saleUserId,
}).catch(() => {
disableSub.value = false;
});
disableSub.value = false;
if (ret.code === 0) {
showToast(
detail.value.upcomingLive.isSubscribe === 1 ? "已取消预约" : "预约成功!"
);
detail.value.upcomingLive.isSubscribe =
detail.value.upcomingLive.isSubscribe === 1 ? 2 : 1;
}
};
const toBack = () => {
if (route.query.sdk) {
console.log("sdk返回");
} else {
router.go(-1);
}
};
</script>
<style scoped lang="scss">
.h100 {
height: 100%;
}
.video-play {
position: relative;
padding-top: var(--statusbar-height);
box-sizing: border-box;
}
.arrow-left {
position: absolute;
left: 32px;
top: 32px;
color: #fff;
font-size: 40px;
z-index: 10;
margin-top: var(--statusbar-height);
}
.video-wrap {
background: rgba(0, 0, 0, 0.4);
video {
display: block;
height: 100%;
width: 100%;
}
}
.footer {
position: absolute;
left: 32px;
right: 32px;
bottom: 100px;
.flex {
display: flex;
}
.live-stream {
display: flex;
align-items: center;
justify-content: space-between;
background: url(../../assets/images/short-subscribe-bg.png) no-repeat center;
background-size: cover;
border-radius: 8px;
margin-bottom: 16px;
color: #fff;
padding: 24px;
width: 460px;
.title {
flex: 1;
}
h3 {
font-size: 32px;
text-align: left;
display: -webkit-box;
-webkit-box-orient: vertical;
overflow: hidden;
-webkit-line-clamp: 1;
margin-bottom: 20px;
}
p {
font-size: 24px;
text-align: left;
}
button {
background: #fff;
height: 56px;
font-size: 24px;
color: #000;
width: 100px;
border-radius: 28px;
}
}
.compilations {
justify-content: space-between;
align-items: center;
background: rgba(0, 0, 0, 0.5);
border-radius: 8px;
margin-bottom: 16px;
color: #fff;
padding: 18px 8px 18px 20px;
p {
font-weight: 400;
font-size: 28px;
color: #ffffff;
line-height: 28px;
flex: 1;
text-align: left;
margin: 0 16px;
}
.hj,
.arrow {
font-size: 32px;
}
}
.intro {
align-items: flex-end;
margin-bottom: 24px;
justify-content: space-between;
.left-content {
flex: 1;
padding-right: 20px;
}
.text {
font-weight: 400;
font-size: 32px;
color: #ffffff;
line-height: 44px;
display: -webkit-box;
-webkit-box-orient: vertical;
overflow: hidden;
-webkit-line-clamp: 2;
margin: 0 10px;
text-align: left;
margin-right: 80px;
flex: 1;
}
li {
margin-top: 30px;
.svg-icon {
font-size: 72px;
}
p {
font-weight: 400;
font-size: 24px;
color: #ffffff;
text-align: center;
line-height: 24px;
padding-top: 10px;
}
}
}
}
.videoRef {
width: 100%;
height: 100%;
::v-deep(.video-js) {
width: 100%;
height: 100%;
font-size: 18px;
.vjs-tech {
position: relative;
display: block;
}
.vjs-control-bar {
margin-bottom: 40px;
}
}
}
.arrow-right {
font-size: 28px;
}
.photo {
position: relative;
margin-bottom: 60px;
.avatar {
width: 80px;
height: 80px;
border-radius: 50%;
}
.svg-icon {
width: 30px;
position: absolute;
font-size: 28px !important;
bottom: 0;
right: 0;
}
}
.car {
position: relative;
span {
position: absolute;
top: 0;
right: 20px;
min-width: 20px;
transform: translateX(18px) translateY(-50%);
display: flex;
align-items: center;
justify-content: center;
padding: 4px;
flex-direction: column;
border-radius: 13px 13px 13px 4px;
background: linear-gradient(
128deg,
#fff2dc -25.5%,
#ffd9a7 89.14%,
#605a52 89.14%
);
color: rgb(142, 81, 5);
font-size: 18px;
font-style: normal;
font-weight: 500;
}
}
.container-mask {
img {
width: 182px;
height: 182px;
margin: 359px auto 55.8px;
}
p {
color: rgb(51, 51, 51);
font-family: "PingFang SC";
font-size: 35px;
font-style: normal;
font-weight: 400;
}
}
</style>