绿满眶商城微信小程序-uniapp
xiangpei
2025-06-25 df4dd9f030ac4e830d93f2f64e2379a9b7dad278
自定义顶部组件、顶部tab对应页面接入该组件
5个文件已修改
1个文件已添加
257 ■■■■■ 已修改文件
components/TopBar.vue 142 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pages.json 34 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pages/commodity-square/commoditySquare.vue 27 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pages/health/healthVideo.vue 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pages/mine/activity/reportActivity.vue 25 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pages/tabbar/index/home.vue 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
components/TopBar.vue
New file
@@ -0,0 +1,142 @@
<template>
    <view class="top-bar" :style="{paddingTop: statusBarHeight + 'px'}">
        <view class="top-bar-content">
            <!-- 标题列表 -->
            <scroll-view class="title-scroll" scroll-x="true" scroll-with-animation :scroll-left="scrollLeft">
                <view class="title-container">
                    <view
                        v-for="(item, index) in titleList"
                        :key="index"
                        :class="{active: selectedTitleIndex === item.index, 'title-item': true}"
                        @click="changeTab(item)"
                    >
                        <text>{{item.title}}</text>
                        <view class="underline" v-if="selectedTitleIndex === item.index"></view>
                    </view>
                </view>
            </scroll-view>
        </view>
    </view>
</template>
<script>
    export default {
        name:"TopBar",
        props: {
            selectedTitleIndex: {
                type: String,
                default: 'home'
            }
        },
        data() {
            return {
                statusBarHeight: 0,
                scrollLeft: 0,
                titleList: [
                    {
                        index: 'home',
                        pagePath: '/pages/tabbar/index/home',
                        title: '推荐'
                    },
                    {
                        index: 'shop',
                        pagePath: '/pages/commodity-square/commoditySquare',
                        title: '商品广场'
                    },
                    {
                        index: 'activity',
                        pagePath: '/pages/mine/activity/reportActivity',
                        title: '活动'
                    },
                    {
                        index: 'health',
                        pagePath: '/pages/health/healthVideo',
                        title: '大健康'
                    }
                ]
            };
        },
        created() {
            // 获取状态栏高度
            const systemInfo = uni.getSystemInfoSync();
            this.statusBarHeight = systemInfo.statusBarHeight;
        },
        methods: {
            changeTab(titleObj) {
                console.log("点击顶部导航", titleObj);
                if (titleObj.index !== this.selectedTitleIndex) {
                    this.$emit("changeTab", titleObj);
                    // 计算滚动位置使当前选中项居中
                    this.$nextTick(() => {
                        const query = uni.createSelectorQuery().in(this);
                        query.select(`.title-item[data-index="${titleObj.index}"]`).boundingClientRect();
                        query.select('.title-scroll').boundingClientRect();
                        query.exec(res => {
                            if (res[0] && res[1]) {
                                const itemLeft = res[0].left;
                                const scrollWidth = res[1].width;
                                const itemWidth = res[0].width;
                                this.scrollLeft = itemLeft - (scrollWidth - itemWidth) / 2;
                            }
                        });
                    });
                }
            }
        }
    }
</script>
<style>
    .top-bar {
            position: fixed;
            top: 0;
            left: 0;
            right: 0;
            z-index: 999;
        }
        .top-bar .top-bar-content {
            box-sizing: border-box;
            display: flex;
            align-items: center;
        }
        .top-bar .title-scroll {
            width: 100%;
            height: 80rpx;
            white-space: nowrap;
        }
        .top-bar .title-scroll .title-container {
            display: inline-flex;
            height: 100%;
            align-items: center;
        }
        .top-bar .title-scroll .title-container .title-item {
            position: relative;
            padding: 0 24rpx;
            font-size: 32rpx;
            color: white;
            height: 100%;
            display: flex;
            align-items: center;
            justify-content: center;
            flex-direction: column;
        }
        .top-bar .title-scroll .title-container .title-item.active {
            font-weight: 500;
        }
        .top-bar .title-scroll .title-container .title-item.active .underline {
            position: absolute;
            bottom: 4rpx;
            left: 50%;
            transform: translateX(-50%);
            width: calc(100% - 76rpx);
            height: 4rpx;
            background-color: white;
            border-radius: 4rpx;
        }
