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

news2024/12/29 0:48:59

目录

  • 1 home.json
  • 2 goods-list组件
  • 3 goods-card组件
  • 总结

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

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

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文件并不复杂,复杂在了既调用了组件库中的组件,又自己封装了很多组件,而且是俄罗斯套娃,一层嵌套一层,这么个看要想用熟练一套模板也不是简单的事情。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/663488.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Git第二章、多人协作

一、多人协作一 目前&#xff0c;我们所完成的工作如下&#xff1a; • 基本完成 Git 的所有本地库的相关操作&#xff0c;git基本操作&#xff0c;分支理解&#xff0c;版本回退&#xff0c;冲突解决等等 • 申请码云账号&#xff0c;将远端信息clone到本地&#xff0c;以及推…

简单认识Nginx网络服务

文章目录 一、简介1、概括2、Nginx和Apache的差异3、Nginx优于Apache的优点 二、编译安装nginx 服务1、在线安装nginx2、 nginx编译安装&#xff08;1&#xff09;、关闭防火墙&#xff0c;将安装nginx所需软件包传到/opt目录下&#xff08;2&#xff09;、#nginx的配置及运行需…

基于分时电价和蓄电池控制策略用电优化研究(matlab代码)

目录 1 主要内容 温控负荷模型 蓄电池模型 2 部分代码 3 程序结果 4 下载链接 点击直达&#xff01; 1 主要内容 该程序复现《基于需求侧家庭能量管理系统用电优化研究》中第三章模型&#xff0c;题目是《基于分时电价和蓄电池控制策略用电优化研究》&#xff0c;该部…

Spring Boot初阶篇笔记

SpringBoot笔记 SpringBoot官方文档 一、SpringBoot的常用注解 ConfigurationProperties、PropertySource、ImportResource的区别 1.ConfigurationProperties: ConfigurationProperties:告诉SpringBoot将本类中的所有属性与配置文件中的相关属性进行绑定; 如&#xff1a;C…

Linux权限维持方法论

Linux权限维持方法论 1.创建超级用户2.SUID后门权限维持3.Strace监听后门4.rookit后门 1.创建超级用户 例如&#xff1a; 创建一个用户名guest&#xff0c;密码123456的root用户 useradd -p openssl passwd -1 -salt salt 123456 guest -o -u 0 -g root -G root -s /bin/bas…

【C语言初阶】带你玩转C语言中的数组,并逐步实现冒泡排序,三子棋,扫雷

君兮_的个人主页 勤时当勉励 岁月不待人 C/C 游戏开发 数组的使用 前言一维数组1.一维数组的定义数组的分类 2.数组的初始化第一种越界情况 3.数组的使用数组的下标&#xff1a;第二种越界情况 4.数组在内存中的存储 二维数组1.二维数组的创建2.二维数组的初始化3.二维数组的…

vue3新特性与vue2的不同点对比

前端必备工具推荐网站(免费图床、API和ChatAI等实用工具): http://luckycola.com.cn/ 一、vue3是什么? Vue 是一款用于构建用户界面的 JavaScript 框架。它基于标准 HTML、CSS 和 JavaScript 构建&#xff0c; 并提供了一套声明式的、组件化的编程模型&#xff0c;帮助你高效…

计算机网络管理-实验4(三) 使用Wireshark 抓取MIB-2中sysUpTime对象的SNMP报文管理信息结构

⬜⬜⬜ &#x1f430;&#x1f7e7;&#x1f7e8;&#x1f7e9;&#x1f7e6;&#x1f7ea;(*^▽^*)欢迎光临 &#x1f7e7;&#x1f7e8;&#x1f7e9;&#x1f7e6;&#x1f7ea;&#x1f430;⬜⬜⬜ ✏️write in front✏️ &#x1f4dd;个人主页&#xff1a;陈丹宇jmu &am…

【win11+Visual Studio 2019 配置 PCL 1.12.1 的经验总结分享】

点云pc库的下载与安装参考另外一篇文章&#xff0c;链接&#xff1a; https://blog.csdn.net/weixin_47869094/article/details/131270772?spm1001.2014.3001.5501 各种教程里面这都很好&#xff0c;就不赘述了&#xff0c;当然&#xff0c;这里也给出一个个人认为不错的安装…

移动互联网行业就业率除车载外,还有“该”的岗位在暴涨!

