TDesign电商小程序模板解析02-首页功能

本文涉及的产品
全局流量管理 GTM,标准版 1个月
云解析 DNS,旗舰版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
简介: TDesign电商小程序模板解析02-首页功能



上一篇我们搭建了底部的导航条,这一篇来拆解一下首页的功能。首页有如下功能

  • 可以进行搜索
  • 显示轮播图
  • 横向可拖动的页签
  • 图文卡片列表

1 home.json

因为是要使用组件库的组件搭建页面,自然是先需要引入自定义组件

{
  "navigationBarTitleText": "首页",
  "onReachBottomDistance": 10,
  "backgroundTextStyle": "light",
  "enablePullDownRefresh": true,
  "usingComponents": {
    "t-search": "tdesign-miniprogram/search/search",
    "t-loading": "tdesign-miniprogram/loading/loading",
    "t-swiper": "tdesign-miniprogram/swiper/swiper",
    "t-swiper-nav": "tdesign-miniprogram/swiper-nav/swiper-nav",
    "t-image": "/components/webp-image/index",
    "t-icon": "tdesign-miniprogram/icon/icon",
    "t-toast": "tdesign-miniprogram/toast/toast",
    "t-tabs": "tdesign-miniprogram/tabs/tabs",
    "t-tab-panel": "tdesign-miniprogram/tab-panel/tab-panel",
    "goods-list": "/components/goods-list/index",
    "load-more": "/components/load-more/index"
  }
}

引入的组件还是不少的,贴入配置后发现控制台报错,因为这里既使用到了TDesign中的组件,也使用到了自定义组件,我们需要将报错的组件,自己搭建一下。

其实解决问题就像俄罗斯套娃一样,拿走一个里边还有一个,直到你拿到最后一个才可以

2 goods-list组件

选中components文件夹,右键新建一个文件夹

输入goods-list,然后在goods-list文件夹上右键,点击新建Page

然后输入index,自动生成四个文件,index.wxml、index.wxss、index.json、index.js

自定义组件也是包含四个文件,要依次看模板的代码

index.json

{
    "component": true,
    "usingComponents": {
        "goods-card": "/components/goods-card/index"
    }
}

这里goods-list又继续引用了goods-card组件

index.js

Component({
  externalClasses: ['wr-class'],
  properties: {
    goodsList: {
      type: Array,
      value: [],
    },
    id: {
      type: String,
      value: '',
      observer: (id) => {
        this.genIndependentID(id);
      },
    },
    thresholds: {
      type: Array,
      value: [],
    },
  },
  data: {
    independentID: '',
  },
  lifetimes: {
    ready() {
      this.init();
    },
  },
  methods: {
    onClickGoods(e) {
      const { index } = e.currentTarget.dataset;
      this.triggerEvent('click', { ...e.detail, index });
    },
    onAddCart(e) {
      const { index } = e.currentTarget.dataset;
      this.triggerEvent('addcart', { ...e.detail, index });
    },
    onClickGoodsThumb(e) {
      const { index } = e.currentTarget.dataset;
      this.triggerEvent('thumb', { ...e.detail, index });
    },
    init() {
      this.genIndependentID(this.id || '');
    },
    genIndependentID(id) {
      if (id) {
        this.setData({ independentID: id });
      } else {
        this.setData({
          independentID: `goods-list-${~~(Math.random() * 10 ** 8)}`,
        });
      }
    },
  },
});

自定义组件的externalClasses表示外部样式类,可以在引用的时候传入样式来改变组件的样式。properties表示组件对外暴露的属性,可以根据组件的需要进行设置。method表示组件可以响应的事件,看目前的设置事件是和电商业务相关的,具体是什么含义,我们在调用的时候再分析

index.wxml

<view class="goods-list-wrap wr-class" id="{{independentID}}">
  <block wx:for="{{goodsList}}" wx:for-item="item" wx:key="index">
    <goods-card
      id="{{independentID}}-gd-{{index}}"
      data="{{item}}"
      currency="{{item.currency || '¥'}}"
      thresholds="{{thresholds}}"
      class="goods-card-inside"
      data-index="{{index}}"
      bind:thumb="onClickGoodsThumb"
      bind:click="onClickGoods"
      bind:add-cart="onAddCart"
    />
  </block>
</view>

这是组件的内容部分,他又使用了一个goods-card组件

index.wxss

.goods-list-wrap {
  display: flex;
  flex-flow: row wrap;
  justify-content: space-between;
  padding: 0;
  background: #fff;
}

样式部分还是很简单的,他是设置了一个流式布局,元素是按行排列,要求自动换行,水平对齐是两端对齐,没有内边距并设置了一定的背景色

3 goods-card组件

