481 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			Vue
		
	
			
		
		
	
	
			481 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			Vue
		
	
<template>
 | 
						||
    <view class="container">
 | 
						||
        <view class="main">
 | 
						||
            <z-paging
 | 
						||
                ref="paging"
 | 
						||
                v-model="indexDataArr"
 | 
						||
                @query="upCallback"
 | 
						||
                @scroll="pagingScroll"
 | 
						||
                @scrolltoupper="scrolltoupper"
 | 
						||
                :fixed="false"
 | 
						||
                height="100%"
 | 
						||
                auto-show-back-to-top
 | 
						||
            >
 | 
						||
                <view class="top_container">
 | 
						||
                    <view
 | 
						||
                        class="title text-[#1b4694] font-bold text-5xl"
 | 
						||
                        :style="{
 | 
						||
                            top: statusBarHeight + 'px',
 | 
						||
                            height: navHeight + 'px',
 | 
						||
                            lineHeight: navHeight + 'px'
 | 
						||
                        }"
 | 
						||
                    >
 | 
						||
                        {{ titleImageObj[0]?.content?.title ?? '思缘生活' }}
 | 
						||
                    </view>
 | 
						||
                    <template v-if="searchList">
 | 
						||
                        <w-search
 | 
						||
                            :content="searchList?.content"
 | 
						||
                            :styles="searchList?.styles"
 | 
						||
                            :navbarStyle="{ top: titleHeight + 10 + 'px' }"
 | 
						||
                        />
 | 
						||
                    </template>
 | 
						||
                    <image
 | 
						||
                        :style="{ height: 70 + titleHeight + 'px' }"
 | 
						||
                        :src="getImageUrl(item.image)"
 | 
						||
                        v-for="(item, ind) in titleImageObj[0]?.content?.data"
 | 
						||
                        :key="ind"
 | 
						||
                    />
 | 
						||
                </view>
 | 
						||
                <!-- 固定顶部搜索栏 -->
 | 
						||
                <view
 | 
						||
                    class="top_fixed fixed bg-white w-[100%] top-0 z-[99]"
 | 
						||
                    :style="{ height: titleHeight + 8 + 'px' }"
 | 
						||
                    v-if="searchFixed"
 | 
						||
                >
 | 
						||
                    <template v-if="searchList">
 | 
						||
                        <w-search
 | 
						||
                            :content="searchList?.content"
 | 
						||
                            :styles="searchList?.styles"
 | 
						||
                            :navbarStyle="{ bottom: '13px', height: capsuleHeight + 'px' }"
 | 
						||
                            :navbarItemStyle="{ background: '#F1F1F1' }"
 | 
						||
                            :inputStyle="{ width: '250rpx' }"
 | 
						||
                        />
 | 
						||
                    </template>
 | 
						||
                </view>
 | 
						||
 | 
						||
                <view>
 | 
						||
                    <view
 | 
						||
                        v-for="(item, index) in indexData.pages"
 | 
						||
                        :key="index"
 | 
						||
                        class="bg-[#F5F5F7]"
 | 
						||
                    >
 | 
						||
                        <!-- <template v-if="item.name == 'search'">
 | 
						||
                            <w-search :content="item.content" :styles="item.styles" />
 | 
						||
                        </template> -->
 | 
						||
                        <template v-if="item.name == 'banner'">
 | 
						||
                            <w-banner :content="item.content" :styles="item.styles" />
 | 
						||
                        </template>
 | 
						||
                        <template v-if="item.name == 'nav'">
 | 
						||
                            <w-nav :content="item.content" :styles="item.styles" />
 | 
						||
                        </template>
 | 
						||
                        <template v-if="item.name == 'category'">
 | 
						||
                            <w-category :content="item.content" :styles="item.styles" />
 | 
						||
                        </template>
 | 
						||
                    </view>
 | 
						||
                    <template v-if="activityList?.length">
 | 
						||
                        <ActivateIndoor :lists="activityList"></ActivateIndoor>
 | 
						||
                    </template>
 | 
						||
                    <!-- 页面展示需要把活动专区放在推荐分类列表和分类服务中间,所以这里要抽离出来重新写 -->
 | 
						||
                    <view
 | 
						||
                        v-for="(item, index) in indexData.pages"
 | 
						||
                        :key="index"
 | 
						||
                        class="bg-[#F5F5F7]"
 | 
						||
                    >
 | 
						||
                        <template v-if="item.name == 'category'">
 | 
						||
                            <CategoryDatalist :content="item.content" />
 | 
						||
                        </template>
 | 
						||
                    </view>
 | 
						||
                </view>
 | 
						||
                <!-- 活动专区:假节日搞活动 -->
 | 
						||
                <!-- <template v-if="indexData?.promotion?.length">
 | 
						||
                    <ActivateIndoor :lists="indexData.promotion"></ActivateIndoor>
 | 
						||
                </template> -->
 | 
						||
 | 
						||
                <!-- 新品推荐 -->
 | 
						||
                <!-- <template v-if="indexData?.newRecommend?.length">
 | 
						||
                    <NewGoods :lists="indexData.newRecommend"></NewGoods>
 | 
						||
                </template> -->
 | 
						||
 | 
						||
                <!-- 热门服务 -->
 | 
						||
                <!-- <template v-if="indexData?.hotService?.length">
 | 
						||
                    <hot :lists="indexData.hotService"></hot>
 | 
						||
                </template> -->
 | 
						||
                <!-- 吸顶分类 -->
 | 
						||
                <template v-if="categoryList?.length > 0">
 | 
						||
                    <stickyCategory
 | 
						||
                        ref="stickycategory"
 | 
						||
                        :lists="categoryList"
 | 
						||
                        :top="titleHeight"
 | 
						||
                        @tabChange="tabChange"
 | 
						||
                        :isFixed="isFixed"
 | 
						||
                    ></stickyCategory>
 | 
						||
                </template>
 | 
						||
 | 
						||
                <!-- 师傅推荐 暂时屏掉,参考竞品,一般不显示 -->
 | 
						||
                <!-- <template v-if="indexData?.staff?.length">
 | 
						||
                    <master :lists="indexData.staff"></master>
 | 
						||
                </template> -->
 | 
						||
 | 
						||
                <!-- 推荐分类 -->
 | 
						||
                <!-- <template v-if="indexData?.category.length || []">
 | 
						||
                    <category :lists="indexData?.category"></category>
 | 
						||
                </template> -->
 | 
						||
 | 
						||
                <!-- 用于占位 -->
 | 
						||
                <div class="cat" v-if="indexData?.hotService.length"></div>
 | 
						||
            </z-paging>
 | 
						||
        </view>
 | 
						||
        <view class="right-btns">
 | 
						||
            <view class="rightcontrol" @click="rightControl">
 | 
						||
                <!-- <image
 | 
						||
                    class="iamge-close"
 | 
						||
                    src="@/static/images/icon_clear.png"
 | 
						||
                    alt=""
 | 
						||
                    v-if="isShowRightBtn"
 | 
						||
                /> -->
 | 
						||
            </view>
 | 
						||
            <!-- <view class="allBtn" v-if="isShowRightBtn">
 | 
						||
                <button class="button" type="default" open-type="share">
 | 
						||
                    <image src="@/static/images/share.png" alt="" />
 | 
						||
                    <view>分享</view>
 | 
						||
                </button>
 | 
						||
                <button class="button" type="default" @click="call">
 | 
						||
                    <image src="@/static/images/phone.png" alt="" />
 | 
						||
                    <view>电话</view>
 | 
						||
                </button>
 | 
						||
                <button class="button" type="default" open-type="contact">
 | 
						||
                    <image src="@/static/images/kefu.png" alt="" />
 | 
						||
                    <view>客服</view>
 | 
						||
                </button>
 | 
						||
            </view> -->
 | 
						||
        </view>
 | 
						||
        <view class="endrightcontrol" @click="rightControl">
 | 
						||
            <image src="@/static/images/icon_user_about.png" alt="" v-if="!isShowRightBtn" />
 | 
						||
        </view>
 | 
						||
        <!-- 底部导航栏 -->
 | 
						||
        <Tabbar />
 | 
						||
    </view>
 | 
						||
