123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500 |
- <template>
- <view class="container">
- <!-- 页面顶部 -->
- <view v-if="list.length" class="head-info">
- <view class="cart-total">
- <text>共</text>
- <text class="active">{{ total }}</text>
- <text>件商品</text>
- </view>
- <view class="cart-edit" @click="handleToggleMode">
- <view v-if="mode == 'normal'" class="normal">
- <text class="icon iconfont icon-bianji"></text>
- <text>编辑</text>
- </view>
- <view v-if="mode == 'edit'" class="edit">
- <text>完成</text>
- </view>
- </view>
- </view>
- <!-- 购物车商品列表 -->
- <view v-if="list.length" class="cart-list">
- <view class="cart-item" v-for="(item, index) in list" :key="index">
- <label class="item-radio" @click.stop="handleCheckItem(item.id)">
- <radio class="radio" color="#fa2209" :checked="inArray(item.id, checkedIds)" />
- </label>
- <view class="goods-image" @click="onTargetGoods(item.goods_id)">
- <image class="image" :src="item.goods.goods_image" mode="scaleToFill"></image>
- </view>
- <view class="item-content">
- <view class="goods-title" @click="onTargetGoods(item.goods_id)">
- <text class="twoline-hide">{{ item.goods.goods_name }}</text>
- </view>
- <view class="goods-props clearfix">
- <view class="goods-props-item" v-for="(props, idx) in item.goods.skuInfo.goods_props" :key="idx">
- <text>{{ props.value.name }}</text>
- </view>
- </view>
- <view class="item-foot">
- <view class="goods-price">
- <text class="unit">¥</text>
- <text class="value">{{ item.goods.skuInfo.goods_price }}</text>
- </view>
- <view class="stepper">
- <u-number-box :min="1" :value="item.goods_num" :step="1" @change="onChangeStepper($event, item)" />
- </view>
- </view>
- </view>
- </view>
- </view>
- <!-- 购物车数据为空 -->
- <empty v-if="!list.length" :isLoading="isLoading" :custom-style="{ padding: '180rpx 50rpx' }" tips="您的购物车是空的, 快去逛逛吧">
- <view slot="slot" class="empty-ipt" @click="onTargetIndex">
- <text>去逛逛</text>
- </view>
- </empty>
- <!-- 底部操作栏 -->
- <view v-if="list.length" class="footer-fixed">
- <label class="all-radio" @click="handleCheckAll">
- <radio class="radio" color="#fa2209" :checked="checkedIds.length > 0 && checkedIds.length === list.length" />
- <text>全选</text>
- </label>
- <view class="total-info">
- <text>合计:</text>
- <view class="goods-price">
- <text class="unit">¥</text>
- <text class="value">{{ totalPrice }}</text>
- </view>
- </view>
- <view class="cart-action">
- <view class="btn-wrapper">
- <!-- dev:下面的disabled条件使用checkedIds.join方式判断 -->
- <!-- dev:通常情况下vue项目使用checkedIds.length更合理, 但是length属性在微信小程序中不起作用 -->
- <view v-if="mode == 'normal'" class="btn-item btn-main" :class="{ disabled: checkedIds.join() == '' }" @click="handleOrder()">
- <text>去结算 {{ checkedIds.length > 0 ? `(${checkedIds.length})` : '' }}</text>
- </view>
- <view v-if="mode == 'edit'" class="btn-item btn-main" :class="{ disabled: !checkedIds.length }" @click="handleDelete()">
- <text>删除</text>
- </view>
- </view>
- </view>
- </view>
- </view>
- </template>
- <script>
- import { inArray, arrayIntersect, debounce } from '@/utils/util'
- import { checkLogin, setCartTotalNum, setCartTabBadge } from '@/core/app'
- import * as CartApi from '@/api/cart'
- import Empty from '@/components/empty'
- const CartIdsIndex = 'CartIds'
- export default {
- components: {
- Empty
- },
- data() {
- return {
- inArray,
- // 正在加载
- isLoading: true,
- // 当前模式: normal正常 edit编辑
- mode: 'normal',
- // 购物车商品列表
- list: [],
- // 购物车商品总数量
- total: null,
- // 选中的商品ID记录
- checkedIds: [],
- // 选中的商品总金额
- totalPrice: '0.00'
- }
- },
- watch: {
- // 监听选中的商品
- checkedIds: {
- handler(val) {
- // 计算合计金额
- this.onCalcTotalPrice()
- // 记录到缓存中
- uni.setStorageSync(CartIdsIndex, val)
- },
- immediate: false
- },
- // 监听购物车商品总数量
- total(val) {
- // 缓存并设置角标
- setCartTotalNum(val)
- setCartTabBadge()
- }
- },
- /**
- * 生命周期函数--监听页面显示
- */
- onShow(options) {
- // 获取购物车商品列表
- checkLogin() ? this.getCartList() : this.isLoading = false
- // 获取缓存中的选中记录
- this.checkedIds = uni.getStorageSync(CartIdsIndex)
- },
- methods: {
- // 计算合计金额 (根据选中的商品)
- onCalcTotalPrice() {
- const app = this
- // 选中的商品记录
- const checkedList = app.list.filter(item => inArray(item.id, app.checkedIds))
- // 计算总金额
- let tempPrice = 0;
- checkedList.forEach(item => {
- // 商品单价, 为了方便计算先转换单位为分 (整数)
- const unitPrice = item.goods.skuInfo.goods_price * 100
- tempPrice += unitPrice * item.goods_num
- })
- app.totalPrice = (tempPrice / 100).toFixed(2)
- },
- // 获取购物车商品列表
- getCartList() {
- const app = this
- app.isLoading = true
- CartApi.list()
- .then(result => {
- app.list = result.data.list
- app.total = result.data.cartTotal
- // 清除checkedIds中无效的ID
- app.onClearInvalidId()
- })
- .finally(() => app.isLoading = false)
- },
- // 清除checkedIds中无效的ID
- onClearInvalidId() {
- const app = this
- const listIds = app.list.map(item => item.id)
- app.checkedIds = arrayIntersect(listIds, app.checkedIds)
- },
- // 切换当前模式
- handleToggleMode() {
- this.mode = this.mode == 'normal' ? 'edit' : 'normal'
- },
- // 监听步进器更改事件
- onChangeStepper({ value }, item) {
- // 这里是组织首次启动时的执行
- if (item.goods_num == value) return
- // 记录一个节流函数句柄
- if (!item.debounceHandle) {
- item.oldValue = item.goods_num
- item.debounceHandle = debounce(this.onUpdateCartNum, 500)
- }
- // 更新商品数量
- item.goods_num = value
- // 提交更新购物车数量 (节流)
- item.debounceHandle(item, item.oldValue, value)
- },
- // 提交更新购物车数量
- onUpdateCartNum(item, oldValue, newValue) {
- const app = this
- CartApi.update(item.goods_id, item.goods_sku_id, newValue)
- .then(result => {
- // 更新商品数量
- app.total = result.data.cartTotal
- // 重新计算合计金额
- app.onCalcTotalPrice()
- // 清除节流函数句柄
- item.debounceHandle = null
- })
- .catch(err => {
- // 还原商品数量
- item.goods_num = oldValue
- setTimeout(() => app.$toast(err.errMsg), 10)
- })
- },
- // 跳转到商品详情页
- onTargetGoods(goodsId) {
- this.$navTo('pages/goods/detail', { goodsId })
- },
- // 点击去逛逛按钮, 跳转到首页
- onTargetIndex() {
- this.$navTo('pages/index/index')
- },
- // 选中商品
- handleCheckItem(cartId) {
- const { checkedIds } = this
- const index = checkedIds.findIndex(id => id === cartId)
- index < 0 ? checkedIds.push(cartId) : checkedIds.splice(index, 1)
- },
- // 全选事件
- handleCheckAll() {
- const { checkedIds, list } = this
- this.checkedIds = checkedIds.length === list.length ? [] : list.map(item => item.id)
- },
- // 结算选中的商品
- handleOrder() {
- const app = this
- if (app.checkedIds.length) {
- const cartIds = app.checkedIds.join()
- app.$navTo('pages/checkout/index', { mode: 'cart', cartIds })
- }
- },
- // 删除选中的商品弹窗事件
- handleDelete() {
- const app = this
- if (!app.checkedIds.length) {
- return false
- }
- uni.showModal({
- title: '友情提示',
- content: '您确定要删除该商品吗?',
- showCancel: true,
- success({ confirm }) {
- // 确认删除
- confirm && app.onClearCart()
- }
- })
- },
- // 确认删除商品
- onClearCart() {
- const app = this
- CartApi.clear(app.checkedIds)
- .then(result => {
- app.getCartList()
- app.handleToggleMode()
- })
- }
- }
- }
- </script>
- <style>
- page {
- background: #f5f5f5;
- }
- </style>
- <style lang="scss" scoped>
- // 页面顶部
- .head-info {
- display: flex;
- justify-content: space-between;
- align-items: center;
- padding: 4rpx 30rpx;
- // background-color: #fff;
- height: 80rpx;
- .cart-total {
- font-size: 28rpx;
- color: #333;
- .active {
- color: #FA2209;
- margin: 0 2rpx;
- }
- }
- .cart-edit {
- padding-left: 20rpx;
- .icon {
- margin-right: 12rpx;
- }
- .edit {
- color: #fa2209;
- }
- }
- }
- // 购物车列表
- .cart-list {
- padding: 0 16rpx 110rpx 16rpx;
- }
- .cart-item {
- background: #fff;
- border-radius: 12rpx;
- display: flex;
- align-items: center;
- padding: 30rpx 16rpx;
- margin-bottom: 24rpx;
- .item-radio {
- width: 56rpx;
- height: 80rpx;
- line-height: 80rpx;
- margin-right: 10rpx;
- text-align: center;
- .radio {
- transform: scale(0.76)
- }
- }
- .goods-image {
- width: 200rpx;
- height: 200rpx;
- .image {
- display: block;
- width: 100%;
- height: 100%;
- border-radius: 8rpx;
- }
- }
- .item-content {
- flex: 1;
- padding-left: 24rpx;
- .goods-title {
- font-size: 28rpx;
- max-height: 76rpx;
- }
- .goods-props {
- margin-top: 14rpx;
- height: 40rpx;
- color: #ababab;
- font-size: 24rpx;
- overflow: hidden;
- .goods-props-item {
- display: inline-block;
- margin-right: 14rpx;
- padding: 4rpx 16rpx;
- border-radius: 12rpx;
- background-color: #F5F5F5;
- width: auto;
- }
- }
- .item-foot {
- display: flex;
- justify-content: space-between;
- align-items: center;
- margin-top: 20rpx;
- .goods-price {
- vertical-align: bottom;
- color: $uni-text-color-active;
- .unit {
- font-size: 24rpx;
- }
- .value {
- font-size: 32rpx;
- }
- }
- }
- }
- }
- // 空数据按钮
- .empty-ipt {
- width: 220rpx;
- margin: 0 auto;
- font-size: 32rpx;
- height: 64rpx;
- line-height: 64rpx;
- text-align: center;
- color: #fff;
- border-radius: 50rpx;
- background: linear-gradient(to right, #f9211c, #ff6335);
- }
- // 底部操作栏
- .footer-fixed {
- display: flex;
- align-items: center;
- height: 96rpx;
- background: #fff;
- padding: 0 30rpx;
- position: fixed;
- bottom: var(--window-bottom);
- left: 0;
- right: 0;
- z-index: 11;
- .all-radio {
- width: 140rpx;
- display: flex;
- align-items: center;
- .radio {
- margin-bottom: -4rpx;
- transform: scale(0.76)
- }
- }
- .total-info {
- flex: 1;
- display: flex;
- align-items: center;
- justify-content: flex-end;
- padding-right: 30rpx;
- .goods-price {
- vertical-align: bottom;
- color: #fa2209;
- .unit {
- font-size: 24rpx;
- }
- .value {
- font-size: 32rpx;
- }
- }
- }
- .cart-action {
- width: 200rpx;
- .btn-wrapper {
- height: 100%;
- display: flex;
- align-items: center;
- }
- .btn-item {
- flex: 1;
- font-size: 28rpx;
- height: 72rpx;
- line-height: 72rpx;
- text-align: center;
- color: #fff;
- border-radius: 50rpx;
- }
- // 立即购买按钮
- .btn-main {
- background: linear-gradient(to right, #f9211c, #ff6335);
- // 禁用按钮
- &.disabled {
- background: #ff9779;
- }
- }
- }
- }
- </style>
|