从2013年进入到移动互联网时代&#xff0c;在Android系统广泛应用在手机厂商中&#xff0c;App承载了我们生活工作的方方面面&#xff0c;原来需要在PC或者线下才能做的事情&#xff0c;现在点一点手指就可以做到了。这类方便也带来更多的系统安全危害。正所谓魔高一尺道高一丈…

一.《UE4奥丁》人物最大属性

​寻找突破口 1.续上节课,此时看到标题,有基础的同学第一反应就是,老师用CE搜索血量,通过改变就能找到&#xff01; 2.额,有这么简单吗&#xff01; 3.既然写了这个帖子,肯定是有原因的 4.为了方便学习,我们就先按照同学们的思路来试一试,能不能找到最大属性,比如最大血&am…

mysql索引方面的知识

1. 查看表的索引 show index from 表名 ex: 重点看Key_name这一列的值, 可以看到只有一个主键, 目前该表还没有创建索引, 接下来进行索引的创建 2.给表添加索引 create index idx_time_classtype_sip_sport on event_20230508(time,classtype,sip,sport) 说明: 上面index后…

【论文精读】DELS-MVS

今天读的是发表在WACV2023上的文章&#xff0c;第一作者来自于格拉茨技术大学。 文章链接&#xff1a;DELS-MVS: Deep Epipolar Line Search for Multi-View Stereo 文章目录 Abstract1. Introduction2. Related Work3. Algorithm3.1 Depth estimation via epipolar residual3.…

【换脸详细教程】手把手教你进行AI换脸:换脸流程及源码详解

目录 1. 换脸基本原理2 人脸检测及可视化3. 人脸轮廓点检测及可视化4. 人脸图像变换--仿射变换5. 生成遮罩并直接替换人脸6. 人脸颜色校正 最近AI换脸貌似比较火爆&#xff0c;就稍微研究了一下相关了内容。AI换脸是一个娱乐性比较强的应用&#xff0c;这种错位感让人觉得非常有…

搭建高性能数据库集群之一:主从复制

一、概述 1. 数据库主从概念、优点、用途   主从数据库是什么意思呢&#xff0c;主是主库的意思&#xff0c;从是从库的意思。数据库主库对外提供读写的操作&#xff0c;从库对外提供读的操作。 数据库为什么需要主从架构呢&#xff1f;   高可用&#xff0c;实时灾备&am…

手把手教你撸一个接口自动化测试平台(一)

项目构思&#xff1a;开发一个web版的接口自动化测试平台 功能包括&#xff1a;接口导入、自动化测试用例生成、自动化测试报告、可持续集成 项目框架&#xff1a;django vue mysql 技术要求&#xff1a;熟悉django后台开发、熟悉vue开发 第一节&#xff1a;搭建django 项…

被ChatGPT戏耍的周末

被ChatGPT戏耍的周末 1. 被ChatGPT戏耍全过程2. 拆穿ChatGPT的把戏3. AIGC与内容安全 1. 被ChatGPT戏耍全过程 电动垂直起降飞行器&#xff08;eVTOL&#xff0c;Electric Vertical Takeoff and Landing&#xff09;技术越来越成熟&#xff0c;为了解下相关产品我周末打开了Cha…

OpenGLES:相机实时滤镜四宫格、九宫格

一.概述 今天继续OpenGLES的学习 今天在之前博文《OpenGLES&#xff1a;GLSurfaceView实现Android Camera预览》 的基础上&#xff0c;使用OpenGLES实现相机 四宫格滤镜 和 九宫格滤镜。 二.四宫格 先定义几个名词&#xff1a; 之前博文中实现的相机普通预览叫&#xff1a;…

【人工智能】“AI + 算力 = 最强龙头”,你怎么看?

文章目录 一、AI 与算力相辅相成1.1 AI 和算力的概念1.2 AI 和算力的应用领域1.3 AI 需要算力的支持1.4 AI 和算力的结合带来的巨大价值1.4.1 人脸识别1.4.2 语音识别1.4.3 自动驾驶1.4.4 医疗诊断1.4.5 自然语言处理 二、AI算力催生“最强龙头”2.1 “最强龙头”的概念2.2 AI …

Unity核心4——SpriteShape

Sprite Shape 是精灵形状的意思&#xff0c;它主要是方便我们以节约美术资源为前提&#xff0c;制作 2D 游戏场景地形或者背景的 ​ 在 Window --> Package Manager 中搜索 2D&#xff0c;选择 2D Sprite Shape&#xff0c;导入项目 一、Sprite Shape Profile 精灵形状概述文…