Апплет Mimic Lite WeChat

Апплет WeChat

Программа WeChat Mini - Xiaomi Lite

Заранее заявлено, что это апплет WeChat, имитирующий Xiaomi Lite.

Я сейчас учусь на третьем и четвертом курсе, эта небольшая программка заняла у меня много времени, и я в принципе написал все функции, которые можно было написать. С менталитетом обмена открытым исходным кодом я изо всех сил стараюсь рассказать вам, как и почему я написал эту небольшую программу, и рассказать вам подробно. Зачем стараться изо всех сил? Это потому, что я не очень хорошо говорю, может быть, это не очень ясно, поэтому я могу только стараться изо всех сил.

Предварительный просмотр проекта

Эффект, достигаемый ок, таков.

Использование инструментов

  1. easy-moke
  2. VSCode
  3. Инструменты разработчика мини-программы WeChat
  4. (Библиотека векторных иконок Alibaba)

Каталог файлов

├<assets>
│  ├<images>
├<components>
│  ├<goodList>
│  ├<icon>
│  ├<tabbar>
│  ├<userList>
├<pages>
│  ├<cart>
│  ├<category>
│  ├<deleteGoods>
│  ├<find>
│  ├<goodDetails>
│  ├<index>
│  ├<selectGoods>
│  ├<user>
├<utils>
│  └util.js
├<weui>
│  └weui.wxss
├<wxapi>
│  ├Api.js
│  ├main.js
│  └mock.js


Для новичков можно писать сразу после получения чертежа конструкции, на самом деле это нехорошо, написанный код будет иметь много повторяющегося кода, что не способствует последующему сопровождению. Поэтому какой-то общий код следует инкапсулировать и вызывать сразу после этого, и его будет удобнее поддерживать позже.

упаковка API

Если наш внешний интерфейс хочет получить данные страницы, нам нужно отправить HTTP-запрос на интерфейс API, предоставленный серверной частью, и получить нужные данные из интерфейса API. В апплете WeChat официальный представитель WeChat предоставляет нам методwx.requestзапросить.

Программе нужно много HTTP-запросов, если на каждый запрос писать wx.request, то код, написанный таким образом, будет выглядеть очень многословно, и другие будут уставать, глядя на наш код, что для нас нехорошо. модификации. Поэтому для чистоты кода и удобства последующих модификаций. Я инкапсулирую все запросы API в файловую директорию wxapi.

// Api.js
const banners = 'https://www.easy-mock.com/mock/5cf9c392006feb28c7eedf28/banners'

const navdata = 'https://www.easy-mock.com/mock/5cf9c392006feb28c7eedf28/navdata'

const goodList = 'https://www.easy-mock.com/mock/5cf9c392006feb28c7eedf28/goodList'

const category = 'https://www.easy-mock.com/mock/5cf9c392006feb28c7eedf28/category'

const findData = 'https://www.easy-mock.com/mock/5cf9c392006feb28c7eedf28/findData'

const userData = 'https://www.easy-mock.com/mock/5cf9c392006feb28c7eedf28/userData'

const goodDetail = 'https://www.easy-mock.com/mock/5cf9c392006feb28c7eedf28/goodDetail'

const QQ_MAP_KEY = 'NNFBZ-6DRCP-IRLDU-VEQ4F-TXLP2-PFBEN'

const MAPURL = 'https://apis.map.qq.com/ws/geocoder/v1/'