</style>
pages.json
@@ -65,10 +65,10 @@
        {
            // "path": "pages/tabbar/category/category",
            "path": "pages/commodity-square/commoditySquare",
            "style": {
                "navigationBarTitleText": "商品广场",
                // "navigationStyle": "custom", // 隐藏系统导航栏
                "navigationStyle": "custom", // 隐藏系统导航栏
                "navigationBarTextStyle": "black",
                // "disableScroll": true,
                "bounce": "none",
@@ -749,6 +749,7 @@
                    "style": {
                        "navigationBarTitleText": "活动",
                        "enablePullDownRefresh": true, //下拉刷新
                        "navigationStyle": "custom", // 隐藏顶部导航栏
                        "componentPlaceholder": {
                            "u-form": "view",
                            "u-form-item": "view",
@@ -987,7 +988,7 @@
                },
                {
                    "path" : "video-goods-detail",
                    "style" :
                    "style" :
                    {
                        "navigationBarTitleText" : "视频推荐商品",
                        "componentPlaceholder": {
@@ -1435,7 +1436,8 @@
            "pages": [{
                "path": "healthVideo",
                "style": {
                    "navigationBarTitleText": "大健康"
                    "navigationBarTitleText": "大健康",
                    "navigationStyle": "custom"
                }
            }]
        },
@@ -1452,13 +1454,13 @@
            "root": "pages/supplier",
            "pages": [{
                "path" : "suppler-order/suppler-order",
                "style" :
                "style" :
                {
                    "navigationBarTitleText" : ""
                }
            }]
        }
        // ,
        // {
        //     "root": "pages/commodity-square",
@@ -1496,13 +1498,13 @@
                "selectedIconPath": "static/tabbar/home-s.png",
                "text": "首页"
            },
            {
                // "pagePath": "pages/tabbar/category/category",
                "pagePath": "pages/commodity-square/commoditySquare",
                "iconPath": "static/tabbar/category.png",
                "selectedIconPath": "static/tabbar/category-s.png",
                "text": "商城"
            },
            // {
            //     // "pagePath": "pages/tabbar/category/category",
            //     "pagePath": "pages/commodity-square/commoditySquare",
            //     "iconPath": "static/tabbar/category.png",
            //     "selectedIconPath": "static/tabbar/category-s.png",
            //     "text": "商城"
            // },
            {
                "pagePath": "pages/tabbar/video/video",
                "iconPath": "static/tabbar/video.png",
@@ -1522,11 +1524,11 @@
            }
        ]
    },
    // #todo 为什么要注释condition下代码?
    // #todo 为什么要注释condition下代码?
    // IOS plus.runtime.arguments 添加 condition节点后, 框架会修改 runtime.arguments
    // 会影响什么功能?
    //    -在h5中唤醒app会一直返回默认值 {"name":"","path":"","query":"","id":0}
    "condition": { //模式配置,仅开发期间生效
    "condition": { //模式配置,仅开发期间生效
        // "current": 0, //当前激活的模式(list 的索引项)
        // "list": [{
        //     "name": "", //模式名称
@@ -1534,4 +1536,4 @@
        //     "query": "" //启动参数,在页面的onLoad函数里面得到
        // }]
    }
}
}
pages/commodity-square/commoditySquare.vue
@@ -1,5 +1,6 @@
<template>
    <view class="container">
        <top-bar selectedTitleIndex="shop" @changeTab="topBarChange" class="topBar"></top-bar>
        <input type="text" v-show="false" v-model="flushDom" />
        <view class="search">
            <u-search class="nav-search" @blur='searchGoods' placeholder="搜索商品" :show-action="false"></u-search>
@@ -12,7 +13,7 @@
                        :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"
                    <image :src="item.thumbnail"
                        mode="aspectFill" class="goodsImg">
                    </image>
                    <view class="goodsInfo">
@@ -31,7 +32,7 @@
                        :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"
                    <image :src="item.thumbnail"
                        mode="aspectFill" class="goodsImg">
                    </image>
                    <view class="goodsInfo">
@@ -60,11 +61,11 @@
        </view>
        <view style="display: flex;align-items: center;justify-content: center;margin-top: 20rpx;" v-if="canLoadMore">
            没有更多数据了.................</view>
        <custom-tabbar bgColor="#ffffff" selected="shop"></custom-tabbar>
    </view>
