uni-app购物车页面详细代码

news2024/9/29 1:31:10

效果图:在这里插入图片描述

这里的购物车加减用的是uni-app中的sku插件

代码附下(全):

<script setup lang="ts">
import {
  reqMemberCartList,
  reqMemberdelentCart,
  reqMemberPutCart,
  putMemberCartSelectedAPI,
} from '@/services/cart/cart'
import type { CartItem } from '@/services/cart/type'
import { onShow } from '@dcloudio/uni-app'
import { ref } from 'vue'
import { useMemberStore } from '@/stores/modules/member'
import type { InputNumberBoxEvent } from '@/components/vk-data-input-number-box/vk-data-input-number-box'
import { computed } from 'vue'
let useMemberStores = useMemberStore()
//列表数组
let cartList = ref<CartItem[]>([])

//获取购物车列表
let GetCart = async () => {
  let res = await reqMemberCartList()
  cartList.value = res.result
}
// 删除购物车
const onDelentCart = (skuId: string) => {
  // 弹窗二次确认
  uni.showModal({
    content: '是否删除',
    success: async (res) => {
      if (res.confirm) {
        // 后端删除单品
        await reqMemberdelentCart({ ids: [skuId] })
        // 重新获取列表
        GetCart()
      }
    },
  })
}
// 修改商品数量
const onChangeCount = (ev: InputNumberBoxEvent) => {
  reqMemberPutCart(ev.index, { count: ev.value })
}
// 选中状态
const onChangeSelected = (item: CartItem) => {
  item.selected = !item.selected
  // 向后端发送请求
  reqMemberPutCart(item.skuId, { selected: item.selected })
}
// 全选
let onChangeSelectedAll = () => {
  // 全选状态取反
  const _isSelectedAll = !selectdAll.value
  // 前端数据更新
  cartList.value.forEach((item) => {
    item.selected = _isSelectedAll
  })
  // 后端数据更新
  putMemberCartSelectedAPI({ selected: _isSelectedAll })
}

// 计算全选状态
let selectdAll = computed(() => {
  return cartList.value.length && cartList.value.every((c) => c.selected)
})

// 计算选中单品列表
let selectedCartList = computed(() => {
  return cartList.value.filter((v) => v.selected)
})
// 计算选中总件数
let selectedCartListCount = computed(() => {
  return selectedCartList.value.reduce((sum, item) => sum + item.count, 0)
})
// 计算选中总金额
const selectedCartListMoney = computed(() => {
  return selectedCartList.value
    .reduce((sum, item) => sum + item.count * item.nowPrice, 0)
    .toFixed(2)
})
// 结算按钮
const gotoPayment = () => {
  if (selectedCartListCount.value === 0) {
    return uni.showToast({
      icon: 'none',
      title: '请选择商品',
    })
  }
  // 跳转到结算页
  uni.navigateTo({ url: '/pagesOrder/create/create' })
}
// 初始化调用
onShow(() => {
  // 用户已登录才允许调用
  if (useMemberStores.profile) {
    GetCart()
  }
})
</script>

<template>
  <scroll-view scroll-y class="scroll-view">
    <!-- 已登录: 显示购物车 -->
    <template v-if="cartList.length">
      <!-- 购物车列表 -->
      <view class="cart-list" v-if="true">
        <!-- 优惠提示 -->
        <view class="tips">
          <text class="label">满减</text>
          <text class="desc">1, 即可享受9折优惠</text>
        </view>
        <!-- 滑动操作分区 -->
        <uni-swipe-action>
          <!-- 滑动操作项 -->
          <uni-swipe-action-item v-for="item in cartList" :key="item.skuId" class="cart-swipe">
            <!-- 商品信息 -->
            <view class="goods">
              <!-- 选中状态 -->
              <text
                @tap="onChangeSelected(item)"
                class="checkbox"
                :class="{ checked: item.selected }"
              ></text>
              <navigator
                :url="`/pages/article/index?id=${item.id}`"
                hover-class="none"
                class="navigator"
              >
                <image mode="aspectFill" class="picture" :src="item.picture"></image>
                <view class="meta">
                  <view class="name ellipsis">{{ item.name }}</view>
                  <view class="attrsText ellipsis">{{ item.attrsText }}</view>
                  <view class="price">{{ item.nowPrice }}</view>
                </view>
              </navigator>
              <!-- 商品数量 -->
              <view class="count">
                <vk-data-input-number-box
                  v-model="item.count"
                  :min="1"
                  :max="item.stock"
                  :index="item.skuId"
                  @change="onChangeCount"
                />
              </view>
            </view>
            <!-- 右侧删除按钮 -->
            <template #right>
              <view class="cart-swipe-right">
                <button class="button delete-button" @tap="onDelentCart(item.skuId)">删除</button>
              </view>
            </template>
          </uni-swipe-action-item>
        </uni-swipe-action>
      </view>
      <!-- 购物车空状态 -->
      <view class="cart-blank" v-else>
        <image src="/static/images/blank_cart.png" class="image" />
        <text class="text">购物车还是空的,快来挑选好货吧</text>
        <navigator open-type="switchTab" url="/pages/index/index" hover-class="none">
          <button class="button">去首页看看</button>
        </navigator>
      </view>
      <!-- 吸底工具栏 -->
      <view class="toolbar">
        <text class="all" :class="{ checked: true }" @tap="onChangeSelectedAll">全选</text>
        <text class="text">合计:</text>
        <text class="amount">{{ selectedCartListMoney }}</text>
        <view class="button-grounp">
          <view class="button payment-button" :class="{ disabled: true }" @tap="gotoPayment">
            去结算({{ selectedCartListCount }})
          </view>
        </view>
      </view>
    </template>
    <!-- 未登录: 提示登录 -->
    <view class="login-blank" v-else>
      <text class="text">登录后可查看购物车中的商品</text>
      <navigator url="/pages/login/login" hover-class="none">
        <button class="button">去登录</button>
      </navigator>
    </view>
    <!-- 猜你喜欢 -->
    <XtxGuess ref="guessRef"></XtxGuess>
    <!-- 底部占位空盒子 -->
    <view class="toolbar-height"></view>
  </scroll-view>
