|
@@ -0,0 +1,217 @@
|
|
|
+<template>
|
|
|
+ <Navbar class="mb-3" />
|
|
|
+ <v-card class="default-width card-box mb-5 pa-5 resume-box">
|
|
|
+ <div class="resume-header">
|
|
|
+ <div class="resume-title">我的购物车</div>
|
|
|
+ <v-btn color="primary" size="small" variant="text" @click="getCartList"><v-icon>mdi-refresh</v-icon>刷新购物车</v-btn>
|
|
|
+ </div>
|
|
|
+ <div v-if="cartList.length" class="mt-3">
|
|
|
+ <v-data-table
|
|
|
+ v-model="selectedData"
|
|
|
+ :items="cartList"
|
|
|
+ :headers="headers"
|
|
|
+ :loading="false"
|
|
|
+ :elevation="0"
|
|
|
+ :show-select="true"
|
|
|
+ select-strategy="all"
|
|
|
+ >
|
|
|
+ <template v-slot:[`header.data-table-select`]>
|
|
|
+ <v-checkbox v-model="selectAll" hide-details color="primary" @update:modelValue="handleSelectAll"></v-checkbox>
|
|
|
+ </template>
|
|
|
+ <template v-slot:[`item.data-table-select`]="{ item }">
|
|
|
+ <v-checkbox v-model="item.selected" hide-details color="primary" @update:modelValue="val => handleSelect(val, item.id)"></v-checkbox>
|
|
|
+ </template>
|
|
|
+ <template v-slot:[`item.spuName`]="{ item }">
|
|
|
+ <GoodsItem :item="{ ...item.sku, ...item.spu, spuName: item.spu.name, picUrl: item.sku?.picUrl || item.spu?.picUrl }" :showLine="false" :showPriceCount="false" class="mb-1" />
|
|
|
+ </template>
|
|
|
+ <template v-slot:[`item.count`]="{ item }">
|
|
|
+ <v-number-input
|
|
|
+ v-model="item.count"
|
|
|
+ color="primary"
|
|
|
+ :min="1" :max="item.sku.stock"
|
|
|
+ hide-details
|
|
|
+ control-variant="split"
|
|
|
+ inset density="compact"
|
|
|
+ style="width: 170px;"
|
|
|
+ @update:modelValue="val => handleChangeCount(val, item.id)"
|
|
|
+ />
|
|
|
+ </template>
|
|
|
+ <template v-slot:[`item.actions`]="{ item }">
|
|
|
+ <v-btn color="error" @click.stop="handleDelete(false, item.id)" variant="text">删除</v-btn>
|
|
|
+ </template>
|
|
|
+ <template #bottom></template>
|
|
|
+ </v-data-table>
|
|
|
+ <v-divider></v-divider>
|
|
|
+ <div class="d-flex align-center justify-space-between py-4">
|
|
|
+ <div class="d-flex align-center">
|
|
|
+ <!-- <v-checkbox v-model="selectAll" hide-details color="primary" @update:modelValue="handleSelectAll"></v-checkbox>
|
|
|
+ <span class="mr-5" style="color: #777;">全选</span> -->
|
|
|
+ <v-btn :disabled="!selectedData.length" color="error" variant="outlined" @click.stop="handleDelete(true)">删除选中的商品</v-btn>
|
|
|
+ </div>
|
|
|
+ <div class="d-flex align-center">
|
|
|
+ <div class="color-666 mr-8">共{{ totalCount }}件商品,合计:¥{{ fen2yuan(totalPrice) }}</div>
|
|
|
+ <v-btn :disabled="!totalCount" color="primary" @click.stop="handleSettlement">结算<span v-if="totalCount > 0">({{ totalCount }})</span></v-btn>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div v-else class="text-center">
|
|
|
+ <Empty :elevation="false" message="购物车空空如也,去首页逛逛吧" />
|
|
|
+ <v-btn elevation="5" color="primary" @click.stop="router.push('/mall')" size="x-large" width="200">去逛逛</v-btn>
|
|
|
+ </div>
|
|
|
+ </v-card>
|
|
|
+ <!-- 结算 -->
|
|
|
+ <CtDialog :visible="showPay" titleClass="text-h6" :widthType="3" title="订单信息" @submit="handleSubmit" @close="handleClose">
|
|
|
+ <confirm ref="confirmRef" :data="skuInfo" @paySuccess="paySuccess"></confirm>
|
|
|
+ </CtDialog>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script setup>
|
|
|
+defineOptions({ name: 'mall-cart'})
|
|
|
+import { ref, computed, onMounted } from 'vue'
|
|
|
+import Navbar from '../components/navbar.vue'
|
|
|
+import { useRouter } from 'vue-router'
|
|
|
+import { getMallUserCartList } from '@/api/mall/cart'
|
|
|
+import { fen2yuan } from '@/hooks/web/useGoods'
|
|
|
+import GoodsItem from '../components/GoodsItem'
|
|
|
+import { updateCartSelected, updateCartCount, deleteCartGoods } from '@/api/mall/cart'
|
|
|
+import Snackbar from '@/plugins/snackbar'
|
|
|
+import confirm from '@/views/mall/components/details/order/confirm.vue'
|
|
|
+import Confirm from '@/plugins/confirm'
|
|
|
+import { useI18n } from '@/hooks/web/useI18n'; const { t } = useI18n()
|
|
|
+
|
|
|
+const router = useRouter()
|
|
|
+const cartList = ref([])
|
|
|
+const selectAll = ref(false)
|
|
|
+const headers = [
|
|
|
+ { title: '商品信息', key: 'spuName', sortable: false },
|
|
|
+ { title: '单价', key: 'sku.price', sortable: false, value: item => '¥' + fen2yuan(item.sku.price) },
|
|
|
+ { title: '数量', key: 'count', sortable: false },
|
|
|
+ { title: '合计', key: 'totalCount', sortable: false, value: item => '¥' + fen2yuan(item.sku.price * item.count) },
|
|
|
+ { title: '操作', key: 'actions', align: 'center', sortable: false }
|
|
|
+]
|
|
|
+
|
|
|
+// 获取购物车列表
|
|
|
+const getCartList = async () => {
|
|
|
+ const data = await getMallUserCartList()
|
|
|
+ cartList.value = data.validList || []
|
|
|
+ selectAll.value = cartList.value.every(e => e.selected)
|
|
|
+}
|
|
|
+onMounted(async () => {
|
|
|
+ getCartList()
|
|
|
+})
|
|
|
+
|
|
|
+// 全选
|
|
|
+const handleSelectAll = async (selected) => {
|
|
|
+ const ids = cartList.value.map(item => item.id)
|
|
|
+ await updateCartSelected({ ids, selected })
|
|
|
+ getCartList()
|
|
|
+}
|
|
|
+
|
|
|
+// 单选
|
|
|
+const handleSelect = async (selected, id) => {
|
|
|
+ await updateCartSelected({ ids: [id], selected })
|
|
|
+ getCartList()
|
|
|
+}
|
|
|
+
|
|
|
+// 选中的商品列表
|
|
|
+const selectedData = computed(() => {
|
|
|
+ return cartList.value.map(item => {
|
|
|
+ if (item.selected) return item.id
|
|
|
+ }).filter(Boolean)
|
|
|
+})
|
|
|
+
|
|
|
+// 总结算金额
|
|
|
+const totalPrice = computed(() => {
|
|
|
+ return cartList.value.reduce((total, item) => {
|
|
|
+ if (item.selected) {
|
|
|
+ return total + item.sku.price * item.count
|
|
|
+ }
|
|
|
+ return total
|
|
|
+ }, 0)
|
|
|
+})
|
|
|
+
|
|
|
+// 总商品件数
|
|
|
+const totalCount = computed(() => {
|
|
|
+ return cartList.value.reduce((total, item) => {
|
|
|
+ if (item.selected) {
|
|
|
+ return total + item.count
|
|
|
+ }
|
|
|
+ return total
|
|
|
+ }, 0)
|
|
|
+})
|
|
|
+
|
|
|
+// 删除购物车中的商品
|
|
|
+const handleDelete = async (isAll, id) => {
|
|
|
+ Confirm(t('common.confirmTitle'), `是否确定删除${isAll? '选中的' : ''}商品?`).then(async () => {
|
|
|
+ const ids = isAll ? selectedData.value.join(',') : id
|
|
|
+ await deleteCartGoods(ids)
|
|
|
+ await getCartList()
|
|
|
+ })
|
|
|
+}
|
|
|
+
|
|
|
+// 商品数量更新
|
|
|
+const handleChangeCount = async (count, id) => {
|
|
|
+ await updateCartCount({ count, id })
|
|
|
+ await getCartList()
|
|
|
+}
|
|
|
+
|
|
|
+const confirmRef = ref()
|
|
|
+const handleSubmit = () => {
|
|
|
+ if (confirmRef.value) confirmRef.value.onConfirm()
|
|
|
+}
|
|
|
+
|
|
|
+const handleClose = () => {
|
|
|
+ showPay.value = false
|
|
|
+}
|
|
|
+const paySuccess = () => {
|
|
|
+ // 更新购物车列表
|
|
|
+ showPay.value = false
|
|
|
+ getCartList()
|
|
|
+}
|
|
|
+
|
|
|
+// 结算
|
|
|
+const showPay = ref(false)
|
|
|
+const selectedList = ref([])
|
|
|
+const skuInfo = ref(null) // 购买商品规格信息
|
|
|
+const handleSettlement = () => {
|
|
|
+ let items = []
|
|
|
+ let goods_list = [];
|
|
|
+ selectedList.value = cartList.value.filter((item) => selectedData.value.includes(item.id));
|
|
|
+ selectedList.value.map((item) => {
|
|
|
+ // 此处前端做出修改
|
|
|
+ items.push({
|
|
|
+ skuId: item.sku.id,
|
|
|
+ count: item.count,
|
|
|
+ cartId: item.id,
|
|
|
+ categoryId: item.spu.categoryId
|
|
|
+ })
|
|
|
+ goods_list.push({
|
|
|
+ goods_id: item.spu.id,
|
|
|
+ goods_num: item.count,
|
|
|
+ });
|
|
|
+ });
|
|
|
+ // return;
|
|
|
+ if (goods_list.length === 0) {
|
|
|
+ Snackbar.warning('请选择商品')
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ skuInfo.value = JSON.stringify({
|
|
|
+ items
|
|
|
+ })
|
|
|
+ showPay.value = true
|
|
|
+}
|
|
|
+</script>
|
|
|
+
|
|
|
+<style scoped lang="scss">
|
|
|
+ :deep(.v-table.v-table--fixed-header > .v-table__wrapper > table > thead > tr > th) {
|
|
|
+ text-wrap: nowrap !important;
|
|
|
+ background-color: #f7f8fa !important;
|
|
|
+ }
|
|
|
+ :deep(.v-selection-control__input) {
|
|
|
+ color: var(--v-primary-base) !important;
|
|
|
+ }
|
|
|
+ :deep(.v-table.v-table--hover > .v-table__wrapper > table > tbody > tr > td) {
|
|
|
+ white-space: nowrap !important;
|
|
|
+ }
|
|
|
+</style>
|