绿满眶商城微信小程序-uniapp
peng
2025-07-02 be80b22a4a0fcd33e1b17ebdb86eba91cc7de4d2
Merge remote-tracking branch 'origin/dev' into dev
4个文件已修改
1个文件已添加
356 ■■■■■ 已修改文件
api/video.js 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pages.json 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pages/tabbar/index/home.vue 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pages/video/video-play.vue 20 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pages/video/video-search.vue 307 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
api/video.js
@@ -250,3 +250,17 @@
    needToken: true
  });
}
/**
 * 视频搜索
 *
 * @param params
 */
 export function videoSearch(params) {
  return http.request({
    url: "/lmk/video/es/search",
    method: Method.GET,
    params: params,
    needToken: true
  });
}
pages.json
@@ -1038,6 +1038,13 @@
                            "u-button": "view"
                        }
                    }
                },
                {
                    "path" : "video-search",
                    "style" :
                    {
                        "navigationBarTitleText" : ""
                    }
                }
            ]
        },
pages/tabbar/index/home.vue
@@ -121,7 +121,7 @@
        <!-- 视频信息层 -->
        <view class="video-info" :style="{bottom: marginBottom + 20 + 'px'}">
          <view>
              <text class="video-author">@{{item.authorName}}</text>
              <text class="video-author" @click="jumpToSearch">@{{item.authorName}}</text>
          </view>
          <view style="width: 100%;word-wrap: break-word;white-space: normal;overflow-wrap: break-word;">
              <text class="video-title">{{item.title}}</text>