module.exports = {
  banners,
  navdata,
  goodList,
  category,
  findData,
  userData,
  goodDetail,
  QQ_MAP_KEY,
  MAPURL
}
import * as MOCK from "./mock"
import * as API from "./Api"
const request = (url,mock = true,data) => {
  let _url = url
  return new Promise((resolve, reject) => {
    if (mock) {
      let res = {
        statusCode: 200,
        data: MOCK[_url]
      }
      if (res && res.statusCode === 200 && res.data) {
        resolve(res.data)
      } else {
        reject(res)
      }
    } else {
      wx.request({
        url: url,
        data,
        success(request) {
          resolve(request.data)
        },
        fail(error) {
          reject(error)
        }
      })
    }
  });
}
// showLoading
const showLoading = () => {
  wx.showLoading({
    title: '数据加载中',
    mask: true,
  });  
}
// 获取地理位置
const geocoder = (lat, lon) => {
  return request(API.MAPURL,false,{
    location: `${lat},${lon}`,
    key: API.QQ_MAP_KEY,
    get_poi: 0
  })
}
module.exports = {
  getBanners: () => {
    // return request('banners')
    return request(API.banners,false)  //首页 banners 
  },
  getNavData: () => {
    // return request('navdata')
    return request(API.navdata,false) //首页 navdata 
  },
  getGoodList: () => {
    // return request('goodList')
    return request(API.goodList,false)  //首页 商品列表
  },
  getCategroy: () => {
    // return request('category')
    return request(API.category,false)  //分类页面
  },
  getFindData: () => {
    // return request('findData')
    return request(API.findData,false)  //发现 页面
  },
  getUserData: () => {
    // return request('userData')
    return request(API.userData,false)  // 我的 页面
  },
  getGoodDetail: () => {
    // return request('goodDetail')
    return request(API.goodDetail,false)  //商品详情
  },
  showLoading,
  geocoder
}

Увидев это, могут возникнуть некоторые сомнения, зачем я добавляю false в каждый запрос?

Это связано с тем, что в начале написания этого апплета WeChat я не использовал easy-mock для имитации данных HTTP-запроса. Я поместил все поддельные данные в файл mock.js. Затем используйте **return request('banners')**, чтобы получить нужные мне данные.

После того, как API упакован, как его вызвать? В качестве примера возьму данные баннера главной страницы

// index.js 
const WXAPI = require('../../wxapi/main')

onLoad: function (options) {
    WXAPI.showLoading()
    this.getBanners()
},
getBanners() {
    WXAPI
    .getBanners()
    .then(res => {
      wx.hideLoading()
      this.setData({
        banners: res.data
      })
    })
},

Помните, если вы хотите отправить данные HTTP-запроса на страницу, вы должны добавить это предложение const WXAPI = require('../../wxapi/main')


определенные компоненты

Начните подготовку Хорошо, теперь начните писать страницы. Первое, что нужно написать, это раздел tabBar.

компонент tabBar

Не выглядит ли это немного странно, почему он немного прозрачен? Потому что этот компонент tabBar написан мной.

В общем случае достаточно написать компонент tabBar прямо в app.json.

Но я не думаю, что это выглядит так хорошо, поэтому я сам создал компонент tabBar.

После того, как я закончил писать, я проверил документ и обнаружил, что апплет WeChat официально предоставляет способ настройки компонента tabBar.Вы можете просмотреть его, если вам это нужно.Документация по мини-программе WeChat

После написания этого компонента я всегда решал проблемы, на которые нужно обратить внимание.

  • Выбрана текущая страница, нажмите еще раз, потому что она недействительна.

    Поэтому я сохранил атрибут страницы в app.js, чтобы сохранить текущую страницу, а затем добавил суждение к методу события click goToPage(), чтобы решить эту проблему.

<!--components/tabbar/tabbar.wxml-->
<view class="tabbar">
  <!-- 首页 -->
  <view class="shouye {{on === 'index' ? 'on': ''}}" data-page="index" bindtap="goToPage">
    <icon type="shouye" size="42" class="icon" color="{{on === 'index' ? '#f96702': ''}}"/>
    <text >首页</text>
  </view>
  <!-- 分类 -->
  <view class="fenlei {{on === 'category' ? 'on': ''}}" data-page="category" bindtap="goToPage">
    <icon type="classify" size="42" class="icon" color="{{on === 'category' ? '#f96702': ''}}"/>
    <text >分类</text>
  </view>
  <!-- 发现 -->
  <view class="faxian {{on === 'find' ? 'on': ''}}" data-page="find" bindtap="goToPage">
    <icon type="faxian" size="42" class="icon" color="{{on === 'find' ? '#f96702': ''}}"/>
    <text >发现</text>
  </view>
  <!-- 购物车 -->
  <view class="gouwuche {{on === 'cart' ? 'on': ''}}" data-page="cart" bindtap="goToPage">
    <icon type="gouwuche" size="42" class="icon" color="{{on === 'cart' ? '#f96702': ''}}"/>
    <text >购物车</text>
  </view>
  <!-- 我的 -->
  <view class="wode {{on === 'user' ? 'on': ''}}" data-page="user" bindtap="goToPage">
    <icon type="wode" size="42" class="icon" color="{{on === 'user' ? '#f96702': ''}}"/>
    <text >我的</text>
  </view>