</template>
 | 
						||
 | 
						||
<script setup lang="ts">
 | 
						||
import { getIndex } from '@/api/shop'
 | 
						||
import { reactive, shallowRef, ref, unref } from 'vue'
 | 
						||
import { onLoad, onUnload, onShareAppMessage, onShow } from '@dcloudio/uni-app'
 | 
						||
import Tabbar from '@/components/tabbar/tabbar.vue'
 | 
						||
// import Category from './comp/category.vue'
 | 
						||
import ActivateIndoor from './comp/activate-indoor.vue'
 | 
						||
// import NewGoods from './comp/new-goods.vue'
 | 
						||
// import Hot from './comp/hot-list.vue'
 | 
						||
// import Master from './comp/master-list.vue'
 | 
						||
import CategoryDatalist from './comp/category-datalist.vue'
 | 
						||
import stickyCategory from './comp/sticky-category.vue'
 | 
						||
import { useAppStore } from '@/stores/app'
 | 
						||
import { getDecorate } from '@/api/shop'
 | 
						||
import { useUserStore } from '@/stores/user'
 | 
						||
import { apiActivityList } from '@/api/goods'
 | 
						||
import { storeToRefs } from 'pinia'
 | 
						||
 | 
						||
const appStore = useAppStore()
 | 
						||
