Selaa lähdekoodia

Merge branch 'dev' of https://git.citupro.com/zhengnaiwen_citu/menduner into dev

lifanagju_citu 4 kuukautta sitten
vanhempi
commit
99162975f3

+ 40 - 0
src/api/mall/cart.js

@@ -0,0 +1,40 @@
+import request from '@/config/axios'
+
+// 获取购物车列表
+export const getMallUserCartList = async () => {
+  return request.get({
+    url: '/app-api/trade/cart/list'
+  })
+}
+
+// 从购物车中删除商品
+export const deleteCartGoods = async (ids) => {
+  return request.delete({
+    url: '/app-api/trade/cart/delete',
+    params: { ids }
+  })
+}
+
+// 更新购物车商品选中状态
+export const updateCartSelected = async (data) => {
+  return request.put({
+    url: '/app-api/trade/cart/update-selected',
+    data
+  })
+}
+
+// 更新购物车商品数量
+export const updateCartCount = async (data) => {
+  return request.put({
+    url: '/app-api/trade/cart/update-count',
+    data
+  })
+}
+
+// 添加商品到购物车
+export const addCartGoods = async (data) => {
+  return request.post({
+    url: '/app-api/trade/cart/add',
+    data
+  })
+}

+ 8 - 2
src/components/CtTable/index.vue

@@ -9,6 +9,7 @@
     </div>
     <v-data-table
       ref="table"
+      v-model="selected"
       :class="`elevation-${elevation} tableColor ${noRadius ? 'noRadius' : ''}`"
       :headers="headers"
       :items="items"
@@ -53,10 +54,12 @@
 
 <script setup>
 defineOptions({ name: 'CtTable'})
-import { ref, computed, useSlots } from 'vue'
+import { ref, computed, useSlots, watch } from 'vue'
 
+const selected = ref([])
 const emit = defineEmits(['pageHandleChange', 'del', 'edit', 'add', 'selected'])
-defineProps({
+const props = defineProps({
+  modelValue: Array,
   elevation: {
     type: [Number, String],
     default: 0
@@ -133,6 +136,9 @@ defineProps({
     default: 'single'
   }
 })
+watch(() => props.modelValue, (val) => {
+  selected.value = val
+}, { deep: true, immediate: true })
 
 const table = ref()
 const slot = useSlots()

+ 3 - 1
src/plugins/vuetify.js

@@ -11,6 +11,7 @@ import 'vuetify/styles'
 import * as directives from 'vuetify/directives'
 import { VDateInput } from 'vuetify/labs/VDateInput'
 import { VTreeview } from 'vuetify/labs/VTreeview'
+import { VNumberInput } from 'vuetify/labs/VNumberInput'
 
 // Composables
 import { createVuetify } from 'vuetify'
@@ -32,7 +33,8 @@ const myCustomLightTheme = {
 export default createVuetify({
   components: {
     VDateInput,
-    VTreeview
+    VTreeview,
+    VNumberInput
   },
   directives,
   theme: {

+ 9 - 1
src/router/modules/recruit.js

@@ -97,7 +97,15 @@ const recruit = [
         meta: {
           title: '订单详情'
         }
-      }
+      },
+      {
+        path: '/mall/cart',
+        component: () => import('@/views/mall/cart/index.vue'),
+        name: 'mallCart',
+        meta: {
+          title: '我的购物车'
+        }
+      },
     ]
   },
   {

+ 124 - 0
src/views/mall/cart/index.vue

@@ -0,0 +1,124 @@
+<template>
+  <Navbar class="mb-3" />
+  <v-card class="default-width card-box mb-5 pa-5">
+    <div v-if="cartList.length">
+      <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" indeterminate @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 }" :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(item.id)" variant="text">删除</v-btn>
+        </template>
+        <template #bottom></template>
+      </v-data-table>
+    </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>
+</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'
+
+const router = useRouter()
+const cartList = ref([])
+const selectAll = ref(false)
+const headers = [
+  { title: '商品信息', key: 'spuName', sortable: false, align: 'center' },
+  { title: '单价', key: 'sku.price', sortable: false, value: item => '¥' + fen2yuan(item.sku.price) },
+  { title: '数量', key: 'count', sortable: false },
+  { 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)
+  console.log(selectAll.value, 'selectAll.value')
+}
+onMounted(async () => {
+  getCartList()
+})
+
+// 全选
+const handleSelectAll = async (selected) => {
+  console.log(selected, '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 handleDelete = async (id) => {
+  await deleteCartGoods(id)
+  await getCartList()
+}
+
+// 商品数量更新
+const handleChangeCount = async (count, id) => {
+  await updateCartCount({ count, id })
+  await getCartList()
+}
+</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>

+ 5 - 1
src/views/mall/components/GoodsItem/index.vue

@@ -7,7 +7,7 @@
       <div class="ml-5">
         <p class="font-size-15" :class="{'goods-name': showHover}">{{ item.spuName }}</p>
         <p class="color-999 font-size-14">{{ item.properties.map((property) => property.valueName).join(' ') }}</p>
-        <p>
+        <p v-if="showPriceCount">
           <span class="color-333">¥{{ fen2yuan(item.price) }}</span>
           <span v-if="item.count" class="color-999 font-size-13 ml-1">x {{ item.count }}</span>
         </p>
@@ -33,6 +33,10 @@ defineProps({
   showHover: {
     type: Boolean,
     default: true
+  },
+  showPriceCount: {
+    type: Boolean,
+    default: true
   }
 })
 

+ 1 - 1
src/views/mall/components/navbar.vue

@@ -11,7 +11,7 @@
           <v-icon>mdi-octagram-outline</v-icon>
           积分兑换
         </span> -->
-        <span class="cursor-">
+        <span class="cursor-pointer" :class="{'active-route' : isActive('/mall/cart')}" @click="handleTo('/mall/cart')">
           <v-icon size="20">mdi-cart-outline</v-icon>
           购物车
         </span>

Tiedoston diff-näkymää rajattu, sillä se on liian suuri
+ 108 - 0
vite.config.mjs.timestamp-1735030220221-ec7db8c3a2187.mjs


Kaikkia tiedostoja ei voida näyttää, sillä liian monta tiedostoa muuttui tässä diffissä