</view>
// components/tabbar/tabbar.js
// 全局里面存了一个page 表示当前 路由
const app =  getApp();
Component({
  /**
   * 组件的属性列表
   */
  properties: {
    // 是否选中
    on:{
      type: String,
      value: ''
    }
  },

  /**
   * 组件的初始数据
   */
  data: {
  },

  /**
   * 组件的方法列表
   */
  methods: {
    // 跳转到相应的页面
    // 加了一个判断
    // 因为如果现在显示的是当前页面就不需要再跳转
    goToPage(e) {
      let page = e.currentTarget.dataset.page || 'user';
      if(app.globalData.page === page) {
        return ;
      }
      wx.redirectTo({
        url: `/pages/${page}/${page}`,
      });
      app.globalData.page = page;
    }
  }
})
/* components/tabbar/tabbar.wxss */
.tabbar {
  width: 100%;
  height: 100rpx;
  background-color: #ffffff;
  display: flex;
  position: fixed;
  bottom: 0;
  font-size: 26rpx;
  z-index: 99;
}
.shouye,.fenlei,.faxian,.gouwuche,.wode {
  flex: 1;
  display: flex;
  flex-direction: column;
  justify-content: center;
  text-align: center;
  opacity: 0.5;
}
.icon {
  height: 60rpx;
}
.on {
  color:#f96702;
}

Что касается вопроса о том, как его использовать, просто добавьте его внизу страницы, которой нужна функция tabBar.

например, индексная страница

<tabbar on="index"></tabbar>

Не забудьте представить компоненты в json

{
  "usingComponents": {
    "goodList": "../../components/goodList/goodList",
    "tabbar": "../../components/tabbar/tabbar"
  }
}

значок компонента

Иконки требуются в компоненте tabBar, я используюБиблиотека икон Али

как использовать?

  1. Сначала перейдите в библиотеку иконок, чтобы найти нужную иконку и добавить ее в корзину.
  2. Затем добавьте его в проект и создайте новый самостоятельно.
  3. В локальной загрузке скопируйте все содержимое файла CSS в файл wxss.

Этот компонент очень прост, его можно использовать сразу после определения, а цвет и размер можно изменить.

<!--components/icon/icon.wxml-->
<text class='iconfont icon-{{type}}' style='color:{{color}}; font-size:{{size}}rpx;'></text>
properties: {
    type: {
      type: String,
      value: ''
    },
    color: {
      type: String,
      value: '#000000'
    },
    size: {
      type: Number,
      value: '45'
    }
  },

компонент goodList

На домашней странице есть много таких списков продуктов, пишите их один за другим, код, написанный таким образом, приведет к тому, что код домашней страницы будет отображаться много, и его непросто поддерживать, поэтому я инкапсулирую его в компонент.

<!--components/goodList/goodList.wxml-->
<view class="goodList-good">
  <!-- 商品的图片 -->
  <view class="goodList-good-img">
    <image src="{{url}}" mode="aspectFill" />
  </view>
  <!-- 商品详细的信息 -->
  <view class="goodList-good_detail">
    <!-- 名称 -->
    <view class="good_detail_name">
      {{name}}
    </view>
    <!-- 信息 -->
    <view class="good_detail_brief">
      {{brief}}
    </view>
    <!-- 价格 -->
    <view class="good_detail_price">
      <text class="price" >¥{{price}}</text>
      <text class="oldPrice" style="display:{{oldPrice == '' ? 'none': 'inline'}};">¥{{oldPrice}}</text>
    </view>
  </view>
</view>
properties: {
    // 图片链接
    url: {  
      type: String,
      value: ''
    },
    // 名称
    name: {
      type: String,
      value: ''
    },
    // 信息
    brief: {
      type: String,
      value: ''
    },
    // 新的价格
    price: {
      type: String,
      value: ''
    },
    // 旧的价格
    oldPrice: {
      type: String,
      value: ''
    }
  },
/* components/goodList/goodList.wxss */