const userStore = useUserStore()
 | 
						||
const { userInfo } = storeToRefs(userStore)
 | 
						||
const paging = shallowRef()
 | 
						||
const { getImageUrl } = useAppStore()
 | 
						||
const indexData = reactive<any>({
 | 
						||
    pages: [],
 | 
						||
    promotion: [],
 | 
						||
    newRecommend: [],
 | 
						||
    hotService: [],
 | 
						||
    staff: [],
 | 
						||
    category: []
 | 
						||
})
 | 
						||
interface ListContent {
 | 
						||
    content?: {
 | 
						||
        title: ''
 | 
						||
        data: [
 | 
						||
            {
 | 
						||
                image: ''
 | 
						||
            }
 | 
						||
        ]
 | 
						||
    }
 | 
						||
    styles?: {}
 | 
						||
    title?: ''
 | 
						||
}
 | 
						||
interface ListContentList {
 | 
						||
    [index: number]: ListContent
 | 
						||
}
 | 
						||
interface ActivityList {
 | 
						||
    goodsIds: string | null
 | 
						||
    id: number
 | 
						||
    name: string
 | 
						||
    status: number
 | 
						||
    surfacePlot: string
 | 
						||
}
 | 
						||
let categoryList = reactive([]) //分类
 | 
						||
let searchList = reactive<ListContent>({}) //搜索
 | 
						||
const activityList = ref<ActivityList[]>([]) //活动专区
 | 
						||
let titleImageObj = reactive<ListContentList>([]) //顶部图片标题
 | 
						||
const titleHeight = ref(0) //标题栏总高度
 | 
						||
const capsuleHeight = ref(0) //胶囊高度
 | 
						||
const navHeight = ref(0) //导航栏高度
 | 
						||
const statusBarHeight = ref(0) //状态栏高度
 | 
						||
const getTitleHeight = () => {
 | 
						||
    const menuButtonInfo = uni.getMenuButtonBoundingClientRect()
 | 
						||
    uni.getSystemInfo({
 | 
						||
        success: res => {
 | 
						||
            statusBarHeight.value = res?.statusBarHeight as number
 | 
						||
            navHeight.value =
 | 
						||
                (menuButtonInfo.top - statusBarHeight.value) * 2 + menuButtonInfo.height //获取导航栏高度
 | 
						||
            titleHeight.value = statusBarHeight.value + navHeight.value
 | 
						||
            capsuleHeight.value = menuButtonInfo.height
 | 
						||
        }
 | 
						||
    })
 | 
						||
}
 | 
						||
 | 
						||
//z-paging直接绑定indexData控制台会报警告[Vue warn]: Invalid prop: type check failed for prop "modelValue". Expected Array, got Object
 | 
						||
//故定义indexDataArr
 | 
						||
interface IndexArray {
 | 
						||
    content: object
 | 
						||
    disabled: boolean
 | 
						||
    display: boolean
 | 
						||
    id: string
 | 
						||
    name: string
 | 
						||
    styles: object
 | 
						||
    title: string
 | 
						||
}
 | 
						||
let indexDataArr: IndexArray[] = []
 | 
						||