@@ -399,6 +399,12 @@
      }
  },
  methods: {
      jumpToSearch() {
          uni.navigateTo({
              url: '/pages/video/video-search'
          });
      },
      // 切换顶部导航栏
      topBarChange(titleObj) {
        if (titleObj.index === 'home') {
            uni.switchTab({
pages/video/video-play.vue
@@ -331,7 +331,8 @@
            pageNumber: 1,
            pageSize: 10,
            authorId: '',
            videoFrom: ''
            videoFrom: '',
            keyword: ''
        },
        marginBottom: 0, // 底部安全区域
        windowHeight: 0 // 可使用屏幕高度
@@ -350,6 +351,7 @@
  },
  onUnload() {
      uni.removeStorageSync("playInfo");
      uni.removeStorageSync("searchPlayInfo");
  },
  onReady() {
      
@@ -357,11 +359,11 @@
  onLoad(option) {
      this.marginBottom = uni.getSystemInfoSync().safeAreaInsets.bottom
      this.windowHeight = uni.getSystemInfoSync().windowHeight
      const playInfo = uni.getStorageSync("playInfo", playInfo);
      const playInfo = uni.getStorageSync("playInfo");
      const searchPlayInfo = uni.getStorageSync("searchPlayInfo");
      if(playInfo) {
          this.currentIndex = playInfo.playIndex;
          this.videoList = playInfo.videoList;
          console.log("拿到数据了",playInfo);
          this.videoQuery.pageNumber = playInfo.pageNumber;
          this.videoNoMore = playInfo.nomore;
          this.videoQuery.authorId = option.authorId;
@@ -371,6 +373,18 @@
              const videoContext = uni.createVideoContext(`video${this.currentIndex}`, this);
              videoContext.play()
          })
      } else if (searchPlayInfo) { // 搜索页跳转过来的数据略有不同
          this.currentIndex = searchPlayInfo.playIndex;
          this.videoList = searchPlayInfo.videoList;
          this.videoQuery.pageNumber = searchPlayInfo.pageNumber;
          this.videoNoMore = searchPlayInfo.nomore;
          this.videoQuery.keyword = searchPlayInfo.keyword;
          this.videoQuery.videoFrom = option.videoFrom;
          this.currentVideoIsPlaying = true;
          this.$nextTick(() => {
                        const videoContext = uni.createVideoContext(`video${this.currentIndex}`, this);
                        videoContext.play()
          })
      } else {
          this.videoQuery.videoFrom = 'recommend';
          this.loadVideos();
pages/video/video-search.vue
New file
@@ -0,0 +1,307 @@
<template>
  <view class="search-page">
    <!-- 搜索区域 -->
    <view class="search-bar">
      <input
        class="search-input"
        v-model="searchQuery.keyword"
        placeholder="请输入搜索关键词"
        placeholder-class="placeholder-style"
        @confirm="handleSearch"
      />
      <button class="search-btn" @click="handleSearch">搜索</button>
    </view>
    <!-- 瀑布流视频列表 -->
    <view class="waterfall-container">
      <view class="waterfall-left">
        <view
          class="video-item"
          v-for="(item, index) in leftList"
          :key="item.id"
          @click="playVideo(item)"
        >
          <image class="video-cover" :src="item.coverUrl" mode="widthFix"></image>
          <view class="video-info">
            <text class="video-title">{{item.title}}</text>
            <view class="video-tags">
              <text class="tag" v-for="(tag, i) in item.tagList" :key="i">{{tag.tagName}}</text>
            </view>
            <view class="video-stats">
              <text class="like-count">❤️ {{item.thumbsUpNum}}</text>
            </view>
          </view>
        </view>
      </view>
      <view class="waterfall-right">
        <view
          class="video-item"
          v-for="(item, index) in rightList"
          :key="item.id"
          @click="playVideo(item)"
        >
          <image class="video-cover" :src="item.coverUrl" mode="widthFix"></image>
          <view class="video-info">
            <text class="video-title">{{item.title}}</text>
            <view class="video-tags">
              <text class="tag" v-for="(tag, i) in item.tagList" :key="i">{{tag.tagName}}</text>
            </view>
            <view class="video-stats">
              <text class="like-count">❤️ {{item.thumbsUpNum}}</text>
            </view>
          </view>
        </view>
      </view>
    </view>
    <!-- 加载更多提示 -->
    <view class="load-more" v-if="loading">
      <text>加载中...</text>
    </view>
    <view class="load-more" v-if="noMore">
      <text>没有更多了</text>
    </view>
  </view>
</template>
<script>
    import {videoSearch} from "@/api/video.js"
export default {
  data() {
    return {
      videoList: [], // 所有视频数据
      leftList: [],  // 左侧列视频
      rightList: [], // 右侧列视频
      searchQuery: {
          keyword: '',
          pageNumber: 1, // 虽然es是从第0页开始的,但是为了在跳转到video-play页之后和其它类型的查询保持一致,这里从1开始,后端默认-1
          pageSize: 10
      },
      loading: false,
      noMore: false
    }
  },
  onLoad() {
    // 初始加载一些数据
    this.loadVideos();
  },
  methods: {
    handleSearch() {
      if (!this.searchQuery.keyword.trim()) {
        uni.showToast({
          title: '请输入搜索关键词',
          icon: 'none'
        });
        return;
      }
      // 重置数据
      this.searchQuery.pageNumber = 1;
      this.videoList = [];
      this.leftList = [];
      this.rightList = [];
      this.noMore = false;
      // 执行搜索
      this.loadVideos();
    },
    async loadVideos() {
      if (this.loading || this.noMore) return;
      this.loading = true;
      uni.showLoading({ title: '加载中' });
      try {
        videoSearch(this.searchQuery).then(res => {
            if (this.searchQuery.pageNumber === 1) {
                this.videoList = res.data.data
            } else {
                this.videoList = [
                  ...this.videoList,
                  ...res.data.data.filter(
                    (newItem) => !this.videoList.some((oldItem) => oldItem.id === newItem.id)
                  ),
                ];
            }
            // 分配瀑布流
            this.distributeWaterfall();
            if (res.data.data.length < this.searchQuery.pageSize) {
                this.noMore = true
            } else {
                this.searchQuery.pageNumber += 1
            }
        })
      } catch (error) {
        console.error('加载视频失败:', error);
        uni.showToast({
          title: '加载失败',
          icon: 'none'
        });
      } finally {
        this.loading = false;
        uni.hideLoading();
      }
    },
    // 分配瀑布流左右列
    distributeWaterfall() {
      let leftHeight = this.getColumnHeight(this.leftList);
      let rightHeight = this.getColumnHeight(this.rightList);
      // 从当前videoList中未分配的开始
      const startIndex = this.leftList.length + this.rightList.length;
      for (let i = startIndex; i < this.videoList.length; i++) {
        const item = this.videoList[i];
        item["index"] = i;
        // 计算这个item的大概高度(根据封面比例)
        const itemHeight = 200 + Math.random() * 50; // 简单模拟高度差异
        if (leftHeight <= rightHeight) {
          this.leftList.push(item);
          leftHeight += itemHeight;
        } else {
          this.rightList.push(item);
          rightHeight += itemHeight;
        }
      }
    },
    // 获取列的高度(简单实现)
    getColumnHeight(list) {
      return list.length * 250; // 假设每个item大约250高度
    },
    // 播放视频
    playVideo(item) {
      const searchPlayInfo = {
          videoList: this.videoList,
          nomore: this.noMore,
          pageNumber: this.searchQuery.pageNumber,
          playIndex: item.index,
          keyword: this.searchQuery.keyword
      }
      uni.setStorageSync("searchPlayInfo", searchPlayInfo)
      uni.navigateTo({
        url: `/pages/video/video-play?videoFrom=search`
      });
    },
  },
  // 上拉加载更多
  onReachBottom() {
    this.loadVideos();
  }
}
</script>
<style scoped>
.search-page {
  padding: 20rpx;
}
.search-bar {
  display: flex;
  align-items: center;
  margin-bottom: 20rpx;
}
.search-input {
  flex: 1;
  height: 70rpx;
  padding: 0 20rpx;
  background-color: #efefef;
  border-radius: 35rpx;
  font-size: 28rpx;
}
.placeholder-style {
  color: #999;
}
.search-btn {
  width: 120rpx;
  height: 70rpx;
  margin-left: 20rpx;
  line-height: 70rpx;
  font-size: 28rpx;
  background-color: #07c160;
  color: white;
  border-radius: 35rpx;
}
.waterfall-container {
  display: flex;
  justify-content: space-between;
}
.waterfall-left,
.waterfall-right {
  width: 48%;
}
.video-item {
  margin-bottom: 20rpx;
  background: #fff;
  border-radius: 12rpx;
  overflow: hidden;
  box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.05);
}
.video-cover {
  width: 100%;
  display: block;
}
.video-info {
  padding: 16rpx;
}
.video-title {
  font-size: 28rpx;
  color: #333;
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
  overflow: hidden;
  line-height: 1.4;
}
.video-tags {
  margin-top: 10rpx;
  display: flex;
  flex-wrap: wrap;
  gap: 20rpx;
}
.tag {
  font-size: 24rpx;
  color: #07c160;
  background: #e8f5e9;
  border-radius: 20rpx;
  flex: 1 1 calc(50% - 20rpx); /* 考虑gap的宽度 */
  min-width: calc(50% - 20rpx);
  max-width: 100%;
}
.video-stats {
  margin-top: 10rpx;
  display: flex;
  align-items: center;
}
.like-count {
  font-size: 24rpx;
  color: #666;
}
.load-more {
  text-align: center;
  padding: 20rpx;
  font-size: 24rpx;
  color: #999;
}
</style>