Skip to content
On this page

详情模块-效果图

image-20221130095603236

详情模块-静态结构

src\pages\goods\index.vue

vue
<template>
  
  <view class="goods">
    <!-- 返回按钮 -->
    <GoBackBtn />

    <scroll-view scroll-y class="viewport">
      <!-- 商品信息 -->
      <view class="goods anchor" data-anchor-index="0">
          <!-- 轮播图 -->
            <view class="preview">
              <swiper autoplay  circular>
                <swiper-item
                  v-for="(item, index) in []"
                  :key="index"
                >
                  <image :src="item" />
                </swiper-item>
              </swiper>
           <!-- 指示器 -->
              <view class="indicator">
                <text class="current">{{ 0 }}</text>
                <text class="split">/</text>
                <text class="total">{{ 0 }}</text>
              </view>
            </view>
      </view>
      <!-- 商品评价 - 静态结构 - 没有接口不需要处理 -->

      <!-- 商品详情 -->

      <!-- 常见问题 -->

      <!-- 推荐 -->
    </scroll-view>

    <!-- 用户操作 -->

    <!-- 弹出层 -->

    <!-- SKU -->
  </view>
</template>

<script>

// id=1188006
export default {};
</script>

<style lang="scss">
page {
  height: 100%;
  overflow: hidden;
  display: flex;
  flex-direction: column;
}
/* 自定义导航栏 */
.navbar {
  width: 750rpx;
  background-color: #fff;
  position: fixed;
  top: 0;
  left: 0;
  z-index: 9;
  .search {
    height: 40px;
    padding: 1px 110px 0 50px;
    .input {
      height: 62rpx;
      border-radius: 60rpx;
      font-size: 26rpx;
      color: #8b8b8b;
      background-color: #f3f4f4;
      position: relative;
      &::before {
        position: absolute;
        left: 24rpx;
        top: 50%;
        color: #b7b7b7;
        font-size: 28rpx;
        transform: translateY(-50%);
      }
    }
  }
  .wrap {
    position: relative;
  }
  .back {
    position: absolute;
    left: 10px;
    top: 2px;
    z-index: 9;
    display: flex;
    justify-content: center;
    align-items: center;
    width: 60rpx;
    height: 60rpx;
    border-radius: 50%;
    font-size: 23px;
    color: #191919;
  }
  .tabs {
    display: flex;
    justify-content: space-evenly;
    border-bottom: 1rpx solid #eaeaea;
    text {
      display: block;
      padding: 10rpx 10rpx 16rpx;
      font-size: 28rpx;
      position: relative;
    }
    .active {
      color: #27ba9b;
      font-weight: 500;
      &::after {
        content: "";
        position: absolute;
        left: 18rpx;
        right: 20rpx;
        bottom: 14rpx;
        height: 4rpx;
        background-color: #27ba9b;
      }
    }
  }
}
.viewport {
  background-color: #f4f4f4;
  height: 100vh;
}
.panel {
  margin-top: 20rpx;
  background-color: #fff;
  .title {
    display: flex;
    justify-content: space-between;
    align-items: center;
    height: 90rpx;
    line-height: 1;
    padding: 30rpx 60rpx 30rpx 6rpx;
    position: relative;
    text {
      padding-left: 10rpx;
      font-size: 28rpx;
      color: #333;
      font-weight: 600;
      border-left: 4rpx solid #27ba9b;
    }
    navigator {
      font-size: 24rpx;
      color: #666;
    }
  }
}
.arrow {
  &::after {
    position: absolute;
    top: 50%;
    right: 30rpx;
    content: "\e6c2";
    color: #ccc;
    font-family: "erabbit" !important;
    font-size: 32rpx;
    transform: translateY(-50%);
  }
}
/* 商品信息 */
.goods {
  background-color: #fff;
  height: 100%;
  display: flex;
  flex-direction: column;
  flex: 1;
  .preview {
    height: 750rpx;
    position: relative;
    .indicator {
      height: 40rpx;
      padding: 0 24rpx;
      line-height: 40rpx;
      border-radius: 30rpx;
      color: #fff;
      font-family: Arial, Helvetica, sans-serif;
      background-color: rgba(0, 0, 0, 0.3);
      position: absolute;
      bottom: 30rpx;
      right: 30rpx;
    }
  }
  .indicator {
    .current {
      font-size: 26rpx;
    }
    .split {
      font-size: 24rpx;
      margin: 0 1rpx 0 2rpx;
    }
    .total {
      font-size: 24rpx;
    }
  }
  .meta {
    position: relative;
    border-bottom: 1rpx solid #eaeaea;
    .price {
      height: 130rpx;
      padding: 25rpx 30rpx 0;
      color: #fff;
      font-size: 34rpx;
      box-sizing: border-box;
      background-color: #35c8a9;
    }
    .number {
      font-size: 56rpx;
    }
    .brand {
      width: 160rpx;
      height: 80rpx;
      overflow: hidden;
      position: absolute;
      top: 26rpx;
      right: 30rpx;
    }
    .name {
      max-height: 88rpx;
      line-height: 1.4;
      margin: 20rpx;
      font-size: 32rpx;
      color: #333;
    }
    .remarks {
      line-height: 1;
      padding: 0 20rpx 30rpx;
      font-size: 24rpx;
      color: #cf4444;
    }
  }
  .related {
    padding-left: 20rpx;
    .item {
      height: 90rpx;
      padding-right: 60rpx;
      border-bottom: 1rpx solid #eaeaea;
      font-size: 26rpx;
      color: #333;
      position: relative;
      display: flex;
      align-items: center;
      &:last-child {
        border-bottom: 0 none;
      }
    }
    .label {
      width: 60rpx;
      color: #898b94;
      margin: 0 16rpx 0 10rpx;
    }
    .text {
      flex: 1;
      -webkit-line-clamp: 1;
    }
  }
}
/* 商品评论 */
.comments {
  padding-left: 20rpx;
  .comment {
    padding: 24rpx 40rpx 24rpx 10rpx;
    border-top: 1rpx solid #eaeaea;
  }
  .caption {
    height: 60rpx;
    display: flex;
    justify-content: space-between;
    align-items: center;
    .avatar {
      width: 60rpx;
      height: 60rpx;
      margin-right: 20rpx;
      border-radius: 50%;
    }
  }
  .user {
    font-size: 26rpx;
    color: #333;
    display: flex;
    align-items: center;
  }
  .rating {
    width: 144rpx;
    height: 24rpx;
    background-image: url(https://static.botue.com/erabbit/static/images/rating_off.png);
    background-size: contain;
    .rank {
      height: 100%;
      background-image: url(https://static.botue.com/erabbit/static/images/rating_on.png);
      background-size: 144rpx 24rpx;
    }
  }
  .content {
    .text {
      line-height: 1.4;
      margin-top: 24rpx;
      font-size: 24rpx;
      color: #333;
    }
    .extra {
      display: flex;
      justify-content: space-between;
      margin: 20rpx 1rpx 0 0;
      font-size: 22rpx;
      color: #666;
    }
  }
  .pictures {
    display: flex;
    flex-wrap: wrap;
    margin: 20rpx 0 0 1rpx;
  }
  .picture {
    width: 150rpx;
    height: 150rpx;
    margin-right: 27rpx;
    &:last-child {
      margin-right: 0;
    }
  }
}
/* 类似商品 */
.similar {
  margin-top: 20rpx;
  .bar {
    display: flex;
    align-items: center;
    height: 90rpx;
    font-size: 28rpx;
    color: #333;
    background-color: #fff;
    text {
      flex: 1;
      text-align: center;
      position: relative;
    }
    .active {
      &::after {
        content: "";
        display: block;
        width: 60rpx;
        height: 4rpx;
        background-color: #27ba9b;
        transform: translateX(-50%);
        position: absolute;
        left: 50%;
        bottom: -5rpx;
      }
    }
  }
  .content {
    padding-top: 20rpx;
    background-color: #f4f4f4;
    white-space: nowrap;
    navigator {
      display: inline-block;
      width: 200rpx;
      height: 270rpx;
      padding: 15rpx 15rpx 0;
      margin-right: 15rpx;
      background-color: #fff;
      border-radius: 6rpx;
      &:first-child {
        margin-left: 15rpx;
      }
    }
    image {
      height: 172rpx;
    }
    .name {
      max-height: 64rpx;
      line-height: 1.2;
      margin-top: 10rpx;
      font-size: 24rpx;
      color: #333;
      -webkit-line-clamp: 1;
    }
    .price {
      font-size: 18rpx;
      color: #cf4444;
    }
    .number {
      font-size: 24rpx;
      margin-left: 2rpx;
    }
  }
}
/* 商品详情 */
.detail {
  padding-left: 20rpx;
  .content {
    margin-left: -20rpx;
  }
  .properties {
    padding: 0 20rpx;
    margin-bottom: 30rpx;
    .item {
      display: flex;
      line-height: 2;
      padding: 10rpx;
      font-size: 26rpx;
      color: #333;
      border-bottom: 1rpx dashed #ccc;
    }
    .label {
      width: 200rpx;
    }
    .value {
      flex: 1;
    }
  }
}
/* 常见问题 */
.help {
  display: flex;
  align-items: center;
  padding: 20rpx 0 20rpx 20rpx;
  margin-top: 20rpx;
  font-size: 28rpx;
  color: #333;
  background-color: #fff;
  position: relative;
  .icon-help {
    font-size: 34rpx;
    margin-right: 6rpx;
    color: #ffa868;
  }
}
/* 商品推荐 */
.recommend {
  padding-left: 20rpx;
  .content {
    padding: 0 20rpx 20rpx;
    margin-left: -20rpx;
    background-color: #f4f4f4;
    overflow: hidden;
    navigator {
      width: 345rpx;
      padding: 24rpx 20rpx 20rpx;
      margin: 20rpx 20rpx 0 0;
      border-radius: 10rpx;
      background-color: #fff;
      float: left;
    }
    .image {
      height: 260rpx;
    }
    .name {
      height: 80rpx;
      margin: 10rpx 0;
      font-size: 26rpx;
      color: #262626;
    }
    .price {
      line-height: 1;
      font-size: 20rpx;
      color: #cf4444;
    }
    .number {
      font-size: 26rpx;
      margin-left: 2rpx;
    }
  }
  navigator {
    &:nth-child(even) {
      margin-right: 0;
    }
  }
}
/* 底部工具栏 */
.toolbar {
  height: 120rpx;
  padding: 0 20rpx;
  border-top: 1rpx solid #eaeaea;
  display: flex;
  justify-content: space-between;
  align-items: center;
  .buttons {
    display: flex;
    & > view {
      width: 220rpx;
      text-align: center;
      line-height: 72rpx;
      font-size: 26rpx;
      color: #fff;
      border-radius: 72rpx;
    }
    .addcart {
      background-color: #ffa868;
    }
    .payment {
      background-color: #27ba9b;
      margin-left: 20rpx;
    }
  }
  .icons {
    padding-right: 10rpx;
    display: flex;
    align-items: center;
    justify-content: space-evenly;
    flex: 1;
    button {
      text-align: center;
      line-height: 1.4;
      padding: 0;
      border-radius: 0;
      font-size: 20rpx;
      color: #333;
      background-color: #fff;
    }
    text {
      display: block;
      font-size: 34rpx;
    }
  }
}
</style>

返回按钮

目标:

  1. 普通的页面自带返回按钮,但是自定义导航的页面需要自己封装返回按钮
  2. 点击返回按钮时,如果有上一页,就返回上一页,如果没有上一页,就返回首页

思路:关键API getCurrentPages

静态结构:

src\components\GoBackBtn\GoBackBtn.vue

vue
<template>
  <view :style="{top: capButton.top +'px'}" class="back icon-left" />
</template>

<script>
import {mapState} from "vuex";

export default {
  computed: {
    ...mapState(['capButton'])
  },
};
</script>

<style>
.back {
  position: absolute;
  left: 50rpx;
  z-index: 9;

  display: flex;
  justify-content: center;
  align-items: center;
  width: 60rpx;
  height: 60rpx;
  border-radius: 50%;
  font-size: 23px;
  color: #fff;
  background-color: rgba(0, 0, 0, 0.5);
}
</style>

代码:

src\components\GoBackBtn\GoBackBtn.vue

js
methods: {
  onBack() {
    const pages = getCurrentPages();
    if(pages.length > 1){
      uni.navigateBack();
    }else {
      uni.switchTab({url: '/pages/index/index'})
    }
  },
},

请求数据

src/api/goods.js

js
import http from "@/utils/http";

/**
 * 商品详情
 */
export const getGoodsDetailAPI = (id) => {
  return http({
    url: "/goods",
    data: { id },
  });
};
vue
<script>	
import { getGoodsDetailAPI } from "@/api/goods";

export default {
  data() {
    return {
      goods: {},
    };
  },
  onLoad({ id }) {
   	this.loadData()
  },
  methods:{
    async lodaData(id){
       const { result } = await getGoodsDetailAPI(id);
    	this.goods = result;
    }
  }
};
</script>

商品信息-轮播图

image-20221130122143374

diff
<view class="goods anchor" data-anchor-index="0">
      <!-- 轮播图 -->
      <view class="preview">
        <swiper 
        	autoplay
          circular
+        	@change="current = $event.detail.current" 
					>
          <swiper-item
-          	v-for="(item, index) in []"          
+          	v-for="(item, index) in goods.mainPictures"
            :key="index"
          >
            <image :src="item" />
          </swiper-item>
        </swiper>
       <!-- 指示器 -->
        <view class="indicator">
+         <text class="current">{{ current + 1 }}</text>
          <text class="split">/</text>
+         <text class="total">{{ goods.mainPictures ? goods.mainPictures.length : '' }}		</text>
        </view>
    	</view>
  	</view>

商品信息-名称和价格

image-20221130122213961

vue
<!-- 轮播图 -->

<!-- 名称和价格 -->
<view class="meta">
  <view class="price">
    <text class="symbol">¥</text>
    <text class="number">{{ 0}}</text>
  </view>
  <view class="brand">
    <image mode="aspectFill"  />
  </view>
  <view class="name ellipsis">
    {{ '名称' }}
  </view>
  <view class="remarks"> {{ '描述' }} </view>
</view>
diff
<!-- 轮播图 -->

<!-- 名称和价格 -->
<view class="meta">
  <view class="price">
    <text class="symbol">¥</text>
+   <text class="number">{{ goods.price }}</text>
  </view>
  <view class="brand">
    <image mode="aspectFill" 
+   	:src="goods.brand.picture"
    />
  </view>
  <view class="name ellipsis">
+   {{ goods.name }}
  </view>
  <view class="remarks"> 
+  	{{ goods.desc }} 
  </view>
</view>

规格参数-Todo

image-20221130123343741

vue
<!-- 名称和价格 -->

			<!-- 规格参数 -->
      <view class="related">
        <view class="item arrow">
          <text class="label">选择</text>
          <text class="text ellipsis">
            {{ '白色 红外体温计 1件' || "请选择商品规格" }}
          </text>
        </view>
        <view class="item arrow">
          <text class="label">送至</text>
          <text class="text ellipsis">北京市顺义区京顺路9号黑马程序员</text>
        </view>
        <view class="item arrow">
          <text class="label">服务</text>
          <text class="text ellipsis">无忧退 快速退款 免费包邮</text>
        </view>
      </view>

商品评价

纯静态结构

image-20221130123820849

vue
<!-- 商品评价 - 静态结构 - 没有接口不需要处理 -->
  <view class="comments panel anchor" data-anchor-index="1">
    <view class="title arrow">
      <text>评价</text>
      <navigator url="/pages/comments/index" hover-class="none" class="more"
        >好评度 70%</navigator
      >
    </view>
    <view class="comment">
      <view class="caption">
        <view class="user">
          <image
            class="avatar"
            src="https://static.botue.com/erabbit/static/uploads/avatar_2.jpg"
          />
          <text>白月初</text>
        </view>
        <view class="rating">
          <view class="rank" style="width: 80%"></view>
        </view>
      </view>
      <view class="content">
        <view class="text"> 质量不错,灵敏度高,结构巧妙,款式也好看 </view>
        <view class="pictures">
          <view class="picture">
            <image
              src="https://static.botue.com/erabbit/static/uploads/comment_1.jpg"
            />
          </view>
          <view class="picture">
            <image
              src="https://static.botue.com/erabbit/static/uploads/comment_2.jpg"
            />
          </view>
          <view class="picture">
            <image
              src="https://static.botue.com/erabbit/static/uploads/comment_2.jpg"
            />
          </view>
        </view>
        <view class="extra">
          <text class="date">购买时间: 2020-11-11</text>
          <text class="type">黑色 公开版 128G</text>
        </view>
      </view>
    </view>
    <view class="comment">
      <view class="caption">
        <view class="user">
          <image
            class="avatar"
            src="https://static.botue.com/erabbit/static/uploads/avatar_3.jpg"
          />
          <text>白月初</text>
        </view>
        <view class="rating">
          <view class="rank" style="width: 60%"></view>
        </view>
      </view>
      <view class="content">
        <view class="text"> 质量不错,灵敏度高,结构巧妙,款式也好看 </view>
        <view class="pictures">
          <view class="picture">
            <image
              src="https://static.botue.com/erabbit/static/uploads/comment_1.jpg"
            />
          </view>
          <view class="picture">
            <image
              src="https://static.botue.com/erabbit/static/uploads/comment_2.jpg"
            />
          </view>
          <view class="picture">
            <image
              src="https://static.botue.com/erabbit/static/uploads/comment_2.jpg"
            />
          </view>
          <view class="picture">
            <image
              src="https://static.botue.com/erabbit/static/uploads/comment_1.jpg"
            />
          </view>
        </view>
        <view class="extra">
          <text class="date">购买时间: 2020-11-11</text>
          <text class="type">黑色 公开版 128G</text>
        </view>
      </view>
    </view>
  </view>

商品详情

image-20221130124017771

vue
<!-- 商品信息 -->


<!-- 商品评价 - 静态结构 - 没有接口不需要处理 -->

<!-- 商品详情 -->
<view class="detail panel anchor" data-anchor-index="2">
  <view class="title">
    <text>详情</text>
  </view>
  <view class="content">
    <view class="properties">
      <!-- 商品详情属性 -->
      <view
        class="item"
        v-for="item in []"
        :key="item.name"
      >
        <text class="label">{{ item.name }}</text>
        <text class="value">{{ item.value }}</text>
      </view>
    </view>

    <!-- 商品详情图片 -->
    <image
      mode="widthFix"
      v-for="(item, index) in []"
      :key="index"
      :src="item"
    />
  </view>
</view>
diff
<!-- 商品信息 -->


<!-- 商品评价 - 静态结构 - 没有接口不需要处理 -->

<!-- 商品详情 -->
<view class="detail panel anchor" data-anchor-index="2">
  <view class="title">
    <text>详情</text>
  </view>
  <view class="content">
    <view class="properties">
      <!-- 商品详情属性 -->
      <view
        class="item"
+        v-for="item in goods.details.properties"
        :key="item.name"
      >
        <text class="label">{{ item.name }}</text>
        <text class="value">{{ item.value }}</text>
      </view>
    </view>

    <!-- 商品详情图片 -->
    <image
      mode="widthFix"
+     v-for="(item, index) in goods.details.pictures"
      :key="index"
      :src="item"
    />
  </view>
</view>

常见问题

image-20221130124128918

vue
<!-- 商品信息 -->


<!-- 商品评价 - 静态结构 - 没有接口不需要处理 -->

<!-- 商品详情 -->

<!-- 常见问题 -->
  <view class="help arrow">
    <text class="icon-help"></text>
    <view hover-class="none">常见问题</view>
  </view>

推荐

image-20221130124638560

请求数据、渲染界面

api/goods.js

js
/**
 * 同类推荐(也支持猜你喜欢)
 * id 传入代表查询相关商品,不传代表查询猜你喜欢
 * limit 查询数量
 */
export const getGoodsRelevantAPI = (id, limit = 4) => {
  return http({
    url: "/goods/relevant",
    data: {
      id,
      limit,
    },
  });
};
diff
<script>

import { 
	getGoodsDetailAPI, 
+	getGoodsRelevantAPI 
} from "@/api/goods";
export default {
  data() {
    return {
      goods: {},
      current: 0,
+     goodsRelevants: [],
    };
  },

  methods: {
    async loadData(id) {
      const { result } = await getGoodsDetailAPI(id);
      this.goods = result;
    },
+    async loadRecommend(id) {
+      const { result } = await getGoodsRelevantAPI(id);
+      this.goodsRelevants = result;
+    },    
  },
  
  onLoad({ id }) {
      this.loadData(id);
+     this.loadRecommend(id);
    },
};
</script>
vue
<!-- 商品信息 -->


<!-- 商品评价 - 静态结构 - 没有接口不需要处理 -->

<!-- 商品详情 -->

<!-- 常见问题 -->

<!-- 推荐 -->
  <view class="recommend panel anchor" data-anchor-index="3">
    <view class="title">
      <text>推荐</text>
    </view>
    <view class="content">
      <navigator
        v-for="item in goodsRelevants"
        :key="item.id"
        :url="`/pages/goods/index?id=${item.id}`"
        hover-class="none"
      >
        <image class="image" mode="aspectFit" :src="item.picture"></image>
        <view class="name ellipsis">
          {{ item.name }}
        </view>
        <view class="price">
          <text class="symbol">¥</text>
          <text class="number">{{ item.price }}</text>
        </view>
      </navigator>
    </view>
  </view>

4个弹出层菜单组件

Clause-服务条款

src\pages\goods\components\Clause\index.vue

vue
<template>
  <view>
    <view class="title">服务说明</view>
    <view class="content">
      <view class="item">
        <view class="dt">无忧退货</view>
        <view class="dd"
          >自收到商品之日起30天内,可在线申请无忧退货服务(内
          裤、食品等特殊商品除外)</view
        >
      </view>
      <view class="item">
        <view class="dt">快速退款</view>
        <view class="dd"
          >收到退货包裹并确认无误后,将在48小时内办理退款,
          退款将原路返回,不同银行处理时间不同,预计1-5个工作日到账</view
        >
      </view>
      <view class="item">
        <view class="dt">满88元免邮费</view>
        <view class="dd"
          >单笔订单金额(不含运费)满88元可免邮费,不满88元,
          单笔订单收取10元邮费</view
        >
      </view>
    </view>
  </view>
</template>

<style>
.content {
  padding: 20rpx 10rpx 100rpx 20rpx !important;
}

.item {
  margin-top: 20rpx;
}

.dt {
  margin-bottom: 10rpx;
  font-size: 28rpx;
  color: #333;
  font-weight: 500;
  position: relative;
}

.dt::before {
  content: "";
  width: 10rpx;
  height: 10rpx;
  border-radius: 50%;
  background-color: #eaeaea;
  transform: translateY(-50%);

  position: absolute;
  top: 50%;
  left: -20rpx;
}

.dd {
  line-height: 1.6;
  font-size: 26rpx;
  color: #999;
}
</style>

Helps-帮助

src\pages\goods\components\Helps\index.vue

vue
<template>
  <view>
    <view class="title">常见问题</view>
    <view class="content">
      <view class="item">
        <view class="dt">购买运费如何收取</view>
        <view class="dd"
          >单笔订单金额(不含运费)满88元免邮;不满88元,每单收取10元运费。(港澳台地区需满500元免邮费;不满500元,每单收取30元运费)</view
        >
      </view>
      <view class="item">
        <view class="dt">使用什么快递发货</view>
        <view class="dd">默认使用顺丰快递发货(个别商品使用其它快递)</view>
        <view class="dd">配送范围覆盖全国大部分地区(港澳台地区除外)</view>
      </view>
      <view class="item">
        <view class="dt">如何申请退货</view>
        <view class="dd"
          >1.
          自收到商品之日起30日内,客户可申请无忧退货,退款将原路返还,不同的银行处理时间不同,预计1-5个工作日到账;</view
        >
        <view class="dd"
          >2.
          因小兔鲜儿产生的退货,如质量问题,退货邮费由小兔鲜儿承担,退款完成后会以现金券的形式报销,因客户个人原因产生的退货,购买和寄回运费由客户个人承担</view
        >
      </view>
    </view>
  </view>
</template>

<style>
.content {
  padding: 20rpx 10rpx 100rpx 20rpx !important;
}

.item {
  margin-top: 20rpx;
}

.dt {
  margin-bottom: 10rpx;
  font-size: 28rpx;
  color: #333;
  font-weight: 500;
  position: relative;
}

.dt::before {
  content: "";
  width: 10rpx;
  height: 10rpx;
  border-radius: 50%;
  background-color: #eaeaea;
  transform: translateY(-50%);

  position: absolute;
  top: 50%;
  left: -20rpx;
}

.dd {
  line-height: 1.6;
  font-size: 26rpx;
  color: #999;
}
</style>

Shipment-快递

src\pages\goods\components\Shipment\index.vue

vue
<template>
  <view>
    <view class="title">配送至</view>
    <view class="shipment">
      <view class="item">
        <view class="user">李明 13824686868</view>
        <view class="address">北京市顺义区后沙峪地区安平北街6号院</view>
        <text class="icon icon-checked"></text>
      </view>
      <view class="item">
        <view class="user">王东 13824686868</view>
        <view class="address">北京市顺义区后沙峪地区安平北街6号院</view>
        <text class="icon icon-ring"></text>
      </view>
      <view class="item">
        <view class="user">张三 13824686868</view>
        <view class="address">北京市朝阳区孙河安平北街6号院</view>
        <text class="icon icon-ring"></text>
      </view>
    </view>
    <view class="footer">
      <view class="button primary"> 新建地址 </view>
      <view v-if="false" class="button primary">确定</view>
    </view>
  </view>
</template>

<style>
.shipment {
  min-height: 300rpx;
  max-height: 540rpx;
  overflow: auto;
  padding: 20rpx 10rpx !important;
}

.shipment .item {
  padding: 30rpx 50rpx 30rpx 60rpx;
  background-size: 40rpx;
  background-repeat: no-repeat;
  background-position: 0 center;
  background-image: url(https://static.botue.com/erabbit/static/images/locate.png);
  position: relative;
}

.shipment .item .icon {
  color: #999;
  font-size: 40rpx;
  transform: translateY(-50%);
  position: absolute;
  top: 50%;
  right: 0;
}

.shipment .item .icon-checked {
  color: #27ba9b;
}

.shipment .item .icon-ring {
  color: #444;
}

.shipment .item .user {
  font-size: 28rpx;
  color: #444;
  font-weight: 500;
}

.shipment .item .address {
  font-size: 26rpx;
  color: #666;
}
</style>

SKU 商品选择

src\pages\goods\components\Sku\index.vue

vue
<template>
  <view>
    <view class="header">
      <image
        class="thumb"
        src="https://static.botue.com/erabbit/static/uploads/goods_preview_1.jpg"
      ></image>
      <view class="wrap">
        <view class="price">
          <view class="discount">
            <text class="symbol">¥</text>
            <text class="number">129</text>
            <text class="decimal">.00</text>
          </view>
          <view class="original">
            <text class="symbol">¥</text>
            <text class="number">199</text>
            <text class="decimal">.00</text>
          </view>
        </view>
        <view class="extra">
          <text class="text">重量: 0.2kg</text>
          <text class="text">编号: 676587698</text>
        </view>
      </view>
    </view>

    <view class="body">
      <view class="specs">
        <view class="label">颜色</view>
        <view class="section">
          <view class="item checked">白色</view>
          <view class="item">黑色</view>
          <view class="item">灰色</view>
          <view class="item">卡其色</view>
        </view>
        <view class="label">类型</view>
        <view class="section">
          <view class="item">红外体温计</view>
          <view class="item disabled">双模</view>
          <view class="item">灵敏</view>
          <view class="item">便携式</view>
        </view>
      </view>
      <view class="number">
        <view class="label">数量</view>
        <view class="counter">
          <text class="text disabled">-</text>
          <input type="text" class="input" value="1" />
          <text class="text">+</text>
        </view>
      </view>
    </view>
    <view class="footer">
      <!-- <view  class="button secondary" >加入购物车</view > -->
      <view class="button primary">立即购买</view>
    </view>
  </view>
</template>

<style>
.header {
  display: flex;
  padding: 30rpx 0 !important;
}

.header .thumb {
  width: 180rpx;
  height: 180rpx;
  margin-right: 20rpx;
  border-radius: 8rpx;
}

.header .wrap {
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
}

.header .price {
  display: flex;
  align-items: baseline;
}

.header .price .discount {
  font-size: 40rpx;
  color: #cf4444;
}

.header .price .original {
  font-size: 28rpx;
  margin-left: 20rpx;
  color: #999;
  text-decoration: line-through;
}

.header .symbol,
.header .decimal {
  font-size: 70%;
}

.header .extra {
  margin-bottom: 10rpx;
  font-size: 22rpx;
  color: #666;
}

.header .extra .text {
  margin-right: 10rpx;
}

.body {
  height: 540rpx !important;
  overflow: auto;
}

.body .label {
  margin: 10rpx 0 20rpx;
  color: #333;
  font-weight: 500;
  font-size: 26rpx;
}

.body .specs .section {
  overflow: hidden;
}

.body .specs .item {
  min-width: 100rpx;
  text-align: center;
  line-height: 1;
  padding: 10rpx 30rpx;
  margin: 0 20rpx 20rpx 0;
  border-radius: 50rpx;
  color: #444;
  font-size: 26rpx;
  border: 1rpx solid #f3f4f4;
  background-color: #f3f4f4;
  float: left;
}

.body .specs .checked {
  color: rgba(39, 186, 155, 0.8);
  border: 1rpx solid rgba(39, 186, 155, 0.3);
  background-color: rgba(39, 186, 155, 0.1);
}

.body .specs .disabled {
  opacity: 0.6;
  border: 1rpx dashed #999;
}

.body .number {
  display: flex;
  justify-content: space-between;
  align-items: center;
  height: 80rpx;
  margin-top: 30rpx;
}

.body .number .counter {
  display: flex;
}

.body .counter .input {
  display: block;
  width: 100rpx;
  height: 48rpx;
  text-align: center;
  border-radius: 4rpx;
  font-size: 24rpx;
  color: #444;
  background-color: #f6f6f6;
}

.body .counter .text {
  display: block;
  width: 48rpx;
  height: 48rpx;
  text-align: center;
  line-height: 48rpx;
  font-size: 32rpx;
}

.body .counter .disabled {
  color: #999;
}
</style>

弹出层

vue
<uni-popup ref="popup" type="bottom" background-color="#fff">
      <view class="popup-root">
        <text class="close icon-close"></text>
       <!-- 4个组件 -->
      </view>
    </uni-popup>

目标:用到了uni-ui中提供的弹出层组件 uni-popup,主要在以下区域使用

image-20221130155310341

步骤:

  1. 声明数据:showType:''
  2. showTypev-show配合, 做条件渲染
  3. 点击事件,修改showType的值, 并且打开弹出层
  4. 点击关闭,关闭弹出层

代码:

1、声明数据:showType:''

js
data() {
    return {
    	// ... 省略其它代码...
      
      showType: ''
    };
  },

2、showTypev-show配合, 做条件渲染

js
import Sku from "./components/Sku";
import Shipment from "./components/Shipment";
import Clause from "./components/Clause";
import Helps from "./components/Helps";
// id=1188006
export default {
  components: {
    Sku,
    Shipment,
    Clause,
    Helps,
  },
}
diff
<uni-popup ref="popup" type="bottom" background-color="#fff">
      <view class="popup-root">
        <text class="close icon-close"></text>     
       
       💥💥 记得注册组件
+       <Sku v-show="showType === 'sku'" /></Sku>
+       <Shipment v-show="showType === 'shipment'" />
+       <Clause v-show="showType === 'clause'" />
+       <Helps v-show="showType === 'helps'" />

      </view>
    </uni-popup>

3、点击事件,修改showType的值, 并且打开弹出层

js
onShowPopup(type) {
   this.showType = type;
   this.$refs.popup.open();
},

用户操作

image-20221130151447619

vue
<template>
  <view class="goods">
    <!-- 返回按钮 -->

    <scroll-view scroll-y class="viewport">
      <!-- 商品信息 -->

      <!-- 商品评价 - 静态结构 - 没有接口不需要处理 -->

      <!-- 商品详情 -->

      <!-- 常见问题 -->

      <!-- 推荐 -->
    </scroll-view>

    <!-- 用户操作 -->
    <view class="toolbar">
      <view class="icons">
        <button class="collect"><text class="icon-heart"></text>收藏</button>
        <button class="contact" open-type="contact">
          <text class="icon-handset"></text>客服
        </button>
        <button class="cart" >
          <text class="icon-cart"></text>购物车
        </button>
      </view>
      <view class="buttons">
        <view  data-button-type="cart" class="addcart">
          加入购物车
        </view>
        <view
         
          data-button-type="payment"
          class="payment"
        >
          立即购买
        </view>
      </view>
    </view>
    <!-- 弹出层 -->

    <!-- SKU -->
  </view>
</template>

Released under the MIT License.