绿满眶商城微信小程序-uniapp
peng
4 天以前 17138ae89dc5a445cb5a910afa234f06c35cf07a
insert 完成商品广场
3个文件已修改
7个文件已添加
1957 ■■■■■ 已修改文件
config/api.js 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pages.json 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pages/commodity-square/commoditySquare.vue 303 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pages/health/healthVideo.vue 1246 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pages/product/goods.vue 31 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
uni_modules/uni-drawer/changelog.md 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
uni_modules/uni-drawer/components/uni-drawer/keypress.js 45 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
uni_modules/uni-drawer/components/uni-drawer/uni-drawer.vue 183 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
uni_modules/uni-drawer/package.json 87 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
uni_modules/uni-drawer/readme.md 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
config/api.js
@@ -4,12 +4,10 @@
 */
// 开发环境
const dev = {
  im: "http://192.168.0.4:8885",
  common: "http://192.168.0.4:8890",
  buyer: "http://192.168.0.4:8888",
  // im: "http://127.0.0.1:8885",
  // common: "http://127.0.0.1:8890",
  // buyer: "http://127.0.0.1:8888",
  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",
pages.json
@@ -138,16 +138,6 @@
                }
            }
        },
        // {
        //     "path": "components/popups/popups",
        //     "style": {
        //         "navigationBarTitleText": "",
        //         "navigationStyle": "custom",
        //         "componentPlaceholder": {
        //             "u-icon": "view"
        //         }
        //     }
        // },
        {
            "path": "components/m-buy/goods",
            "style": {
@@ -1424,6 +1414,25 @@
                    }
                }
            ]
        },
        {
            "root": "pages/health",
            "pages": [{
                "path": "healthVideo",
                "style": {
                    "navigationBarTitleText": "大健康"
                }
            }]
        },
        {
            "root": "pages/commodity-square",
            "pages": [{
                "path" : "commoditySquare",
                "style" :
                {
                    "navigationBarTitleText" : "商品广场"
                }
            }]
        }
    ],
    "globalStyle": {
pages/commodity-square/commoditySquare.vue
New file
@@ -0,0 +1,303 @@
<template>
    <view class="container">
        <input type="text" v-show="false" v-model="flushDom"/>
        <view class="commoditySquare">
            <view class="left" style="width: 310rpx;">
                <view class="commoditySquareItem" v-for="(item,index) in goodsList1" @click="goToGoodsInfo('goodsList1',item.id)">
                    <video :src="item.goodsVideo" v-if="item.goodsVideo " v-show="item.show" :initial-time="0"
                        :controls="false" object-fit="contain" :show-play-btn="false" :show-center-play-btn="false"
                        @loadedmetadata="getvideoInfo($event,'goodsList1',index)" :ref="'video'+item.id"
                        :style="{width:item.width,height:item.height}"></video>
                    <image :src="item.thumbnail" v-if="item.goodsVideo ==null  || item.goodsVideo == ''"
                        mode="aspectFill" class="goodsImg">
                    </image>
                    <view class="goodsInfo">
                        <view class="">{{item.goodsName}}</view>
                        <view class="priceInfo">
                            <view class="">¥{{item.price}}</view>
                            <view class="">已售: {{item.buyCount}}</view>
                        </view>
                    </view>
                </view>
            </view>
            <view class="right" style="width: 310rpx;">
                <view class="commoditySquareItem" v-for="(item,index) in goodsList2" @click="goToGoodsInfo('goodsList2',item.id)">
                    <video :src="item.goodsVideo" v-if="item.goodsVideo " v-show="item.show" :initial-time="0"
                        :controls="false" object-fit="contain" :show-play-btn="false" :show-center-play-btn="false"
                        @loadedmetadata="getvideoInfo($event,'goodsList2',index)" :ref="'video'+item.id"
                        :style="{width:item.width,height:item.height}"></video>
                    <image :src="item.thumbnail" v-if="item.goodsVideo ==null || item.goodsVideo == ''"
                        mode="aspectFill" class="goodsImg">
                    </image>
                    <view class="goodsInfo">
                        <view class="">{{item.goodsName}}</view>
                        <view class="priceInfo">
                            <view class="">¥{{item.price}}</view>
                            <view class="">已售: {{item.buyCount}}</view>
                        </view>
                    </view>
                </view>
            </view>
            <view class="openShowLeft" @click="showDrawer('showLeft')" v-if="!showLeft">
                <uni-icons type="right" size="30"></uni-icons>
            </view>
            <uni-drawer ref="showLeft" mode="left" width="120" @change="change($event,'showLeft')"
                class="navigationLeft">
                <view class="typeNavigation">
                    <view class="typeNavigationItem" :class="{typeNavigationItemCheck:currentCategort ==item.id}"
                        @click="chooseCategory(item.id)" v-for="item in categoryList" :key="item.id">
                        {{item.name}}
                    </view>
                </view>
            </uni-drawer>
        </view>
         <view style="display: flex;align-items: center;justify-content: center;margin-top: 20rpx;" v-if="canLoadMore">没有更多数据了.................</view>
    </view>
</template>
<script>
    import {
        getCategoryList,
        getGoodsList
    } from "@/api/goods.js";
    import {
        getSTSToken
    } from '@/api/common.js'
    export default {
        data() {
            return {
                //是否显示打开左侧弹窗
                showLeft: false,
                //商品导航分类
                categoryList: [],
                //当前选中的分类
                currentCategort: '',
                //显示没有数据
                canLoadMore:false,
                //最大页数
                maxPages: 0,
                //刷新dom使用
                flushDom:'',
                //查询商品需要的参数
                getGoodsParam: {
                    keyword: '',
                    pageSize: 12,
                    pageNumber: 1,
                    categoryId: null,
                },
                //商品双列显示
                goodsList1: [],
                goodsList2: [],
            }
        },
        methods: {
            confirm() {},
            // 打开窗口
            showDrawer(e) {
                this.$refs[e].open()
            },
            // 关闭窗口
            closeDrawer(e) {
                this.$refs[e].close()
            },
            // 抽屉状态发生变化触发
            change(e, type) {
                this[type] = e
            },
            getvideoInfo(e, arrName, index) {
                const wight = e.detail.width;
                const height = e.detail.height;
                this[arrName][index].width = 310 + 'rpx';
                //计算宽高比
                const videoHeight = 310 / (wight / height)
                this[arrName][index].height = Math.floor(videoHeight) + 'rpx';
                this[arrName][index].show = true;
                console.log(this[arrName][index].width, this[arrName][index].height)
                this.flushDom = new Date();
            },
            goToGoodsInfo(arrName,id){
            const item =this[arrName].find(item=> id === item.id);
            uni.navigateTo({
                url: `/pages/product/goods?id=${item.id}&goodsId=${item.goodsId}`
            });
            },
            //获取分类导航栏
            async loadCategoryList() {
                let list = await getCategoryList(0);
                this.categoryList = list.data.result
            },
            async chooseCategory(id) {
                if (this.currentCategort === id) return
                this.canLoadMore = false;
                this.currentCategort = id
                this.getGoodsParam.categoryId = id
                this.getGoodsParam.keyword = ''
                this.getGoodsParam.pageNumber = 1
                const goodsList = await getGoodsList(this.getGoodsParam);
                const sts = await getSTSToken();
                const stsUrl = sts.data.data.endpoint
                // 处理数据
                goodsList.data.result.records.forEach(item => {
                    if (item.thumbnail !== '' && item.thumbnail !== null && item.thumbnail.indexOf('http') ===
                        -1) {
                        item.thumbnail = stsUrl + '/' + item.thumbnail
                    }
                    if (item.goodsVideo !== '' && item.goodsVideo !== null && item.goodsVideo.indexOf(
                            'http') === -1) {
                        item.goodsVideo = stsUrl + '/' + item.goodsVideo
                    }
                })
                //平分给两个数组
                    const middle = Math.ceil(goodsList.data.result.records.length / 2);
                    this.goodsList1 = goodsList.data.result.records.slice(0, middle);
                    this.goodsList2 = goodsList.data.result.records.slice(middle);
                    this.maxPages = goodsList.data.result.pages
                    console.log(this.maxPages)
            }
        },
        onShow() {
            this.showDrawer('showLeft')
        },
        onLoad() {
            this.loadCategoryList();
        },
        async onReachBottom(){
            if(this.getGoodsParam.pageNumber<this.maxPages){
                this.getGoodsParam.pageNumber++;
                const goodsList = await getGoodsList(this.getGoodsParam);
                const sts = await getSTSToken();
                const stsUrl = sts.data.data.endpoint
                // 处理数据
                goodsList.data.result.records.forEach(item => {
                    if (item.thumbnail !== '' && item.thumbnail !== null && item.thumbnail.indexOf('http') ===
                        -1) {
                        item.thumbnail = stsUrl + '/' + item.thumbnail
                    }
                    if (item.goodsVideo !== '' && item.goodsVideo !== null && item.goodsVideo.indexOf(
                            'http') === -1) {
                        item.goodsVideo = stsUrl + '/' + item.goodsVideo
                    }
                })
                //平分给两个数组
                    const middle = Math.ceil(goodsList.data.result.records.length / 2);
                    this.goodsList1 = [...this.goodsList1,...goodsList.data.result.records.slice(0, middle)];
                    this.goodsList2 = [...this.goodsList2,...goodsList.data.result.records.slice(middle)];
                    this.maxPages = goodsList.data.result.pages
            }else{
                this.canLoadMore = true;
            }
        }
    }
</script>
<style lang="scss" scoped>
    .container {
        width: 750rpx;
        padding-bottom: 64rpx;
    }
    .commoditySquare {
        width: 750rpx;
        position: relative;
        box-sizing: border-box;
        display: flex;
        justify-content: space-around;
    }
    .left {
        box-sizing: border-box;
    }
    .right {
        box-sizing: border-box;
    }
    .goodsImg {
        width: 300rpx;
        height: 300rpx;
        padding-bottom: 10rpx;
    }
    .commoditySquareItem {
        border: 1rpx solid darkgray;
        border-radius: 12rpx;
        box-sizing: border-box;
        padding: 0 32rpx;
        display: flex;
        flex-direction: column;
        align-items: center;
        justify-content: center;
    }
    .commoditySquareItem:nth-child(n+1) {
        margin-top: 32rpx;
    }
    .openShowLeft {
        position: fixed;
        top: 30rpx;
        left: 0;
    }
    .navigationLeft {
        position: relative;
        background-color: #fcfcfc;
        // opacity: 0.5;
        box-sizing: border-box;
    }
    .scroll-view {
        /* #ifndef APP-NVUE */
        width: 100%;
        height: 100%;
        /* #endif */
        flex: 1
    }
    .priceInfo {
        margin-top: 10rpx;
        display: flex;
        justify-content: space-around;
    }
    // 处理抽屉内容滚动
    .scroll-view-box {
        flex: 1;
        position: absolute;
        top: 0;
        right: 0;
        bottom: 0;
        left: 0;
    }
    .typeNavigation {
        padding: 10rpx;
        position: relative;
    }
    .typeNavigationItem {
        padding: 24rpx;
        font-size: 28rpx;
        color: black;
        margin-top: 10rpx;
        border-radius: 12rpx;
        border: 1rpx solid gray;
    }
    .typeNavigationItemCheck {
        background-color: #42b993;
        color: #fff;
        border: 0;
    }
    .closeShowLeft {
        position: absolute;
        top: 20rpx;
        right: 0;
    }
</style>
pages/health/healthVideo.vue
New file
@@ -0,0 +1,1246 @@
<template>
    <view class="video-container">
        <!-- 视频列表 -->
        <swiper class="video-swiper" vertical circular :current="currentIndex" @change="onSwiperChange">
            <swiper-item v-for="(item, index) in videoList" :key="item.id">
                <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[index]}}</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="false">
                    <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>
                        </view>
                    </view>
                </view>
                <!-- 视频信息层 -->
                <view class="video-info">
                    <view>
                        <text class="video-author">@{{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>
                        <text class="video-tag" v-for="(tag, index) in item.tagList"
                            :key="tag.id">#{{tag.tagName}}</text>
                    </view>
                </view>
                <!-- 右侧互动按钮 -->
                <view class="action-buttons" v-if="false">
                    <view class="avatar-container">
                        <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">&#xe629;</text>
                        </view>
                    </view>
                    <view class="action-item" @click="toggleCollect(item, index)">
                        <text class="iconfont" v-if="item.collected">&#xe605;</text>
                        <text class="iconfont" v-else>&#xe601;</text>
                        <text style="font-size: 10px;font-weight: lighter;">{{item.collectNum}}</text>
                    </view>
                    <view class="action-item" @click="showComments(item)">
                        <text class="iconfont">&#xe7f7;</text>
                        <text style="font-size: 10px;font-weight: lighter;">{{item.commentNum}}</text>
                    </view>
                </view>
            </swiper-item>
        </swiper>
        <!-- 评论弹窗 -->
        <uni-popup ref="commentPopup" type="bottom" :is-mask-click="true" @maskClick="closeCommentPopup">
            <view class="comment-popup">
                <view class="popup-header">
                    <text class="popup-title" v-if="!commentForm.replyId">评论({{commentsTotal}})</text>
                    <view class="reply-title" v-else>
                        <text>回复 @{{commentForm.replyUserNickname}}</text>
                        <text class="cancel-reply" @click="cancelReply">取消</text>
                    </view>
                    <text class="iconfont close-icon" @click="closeCommentPopup">&#xe675;</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;">
                                    <text class="time">{{formatTime(comment.createTime)}}</text>
                                    <text @click="openReply(comment)" class="reply-btu time">回复</text>
                                    <text v-if="!comment.hasThumbsUp" class="thumbs-up time iconfont"
                                        @click="thubmsUp(comment.id, index, null)">&#xe614;<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)">&#xe607;<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, 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">&#xe666;</text>{{reply.replyUserNickname}}</text>
                                    </view>
                                    <text class="content">{{reply.commentContent}}</text>
                                    <view class="reply-footer">
                                        <text class="time">{{formatTime(reply.createTime)}}</text>
                                        <text @click="openReply(comment, reply)" class="reply-btu time">回复</text>
                                        <text v-if="!reply.hasThumbsUp" class="thumbs-up time iconfont"
                                            @click="thubmsUp(reply.id, index, replyIndex)">&#xe614;<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)">&#xe607;<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)">
                            <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="line">——</text>展开更多<text class="iconfont textSideIcon">&#xeb8d;</text></view>
                            <view @click="retractReplyComment(index)" class="reply-op-item" style="margin-left: 50rpx;">
                                收起<text class="iconfont textSideIcon">&#xeb9b;</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="line">——</text>收起<text class="iconfont textSideIcon">&#xeb9b;</text></view>
                        </view>
                    </view>
                </scroll-view>
                <view class="comment-input-area">
                    <input ref="commentInput" 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>
    </view>
</template>
<script>
    import {
        getHealthRecommendVideos,
        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, // 是否正在拖动
                processHidenTimer: null, // 进度条隐藏定时器
                showProcess: false, // 是否显示进度条
                videoNoMore: false, // 是否还有更多视频
                commentNoMore: false, // 是否还有更多评论
                commentQuery: {
                    pageNumber: 1,
                    pageSize: 5,
                    videoId: '',
                    masterCommentId: ''
                },
                replyCommentQuery: {
                    pageNumber: 1,
                    pageSize: 5,
                    videoId: '',
                    masterCommentId: ''
                },
                commentForm: { // 评论表单数据
                    id: '',
                    videoId: '',
                    commentContent: '',
                    replyId: '',
                    replyUserId: '',
                    replyUserNickname: '',
                    replyUserAvatar: '',
                    masterCommentId: null
                },
                comments: [], // 评论列表
                commentsTotal: 0, // 评论总条数
                commentLoading: false, // 评论加载状态
                startHidenTime: 0, // 记录切换至其它页面的时间,用于计算视频观看时间减去的部分
                totalHidenTime: 0, // 总共隐藏页面的时间
                startPauseTime: 0, // 开始暂停的时间
                totalPauseTime: 0, // 总共暂停的时间
                playRecord: {
                    videoId: null,
                    viewDuration: 0, // 这个视频总共观看了多久
                    playAt: 0, // 这个视频播放到哪了
                    startPlayTime: 0 // 这个视频从什么时候开始播放的
                },
                currentVideoIsPlaying: true, // 当前视频是否正在播放
                isFullScreen: false,
                windowHeight: 0,
                currentIndex: 0, // 当前播放的视频索引
                videoList: [
                ], // 视频列表数据
                videoContexts: [], // 视频上下文对象集合
                loading: false, // 是否正在加载
                videoQuery: {
                    pageNumber: 1,
                    pageSize: 6,
                    videoFrom: 'recommend',
                    videoType: 'health'
                }
            }
        },
        onShow() {
            this.loadVideos()
            // 如果视频按下暂停后切换页面再回到页面时,只算暂停时间(因为暂停时间和离开页面时间是重复的,只算一个)
            if (this.startHidenTime !== 0 && this.currentVideoIsPlaying) {
                const duration = Date.now() - this.startHidenTime
                this.totalHidenTime += duration
            }
        },
        onHide() {
            this.startHidenTime = Date.now()
        },
        onLoad() {
            this.loadVideos();
        },
        onReady() {
            // 初始化视频上下文
            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++;
                getVideoComments(this.replyCommentQuery).then(res => {
                    this.comments[index].replies = [
                        ...this.comments[index].replies,
                        ...res.data.data.filter(
                            (newItem) => !this.comments[index].replies.some((oldItem) => oldItem.id === newItem
                                .id)
                        ),
                    ];
                })
            },
            // 收起回复
            retractReplyComment(index) {
                this.comments[index].expandReply = false;
                this.comments[index].replies = [];
            },
            // 加载回复
            loadRepliesPage(comment, index) {
                this.replyCommentQuery.pageNumber = 1;
                this.replyCommentQuery.masterCommentId = comment.id
                getVideoComments(this.replyCommentQuery).then(res => {
                    this.comments[index].replies = res.data.data;
                    this.comments[index].expandReply = true;
                })
            },
            resetCommentForm() {
                const videoId = this.commentForm.videoId;
                this.commentForm = { // 评论表单数据
                    id: '',
                    videoId: videoId,
                    commentContent: '',
                    replyId: '',
                    replyUserId: '',
                    replyUserNickname: '',
                    replyUserAvatar: '',
                    masterCommentId: null
                }
            },
            // 取消回复
            cancelReply() {
                this.resetCommentForm()
            },
            // 打开回复框
            openReply(comment, reply = null) {
                if (reply) {
                    comment = reply
                }
                this.commentForm.masterCommentId = comment.masterCommentId ? comment.masterCommentId : comment.id;
                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()}日`;
            },
            // 提交评论
            async submitComment() {
                if (!this.commentForm.commentContent.trim()) {
                    uni.showToast({
                        title: '评论内容不能为空',
                        icon: 'none'
                    });
                    return;
                }
                // 发表评论
                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()) {
                                if (item.id === res.data.data.replyId) {
                                    item.replies.unshift(res.data.data);
                                    // this.loadRepliesPage(item, index)
                                    break; // 跳出循环
                                }
                            }
                        } else {
                            this.comments.unshift(res.data.data);
                        }
                        console.log("新增后", this.comments);
                        uni.showToast({
                            title: '评论成功'
                        });
                        // 当前视频评论数加一
                        this.commentsTotal += 1;
                        this.videoList[this.currentIndex].commentNum += 1;
                    } else {
                        uni.showToast({
                            title: res.data.msg,
                            icon: 'none'
                        });
                    }
                }).catch(() => {
                    uni.showToast({
                        title: '评论失败',
                        icon: 'none'
                    });
                })
            },
            // 关闭评论弹窗
            closeCommentPopup() {
                this.$refs.commentPopup.close()
                this.showCommentPopup = false;
                this.comments = [];
                this.resetCommentForm()
                this.commentQuery.pageNumber = 1;
                this.commentNoMore = false;
            },
            // 下滑评论区加载评论
            async getCommentPage() {
                if (this.commentNoMore) {
                    return;
                }
                getVideoComments(this.commentQuery).then(res => {
                    if (this.commentQuery.pageNumber === 1) {
                        this.comments = res.data.data
                    } else {
                        this.comments = [
                            ...this.comments,
                            ...res.data.data.filter(
                                (newItem) => !this.comments.some((oldItem) => oldItem.id === newItem.id)
                            ),
                        ];
                    }
                    if (res.data.data.length < this.commentQuery.pageSize) {
                        this.commentNoMore = true;
                        return;
                    }
                    this.commentQuery.pageNumber++;
                })
            },
            // 显示评论弹窗
            async showComments(item) {
                this.commentForm.videoId = item.id;
                this.$refs.commentPopup.open();
                this.commentLoading = true;
                this.commentQuery.videoId = item.id
                this.replyCommentQuery.videoId = item.id
                // 首次加载评论分页大小增加一倍,以产生滚动条,后续可触发
                this.commentQuery.pageSize *= 2;
                getVideoComments(this.commentQuery).then(res => {
                    this.commentsTotal = res.data.total;
                    this.comments = res.data.data;
                    this.commentQuery.pageNumber += 2;
                    this.commentQuery.pageSize /= 2;
                }).catch(() => {
                    uni.showToast({
                        title: '获取评论失败',
                        icon: 'none'
                    });
                }).finally(() => {
                    this.commentLoading = false;
                })
            },
            // 关注作者
            subscribeAuth(index, authorId) {
                this.videoList.forEach(video => {
                    if (video.authorId === authorId) {
                        video.subscribeThisAuthor = true
                    }
                })
                subscribe(authorId).then(res => {
                    if (res.data.code === 200) {
                        uni.showToast({
                            title: '关注成功~',
                            icon: 'none'
                        });
                    } else {
                        this.videoList.forEach(video => {
                            if (video.authorId === authorId) {
                                video.subscribeThisAuthor = false
                            }
                        })
                    }
                })
            },
            // 初始化视频上下文
            initVideoContexts() {
                this.videoContexts = this.videoList.map((_, index) => {
                    let videoContent = uni.createVideoContext(`video${index}`, this);
                    return videoContent;
                });
            },
            // 加载视频数据
            async loadVideos() {
                if (this.loading || this.videoNoMore) return;
                this.loading = true;
                getHealthRecommendVideos(this.videoQuery).then(res => {
                    console.log(res, "视频数据");
                    if (this.videoQuery.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.$nextTick(() => {
                        this.initVideoContexts();
                    });
                    this.loading = false;
                    if (res.data.data.length < this.videoQuery.pageSize) {
                        this.videoNoMore = true;
                        return;
                    }
                    this.videoQuery.pageNumber++;
                })
            },
            // 滑动切换视频
            onSwiperChange(e) {
                // 如果视频处于暂停状态往下刷视频,那么需要再计算一次暂停时间
                if (!this.currentVideoIsPlaying) {
                    if (this.startPauseTime !== 0) {
                        const duration = Date.now() - this.startPauseTime
                        this.totalPauseTime += duration
                    }
                }
                // 保存上一个视频的播放记录
                this.savePlayRecord()
                const oldIndex = this.currentIndex;
                this.currentIndex = e.detail.current;
                // 暂停上一个视频
                if (this.videoContexts[oldIndex]) {
                    this.videoContexts[oldIndex].pause();
                }
                this.startPauseTime = 0;
                // 播放当前视频
                if (this.videoContexts[this.currentIndex]) {
                    this.videoContexts[this.currentIndex].play();
                }
            },
            // 收藏/取消收藏
            toggleCollect(item, index) {
                let data = {
                    refId: item.id,
                    collectType: 'video'
                }
                const beforeCollected = item.collected
                const beforeCollectNum = item.collectNum
                if (item.collected) {
                    this.videoList[index].collected = false
                    this.videoList[index].collectNum -= 1
                } else {
                    this.videoList[index].collected = true
                    this.videoList[index].collectNum += 1
                }
                changeCollect(data).then(res => {
                    if (res.data.code !== 200) {
                        this.videoList[index].collected = beforeCollected
                        this.videoList[index].collectNum = beforeCollectNum
                    }
                })
            },
            // 单击屏幕:暂停或继续播放
            togglePlay(index) {
                console.log("单击视频", index, this.videoContexts);
                if (this.currentVideoIsPlaying) {
                    this.videoContexts[index].pause();
                } else {
                    this.videoContexts[index].play();
                }
            },
            // 视频播放事件
            onPlay(id, index) {
                this.getBarRect()
                this.progress = 0
                console.log(id, index, "触发播放");
                if (index === this.currentIndex) {
                    this.currentVideoIsPlaying = true;
                } else {
                    this.currentVideoIsPlaying = false;
                    return
                }
                this.playRecord.videoId = id;
                // 没初始化才赋值,因为一个视频重复播放onPlay会重复触发
                if (this.playRecord.startPlayTime === 0) {
                    this.playRecord.startPlayTime = Date.now();
                }
                if (this.startPauseTime !== 0) {
                    const duration = Date.now() - this.startPauseTime
                    this.totalPauseTime += duration
                }
            },
            // 视频暂停事件
            onPause(index) {
                console.log(index, "触发暂停");
                if (index === this.currentIndex) {
                    this.currentVideoIsPlaying = false;
                } else {
                    this.currentVideoIsPlaying = true;
                    return
                }
                this.startPauseTime = Date.now()
            },
            // 视频结束事件
            onEnded(index) {
                // this.currentVideoIsPlaying = false;
            },
            // 记录播放时长
            onTimeUpdate(e) {
                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) {
                // 获取当前触摸点X坐标
                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.push(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,
                    playAt: this.playRecord.playAt
                }
                this.playRecord = {
                    videoId: null,
                    viewDuration: 0, // 这个视频总共观看了多久
                    playAt: 0, // 这个视频播放到哪了
                    startPlayTime: 0 // 这个视频从什么时候开始播放的
                }
                this.totalHidenTime = 0
                this.totalPauseTime = 0
                savePlayRecord(data)
            }
        }
    }
</script>
<style scoped>
    ::v-deep .custom-tabbar {
        border-top: none !important;
    }
    .video-container {
        width: 100%;
        height: 100vh;
        background-color: #000;
    }
    .video-swiper {
        width: 100%;
        height: calc(100% - 50px);
    }
    .video-item {
        width: 100%;
        height: 100%;
        object-fit: cover;
    }
    .play-icon {
        position: absolute;
        top: 50%;
        left: 50%;
        transform: translate(-50%, -50%);
        width: 45px;
        height: 45px;
        z-index: 10;
        opacity: 0.6;
    }
    .video-info {
        width: 70%;
        position: absolute;
        bottom: 70px;
        left: 20px;
        color: #f8f8f8;
        z-index: 10;
        letter-spacing: 1px;
    }
    .action-buttons {
        position: absolute;
        right: 20px;
        bottom: 150px;
        display: flex;
        flex-direction: column;
        align-items: center;
        z-index: 10;
    }
    .action-item {
        margin-bottom: 18px;
        display: flex;
        flex-direction: column;
        justify-content: center;
        align-items: center;
        color: #fff;
    }
    .avatar-container {
        margin-bottom: 27px;
        position: relative;
        /* 为绝对定位的子元素提供定位上下文 */
        width: 40px;
        height: 40px;
        display: inline-block;
        /* 使容器根据内容调整大小 */
    }
    .avatar {
        border: 2px solid #FFFFFF;
        box-sizing: border-box;
        width: 100%;
        height: 100%;
        border-radius: 50%;
        /* 关键属性,设置为50%即可实现圆形 */
        overflow: hidden;
        /* 确保图片不会超出圆形边界 */
        display: block;
    }
    .follow-icon {
        position: absolute;
        bottom: 0;
        /* 定位到底部 */
        left: 50%;
        /* 水平居中开始位置 */
        transform: translate(-50%, 50%);
        /* 水平居中并向下移动50% */
        width: 18px;
        /* 图标大小 */
        height: 18px;
        background-color: #FF5A5F;
        /* 图标背景色 */
        border-radius: 50%;
        display: flex;
        justify-content: center;
        align-items: center;
        box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
        /* 轻微阴影 */
    }
    .video-tag {
        margin-left: 5px;
        font-weight: bold;
        color: #eeeeee;
    }
    .video-author {
        font-size: 1.2em;
    }
    /* 商品链接悬挂层样式 */
    .goods-link-warp {
        position: absolute;
        bottom: 160px;
        left: 20px;
        color: #f8f8f8;
        z-index: 10;
    }
    .goods-link {
        position: relative;
        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 {
        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;
    }
    .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;
        padding: 10rpx 28rpx;
        border-radius: 20rpx;
        font-size: 26rpx;
        font-weight: bold;
    }
    /* 评论弹窗样式 */
    .comment-popup {
        background-color: #fff;
        border-radius: 20rpx 20rpx 0 0;
        padding-bottom: env(safe-area-inset-bottom);
        height: 60vh;
        display: flex;
        flex-direction: column;
    }
    .popup-header {
        padding: 30rpx;
        display: flex;
        justify-content: space-between;
        align-items: center;
        border-bottom: 1rpx solid #f5f5f5;
    }
    .popup-title {
        font-size: 32rpx;
        font-weight: bold;
    }
    .close-icon {
        /* font-size: 36rpx; */
        color: #999;
    }
    .comment-list {
        flex: 1;
        padding: 0rpx 20rpx 20rpx 20rpx;
        box-sizing: border-box;
        height: calc(60vh - 260rpx);
    }
    .comment-item {
        display: flex;
        flex-direction: column;
        padding: 10rpx 0 20rpx 0;
    }
    .comment-avatar {
        width: 70rpx;
        height: 70rpx;
        border-radius: 50%;
        margin-right: 10rpx;
    }
    .comment-reply-avatar {
        width: 40rpx;
        height: 40rpx;
        border-radius: 50%;
        margin-right: 10rpx;
    }
    .comment-content {
        flex: 1;
    }
    .nickname {
        font-size: 28rpx;
        color: #666;
        display: block;
        margin-bottom: 10rpx;
    }
    .content {
        font-size: 28rpx;
        color: #333;
        display: block;
        margin-bottom: 10rpx;
    }
    .time {
        font-size: 28rpx;
        color: #999;
    }
    .comment-input-area {
        display: flex;
        padding: 20rpx 30rpx;
        align-items: center;
    }
    .comment-input {
        flex: 1;
        background-color: #fff;
        height: 80rpx;
        border: 1px solid #dcdcdc;
        border-radius: 40rpx;
        padding: 0 30rpx;
        font-size: 28rpx;
    }
    .placeholder {
        color: #ccc;
    }
    .submit-btn {
        margin-left: 20rpx;
        background-color: #07c160;
        color: #fff;
        border-radius: 40rpx;
        padding: 0 30rpx;
        height: 80rpx;
        line-height: 80rpx;
        font-size: 28rpx;
    }
    .loading,
    .empty {
        padding: 40rpx 0;
        text-align: center;
        color: #999;
    }
    .reply-list {
        margin-top: 20rpx;
        padding-left: 80rpx;
    }
    .reply-op {
        margin-top: 10rpx;
        padding-left: 80rpx;
        display: flex;
        font-size: 28rpx;
        color: #333;
    }
    .reply-op-item {
        display: flex;
        align-items: center;
        height: 40rpx;
    }
    .reply-item {
        display: flex;
        margin-bottom: 20rpx;
    }
    .reply-content {
        flex: 1;
    }
    .reply-to {
        color: #576b95;
        margin: 0 10rpx;
        font-size: 28rpx;
    }
    .reply-title {
        display: flex;
        align-items: center;
        font-size: 28rpx;
        color: #333;
    }
    .cancel-reply {
        margin-left: 20rpx;
        color: #576b95;
        font-size: 28rpx;
        padding: 6rpx 12rpx;
        background: #f5f5f5;
        border-radius: 20rpx;
    }
    .view-more-replies {
        color: #576b95;
        font-size: 28rpx;
        padding: 10rpx 0;
        padding-left: 80rpx;
    }
    .comment-footer,
    .reply-footer {
        display: flex;
        align-items: center;
        font-size: 28rpx;
        color: #999;
    }
    .reply-btu {
        margin-left: 30rpx;
    }
    .thumbs-up {
        position: absolute;
        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>
pages/product/goods.vue
@@ -305,7 +305,9 @@
import popups from "@/components/popups/popups"; //气泡框
import takeDownFormSaleGoods from "@/components/m-take-down-sale-goods/index"; //下架框
import setup from "./product/popup/popup";
    import {
        getSTSToken
    } from '@/api/common.js'
export default {
  components: {
    popups,
@@ -576,8 +578,35 @@
          this.$store.state.distributionId = distributionId;
        }
      }
      const sts = await getSTSToken();
      const stsUrl = sts.data.data.endpoint
      // // 处理数据
      // goodsList.data.result.records.forEach(item => {
      //     if (item.thumbnail !== '' && item.thumbnail !== null && item.thumbnail.indexOf('http') ===
      //         -1) {
      //         item.thumbnail = stsUrl + '/' + item.thumbnail
      //     }
      //     if (item.goodsVideo !== '' && item.goodsVideo !== null && item.goodsVideo.indexOf(
      //             'http') === -1) {
      //         item.goodsVideo = stsUrl + '/' + item.goodsVideo
      //     }
      // })
      /**商品信息以及规格信息存储 */
      console.log('--------------------------1>',response.data.result.data.goodsGalleryList)
      response.data.result.data.goodsGalleryList.forEach((item,index)=>{
              if (item !== '' && item !== null && item.indexOf('http') ===
                  -1) {
                  response.data.result.data.goodsGalleryList[index] =  stsUrl + '/' + item
                console.log('是否执行-------------》',item)
              }
      })
       console.log('--------------------------2>',response.data.result.data.goodsGalleryList)
      this.goodsDetail = response.data.result.data;
      if (this.goodsDetail.goodsVideo !== '' && this.goodsDetail.goodsVideo !== null && this.goodsDetail.goodsVideo.indexOf(
              'http') === -1) {
          this.goodsDetail.goodsVideo = stsUrl + '/' + this.goodsDetail.goodsVideo
      }
      console.log('--------------------------2>',this.goodsDetail.goodsGalleryList)
      this.wholesaleList = response.data.result.wholesaleList;
      this.goodsSpec = response.data.result.specs;
      this.PromotionList = response.data.result.promotionMap;
uni_modules/uni-drawer/changelog.md
New file
@@ -0,0 +1,13 @@
## 1.2.1(2021-11-22)
- 修复 vue3中个别scss变量无法找到的问题
## 1.2.0(2021-11-19)
- 优化 组件UI,并提供设计资源,详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource)
- 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-drawer](https://uniapp.dcloud.io/component/uniui/uni-drawer)
## 1.1.1(2021-07-30)
- 优化 vue3下事件警告的问题
## 1.1.0(2021-07-13)
- 组件兼容 vue3,如何创建vue3项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834)
## 1.0.7(2021-05-12)
- 新增 组件示例地址
## 1.0.6(2021-02-04)
- 调整为uni_modules目录规范
uni_modules/uni-drawer/components/uni-drawer/keypress.js
New file
@@ -0,0 +1,45 @@
// #ifdef H5
export default {
  name: 'Keypress',
  props: {
    disable: {
      type: Boolean,
      default: false
    }
  },
  mounted () {
    const keyNames = {
      esc: ['Esc', 'Escape'],
      tab: 'Tab',
      enter: 'Enter',
      space: [' ', 'Spacebar'],
      up: ['Up', 'ArrowUp'],
      left: ['Left', 'ArrowLeft'],
      right: ['Right', 'ArrowRight'],
      down: ['Down', 'ArrowDown'],
      delete: ['Backspace', 'Delete', 'Del']
    }
    const listener = ($event) => {
      if (this.disable) {
        return
      }
      const keyName = Object.keys(keyNames).find(key => {
        const keyName = $event.key
        const value = keyNames[key]
        return value === keyName || (Array.isArray(value) && value.includes(keyName))
      })
      if (keyName) {
        // 避免和其他按键事件冲突
        setTimeout(() => {
          this.$emit(keyName, {})
        }, 0)
      }
    }
    document.addEventListener('keyup', listener)
    // this.$once('hook:beforeDestroy', () => {
    //   document.removeEventListener('keyup', listener)
    // })
  },
    render: () => {}
}
// #endif
uni_modules/uni-drawer/components/uni-drawer/uni-drawer.vue
New file
@@ -0,0 +1,183 @@
<template>
    <view v-if="visibleSync" :class="{ 'uni-drawer--visible': showDrawer }" class="uni-drawer" @touchmove.stop.prevent="clear">
        <view class="uni-drawer__mask" :class="{ 'uni-drawer__mask--visible': showDrawer && mask }" @tap="close('mask')" />
        <view class="uni-drawer__content" :class="{'uni-drawer--right': rightMode,'uni-drawer--left': !rightMode, 'uni-drawer__content--visible': showDrawer}" :style="{width:drawerWidth+'px'}">
            <slot />
        </view>
        <!-- #ifdef H5 -->
        <keypress @esc="close('mask')" />
        <!-- #endif -->
    </view>
</template>
<script>
    // #ifdef H5
    import keypress from './keypress.js'
    // #endif
    /**
     * Drawer 抽屉
     * @description 抽屉侧滑菜单
     * @tutorial https://ext.dcloud.net.cn/plugin?id=26
     * @property {Boolean} mask = [true | false] 是否显示遮罩
     * @property {Boolean} maskClick = [true | false] 点击遮罩是否关闭
     * @property {Boolean} mode = [left | right] Drawer 滑出位置
     *     @value left 从左侧滑出
     *     @value right 从右侧侧滑出
     * @property {Number} width 抽屉的宽度 ,仅 vue 页面生效
     * @event {Function} close 组件关闭时触发事件
     */
    export default {
        name: 'UniDrawer',
        components: {
            // #ifdef H5
            keypress
            // #endif
        },
        emits:['change'],
        props: {
            /**
             * 显示模式(左、右),只在初始化生效
             */
            mode: {
                type: String,
                default: ''
            },
            /**
             * 蒙层显示状态
             */
            mask: {
                type: Boolean,
                default: true
            },
            /**
             * 遮罩是否可点击关闭
             */
            maskClick:{
                type: Boolean,
                default: true
            },
            /**
             * 抽屉宽度
             */
            width: {
                type: Number,
                default: 220
            }
        },
        data() {
            return {
                visibleSync: false,
                showDrawer: false,
                rightMode: false,
                watchTimer: null,
                drawerWidth: 220
            }
        },
        created() {
            // #ifndef APP-NVUE
            this.drawerWidth = this.width
            // #endif
            this.rightMode = this.mode === 'right'
        },
        methods: {
            clear(){},
            close(type) {
                // fixed by mehaotian 抽屉尚未完全关闭或遮罩禁止点击时不触发以下逻辑
                if((type === 'mask' && !this.maskClick) || !this.visibleSync) return
                this._change('showDrawer', 'visibleSync', false)
            },
            open() {
                // fixed by mehaotian 处理重复点击打开的事件
                if(this.visibleSync) return
                this._change('visibleSync', 'showDrawer', true)
            },
            _change(param1, param2, status) {
                this[param1] = status
                if (this.watchTimer) {
                    clearTimeout(this.watchTimer)
                }
                this.watchTimer = setTimeout(() => {
                    this[param2] = status
                    this.$emit('change',status)
                }, status ? 50 : 300)
            }
        }
    }
</script>
<style lang="scss" scoped>
    $uni-mask: rgba($color: #000000, $alpha: 0.4) ;
    // 抽屉宽度
    $drawer-width: 220px;
    .uni-drawer {
        /* #ifndef APP-NVUE */
        display: block;
        /* #endif */
        position: fixed;
        top: 0;
        left: 0;
        right: 0;
        bottom: 0;
        overflow: hidden;
        z-index: 999;
    }
    .uni-drawer__content {
        /* #ifndef APP-NVUE */
        display: block;
        /* #endif */
        position: absolute;
        top: 0;
        width: $drawer-width;
        bottom: 0;
        background-color: $uni-bg-color;
        transition: transform 0.3s ease;
    }
    .uni-drawer--left {
        left: 0;
        /* #ifdef APP-NVUE */
        transform: translateX(-$drawer-width);
        /* #endif */
        /* #ifndef APP-NVUE */
        transform: translateX(-100%);
        /* #endif */
    }
    .uni-drawer--right {
        right: 0;
        /* #ifdef APP-NVUE */
        transform: translateX($drawer-width);
        /* #endif */
        /* #ifndef APP-NVUE */
        transform: translateX(100%);
        /* #endif */
    }
    .uni-drawer__content--visible {
        transform: translateX(0px);
    }
    .uni-drawer__mask {
        /* #ifndef APP-NVUE */
        display: block;
        /* #endif */
        opacity: 0;
        position: absolute;
        top: 0;
        left: 0;
        bottom: 0;
        right: 0;
        background-color: $uni-mask;
        transition: opacity 0.3s;
    }
    .uni-drawer__mask--visible {
        /* #ifndef APP-NVUE */
        display: block;
        /* #endif */
        opacity: 1;
    }
</style>
uni_modules/uni-drawer/package.json
New file
@@ -0,0 +1,87 @@
{
  "id": "uni-drawer",
  "displayName": "uni-drawer 抽屉",
  "version": "1.2.1",
  "description": "抽屉式导航,用于展示侧滑菜单,侧滑导航。",
  "keywords": [
    "uni-ui",
    "uniui",
    "drawer",
    "抽屉",
    "侧滑导航"
],
  "repository": "https://github.com/dcloudio/uni-ui",
  "engines": {
    "HBuilderX": ""
  },
  "directories": {
    "example": "../../temps/example_temps"
  },
  "dcloudext": {
    "category": [
      "前端组件",
      "通用组件"
    ],
    "sale": {
      "regular": {
        "price": "0.00"
      },
      "sourcecode": {
        "price": "0.00"
      }
    },
    "contact": {
      "qq": ""
    },
    "declaration": {
      "ads": "无",
      "data": "无",
      "permissions": "无"
    },
    "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui"
  },
  "uni_modules": {
    "dependencies": ["uni-scss"],
    "encrypt": [],
    "platforms": {
      "cloud": {
        "tcb": "y",
        "aliyun": "y"
      },
      "client": {
        "App": {
          "app-vue": "y",
          "app-nvue": "y"
        },
        "H5-mobile": {
          "Safari": "y",
          "Android Browser": "y",
          "微信浏览器(Android)": "y",
          "QQ浏览器(Android)": "y"
        },
        "H5-pc": {
          "Chrome": "y",
          "IE": "y",
          "Edge": "y",
          "Firefox": "y",
          "Safari": "y"
        },
        "小程序": {
          "微信": "y",
          "阿里": "y",
          "百度": "y",
          "字节跳动": "y",
          "QQ": "y"
        },
        "快应用": {
          "华为": "u",
          "联盟": "u"
        },
        "Vue": {
            "vue2": "y",
            "vue3": "y"
        }
      }
    }
  }
}
uni_modules/uni-drawer/readme.md
New file
@@ -0,0 +1,10 @@
## Drawer 抽屉
> **组件名:uni-drawer**
> 代码块: `uDrawer`
抽屉侧滑菜单。
### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-drawer)
#### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839