uni-app D8 实战(小兔鲜)END

120 阅读16分钟

1.填写订单

1.1 渲染基本信息

image.png

1.1.1 设置独立的分包(静态结构)


import { computed, ref } from 'vue';

// 获取屏幕边界到安全区域距离
const { safeAreaInsets } = uni.getSystemInfoSync();
// 订单备注
const buyerMessage = ref('');
// 配送时间
const deliveryList = ref([    { type: 1, text: '时间不限 (周一至周日)' },    { type: 2, text: '工作日送 (周一至周五)' },    { type: 3, text: '周末配送 (周六至周日)' }]);
// 当前配送时间下标
const activeIndex = ref(0);
// 当前配送时间
const activeDelivery = computed(() => deliveryList.value[activeIndex.value]);
// 修改配送时间
const onChangeDelivery: UniHelper.SelectorPickerOnChange = (ev) => {
    activeIndex.value = ev.detail.value;
};



    
        
        
            张三 13333333333
            广东省 广州市 天河区 黑马程序员3
            
        
        
            请选择收货地址
            
        

        
        
            
                
                
                    ins风小碎花泡泡袖衬110-160cm
                    藏青小花 130
                    
                        99.00
                        99.00
                    
                    x5
                
            
        

        
        
            
                配送时间
                
                    {{ activeDelivery.text }}
                
            
            
                订单备注
                
            
        

        
        
            
                商品总价:
                495.00
            
            
                运费:
                5.00
            
        
    

    
    
        
            99.00
        
        提交订单
    



page {
    display: flex;
    flex-direction: column;
    height: 100%;
    overflow: hidden;
    background-color: #f4f4f4;
}

.symbol::before {
    content: '¥';
    font-size: 80%;
    margin-right: 5rpx;
}

