tanghaolin
2025-03-09 2839044a1269268e277f23f66cf2b86dcf85de3b
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>