</template>

<style lang="scss">
// 根元素
:host {
  height: 100vh;
  display: flex;
  flex-direction: column;
  overflow: hidden;
  background-color: #f7f7f8;
}

// 滚动容器
.scroll-view {
  flex: 1;
}

// 购物车列表
.cart-list {
  padding: 0 20rpx;

  // 优惠提示
  .tips {
    display: flex;
    align-items: center;
    line-height: 1;
    margin: 30rpx 10rpx;
    font-size: 26rpx;
    color: #666;

    .label {
      color: #fff;
      padding: 7rpx 15rpx 5rpx;
      border-radius: 4rpx;
      font-size: 24rpx;
      background-color: #27ba9b;
      margin-right: 10rpx;
    }
  }

  // 购物车商品
  .goods {
    display: flex;
    padding: 20rpx 20rpx 20rpx 80rpx;
    border-radius: 10rpx;
    background-color: #fff;
    position: relative;

    .navigator {
      display: flex;
    }

    .checkbox {
      position: absolute;
      top: 0;
      left: 0;

      display: flex;
      align-items: center;
      justify-content: center;
      width: 80rpx;
      height: 100%;

      &::before {
        content: '\e6cd';
        font-family: 'erabbit' !important;
        font-size: 40rpx;
        color: #444;
      }

      &.checked::before {
        content: '\e6cc';
        color: #27ba9b;
      }
    }

    .picture {
      width: 170rpx;
      height: 170rpx;
    }

    .meta {
      flex: 1;
      display: flex;
      flex-direction: column;
      justify-content: space-between;
      margin-left: 20rpx;
    }

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

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

    .price {
      line-height: 1;
      font-size: 26rpx;
      color: #444;
      margin-bottom: 2rpx;
      color: #cf4444;

      &::before {
        content: '¥';
        font-size: 80%;
      }
    }

    // 商品数量
    .count {
      position: absolute;
      bottom: 20rpx;
      right: 5rpx;

      display: flex;
      justify-content: space-between;
      align-items: center;
      width: 220rpx;
      height: 48rpx;

      .text {
        height: 100%;
        padding: 0 20rpx;
        font-size: 32rpx;
        color: #444;
      }

      .input {
        height: 100%;
        text-align: center;
        border-radius: 4rpx;
        font-size: 24rpx;
        color: #444;
        background-color: #f6f6f6;
      }
    }
  }

  .cart-swipe {
    display: block;
    margin: 20rpx 0;
  }

  .cart-swipe-right {
    display: flex;
    height: 100%;

    .button {
      display: flex;
      justify-content: center;
      align-items: center;
      width: 50px;
      padding: 6px;
      line-height: 1.5;
      color: #fff;
      font-size: 26rpx;
      border-radius: 0;
    }

    .delete-button {
      background-color: #cf4444;
    }
  }
}