.goodList-good {
  position: relative;
  width: 100%;
  height: 100%;
}
.goodList-good-img {
  width: 100%;
  height: 312rpx;
  position: relative;
}
.goodList-good-img image {
  width: 100%;
  height: 100%;
}
.goodList-good_detail {
  padding: 26rpx 23rpx;
}
.good_detail_name {
  width:100%;
  overflow: hidden;
  text-overflow: ellipsis;
  display: -webkit-box;
  -webkit-line-clamp: 1;
  -webkit-box-orient: vertical;
}
.good_detail_brief {
  width:100%;
  overflow: hidden;
  text-overflow: ellipsis;
  display: -webkit-box;
  -webkit-line-clamp: 1;
  -webkit-box-orient: vertical;
  font-size: 25rpx;
  color: #8c8c8c;
}
.good_detail_price {
  display: flex;
  justify-content: flex-start;
  align-items: flex-end;
}
.good_detail_price .price {
  color: #a36d4a;
  font-size: 28rpx;
  padding-right: 16rpx;
}
.good_detail_price .oldPrice {
  font-size: 24rpx;
  color: #8c8c8c;
  text-decoration: line-through;
}

Ты узнаешь, почему мои границы так тонки.


Как нарисовать границу 0,5px

Новички могут сказать, что граница 0,5 пикселя — это не граница: 0,5 пикселя, на самом деле это неправильно.

Браузеры будут анализировать любую границу размером менее 1 пикселя до 1 пикселя., поэтому если вы напишете 0,5px, браузер разобьет его на 1px, поэтому эффекта не будет.

На самом деле все просто,Используйте псевдоклассы для рисования.

Например, правая граница компонента goodList.

В html индексной страницы я добавил класс тега представления, который обертывает goodList.rightBorderЭтот класс используется для представления рисования верхней границы.

<view class="item topBorder rightBorder" data-id="{{item.id}}" bindtap="goDetails">
  <goodList url="{{item.url}}"
    name="{{item.name}}"
    brief="{{item.brief}}"
    price="{{item.price}}"
    oldPrice="{{item.oldPrice}}" ></goodList>
</view>
.rightBorder::after {
  content: '';
  position: absolute;
  height: 200%;
  width: 1rpx;
  right: -1rpx;
  top: 0;
  transform-origin: 0 0;
  border-right: 1rpx solid#e0e0e0;
  transform: scale(.5);
  z-index: 1;
}

На самом деле, не забудьте нарисовать границу в 0,5 пикселя.

  1. Используйте псевдокласс и позицию: absolute;
  2. Если вы хотите нарисовать верхнюю и нижнюю границы, установите ширину на 200% и высоту на 200% для левой и правой сторон.
  3. Затем используйте преобразование: масштаб (.5);Ширина и высота уменьшаются в 0,5 раза, чтобы стать границей 0,5 пикселя.

Есть еще кое-какие подробности, пишите сколько нужно.


компонент списка пользователей

Просто список информации на моей странице, это очень просто

<!--components/userList/userList.wxml-->
<view class="main">
  <image src="{{List.img}}" />
  <view class="text" >
    <text >{{List.text}}</text>
  </view>
  <view class="state" wx:if="{{List.state !== ''}}">
    <text >({{List.state}})</text>
  </view>
</view>
/* components/userList/userList.wxss */
.main {
  width: 100%;
  height: 120rpx;
  display: flex;
  align-items: center;
  background-color: #fff;
  padding: 40rpx;
  box-sizing: border-box;
}
.main image {
  width: 80rpx;
  height: 80rpx;
}
.main .text {
  font-size: 32rpx;
  padding: 0 10rpx 0 5rpx;
}
.main .state {
  font-size: 26rpx;
  color: #8c8c8c;
}
properties: {
    List: {
      type: Object
    }
  },

Введение страницы и функции

Раздел домашнего поиска

Здесь я представил стиль поиска компонента weui. Как использовать?

  1. скачатьВерсия апплета WEUI, который можно открыть в инструментах разработчика WeChat.
  2. (Рекомендуется) Поместите weui.wxss из папки style в папку weui в вашем проекте.
  3. В app.wxss @import "./weui/weui.wxss". Его можно использовать