.shipment {
    margin: 20rpx;
    padding: 30rpx 30rpx 30rpx 84rpx;
    font-size: 26rpx;
    border-radius: 10rpx;
    background: url(https://pcapi-xiaotuxian-front-devtest.itheima.net/miniapp/images/locate.png) 20rpx center / 50rpx no-repeat #fff;
    position: relative;

    .icon {
        font-size: 36rpx;
        color: #333;
        transform: translateY(-50%);
        position: absolute;
        top: 50%;
        right: 20rpx;
    }

    .user {
        color: #333;
        margin-bottom: 5rpx;
    }

    .address {
        color: #666;
    }
}

.goods {
    margin: 20rpx;
    padding: 0 20rpx;
    border-radius: 10rpx;
    background-color: #fff;

    .item {
        display: flex;
        padding: 30rpx 0;
        border-top: 1rpx solid #eee;

        &:first-child {
            border-top: none;
        }

        .picture {
            width: 170rpx;
            height: 170rpx;
            border-radius: 10rpx;
            margin-right: 20rpx;
        }

        .meta {
            flex: 1;
            display: flex;
            flex-direction: column;
            justify-content: center;
            position: relative;
        }

        .name {
            height: 80rpx;
            font-size: 26rpx;
            color: #444;
        }

        .attrs {
            line-height: 1.8;
            padding: 0 15rpx;
            margin-top: 6rpx;
            font-size: 24rpx;
            align-self: flex-start;
            border-radius: 4rpx;
            color: #888;
            background-color: #f7f7f8;
        }

        .prices {
            display: flex;
            align-items: baseline;
            margin-top: 6rpx;
            font-size: 28rpx;

            .pay-price {
                margin-right: 10rpx;
                color: #cf4444;
            }

            .price {
                font-size: 24rpx;
                color: #999;
                text-decoration: line-through;
            }
        }

        .count {
            position: absolute;
            bottom: 0;
            right: 0;
            font-size: 26rpx;
            color: #444;
        }
    }
}

.related {
    margin: 20rpx;
    padding: 0 20rpx;
    border-radius: 10rpx;
    background-color: #fff;

    .item {
        display: flex;
        justify-content: space-between;
        align-items: center;
        min-height: 80rpx;
        font-size: 26rpx;
        color: #333;
    }

    .input {
        flex: 1;
        text-align: right;
        margin: 20rpx 0;
        padding-right: 20rpx;
        font-size: 26rpx;
        color: #999;
    }

    .item .text {
        width: 125rpx;
    }

    .picker {
        color: #666;
    }

    .picker::after {
        content: '\e6c2';
    }
}

/* 结算清单 */
.settlement {
    margin: 20rpx;
    padding: 0 20rpx;
    border-radius: 10rpx;
    background-color: #fff;

    .item {
        display: flex;
        align-items: center;
        justify-content: space-between;
        height: 80rpx;
        font-size: 26rpx;
        color: #333;
    }

    .danger {
        color: #cf4444;
    }
}

/* 吸底工具栏 */
.toolbar {
    position: fixed;
    left: 0;
    right: 0;
    bottom: calc(var(--window-bottom));
    z-index: 1;

    background-color: #fff;
    height: 100rpx;
    padding: 0 20rpx;
    border-top: 1rpx solid #eaeaea;
    display: flex;
    justify-content: space-between;
    align-items: center;
    box-sizing: content-box;

    .total-pay {
        font-size: 40rpx;
        color: #cf4444;

        .decimal {
            font-size: 75%;
        }
    }

    .button {
        width: 220rpx;
        text-align: center;
        line-height: 72rpx;
        font-size: 26rpx;
        color: #fff;
        border-radius: 72rpx;
        background-color: #27ba9b;
    }

    .disabled {
        opacity: 0.6;
    }
}


结果:

image.png

1.1.2 封装订单API

image.png

1.1.3 获取订单的函数

image.png

1.1.4 封装类型声明文件

import type { AddressItem } from './address'

/** 获取预付订单 返回信息 */
export type OrderPreResult = {
  /** 商品集合 [ 商品信息 ] */
  goods: OrderPreGoods[]
  /** 结算信息 */
  summary: {
    /** 商品总价 */
    totalPrice: number
    /** 邮费 */
    postFee: number
    /** 应付金额 */
    totalPayPrice: number
  }
  /** 用户地址列表 [ 地址信息 ] */
  userAddresses: AddressItem[]
}

/** 商品信息 */
export type OrderPreGoods = {
  /** 属性文字,例如“颜色:瓷白色 尺寸:8寸” */
  attrsText: string
  /** 数量 */
  count: number
  /** id */
  id: string
  /** 商品名称 */
  name: string
  /** 实付单价 */
  payPrice: string
  /** 图片 */
  picture: string
  /** 原单价 */
  price: string
  /** SKUID */
  skuId: string
  /** 实付价格小计 */
  totalPayPrice: string
  /** 小计总价 */
  totalPrice: string
}

image.png

1.1.5 存储获取到的订单信息到ref

image.png

1.1.6 渲染获取到的数据到标签中

image.png

image.png

1.1.7 再在先前的购物车代码中添加一行结算代码

image.png 结果: image.png
渲染基本地址:(加上邮费) image.png 结果:

image.png

1.2 修改订单页面的地址

image.png

1.2.1 计算默认收货地址

image.png 结果:

image.png

1.2.2 涉及多页面通讯-使用store

1.2.2.1 创建store

image.png

1.2.2.2 使用Store 传参

image.png

1.2.2.3 渲染页面

通俗理解就是pinia是一个仓库,create和address都有读取的权限,但是这里的业务实现单项通信,所以可以理解为address在读,create在写 修改订单页使用store的数据

image.png 成功:

image.png

1.2.3 填写订单

image.png

1.2.3.1 封装请求API

image.png

image.png

1.2.3.2 从购物车那获取到需要的SKU组件

image.png image.png

1.2.3.3 从购物车那获取到需要的SKU组件

image.png

image.png

1.2.3.4 判断是否有参数 有的话就立即购买,没有就跳转到预付订单的信息

参数传递的来源

image.png

1.2.4 提交订单

image.png

1.2.4.1 类型声明文件

image.png

1.2.4.2 提交按钮的事件

image.png 提交成功后跳转

image.png

2 订单详情页

image.png

2.1 订单静态结构


import { useGuessList } from '@/composables'
import { ref } from 'vue'

// 获取屏幕边界到安全区域距离
const { safeAreaInsets } = uni.getSystemInfoSync()
// 猜你喜欢
const { guessRef, onScrolltolower } = useGuessList()
// 弹出层组件
const popup = ref()
// 取消原因列表
const reasonList = ref([  '商品无货',  '不想要了',  '商品信息填错了',  '地址信息填写错误',  '商品降价',  '其它',])
// 订单取消原因
const reason = ref('')
// 复制内容
const onCopy = (id: string) => {
  // 设置系统剪贴板的内容
  uni.setClipboardData({ data: id })
}
// 获取页面参数
const query = defineProps<{
  id: string
}>()



  
  
    
      
      
      
      订单详情
    
  
  
    
      
      
        
        
          等待付款
          
            应付金额: ¥ 99.00
            支付剩余
            002959 秒
          
          去支付
        
        
        
          
           待付款 
          
            
              再次购买
            
            
             模拟发货 
          
        
      
      
      
        
        
          
            您已在广州市天河区黑马程序员完成取件,感谢使用菜鸟驿站,期待再次为您服务。
          
           2023-04-14 13:14:20 
        
        
        
           张三 13333333333 
           广东省 广州市 天河区 黑马程序员 
        
      

      
      
        
          
            
            
              ins风小碎花泡泡袖衬110-160cm
              藏青小花, 130
              
                
                  ¥
                  99.00
                
              
              x1
            
          
          
          
            申请售后
             去评价 
          
        
        
        
          
            商品总价: 
            99.00
          
          
            运费: 
            10.00
          
          
            应付金额: 
            109.00
          
        
      

      
      
        订单信息
        
          
            订单编号: {{ query.id }} 复制
          
          下单时间: 2023-04-14 13:14:20
        
      

      
      

      
      
      
        
        
           去支付 
           取消订单 
        
        
        
          
            再次购买
          
          
           确认收货 
          
           去评价 
          
           删除订单 
        
      
    
    
      
      
    
  
  
  
    
      订单取消
      
        请选择取消订单的原因:
        
          {{ item }}
          
        
      
      
        取消
        确认
      
    
  



page {
  display: flex;
  flex-direction: column;
  height: 100%;
  overflow: hidden;
}

.navbar {
  width: 750rpx;
  color: #000;
  position: fixed;
  top: 0;
  left: 0;
  z-index: 9;
  /* background-color: #f8f8f8; */
  background-color: transparent;

  .wrap {
    position: relative;

    .title {
      height: 44px;
      display: flex;
      justify-content: center;
      align-items: center;
      font-size: 32rpx;
      /* color: #000; */
      color: transparent;
    }

    .back {
      position: absolute;
      left: 0;
      height: 44px;
      width: 44px;
      font-size: 44rpx;
      display: flex;
      align-items: center;
      justify-content: center;
      /* color: #000; */
      color: #fff;
    }
  }
}

.viewport {
  background-color: #f7f7f8;
}

.overview {
  display: flex;
  flex-direction: column;
  align-items: center;

  line-height: 1;
  padding-bottom: 30rpx;
  color: #fff;
  background-image: url(https://pcapi-xiaotuxian-front-devtest.itheima.net/miniapp/images/order_bg.png);
  background-size: cover;

  .status {
    font-size: 36rpx;
  }

  .status::before {
    margin-right: 6rpx;
    font-weight: 500;
  }

  .tips {
    margin: 30rpx 0;
    display: flex;
    font-size: 14px;
    align-items: center;

    .money {
      margin-right: 30rpx;
    }
  }

  .button-group {
    margin-top: 30rpx;
    display: flex;
    justify-content: center;
    align-items: center;
  }

  .button {
    width: 260rpx;
    height: 64rpx;
    margin: 0 10rpx;
    text-align: center;
    line-height: 64rpx;
    font-size: 28rpx;
    color: #27ba9b;
    border-radius: 68rpx;
    background-color: #fff;
  }
}

.shipment {
  line-height: 1.4;
  padding: 0 20rpx;
  margin: 20rpx 20rpx 0;
  border-radius: 10rpx;
  background-color: #fff;

  .locate,
  .item {
    min-height: 120rpx;
    padding: 30rpx 30rpx 25rpx 75rpx;
    background-size: 50rpx;
    background-repeat: no-repeat;
    background-position: 6rpx center;
  }

  .locate {
    background-image: url(https://pcapi-xiaotuxian-front-devtest.itheima.net/miniapp/images/locate.png);

    .user {
      font-size: 26rpx;
      color: #444;
    }

    .address {
      font-size: 24rpx;
      color: #666;
    }
  }

  .item {
    background-image: url(https://pcapi-xiaotuxian-front-devtest.itheima.net/miniapp/images/car.png);
    border-bottom: 1rpx solid #eee;
    position: relative;

    .message {
      font-size: 26rpx;
      color: #444;
    }

    .date {
      font-size: 24rpx;
      color: #666;
    }
  }
}

.goods {
  margin: 20rpx 20rpx 0;
  padding: 0 20rpx;
  border-radius: 10rpx;
  background-color: #fff;

  .item {
    padding: 30rpx 0;
    border-bottom: 1rpx solid #eee;

    .navigator {
      display: flex;
      margin: 20rpx 0;
    }

    .cover {
      width: 170rpx;
      height: 170rpx;
      border-radius: 10rpx;
      margin-right: 20rpx;
    }

    .meta {
      flex: 1;
      display: flex;
      flex-direction: column;
      justify-content: center;
      position: relative;
    }

    .name {
      height: 80rpx;
      font-size: 26rpx;
      color: #444;
    }

    .type {
      line-height: 1.8;
      padding: 0 15rpx;
      margin-top: 6rpx;
      font-size: 24rpx;
      align-self: flex-start;
      border-radius: 4rpx;
      color: #888;
      background-color: #f7f7f8;
    }

    .price {
      display: flex;
      margin-top: 6rpx;
      font-size: 24rpx;
    }

    .symbol {
      font-size: 20rpx;
    }

    .original {
      color: #999;
      text-decoration: line-through;
    }

    .actual {
      margin-left: 10rpx;
      color: #444;
    }

    .text {
      font-size: 22rpx;
    }

    .quantity {
      position: absolute;
      bottom: 0;
      right: 0;
      font-size: 24rpx;
      color: #444;
    }

    .action {
      display: flex;
      flex-direction: row-reverse;
      justify-content: flex-start;
      padding: 30rpx 0 0;

      .button {
        width: 200rpx;
        height: 60rpx;
        text-align: center;
        justify-content: center;
        line-height: 60rpx;
        margin-left: 20rpx;
        border-radius: 60rpx;
        border: 1rpx solid #ccc;
        font-size: 26rpx;
        color: #444;
      }

      .primary {
        color: #27ba9b;
        border-color: #27ba9b;
      }
    }
  }

  .total {
    line-height: 1;
    font-size: 26rpx;
    padding: 20rpx 0;
    color: #666;

    .row {
      display: flex;
      align-items: center;
      justify-content: space-between;
      padding: 10rpx 0;
    }

    .symbol::before {
      content: '¥';
      font-size: 80%;
      margin-right: 3rpx;
    }

    .primary {
      color: #cf4444;
      font-size: 36rpx;
    }
  }
}

.detail {
  line-height: 1;
  padding: 30rpx 20rpx 0;
  margin: 20rpx 20rpx 0;
  font-size: 26rpx;
  color: #666;
  border-radius: 10rpx;
  background-color: #fff;

  .title {
    font-size: 30rpx;
    color: #444;
  }

  .row {
    padding: 20rpx 0;

    .item {
      padding: 10rpx 0;
      display: flex;
      align-items: center;
    }

    .copy {
      border-radius: 20rpx;
      font-size: 20rpx;
      border: 1px solid #ccc;
      padding: 5rpx 10rpx;
      margin-left: 10rpx;
    }
  }
}

.toolbar-height {
  height: 100rpx;
  box-sizing: content-box;
}

.toolbar {
  position: fixed;
  left: 0;
  right: 0;
  bottom: calc(var(--window-bottom));
  z-index: 1;

  height: 100rpx;
  padding: 0 20rpx;
  display: flex;
  align-items: center;
  flex-direction: row-reverse;
  border-top: 1rpx solid #ededed;
  border-bottom: 1rpx solid #ededed;
  background-color: #fff;
  box-sizing: content-box;

  .button {
    display: flex;
    justify-content: center;
    align-items: center;

    width: 200rpx;
    height: 72rpx;
    margin-left: 15rpx;
    font-size: 26rpx;
    border-radius: 72rpx;
    border: 1rpx solid #ccc;
    color: #444;
  }

  .delete {
    order: 4;
  }

  .button {
    order: 3;
  }

  .secondary {
    order: 2;
    color: #27ba9b;
    border-color: #27ba9b;
  }

  .primary {
    order: 1;
    color: #fff;
    background-color: #27ba9b;
  }
}

.popup-root {
  padding: 30rpx 30rpx 0;
  border-radius: 10rpx 10rpx 0 0;
  overflow: hidden;

  .title {
    font-size: 30rpx;
    text-align: center;
    margin-bottom: 30rpx;
  }

  .description {
    font-size: 28rpx;
    padding: 0 20rpx;

    .tips {
      color: #444;
      margin-bottom: 12rpx;
    }

    .cell {
      display: flex;
      justify-content: space-between;
      align-items: center;
      padding: 15rpx 0;
      color: #666;
    }

    .icon::before {
      content: '\e6cd';
      font-family: 'erabbit' !important;
      font-size: 38rpx;
      color: #999;
    }

    .icon.checked::before {
      content: '\e6cc';
      font-size: 38rpx;
      color: #27ba9b;
    }
  }

  .footer {
    display: flex;
    justify-content: space-between;
    padding: 30rpx 0 40rpx;
    font-size: 28rpx;
    color: #444;

    .button {
      flex: 1;
      height: 72rpx;
      text-align: center;
      line-height: 72rpx;
      margin: 0 20rpx;
      color: #444;
      border-radius: 72rpx;
      border: 1rpx solid #ccc;
    }

    .primary {
      color: #fff;
      background-color: #27ba9b;
      border: none;
    }
  }
}


初始的静态页面:

image.png

1.2 判断页面项是否大于一项

如果当前页面栈只有一个页面的话,左上角就会只显示返回首页;
而如果是多个页面一个个进入的话,左上角还会有页面
所以我们要判断页面项是否大于一项

1.2.1 隐藏掉默认导航栏

image.png 原生导航栏被隐藏了(原本顶部刘海区域是白色的)

image.png

1.2.2 如果没有返回的上一页,那导航栏区域的‘<’返回箭头就不显示

使用currentPages获取页面栈,然后再判断,如果页面栈大于一,则说明可以返回上一级,就显示左返回箭头,否则显示返回首页的按钮 image.png 层层页面进入的效果:

image.png

1.3 滚动驱动动画

微信小程序的滚动模版:

image.png

1.3.1 找到原生的页面实例,并遵从语法写动态

image.png 结果:

image.png

image.png

1.3.2 对于导航栏和导航栏返回按钮也和1.3.2一样写动画效果

image.png 效果:

image.png

image.png

1.4订单详情渲染

image.png

1.4.1 封装请求API(这里有个id,作为请求参数)

image.png

数据类型文件

/** 订单详情 返回信息 */
export type OrderResult = {
  /** 订单编号 */
  id: string
  /** 订单状态,1为待付款、2为待发货、3为待收货、4为待评价、5为已完成、6为已取消 */
  orderState: number
  /** 倒计时--剩余的秒数 -1 表示已经超时,正数表示倒计时未结束 */
  countdown: number
  /** 商品集合 [ 商品信息 ] */
  skus: OrderSkuItem[]
  /** 收货人 */
  receiverContact: string
  /** 收货人手机 */
  receiverMobile: string
  /** 收货人完整地址 */
  receiverAddress: string
  /** 下单时间 */
  createTime: string
  /** 商品总价 */
  totalMoney: number
  /** 运费 */
  postFee: number
  /** 应付金额 */
  payMoney: number
}

/** 商品信息 */
export type OrderSkuItem = {
  /** sku id */
  id: string
  /** 商品 id */
  spuId: string
  /** 商品名称 */
  name: string
  /** 商品属性文字 */
  attrsText: string
  /** 数量 */
  quantity: number
  /** 购买时单价 */
  curPrice: number
  /** 图片地址 */
  image: string
}

1.4.2 封装对应的调用函数

image.png

1.4.3 使用条件语句渲染订单状态

image.png 为了便于使用,以及语义化,而不是1/2/3/4/5 来代表订单状态,我们封装一个文件

/** 订单状态枚举 */
export enum OrderState {
  /** 待付款 */
  DaiFuKuan = 1,
  /** 待发货 */
  DaiFaHuo = 2,
  /** 待收货 */
  DaiShouHuo = 3,
  /** 待评价 */
  DaiPingJia = 4,
  /** 已完成 */
  YiWanCheng = 5,
  /** 已取消 */
  YiQuXiao = 6,
}

/** 订单状态列表 */
export const orderStateList = [
  { id: 0, text: '' },
  { id: 1, text: '待付款' },
  { id: 2, text: '待发货' },
  { id: 3, text: '待收货' },
  { id: 4, text: '待评价' },
  { id: 5, text: '已完成' },
  { id: 6, text: '已取消' },
]

image.png

1.5 订单详情-代付款倒计时

使用uni-ui中的uni-countdown组件进行倒计时

image.png

1.5.1 准备uni-countdown组件

image.png

1.5.2 给countdown组件绑定一个onTimeup的事件并定义事件

image.png 倒计时结束后标记状态为已经取消 image.png 效果:

image.png 三秒倒计时结束后显示:

image.png

1.6 订单详情-订单支付

image.png

1.6.1 封装API

1.6.1.1 封装生产环境所需的API

image.png

1.6.1.2 封装开发环境所需要的API

1.6.2 调用API

image.png

1.6.3 处理事件

image.png

1.6.4 封装支付成功的静态页面


import { useGuessList } from '@/composables'

// 获取页面参数
const query = defineProps<{
  id: string
}>()

// 猜你喜欢
const { guessRef, onScrolltolower } = useGuessList()



  
    
    
      支付成功
      
        
          返回首页
        
        
          查看订单
        
      
    

    
    
  



page {
  display: flex;
  flex-direction: column;
  height: 100%;
  overflow: hidden;
}

.viewport {
  background-color: #f7f7f8;
}

.overview {
  line-height: 1;
  padding: 50rpx 0;
  color: #fff;
  background-color: #27ba9b;

  .status {
    font-size: 36rpx;
    font-weight: 500;
    text-align: center;
  }

  .status::before {
    display: block;
    font-size: 110rpx;
    margin-bottom: 20rpx;
  }

  .buttons {
    height: 60rpx;
    line-height: 60rpx;
    display: flex;
    justify-content: center;
    align-items: center;
    margin-top: 60rpx;
  }

  .button {
    text-align: center;
    margin: 0 10rpx;
    font-size: 28rpx;
    color: #fff;

    &:first-child {
      width: 200rpx;
      border-radius: 64rpx;
      border: 1rpx solid #fff;
    }
  }
}


1.6.5 跳转到支付成功页面

image.png

1.6.6 封装模拟支付的API

因为获取微信支付参数的那个API只能面向对应wxappid的用户开放,为了效果流程更加完善,我们封装一个模拟支付

image.png

1.6.7 页面调用模拟支付

image.png 支付成功:

image.png

1.7 订单详情-模拟发货

image.png

1.7.1 API

/**
 * 模拟发货-内测版
 * @description 在DEV环境下使用,仅在订单状态为待发货时,可模拟发货,调用后订单状态修改为待收货,包含模拟物流。
 * @param id 订单id
 */
export const getMemberOrderConsignmentByIdAPI = (id: string) => {
  return http({
    method: 'GET',
    url: `/member/order/consignment/${id}`,
  })
}

1.7.2 条件渲染

image.png

image.png

image.png

1.7.3 绑定事件

image.png

image.png 效果:

image.png

1.8 订单详情-确认收货

image.png

1.8.1 API

/**
 * 确认收货
 * @description 仅在订单状态为待收货时,可确认收货。
 * @param id 订单id
 */
export const putMemberOrderReceiptByIdAPI = (id: string) => {
  return http({
    method: 'PUT',
    url: `/member/order/${id}/receipt`,
  })
}

1.8.2条件渲染、事件绑定

image.png

1.8.3 事件处理函数

image.png 效果:

image.png

image.png

1.8 订单详情-订单物流

image.png

1.8.1 物流API封装以及类型说明文件

image.png

image.png

1.8.2 封装一个获取函数

image.png 保存获取到的物流信息

image.png

image.png

1.9 删除订单

image.png

1.9.1 封装API

/**
 * 删除订单
 * @description 仅在订单状态为待评价,已完成,已取消时,可删除订单。
 * @param data ids 订单集合
 */
export const deleteMemberOrderAPI = (data: { ids: string[] }) => {
  return http({
    method: 'DELETE',
    url: `/member/order`,
    data,
  })
}

1.9.2 绑定事件

image.png

1.9.3 处理绑定事件

image.png 效果:

image.png

1.10 Tabs滑动切换

image.png

1.10.1 静态结构

<script setup lang="ts">
import { ref } from 'vue'

// 获取屏幕边界到安全区域距离
const { safeAreaInsets } = uni.getSystemInfoSync()
// tabs 数据
const orderTabs = ref([
  { orderState: 0, title: '全部' },
  { orderState: 1, title: '待付款' },
  { orderState: 2, title: '待发货' },
  { orderState: 3, title: '待收货' },
  { orderState: 4, title: '待评价' },
])
</script>

<template>
  <view class="viewport">
    <!-- tabs -->
    <view class="tabs">
      <text class="item" v-for="item in 5" :key="item"> 待付款 </text>
      <!-- 游标 -->
      <view class="cursor" :style="{ left: 0 * 20 + '%' }"></view>
    </view>
    <!-- 滑动容器 -->
    <swiper class="swiper">
      <!-- 滑动项 -->
      <swiper-item v-for="item in 5" :key="item">
        <!-- 订单列表 -->
        <scroll-view scroll-y class="orders">
          <view class="card" v-for="item in 2" :key="item">
            <!-- 订单信息 -->
            <view class="status">
              <text class="date">2023-04-14 13:14:20</text>
              <!-- 订单状态文字 -->
              <text>待付款</text>
              <!-- 待评价/已完成/已取消 状态: 展示删除订单 -->
              <text class="icon-delete"></text>
            </view>
            <!-- 商品信息,点击商品跳转到订单详情,不是商品详情 -->
            <navigator
              v-for="sku in 2"
              :key="sku"
              class="goods"
              :url="`/pagesOrder/detail/detail?id=1`"
              hover-class="none"
            >
              <view class="cover">
                <image
                  mode="aspectFit"
                  src="https://yanxuan-item.nosdn.127.net/c07edde1047fa1bd0b795bed136c2bb2.jpg"
                ></image>
              </view>
              <view class="meta">
                <view class="name ellipsis">ins风小碎花泡泡袖衬110-160cm</view>
                <view class="type">藏青小花 130</view>
              </view>
            </navigator>
            <!-- 支付信息 -->
            <view class="payment">
              <text class="quantity">共5件商品</text>
              <text>实付</text>
              <text class="amount"> <text class="symbol">¥</text>99</text>
            </view>
            <!-- 订单操作按钮 -->
            <view class="action">
              <!-- 待付款状态:显示去支付按钮 -->
              <template v-if="true">
                <view class="button primary">去支付</view>
              </template>
              <template v-else>
                <navigator
                  class="button secondary"
                  :url="`/pagesOrder/create/create?orderId=id`"
                  hover-class="none"
                >
                  再次购买
                </navigator>
                <!-- 待收货状态: 展示确认收货 -->
                <view v-if="false" class="button primary">确认收货</view>
              </template>
            </view>
          </view>
          <!-- 底部提示文字 -->
          <view class="loading-text" :style="{ paddingBottom: safeAreaInsets?.bottom + 'px' }">
            {{ true ? '没有更多数据~' : '正在加载...' }}
          </view>
        </scroll-view>
      </swiper-item>
    </swiper>
  </view>
</template>

<style lang="scss">
page {
  height: 100%;
  overflow: hidden;
}

.viewport {
  height: 100%;
  display: flex;
  flex-direction: column;
  background-color: #fff;
}

// tabs
.tabs {
  display: flex;
  justify-content: space-around;
  line-height: 60rpx;
  margin: 0 10rpx;
  background-color: #fff;
  box-shadow: 0 4rpx 6rpx rgba(240, 240, 240, 0.6);
  position: relative;
  z-index: 9;

  .item {
    flex: 1;
    text-align: center;
    padding: 20rpx;
    font-size: 28rpx;
    color: #262626;
  }

  .cursor {
    position: absolute;
    left: 0;
    bottom: 0;
    width: 20%;
    height: 6rpx;
    padding: 0 50rpx;
    background-color: #27ba9b;
    /* 过渡效果 */
    transition: all 0.4s;
  }
}

// swiper
.swiper {
  background-color: #f7f7f8;
}

// 订单列表
.orders {
  .card {
    min-height: 100rpx;
    padding: 20rpx;
    margin: 20rpx 20rpx 0;
    border-radius: 10rpx;
    background-color: #fff;

    &:last-child {
      padding-bottom: 40rpx;
    }
  }

  .status {
    display: flex;
    align-items: center;
    justify-content: space-between;
    font-size: 28rpx;
    color: #999;
    margin-bottom: 15rpx;

    .date {
      color: #666;
      flex: 1;
    }

    .primary {
      color: #ff9240;
    }

    .icon-delete {
      line-height: 1;
      margin-left: 10rpx;
      padding-left: 10rpx;
      border-left: 1rpx solid #e3e3e3;
    }
  }

  .goods {
    display: flex;
    margin-bottom: 20rpx;

    .cover {
      width: 170rpx;
      height: 170rpx;
      margin-right: 20rpx;
      border-radius: 10rpx;
      overflow: hidden;
      position: relative;
    }

    .quantity {
      position: absolute;
      bottom: 0;
      right: 0;
      line-height: 1;
      padding: 6rpx 4rpx 6rpx 8rpx;
      font-size: 24rpx;
      color: #fff;
      border-radius: 10rpx 0 0 0;
      background-color: rgba(0, 0, 0, 0.6);
    }

    .meta {
      flex: 1;
      display: flex;
      flex-direction: column;
      justify-content: center;
    }

    .name {
      height: 80rpx;
      font-size: 26rpx;
      color: #444;
    }

    .type {
      line-height: 1.8;
      padding: 0 15rpx;
      margin-top: 10rpx;
      font-size: 24rpx;
      align-self: flex-start;
      border-radius: 4rpx;
      color: #888;
      background-color: #f7f7f8;
    }

    .more {
      flex: 1;
      display: flex;
      align-items: center;
      justify-content: center;
      font-size: 22rpx;
      color: #333;
    }
  }

  .payment {
    display: flex;
    justify-content: flex-end;
    align-items: center;
    line-height: 1;
    padding: 20rpx 0;
    text-align: right;
    color: #999;
    font-size: 28rpx;
    border-bottom: 1rpx solid #eee;

    .quantity {
      font-size: 24rpx;
      margin-right: 16rpx;
    }

    .amount {
      color: #444;
      margin-left: 6rpx;
    }

    .symbol {
      font-size: 20rpx;
    }
  }

  .action {
    display: flex;
    justify-content: flex-end;
    align-items: center;
    padding-top: 20rpx;

    .button {
      width: 180rpx;
      height: 60rpx;
      display: flex;
      justify-content: center;
      align-items: center;
      margin-left: 20rpx;
      border-radius: 60rpx;
      border: 1rpx solid #ccc;
      font-size: 26rpx;
      color: #444;
    }

    .secondary {
      color: #27ba9b;
      border-color: #27ba9b;
    }

    .primary {
      color: #fff;
      background-color: #27ba9b;
    }
  }

  .loading-text {
    text-align: center;
    font-size: 28rpx;
    color: #666;
    padding: 20rpx 0;
  }
}
</style>

效果:

image.png

1.10.2 处理下标

image.png

image.png

1.10.3 列表页面要与订单对应

image.png 游标和滑动容器绑定的实现逻辑:

image.png

image.png 成功:

image.png

1.11 tabs页面跳转高亮(点击那个,进入页面就那个高亮)

image.png

image.png 成功

1.12 列表渲染

image.png

1.12.1 列表组件的封装

准备结构:

image.png

1.12.2 父传子

image.png

image.png

1.12.3 获取API

/**
 * 获取订单列表
 * @param data orderState 订单状态
 */
export const getMemberOrderAPI = (data: OrderListParams) => {
  return http<OrderListResult>({
    method: 'GET',
    url: `/member/order`,
    data,
  })
}

import type { PageParams } from '@/types/global'
/** 订单列表参数 */
export type OrderListParams = PageParams & { orderState: number }

/** 订单列表 */
export type OrderListResult = {
  /** 总记录数 */
  counts: number
  /** 数据集合    [ 订单信息 ] */
  items: OrderItem[]
  /** 当前页码 */
  page: number
  /** 总页数 */
  pages: number
  /** 页尺寸 */
  pageSize: number
}

/** 订单列表项 */
export type OrderItem = OrderResult & {
  /** 总件数 */
  totalNum: number
}

1.12.3 准备请求参数

image.png

1.12.4 页面渲染