From 2839044a1269268e277f23f66cf2b86dcf85de3b Mon Sep 17 00:00:00 2001 From: tanghaolin <1723298894@qq.com> Date: 星期日, 09 三月 2025 13:53:27 +0800 Subject: [PATCH] 修改购物车功能 --- src/views/OrderInfo.vue | 556 +++++++++++++++++++++++++++++++++++-------------------- 1 files changed, 356 insertions(+), 200 deletions(-) diff --git a/src/views/OrderInfo.vue b/src/views/OrderInfo.vue index b3abe83..4e231ed 100644 --- a/src/views/OrderInfo.vue +++ b/src/views/OrderInfo.vue @@ -1,255 +1,411 @@ <template> - <div class="container mx-auto px-4 py-8"> - <div class="bg-white rounded-lg shadow"> - <!-- 璁㈠崟澶撮儴 --> - <div class="p-4 border-b"> - <h1 class="text-xl font-medium">纭璁㈠崟</h1> + <div class="container mx-auto py-8"> + <div class="bg-white rounded-sm"> + <div class="border-b border-[#f2f2f2]"> + <h2 class="text-[16px] font-bold px-6 py-4">鏀惰揣浜轰俊鎭�</h2> </div> - - <!-- 鏀惰揣鍦板潃 --> - <div class="p-4 border-b"> - <div class="flex justify-between items-center mb-4"> - <h2 class="text-lg">鏀惰揣鍦板潃</h2> + <div class="p-6"> + <div class="flex items-center justify-between mb-4"> <el-button type="primary" link @click="showAddressDialog = true"> - {{ selectedAddress ? '淇敼鍦板潃' : '娣诲姞鍦板潃' }} + 鏂板鏀惰揣鍦板潃 </el-button> </div> - - <div v-if="selectedAddress" class="bg-gray-50 p-4 rounded"> - <div class="flex justify-between"> + <div class="bg-[#f5f5f5] rounded p-4" v-if="selectedAddress"> + <div class="flex items-center justify-between"> <div> <span class="font-medium">{{ selectedAddress.name }}</span> - <span class="ml-4 text-gray-600">{{ selectedAddress.phone }}</span> + <span class="ml-4 text-gray-500">{{ selectedAddress.phone }}</span> </div> - <el-tag size="small" v-if="selectedAddress.isDefault">榛樿鍦板潃</el-tag> + <el-tag size="small" type="success" effect="plain" v-if="selectedAddress.isDefault">榛樿鍦板潃</el-tag> </div> - <div class="mt-2 text-gray-600">{{ selectedAddress.address }}</div> + <div class="text-gray-600 mt-2">{{ selectedAddress.fullAddress }}</div> </div> - <el-empty v-else description="璇锋坊鍔犳敹璐у湴鍧�" /> - </div> - - <!-- 鍟嗗搧鍒楄〃 --> - <div class="p-4 border-b"> - <h2 class="text-lg mb-4">鍟嗗搧娓呭崟</h2> - <div class="space-y-4"> - <div v-for="item in selectedItems" :key="item.addID" class="flex items-center"> - <el-image :src="item.image" class="w-20 h-20 object-cover rounded" /> - <div class="flex-1 ml-4"> - <div class="text-sm">{{ item.name }}</div> - <div class="text-gray-500 text-sm mt-1">鏁伴噺锛歿{ item.quantity }}</div> - </div> - <div class="text-red-500">楼{{ (item.price * item.quantity).toFixed(2) }}</div> - </div> - </div> - </div> - - <!-- 璁㈠崟淇℃伅 --> - <div class="p-4 border-b"> - <h2 class="text-lg mb-4">璁㈠崟淇℃伅</h2> - <el-form :model="orderForm" label-width="100px"> - <el-form-item label="鏀粯鏂瑰紡"> - <el-radio-group v-model="orderForm.paymentMethod"> - <el-radio label="wechat">寰俊鏀粯</el-radio> - <el-radio label="alipay">鏀粯瀹�</el-radio> - </el-radio-group> - </el-form-item> - <el-form-item label="澶囨敞"> - <el-input v-model="orderForm.remark" type="textarea" placeholder="璇疯緭鍏ヨ鍗曞娉�" /> - </el-form-item> - </el-form> - </div> - - <!-- 璁㈠崟閲戦 --> - <div class="p-4"> - <div class="flex justify-end items-center"> - <div class="text-gray-500"> - 鍏� <span class="text-red-500">{{ totalQuantity }}</span> 浠跺晢鍝侊紝 - 鎬昏锛�<span class="text-red-500 text-xl font-medium">楼{{ totalAmount.toFixed(2) }}</span> - </div> - <el-button type="primary" class="ml-4" @click="submitOrder" :loading="submitting"> - 鎻愪氦璁㈠崟 - </el-button> + <div v-else class="bg-[#f5f5f5] text-center py-4 text-gray-500"> + 璇锋坊鍔犳敹璐у湴鍧� </div> </div> </div> - <!-- 鍦板潃閫夋嫨瀵硅瘽妗� --> - <el-dialog v-model="showAddressDialog" title="閫夋嫨鏀惰揣鍦板潃" width="600px"> - <div class="space-y-4"> - <div v-for="address in addressList" :key="address.id" - class="p-4 border rounded cursor-pointer hover:border-primary" - :class="{ 'border-primary': selectedAddress?.id === address.id }" - @click="selectAddress(address)" - > - <div class="flex justify-between"> - <div> - <span class="font-medium">{{ address.name }}</span> - <span class="ml-4 text-gray-600">{{ address.phone }}</span> - </div> - <el-tag size="small" v-if="address.isDefault">榛樿鍦板潃</el-tag> - </div> - <div class="mt-2 text-gray-600">{{ address.address }}</div> + <!-- 鏀粯鏂瑰紡 --> + <div class="bg-white rounded-sm"> + <div class="border-b border-[#f2f2f2]"> + <h2 class="text-[16px] font-bold px-6 py-4">鏀粯鏂瑰紡</h2> + </div> + <div class="p-6"> + <div class="bg-[#f5f5f5] rounded p-4"> + <el-radio-group v-model="paymentMethod"> + <el-radio label="online">鍦ㄧ嚎鏀粯</el-radio> + </el-radio-group> </div> </div> - <template #footer> - <el-button @click="showAddressDialog = false">鍙栨秷</el-button> - <el-button type="primary" @click="addNewAddress">鏂板鍦板潃</el-button> - </template> - </el-dialog> + </div> - <!-- 鏂板鍦板潃瀵硅瘽妗� --> - <el-dialog v-model="showNewAddressDialog" title="鏂板鏀惰揣鍦板潃" width="500px"> - <el-form :model="newAddress" :rules="addressRules" ref="addressFormRef" label-width="100px"> - <el-form-item label="鏀惰揣浜�" prop="name"> - <el-input v-model="newAddress.name" placeholder="璇疯緭鍏ユ敹璐т汉濮撳悕" /> - </el-form-item> - <el-form-item label="鎵嬫満鍙风爜" prop="phone"> - <el-input v-model="newAddress.phone" placeholder="璇疯緭鍏ユ墜鏈哄彿鐮�" /> - </el-form-item> - <el-form-item label="璇︾粏鍦板潃" prop="address"> - <el-input v-model="newAddress.address" type="textarea" placeholder="璇疯緭鍏ヨ缁嗗湴鍧�" /> - </el-form-item> - <el-form-item> - <el-checkbox v-model="newAddress.isDefault">璁句负榛樿鍦板潃</el-checkbox> - </el-form-item> - </el-form> - <template #footer> - <el-button @click="showNewAddressDialog = false">鍙栨秷</el-button> - <el-button type="primary" @click="saveAddress">淇濆瓨</el-button> - </template> - </el-dialog> + <!-- 閰嶉�佹柟寮� --> + <div class="bg-white rounded-sm"> + <div class="border-b border-[#f2f2f2]"> + <h2 class="text-[16px] font-bold px-6 py-4">閰嶉�佹柟寮�</h2> + </div> + <div class="p-6"> + <div class="bg-[#f5f5f5] rounded p-4"> + <div class="flex items-center justify-between"> + <div class="flex items-center gap-6"> + <el-radio-group v-model="deliveryMethod"> + <el-radio label="sf">椤轰赴閫熻繍</el-radio> + <el-radio label="zt">涓�氬揩閫�</el-radio> + <el-radio label="yd">闊佃揪蹇��</el-radio> + <el-radio label="ems">EMS</el-radio> + </el-radio-group> + </div> + <!-- <div class="text-gray-500 text-sm"> + <i class="el-icon-time mr-1"></i> + 閰嶉�佹椂闂达細棰勮3鏈�10鏃�(鍛ㄤ竴) 09:00-15:00 閫佽揪 + </div> --> + </div> + </div> + </div> + </div> + + <!-- 鍙戠エ淇℃伅 --> + <div class="bg-white rounded-sm"> + <div class="border-b border-[#f2f2f2]"> + <h2 class="text-[16px] font-bold px-6 py-4">鍙戠エ淇℃伅</h2> + </div> + <div class="p-6"> + <div class="flex items-center justify-between mb-4"> + <div class="flex items-center gap-4"> + <el-radio-group v-model="invoiceForm.needInvoice" class="flex items-center"> + <el-radio :label="false">涓嶅紑鍙戠エ</el-radio> + <el-radio :label="true">寮�鍙戠エ</el-radio> + </el-radio-group> + </div> + </div> + <div v-if="invoiceForm.needInvoice" class="bg-[#f5f5f5] rounded p-4"> + <el-form :model="invoiceForm" label-width="100px"> + <el-form-item label="鍙戠エ鎶ご"> + <div class="flex items-center gap-4" style="flex-direction: column;"> + <el-radio-group v-model="invoiceForm.type" class="mb-4"> + <el-radio label="personal">涓汉</el-radio> + <el-radio label="company">鍗曚綅</el-radio> + </el-radio-group> + + <template v-if="invoiceForm.type === 'personal'"> + <div class="w-full"> + <el-input v-model="invoiceForm.personalName" placeholder="璇峰~鍐欎釜浜哄鍚�" /> + </div> + </template> + + <template v-if="invoiceForm.type === 'company'"> + <div class="space-y-4"> + <el-input v-model="invoiceForm.title" placeholder="璇峰~鍐欏崟浣嶅悕绉�" /> + <el-input v-model="invoiceForm.taxNumber" placeholder="璇峰~鍐欑撼绋庝汉璇嗗埆鍙�" /> + <div class="flex gap-4"> + <el-input v-model="invoiceForm.address" placeholder="璇峰~鍐欏崟浣嶅湴鍧�" /> + <el-input v-model="invoiceForm.phone" placeholder="璇峰~鍐欏崟浣嶇數璇�" /> + </div> + <div class="flex gap-4"> + <el-input v-model="invoiceForm.bank" placeholder="璇峰~鍐欏紑鎴烽摱琛�" /> + <el-input v-model="invoiceForm.bankAccount" placeholder="璇峰~鍐欓摱琛岃处鍙�" /> + </div> + </div> + </template> + </div> + </el-form-item> + </el-form> + </div> + </div> + </div> + + <!-- 鍟嗗搧娓呭崟 --> + <div class="bg-white rounded-sm"> + <div class="border-b border-[#f2f2f2]"> + <h2 class="text-[16px] font-bold px-6 py-4">鍟嗗搧娓呭崟</h2> + </div> + <div class="p-6"> + <div class="rounded overflow-hidden"> + <div class="bg-[#f5f5f5] p-4 flex items-center text-gray-500 text-sm"> + <div class="flex-1">鍟嗗搧淇℃伅</div> + <div class="w-[160px] text-center">鍗曚环</div> + <div class="w-[160px] text-center">鏁伴噺</div> + <div class="w-[160px] text-center">灏忚</div> + </div> + <div v-for="item in selectedItems" :key="item.addID" class="p-4 flex items-center border-t border-[#f0f0f0] hover:bg-[#f5f5f5]"> + <div class="flex-1 flex items-center gap-4"> + <el-image :src="item.image" class="w-20 h-20 object-cover border" /> + <div class="flex-1"> + <div class="text-base font-medium mb-2 line-clamp-2">{{ item.name }}</div> + <div class="text-gray-500 text-[12px] mb-1">鍘傚晢锛歿{ item.companyName }}</div> + <div class="text-gray-500 text-[12px]">鍨嬪彿锛歿{ item.model }}</div> + </div> + </div> + <div class="w-[160px] text-center">楼{{ item.price.toFixed(2) }}</div> + <div class="w-[160px] text-center">{{ item.quantity }}</div> + <div class="w-[160px] text-center text-[#e4393c] font-medium">楼{{ (item.price * item.quantity).toFixed(2) }}</div> + </div> + </div> + </div> + </div> + + <!-- 璁㈠崟閲戦 --> + <div class="bg-white rounded-sm"> + <div class="border-b border-[#f2f2f2]"> + <h2 class="text-[16px] font-bold px-6 py-4">璁㈠崟閲戦</h2> + </div> + <div class="p-6"> + <div class="flex justify-end"> + <div class="w-[400px] space-y-3"> + <div class="flex justify-between text-gray-600"> + <span>鍟嗗搧鎬婚锛�</span> + <span>楼{{ totalAmount.toFixed(2) }}</span> + </div> + <div class="flex justify-between text-gray-600"> + <span>杩愯垂锛�</span> + <span>楼{{ shippingFee.toFixed(2) }}</span> + </div> + <div class="flex justify-between items-center pt-4 border-t text-xl"> + <span>搴斾粯閲戦锛�</span> + <span class="text-[#e4393c] font-bold">楼{{ finalAmount.toFixed(2) }}</span> + </div> + <div class="flex justify-end mt-4"> + <el-button + type="danger" + size="large" + class="w-[180px]" + @click="submitOrder" + > + 鎻愪氦璁㈠崟 + </el-button> + </div> + </div> + </div> + </div> + </div> </div> + + <!-- 鏂板鍦板潃瀵硅瘽妗� --> + <el-dialog v-model="showAddressDialog" title="鏂板鏀惰揣鍦板潃" width="500px" destroy-on-close> + <el-form :model="addressForm" label-width="80px"> + <el-form-item label="鏀惰揣浜�"> + <el-input v-model="addressForm.name" placeholder="璇疯緭鍏ユ敹璐т汉濮撳悕" /> + </el-form-item> + <el-form-item label="鎵嬫満鍙风爜"> + <el-input v-model="addressForm.phone" placeholder="璇疯緭鍏ユ墜鏈哄彿鐮�" /> + </el-form-item> + <el-form-item label="鎵�鍦ㄥ湴鍖�"> + <el-cascader + v-model="addressForm.region" + :options="regionOptions" + placeholder="璇烽�夋嫨鎵�鍦ㄥ湴鍖�" + /> + </el-form-item> + <el-form-item label="璇︾粏鍦板潃"> + <el-input + v-model="addressForm.detail" + type="textarea" + placeholder="璇疯緭鍏ヨ缁嗗湴鍧�" + /> + </el-form-item> + <el-form-item> + <el-checkbox v-model="addressForm.isDefault">璁句负榛樿鍦板潃</el-checkbox> + </el-form-item> + </el-form> + <template #footer> + <div class="flex justify-end gap-2"> + <el-button @click="showAddressDialog = false">鍙栨秷</el-button> + <el-button type="primary" @click="saveAddress">淇濆瓨</el-button> + </div> + </template> + </el-dialog> </template> <script setup lang="ts"> import { ref, computed } from 'vue'; import { useRouter } from 'vue-router'; import { ElMessage } from 'element-plus'; +import { CreditCard, Money } from '@element-plus/icons-vue'; import { useCartStore } from '@/stores/useCartStore'; - +import wechat from '@/assets/icons/wechat-pay.svg'; +import alipay from '@/assets/icons/alipay.svg'; const router = useRouter(); const cartStore = useCartStore(); // 閫変腑鐨勫晢鍝� const selectedItems = computed(() => { - if(cartStore.items.length == 0)return [] return cartStore.items.filter(item => item.selected); }); -// 鎬绘暟閲� -const totalQuantity = computed(() => { - return selectedItems.value.reduce((sum, item) => sum + item.quantity, 0); -}); - -// 鎬婚噾棰� -const totalAmount = computed(() => { - return selectedItems.value.reduce((sum, item) => sum + item.price * item.quantity, 0); -}); - -// 璁㈠崟琛ㄥ崟 -const orderForm = ref({ - paymentMethod: 'wechat', - remark: '' -}); - -// 鍦板潃鐩稿叧 +// 鏀惰揣鍦板潃鐩稿叧 const showAddressDialog = ref(false); -const showNewAddressDialog = ref(false); const selectedAddress = ref(null); -const addressList = ref([ - { - id: 1, - name: '寮犱笁', - phone: '13800138000', - address: '涓婃捣甯傛郸涓滄柊鍖洪檰瀹跺槾', - isDefault: true - } -]); - -// 鏂板鍦板潃琛ㄥ崟 -const addressFormRef = ref(); -const newAddress = ref({ +const addressForm = ref({ name: '', phone: '', - address: '', + region: [], + detail: '', isDefault: false }); -// 鍦板潃楠岃瘉瑙勫垯 -const addressRules = { - name: [{ required: true, message: '璇疯緭鍏ユ敹璐т汉濮撳悕', trigger: 'blur' }], - phone: [ - { required: true, message: '璇疯緭鍏ユ墜鏈哄彿鐮�', trigger: 'blur' }, - { pattern: /^1[3-9]\d{9}$/, message: '璇疯緭鍏ユ纭殑鎵嬫満鍙风爜', trigger: 'blur' } - ], - address: [{ required: true, message: '璇疯緭鍏ヨ缁嗗湴鍧�', trigger: 'blur' }] -}; +// 鏀粯鏂瑰紡 +const paymentMethod = ref('online'); -// 閫夋嫨鍦板潃 -const selectAddress = (address) => { - selectedAddress.value = address; - showAddressDialog.value = false; -}; +// 閰嶉�佹柟寮� +const deliveryMethod = ref('sf'); -// 娣诲姞鏂板湴鍧� -const addNewAddress = () => { - showAddressDialog.value = false; - showNewAddressDialog.value = true; -}; +// 鍙戠エ淇℃伅 +const invoiceForm = ref({ + needInvoice: false, + type: 'personal', + personalName: '', + title: '', + taxNumber: '', + address: '', + phone: '', + bank: '', + bankAccount: '' +}); + +// 閲戦璁$畻 +const totalAmount = computed(() => { + return selectedItems.value.reduce((total, item) => total + item.price * item.quantity, 0); +}); + +const shippingFee = computed(() => { + return deliveryMethod.value === 'express' ? 0 : 0; +}); + +const finalAmount = computed(() => { + return totalAmount.value + shippingFee.value; +}); // 淇濆瓨鍦板潃 -const saveAddress = async () => { - if (!addressFormRef.value) return; +const saveAddress = () => { + if (!addressForm.value.name || !addressForm.value.phone || !addressForm.value.detail) { + ElMessage.warning('璇峰~鍐欏畬鏁寸殑鍦板潃淇℃伅'); + return; + } + // 杩欓噷搴旇璋冪敤API淇濆瓨鍦板潃 + selectedAddress.value = { + name: addressForm.value.name, + phone: addressForm.value.phone, + fullAddress: addressForm.value.detail, + isDefault: addressForm.value.isDefault + }; - await addressFormRef.value.validate((valid, fields) => { - if (valid) { - const address = { - id: Date.now(), - ...newAddress.value - }; - - if (address.isDefault) { - addressList.value.forEach(item => item.isDefault = false); - } - - addressList.value.push(address); - selectedAddress.value = address; - showNewAddressDialog.value = false; - - // 閲嶇疆琛ㄥ崟 - newAddress.value = { - name: '', - phone: '', - address: '', - isDefault: false - }; - - ElMessage.success('鍦板潃娣诲姞鎴愬姛'); - } - }); + // 閲嶇疆琛ㄥ崟鍐呭 + addressForm.value = { + name: '', + phone: '', + region: [], + detail: '', + isDefault: false + }; + + showAddressDialog.value = false; + ElMessage.success('鍦板潃淇濆瓨鎴愬姛'); }; // 鎻愪氦璁㈠崟 -const submitting = ref(false); -const submitOrder = async () => { +const submitOrder = () => { if (!selectedAddress.value) { ElMessage.warning('璇烽�夋嫨鏀惰揣鍦板潃'); return; } - submitting.value = true; - try { - // 杩欓噷娣诲姞鎻愪氦璁㈠崟鐨勯�昏緫 - await new Promise(resolve => setTimeout(resolve, 1000)); - - // 娓呯┖宸查�夊晢鍝� - const remainingItems = cartStore.cartItems.filter(item => !item.selected); - cartStore.setCartItems(remainingItems); - - ElMessage.success('璁㈠崟鎻愪氦鎴愬姛'); - router.push('/payment'); - } finally { - submitting.value = false; - } + // 鍒涘缓璁㈠崟瀵硅薄 + const order = { + orderNo: 'DD' + Date.now(), + items: selectedItems.value, + address: selectedAddress.value, + paymentMethod: '鍦ㄧ嚎鏀粯', + deliveryMethod: getDeliveryMethodName(deliveryMethod.value), + invoice: invoiceForm.value.needInvoice ? invoiceForm.value : null, + totalAmount: totalAmount.value, + shippingFee: shippingFee.value, + finalAmount: finalAmount.value, + createTime: new Date().toLocaleString() + }; + + // 淇濆瓨璁㈠崟淇℃伅 + localStorage.setItem('currentOrder', JSON.stringify(order)); + + // 娓呴櫎璐墿杞︿腑宸蹭笅鍗曠殑鍟嗗搧 + selectedItems.value.forEach(item => { + cartStore.removeFromCart(item.addID); + }); + + // 璺宠浆鍒版敮浠橀〉闈� + router.push('/payment'); }; + +// 鑾峰彇閰嶉�佹柟寮忓悕绉� +const getDeliveryMethodName = (method: string) => { + const methodMap = { + sf: '椤轰赴閫熻繍', + zt: '涓�氬揩閫�', + yd: '闊佃揪蹇��', + ems: 'EMS' + }; + return methodMap[method] || method; +}; + +// 鍦板尯閫夐」鏁版嵁锛堢ず渚嬶級 +const regionOptions = ref([ + { + value: 'shanghai', + label: '涓婃捣', + children: [ + { + value: 'pudong', + label: '娴︿笢鏂板尯' + }, + { + value: 'xuhui', + label: '寰愭眹鍖�' + } + ] + } + // 鍏朵粬鐪佸競鍖烘暟鎹�... +]); </script> + +<style scoped> +:deep(.el-radio__input.is-checked .el-radio__inner) { + border-color: #e4393c; + background: #e4393c; +} + +:deep(.el-radio__input.is-checked + .el-radio__label) { + color: #e4393c; +} + +:deep(.el-button--primary) { + --el-button-bg-color: #e4393c; + --el-button-border-color: #e4393c; + --el-button-hover-bg-color: #f15b5f; + --el-button-hover-border-color: #f15b5f; +} + +:deep(.el-button--primary.is-plain) { + --el-button-bg-color: #fff; + --el-button-border-color: #e4393c; + --el-button-hover-bg-color: #fff4f4; + --el-button-hover-border-color: #e4393c; + --el-button-text-color: #e4393c; +} + +.line-clamp-2 { + display: -webkit-box; + -webkit-line-clamp: 2; + -webkit-box-orient: vertical; + overflow: hidden; +} + +:deep(.el-form-item__label) { + color: #666; +} + +:deep(.el-input) { + --el-input-border-color: #dcdfe6; + --el-input-hover-border-color: #c0c4cc; + --el-input-focus-border-color: #e4393c; +} +</style> -- Gitblit v1.9.3