Главная Карусель Раздел

Это компонент swiper, предоставляемый апплетом WeChat,Документация разработчика программы WeChat Mini

Комбинация swiper и swiper-item может быть достигнута Для получения некоторой информации о конфигурации, пожалуйста, ознакомьтесь с официальной документацией.

специальный код

<swiper indicator-dots="{{indicatorDots}}" indicator-active-color="#ffffff" autoplay="{{autoPlay}}" interval="{{interval}}" duration="{{duration}}">
    <block wx:for="{{banners}}" wx:key="index">
      <swiper-item>
        <image src="{{item.imgurl}}" mode="aspectFill" class="banner-image" />
      </swiper-item>
    </block>
  </swiper>
 data: {
    banners: [],
    indicatorDots: true,
    autoPlay: true,
    interval: 3000,
    duration: 1000,
    navdata: [],
    goodList: [],
    goodListOne: {},
    name:'',
  },

часто используемая функция

Такая функция часто выполняется в апплете торгового центра, например:

Функциональные требования:

  1. Щелкните список продуктов слева, и информация о продукте справа переместится в соответствующую позицию.
  2. Проведите пальцем по информации о продукте справа, и выделение, отображаемое в списке продуктов слева, изменится соответствующим образом.

Требования к функциям несложные, но у новичков могут возникнуть проблемы. Я просто скажу, что должна делать функция

Прежде всего: проанализируйте, структура страницы — это левая и правая верстка. а такжеОбе стороны могут скользить.Так что вы можете использовать предоставленный нам WeChat

scroll-viewКомпоненты Два компонента могут использовать float, распределенный по левой и правой сторонам.

<!-- miniprogram/pages/category/category.wxml -->
<view class="container">
  <!-- 左边商品的标签信息 -->
  <scroll-view scroll-y scroll-with-animation="{{true}}" class="categroy-left">
    <view wx:for="{{categroy}}" wx:key="{{index}}" data-index="{{index}}" bindtap="switchTab" class="cate-list {{curIndex === index ? 'on': ''}}">
      {{item[0].name}}
    </view>
  </scroll-view>
  <!-- 右边 标签对应的商品信息 -->
  <scroll-view scroll-y scroll-into-view="{{toView}}" scroll-with-animation="true" bindscroll="scrollContent" bindscrolltolower="scrollEnd" class="categroy-right">
    <block wx:for="{{categroy}}" wx:key="inedx">
      <view id="right-list" class="right-list" id="{{index}}">
        <view class="right-title">
          <text>{{item[0].name}}</text>
        </view>
        <view class="right-content">
          <block wx:for="{{item}}" wx:key="idex" wx:for-item="product" wx:for-index="idex">
            <view class="list-detail" wx:if="{{idex > 0}}">
              <image src="{{product.picture}}" />
              <view class="detail-name">
                <text>{{product.desc}}</text>
              </view>
            </view>
          </block>
        </view>
      </view>
    </block>
  </scroll-view>
</view>
<tabbar on="category"></tabbar>

/* miniprogram/pages/category/category.wxss */
/*定义滚动条高宽及背景 高宽分别对应横竖滚动条的尺寸*/
::-webkit-scrollbar
{
width: 0px;
height: 0px;
background-color: pink;
}
.categroy-left {
  height: 100%;
  width: 150rpx;
  float: left;
  border-right: 1px solid #ebebeb;
  box-sizing: border-box;
  position: fixed;
  font-size: 30rpx;
  padding-bottom: 100rpx;
  box-sizing: border-box;
}
.categroy-left .cate-list {
  height: 90rpx;
  line-height: 90rpx;
  text-align: center;
  border: 2px solid #fff;
}
.categroy-left .cate-list.on {
  color: #ff4800;
  font-size: 34rpx;
}
/* 右边的列表 */
.categroy-right {
  width: 600rpx;
  float: right; 
  height: 1334rpx;
  /* height: 100%; */
  padding-bottom: 100rpx;
  box-sizing: border-box;
  overflow: hidden;
}
.right-title {
  width: 100%;
  text-align: center;
  position: relative;  
  padding-top: 30rpx;
  /* font-size: 30rpx; */
  padding-bottom: 30rpx;
}
 .right-title text::before, .right-title text::after {
  content: '';
  position: absolute;
  width: 60rpx;
  /* height: 1px; */
  top: 50%;
  border-top: 1px solid #e0e0e0;
  /* transform: scale(.5); */
}
.right-title text::before { 
  left: 30%;
}
.right-title text::after {
  right: 30%;
}
.right-list {
  /* height: 100%; */
  background-color: #fff;
}
.right-content {
  width: 100%;
  height: 100%;
  display: flex;
  flex-wrap: wrap;
}
.right-content .list-detail {
  flex-shrink: 0;
  width: 33.3%;
  height: 100%;
  font-size: 26rpx;
  text-align: center;
  
}
.right-content .list-detail image {
  width: 120rpx;
  height: 120rpx;
  padding: 10rpx;
  /* background-color: pink; */
}