在components文件夹下再新建一个goods-card文件夹

然后在goods-card文件夹新建一个Page

index.json

{
    "component": true,
    "usingComponents": {
        "price": "/components/price/index",
        "t-icon": "tdesign-miniprogram/icon/icon",
        "t-image": "/components/webp-image/index"
    }
}

好家伙真还是俄罗斯套娃,这个组件又套了两个组件,需要继续新建

index.js

Component({
  options: {
    addGlobalClass: true,
  },
  properties: {
    id: {
      type: String,
      value: '',
      observer(id) {
        this.genIndependentID(id);
        if (this.properties.thresholds?.length) {
          this.createIntersectionObserverHandle();
        }
      },
    },
    data: {
      type: Object,
      observer(data) {
        if (!data) {
          return;
        }
        let isValidityLinePrice = true;
        if (data.originPrice && data.price && data.originPrice < data.price) {
          isValidityLinePrice = false;
        }
        this.setData({ goods: data, isValidityLinePrice });
      },
    },
    currency: {
      type: String,
      value: '¥',
    },
    thresholds: {
      type: Array,
      value: [],
      observer(thresholds) {
        if (thresholds && thresholds.length) {
          this.createIntersectionObserverHandle();
        } else {
          this.clearIntersectionObserverHandle();
        }
      },
    },
  },
  data: {
    independentID: '',
    goods: { id: '' },
    isValidityLinePrice: false,
  },
  lifetimes: {
    ready() {
      this.init();
    },
    detached() {
      this.clear();
    },
  },
  pageLifeTimes: {},
  methods: {
    clickHandle() {
      this.triggerEvent('click', { goods: this.data.goods });
    },
    clickThumbHandle() {
      this.triggerEvent('thumb', { goods: this.data.goods });
    },
    addCartHandle(e) {
      const { id } = e.currentTarget;
      const { id: cardID } = e.currentTarget.dataset;
      this.triggerEvent('add-cart', {
        ...e.detail,
        id,
        cardID,
        goods: this.data.goods,
      });
    },
    genIndependentID(id) {
      let independentID;
      if (id) {
        independentID = id;
      } else {
        independentID = `goods-card-${~~(Math.random() * 10 ** 8)}`;
      }
      this.setData({ independentID });
    },
    init() {
      const { thresholds, id } = this.properties;
      this.genIndependentID(id);
      if (thresholds && thresholds.length) {
        this.createIntersectionObserverHandle();
      }
    },
    clear() {
      this.clearIntersectionObserverHandle();
    },
    intersectionObserverContext: null,
    createIntersectionObserverHandle() {
      if (this.intersectionObserverContext || !this.data.independentID) {
        return;
      }
      this.intersectionObserverContext = this.createIntersectionObserver({
        thresholds: this.properties.thresholds,
      }).relativeToViewport();
      this.intersectionObserverContext.observe(
        `#${this.data.independentID}`,
        (res) => {
          this.intersectionObserverCB(res);
        },
      );
    },
    intersectionObserverCB() {
      this.triggerEvent('ob', {
        goods: this.data.goods,
        context: this.intersectionObserverContext,
      });
    },
    clearIntersectionObserverHandle() {
      if (this.intersectionObserverContext) {
        try {
          this.intersectionObserverContext.disconnect();
        } catch (e) {}
        this.intersectionObserverContext = null;
      }
    },
  },
});

这个组件里边的代码会更复杂一点

index.wxml

<view
  id="{{independentID}}"
  class="goods-card"
  bind:tap="clickHandle"
  data-goods="{{ goods }}"
>
  <view class="goods-card__main">
    <view class="goods-card__thumb" bind:tap="clickThumbHandle">
      <t-image
        wx:if="{{ !!goods.thumb }}"
        t-class="goods-card__img"
        src="{{ goods.thumb }}"
        mode="aspectFill"
        lazy-load
      />
    </view>
    <view class="goods-card__body">
      <view class="goods-card__upper">
        <view wx:if="{{ goods.title }}" class="goods-card__title">
          {{ goods.title }}
        </view>
        <view wx:if="{{ goods.tags && !!goods.tags.length }}" class="goods-card__tags">
          <view
            wx:for="{{ goods.tags }}"
            wx:key="index"
            wx:for-item="tag"
            class="goods-card__tag"
            data-index="{{index}}"
          >
            {{tag}}
          </view>
        </view>
      </view>
      <view class="goods-card__down">
        <price
          wx:if="{{ goods.price }}"
          wr-class="spec-for-price"
          symbol-class="spec-for-symbol"
          symbol="{{currency}}"
          price="{{goods.price}}"
        />
        <price
          wx:if="{{ goods.originPrice && isValidityLinePrice }}"
          wr-class="goods-card__origin-price"
          symbol="{{currency}}"
          price="{{goods.originPrice}}"
          type="delthrough"
        />
        <t-icon
          class="goods-card__add-cart"
          prefix="wr"
          name="cartAdd"
          id="{{independentID}}-cart"
          data-id="{{independentID}}"
          catchtap="addCartHandle"
          size="48rpx"
          color="#FA550F"
        />
      </view>
    </view>
  </view>