const upCallback = async (): Promise<void> => {
 | 
						||
    try {
 | 
						||
        const data = await getIndex({
 | 
						||
            cityId: appStore.cityInfo.city_id
 | 
						||
        })
 | 
						||
        for (const key in indexData) {
 | 
						||
            if (data[key] != null && data[key] != undefined) {
 | 
						||
                indexData[key] = data[key]
 | 
						||
            }
 | 
						||
        }
 | 
						||
        indexData.pages = JSON.parse(data.pages)
 | 
						||
        indexDataArr = new Array(indexData.pages)
 | 
						||
        console.log('indexData.pages', indexData.pages)
 | 
						||
 | 
						||
        // 吸顶分类列表
 | 
						||
        const cate_list = indexData.pages.filter((i: any) => i.name == 'sticky-category')
 | 
						||
        if (cate_list.length > 0) {
 | 
						||
            categoryList = cate_list[0].content.data
 | 
						||
        }
 | 
						||
        // 搜索栏
 | 
						||
        const search_list = indexData.pages.filter((i: any) => i.name == 'search')
 | 
						||
        if (search_list.length > 0) {
 | 
						||
            searchList = search_list[0]
 | 
						||
        }
 | 
						||
        // 顶部图片标题信息
 | 
						||
        titleImageObj = indexData.pages.filter((i: any) => i.name == 'home-title')
 | 
						||
 | 
						||
        // 导航菜单(金刚区)
 | 
						||
        paging.value.complete([data])
 | 
						||
    } catch (err) {
 | 
						||
        console.log(err)
 | 
						||
        // refreshRef.value.endErr()
 | 
						||
        paging.value.complete(false)
 | 
						||
    }
 | 
						||
}
 | 
						||
 | 
						||
onShow(async () => {
 | 
						||
    const res = await apiActivityList({ status: 1 })
 | 
						||
    activityList.value = res.lists
 | 
						||
})
 | 
						||
const stickycategory = ref()
 | 
						||
const searchFixed = ref(false) //显示顶部搜索栏
 | 
						||
const pagingScroll = async (e: any) => {
 | 
						||
    const banner_h = uni.upx2px(300) //300为顶部图片的高度
 | 
						||
    const sc_top = e.detail.scrollTop
 | 
						||
    stickycategory?.value?.handleActive(sc_top)
 | 
						||
 | 
						||
    if (sc_top > banner_h) searchFixed.value = true
 | 
						||
    else searchFixed.value = false
 | 
						||
}
 | 
						||
 | 
						||
const isFixed = ref(true)
 | 
						||
const scrolltoupper = () => {
 | 
						||
    // 解决点击返回顶部按钮返回顶部后,还有吸顶的问题
 | 
						||
    isFixed.value = false
 | 
						||
    setTimeout(() => {
 | 
						||
        isFixed.value = true
 | 
						||
    })
 | 
						||
}
 | 
						||
// 吸顶分类改变
 | 
						||
const tabChange = async (e: any) => {
 | 
						||
    paging.value.scrollToY(e)
 | 
						||
}
 | 
						||
 | 
						||
onShareAppMessage(() => {
 | 
						||
    return {
 | 
						||
        title: `粤好生活`,
 | 
						||
        imageUrl: '',
 | 
						||
        path: `/pages/index/index?scene=${unref(userInfo).distributorId}`
 | 
						||
    }
 | 
						||
})
 | 
						||
const call = async () => {
 | 
						||
    // 获取页面装修-客服设置中配置的客服联系电话
 | 
						||
    const data = await getDecorate({ id: 3 })
 | 
						||
    const datas = JSON.parse(data.pages)
 | 
						||
    const mobile = datas[0].content.mobile
 | 
						||
    uni.makePhoneCall({
 | 
						||
        phoneNumber: mobile,
 | 
						||
        success: function () {
 | 
						||
            console.log('拨打电话成功')
 | 
						||
        },
 | 
						||
        fail: function () {
 | 
						||
            console.log('拨打电话失败')
 | 
						||
        }
 | 
						||
    })
 | 
						||
}
 | 
						||
 | 
						||
// const sharedState = inject('sharedState')
 | 
						||
 | 
						||
onLoad(options => {
 | 
						||
    // 扫描普通二维码
 | 
						||
    // 有distId,重新扫码进来
 | 
						||
    // 有distId,重新扫新码
 | 
						||
    // #ifdef MP-WEIXIN
 | 
						||
    // const q = decodeURIComponent(options.q)
 | 
						||
    // if (q.indexOf('distributorId=') != -1) {
 | 
						||
    //     // 已绑定且已登录
 | 
						||
    //     if (userStore.distId && userStore.token) return
 | 
						||
    //     const distributorId = parseFloat(q.split('distributorId=')[1])
 | 
						||
    //     userStore.setDistId(distributorId)
 | 
						||
    //     if (userStore.token) {
 | 
						||
    //         userStore.bindingDistId()
 | 
						||
    //     }
 | 
						||
    // }
 | 
						||
 | 
						||
    // 扫太阳码或分享
 | 
						||
    const { scene } = options
 | 
						||
    if (scene) {
 | 
						||
        const distributorId = parseFloat(scene)
 | 
						||
        userStore.setDistId(distributorId)
 | 
						||
        if (userStore.token) {
 | 
						||
            userStore.bindingDistId()
 | 
						||
        }
 | 
						||
    }
 | 
						||
 | 
						||
    // #endif
 | 
						||
    getTitleHeight()
 | 
						||
    uni.$on('refreshhome', () => {
 | 
						||
        upCallback()
 | 
						||
    })
 | 
						||
})
 | 
						||
 | 
						||
