New file |
| | |
| | | |
| | | import { http, Method } from "@/utils/request.js"; |
| | | |
| | | import api from "@/config/api.js"; |
| | | |
| | | export function setPopupRedisTime() { |
| | | return http.request({ |
| | | url: "/lmk/popup/setPopupRedisTime", |
| | | method: Method.GET, |
| | | needToken: true, |
| | | }); |
| | | } |
| | | |
| | | export function getPopupAcitivty() { |
| | | return http.request({ |
| | | url: "/lmk/popup/getPopupActivity", |
| | | method: Method.GET, |
| | | needToken: true, |
| | | }); |
| | | } |
| | |
| | | } |
| | | |
| | | /** |
| | | * |
| | | */ |
| | | |
| | | export function removeByIdVideoComment(params){ |
| | | return http.request({ |
| | | url:"/lmk/video-comment/" + params, |
| | | method: Method.DELETE, |
| | | needToken: true, |
| | | }) |
| | | } |
| | | /** |
| | | * 发布视频评论 |
| | | * |
| | | * @param params |
| | |
| | | // buyer: "http://192.168.0.15:8888", |
| | | // store: "http://192.168.0.15:8889", |
| | | |
| | | // im: "http://127.0.0.1:8885", |
| | | // common: "http://127.0.0.1:8890", |
| | | // buyer: "http://127.0.0.1:8888", |
| | | // store: "http://127.0.0.1:8889", |
| | | im: "http://127.0.0.1:8885", |
| | | common: "http://127.0.0.1:8890", |
| | | buyer: "http://127.0.0.1:8888", |
| | | store: "http://127.0.0.1:8889", |
| | | |
| | | // common: "http://192.168.0.113:8890", |
| | | // buyer: "http://192.168.0.113:8888", |
| | | // im: "http://192.168.0.113:8885", |
| | | im: "https://myk.9village.cn", |
| | | common: "https://myk.9village.cn", |
| | | buyer: "https://myk.9village.cn", |
| | | store: "https://myk.9village.cn", |
| | | // im: "https://myk.9village.cn", |
| | | // common: "https://myk.9village.cn", |
| | | // buyer: "https://myk.9village.cn", |
| | | // store: "https://myk.9village.cn", |
| | | // im: "https://www.meiyikuang.com/mykapi", |
| | | // common: "https://www.meiyikuang.com/mykapi", |
| | | // buyer: "https://www.meiyikuang.com/mykapi", |
| | |
| | | "navigationStyle": "custom" |
| | | // 隐藏顶部导航栏 |
| | | } |
| | | }, |
| | | { |
| | | "path" : "pages/ActivityPopup/ActivityPopup", |
| | | "style" : |
| | | { |
| | | "navigationBarTitleText" : "" |
| | | } |
| | | } |
| | | // { |
| | | // "path": "pages/tabbar/home/index", |
New file |
| | |
| | | <template> |
| | | <view class="activity-popup" v-if="show" @click="onMaskClick"> |
| | | <!-- 遮罩层 --> |
| | | <view class="popup-mask" :class="{ 'mask-enter': show, 'mask-leave': !show }"></view> |
| | | |
| | | <!-- 弹窗内容 --> |
| | | <view class="popup-content" |
| | | :class="{ 'content-enter': show, 'content-leave': !show }" |
| | | @click.stop> |
| | | |
| | | <!-- 关闭按钮 --> |
| | | <view class="close-btn" @click="onClose"> |
| | | <uni-icons type="close" size="24" color="#666"></uni-icons> |
| | | </view> |
| | | |
| | | <!-- 活动图片 --> |
| | | <view class="activity-img"> |
| | | <image :src="activityImage" mode="widthFix" class="popup-img"></image> |
| | | </view> |
| | | |
| | | <!-- 活动信息 --> |
| | | <view class="activity-info"> |
| | | <h3 class="activity-title">{{ activityTitle }}</h3> |
| | | <p class="activity-desc">{{ activityDesc }}</p> |
| | | |
| | | <!-- 倒计时(如果需要) --> |
| | | <view class="countdown" v-if="showCountdown"> |
| | | <text class="countdown-text">活动剩余时间:</text> |
| | | <view class="countdown-time"> |
| | | <text class="time-box">{{ days }}</text> |
| | | <text class="time-sep">:</text> |
| | | <text class="time-box">{{ hours }}</text> |
| | | <text class="time-sep">:</text> |
| | | <text class="time-box">{{ minutes }}</text> |
| | | <text class="time-sep">:</text> |
| | | <text class="time-box">{{ seconds }}</text> |
| | | </view> |
| | | </view> |
| | | |
| | | <!-- 参与按钮 --> |
| | | <button class="join-btn" @click="onJoinActivity"> |
| | | {{ joinButtonText }} |
| | | </button> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | </template> |
| | | |
| | | <script> |
| | | import {getPopupAcitivty} from '@/api/popup.js' |
| | | export default { |
| | | name: 'ActivityPopup', |
| | | props: { |
| | | // 控制弹窗显示/隐藏 |
| | | show: { |
| | | type: Boolean, |
| | | default: false |
| | | }, |
| | | // 活动图片URL |
| | | activityImage: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | // 活动标题 |
| | | activityTitle: { |
| | | type: String, |
| | | default: '限时优惠活动' |
| | | }, |
| | | // 活动描述 |
| | | activityDesc: { |
| | | type: String, |
| | | default: '参与本次活动,即可获得超值大奖,机会难得,不要错过!' |
| | | }, |
| | | // 参与按钮文本 |
| | | joinButtonText: { |
| | | type: String, |
| | | default: '立即参与' |
| | | }, |
| | | // 是否显示倒计时 |
| | | showCountdown: { |
| | | type: Boolean, |
| | | default: true |
| | | }, |
| | | // 倒计时结束时间(时间戳) |
| | | endTime: { |
| | | type: Number, |
| | | default: () => { |
| | | // 默认7天后结束 |
| | | return Date.now() + 7 * 24 * 60 * 60 * 1000; |
| | | } |
| | | } |
| | | }, |
| | | data() { |
| | | return { |
| | | days: '00', |
| | | hours: '00', |
| | | minutes: '00', |
| | | seconds: '00', |
| | | countdownTimer: null |
| | | }; |
| | | }, |
| | | watch: { |
| | | show(newVal) { |
| | | console.log("弹窗监听变化",newVal) |
| | | if (newVal && this.showCountdown) { |
| | | this.startCountdown(); |
| | | } else if (!newVal && this.countdownTimer) { |
| | | clearInterval(this.countdownTimer); |
| | | this.countdownTimer = null; |
| | | } |
| | | } |
| | | }, |
| | | mounted() { |
| | | console.log('组件已挂载,此时可以访问 props 和 DOM'); |
| | | console.log('当前 show 状态:', this.show); // 可以打印 props 中的 show |
| | | |
| | | }, |
| | | methods: { |
| | | onpan(){ |
| | | |
| | | }, |
| | | // 开始倒计时 |
| | | startCountdown() { |
| | | this.updateCountdown(); |
| | | if (this.countdownTimer) { |
| | | clearInterval(this.countdownTimer); |
| | | } |
| | | this.countdownTimer = setInterval(() => { |
| | | this.updateCountdown(); |
| | | }, 1000); |
| | | }, |
| | | |
| | | // 更新倒计时 |
| | | updateCountdown() { |
| | | const now = Date.now(); |
| | | const diff = this.endTime - now; |
| | | |
| | | if (diff <= 0) { |
| | | this.days = '00'; |
| | | this.hours = '00'; |
| | | this.minutes = '00'; |
| | | this.seconds = '00'; |
| | | clearInterval(this.countdownTimer); |
| | | this.countdownTimer = null; |
| | | return; |
| | | } |
| | | |
| | | // 计算天、时、分、秒 |
| | | const days = Math.floor(diff / (1000 * 60 * 60 * 24)); |
| | | const hours = Math.floor((diff % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60)); |
| | | const minutes = Math.floor((diff % (1000 * 60 * 60)) / (1000 * 60)); |
| | | const seconds = Math.floor((diff % (1000 * 60)) / 1000); |
| | | |
| | | // 格式化数字为两位数 |
| | | this.days = days.toString().padStart(2, '0'); |
| | | this.hours = hours.toString().padStart(2, '0'); |
| | | this.minutes = minutes.toString().padStart(2, '0'); |
| | | this.seconds = seconds.toString().padStart(2, '0'); |
| | | }, |
| | | |
| | | // 关闭弹窗 |
| | | onClose() { |
| | | this.$emit('close'); |
| | | }, |
| | | |
| | | // 点击遮罩层关闭 |
| | | onMaskClick() { |
| | | this.$emit('close'); |
| | | }, |
| | | |
| | | // 点击参与活动 |
| | | onJoinActivity() { |
| | | this.$emit('join'); |
| | | // 可以在这里添加参与活动后的逻辑,比如关闭弹窗 |
| | | // this.onClose(); |
| | | } |
| | | }, |
| | | beforeDestroy() { |
| | | if (this.countdownTimer) { |
| | | clearInterval(this.countdownTimer); |
| | | } |
| | | } |
| | | }; |
| | | </script> |
| | | |
| | | <style scoped> |
| | | .activity-popup { |
| | | position: fixed; |
| | | top: 0; |
| | | left: 0; |
| | | right: 0; |
| | | bottom: 0; |
| | | z-index: 9999; |
| | | display: flex; |
| | | justify-content: center; |
| | | align-items: center; |
| | | padding: 20rpx; |
| | | } |
| | | |
| | | |
| | | /* 遮罩层样式 */ |
| | | .popup-mask { |
| | | position: absolute; |
| | | top: 0; |
| | | left: 0; |
| | | right: 0; |
| | | bottom: 0; |
| | | background-color: rgba(0, 0, 0, 0.5); |
| | | |
| | | } |
| | | |
| | | .mask-enter { |
| | | opacity: 1; |
| | | } |
| | | |
| | | .mask-leave { |
| | | opacity: 0; |
| | | } |
| | | |
| | | /* 弹窗内容样式 */ |
| | | .popup-content { |
| | | width: 100%; |
| | | max-width: 600rpx; |
| | | background-color: #ffffff; |
| | | border-radius: 24rpx; |
| | | box-shadow: 0 10rpx 30rpx rgba(0, 0, 0, 0.2); |
| | | overflow: hidden; |
| | | transform: translateY(50rpx) scale(0.9); |
| | | opacity: 0; |
| | | transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); |
| | | } |
| | | |
| | | .content-enter { |
| | | transform: translateY(0) scale(1); |
| | | opacity: 1; |
| | | } |
| | | |
| | | .content-leave { |
| | | transform: translateY(50rpx) scale(0.9); |
| | | opacity: 0; |
| | | } |
| | | |
| | | /* 关闭按钮 */ |
| | | .close-btn { |
| | | position: absolute; |
| | | top: 20rpx; |
| | | right: 20rpx; |
| | | width: 50rpx; |
| | | height: 50rpx; |
| | | display: flex; |
| | | justify-content: center; |
| | | align-items: center; |
| | | background-color: rgba(255, 255, 255, 0.8); |
| | | border-radius: 50%; |
| | | z-index: 10; |
| | | } |
| | | |
| | | /* 活动图片 */ |
| | | .activity-img { |
| | | position: relative; |
| | | width: 100%; /* 容器宽度铺满弹窗内容区 */ |
| | | /* 可选:添加背景色,避免图片加载前显示空白 */ |
| | | background-color: #f5f5f5; |
| | | border-top-left-radius: 24rpx; /* 与图片圆角一致,避免容器露白 */ |
| | | border-top-right-radius: 24rpx; |
| | | } |
| | | |
| | | .popup-img { |
| | | width: 100%; |
| | | /* 核心:用 max-height 限制最大高度,不限制 min-height(保留比例) */ |
| | | max-height: 300rpx; /* 图片最大高度(超过则按比例缩小,保证完整显示) */ |
| | | } |
| | | |
| | | /* 活动信息 */ |
| | | .activity-info { |
| | | padding: 30rpx; |
| | | text-align: center; |
| | | } |
| | | |
| | | .activity-title { |
| | | font-size: 32rpx; |
| | | font-weight: bold; |
| | | color: #333333; |
| | | margin-bottom: 20rpx; |
| | | line-height: 1.3; |
| | | } |
| | | |
| | | .activity-desc { |
| | | font-size: 26rpx; |
| | | color: #666666; |
| | | margin-bottom: 30rpx; |
| | | line-height: 1.5; |
| | | } |
| | | |
| | | /* 倒计时样式 */ |
| | | .countdown { |
| | | margin-bottom: 30rpx; |
| | | text-align: center; |
| | | } |
| | | |
| | | .countdown-text { |
| | | font-size: 24rpx; |
| | | color: #ff6b3b; |
| | | margin-right: 10rpx; |
| | | } |
| | | |
| | | .countdown-time { |
| | | display: inline-flex; |
| | | align-items: center; |
| | | } |
| | | |
| | | .time-box { |
| | | display: inline-block; |
| | | width: 50rpx; |
| | | height: 50rpx; |
| | | line-height: 50rpx; |
| | | background-color: #ff6b3b; |
| | | color: #ffffff; |
| | | font-size: 26rpx; |
| | | font-weight: bold; |
| | | border-radius: 8rpx; |
| | | text-align: center; |
| | | } |
| | | |
| | | .time-sep { |
| | | margin: 0 10rpx; |
| | | color: #ff6b3b; |
| | | font-size: 28rpx; |
| | | font-weight: bold; |
| | | } |
| | | |
| | | /* 参与按钮 */ |
| | | .join-btn { |
| | | width: 100%; |
| | | height: 80rpx; |
| | | line-height: 80rpx; |
| | | background-color: #ff6b3b; |
| | | color: #ffffff; |
| | | font-size: 30rpx; |
| | | border-radius: 40rpx; |
| | | border: none; |
| | | box-shadow: 0 5rpx 15rpx rgba(255, 107, 59, 0.4); |
| | | transition: all 0.2s ease; |
| | | } |
| | | |
| | | .join-btn::after { |
| | | border: none; |
| | | } |
| | | |
| | | .join-btn:active { |
| | | background-color: #e55a2a; |
| | | transform: scale(0.98); |
| | | } |
| | | </style> |
| | |
| | | <view class="settlement">去购物车结算</view> |
| | | </view> --> |
| | | </view> |
| | | <view class="squareFotter" style="width: 750rpx; display: flex;align-items: center; |
| | | <view @click="gotoCardList()" class="squareFotter" style="width: 750rpx; display: flex;align-items: center; |
| | | justify-content: space-between;padding: 0 32rpx;box-sizing: border-box;"> |
| | | <view style="display: flex;align-items: center;justify-content: center;"> |
| | | <view class="icon" style="position: relative;"> |
| | |
| | | <view style="margin-left: 10rpx;color: #e06c75;font-size: 48rpx;font-weight: bold;"> |
| | | ¥{{priceInfo.price}}</view> |
| | | </view> |
| | | <view class="settlement" @click="gotoCardList()">去结算</view> |
| | | <view class="settlement">去结算</view> |
| | | </view> |
| | | </view> |
| | | </template> |
| | |
| | | </view> |
| | | |
| | | <view v-else class="comment-item" v-for="(comment, index) in comments" :key="comment.id"> |
| | | <view style="display: flex;"> |
| | | <view style="display: flex;" > |
| | | <image class="comment-avatar" :src="comment.userAvatar || '/static/default-avatar.png'"></image> |
| | | <view class="comment-content"> |
| | | <text class="nickname">{{comment.userNickname}} <text v-if="userId===comment.userId">(我)</text> </text> |
| | | <text class="content">{{comment.commentContent}}</text> |
| | | <view @click="replyClick(comment)"> |
| | | <text class="nickname">{{comment.userNickname}} <text v-if="userId===comment.userId">(我)</text> </text> |
| | | <text class="content">{{comment.commentContent}}</text> |
| | | </view> |
| | | <view style="position: relative;"> |
| | | <text class="time">{{formatTime(comment.createTime)}}</text> |
| | | <text @click="openReply(comment)" class="reply-btu time">回复</text> |
| | |
| | | <!-- 回复列表 --> |
| | | <view class="reply-list" v-if="comment.replies && comment.replies.length > 0"> |
| | | <view class="reply-item" v-for="(reply, replyIndex) in comment.replies" :key="reply.id"> |
| | | <view class="reply-content"> |
| | | <view class="reply-content" @click="replyClick(reply)"> |
| | | <view style="display: flex;"> |
| | | <image class="comment-reply-avatar" :src="reply.replyUserAvatar || '/static/default-avatar.png'"></image> |
| | | <text class="nickname">{{reply.userNickname}}<text v-if="userId===comment.userId">(我)</text></text> |
| | |
| | | |
| | | |
| | | <custom-tabbar bgColor="#333333" selected="index" selectedTextColor="#ffffff"></custom-tabbar> |
| | | |
| | | <ActivityPopup |
| | | :show="activityPopup.show" |
| | | :activityTitle="activityPopup.title" |
| | | :activityDesc="activityPopup.desc" |
| | | :activityImage="activityPopup.image" |
| | | :endTime="activityPopup.endTime" |
| | | @close="onClosePopup" |
| | | @join="onJoinActivity" |
| | | /> |
| | | </view> |
| | | </template> |
| | | |
| | |
| | | thubmsUpComment, |
| | | cancelThubmsUpComment, |
| | | changeThumbsUp, |
| | | getGoodsSimilarlyVideos |
| | | getGoodsSimilarlyVideos, |
| | | removeByIdVideoComment, |
| | | } from "@/api/video.js"; |
| | | |
| | | import ActivityPopup from '@/pages/ActivityPopup/ActivityPopup.vue' |
| | | import { mapState, mapMutations } from 'vuex' |
| | | import {setPopupRedisTime,getPopupAcitivty} from '@/api/popup.js' |
| | | import { changeCollect } from "@/api/collect.js"; |
| | | import { saveShare, saveShareClickRecord } from "@/api/share.js"; |
| | | import { silentLogin } from "@/api/connect.js"; |
| | |
| | | import storage from "@/utils/storage.js"; |
| | | import TopBar from "@/components/TopBar.vue"; |
| | | import { nextTick } from "vue"; |
| | | |
| | | import {getVideoCover } from "@/api/common.js" |
| | | export default { |
| | | components: {TopBar}, |
| | | components: {TopBar,ActivityPopup}, |
| | | computed: { |
| | | hasPlayTime() { |
| | | return this.sliderFormatTime(this.progress > 0 ? this.duration * this.progress / 100 : 0); |
| | | } |
| | | }, |
| | | // 错误:没有用 ... 展开,导致 activityPopup 是函数 |
| | | ...mapState(['activityPopup']) |
| | | }, |
| | | data() { |
| | | return { |
| | |
| | | } |
| | | }, |
| | | onShow() { |
| | | |
| | | this.openActivityPopup() |
| | | |
| | | |
| | | if(!this.userId){ |
| | | this.getUserId() |
| | | } |
| | |
| | | shareUser: userInfo.id |
| | | } |
| | | saveShare(data) |
| | | return { |
| | | title: videoInfo.title, |
| | | path: `/pages/tabbar/index/home?videoId=${videoInfo.id}&userId=${userInfo.id}`, |
| | | } |
| | | }, |
| | | // getVideoCover(videoInfo.id).then(res =>{ |
| | | // if(res.statusCode === 200){ |
| | | // imageUrl = res.data.data |
| | | // console.log(imageUrl) |
| | | // return { |
| | | // title: videoInfo.title, |
| | | // path: `/pages/tabbar/index/home?videoId=${videoInfo.id}&userId=${userInfo.id}`, |
| | | // imageUrl: imageUrl |
| | | // } |
| | | // } |
| | | |
| | | // }) |
| | | console.log(videoInfo) |
| | | return { |
| | | title: videoInfo.title, |
| | | path: `/pages/tabbar/index/home?videoId=${videoInfo.id}&userId=${userInfo.id}`, |
| | | imageUrl: videoInfo.coverUrl |
| | | } |
| | | // 保存分享记录 |
| | | }, |
| | | methods: { |
| | | async openActivityPopup() { |
| | | await getPopupAcitivty().then(res =>{ |
| | | if(res.statusCode === 200){ |
| | | 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() |
| | | }) |
| | | }else{ |
| | | this.hideActivityPopup() |
| | | } |
| | | |
| | | } |
| | | }); |
| | | } |
| | | |
| | | } |
| | | }) |
| | | |
| | | }, |
| | | ...mapMutations(['showActivityPopup','hideActivityPopup']), // 引入Vuex的方法 |
| | | onClosePopup() { |
| | | this.hideActivityPopup() |
| | | }, |
| | | onJoinActivity() { |
| | | // 处理参与活动逻辑 |
| | | console.log('全局:用户参与活动') |
| | | this.hideActivityPopup() |
| | | }, |
| | | replyClick(reply){ |
| | | |
| | | if(this.userId === reply.userId){ |
| | | let that = this; |
| | | uni.showModal({ |
| | | title: '提示', |
| | | content: '你确定要删除吗', |
| | | success: function (res) { |
| | | if (res.confirm) { |
| | | console.log('确定'); |
| | | //调用删除的逻辑 |
| | | console.log(reply) |
| | | removeByIdVideoComment(reply.id).then(res =>{ |
| | | const item = { |
| | | id:reply.videoId |
| | | } |
| | | that.commentQuery.pageNumber = 1; |
| | | //重新更新评论 |
| | | that.showComments(item); |
| | | }) |
| | | |
| | | } else if (res.cancel) { |
| | | console.log('取消'); |
| | | } |
| | | } |
| | | }); |
| | | |
| | | } |
| | | console.log(reply) |
| | | }, |
| | | // 截取视频当前帧 |
| | | captureVideoFrame(videoCtx) { |
| | | return new Promise((resolve) => { |
| | | videoCtx.requestFrame(() => { |
| | | wx.canvasToTempFilePath({ |
| | | canvasId: 'shareCanvas', |
| | | success: (res) => resolve(res), |
| | | fail: () => resolve({ tempFilePath: '/assets/default-cover.jpg' }) |
| | | }); |
| | | }); |
| | | }); |
| | | }, |
| | | requestFullScreen(id,item){ |
| | | console.log(item) |
| | | |
| | |
| | | this.commentQuery.pageNumber++; |
| | | }) |
| | | }, |
| | | |
| | | // 显示评论弹窗 |
| | | async showComments(item) { |
| | | this.commentForm.videoId = item.id; |
| | |
| | | userInfo: storage.getUserInfo(), |
| | | uuid: storage.getUuid(), |
| | | token: "", |
| | | // 活动弹窗状态 |
| | | activityPopup: { |
| | | show: false, |
| | | title: '', |
| | | desc: '', |
| | | image: '', |
| | | endTime: 0 |
| | | } |
| | | }, |
| | | mutations: { |
| | | // 显示弹窗 |
| | | showActivityPopup(state, data) { |
| | | state.activityPopup = { |
| | | show: true, |
| | | ...data // 合并传入的弹窗数据(标题、描述等) |
| | | } |
| | | console.log("Vuex 状态更新后:", state.activityPopup); |
| | | }, |
| | | // 隐藏弹窗 |
| | | hideActivityPopup(state) { |
| | | state.activityPopup.show = false |
| | | }, |
| | | login(state, userInfo) { |
| | | state.userInfo = userInfo || {}; |
| | | state.userName = |