From 1172c0ff85963a0f3b8fe5212082462b9ce0b1bc Mon Sep 17 00:00:00 2001
From: peng <peng.com>
Date: 星期二, 08 七月 2025 15:52:06 +0800
Subject: [PATCH] 商品发布
---
pages/tabbar/index/home.vue | 909 ++++++++++++++++++++++++++++++++++++++++++++++----------
1 files changed, 742 insertions(+), 167 deletions(-)
diff --git a/pages/tabbar/index/home.vue b/pages/tabbar/index/home.vue
index 53dd3de..3f07dde 100644
--- a/pages/tabbar/index/home.vue
+++ b/pages/tabbar/index/home.vue
@@ -1,98 +1,169 @@
<template>
<view class="video-container">
+ <top-bar selectedTitleIndex="home" textColor="white" @changeTab="topBarChange" class="topBar"></top-bar>
+ <!-- 瑙嗛鍔犺浇 -->
+ <zero-loading v-show="videoLoading" type="circle" color="#0ebd57" text=""></zero-loading>
<!-- 瑙嗛鍒楄〃 -->
- <swiper
- class="video-swiper"
- vertical
- circular
+ <swiper
+ class="video-swiper"
+ vertical
:current="currentIndex"
@change="onSwiperChange"
+ :duration="250"
+ easing-function="linear"
>
- <swiper-item v-for="(item, index) in videoList" :key="item.id">
- <!-- 鎾斁鎸夐挳锛堜粎褰撹棰戞殏鍋滄椂鏄剧ず锛� -->
- <view
- class="play-icon"
- @click="togglePlay(index)"
- v-if="!currentVideoIsPlaying"
- >
- <image src="/static/video/play.png" style="width: 45px;height: 45px" mode="aspectFit"></image>
- </view>
- <video
- :id="'video'+index"
- :ref="'video'+index"
- :src="item.videoUrl"
- :autoplay="currentIndex === index"
- :controls="false"
- :loop="true"
- :object-fit="item.objectFit"
- class="video-item"
- @play="onPlay(item.id, index)"
- @pause="onPause(index)"
- @ended="onEnded(index)"
- @click="togglePlay(index)"
- @timeupdate="onTimeUpdate($event)"
- ></video>
-
- <!-- 鎮寕鍟嗗搧閾炬帴灞� -->
- <view class="goods-link-warp">
- <view class="goods-link">
- <view class="goods-container">
- <!-- 鍟嗗搧鍥剧墖 -->
- <image class="goods-image" :src="item.goods.imageUrl" mode="aspectFill"></image>
-
- <!-- 鍟嗗搧淇℃伅 -->
- <view class="goods-info">
- <text class="goods-name">{{item.goods.name}}</text>
- <view class="price-section">
- <text class="current-price">楼{{item.goods.price}}</text>
- <text class="original-price" v-if="item.goods.originalPrice">楼{{item.goods.originalPrice}}</text>
- </view>
- <text class="sales-count">{{item.goods.saleNum}}浜哄凡璐�</text>
- </view>
-
- <!-- 璐拱鎸夐挳 -->
- <view class="buy-button">
- <text>璐拱</text>
- </view>
+ <swiper-item
+ v-for="(item, index) in videoList"
+ :key="item.updateKey"
+ @touchstart="handleSwiperStart"
+ @touchmove="handleSwiperMove"
+ @touchend="handleSwiperEnd(item)"
+ >
+ <view style="width: 100%;height: 100%;" v-if="item.videoContentType === 'video'">
+ <!-- 鎾斁鎸夐挳锛堜粎褰撹棰戞殏鍋滄椂鏄剧ず锛� -->
+ <view
+ class="play-icon"
+ @click="togglePlay(index)"
+ v-show="!currentVideoIsPlaying"
+ >
+ <image src="/static/video/play.png" style="width: 45px;height: 45px" mode="aspectFit"></image>
</view>
+ <video
+ v-if="index >= currentIndex - videoLiveOffset && index <= currentIndex + videoLiveOffset"
+ :id="'video'+index"
+ :ref="'video'+index"
+ :src="item.videoUrl"
+ :autoplay="index === currentIndex"
+ :controls="false"
+ :loop="true"
+ :object-fit="item.videoFit"
+ :enable-progress-gesture="false"
+ :show-center-play-btn="false"
+ class="video-item"
+ @play="onPlay(item.id, index)"
+ @pause="onPause(index)"
+ @ended="onEnded(index)"
+ @click="togglePlay(index)"
+ @timeupdate="onTimeUpdate($event)"
+ @loadedmetadata="onLoadedMetadata($event)"
+ @waiting="videoWaiting(index)"
+ ></video>
+ <!-- 鑷畾涔夋帶鍒舵潯 -->
+ <view
+ @touchstart.stop="handleTouchStart"
+ @touchmove.stop="handleTouchMove"
+ @touchend.stop="handleTouchEnd"
+ :style="{bottom: marginBottom + 'px'}"
+ class="container">
+ <!-- 杩涘害鏉� - 鏁翠釜鍖哄煙鍙嫋鍔� -->
+ <view class="process-warp" :style="{ opacity: showProcess ? 1 : 0 }">
+ <!-- 鏄剧ず褰撳墠杩涘害 -->
+ <view class="progress-text">{{ hasPlayTime }}/{{formartDuration}}</view>
+ <view
+ class="progress-bar"
+ id="progressBar"
+ >
+
+ <!-- 宸插~鍏呴儴鍒� -->
+ <view class="progress-fill" :style="{ width: progress + '%' }"></view>
+ </view>
+ </view>
+ </view>
+ </view>
+ <view style="width: 100%; height: 100%;" v-else-if="item.videoContentType === 'img'">
+ <uni-swiper-dot
+ :info="item.imgs"
+ :current="currentImgIndex"
+ mode="round"
+ style="width: 100%;height: 100%;display: flex;justify-content: center;align-items: center;"
+ :dots-styles="{width: 24, bottom: 24,selectedBackgroundColor: 'green', backgroundColor: 'gray'}"
+ >
+ <swiper class="swiper-box" @change="imgChange" :autoplay="true" :interval="3000">
+ <swiper-item v-for="img in item.imgs" :key="img">
+ <view class="swiper-item">
+ <!-- 璋冩暣 image 鏍峰紡锛屼娇鍏跺眳涓笖鎸夋瘮渚嬬缉鏀� -->
+ <image
+ :src="img"
+ mode="aspectFit"
+ style="width: 100%; height: 100%; display: block; margin: 0 auto;"
+ ></image>
+ </view>
+ </swiper-item>
+ </swiper>
+ </uni-swiper-dot>
+ </view>
+
+
+ <!-- 鎮寕鍟嗗搧閾炬帴灞� -->
+ <view class="goods-link-warp" 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="goods-container" @click="jumpToPay(item.id)">
+ <!-- 鍟嗗搧鍥剧墖 -->
+ <image class="goods-image" :src="goods.thumbnail" mode="aspectFill"></image>
+
+ <!-- 鍟嗗搧淇℃伅 -->
+ <view class="goods-info">
+ <text class="goods-name">{{goods.goodsName}}</text>
+ <view class="price-section">
+ <text class="current-price">楼{{goods.price}}</text>
+ <text class="original-price" v-if="goods.originalPrice">楼{{goods.originalPrice}}</text>
+ </view>
+ </view>
+ </view>
+ </swiper-item>
+ </swiper>
</view>
</view>
-
-
+
+
<!-- 瑙嗛淇℃伅灞� -->
- <view class="video-info">
- <view>
+ <view class="video-info" :style="{bottom: marginBottom + 20 + 'px'}">
+ <view style="width: 100%; position: relative;">
<text class="video-author">@{{item.authorName}}</text>
+ <text class="iconfont" @click="jumpToSearch" style="position: absolute;right: 45px;"></text>
</view>
<view style="width: 100%;word-wrap: break-word;white-space: normal;overflow-wrap: break-word;">
<text class="video-title">{{item.title}}</text>
- <text class="video-tag" v-for="(tag, index) in item.tagList" :key="tag">#{{tag.tagName}}</text>
+ <text class="video-tag" v-for="(tag, index) in item.tagList" :key="tag.id">#{{tag.tagName}}</text>
</view>
+
</view>
-
- <!-- 鍙充晶浜掑姩鎸夐挳 -->
+
+ <!-- 鍙充晶浜掑姩鎸夐挳 -->
<view class="action-buttons">
<view class="avatar-container">
- <image class="avatar" :src="item.authorAvatar" mode="aspectFill"></image>
+ <image class="avatar" @click="jumpToHomePage(item.authorId)" @touchend.stop :src="item.authorAvatar" mode="aspectFill"></image>
<!-- 鍏虫敞鍥炬爣 - 浣跨敤缁濆瀹氫綅 -->
- <view v-if="!item.subscribeThisAuthor" class="follow-icon" @click="subscribeAuth(index, item.authorId)">
+ <view v-if="!item.subscribeThisAuthor" class="follow-icon" @click="subscribeAuth(index, item.authorId)" @touchend.stop>
<text class="iconfont"></text>
</view>
</view>
- <view class="action-item" @click="toggleCollect(item, index)">
- <text class="iconfont" v-if="item.collected"></text>
+ <view class="action-item" @click="toggleThumbsUp(item, index)">
+ <text class="iconfont" v-if="item.thumbsUp"></text>
<text class="iconfont" v-else></text>
- <text style="font-size: 10px;font-weight: lighter;">{{item.collectNum}}</text>
+ <text style="font-size: 10px;font-weight: lighter;">{{item.thumbsUpNum}}</text>
</view>
<view class="action-item" @click="showComments(item)">
<text class="iconfont"></text>
<text style="font-size: 10px;font-weight: lighter;">{{item.commentNum}}</text>
</view>
+ <view class="action-item" @click="toggleCollect(item, index)">
+ <text class="iconfont" v-if="item.collected"></text>
+ <text class="iconfont" v-else></text>
+ <text style="font-size: 10px;font-weight: lighter;">{{item.collectNum}}</text>
+ </view>
+ <view class="action-item">
+ <button open-type="share" class="custom-share-btn" :data-obj="item">
+ <text class="iconfont"></text>
+ </button>
+ </view>
</view>
-
+
</swiper-item>
</swiper>
-
+
<!-- 璇勮寮圭獥 -->
<uni-popup ref="commentPopup" type="bottom" :is-mask-click="true" @maskClick="closeCommentPopup">
<view class="comment-popup">
@@ -104,34 +175,36 @@
</view>
<text class="iconfont close-icon" @click="closeCommentPopup"></text>
</view>
-
+
<scroll-view class="comment-list" scroll-y :show-scrollbar="false" @scrolltolower="getCommentPage">
<view v-if="commentLoading" class="loading">
<uni-load-more status="loading"></uni-load-more>
</view>
-
+
<view v-else-if="comments.length === 0" class="empty">
鏆傛棤璇勮锛屽揩鏉ュ彂琛ㄧ涓�鏉¤瘎璁哄惂~
</view>
-
+
<view v-else class="comment-item" v-for="(comment, index) in comments" :key="comment.id">
<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>
<text class="content">{{comment.commentContent}}</text>
- <view style="position: relative;">
+ <view style="position: relative;">
<text class="time">{{formatTime(comment.createTime)}}</text>
<text @click="openReply(comment)" class="reply-btu time">鍥炲</text>
- <text class="thumbs-up time iconfont"></text>
+ <text v-if="!comment.hasThumbsUp" class="thumbs-up time iconfont" @click="thubmsUp(comment.id, index, null)"><text v-show="comment.thumbsUpNum > 0" class="thumbs-num">{{comment.thumbsUpNum}}</text></text>
+ <text v-else class="thumbs-up time iconfont" @click="cancelThumbsUp(comment.id, index, null)"><text v-show="comment.thumbsUpNum > 0" class="thumbs-num">{{comment.thumbsUpNum}}</text></text>
</view>
</view>
</view>
<!-- 鍥炲鍒楄〃 -->
<view class="reply-list" v-if="comment.replies && comment.replies.length > 0">
- <view class="reply-item" v-for="reply in comment.replies" :key="reply.id">
+ <view class="reply-item" v-for="(reply, replyIndex) in comment.replies" :key="reply.id">
<view class="reply-content">
<view style="display: flex;">
+ <image class="comment-reply-avatar" :src="reply.replyUserAvatar || '/static/default-avatar.png'"></image>
<text class="nickname">{{reply.userNickname}}</text>
<text v-if="reply.replyUserId && reply.masterCommentId !== reply.replyId" class="reply-to"><text style="margin-right: 10rpx;font-size: 28rpx;" class="iconfont"></text>{{reply.replyUserNickname}}</text>
</view>
@@ -139,7 +212,8 @@
<view class="reply-footer">
<text class="time">{{formatTime(reply.createTime)}}</text>
<text @click="openReply(comment, reply)" class="reply-btu time">鍥炲</text>
- <text class="thumbs-up time iconfont"> </text>
+ <text v-if="!reply.hasThumbsUp" class="thumbs-up time iconfont" @click="thubmsUp(reply.id, index, replyIndex)"><text v-show="reply.thumbsUpNum > 0" class="thumbs-num">{{reply.thumbsUpNum}}</text></text>
+ <text v-else class="thumbs-up time iconfont" @click="cancelThumbsUp(reply.id, index, replyIndex)"><text v-show="reply.thumbsUpNum > 0" class="thumbs-num">{{reply.thumbsUpNum}}</text></text>
</view>
</view>
</view>
@@ -157,29 +231,65 @@
</view>
</scroll-view>
<view class="comment-input-area">
- <input
+ <input
ref="commentInput"
- class="comment-input"
- v-model="commentForm.commentContent"
- :placeholder="commentForm.replyId ? `鍥炲 @${commentForm.replyUserNickname}` : '鍐欎笅浣犵殑璇勮...'"
+ class="comment-input"
+ v-model="commentForm.commentContent"
+ :placeholder="commentForm.replyId ? `鍥炲 @${commentForm.replyUserNickname}` : '鍐欎笅浣犵殑璇勮...'"
placeholder-class="placeholder"
/>
<button class="submit-btn" @click="submitComment" :disabled="!commentForm.commentContent.trim()">鍙戦��</button>
</view>
</view>
</uni-popup>
-
-
+
+
<custom-tabbar bgColor="#333333" selected="index" selectedTextColor="#ffffff"></custom-tabbar>
</view>
</template>
<script>
-import { getRecommendVideos, savePlayRecord, subscribe, getVideoComments, addVideoComment } from "@/api/video.js";
+import {
+ getRecommendVideos,
+ savePlayRecord,
+ subscribe,
+ getVideoComments,
+ addVideoComment,
+ thubmsUpComment,
+ cancelThubmsUpComment,
+ changeThumbsUp,
+ getGoodsSimilarlyVideos
+} from "@/api/video.js";
import { changeCollect } from "@/api/collect.js";
+import { saveShare, saveShareClickRecord } from "@/api/share.js";
+import { silentLogin } from "@/api/connect.js";
+import { getUserInfo } from "@/api/members";
+import storage from "@/utils/storage.js";
+import TopBar from "@/components/TopBar.vue";
+
export default {
+ components: {TopBar},
+ computed: {
+ hasPlayTime() {
+ return this.sliderFormatTime(this.progress > 0 ? this.duration * this.progress / 100 : 0);
+ }
+ },
data() {
return {
+ currentImgIndex: 0, // 鎾斁鍒扮鍑犲紶鍥�--绱㈠紩
+ currentGoodsIndex: 0, // 鎾斁鍒扮鍑犱釜鍟嗗搧--绱㈠紩
+ currentTime: 0,
+ formartDuration: '',
+ duration: 0,
+ startX: 0,
+ progress: 0, // 瑙嗛杩涘害
+ startProgress : 0, // 寮�濮嬫粦鍔ㄦ椂鐨勮繘搴�
+ barLeft: 0, // 杩涘害鏉″乏杈圭晫浣嶇疆
+ barWidth: 0, // 杩涘害鏉″搴�
+ isDragging: false, // 鏄惁姝e湪鎷栧姩
+ processHidenTimer: null, // 杩涘害鏉¢殣钘忓畾鏃跺櫒
+ showProcess: false, // 鏄惁鏄剧ず杩涘害鏉�
+ videoNoMore: false, // 鏄惁杩樻湁鏇村瑙嗛
commentNoMore: false, // 鏄惁杩樻湁鏇村璇勮
commentQuery: {
pageNumber: 1,
@@ -200,7 +310,8 @@
replyId: '',
replyUserId: '',
replyUserNickname: '',
- masterCommentId: ''
+ replyUserAvatar: '',
+ masterCommentId: null
},
comments: [], // 璇勮鍒楄〃
commentsTotal: 0, // 璇勮鎬绘潯鏁�
@@ -216,20 +327,46 @@
startPlayTime: 0 // 杩欎釜瑙嗛浠庝粈涔堟椂鍊欏紑濮嬫挱鏀剧殑
},
currentVideoIsPlaying: true, // 褰撳墠瑙嗛鏄惁姝e湪鎾斁
- isFullScreen: false,
- windowHeight: 0,
currentIndex: 0, // 褰撳墠鎾斁鐨勮棰戠储寮�
- videoList: [
-
- ], // 瑙嗛鍒楄〃鏁版嵁
- videoContexts: [], // 瑙嗛涓婁笅鏂囧璞¢泦鍚�
+ videoLoading: false, // 瑙嗛缂撳啿涓�
+ videoList: [], // 瑙嗛鍒楄〃鏁版嵁
+ videoBufferOffset: 0.1 ,// 瑙嗛棰勫姞杞藉弬鏁�
+ videoLiveOffset: 2, // 淇濈暀褰撳墠瑙嗛鍓嶅悗鍚勫灏戜釜瑙嗛涓婁笅鏂�
+ touchXY: { // 鐩戝惉宸︽粦鍙虫粦
+ startX: 0,
+ endX: 0,
+ startY: 0,
+ endY: 0
+ },
loading: false, // 鏄惁姝e湪鍔犺浇
- page: 1, // 褰撳墠椤电爜
- pageSize: 10 // 姣忛〉鏁伴噺
+ videoQuery: {
+ pageNumber: 1,
+ pageSize: 10,
+ videoFrom: 'recommend'
+ },
+ goodsSimilarlyQuery: { // 鐩镐技瑙嗛鏌ヨ
+ pageNumber: 1,
+ pageSize: 10,
+ videoFrom: 'goodsSimilarly',
+ goodsIds: [],
+ currentVideoId: ''
+ },
+ similarlyVideoList: [], // 鐩镐技瑙嗛
+ similarlyNomore: false, // 鏄惁杩樻湁鏇村鐩镐技瑙嗛
+ similaryVideoIndex: 0, // 鐩镐技瑙嗛鐨勬挱鏀句綅缃�
+ similarlyLoading: false, // 鐩镐技瑙嗛鍔犺浇
+ marginBottom: 0 // 搴曢儴瀹夊叏鍖哄煙
}
},
onShow() {
- this.loadVideos()
+ // const token = storage.getAccessToken();
+ // if (! token) {
+ // this.wxSilentLogin(() => {
+ // this.loadVideos();
+ // })
+ // } else {
+ // this.loadVideos();
+ // }
// 濡傛灉瑙嗛鎸変笅鏆傚仠鍚庡垏鎹㈤〉闈㈠啀鍥炲埌椤甸潰鏃讹紝鍙畻鏆傚仠鏃堕棿锛堝洜涓烘殏鍋滄椂闂村拰绂诲紑椤甸潰鏃堕棿鏄噸澶嶇殑锛屽彧绠椾竴涓級
if(this.startHidenTime !== 0 && this.currentVideoIsPlaying) {
const duration = Date.now() - this.startHidenTime
@@ -239,14 +376,178 @@
onHide() {
this.startHidenTime = Date.now()
},
- onLoad() {
- this.loadVideos();
+ onLoad(option) {
+ this.marginBottom = uni.getSystemInfoSync().safeAreaInsets.bottom
+ const token = storage.getAccessToken();
+ if (! token) {
+ this.wxSilentLogin(() => {
+ this.loadVideos();
+ // 鍒ゆ柇鏄笉鏄偣鍑诲垎浜摼鎺ヨ繘鏉ョ殑
+ if (option.userId && option.videoId) {
+ // 淇濆瓨鍒嗕韩鐐瑰嚮璁板綍
+ saveShareClickRecord({refId: option.videoId, shareUserId: option.userId})
+ }
+ })
+ } else {
+ this.loadVideos();
+ }
},
- onReady() {
- // 鍒濆鍖栬棰戜笂涓嬫枃
- this.initVideoContexts();
+ onShareAppMessage(e) {
+ const userInfo = storage.getUserInfo();
+ if(!userInfo) {
+ console.log("鏈櫥褰曚笉鑳藉垎浜�");
+ return
+ }
+ const videoInfo = e.target.dataset.obj;
+ // 淇濆瓨鍒嗕韩璁板綍
+ const data = {
+ shareType: 'video',
+ refId: videoInfo.id,
+ shareUser: userInfo.id
+ }
+ saveShare(data)
+ return {
+ title: videoInfo.title,
+ path: `/pages/tabbar/index/home?videoId=${videoInfo.id}&userId=${userInfo.id}`,
+ imageUrl: videoInfo.coverUrl
+ }
},
methods: {
+ // 鏌ヨ褰撳墠瑙嗛鐨勫叧鑱旇棰戯紙鎸備簡鍚屼竴鍟嗗搧鐨勶級
+ async getGoodsSimilarly() {
+ if (this.similarlyLoading || this.similarlyNomore) return Promise.resolve();;
+ const video = this.videoList[this.currentIndex];
+ if (video.goodsList && video.goodsList.length > 0) {
+ this.goodsSimilarlyQuery.goodsIds = video.goodsList.map(goods => goods.goodsId);
+ this.goodsSimilarlyQuery.currentVideoId = video.id;
+ if (this.similarlyVideoList.length < 1) {
+ this.similarlyVideoList.push(video); // 纭繚鍘熻棰戞槸妯悜瑙嗛鐨勭涓�涓厓绱�
+ }
+ this.similarlyLoading = true;
+ return getGoodsSimilarlyVideos(this.goodsSimilarlyQuery).then(res => {
+
+ this.similarlyVideoList = [
+ ...this.similarlyVideoList,
+ ...res.data.data.filter(
+ (newItem) => !this.similarlyVideoList.some((oldItem) => oldItem.id === newItem.id)
+ ),
+ ];
+
+ this.similarlyLoading = false;
+ if(res.data.data.length < this.goodsSimilarlyQuery.pageSize) {
+ this.similarlyNomore = true;
+ return;
+ }
+ this.goodsSimilarlyQuery.pageNumber++;
+ })
+ }
+ },
+ // 璺宠浆鍒版悳绱㈤〉
+ jumpToSearch() {
+ uni.navigateTo({
+ url: '/pages/video/video-search'
+ });
+ },
+ // 鍒囨崲椤堕儴瀵艰埅鏍�
+ topBarChange(titleObj) {
+ if (titleObj.index === 'home') {
+ uni.switchTab({
+ url: titleObj.pagePath
+ });
+ } else {
+ uni.redirectTo({
+ url: titleObj.pagePath
+ });
+ }
+ },
+ // 闈欓粯鐧诲綍
+ wxSilentLogin(callback) {
+ //鑾峰彇code
+ uni.login({
+ success: (codeRes) => {
+ if(codeRes.errMsg === "login:ok") {
+ // 闈欓粯鐧诲綍
+ silentLogin({code: codeRes.code}).then(res => {
+ storage.setAccessToken(res.data.data.accessToken);
+ storage.setRefreshToken(res.data.data.refreshToken);
+ //鑾峰彇鐢ㄦ埛淇℃伅
+ getUserInfo().then((user) => {
+ storage.setUserInfo(user.data.result);
+ storage.setHasLogin(true);
+ callback()
+ });
+ })
+ } else {
+ uni.showToast({
+ title: "绯荤粺寮傚父锛岃鑱旂郴绠$悊鍛橈紒"
+ })
+ }
+ },
+ });
+ },
+ // 鐐瑰嚮鍟嗗搧璺宠浆
+ jumpToPay(videoId) {
+ uni.navigateTo({
+ url: '/pages/video/video-goods-detail?videoId=' + videoId
+ });
+ },
+ // 杞挱鍥惧彉鍖�
+ imgChange(e) {
+ this.currentImgIndex = e.detail.current;
+ },
+ // 鍟嗗搧杞挱鍥惧彉鍖�
+ goodsChange(e) {
+ this.currentGoodsIndex = e.detail.current;
+ },
+ // 鑾峰彇杩涘害鏉$殑浣嶇疆鍜屽昂瀵�
+ getBarRect() {
+ const query = uni.createSelectorQuery().in(this);
+ query.select('#progressBar').boundingClientRect(rect => {
+ if (rect) {
+ this.barLeft = rect.left;
+ this.barWidth = rect.width;
+ }
+ }).exec();
+ },
+ // 璺宠浆涓汉涓婚〉
+ jumpToHomePage(authorId) {
+ console.log("浣滆�卛d", authorId);
+ uni.navigateTo({
+ url: "/pages/video/home-page?authorId=" + authorId
+ })
+ },
+ // 鍙栨秷鐐硅禐
+ async cancelThumbsUp(id, commentIndex, replyIndex) {
+ const data = {
+ refId: id,
+ thumbsUpType: 'video_comment'
+ }
+ cancelThubmsUpComment(data).then(res => {
+ if(replyIndex != null) {
+ this.comments[commentIndex].replies[replyIndex].hasThumbsUp = false;
+ this.comments[commentIndex].replies[replyIndex].thumbsUpNum -= 1;
+ } else {
+ this.comments[commentIndex].hasThumbsUp = false;
+ this.comments[commentIndex].thumbsUpNum -= 1;
+ }
+ })
+ },
+ // 璇勮鐐硅禐
+ async thubmsUp(id, commentIndex, replyIndex) {
+ const data = {
+ refId: id,
+ thumbsUpType: 'video_comment'
+ }
+ thubmsUpComment(data).then(res => {
+ if(replyIndex != null) {
+ this.comments[commentIndex].replies[replyIndex].hasThumbsUp = true;
+ this.comments[commentIndex].replies[replyIndex].thumbsUpNum += 1;
+ } else {
+ this.comments[commentIndex].hasThumbsUp = true;
+ this.comments[commentIndex].thumbsUpNum += 1;
+ }
+ })
+ },
// 鍔犺浇涓嬩竴椤靛洖澶�
loadNextPageReply(index) {
this.replyCommentQuery.pageNumber++;
@@ -282,7 +583,8 @@
replyId: '',
replyUserId: '',
replyUserNickname: '',
- masterCommentId: ''
+ replyUserAvatar: '',
+ masterCommentId: null
}
},
// 鍙栨秷鍥炲
@@ -298,22 +600,29 @@
this.commentForm.replyId = comment.id;
this.commentForm.replyUserId = comment.userId;
this.commentForm.replyUserNickname = comment.userNickname;
+ this.commentForm.replyUserAvatar = comment.userAvatar;
// 鑷姩鑱氱劍杈撳叆妗�
this.$nextTick(() => {
const input = this.$refs.commentInput;
if (input) input.focus();
});
},
+ // 杩涘害鏉℃椂闂存牸寮忓寲 (00:00)
+ sliderFormatTime(seconds) {
+ const mins = Math.floor(seconds / 60);
+ const secs = Math.floor(seconds % 60);
+ return `${mins.toString().padStart(2, '0')}:${secs.toString().padStart(2, '0')}`;
+ },
// 鏍煎紡鍖栨椂闂�
formatTime(time) {
const date = new Date(time);
const now = new Date();
const diff = Math.floor((now - date) / 1000); // 绉�
-
+
if (diff < 60) return '鍒氬垰';
if (diff < 3600) return `${Math.floor(diff / 60)}鍒嗛挓鍓峘;
if (diff < 86400) return `${Math.floor(diff / 3600)}灏忔椂鍓峘;
-
+
return `${date.getMonth() + 1}鏈�${date.getDate()}鏃;
},
// 鎻愪氦璇勮
@@ -329,7 +638,7 @@
addVideoComment(this.commentForm).then(res => {
if(res.data.code === 200) {
this.resetCommentForm()
-
+
// 濡傛灉鏄瘎璁哄埆浜虹殑鍥炲锛岄偅涔堝氨灏嗚繖涓彂甯冨埌replies閲岄潰
if(res.data.data.replyId) {
for (const [index, item] of this.comments.entries()) {
@@ -439,37 +748,44 @@
}
})
},
- // 鍒濆鍖栬棰戜笂涓嬫枃
- initVideoContexts() {
- this.videoContexts = this.videoList.map((_, index) => {
- let videoContent = uni.createVideoContext(`video${index}`, this);
- return videoContent;
- });
- },
-
+
// 鍔犺浇瑙嗛鏁版嵁
async loadVideos() {
- if (this.loading) return;
+ if (this.loading || this.videoNoMore) return;
this.loading = true;
-
- getRecommendVideos({pageNumber: this.page, pageSize: this.pageSize}).then(res => {
- console.log(res, "瑙嗛鏁版嵁");
- if (this.page === 1) {
- this.videoList = res.data.data;
+
+ getRecommendVideos(this.videoQuery).then(res => {
+ // 鏂板涓�涓瓧娈电敤浜庡惊鐜椂鐨刱ey
+ const data = res.data.data.map(item => {
+ return {
+ ...item,
+ updateKey: item.id
+ }
+ })
+ if (this.videoQuery.pageNumber === 1) {
+ this.videoList = data;
} else {
- this.videoList = [...this.videoList, ...res.data.data];
+ this.videoList = [
+ ...this.videoList,
+ ...data.filter(
+ (newItem) => !this.videoList.some((oldItem) => oldItem.id === newItem.id)
+ ),
+ ];
+
}
-
- this.page++;
- this.$nextTick(() => {
- this.initVideoContexts();
- });
this.loading = false;
+ if(res.data.data.length < this.videoQuery.pageSize) {
+ this.videoNoMore = true;
+ return;
+ }
+ this.videoQuery.pageNumber++;
+
})
},
-
+
// 婊戝姩鍒囨崲瑙嗛
onSwiperChange(e) {
+ this.videoLoading = false
// 濡傛灉瑙嗛澶勪簬鏆傚仠鐘舵�佸線涓嬪埛瑙嗛锛岄偅涔堥渶瑕佸啀璁$畻涓�娆℃殏鍋滄椂闂�
if(!this.currentVideoIsPlaying) {
if(this.startPauseTime !== 0) {
@@ -481,19 +797,108 @@
this.savePlayRecord()
const oldIndex = this.currentIndex;
this.currentIndex = e.detail.current;
-
+ const videoContext = uni.createVideoContext(`video${oldIndex}`, this);
// 鏆傚仠涓婁竴涓棰�
- if (this.videoContexts[oldIndex]) {
- this.videoContexts[oldIndex].pause();
+ videoContext.pause();
+ this.startPauseTime = 0;
+
+ // 璁剧疆褰撳墠鎾斁瑙嗛鐨勬�绘椂闀�
+ this.duration = this.videoList[this.currentIndex].videoDuration;
+ this.formartDuration = this.sliderFormatTime(this.duration);
+
+ // 鎾斁褰撳墠瑙嗛
+ const videoContext1 = uni.createVideoContext(`video${this.currentIndex}`, this);
+ videoContext1.play()
+
+ // 涓嬫粦鏃讹紝闇�瑕佸皢涓婁竴涓棰戦噸缃负鍘熷瑙嗛(濡傛灉妯悜婊戝姩浜嗙浉鍏宠棰�)
+ if (this.similaryVideoIndex !== 0) {
+ this.videoList[oldIndex] = this.similarlyVideoList[0]
+ }
+ this.similarlyVideoList = [];
+ this.similaryVideoIndex = 0;
+ this.similarlyNomore = false;
+ this.similarlyLoading = false;
+ this.goodsSimilarlyQuery = {
+ pageNumber: 1,
+ pageSize: 6,
+ videoFrom: 'goodsSimilarly',
+ goodsIds: [],
+ currentVideoId: ''
}
- this.startPauseTime = 0;
- // 鎾斁褰撳墠瑙嗛
- if (this.videoContexts[this.currentIndex]) {
- this.videoContexts[this.currentIndex].play();
+ // 濡傛灉鍓╀綑瑙嗛涓嶈冻锛岃Е鍙戣姹傝幏鍙栨洿澶氳棰�
+ if (this.videoList.length - 1 < this.currentIndex + this.videoLiveOffset) {
+ this.loadVideos()
}
+
},
-
+
+ // 寮�濮嬭Е鎽�
+ handleSwiperStart(e) {
+ console.log("寮�濮嬭Е鎽�", e);
+ this.touchXY.startX = e.touches[0].pageX
+ this.touchXY.startY = e.touches[0].pageY
+ },
+ // 瑙︽懜涓�
+ handleSwiperMove(e) {
+ this.touchXY.endX = e.touches[0].pageX
+ this.touchXY.endY = e.touches[0].pageY
+ },
+ // 缁撴潫瑙︽懜
+ async handleSwiperEnd(item) {
+ // 闃叉婊戝姩婊氬姩鏉′篃瑙﹀彂璺宠浆
+ if (this.showProcess) {
+ return
+ }
+ const diffX = this.touchXY.endX - this.touchXY.startX
+ const diffY = this.touchXY.endY - this.touchXY.startY
+
+ // 鍒ゆ柇鏄惁鏄í鍚戞粦鍔紙X杞村彉鍖栧ぇ浜嶻杞村彉鍖栵級
+ if (Math.abs(diffX) > Math.abs(diffY)) {
+ if (diffX > 0) {
+ console.log('鍙虫粦')
+ if (this.similaryVideoIndex !== 0) {
+ // 濡傛灉婊戝姩浜嗘í鍚戣棰戯紝閭d箞鍙虫粦灏卞仛瑙嗛鍒囨崲鑰屼笉鏄烦杞晢鍝侀〉
+ // 鍒囨崲涓嬩竴涓棰�
+ const oldIndex = this.similaryVideoIndex;
+ this.similaryVideoIndex = Math.max(this.similaryVideoIndex - 1, 0);
+ if (this.similaryVideoIndex < oldIndex) {
+ // 鎶婄珫鍚戣棰戠殑褰撳墠鎾斁浣嶇疆鏇挎崲涓烘í鍚戣棰戠殑褰撳墠绱㈠紩鍏冪礌
+ const video = this.similarlyVideoList[this.similaryVideoIndex];
+ video["updateKey"] = video.id + this.similaryVideoIndex
+ this.videoList.splice(this.currentIndex, 1, video);
+ // this.videoList[this.currentIndex] = video
+ }
+ }
+ else if (item.goodsList && item.goodsList.length > 0) {
+ this.jumpToPay(item.id)
+ }
+ } else {
+ console.log('宸︽粦')
+ if (this.similarlyVideoList.length < 1 || this.similarlyVideoList.length - this.similaryVideoIndex - 1 <= 3) {
+ // 鐩稿叧瑙嗛涓虹┖鎴栬�呭墿浣欒棰戜笉瓒筹紝瑙﹀彂鍔犺浇鐩稿叧瑙嗛
+ await this.getGoodsSimilarly()
+ }
+ // 鍒囨崲涓嬩竴涓棰�
+ const oldIndex = this.similaryVideoIndex;
+ this.similaryVideoIndex = Math.min(this.similaryVideoIndex + 1, this.similarlyVideoList.length - 1);
+ if (this.similaryVideoIndex > oldIndex) {
+ // 鎶婄珫鍚戣棰戠殑褰撳墠鎾斁浣嶇疆鏇挎崲涓烘í鍚戣棰戠殑褰撳墠绱㈠紩鍏冪礌
+ const video = this.similarlyVideoList[this.similaryVideoIndex];
+ video["updateKey"] = video.id + this.similaryVideoIndex
+ this.videoList.splice(this.currentIndex, 1, video);
+ }
+ }
+ }
+ // 閲嶇疆鍧愭爣
+ this.touchXY = {
+ startX: 0,
+ endX: 0,
+ startY: 0,
+ endY: 0
+ }
+ },
+
// 鏀惰棌/鍙栨秷鏀惰棌
toggleCollect(item, index) {
let data = {
@@ -516,23 +921,53 @@
}
})
},
+ // 鐐硅禐/鍙栨秷鐐硅禐
+ toggleThumbsUp(item, index) {
+ let data = {
+ refId: item.id,
+ thumbsUpType: 'video'
+ }
+ const beforeThumbsUp = item.thumbsUp
+ const beforeThumbsUpNum = item.thumbsUpNum
+ if(item.thumbsUp) {
+ this.videoList[index].thumbsUp = false
+ this.videoList[index].thumbsUpNum -= 1
+ } else {
+ this.videoList[index].thumbsUp = true
+ this.videoList[index].thumbsUpNum += 1
+ }
+ changeThumbsUp(data).then(res => {
+ if(res.data.code !== 200) {
+ this.videoList[index].thumbsUp = beforeThumbsUp
+ this.videoList[index].thumbsUpNum = beforeThumbsUpNum
+ }
+ })
+ },
// 鍗曞嚮灞忓箷锛氭殏鍋滄垨缁х画鎾斁
togglePlay(index) {
+ console.log("鍗曞嚮瑙嗛", index);
+ const videoContext = uni.createVideoContext(`video${index}`, this);
if(this.currentVideoIsPlaying) {
- this.videoContexts[index].pause();
+ videoContext.pause();
} else {
- this.videoContexts[index].play();
+ videoContext.play();
}
},
// 瑙嗛鎾斁浜嬩欢
onPlay(id, index) {
- console.log(id, index, "瑙﹀彂鎾斁");
if(index === this.currentIndex) {
this.currentVideoIsPlaying = true;
+ if(! this.duration) {
+ // 璁剧疆褰撳墠鎾斁瑙嗛鐨勬�绘椂闀�
+ this.duration = this.videoList[this.currentIndex].videoDuration;
+ this.formartDuration = this.sliderFormatTime(this.duration);
+ }
} else {
- this.currentVideoIsPlaying = false;
return
}
+ this.getBarRect()
+ this.progress = 0
+ console.log(id, index, "瑙﹀彂鎾斁");
this.playRecord.videoId = id;
// 娌″垵濮嬪寲鎵嶈祴鍊硷紝鍥犱负涓�涓棰戦噸澶嶆挱鏀緊nPlay浼氶噸澶嶈Е鍙�
if(this.playRecord.startPlayTime === 0) {
@@ -542,34 +977,96 @@
const duration = Date.now() - this.startPauseTime
this.totalPauseTime += duration
}
+ this.videoLoading = false
},
-
+
// 瑙嗛鏆傚仠浜嬩欢
onPause(index) {
console.log(index, "瑙﹀彂鏆傚仠");
if(index === this.currentIndex) {
this.currentVideoIsPlaying = false;
- } else {
- this.currentVideoIsPlaying = true;
- return
+ this.startPauseTime = Date.now()
}
- this.startPauseTime = Date.now()
},
-
// 瑙嗛缁撴潫浜嬩欢
onEnded(index) {
// this.currentVideoIsPlaying = false;
},
-
+
// 璁板綍鎾斁鏃堕暱
onTimeUpdate(e) {
- this.playRecord.playAt = e.detail.currentTime
+ this.videoLoading = false
+ this.playRecord.playAt = e.detail.currentTime;
+
+ this.currentTime = e.detail.currentTime;
+ this.progress = (e.detail.currentTime / this.duration) * 100
},
-
+ // 瑙︽懜寮�濮�
+ handleTouchStart(e) {
+ this.isDragging = true;
+ this.showProcess = true;
+ this.startProgress = this.progress; // 璁板綍寮�濮嬫椂鐨勮繘搴�
+ this.startX = e.touches[0].pageX;
+ console.log("璁板綍寮�濮嬫椂鐨勮繘搴�", this.startProgress);
+ const videoContext = uni.createVideoContext(`video${this.currentIndex}`, this);
+ videoContext.pause()
+ // this.updateProgress(e);
+ },
+
+ // 瑙︽懜绉诲姩
+ handleTouchMove(e) {
+ if (!this.isDragging || !this.barWidth) return;
+ clearTimeout(this.processHidenTimer)
+ this.updateProgress(e);
+ },
+
+ // 瑙︽懜缁撴潫
+ handleTouchEnd() {
+ this.isDragging = false;
+ console.log("婊戝姩缁撴潫", this.duration * this.progress);
+ const videoContext = uni.createVideoContext(`video${this.currentIndex}`, this);
+ videoContext.seek(this.duration * this.progress / 100)
+ videoContext.play()
+ this.processHidenTimer = setTimeout(() => {
+ this.showProcess = false;
+ }, 1000);
+ },
+
+ // 鏇存柊杩涘害
+ updateProgress(e) {
+ // 鑾峰彇褰撳墠瑙︽懜鐐筙鍧愭爣
+ const currentX = e.touches[0].pageX;
+
+ // 璁$畻婊戝姩璺濈(鍍忕礌)
+ const deltaX = currentX - this.startX;
+
+ // 灏嗗儚绱犺窛绂昏浆鎹负杩涘害澧為噺
+ const deltaProgress = (deltaX / this.barWidth) * 100;
+ // 璁$畻鏂拌繘搴� = 寮�濮嬫椂鐨勮繘搴� + 婊戝姩澧為噺
+ let newProgress = this.startProgress + deltaProgress;
+
+ // 闄愬埗鑼冨洿鍦�0-100涔嬮棿
+ newProgress = Math.max(0, Math.min(100, newProgress));
+
+ this.progress = newProgress;
+ },
+ // 瑙嗛缂撳啿
+ videoWaiting(index) {
+ if (index === this.currentIndex) {
+ console.log("瑙嗛缂撳啿涓�傘�傘��");
+ this.videoLoading = true;
+ }
+ },
+ // 鑾峰彇瑙嗛鎬绘椂闀�
+ onLoadedMetadata(e) {
+ // this.duration = e.detail.duration;
+ // this.formartDuration = this.sliderFormatTime(this.duration);
+ // console.log("瑙嗛鎬绘椂闀�", this.duration);
+ },
// 淇濆瓨鎾斁璁板綍
async savePlayRecord() {
console.log(Date.now(), this.playRecord.startPlayTime, this.totalHidenTime);
-
+
const data = {
videoId: this.playRecord.videoId,
viewDuration: Date.now() - this.playRecord.startPlayTime - this.totalHidenTime - this.totalPauseTime,
@@ -598,16 +1095,16 @@
height: 100vh;
background-color: #000;
}
-
+
.video-swiper {
width: 100%;
- height: 100%;
+ height: calc(100% - 50px);
}
-
+
.video-item {
width: 100%;
height: 100%;
- object-fit: cover;
+ /* object-fit: cover; */
}
.play-icon {
position: absolute;
@@ -619,17 +1116,17 @@
z-index: 10;
opacity: 0.6;
}
-
+
.video-info {
- width: 70%;
+ width: 100%;
position: absolute;
- bottom: 70px;
+ bottom: 20px;
left: 20px;
color: #f8f8f8;
z-index: 10;
letter-spacing: 1px;
}
-
+
.action-buttons {
position: absolute;
right: 20px;
@@ -639,7 +1136,7 @@
align-items: center;
z-index: 10;
}
-
+
.action-item {
margin-bottom: 18px;
display: flex;
@@ -669,7 +1166,7 @@
bottom: 0; /* 瀹氫綅鍒板簳閮� */
left: 50%; /* 姘村钩灞呬腑寮�濮嬩綅缃� */
transform: translate(-50%, 50%); /* 姘村钩灞呬腑骞跺悜涓嬬Щ鍔�50% */
-
+
width: 18px; /* 鍥炬爣澶у皬 */
height: 18px;
background-color: #FF5A5F; /* 鍥炬爣鑳屾櫙鑹� */
@@ -697,67 +1194,71 @@
}
.goods-link {
position: relative;
+ width: 450rpx;
margin: 20rpx 0;
padding: 12rpx;
background-color: rgba(255, 255, 255, 0.9);
border-radius: 12rpx;
box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.08);
}
-
+
.goods-container {
+ width: 100%;
display: flex;
align-items: center;
}
-
+
.goods-image {
width: 120rpx;
height: 120rpx;
border-radius: 8rpx;
margin-right: 20rpx;
}
-
+
.goods-info {
flex: 1;
display: flex;
flex-direction: column;
justify-content: center;
}
-
+
.goods-name {
font-size: 28rpx;
color: #333;
font-weight: bold;
- display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
- overflow: hidden;
margin-bottom: 8rpx;
+ width: 280rpx; /* 闇�瑕佹寚瀹氬搴� */
+ overflow: hidden;
+ white-space: nowrap;
+ text-overflow: ellipsis;
}
-
+
.price-section {
display: flex;
align-items: center;
margin-bottom: 6rpx;
}
-
+
.current-price {
font-size: 32rpx;
color: #ff2e4d;
font-weight: bold;
margin-right: 12rpx;
}
-
+
.original-price {
font-size: 28rpx;
color: #999;
text-decoration: line-through;
}
-
+
.sales-count {
font-size: 22rpx;
color: #999;
}
-
+
.buy-button {
background: linear-gradient(to right, #ff5a5f, #ff2e4d);
color: white;
@@ -812,6 +1313,12 @@
height: 70rpx;
border-radius: 50%;
margin-right: 10rpx;
+ }
+ .comment-reply-avatar {
+ width: 40rpx;
+ height: 40rpx;
+ border-radius: 50%;
+ margin-right: 10rpx;
}
.comment-content {
@@ -889,16 +1396,16 @@
align-items: center;
height: 40rpx;
}
-
+
.reply-item {
display: flex;
margin-bottom: 20rpx;
}
-
+
.reply-content {
flex: 1;
}
-
+
.reply-to {
color: #576b95;
margin: 0 10rpx;
@@ -910,7 +1417,7 @@
font-size: 28rpx;
color: #333;
}
-
+
.cancel-reply {
margin-left: 20rpx;
color: #576b95;
@@ -936,8 +1443,9 @@
}
.thumbs-up {
position: absolute;
- right: 80rpx;
+ right: 20rpx;
font-size: 32rpx;
+ width: 120rpx;
}
.textSideIcon {
font-size: 36rpx;
@@ -947,4 +1455,71 @@
margin-right: 10rpx;
color: #cccccc;
}
-</style>
\ No newline at end of file
+ .thumbs-num {
+ margin-left: 4rpx;
+ }
+ .container {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ position: absolute;
+ bottom: 0;
+ width: 100%;
+ }
+
+ .progress-bar {
+ position: relative;
+ width: 100%;
+ height: 16px;
+ background-color: #eee;
+ overflow: hidden;
+ }
+
+ .progress-fill {
+ position: absolute;
+ left: 0;
+ top: 0;
+ height: 100%;
+ background-color: lightgray;
+ transition: width 0.1s;
+ }
+ .process-warp {
+ width: 100%;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ }
+ .progress-text {
+ margin-top: 10px;
+ font-size: 14px;
+ color: #fff;
+ }
+ .swiper-box {
+ width: 100%;
+ height: 1400rpx;
+ }
+ .swiper-item {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ width: 100%;
+ height: 100%;
+ }
+ .custom-share-btn {
+ font-size: unset;
+ background: none;
+ padding: 0;
+ margin: 0;
+ line-height: normal;
+ border: none;
+ }
+ .custom-share-btn::after {
+ border: none;
+ }
+ .topBar {
+ position: fixed;
+ top: 20rpx;
+ left: 20rpx;
+ z-index: 1000
+ }
+</style>
--
Gitblit v1.8.0