</view>

index.wxss

.goods-card {
  box-sizing: border-box;
  font-size: 24rpx;
  border-radius: 0 0 16rpx 16rpx;
  border-bottom: none;
}
.goods-card__main {
  position: relative;
  display: flex;
  line-height: 1;
  padding: 0;
  background: transparent;
  width: 342rpx;
  border-radius: 0 0 16rpx 16rpx;
  align-items: center;
  justify-content: center;
  margin-bottom: 16rpx;
  flex-direction: column;
}
.goods-card__thumb {
  flex-shrink: 0;
  position: relative;
  width: 340rpx;
  height: 340rpx;
}
.goods-card__thumb:empty {
  display: none;
  margin: 0;
}
.goods-card__img {
  display: block;
  width: 100%;
  height: 100%;
  border-radius: 16rpx 16rpx 0 0;
  overflow: hidden;
}
.goods-card__body {
  display: flex;
  flex: 1 1 auto;
  background: #fff;
  border-radius: 0 0 16rpx 16rpx;
  padding: 16rpx 24rpx 18rpx;
  flex-direction: column;
}
.goods-card__upper {
  display: flex;
  flex-direction: column;
  overflow: hidden;
  flex: 1 1 auto;
}
.goods-card__title {
  flex-shrink: 0;
  font-size: 28rpx;
  color: #333;
  font-weight: 400;
  display: -webkit-box;
  height: 72rpx;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
  overflow: hidden;
  word-break: break-word;
  line-height: 36rpx;
}
.goods-card__tags {
  display: flex;
  flex-direction: row;
  flex-wrap: wrap;
  margin: 8rpx 0 0 0;
}
.goods-card__tag {
  color: #fa4126;
  background: transparent;
  font-size: 20rpx;
  border: 1rpx solid #fa4126;
  padding: 0 8rpx;
  border-radius: 16rpx;
  line-height: 30rpx;
  margin: 0 8rpx 8rpx 0;
  display: block;
  overflow: hidden;
  white-space: nowrap;
  word-break: keep-all;
  text-overflow: ellipsis;
}
.goods-card__down {
  display: flex;
  position: relative;
  flex-direction: row;
  justify-content: flex-start;
  align-items: baseline;
  line-height: 32rpx;
  margin: 8rpx 0 0 0;
}
.goods-card__origin-price {
  white-space: nowrap;
  font-weight: 700;
  order: 2;
  color: #bbbbbb;
  font-size: 24rpx;
  margin: 0 0 0 8rpx;
}
.goods-card__add-cart {
  order: 3;
  margin: auto 0 0 auto;
  position: absolute;
  bottom: 0;
  right: 0;
}
.spec-for-price {
  font-size: 36rpx;
  white-space: nowrap;
  font-weight: 700;
  order: 1;
  color: #fa4126;
  margin: 0;
}
.spec-for-symbol {
  font-size: 24rpx;
}

总结

看首页功能,其实看wxml文件并不复杂,复杂在了既调用了组件库中的组件,又自己封装了很多组件,而且是俄罗斯套娃,一层嵌套一层,这么个看要想用熟练一套模板也不是简单的事情。

