552 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			Vue
		
	
	
	
	
	
		
		
			
		
	
	
			552 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			Vue
		
	
	
	
	
	
|   | <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 { closeWebViewSDK } from "@/utils/upNativeComm"; | |||
|  | 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.account}-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.account}-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) { | |||
|  |     closeWebViewSDK(); | |||
|  |   } 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> |