Сложность этой функции заключается в логике js

Сначала разместите данные, которые вам нужны в данных

data: {
    categroy:[],    //商品信息
    curIndex: 'A',  //当前的选中的标签
    toView: 'A',    //去到的标签
    // 存入每个list的高度叠加
    heightArr: [],
    // 最后一个list,就是最后一个标签的id
    endActive: 'A'
  },

Проведите пальцем влево и вправо

Эту функцию легко реализовать

Просто добавьте событие в компонент прокрутки справа.scroll-into-view="{{toView}}", toView — отображаемый продуктid

Уведомлениекаждая информация о продукте справа, когда страница отображаетсядолжендобавить атрибут идентификатора

Затем компоненту прокрутки слева просто нужно добавить событие щелчка, чтобы изменить значение toView.

// 点击左边标签要修改的信息
  switchTab(e) {
    this.setData({
      curIndex: e.target.dataset.index,
      toView: e.target.dataset.index
    })
  },

Проведите пальцем вправо и влево, чтобы выделить соответствующие изменения

  1. Сначала нужно рассчитатьВысота, занимаемая каждой единицей товара справа, хранится в массиве, можно поместить в жизненный цикл onReady

    // 计算出右边每个商品占据的高度
      getPageMessage() {
        // console.log(4)
        let self = this
        let heightArr = []
        let h = 0
        const query = wx.createSelectorQuery()
        query.selectAll('.right-list').boundingClientRect()
        query.exec( res => {
          res[0].forEach( item => {
            h += item.height
            heightArr.push(h)
          })
          self.setData({
            heightArr: heightArr
          })
        })
      },
    
  2. Добавьте события в компонент представления прокрутки справа.bindscroll="scrollContent, которое является событием, предоставляемым прокруткой, которое запускается при скольжении.

    // 页面滑动时触发
      scrollContent(e) {
        const scrollTop = e.detail.scrollTop
        const scrollArr = this.data.heightArr
        const length = scrollArr.length - 1
        let endChar = String.fromCharCode(65 + length)
        let curChar = this.getCurrentIndex(scrollTop)
        if(this.data.endActive != endChar) {
          this.setData({
            curIndex: curChar
          })
        } else {
          this.setData({
            endActive: 'A'
          })
        }
      },
    
    // 判断curIndex应该是那个
      getCurrentIndex(scrollTop) {
        const scrollArr = this.data.heightArr
        let find = scrollArr.findIndex(item => {
          // 提前10rpx触发效果
          return scrollTop < item - 10
        })
        let curChar = String.fromCharCode(65 + find)
        return curChar
      },
    

    Вышеупомянутое может удовлетворить все функциональные требования.

    Но такое скольжение не идеально, правый скользит вниз, а левый блик не последний.

    Относительно идеальный эффект выглядит так

Если вы хотите быть идеальным, добавьте событие в прокрутку справа:bindscrolltolower="scrollEnd"

// 页面滑动到底部触发
  scrollEnd() {
    const scrollArr = this.data.heightArr
    const length = scrollArr.length - 1
    let endChar = String.fromCharCode(65 + length)
    this.setData({
      curIndex: endChar,
      endActive: endChar
    })
  },

логика покупок

Добиться такого эффекта несложно.

Логическая последовательность: Щелкните информацию о продукте на главной странице -> на странице сведений о продукте отображается соответствующая информация о продукте -> на странице корзины отображается информация о приобретенном продукте -> После модификации на странице сведений о продукте отображается модифицированная информация.