相关文章
|
1月前
|
人工智能 监控 算法
销售易CRM:功能与优势全解析
销售易CRM是国内领先的客户关系管理(CRM)系统,提供强大的销售管理、全方位客户管理、丰富的营销自动化工具、智能AI赋能及灵活的开放性平台。其功能涵盖线索获取、商机管理、客户画像、营销活动策划、智能预测等,支持企业高效管理客户、优化业务流程、提升销售效率和客户满意度。通过灵活的二次开发和API接口,销售易CRM可无缝集成企业现有系统,助力企业在数字化转型中实现业绩高质量增长。
|
1月前
|
弹性计算 运维 安全
优化管理与服务:操作系统控制平台的订阅功能解析
本文介绍了如何通过操作系统控制平台提升系统效率,优化资源利用。首先,通过阿里云官方平台开通服务并安装SysOM组件,体验操作系统控制平台的功能。接着,详细讲解了订阅管理功能,包括创建订阅、查看和管理ECS实例的私有YUM仓库权限。订阅私有YUM仓库能够集中管理软件包版本、提升安全性,并提供灵活的配置选项。最后总结指出,使用阿里云的订阅和私有YUM仓库功能,可以提高系统可靠性和运维效率,确保业务顺畅运行。
|
30天前
|
人工智能 API 语音技术
HarmonyOS Next~鸿蒙AI功能开发:Core Speech Kit与Core Vision Kit的技术解析与实践
本文深入解析鸿蒙操作系统(HarmonyOS)中的Core Speech Kit与Core Vision Kit,探讨其在AI功能开发中的核心能力与实践方法。Core Speech Kit聚焦语音交互,提供语音识别、合成等功能,支持多场景应用;Core Vision Kit专注视觉处理,涵盖人脸检测、OCR等技术。文章还分析了两者的协同应用及生态发展趋势,展望未来AI技术与鸿蒙系统结合带来的智能交互新阶段。
113 31
|
27天前
|
供应链 监控 搜索推荐
反向海淘代购独立站:功能解析与搭建指南
“反向海淘”指海外消费者购买中国商品的现象,体现了中国制造的创新与强大。国产商品凭借高性价比和丰富功能,在全球市场备受欢迎。跨境电商平台的兴起为“反向海淘”提供了桥梁,而独立站因其自主权和品牌溢价能力逐渐成为趋势。一个成功的反向海淘代购独立站需具备多语言支持、多币种支付、物流跟踪、商品展示、购物车管理等功能,并通过SEO优化、社交媒体营销等手段提升运营效果。这不仅助力中国企业开拓海外市场,还推动了品牌全球化进程。
68 19
|
21天前
|
存储 前端开发 JavaScript
调用DeepSeek API增强版纯前端实现方案,支持文件上传和内容解析功能
本方案基于DeepSeek API增强版,提供纯前端实现的文件上传与内容解析功能。通过HTML和JavaScript,用户可选择文件并调用API完成上传及解析操作。方案支持多种文件格式(如PDF、TXT、DOCX),具备简化架构、提高响应速度和增强安全性等优势。示例代码展示了文件上传、内容解析及结果展示的完整流程,适合快速构建高效Web应用。开发者可根据需求扩展功能,满足多样化场景要求。
|
23天前
|
SQL 运维 监控
高效定位 Go 应用问题:Go 可观测性功能深度解析
为进一步赋能用户在复杂场景下快速定位与解决问题,我们结合近期发布的一系列全新功能,精心梳理了一套从接入到问题发现、再到问题排查与精准定位的最佳实践指南。
|
1月前
|
算法 前端开发 定位技术
地铁站内导航系统解决方案:技术架构与核心功能设计解析
本文旨在分享一套地铁站内导航系统技术方案,通过蓝牙Beacon技术与AI算法的结合,解决传统导航定位不准确、路径规划不合理等问题,提升乘客出行体验,同时为地铁运营商提供数据支持与增值服务。 如需获取校地铁站内智能导航系统方案文档可前往文章最下方获取,如有项目合作及技术交流欢迎私信我们哦~
100 1
|
1月前
|
JSON 自然语言处理 前端开发
WebSocket调试工具深度对比:Postman与Apipost功能实测解析
本文深入对比了Postman与Apipost两款WebSocket调试工具。作为实时通讯系统工程师,作者在开发智能客服系统时遇到了传统工具调试复杂、文档管理不便的问题。通过引入Apipost的智能连接池、消息分组管理和自动化文档生成等功能,实现了多环境自动切换、消息分类和接口文档自动生成,极大提升了调试效率和团队协作效果。最终,使用Apipost使接口调试时间减少40%,文档维护成本降低70%,跨团队沟通效率提升50%。
|
1月前
|
人工智能 搜索推荐 数据挖掘
销售易CRM:功能与优势全解析
销售易CRM是国内领先的客户关系管理系统,提供从线索获取到订单成交的完整销售漏斗管理,涵盖销售、客户、营销管理和AI赋能等功能。其强大的销售管理功能包括线索与商机管理、销售预测等;全方位客户管理实现360度客户视图;丰富的营销自动化工具支持多渠道营销活动;智能AI技术提升销售效率和客户满意度;灵活的开放性平台满足定制化需求;现代化界面设计简洁直观,支持多设备访问;移动端功能齐全,协同工具丰富;优质的客户服务确保快速响应和技术支持。销售易CRM助力企业优化业务流程,推动销售增长。
|
2月前
|
JSON 前端开发 安全
WebSocket调试工具深度对比:Postman与Apipost功能实测解析
如果你在寻找既能搞定WebSocket调试,又能完美管理文档的工具,不妨试试Apipos!
54 1

热门文章

最新文章

推荐镜像

更多