From 3d43cd90f46f76316dbde60cb96cfbdc43152e90 Mon Sep 17 00:00:00 2001
From: zxl <763096477@qq.com>
Date: 星期四, 19 六月 2025 20:03:55 +0800
Subject: [PATCH] 注释客户管理,用户权限页面优化
---
pages/tabbar/index/home.vue | 355 +++++++++++++++++++++++++++++++++++++++++++++++++++-------
1 files changed, 309 insertions(+), 46 deletions(-)
diff --git a/pages/tabbar/index/home.vue b/pages/tabbar/index/home.vue
index 66dbb7d..8444073 100644
--- a/pages/tabbar/index/home.vue
+++ b/pages/tabbar/index/home.vue
@@ -9,29 +9,77 @@
@change="onSwiperChange"
>
<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 style="width: 100%;height: 100%;" v-if="item.videoContentType === 'video'">
+ <!-- 鎾斁鎸夐挳锛堜粎褰撹棰戞殏鍋滄椂鏄剧ず锛� -->
+ <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"
+ :enable-progress-gesture="false"
+ class="video-item"
+ @play="onPlay(item.id, index)"
+ @pause="onPause(index)"
+ @ended="onEnded(index)"
+ @click="togglePlay(index)"
+ @timeupdate="onTimeUpdate($event)"
+ @loadedmetadata="onLoadedMetadata($event)"
+
+ ></video>
+ <!-- 鑷畾涔夋帶鍒舵潯 -->
+ <view
+ @touchstart="handleTouchStart"
+ @touchmove="handleTouchMove"
+ @touchend="handleTouchEnd"
+ 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>
- <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 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">
@@ -66,14 +114,14 @@
</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)" :src="item.authorAvatar" mode="aspectFill"></image>
<!-- 鍏虫敞鍥炬爣 - 浣跨敤缁濆瀹氫綅 -->
<view v-if="!item.subscribeThisAuthor" class="follow-icon" @click="subscribeAuth(index, item.authorId)">
<text class="iconfont"></text>
@@ -123,15 +171,17 @@
<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,20 +189,21 @@
<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>
</view>
<view class="view-more-replies" v-if="comment.replyTotalCount > 0 && !comment.expandReply" @click="loadRepliesPage(comment, index)">
- 灞曞紑{{comment.replyTotalCount}}鏉″洖澶� 鈫�
+ <text class="line">鈥斺��</text>灞曞紑{{comment.replyTotalCount}}鏉″洖澶� 鈫�
</view>
<view class="reply-op" v-if="comment.replyTotalCount > replyCommentQuery.pageNumber * replyCommentQuery.pageSize && comment.expandReply">
- <view @click="loadNextPageReply(index)" class="reply-op-item">灞曞紑鏇村<text class="iconfont textSideIcon"></text></view>
+ <view @click="loadNextPageReply(index)" class="reply-op-item"><text class="line">鈥斺��</text>灞曞紑鏇村<text class="iconfont textSideIcon"></text></view>
<view @click="retractReplyComment(index)" class="reply-op-item" style="margin-left: 50rpx;">鏀惰捣<text class="iconfont textSideIcon"></text></view>
</view>
<view class="reply-op" v-else-if="comment.replyTotalCount <= replyCommentQuery.pageNumber * replyCommentQuery.pageSize && comment.expandReply">
- <view @click="retractReplyComment(index)" class="reply-op-item">鏀惰捣<text class="iconfont textSideIcon"></text></view>
+ <view @click="retractReplyComment(index)" class="reply-op-item"><text class="line">鈥斺��</text>鏀惰捣<text class="iconfont textSideIcon"></text></view>
</view>
</view>
</scroll-view>
@@ -175,11 +226,29 @@
</template>
<script>
-import { getRecommendVideos, savePlayRecord, subscribe, getVideoComments, addVideoComment } from "@/api/video.js";
+import { getRecommendVideos, savePlayRecord, subscribe, getVideoComments, addVideoComment, thubmsUpComment, cancelThubmsUpComment } from "@/api/video.js";
import { changeCollect } from "@/api/collect.js";
export default {
+ computed: {
+ hasPlayTime() {
+ return this.sliderFormatTime(this.progress > 0 ? this.duration * this.progress / 100 : 0);
+ }
+ },
data() {
return {
+ currentImgIndex: 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 +269,8 @@
replyId: '',
replyUserId: '',
replyUserNickname: '',
- masterCommentId: ''
+ replyUserAvatar: '',
+ masterCommentId: null
},
comments: [], // 璇勮鍒楄〃
commentsTotal: 0, // 璇勮鎬绘潯鏁�
@@ -224,8 +294,11 @@
], // 瑙嗛鍒楄〃鏁版嵁
videoContexts: [], // 瑙嗛涓婁笅鏂囧璞¢泦鍚�
loading: false, // 鏄惁姝e湪鍔犺浇
- page: 1, // 褰撳墠椤电爜
- pageSize: 10 // 姣忛〉鏁伴噺
+ videoQuery: {
+ pageNumber: 1,
+ pageSize: 6,
+ videoFrom: 'recommend'
+ }
}
},
onShow() {
@@ -247,6 +320,58 @@
this.initVideoContexts();
},
methods: {
+ // 杞挱鍥惧彉鍖�
+ imgChange(e) {
+ this.currentImgIndex = 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) {
+ 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 +407,8 @@
replyId: '',
replyUserId: '',
replyUserNickname: '',
- masterCommentId: ''
+ replyUserAvatar: '',
+ masterCommentId: null
}
},
// 鍙栨秷鍥炲
@@ -298,11 +424,18 @@
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) {
@@ -449,22 +582,31 @@
// 鍔犺浇瑙嗛鏁版嵁
async loadVideos() {
- if (this.loading) return;
+ if (this.loading || this.videoNoMore) return;
this.loading = true;
- getRecommendVideos({pageNumber: this.page, pageSize: this.pageSize}).then(res => {
+ getRecommendVideos(this.videoQuery).then(res => {
console.log(res, "瑙嗛鏁版嵁");
- if (this.page === 1) {
+ if (this.videoQuery.pageNumber === 1) {
this.videoList = res.data.data;
} else {
- this.videoList = [...this.videoList, ...res.data.data];
+ this.videoList = [
+ ...this.videoList,
+ ...res.data.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++;
+
})
},
@@ -518,6 +660,7 @@
},
// 鍗曞嚮灞忓箷锛氭殏鍋滄垨缁х画鎾斁
togglePlay(index) {
+ console.log("鍗曞嚮瑙嗛", index, this.videoContexts);
if(this.currentVideoIsPlaying) {
this.videoContexts[index].pause();
} else {
@@ -526,6 +669,8 @@
},
// 瑙嗛鎾斁浜嬩欢
onPlay(id, index) {
+ this.getBarRect()
+ this.progress = 0
console.log(id, index, "瑙﹀彂鎾斁");
if(index === this.currentIndex) {
this.currentVideoIsPlaying = true;
@@ -555,7 +700,6 @@
}
this.startPauseTime = Date.now()
},
-
// 瑙嗛缁撴潫浜嬩欢
onEnded(index) {
// this.currentVideoIsPlaying = false;
@@ -563,9 +707,66 @@
// 璁板綍鎾斁鏃堕暱
onTimeUpdate(e) {
- this.playRecord.playAt = e.detail.currentTime
+ 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);
+ this.videoContexts[this.currentIndex].pause()
+ // this.updateProgress(e);
},
+ // 瑙︽懜绉诲姩
+ handleTouchMove(e) {
+ if (!this.isDragging || !this.barWidth) return;
+ clearTimeout(this.processHidenTimer)
+ this.videoContexts[this.currentIndex].pause()
+ this.updateProgress(e);
+ },
+
+ // 瑙︽懜缁撴潫
+ handleTouchEnd() {
+ this.isDragging = false;
+ console.log("婊戝姩缁撴潫", this.duration * this.progress);
+ this.videoContexts[this.currentIndex].seek(this.duration * this.progress / 100)
+ this.videoContexts[this.currentIndex].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;
+ console.log("杩涘害澧為噺", deltaProgress);
+ // 璁$畻鏂拌繘搴� = 寮�濮嬫椂鐨勮繘搴� + 婊戝姩澧為噺
+ let newProgress = this.startProgress + deltaProgress;
+
+ // 闄愬埗鑼冨洿鍦�0-100涔嬮棿
+ newProgress = Math.max(0, Math.min(100, newProgress));
+
+ this.progress = newProgress;
+ },
+ // 鑾峰彇瑙嗛鎬绘椂闀�
+ 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);
@@ -601,7 +802,7 @@
.video-swiper {
width: 100%;
- height: 100%;
+ height: calc(100% - 50px);
}
.video-item {
@@ -804,7 +1005,7 @@
.comment-item {
display: flex;
flex-direction: column;
- padding: 10rpx 0;
+ padding: 10rpx 0 20rpx 0;
}
.comment-avatar {
@@ -812,6 +1013,12 @@
height: 70rpx;
border-radius: 50%;
margin-right: 10rpx;
+ }
+ .comment-reply-avatar {
+ width: 40rpx;
+ height: 40rpx;
+ border-radius: 50%;
+ margin-right: 10rpx;
}
.comment-content {
@@ -887,6 +1094,7 @@
.reply-op-item {
display: flex;
align-items: center;
+ height: 40rpx;
}
.reply-item {
@@ -935,11 +1143,66 @@
}
.thumbs-up {
position: absolute;
- right: 80rpx;
+ right: 20rpx;
font-size: 32rpx;
+ width: 120rpx;
}
.textSideIcon {
font-size: 36rpx;
margin-left: 5rpx;
}
+ .line {
+ margin-right: 10rpx;
+ color: #cccccc;
+ }
+ .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: #666;
+ }
+ .swiper-box {
+ width: 100%;
+ height: 1400rpx;
+ }
+ .swiper-item {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ width: 100%;
+ height: 100%;
+ }
</style>
\ No newline at end of file
--
Gitblit v1.8.0