Чтобы реализовать такую ​​функцию, при переходе на страницу перехода должен быть передан идентификатор, а затем страница перехода получает данные, требуемые страницей, через значение идентификатора.

Например: Главная страница -> Страница сведений о продукте.

Это информация о списке продуктов через событие клика.bindtap="goDetails", перейдите на соответствующую страницу.

<view class="list">
      <block wx:for="{{goodList}}" wx:key="{{item.id}}">
        <view class="item topBorder rightBorder" data-id="{{item.id}}" bindtap="goDetails">
          <goodList url="{{item.url}}"
            name="{{item.name}}"
            brief="{{item.brief}}"
            price="{{item.price}}"
            oldPrice="{{item.oldPrice}}" ></goodList>
        </view>  
      </block>
    </view>
goDetails(e) {
    const id = e.currentTarget.dataset.id
    wx.navigateTo({
      url: `/pages/goodDetails/goodDetails?id=${id}`,
    });
  },

Страница сведений о продукте:

входящийзначение идентификатораЕго можно получить из параметра options жизненного цикла onLoad.

onLoad: function (options) {
    WXAPI.showLoading()
    // 获取用户的地址信息
    const address = wx.getStorageSync('Address');
    this.setData({
      id: options.id,
      address
    })
    this.getGoodDetail()
  },

логика хранения данных

Я храню данные непосредственно в локальном кеше (или прямо в облачной базе данных), используяwx.setStorage()

Две данные хранятся локально,Один - это информация обо всех купленных продуктах, а другой - общее количество приобретенных продуктов.

// 添加到购物车
  toAddCart() {
    let cartData = wx.getStorageSync('goods') || [];    
    let data = {
      id: this.data.id,
      name: this.data.goodData.name,
      memory: this.data.memory,
      color: this.data.color,
      price: this.data.price,
      num: this.data.selectNum,
      img: this.data.imgSrc,
      select: true
    }
    // wx.removeStorageSync('goods');
    cartData.push(data)
    const allNum =this.getAllNum(cartData)
    wx.setStorage({
      key: 'goods',
      data: cartData,
      success: (res) => {
        console.log(res)
        let pageIndex = getCurrentPages()
        let backIndex = pageIndex.length - 2
        wx.navigateBack({
          delta: backIndex
        })
      },
      fail: () => {},
      complete: () => {}
    });
    // 存储数量到storage
    wx.setStorageSync('allNum', allNum);
    // 写到外面就可以让showToast 显示在前一个页面
    setTimeout(()=>{
      wx.showToast({
        title: '已加入购物车',
        icon: 'success',
        duration: 2000
      });
   },500)
  },
  // 获取所有的数量
  getAllNum(cartData) {
    return cartData.reduce((sum, item) => {
      return sum + (+item.num)
    },0)
  },

Обзор переключения параметров

Чтобы реализовать эту функцию, вам нужно только добавить состояние, изменить значение состояния при нажатии и изменить соответствующие данные рендеринга.

data: {
 state: 'details_img', //判断概述 参数
}
<view class="summarize-parameter">
    <view class="title">
      <view class="summarize" bindtap="changeState">
        <text class="{{state === 'details_img'? 'on' : ''}}">概述</text>
      </view>
      <view class="parameter" bindtap="changeState">
        <text class="{{state === 'param_img'? 'on' : ''}}">参数</text>
      </view>
    </view>
    <view class="state">
      <block wx:for="{{state === 'details_img'? details_img : param_img}}" wx:key="index">
          <image src="{{item}}" mode="widthFix"/>
      </block>
    </view>
  </view>
// 改变概述和参数
  changeState() {
    let state = this.data.state
    if(state === 'details_img') {
      state = 'param_img'
    } else {
      state = 'details_img'
    }
    this.setData({
      state
    })
  },

Изменение данных о покупках, изменение данных о продукте

Сравните разницу между двумя картинками выше.

Выберите количество продуктов и конкретную информацию о продукте на странице покупок, а затем вернитесь на страницу сведений о продукте, и соответствующие данные будут изменены.

