| | |
| | | <template> |
| | | <view class="video-container"> |
| | | <view class="fixed-float-window" @click="gotoPrizeActivity" v-if="prizeActivity.id"> |
| | | <!-- 悬浮窗内容:可替换为图标+文字/纯图标/图片 --> |
| | | <text style="font-size: 36rpx;">🎁</text> |
| | | <text class="float-text">抽奖活动</text> |
| | | </view> |
| | | |
| | | <top-bar selectedTitleIndex="home" textColor="white" @changeTab="topBarChange" class="topBar"></top-bar> |
| | | <!-- 视频加载 --> |
| | | <zero-loading v-show="videoLoading" type="circle" color="#0ebd57" text=""></zero-loading> |
| | |
| | | |
| | | |
| | | <!-- 悬挂商品链接层 --> |
| | | <view class="goods-link-warp" v-if="item.goodsList.length > 0"> |
| | | <view class="goods-link-warp" :style="{ bottom: marginBottom + 55 + 'px' }" |
| | | v-if="item.goodsList.length > 0"> |
| | | <view class="goods-link"> |
| | | <swiper @change="goodsChange" :autoplay="true" :interval="4000" style="height: 120rpx;"> |
| | | <swiper-item v-for="goods in item.goodsList" :key="goods.goodsId"> |
| | |
| | | |
| | | <!-- 视频信息层 --> |
| | | <view class="video-info" :style="{ bottom: marginBottom + 20 + 'px' }"> |
| | | <view style="width: 100%; position: relative;" @click="jumpToSearch"> |
| | | <view style="width: 100%; position: relative;"> |
| | | <text class="video-author">@{{ item.authorName }}</text> |
| | | <text class="iconfont" style="position: absolute;right: 42px;bottom: 50rpx;"></text> |
| | | <text class="iconfont" @click="jumpToSearch" |
| | | style="position: absolute;right: 42px;bottom: 50rpx;"></text> |
| | | </view> |
| | | <view style="width: 100%;word-wrap: break-word;white-space: normal;overflow-wrap: break-word;"> |
| | | <text class="video-title">{{ item.title }}</text> |
| | |
| | | |
| | | import ActivityPopup from '@/pages/ActivityPopup/ActivityPopup.vue' |
| | | import { mapState, mapMutations } from 'vuex' |
| | | import { setPopupRedisTime, getPopupAcitivty } from '@/api/popup.js' |
| | | import { setPopupRedisTime, getPopupAcitivty,hideActivityPopupToday } from '@/api/popup.js' |
| | | import { changeCollect } from "@/api/collect.js"; |
| | | import { saveShare, saveShareClickRecord } from "@/api/share.js"; |
| | | import { saveShare, saveShareClickRecord} from "@/api/share.js"; |
| | | import { getSessionId, userAction,userShare } from "@/api/userAction.js"; |
| | | import { silentLogin } from "@/api/connect.js"; |
| | | import { getUserInfo } from "@/api/members"; |
| | | import storage from "@/utils/storage.js"; |
| | | import TopBar from "@/components/TopBar.vue"; |
| | | import { nextTick } from "vue"; |
| | | import { getVideoCover } from "@/api/common.js" |
| | | import {addPrizeNum} from '@/api/prize.js' |
| | | import UIcon from "../../subComponents/uview-components/uview-ui/components/u-icon/u-icon.vue"; |
| | | import {getONPrizeActivity} from "../../../api/prize-activity"; |
| | | export default { |
| | | components: { TopBar, ActivityPopup }, |
| | | components: {UIcon, TopBar, ActivityPopup }, |
| | | computed: { |
| | | hasPlayTime() { |
| | | return this.sliderFormatTime(this.progress > 0 ? this.duration * this.progress / 100 : 0); |
| | |
| | | loading: false, // 是否正在加载 |
| | | videoQuery: { |
| | | pageNumber: 1, |
| | | pageSize: 10, |
| | | pageSize: 3, |
| | | videoFrom: 'recommend' |
| | | }, |
| | | goodsSimilarlyQuery: { // 相似视频查询 |
| | | pageNumber: 1, |
| | | pageSize: 10, |
| | | pageSize: 3, |
| | | videoFrom: 'goodsSimilarly', |
| | | goodsIds: [], |
| | | currentVideoId: '' |
| | |
| | | similarlyNomore: false, // 是否还有更多相似视频 |
| | | similaryVideoIndex: 0, // 相似视频的播放位置 |
| | | similarlyLoading: false, // 相似视频加载 |
| | | marginBottom: 0 // 底部安全区域 |
| | | marginBottom: 0 ,// 底部安全区域 |
| | | pageSessionNo:"", |
| | | shareId:"", |
| | | actionParam:{ |
| | | sessionId:'', |
| | | actionType:"PAGE", |
| | | joinType:"SELF", |
| | | pageCode:"RECOMMEND_VIDEO", |
| | | pageParams:"{}", |
| | | pageStatus:"JOIN", |
| | | pageType:"LIST" |
| | | }, |
| | | shareParam:{ |
| | | pageCode:"RECOMMEND_VIDEO", |
| | | shareOption:"{}", |
| | | pageType:"LIST" |
| | | }, |
| | | prizeActivity:{}, |
| | | } |
| | | }, |
| | | onShow() { |
| | | getSessionId().then(res=>{ |
| | | console.log('res',JSON.stringify(res)) |
| | | this.pageSessionNo = res.data.data |
| | | if(this.pageSessionNo){ |
| | | let param = Object.assign({},this.actionParam); |
| | | this.actionParam.sessionId = this.pageSessionNo |
| | | param.sessionId = this.pageSessionNo |
| | | userAction(param) |
| | | } |
| | | }) |
| | | this.getONPrizeActivity() |
| | | let showPopup = storage.getPopupShow(); |
| | | console.log(showPopup) |
| | | if (showPopup) { |
| | | console.log("首次打开,显示弹窗"); |
| | | storage.setPopupShow(true); // 标记为已显示 |
| | | } else { |
| | | console.log("已显示过,不弹窗"); |
| | | } |
| | | if(showPopup){ |
| | | this.openActivityPopup() |
| | | } |
| | | |
| | | this.openActivityPopup() |
| | | |
| | | |
| | | if (!this.userId) { |
| | |
| | | this.totalHidenTime += duration |
| | | } |
| | | }, |
| | | onHide() { |
| | | this.startHidenTime = Date.now() |
| | | }, |
| | | onUnload() { |
| | | let param = Object.assign({},this.actionParam); |
| | | if (this.sendOnShow)return |
| | | param.pageStatus = "LEAVE" |
| | | userAction(param) |
| | | }, |
| | | onHide() { |
| | | this.startHidenTime = Date.now() |
| | | let param = Object.assign({},this.actionParam); |
| | | this.sendOnShow = true; |
| | | param.pageStatus = "LEAVE" |
| | | userAction(param) |
| | | }, |
| | | onLoad(option) { |
| | | |
| | | if(option.shareId){ |
| | | console.log('触发onLoad') |
| | | this.actionParam.shareId = option.shareId; |
| | | this.actionParam.joinType = 'SHARE' |
| | | uni.setStorage({ |
| | | key: 'shareId', |
| | | data: option.shareId, |
| | | success: function () { |
| | | console.log('缓存shareId成功'); |
| | | } |
| | | }); |
| | | } |
| | | console.log('-----------分享出的数据---------->', option) |
| | | //处理扫码出来的视频 |
| | | this.marginBottom = uni.getSystemInfoSync().safeAreaInsets.bottom |
| | |
| | | const shareType = params.shareType; |
| | | const videoId = params.videoId; |
| | | queryParam.videoId = videoId |
| | | this.actionParam.joinType = "SCAN"; |
| | | this.actionParam.pageParams = JSON.stringify(params); |
| | | |
| | | console.log('解析参数:', { shareType, videoId }); |
| | | } |
| | | const token = storage.getAccessToken(); |
| | |
| | | this.wxSilentLogin(() => { |
| | | // 判断是不是点击分享链接进来的 |
| | | if (option.userId && option.videoId) { |
| | | this.actionParam.joinType = "SHARE"; |
| | | this.actionParam.pageParams = JSON.stringify(option); |
| | | queryParam.videoId = option.videoId |
| | | // 保存分享点击记录 |
| | | saveShareClickRecord({ refId: option.videoId, shareUserId: option.userId }) |
| | |
| | | // } |
| | | |
| | | // }) |
| | | console.log(videoInfo) |
| | | return { |
| | | title: videoInfo.title, |
| | | path: `/pages/tabbar/index/home?videoId=${videoInfo.id}&userId=${userInfo.id}`, |
| | | imageUrl: videoInfo.coverUrl |
| | | } |
| | | // 保存分享记录 |
| | | |
| | | // 返回一个Promise |
| | | return new Promise((resolve) => { |
| | | this.shareId = ''; |
| | | let shareObj ={ |
| | | videoId:videoInfo.id, |
| | | userId:userInfo.id |
| | | } |
| | | this.shareParam.shareOption = JSON.stringify(shareObj) |
| | | userShare(this.shareParam).then(res => { |
| | | this.shareId = res.data.data; |
| | | let param = { |
| | | addType:"SHARE_GOODS_VIDEO", |
| | | extend:"", |
| | | } |
| | | let extend = {shareId:this.shareId} |
| | | param.extend = JSON.stringify(extend) |
| | | addPrizeNum(param); |
| | | // 当获取到shareId后,再resolve分享配置 |
| | | resolve({ |
| | | title: videoInfo.title, |
| | | path: `/pages/tabbar/index/home?videoId=${videoInfo.id}&userId=${userInfo.id}&shareId=${this.shareId}`, |
| | | imageUrl: videoInfo.coverUrl, |
| | | success(e) { |
| | | console.log("分享成功", e); |
| | | }, |
| | | fail(e) { |
| | | console.log('分享失败', e); |
| | | } |
| | | }); |
| | | }).catch(err => { |
| | | // 处理错误情况,例如使用默认参数 |
| | | console.error('获取分享ID失败', err); |
| | | resolve({ |
| | | title: videoInfo.title, |
| | | path: `/pages/tabbar/index/home?videoId=${videoInfo.id}&userId=${userInfo.id}`, |
| | | imageUrl: videoInfo.coverUrl |
| | | }); |
| | | }); |
| | | }); |
| | | }, |
| | | methods: { |
| | | getONPrizeActivity(){ |
| | | getONPrizeActivity().then(res =>{ |
| | | if(res.statusCode=== 200){ |
| | | //后端没查到开启的抽奖活动 res.data.data.id值为null |
| | | this.prizeActivity = res.data.data; // 存储完整活动数据 |
| | | if (this.prizeActivity.endTime) { |
| | | }else { |
| | | this.prizeActivity = {}; // 无活动时清空 |
| | | } |
| | | } |
| | | }) |
| | | }, |
| | | gotoPrizeActivity(){ |
| | | uni.navigateTo({ |
| | | url:'/pages/prize/PrizeDetail/PrizeDetail?id=' + this.prizeActivity.id, |
| | | }); |
| | | }, |
| | | |
| | | async openActivityPopup() { |
| | | await getPopupAcitivty().then(res => { |
| | | if (res.statusCode === 200) { |
| | | //请求成功修改弹窗展示状态 为false |
| | | storage.setPopupShow(false); |
| | | let obj = res.data.data; |
| | | if (obj.enableStatus === 'ON') { |
| | | setPopupRedisTime().then(res => { |
| | | if (res.statusCode === 200) { |
| | | if (res.data.state) { |
| | | this.showActivityPopup({ |
| | | title: obj.activityName, |
| | | desc: obj.activityDes, |
| | | image: obj.activityCoverUrl, |
| | | endTime: new Date(obj.endTime).getTime(), |
| | | prizeActivityId: obj.id |
| | | }) |
| | | } else { |
| | | this.hideActivityPopup() |
| | | } |
| | | |
| | | } |
| | | }); |
| | | this.showActivityPopup({ |
| | | title: obj.activityName, |
| | | desc: obj.activityDes, |
| | | image: obj.activityCoverUrl, |
| | | endTime: new Date(obj.endTime).getTime(), |
| | | prizeActivityId: obj.id |
| | | }) |
| | | // setPopupRedisTime().then(res => { |
| | | // if (res.statusCode === 200) { |
| | | // if (res.data.state) { |
| | | // this.showActivityPopup({ |
| | | // title: obj.activityName, |
| | | // desc: obj.activityDes, |
| | | // image: obj.activityCoverUrl, |
| | | // endTime: new Date(obj.endTime).getTime(), |
| | | // prizeActivityId: obj.id |
| | | // }) |
| | | // } else { |
| | | // this.hideActivityPopup() |
| | | // } |
| | | // |
| | | // } |
| | | // }); |
| | | } |
| | | |
| | | } |
| | |
| | | |
| | | }, |
| | | ...mapMutations(['showActivityPopup', 'hideActivityPopup']), // 引入Vuex的方法 |
| | | |
| | | |
| | | onClosePopup() { |
| | | this.hideActivityPopup() |
| | | }, |
| | |
| | | const data = res.data.data.map(item => { |
| | | return { |
| | | ...item, |
| | | updateKey: item.id |
| | | updateKey: item.id, |
| | | videoUrl: item.videoUrl.replace('https://lmk-1356772813.cos.ap-chengdu.myqcloud.com/', 'https://media.meiyikuang.com/') |
| | | } |
| | | }) |
| | | if (this.videoQuery.pageNumber === 1) { |
| | |
| | | this.videoNoMore = true; |
| | | return; |
| | | } |
| | | this.videoQuery.pageNumber++; |
| | | if (res.data.code === 200) { |
| | | this.videoQuery.pageNumber++; |
| | | } |
| | | |
| | | }) |
| | | } else { |
| | |
| | | const data = res.data.data.map(item => { |
| | | return { |
| | | ...item, |
| | | updateKey: item.id |
| | | updateKey: item.id, |
| | | videoUrl: item.videoUrl.replace('https://lmk-1356772813.cos.ap-chengdu.myqcloud.com/', 'https://media.meiyikuang.com/') |
| | | } |
| | | }) |
| | | if (this.videoQuery.pageNumber === 1) { |
| | |
| | | this.videoNoMore = true; |
| | | return; |
| | | } |
| | | this.videoQuery.pageNumber++; |
| | | if (res.data.code === 200) { |
| | | this.videoQuery.pageNumber++; |
| | | } |
| | | |
| | | }) |
| | | } |
| | |
| | | /* 商品链接悬挂层样式 */ |
| | | .goods-link-warp { |
| | | position: absolute; |
| | | bottom: 160px; |
| | | bottom: 100px; |
| | | left: 20px; |
| | | color: #f8f8f8; |
| | | z-index: 10; |
| | |
| | | .progress-bar { |
| | | position: relative; |
| | | width: 100%; |
| | | height: 5px; |
| | | height: 10px; |
| | | background-color: rgba(255, 255, 255, 0.2); |
| | | /* 半透明背景 */ |
| | | overflow: hidden; |
| | |
| | | left: 20rpx; |
| | | z-index: 1000 |
| | | } |
| | | .fixed-float-window { |
| | | position: fixed; |
| | | right: 20rpx; |
| | | top: 10%; |
| | | z-index: 1000; |
| | | width: 150rpx; |
| | | height: 150rpx; |
| | | border-radius: 50%; |
| | | box-shadow: 0 10rpx 16rpx rgba(110, 103, 103, 0.4); |
| | | background-color: rgba(50, 48, 48, 0.7); /* 加深主体背景,增强对比 */ |
| | | display: flex; |
| | | flex-direction: column; |
| | | align-items: center; |
| | | justify-content: center; |
| | | transition: all 0.2s ease; |
| | | overflow: visible; |
| | | } |
| | | |
| | | /* 内层主光圈(粗线条+强对比) */ |
| | | .fixed-float-window::after { |
| | | content: ""; |
| | | position: absolute; |
| | | width: calc(100% + 25rpx); |
| | | height: calc(100% + 25rpx); |
| | | border-radius: 50%; |
| | | /* 金色渐变边框,线条加粗至5rpx */ |
| | | border: 5rpx solid transparent; |
| | | border-top-color: rgba(255, 215, 0, 0.9); |
| | | border-right-color: rgba(255, 180, 0, 0.6); |
| | | border-bottom-color: rgba(255, 215, 0, 0.9); |
| | | border-left-color: rgba(255, 180, 0, 0.6); |
| | | /* 加快旋转速度(4秒一圈) */ |
| | | animation: rotate 4s linear infinite; |
| | | pointer-events: none; |
| | | } |
| | | |
| | | /* 外层扩散光效(增强存在感) */ |
| | | .fixed-float-window::before { |
| | | content: ""; |
| | | position: absolute; |
| | | width: calc(100% + 25rpx); |
| | | height: calc(100% + 25rpx); |
| | | border-radius: 50%; |
| | | /* 模糊光效 */ |
| | | background: radial-gradient(circle, rgba(255,215,0,0.5) 0%, rgba(255,215,0,0) 70%); |
| | | /* 呼吸式缩放动画 */ |
| | | animation: pulse 3s ease-in-out infinite; |
| | | pointer-events: none; |
| | | } |
| | | |
| | | /* 旋转动画 */ |
| | | @keyframes rotate { |
| | | 0% { transform: rotate(0deg); } |
| | | 100% { transform: rotate(360deg); } |
| | | } |
| | | |
| | | /* 呼吸扩散动画 */ |
| | | @keyframes pulse { |
| | | 0%, 100% { |
| | | transform: scale(1); |
| | | opacity: 0.3; |
| | | } |
| | | 50% { |
| | | transform: scale(1.15); |
| | | opacity: 0.6; |
| | | } |
| | | } |
| | | |
| | | .float-text { |
| | | color: rgba(255, 215, 0, 0.95); /* 文字更亮 */ |
| | | font-size: 26rpx; |
| | | font-weight: bold; /* 加粗文字 */ |
| | | margin-top: 8rpx; |
| | | } |
| | | |
| | | .fixed-float-window:active { |
| | | transform: scale(0.95); |
| | | } |
| | | </style> |