微信小程序 动态渲染

时间:2025-01-21 12:39:06

一、 xml文件

根据template is 属性判断是哪一个组件
根据elementType来映射不同组件


<import src="./components/goods/"/>
<import src="./components/icon/"/>
<import src="./components/img/"/>
<import src="./components/label/"/>
<import src="./components/placeholder/"/>
<import src="./components/tab/"/>
<import src="./components/title/"/>
<import src="./components/welfare/"/>

<view class="container" style="background: {{bgColor}}">
  <header title="{{title}}" fixed/>

  <block wx:for="{{components}}" wx:for-index="idx" wx:for-item="item" wx:key="componentId">
    <template is="{{ componentMap[] }}" data="{{...item, labelGoods, subLabels, customBar, bgColor}}"/>
  </block>
  
</view>

二、 js文件


import { getHomeData } from '../../../service/tabbar/'

const app = getApp()

Page({
  data: {
    customBar: app.globalData.customBar,
    title: '',
    bgColor: '',
    components: [],
    componentMap: {
      1: 'tab',
      2: 'icon',
      3: 'img',
      4: 'welfare',
      5: 'label',
      6: 'title',
      7: 'placeholder',
      8: 'goods',
    },
    

    activeTab: 0,// tab 组件专属
    activeLabel: 0,// label组件专属
    activeSubLabel: 0,// label组件专属
    
    subLabels: [],// 子标签
    labelGoods: [1, 2, 1, 2, 3, 4],// label 的商品列表

  },
  // 获取首页数据
  async fetchHomeData(){
    let res = await getHomeData()
    if(!res)return false;
    let { title, bgColor, components } = res;
    console.log('components', components)
    components.forEach(v=>{
      if(v.elementType === 5){
        if(v.list.length){
          this.setData({subLabels: v.list[0]['subTags'] || []})
        }
        
        // 调接口获取商品列表
      }
    })
    this.setData({
      title,
      bgColor,
      components,
    })
  },

  onTabClick(e) {
    const index = e.detail.index
    if(index !== this.data.activeTab){
      const tabs = e.target.dataset.tabs || [];
      this.setData({ 
        activeTab: index 
      })
    }
    
  },

  onIconClick(e){
    let info = e.currentTarget.dataset.info || {};
    console.log(info)
    let { linkType, linkUrl } = info
    if(linkType === 4){
      wx.navigateTo({
        url: `/pages/webview/index?url=${linkUrl}`
      })
    }
  },

  onImgClick(e){
    
    let info = e.currentTarget.dataset.info || {};
    console.log(info)
    let { linkType, linkUrl } = info
    if(linkType === 4){
      wx.navigateTo({
        url: `/pages/webview/index?url=${linkUrl}`
      })
    }
  },

  onLabelClick(e){
    console.log('e', e)
  },
  
  onSubLabelClick(e){
    console.log('e', e)
  },

  jump(e){
    wx.switchTab({
      url: '/pages/tabBar/category/index'
    })
  },

  onShow(){
    this.fetchHomeData();
  }
})

三、 json文件

{
  "navigationStyle": "custom",
  "usingComponents": {
    "header": "/components/Header/index",
    "goods-item": "/components/GoodsItem/index",
    "mp-tabs": "@miniprogram-component-plus/tabs"
  },
  "onReachBottomDistance": 50
}

四、 css文件



.container{
  position: relative;
  min-height: 100vh;
}
.tabs-content{
  height: 0rpx;
}

/* tab组件 */
.tab{
  position: relative;
  height: 98rpx;
  background: #000;
}

.tab_bg--img{
  position: absolute;
  top: 0rpx;
  left: 0rpx;
  width: 750rpx;
  height: 284rpx;
}
.tab-class{
  height: 80rpx;
  margin: 0 30rpx;
  padding-top: 20rpx;
  font-size: 32rpx;
  color: #fff;
}
.tab-active{
  position: relative;
}
.tab-active::before{
  position: absolute;
  content: '';
  bottom: 0;
  left: 50%;
  transform: translateX(-50%);
  width: 46rpx;
  height: 4rpx;
  background: #ffffff;
  border-radius: 2rpx;
}
.weui-tabs-bar__wrp{
  background: transparent !important;
}

.tab-container{
  display: flex;
  justify-content: space-between;
  width: 100%;
  height: 98rpx;
  overflow: hidden;
}
.tab-scroll{
  width: 670rpx;
}
.tab-more{
  height: 98rpx;
  width: 79rpx;
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 2;
  box-shadow: -7rpx 0 20rpx 3rpx rgba(217,52,90,0.5); 
}
.tab-more image{
  width: 32rpx;
  height: 32rpx;
}

/* placeholder组件 */
.placeholder{
  position: relative;
  width: 100%;
  height: 20rpx;
}