// 空状态
.cart-blank,
.login-blank {
  display: flex;
  justify-content: center;
  align-items: center;
  flex-direction: column;
  height: 60vh;
  .image {
    width: 400rpx;
    height: 281rpx;
  }
  .text {
    color: #444;
    font-size: 26rpx;
    margin: 20rpx 0;
  }
  .button {
    width: 240rpx !important;
    height: 60rpx;
    line-height: 60rpx;
    margin-top: 20rpx;
    font-size: 26rpx;
    border-radius: 60rpx;
    color: #fff;
    background-color: #27ba9b;
  }
}

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

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

  .all {
    margin-left: 25rpx;
    font-size: 14px;
    color: #444;
    display: flex;
    align-items: center;
  }

  .all::before {
    font-family: 'erabbit' !important;
    content: '\e6cd';
    font-size: 40rpx;
    margin-right: 8rpx;
  }

  .checked::before {
    content: '\e6cc';
    color: #27ba9b;
  }

  .text {
    margin-right: 8rpx;
    margin-left: 32rpx;
    color: #444;
    font-size: 14px;
  }

  .amount {
    font-size: 20px;
    color: #cf4444;

    .decimal {
      font-size: 12px;
    }

    &::before {
      content: '¥';
      font-size: 12px;
    }
  }

  .button-grounp {
    margin-left: auto;
    display: flex;
    justify-content: space-between;
    text-align: center;
    line-height: 72rpx;
    font-size: 13px;
    color: #fff;

    .button {
      width: 240rpx;
      margin: 0 10rpx;
      border-radius: 72rpx;
    }

    .payment-button {
      background-color: #27ba9b;

      &.disabled {
        opacity: 0.6;
      }
    }
  }
}
// 底部占位空盒子
.toolbar-height {
  height: 100rpx;
}
</style>

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

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

相关文章

linux后台进程的总结

文章目录 方案1 nohup &方案2 screen 方案1 nohup & 1、单独使用 nohup 执行脚本&#xff0c;如下图所示&#xff0c;终端会被接管&#xff0c;就是标准输入stdin 被关闭了&#xff0c;使用ctrlc会导致终止执行&#xff0c;但是可以关闭这个终端&#xff0c;重新打开终…

c5060:out can‘t be used with used with non-varying visibility

openGL系列文章目录 文章目录 openGL系列文章目录前言一、GLSL language integration是什么&#xff1f;二、GLSL language integration配置二、GLSL language integration编译报错解决 前言 GLSL插件下载和安装&#xff1a;GLSL language integration下载地址 你也可以在visu…

【python可视化大屏】使用python实现可拖拽数据可视化大屏

介绍&#xff1a; 我在前几期分享了关于爬取weibo评论的爬虫&#xff0c;同时也分享了如何去进行数据可视化的操作。但是之前的可视化都是单独的&#xff0c;没有办法在一个界面上展示的。这样一来呢&#xff0c;大家在看的时候其实是很不方便的&#xff0c;就是没有办法一目了…

软件测试|教你使用dataclass

前言 当我们需要在Python中定义一种简单的数据容器类时&#xff0c;dataclass是一个非常有用的工具。它允许我们轻松地创建具有一些自动化特性的类&#xff0c;例如自动生成__init__()、__repr__()和__eq__()等方法。本文将详细介绍dataclass的使用&#xff0c;并提供示例来说…

Qt点击按钮在其附近弹出一个窗口

效果 FS_PopupWidget.h #ifndef FS_POPUPWIDGET_H #define FS_POPUPWIDGET_H#pragma once#include <QToolButton> #include <QWidgetAction> #include <QPointer>class QMenu;class FS_PopupWidget : public QToolButton {Q_OBJECTpublic:FS_PopupWidget(QW…

3dmax有哪些技巧?3damx不为人知的秘密

在装修设计和建筑可视化领域&#xff0c;3D MAX是一款强大的工具&#xff0c;可以帮助我们创建出高质量的3D模型和动画。然而&#xff0c;要充分发挥其潜力&#xff0c;我们需要掌握一些3D MAX渲染的技巧。以下是一些实用的技巧&#xff0c;帮助你提高渲染效率和质量。 合理设…

【总结】Dinky学习笔记

概述 Dinky 是一个开箱即用、易扩展&#xff0c;以 Apache Flink 为基础&#xff0c;连接 OLAP 和数据湖等众多框架的一站式实时计算平台&#xff0c;致力于流批一体和湖仓一体的探索与实践 官网&#xff1a;Dinky 核心特性 沉浸式&#xff1a;提供专业的 DataStudio 功能&a…

mysql8 源码编译 客户端连接运行 报段异常解决

mysql8 源码编译 客户端连接运行 报段异常解决。解决方案&#xff1a;删除之前编译的文件。先安装libncurses-dev依赖&#xff0c;在重新编译。原因&#xff1a;第一次编译没有libncurses-dev依赖&#xff0c;编译告警&#xff0c;再次编译有缓存&#xff0c;没有引入声明头文件…

Umi3 创建,配置环境,路由传参(代码示例)