</template>
<script>
    import TopBar from "@/components/TopBar.vue";
    import {
        getCategoryList,
        getGoodsList
@@ -73,6 +74,7 @@
        getSTSToken
    } from '@/api/common.js'
    export default {
        components: {TopBar},
        data() {
            return {
                //记录两列高度
@@ -104,6 +106,17 @@
            }
        },
        methods: {
            topBarChange(titleObj) {
                if (titleObj.index === 'home') {
                    uni.switchTab({
                        url: titleObj.pagePath
                    });
                } else {
                    uni.redirectTo({
                        url: titleObj.pagePath
                    });
                }
            },
            async searchGoods(keyWard) {
                this.getGoodsParam.keyword = keyWard
                this.getGoodsParam.pageNumber = 1
@@ -393,4 +406,10 @@
        color: red;
        font-weight: bold;
    }
</style>
    .topBar {
        position: fixed;
        top: 20rpx;
        left: 20rpx;
        z-index: 1000
    }
</style>
pages/health/healthVideo.vue
@@ -1,5 +1,6 @@
<template>
  <view class="video-container">
    <top-bar selectedTitleIndex="health" @changeTab="topBarChange" class="topBar"></top-bar>
    <!-- 视频加载 -->
    <zero-loading v-show="videoLoading" type="circle" color="#0ebd57" text=""></zero-loading>
    <!-- 视频列表 -->
@@ -241,6 +242,7 @@
</template>
<script>
import TopBar from "@/components/TopBar.vue";
import { getHealthRecommendVideos, savePlayRecord, subscribe, getVideoComments, addVideoComment, thubmsUpComment, cancelThubmsUpComment } from "@/api/video.js";
import { changeCollect } from "@/api/collect.js";
import { saveShare, saveShareClickRecord } from "@/api/share.js";
@@ -1361,4 +1363,10 @@
    .custom-share-btn::after {
      border: none;
    }
    .topBar {
        position: fixed;
        top: 20rpx;
        left: 20rpx;
        z-index: 1000
    }
</style>
pages/mine/activity/reportActivity.vue
@@ -1,7 +1,7 @@
<template>
    <view class="wrapper">
        <top-bar selectedTitleIndex="activity" @changeTab="topBarChange" class="topBar"></top-bar>
        <view style="height: 100rpx"></view>
        <!-- 内容区域 -->
        <scroll-view scroll-y class="content" style="height: 40vh;" @scrolltolower="loadMore" :lower-threshold="100">
@@ -47,9 +47,11 @@
</template>
<script>
    import TopBar from "@/components/TopBar.vue";
    import '@/components/uview-components/uview-ui';
    import {getActivityReportList} from '@/api/activity.js';
    export default {
        components: {TopBar},
        data() {
            return {
                columns: [
@@ -74,6 +76,17 @@
            this.getActivityList();
        },
        methods: {
            topBarChange(titleObj) {
                if (titleObj.index === 'home') {
                    uni.switchTab({
                        url: titleObj.pagePath
                    });
                } else {
                    uni.redirectTo({
                        url: titleObj.pagePath
                    });
                }
            },
            /**
             * 下拉刷新时
             */
@@ -367,4 +380,10 @@
            font-size: 28rpx;
        }
    }
</style>
    .topBar {
        position: fixed;
        top: 20rpx;
        left: 20rpx;
        z-index: 1000
    }
</style>
pages/tabbar/index/home.vue
@@ -1,5 +1,6 @@
<template>
  <view class="video-container">
    <top-bar selectedTitleIndex="home" @changeTab="topBarChange" class="topBar"></top-bar>
    <!-- 视频加载 -->
    <zero-loading v-show="videoLoading" type="circle" color="#0ebd57" text=""></zero-loading>
    <!-- 视频列表 -->
@@ -247,7 +248,10 @@
import { silentLogin } from "@/api/connect.js";
import { getUserInfo } from "@/api/members";
import storage from "@/utils/storage.js";
import TopBar from "@/components/TopBar.vue";
export default {
  components: {TopBar},
  computed: {
        hasPlayTime() {
          return this.sliderFormatTime(this.progress > 0 ? this.duration * this.progress / 100 : 0);
@@ -379,6 +383,17 @@
      }
  },
  methods: {
      topBarChange(titleObj) {
        if (titleObj.index === 'home') {
            uni.switchTab({
                url: titleObj.pagePath
            });
        } else {
            uni.redirectTo({
                url: titleObj.pagePath
            });
        }
      },
      // 静默登录
      wxSilentLogin(callback) {
          //获取code
@@ -1361,4 +1376,10 @@
    .custom-share-btn::after {
      border: none;
    }
    .topBar {
        position: fixed;
        top: 20rpx;
        left: 20rpx;
        z-index: 1000
    }
</style>