<view class="sales" bindtap="goSelectGoods">
      <text class="describe">已选</text>
      <view class="detail detail-change">
        {{default_change.name}}
        {{default_change.memory}}
        {{default_change.color}}
        <text >× {{default_change.num}}</text>
      </view>
      <view class="right"></view>
    </view>
<view class="shopping-img" bindtap="goCart">
    <icon type="gouwuche" color="#e0e0e0" size="40"/>
    <text wx:if="{{allNum != 0}}">{{allNum}}</text>
  </view>

Две модифицированные HTML-структуры выше

После нажатия Подтверждение на странице покупок я добавляю продукт в корзину по умолчанию, и он расположен в последнем элементе данных

При возврате на страницу сведений о продукте функция жизненного цикла onShow будет запущена повторно.

Так что мне просто нужно запустить метод модификации в onShow.

// 改变默认的版本数据 default_change
  changeDefauleChange() {
    const goods = wx.getStorageSync('goods') || [];
    if(goods.length === 0) {
      return
    }
    const id = this.data.id
    const default_change = goods[goods.length - 1]
    let memory = default_change.memory.toString()
    memory = memory.substring(0,memory.length - 4)
    default_change.memory = memory
    this.setData({
      default_change
    })
  },

нарисовать треугольник

Этот треугольник нарисован с помощью CSS, а не значка.

Использование CSS для рисования треугольника также не так сложно. Используются псевдоклассы и граничные свойства

.right:before,
.right:after {
  content: '';
  position: absolute;
  top: 35%;
  right: 0;
  border-width: 8px;
  /* transform: translateY(10000rpx); */
  border-color: transparent transparent transparent transparent;
  border-style: solid;
  transform: rotate(90deg);
}

.right:before {
  border-bottom: 8px #aaaaaa solid;
}

.right:after {
  right: 1px;
  /*覆盖并错开1px*/
  border-bottom: 8px #fff solid;
}

Изменить количество продукта

Вы можете напрямую использовать апплет WeChat, предоставленныйpickerКомпоненты, пожалуйста, обратитесь к документации для конкретной конфигурации

Получить географическое местоположение с помощью Tencent Maps

искать сначалаКарта Тенсенти зарегистрируйте информацию о разработчике и подайте заявку на получение ключа.

// 获取地理位置
const geocoder = (lat, lon) => {
  return request(API.MAPURL,false,{
    location: `${lat},${lon}`,
    key: API.QQ_MAP_KEY,
    get_poi: 0
  })
}

Затем скопируйте ключ, потому что я инкапсулировал все интерфейсы API. так используетсяAPI.QQ_MAP_KEYВместо этого просто введите здесь примененный ключ.

Чтобы получить информацию о широте и долготе пользователя, вы можете использоватьwx.getLocation(), вы можете получить информацию о широте и долготе пользователя.

getLocation() {
    wx.getLocation({
      type: 'gcj02',
      success: this.getAddress,
      fail: () => {
        this.openLocation()
      }
    })
  },
  getAddress(res) {
    let { latitude: lat, longitude: lon} = res
    WXAPI.geocoder(lat, lon)
    .then(res => {
      if(res.status !== 0 || !res.result) {
        return
      } 
      let {address_component
      } = res.result
      const Address = {
        city: address_component.city,
        district: address_component.district
      }
      wx.setStorageSync("Address", Address);
    })
  },

Поскольку я не разрешал пользователю авторизоваться, я напрямую сохранял полученное географическое положение в локальном хранилище.

Полученное географическое положение можно отобразить на странице сведений о продукте.

Эпилог

Процесс выполнения этого проекта радует, без использования облачных функций (данных страницы не так много, я думаю, что их можно записать без необходимости), поэтому общее время написания также очень короткое, меньше недели, чтобы написать Done. Момент достижения после написания тоже очень хорош. Если вы считаете эту статью полезной, пожалуйста, поставьте лайк! Также хотелось бы увидеть предложения, приведенные ниже! наконец служитьисходный код.Поднимите его, если вам это нужно!

Наконец, небольшое отступление, потому что я выпускница 2020 года и сейчас сталкиваюсь с давлением стажировки. Поскольку мне нужно время, чтобы прочитать вопросы интервью, в следующей статье я просто кратко описал важную функциональную логику.Если текст неясен, пожалуйста, простите меня.