/* icon组件 */
.icon{
  position: relative;
  display: flex;
  flex-wrap: wrap;
  width: 714rpx;
  margin: 0 auto;
  background-color: #fff;
  border-radius: 30rpx;
  padding-top: 20rpx;
}
.icon-item{
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  width: 136rpx;
  margin-bottom: 30rpx;
  margin-right: 9rpx;
}
.icon-item:nth-child(5n+5){
  margin-right: 0rpx;
}

.icon-img{
  width: 96rpx;
  height: 80rpx;
}


.icon-text{
  font-size: 24rpx;
  font-weight: 400;
  color: #333333;
}

/* img组件 */
.banner{
  position: relative;
  width: 714rpx;
  height: 301rpx;
  margin: 0 auto;
  border-radius: 30rpx;
  transform: translateY(0);
  overflow: hidden;
}

.banner-img{
  width: 714rpx;
  height: 301rpx;
}

/* title组件 */
.title{
  width: 750rpx;
  height: 76rpx;
  margin: 0 auto;
  overflow: hidden;
  display: flex;
  justify-content: space-between;
}
.title-bg{
  width: 750rpx;
  height: 76rpx;
}
.title-left{

}
.title-right{

}

/* 特价福利组件 */
.welfare{
  width: 714rpx;
  height: 225rpx;
  padding: 0 20rpx;
  background-color: #ffffff;
  margin: 0 auto;
  border-bottom-left-radius: 30rpx;
  border-bottom-right-radius: 30rpx;
  overflow: hidden;
}
.welfare-scroll{
  width: 100%;
  height: 100%;
  white-space: nowrap;
}

.welfare-goods-item{
  display: inline-block;
  width: 180rpx;
  height: 224rpx;
  margin-right: 12rpx;
}
.welfare-goods-img{
  position: relative;
  text-align: center;
}
.welfare-goods-img image{
  width: 148rpx;
  height: 148rpx;
}
.welfare-goods-tag{
  position: absolute;
  bottom: 0rpx;
  left: 50%;
  display: flex;
  justify-content: center;
  align-items: center;
  height: 30rpx;
  max-width: 180rpx;
  text-overflow: ellipsis;
  white-space: nowrap;
  overflow: hidden;
  padding: 0 7rpx;
  background: #eca482;
  border-radius: 5rpx;
  transform: translateX(-50%);
  font-size: 22rpx;
  font-weight: 400;
  color: #ffffff;
}
.welfare-goods-price{
  text-align: center;
}
.welfare-goods-price text:first-child{
  font-size: 24rpx;
  color: #e85148;
}
.welfare-goods-price text:last-child{
  font-size: 28rpx;
  font-weight: 500;
  color: #e85148;
}
/* 商品组件 */
.goods{
  
}

/* 标签组件 */
.label{
  position: sticky;
  width: 100%;
  height: 129rpx;
  z-index: 10;
}

.label-goods__list{
  width: 100%;
  border-top-left-radius: 30rpx;
  border-top-right-radius: 30rpx;
  overflow: hidden;
  background-color: #ffffff;
  min-height: calc(100vh - 260rpx);
  padding-top: 24rpx;
}
.label-class{
  padding: 0 96rpx;
  height: 60rpx;
  border-bottom-width: 6rpx !important;
  padding-top: 10rpx;
}
.label-class text{
  font-size: 28rpx;
  color: #333;
}

.label-tabs .weui-tabs-bar__item{
  position: relative;
}
.label-tabs .weui-tabs-bar__item:not(:last-child)::after{
  position: absolute;
  content: '';
  top: 24rpx;
  right: 0;
  width: 1rpx;
  height: 26rpx;
  background-color: #d5d5d5;
}
.label-active{
  position: relative;
}
.label-active::before{
  position: absolute;
  content: '';
  bottom: 1rpx;
  left: 50%;
  width: 63rpx;
  height: 8rpx;
  background: linear-gradient(102deg,#fb4b3d, #fd8044);
  box-shadow: 0rpx 1rpx 7rpx 4rpx rgba(246,66,52,0.33); 
  border-radius: 4rpx;
  transform: translateX(-50%);
  z-index: -1;
}

.label-sub__tabs{

}

.sub-label-class{
  display: flex !important;
  align-items: center;
  justify-content: center;
  height: 40rpx;
  background-color: #fff;
  margin-left: 18rpx;
  margin: 14rpx 0 14rpx 18rpx;
  border: 2rpx solid #fff !important;
  border-radius: 10rpx;
  padding: 0 23rpx;
  font-size: 24rpx;
  font-weight: 400;
  box-sizing: border-box;
}
.label-sub__tabs .weui-tabs-bar__item:last-child .sub-label-class{
  margin-right: 18rpx;
}
.sub-label-active{
  background-color: #fcefee;
  border: 2rpx solid #e85148 !important;
  color: #E85148;
}

五、 文件目录

在这里插入图片描述