目录 创建项目 配置环境 创建脚手架 项目结构及其目录、 路由 配置路由 嵌套路由 编程式导航和声明式导航 声明式导航 编程式导航 约定式路由 路由传参 query传参&#xff08;问号&#xff09; 接收参数 params传参&#xff08;动态传参&#xff09; 接收参数 创…

[zabbix] zabbix监控

一、温习zabbix自定义监控 二、zabbix 自动发现与自动注册 2.1 zabbix 自动发现 //zabbix 自动发现&#xff08;对于 agent2 是被动模式&#xff09; zabbix server 主动的去发现所有的客户端&#xff0c;然后将客户端的信息登记在服务端上。 缺点是如果定义的网段中的主机数…

SpringBoot教程(五) | SpringBoot中Controller详解

SpringBoot教程(五) | SpringBoot中Controller详解 SpringBoot整合SpringMvc其实千面一直讲的都是。只需要我们在pom文件中引入 web的starter就可以了&#xff0c;然后我们就可以正常使用springMvc中的功能了。所以本篇文章可能更多的是回顾&#xff0c;回顾一下springMVC中的…

Centos7 制作系统镜像iso文件

Centos7 制作系统镜像iso文件 1. 系统备份镜像1.1 安装mondo1.2 制作备份镜像1.3 恢复系统 1. 系统备份镜像 这部分针对有系统备份需求&#xff0c;防止系统遭受意外状况&#xff0c;无法紧急恢复。整体备份内容较大&#xff0c;建议考虑后再进行操作 本次使用mondo软件进行备…

代码随想录算法训练营第28天 | 93.复原IP地址 78.子集 90.子集II

目录 93.复原IP地址 &#x1f4a1;解题思路 回溯三部曲 # 判断子串是否合法 &#x1f4bb;实现代码 78.子集 &#x1f4a1;解题思路 回溯三部曲 &#x1f4bb;实现代码 90.子集II &#x1f4a1;解题思路 &#x1f4bb;实现代码 93.复原IP地址 题目链接&#x…

Rocketmq rust版本-开篇

我是蚂蚁背大象(Apache EventMesh PMC&Committer)&#xff0c;文章对你有帮助给Rocketmq-rust star,关注我GitHub:mxsm&#xff0c;文章有不正确的地方请您斧正,创建ISSUE提交PR~谢谢! Emal:mxsmapache.com Rust重构Rocketmq,大家好我是mxsm(Apache EventMesh PMC&Comm…

Linux网络编程---IP 地址格式转换函数

Linux网络编程—IP 地址格式转换函数 我们更容易阅读的IP地址是以点分十进制表示的&#xff0c;例如&#xff1a;192.168.5.10 &#xff0c;这是一种字符串的形式&#xff0c;但是计算器所需要的IP地址是以二进制进行表示&#xff0c;这便需要我们在点分十进制字符串和二进制地…

Swift 周报 第四十五期

文章目录 前言新闻和社区苹果或将扩充健康版图&#xff0c;为Apple Watch X铺路更新后的《Apple Developer Program 许可协议》现已发布 提案通过的提案 Swift论坛推荐博文话题讨论关于我们 前言 本期是 Swift 编辑组整理周报的第四十五期&#xff0c;每个模块已初步成型。各位…

【Linux】初识Linux及几个基本指令

Hello everybody!算算时间我已经有一个多月没有更新啦&#xff01;因为本专业是纺织工程&#xff0c;所以一直在复习应付期末考试\(0^◇^0)/。那好&#xff0c;废话不多说。让我们进入今天的主题&#xff01; 关于Linux系统可能很多同学不是很熟悉&#xff0c;有的人可能听过&…

《ORANGE’S:一个操作系统的实现》读书笔记(三十一)文件系统(六)

上一篇文章记录了对文件的读写操作&#xff0c;那么文件操作到目前为止&#xff0c;已经完成了创建和读写&#xff0c;还剩下的常用操作就是删除文件了。这篇文章就来记录删除文件的实现以及总结一下为文件系统添加系统调用的步骤。 删除文件 删除是添加的反过程&#xff0c;…

EndNote登录一直显示The username/password specified is not valid

EndNote登录一直显示The username/password specified is not valid EndNote20今天想打开之前的share library的时候一直显示 ‘The Username/password specified is not valid’&#xff0c;查了很多解决方案&#xff0c;现在献上解决方案&#xff1a; 该密码然后重新登陆…

如何在 SwiftUI 中使用 AccessibilityCustomContentKey 修饰符

文章目录 前言创建 User 结构体添加辅助修饰符使用新的修饰符使用修饰符来替换和覆盖数据可运行代码总结 前言 SwiftUI 3 发布了许多新的辅助功能 API&#xff0c;我们可以利用这些 API 以轻松的方式显著提高用户体验。本篇文章来聊聊另一个新的 API&#xff0c;我们可以使用 …