onUnload(() => {
 | 
						||
    uni.$off('refreshhome')
 | 
						||
})
 | 
						||
 | 
						||
const isShowRightBtn = ref(true)
 | 
						||
const rightControl = () => {
 | 
						||
    isShowRightBtn.value = !isShowRightBtn.value
 | 
						||
}
 | 
						||
</script>
 | 
						||
 | 
						||
<style lang="scss">
 | 
						||
.container {
 | 
						||
    display: flex;
 | 
						||
    height: 100vh;
 | 
						||
    overflow: hidden;
 | 
						||
    flex-direction: column;
 | 
						||
    font-family: 'Source Han Sans CN', 'Helvetica Neue', 'Helvetica', 'Arial', sans-serif;
 | 
						||
 | 
						||
    .main {
 | 
						||
        flex: 1;
 | 
						||
        min-height: 0;
 | 
						||
        overflow: scroll;
 | 
						||
        background: #f8f8f9;
 | 
						||
        position: relative;
 | 
						||
        // background-color: #fff;
 | 
						||
    }
 | 
						||
 | 
						||
    .top_container {
 | 
						||
        // position: absolute;
 | 
						||
        image {
 | 
						||
            width: 100%;
 | 
						||
            // height: 300rpx;
 | 
						||
            vertical-align: top;
 | 
						||
        }
 | 
						||
 | 
						||
        .title {
 | 
						||
            position: absolute;
 | 
						||
            // top: 93rpx;
 | 
						||
            left: 50%;
 | 
						||
            transform: translateX(-50%);
 | 
						||
        }
 | 
						||
    }
 | 
						||
 | 
						||
    .right-btns {
 | 
						||
        position: fixed;
 | 
						||
        right: 20rpx;
 | 
						||
        bottom: 25%;
 | 
						||
        width: 100rpx;
 | 
						||
        min-height: 35px;
 | 
						||
        z-index: 1000;
 | 
						||
 | 
						||
        .rightcontrol {
 | 
						||
            width: 100%;
 | 
						||
            height: 50rpx;
 | 
						||
            display: flex;
 | 
						||
 | 
						||
            image {
 | 
						||
                width: 40rpx;
 | 
						||
                height: 40rpx;
 | 
						||
                object-fit: contain;
 | 
						||
                margin-left: auto;
 | 
						||
            }
 | 
						||
 | 
						||
            .iamge-close {
 | 
						||
                background: rgba(34, 39, 21, 0.75);
 | 
						||
                border-radius: 50%;
 | 
						||
            }
 | 
						||
        }
 | 
						||
 | 
						||
        .button {
 | 
						||
            width: 100rpx;
 | 
						||
            height: 100rpx;
 | 
						||
            background: rgba(34, 39, 21, 0.75);
 | 
						||
            border-radius: 50%;
 | 
						||
            margin-bottom: 20rpx;
 | 
						||
            display: flex;
 | 
						||
            align-items: center;
 | 
						||
            flex-direction: column;
 | 
						||
            justify-content: center;
 | 
						||
            color: #ffffff;
 | 
						||
            padding: 0;
 | 
						||
 | 
						||
            image {
 | 
						||
                width: 35rpx;
 | 
						||
                height: 35rpx;
 | 
						||
                object-fit: contain;
 | 
						||
            }
 | 
						||
 | 
						||
            view {
 | 
						||
                font-size: 24rpx;
 | 
						||
                height: 43rpx;
 | 
						||
                line-height: 43rpx;
 | 
						||
            }
 | 
						||
        }
 | 
						||
    }
 | 
						||
 | 
						||
    .endrightcontrol {
 | 
						||
        position: fixed;
 | 
						||
        right: 20rpx;
 | 
						||
        bottom: 168rpx;
 | 
						||
        width: 60rpx;
 | 
						||
        min-height: 40rpx;
 | 
						||
        z-index: 1000;
 | 
						||
        display: flex;
 | 
						||
 | 
						||
        image {
 | 
						||
            width: 40rpx;
 | 
						||
            height: 40rpx;
 | 
						||
            object-fit: contain;
 | 
						||
        }
 | 
						||
    }
 | 
						||
}
 | 
						||
</style>
 |