Bladeren bron

订单支付

lifanagju_citu 5 maanden geleden
bovenliggende
commit
2c889169e4

+ 2 - 2
src/api/mall/product.js

@@ -11,7 +11,7 @@ export const getProductDetail = async (params) => {
 // 获得商品评价分页
 export const getCommentPage = async (spuId, pageNo, pageSize, type) => {
   return request.get({
-    url: '/app-api//product/comment/page',
+    url: '/app-api/product/comment/page',
     params: {
       spuId,
       pageNo,
@@ -19,4 +19,4 @@ export const getCommentPage = async (spuId, pageNo, pageSize, type) => {
       type,
     },
   })
-}
+}

+ 72 - 0
src/api/mall/trade.js

@@ -0,0 +1,72 @@
+import request from '@/config/axios'
+import { isEmpty1 } from '@/utils/is'
+
+// 查询交易中心配置详情
+export const getTradeConfig = async () => {
+  return request.get({
+    url: '/app-api/trade/config/get',
+  })
+}
+
+// 创建订单
+export const createOrder = async (data) => {
+  return await request.post({
+    url: '/app-api/trade/order/create',
+    data
+  })
+}
+
+// 计算订单信息
+export const settlementOrder = async (data) => {
+    const data2 = {
+      ...data,
+    };
+    // 移除多余字段
+    if (!(data.couponId > 0)) {
+      delete data2.couponId;
+    }
+    if (!(data.addressId > 0)) {
+      delete data2.addressId;
+    }
+    if (!(data.pickUpStoreId > 0)) {
+      delete data2.pickUpStoreId;
+    }
+    if (isEmpty1(data.receiverName)) {
+      delete data2.receiverName;
+    }
+    if (isEmpty1(data.receiverMobile)) {
+      delete data2.receiverMobile;
+    }
+    if (!(data.combinationActivityId > 0)) {
+      delete data2.combinationActivityId;
+    }
+    if (!(data.combinationHeadId > 0)) {
+      delete data2.combinationHeadId;
+    }
+    if (!(data.seckillActivityId > 0)) {
+      delete data2.seckillActivityId;
+    }
+    if (!(data.pointActivityId > 0)) {
+      delete data2.pointActivityId;
+    }
+    if (!(data.deliveryType > 0)) {
+      delete data2.deliveryType;
+    }
+    // 解决 SpringMVC 接受 List<Item> 参数的问题
+    delete data2.items;
+    if (data?.items?.length) {
+      for (let i = 0; i < data.items.length; i++) {
+        data2[encodeURIComponent('items[' + i + '' + '].skuId')] = data.items[i].skuId + '';
+        data2[encodeURIComponent('items[' + i + '' + '].count')] = data.items[i].count + '';
+        if (data.items[i].cartId) {
+          data2[encodeURIComponent('items[' + i + '' + '].cartId')] = data.items[i].cartId + '';
+        }
+      }
+    }
+    const queryString = Object.keys(data2)
+      .map((key) => key + '=' + data2[key])
+      .join('&');
+  return request.get({
+    url:  `/app-api/trade/order/settlement?${queryString}`
+  })
+}

+ 16 - 0
src/utils/is.js

@@ -18,6 +18,22 @@ export const isObject = (val) => {
   return val !== null && is(val, 'Object')
 }
 
+export const isEmpty1 = (value) => {
+  if (value === '' || value === undefined || value === null){
+    return true;
+  }
+
+  if (isArray(value)) {
+    return value.length === 0;
+  }
+
+  if (isObject(value)) {
+    return Object.keys(value).length === 0;
+  }
+
+  return false
+}
+
 export const isEmpty = (val) => {
   if (val === null) {
     return true

+ 54 - 19
src/views/mall/components/details.vue

@@ -60,6 +60,9 @@
   </div>
   <!-- 快速登录 -->
   <loginPage v-if="showLogin" @loginSuccess="loginSuccess" @close="loginClose"></loginPage>
+  <CtDialog :visible="showPay" titleClass="text-h6" :widthType="3" title="订单信息" @submit="handleSubmit" @close="handleClose">
+    <confirm ref="confirmRef" :data="skuInfo" @paySuccess="showPay = false"></confirm>
+  </CtDialog>
 </template>
 
 <script setup>
@@ -68,6 +71,7 @@ import { getProductDetail } from '@/api/mall/product'
 import selectSku from './details/s-select-sku.vue'
 import describe from './details/describe.vue'
 import comment from './details/detail-comment-card.vue'
+import confirm from './details/order/confirm.vue'
 import { ref, reactive } from 'vue'
 import { useRouter } from 'vue-router'
 import Snackbar from '@/plugins/snackbar'
@@ -77,7 +81,7 @@ import loginPage from '@/views/common/loginDialog.vue'
 const router = useRouter()
 const { id } = router.currentRoute.value.params
 
-const describeTab = ref(1)
+const describeTab = ref(0)
 const selectedSkuPicUrl = ref('')
 const selectedSkuPrice = ref('')
 const selectedSkuMarketPrice = ref('')
@@ -133,28 +137,59 @@ function onSkuChange(e) {
   selectedSkuMarketPrice.value = calcPrice(state.selectedSku?.marketPrice || state.goodsInfo.marketPrice)
 }
 
-const selectedSkuInfo = ref(null) // 购买规格信息
-const onBuy = async (info) => {
+const showPay = ref(false)
+const skuInfo = ref(null) // 购买商品规格信息
+const onBuy = async (e) => {
   if (!getToken()) return handleLogin()
-  selectedSkuInfo.value = info
-  console.log('购买规格信息:', info)
+  if (!e?.id) return Snackbar.warning('请选择商品规格!')
+  console.log('购买规格信息:', e)
+  //
+  skuInfo.value = JSON.stringify({
+    items: [{ skuId: e.id, count: e.goods_num, categoryId: state.goodsInfo.categoryId, }]
+  })
+  showPay.value = true
 }
 // Snackbar.warning('购买功能暂未开放,敬请期待!')
-// function onBuy(sku) {
-//   sheep.$router.go('/pages/order/confirm', {
-//     data: JSON.stringify({
-//       order_type: 'goods',
-//       combinationActivityId: state.activity.id,
-//       combinationHeadId: state.combinationHeadId,
-//       items: [
+
+// onMounted(() => {
+//   onBuy({
+//     id: 45, 
+//     properties: [
+//         {
+//             propertyId: 19, 
+//             propertyName: "颜色", 
+//             valueId: 31, 
+//             valueName: "酒红色"
+//         }, 
 //         {
-//           skuId: sku.id,
-//           count: sku.count,
-//         },
-//       ],
-//     }),
-//   });
-// }
+//             propertyId: 21, 
+//             propertyName: "材质", 
+//             valueId: 33, 
+//             valueName: "纯棉"
+//         }
+//     ], 
+//     price: 2190, 
+//     marketPrice: 2190, 
+//     vipPrice: null, 
+//     picUrl: "http://menduner.citupro.com:6868/admin-api/infra/file/24/get/84d51e3ce5c466667c8cdf4b05c432b98a845559ef81461a78677c08a0608d62.jpg", 
+//     stock: 99, 
+//     weight: 0.1, 
+//     volume: 0.1, 
+//     value_id_array: [
+//         31, 
+//         33
+//     ], 
+//     goods_num: 1
+//   })
+// })
+
+const confirmRef = ref()
+const handleSubmit = () => {
+  if (confirmRef.value) confirmRef.value.onConfirm()
+}
+const handleClose = () => {
+  showPay.value = false
+}
 
 const showLogin = ref(false)
 const handleLogin = () => {

+ 285 - 0
src/views/mall/components/details/order/addressSelection.vue

@@ -0,0 +1,285 @@
+<!-- 下单界面,收货地址 or 自提门店的选择组件 -->
+<template>
+  <div class="allAddress" :style="state.isPickUp ? '' : 'padding-top:10rpx;'">
+    <!-- <div class="nav flex flex-wrap">
+      <div
+        class="item font-color"
+        :class="state.deliveryType === 1 ? 'on' : 'on2'"
+        @click="switchDeliveryType(1)"
+        v-if="state.isPickUp"
+      />
+      <div
+        class="item font-color"
+        :class="state.deliveryType === 2 ? 'on' : 'on2'"
+        @click="switchDeliveryType(2)"
+        v-if="state.isPickUp"
+      />
+    </div> -->
+    <!-- 情况一:收货地址的选择 -->
+    <div
+      class="address"
+      @click="onSelectAddress"
+      v-if="state.deliveryType === 1"
+      :style="state.isPickUp ? '' : 'border-top-left-radius: 14rpx;border-top-right-radius: 14rpx;'"
+    >
+      <div class="addressCon" v-if="state.addressInfo.name">
+        <div class="name d-flex"
+          >{{ state.addressInfo.name }}
+          <div class="phone ml-3">{{ state.addressInfo.mobile }}</div>
+        </div>
+        <div class="d-flex mt-1">
+          <div class="default font-color" v-if="state.addressInfo.defaultStatus">[默认]</div>
+          <div class="line2 ml-3">
+            {{ state.addressInfo.areaName }} {{ state.addressInfo.detailAddress }}
+          </div>
+        </div>
+      </div>
+      <div class="addressCon" v-else>
+        <div class="setaddress">设置收货地址</div>
+      </div>
+      <div class="iconfont">
+        <div class="ss-rest-button">
+          <div class="_icon-forward" />
+        </div>
+      </div>
+    </div>
+    <!-- 情况二:门店的选择 -->
+    <!-- <div
+      class="address flex flex-wrap flex-center ss-row-between"
+      v-if="state.deliveryType === 2"
+      @click="onSelectAddress"
+    >
+      <div class="addressCon" v-if="state.pickUpInfo.name">
+        <div class="name"
+          >{{ state.pickUpInfo.name }}
+          <div class="phone">{{ state.pickUpInfo.phone }}</div>
+        </div>
+        <div class="line1">
+          {{ state.pickUpInfo.areaName }}{{ ', ' + state.pickUpInfo.detailAddress }}
+        </div>
+      </div>
+      <div class="addressCon" v-else>
+        <div class="setaddress">选择自提门店</div>
+      </div>
+      <div class="iconfont">
+        <div class="ss-rest-button">
+          <div class="_icon-forward" />
+        </div>
+      </div>
+    </div> -->
+    <!-- <div class="line">
+      <image :src="sheep.$url.static('/static/images/line.png', 'local')" />
+    </div> -->
+  </div>
+</template>
+
+<script setup>
+  import { computed } from 'vue';
+  // import sheep from '@/sheep';
+  import { isEmpty1 } from '@/utils/is'
+  import Snackbar from '@/plugins/snackbar'
+
+  const props = defineProps({
+    modelValue: {
+      type: Object,
+      default() {},
+    },
+  });
+  const emits = defineEmits(['update:modelValue']);
+
+  // computed 解决父子组件双向数据同步
+  const state = computed({
+    get() {
+      return new Proxy(props.modelValue, {
+        set(obj, name, val) {
+          emits('update:modelValue', {
+            ...obj,
+            [name]: val,
+          });
+          return true;
+        },
+      });
+    },
+    set(val) {
+      emits('update:modelValue', val);
+    },
+  });
+
+  // 选择地址
+  function onSelectAddress() {
+    // let emitName = 'SELECT_ADDRESS';
+    // let addressPage = '/pages/user/address/list?type=select';
+    // if (state.value.deliveryType === 2) {
+    //   emitName = 'SELECT_PICK_UP_INFO';
+    //   addressPage = '/pages/user/goods_details_store/index';
+    // }
+    // uni.$once(emitName, (e) => {
+    //   changeConsignee(e.addressInfo);
+    // });
+    // sheep.$router.go(addressPage);
+  }
+
+  // 更改收货人地址&计算订单信息
+  async function changeConsignee(addressInfo = {}) {
+    if (!isEmpty1(addressInfo)) {
+      if (state.value.deliveryType === 1) {
+        state.value.addressInfo = addressInfo;
+      }
+      if (state.value.deliveryType === 2) {
+        state.value.pickUpInfo = addressInfo;
+      }
+    }
+  }
+
+  // 收货方式切换
+  const switchDeliveryType = (type) => {
+    state.value.deliveryType = type;
+  };
+</script>
+
+<style scoped lang="scss">
+  .allAddress .font-color {
+    color: #e93323 !important;
+  }
+  .line2 {
+    width: 504rpx;
+  }
+  .textR {
+    text-align: right;
+  }
+
+  .line {
+    width: 100%;
+    height: 3rpx;
+  }
+
+  .line image {
+    width: 100%;
+    height: 100%;
+    display: block;
+  }
+
+  .address {
+    background-color: #fff;
+    box-sizing: border-box;
+    padding: 12px;
+    border-radius: 10px;
+    background-color: var(--default-bgc);
+  }
+
+  .address .addressCon {
+    width: 596rpx;
+    font-size: 26rpx;
+    color: #666;
+  }
+
+  .address .addressCon .name {
+    font-size: 30rpx;
+    color: #282828;
+    font-weight: bold;
+    margin-bottom: 10rpx;
+  }
+
+  .address .addressCon .name .phone {
+    margin-left: 50rpx;
+  }
+
+  .address .addressCon .default {
+    margin-right: 12rpx;
+  }
+
+  .address .addressCon .setaddress {
+    color: #333;
+    font-size: 28rpx;
+  }
+
+  .address .iconfont {
+    font-size: 35rpx;
+    color: #707070;
+  }
+
+  .allAddress {
+    width: 100%;
+    background: linear-gradient(to bottom, #e93323 0%, #f5f5f5 100%);
+    // background-image: linear-gradient(to bottom, #e93323 0%, #f5f5f5 100%);
+    // background-image: -webkit-linear-gradient(to bottom, #e93323 0%, #f5f5f5 100%);
+    // background-image: -moz-linear-gradient(to bottom, #e93323 0%, #f5f5f5 100%);
+    //padding: 100rpx 30rpx 0 30rpx;
+    padding-top: 100rpx;
+    padding-bottom: 10rpx;
+  }
+
+  .allAddress .nav {
+    width: 690rpx;
+    margin: 0 auto;
+  }
+
+  .allAddress .nav .item {
+    width: 334rpx;
+  }
+
+  .allAddress .nav .item.on {
+    position: relative;
+    width: 230rpx;
+  }
+
+  .allAddress .nav .item.on::before {
+    position: absolute;
+    bottom: 0;
+    content: '快递配送';
+    font-size: 28rpx;
+    display: block;
+    height: 0;
+    width: 336rpx;
+    border-width: 0 20rpx 80rpx 0;
+    border-style: none solid solid;
+    border-color: transparent transparent #fff;
+    z-index: 2;
+    border-radius: 14rpx 36rpx 0 0;
+    text-align: center;
+    line-height: 80rpx;
+  }
+
+  .allAddress .nav .item:nth-of-type(2).on::before {
+    content: '到店自提';
+    border-width: 0 0 80rpx 20rpx;
+    border-radius: 36rpx 14rpx 0 0;
+  }
+
+  .allAddress .nav .item.on2 {
+    position: relative;
+  }
+
+  .allAddress .nav .item.on2::before {
+    position: absolute;
+    bottom: 0;
+    content: '到店自提';
+    font-size: 28rpx;
+    display: block;
+    height: 0;
+    width: 401rpx;
+    border-width: 0 0 60rpx 60rpx;
+    border-style: none solid solid;
+    border-color: transparent transparent #f7c1bd;
+    border-radius: 36rpx 14rpx 0 0;
+    text-align: center;
+    line-height: 60rpx;
+  }
+
+  .allAddress .nav .item:nth-of-type(1).on2::before {
+    content: '快递配送';
+    border-width: 0 60rpx 60rpx 0;
+    border-radius: 14rpx 36rpx 0 0;
+  }
+
+  .allAddress .address {
+    width: 690rpx;
+    max-height: 180rpx;
+    margin: 0 auto;
+  }
+
+  .allAddress .line {
+    width: 100%;
+    margin: 0 auto;
+  }
+</style>

+ 567 - 0
src/views/mall/components/details/order/confirm.vue

@@ -0,0 +1,567 @@
+<template>
+  <div title="确认订单">
+    <!-- 头部地址选择【配送地址】【自提地址】 -->
+    <AddressSelection v-model="addressState" class="mb-3" />
+
+    <!-- 商品信息 -->
+    <!-- <div>
+      <s-goods-item
+        v-for="item in state.orderInfo.items"
+        :key="item.skuId"
+        :img="item.picUrl"
+        :title="item.spuName"
+        :skuText="item.properties.map((property) => property.valueName).join(' ')"
+        :price="item.price"
+        :num="item.count"
+        marginBottom="10"
+      />
+    </div> -->
+
+    <!-- 价格信息 -->
+    <div>
+      <div>
+        <div class="order-item d-flex">
+          <div class="item-title mr-3">商品金额:</div>
+          <div>
+            <span class="item-value">
+              ¥{{ fen2yuan(state.orderInfo.price.totalPrice) }}
+            </span>
+          </div>
+        </div>
+        <!-- <div
+          v-if="state.orderPayload.pointActivityId"
+          class="order-item ss-flex ss-col-center ss-row-between"
+        >
+          <div class="item-title">兑换积分</div>
+          <div class="ss-flex ss-col-center">
+            <image
+              :src="sheep.$url.static('/static/img/shop/goods/score1.svg')"
+              class="score-img"
+            />
+            <span class="item-value ss-m-r-24">
+              {{ state.orderInfo.usePoint }}
+            </span>
+          </div>
+        </div>
+        <div
+          class="order-item ss-flex ss-col-center ss-row-between"
+          v-if="state.orderInfo.type === 0 || state.orderPayload.pointActivityId"
+        >
+          <div class="item-title">积分抵扣</div>
+          <div class="ss-flex ss-col-center">
+            {{ state.pointStatus || state.orderPayload.pointActivityId ? '剩余积分' : '当前积分' }}
+            <image
+              :src="sheep.$url.static('/static/img/shop/goods/score1.svg')"
+              class="score-img"
+            />
+            <span class="item-value ss-m-r-24">
+              {{
+                state.pointStatus || state.orderPayload.pointActivityId
+                  ? state.orderInfo.totalPoint - state.orderInfo.usePoint
+                  : state.orderInfo.totalPoint || 0
+              }}
+            </span>
+            <checkbox-group @change="changeIntegral" v-if="!state.orderPayload.pointActivityId">
+              <checkbox
+                :checked="state.pointStatus"
+                :disabled="!state.orderInfo.totalPoint || state.orderInfo.totalPoint <= 0"
+              />
+            </checkbox-group>
+          </div>
+        </div> -->
+        <!-- 快递配置时,信息的展示 -->
+        <div
+          class="order-item d-flex"
+          v-if="addressState.deliveryType === 1"
+        >
+          <div class="item-title mr-3">运费:</div>
+          <div class="ss-flex ss-col-center">
+            <span class="item-value ss-m-r-24" v-if="state.orderInfo.price.deliveryPrice > 0">
+              +¥{{ fen2yuan(state.orderInfo.price.deliveryPrice) }}
+            </span>
+            <div class="item-value ss-m-r-24" v-else>免运费</div>
+          </div>
+        </div>
+        <!-- 门店自提时,需要填写姓名和手机号 -->
+        <div
+          class="order-item ss-flex ss-col-center ss-row-between"
+          v-if="addressState.deliveryType === 2"
+        >
+          <div class="item-title">联系人</div>
+          <div class="ss-flex ss-col-center">
+            请填写您的联系姓名
+            <!-- <uni-easyinput
+              maxlength="20"
+              placeholder="请填写您的联系姓名"
+              v-model="addressState.receiverName"
+              :inputBorder="false"
+              :clearable="false"
+            /> -->
+          </div>
+        </div>
+        <div
+          class="order-item ss-flex ss-col-center ss-row-between"
+          v-if="addressState.deliveryType === 2"
+        >
+          <div class="item-title">联系电话</div>
+          <div class="ss-flex ss-col-center">
+            请填写您的联系电话
+            <!-- <uni-easyinput
+              maxlength="20"
+              placeholder="请填写您的联系电话"
+              v-model="addressState.receiverMobile"
+              :inputBorder="false"
+              :clearable="false"
+            /> -->
+          </div>
+        </div>
+        <!-- 优惠劵:只有 type = 0 普通订单(非拼团、秒杀、砍价),才可以使用优惠劵 -->
+        <!-- <div
+          class="order-item ss-flex ss-col-center ss-row-between"
+          v-if="state.orderInfo.type === 0"
+        >
+          <div class="item-title">优惠券</div>
+          <div class="ss-flex ss-col-center" @tap="state.showCoupon = true">
+            <span class="item-value text-red" v-if="state.orderPayload.couponId > 0">
+              -¥{{ fen2yuan(state.orderInfo.price.couponPrice) }}
+            </span>
+            <span
+              class="item-value"
+              :class="
+                state.couponInfo.filter((coupon) => coupon.match).length > 0
+                  ? 'text-red'
+                  : 'text-disabled'
+              "
+              v-else
+            >
+              {{
+                state.couponInfo.filter((coupon) => coupon.match).length > 0
+                  ? state.couponInfo.filter((coupon) => coupon.match).length + ' 张可用'
+                  : '暂无可用优惠券'
+              }}
+            </span>
+            <span class="_icon-forward item-icon" />
+          </div>
+        </div>
+        <div
+          class="order-item ss-flex ss-col-center ss-row-between"
+          v-if="state.orderInfo.price.discountPrice > 0"
+        >
+          <div class="item-title">活动优惠</div>
+          <div class="ss-flex ss-col-center" @tap="state.showDiscount = true">
+            <span class="item-value text-red">
+              -¥{{ fen2yuan(state.orderInfo.price.discountPrice) }}
+            </span>
+            <span class="_icon-forward item-icon" />
+          </div>
+        </div>
+        <div
+          class="order-item ss-flex ss-col-center ss-row-between"
+          v-if="state.orderInfo.price.vipPrice > 0"
+        >
+          <div class="item-title">会员优惠</div>
+          <div class="ss-flex ss-col-center">
+            <span class="item-value text-red">
+              -¥{{ fen2yuan(state.orderInfo.price.vipPrice) }}
+            </span>
+          </div>
+        </div> -->
+      </div>
+      <div class="total-num ss-m-r-20">
+        共{{ state.orderInfo.items.reduce((acc, item) => acc + item.count, 0) }}件
+      </div>
+      
+      <div class="mt-5">
+        <v-text-field
+          v-model="state.orderPayload.remark"
+          label="订单备注" 
+          placeholder="建议留言前先与商家沟通"
+          variant="outlined" 
+          density="compact"
+          color="primary"
+        ></v-text-field>
+      </div>
+
+      <div class="total-box-footer d-flex flex-column align-end">
+        <div class="d-flex">
+          <div>合计:</div>
+          <div class="total-num text-red"> ¥{{ fen2yuan(state.orderInfo.price.payPrice) }}</div>
+        </div>
+      </div>
+    </div>
+
+    <!-- 选择优惠券弹框 -->
+    <!-- <s-coupon-select
+      v-model="state.couponInfo"
+      :show="state.showCoupon"
+      @confirm="onSelectCoupon"
+      @close="state.showCoupon = false"
+    /> -->
+
+    <!-- 满额折扣弹框 TODO @puhui999:【折扣】后续要把优惠信息打进去 -->
+    <!-- <s-discount-list
+      v-model="state.orderInfo"
+      :show="state.showDiscount"
+      @close="state.showDiscount = false"
+    /> -->
+
+    <!-- 底部 -->
+    <!-- <su-fixed bottom :opacity="false" bg="bg-white" placeholder :noFixed="false" :index="200">
+      <div class="footer-box border-top ss-flex ss-row-between ss-p-x-20 ss-col-center">
+        <div class="total-box-footer ss-flex ss-col-center">
+          <div class="total-num ss-font-30 text-red">
+            ¥{{ fen2yuan(state.orderInfo.price.payPrice) }}
+          </div>
+        </div>
+        <button
+          class="ss-reset-button ui-BG-Main-Gradient ss-r-40 submit-btn ui-Shadow-Main"
+          @tap="onConfirm"
+        >
+          提交订单
+        </button>
+      </div>
+    </su-fixed> -->
+  </div>
+</template>
+
+<script setup>
+  import { reactive, ref, watch } from 'vue'
+  import AddressSelection from './addressSelection.vue'
+  import { fen2yuan } from '@/hooks/web/useGoods'
+  import { getTradeConfig, createOrder, settlementOrder } from '@/api/mall/trade'
+  import Snackbar from '@/plugins/snackbar'
+  import { getOrderPayStatus, payOrderSubmit } from '@/api/common'
+  import Confirm from '@/plugins/confirm'
+  import { useRouter } from 'vue-router'; const router = useRouter()
+  import { useUserStore } from '@/store/user'; const userStore = useUserStore()
+  import { useI18n } from '@/hooks/web/useI18n'; const { t } = useI18n()
+  const emit = defineEmits(['paySuccess'])
+
+  const props = defineProps({
+    data: {
+      type: String,
+      default: '',
+    },
+  });
+
+  const state = reactive({
+    orderPayload: {},
+    orderInfo: {
+      items: [], // 商品项列表
+      price: {}, // 价格信息
+    },
+    showCoupon: false, // 是否展示优惠劵
+    couponInfo: [], // 优惠劵列表
+    showDiscount: false, // 是否展示营销活动
+    // ========== 积分 ==========
+    pointStatus: false, //是否使用积分
+  });
+  // 检测支付环境
+  const payState = reactive({
+    orderType: 'goods', // 订单类型; goods - 商品订单, recharge - 充值订单
+    orderInfo: {}, // 支付单信息
+    payStatus: 0, // 0=检测支付环境, -2=未查询到支付单信息, -1=支付已过期, 1=待支付,2=订单已支付
+    payMethods: [], // 可选的支付方式
+    payment: '', // 选中的支付方式
+  });
+
+  const addressState = ref({
+    addressInfo: {}, // 选择的收货地址
+    deliveryType: undefined, // 收货方式:1-快递配送,2-门店自提
+    isPickUp: true, // 门店自提是否开启
+    pickUpInfo: {}, // 选择的自提门店信息
+    receiverName: '', // 收件人名称
+    receiverMobile: '', // 收件人手机
+  });
+  
+  watch(
+    () => props.data, 
+    (newVal) => {
+      if (newVal) {
+        state.orderPayload = JSON.parse(newVal);
+        tradeConfig()
+      }
+    },
+    { immediate: true },
+    // { deep: true }
+  )
+  async function tradeConfig () {
+    // 获取交易配置
+    const data = await getTradeConfig();
+    addressState.value.isPickUp = data.deliveryPickUpEnabled;
+
+    // 价格计算
+    // 情况一:先自动选择“快递物流”
+    addressState.value.deliveryType = 1;
+    let orderCode = await getOrderInfo();
+    if (orderCode === 0) {
+      return;
+    }
+    // 情况二:失败,再自动选择“门店自提”
+    if (addressState.value.isPickUp) {
+      addressState.value.deliveryType = 2;
+      let orderCode = await getOrderInfo();
+      if (orderCode === 0) {
+        return;
+      }
+    }
+    // 情况三:都失败,则不选择
+    addressState.value.deliveryType = undefined;
+    await getOrderInfo()
+  }
+
+
+  // ========== 积分 ==========
+  /**
+   * 使用积分抵扣
+   */
+  const changeIntegral = async () => {
+    state.pointStatus = !state.pointStatus;
+    await getOrderInfo();
+  };
+
+  // 选择优惠券
+  async function onSelectCoupon(couponId) {
+    state.orderPayload.couponId = couponId;
+    await getOrderInfo();
+    state.showCoupon = false;
+  }
+
+  // 提交订单
+  function onConfirm() {
+    if (addressState.value.deliveryType === 1 && !addressState.value.addressInfo.id) {
+      Snackbar.warning('请选择收货地址')
+      return;
+    }
+    // if (addressState.value.deliveryType === 2) {
+    //   if (!addressState.value.pickUpInfo.id) {
+    //     Snackbar.warning('请选择自提门店地址')
+    //     return;
+    //   }
+    //   if (addressState.value.receiverName === '' || addressState.value.receiverMobile === '') {
+    //     Snackbar.warning('请填写联系人或联系人电话')
+    //     return;
+    //   }
+    //   if (!/^[\u4e00-\u9fa5\w]{2,16}$/.test(addressState.value.receiverName)) {
+    //     Snackbar.warning('请填写您的真实姓名')
+    //     return;
+    //   }
+    //   if (!/^1(3|4|5|7|8|9|6)\d{9}$/.test(addressState.value.receiverMobile)) {
+    //     Snackbar.warning('请填写正确的手机号')
+    //     return;
+    //   }
+    // }
+    submitOrder();
+  }
+  
+  const userAccount = ref(JSON.parse(localStorage.getItem('userAccount')) || {}) // 账户信息
+
+  userStore.$subscribe((mutation, state) => {
+    if (Object.keys(state.userAccount).length) userAccount.value = state.userAccount
+  })
+
+  // 设置支付订单信息
+  const payImmediately = async (id) => {
+    // 获得支付订单信息
+    const orderRes = await getOrderPayStatus({ id, sync: true })
+    if (!orderRes) {
+      payState.payStatus = -2;
+      return;
+    }
+    payState.orderInfo = orderRes;
+    // 获得支付方式
+    // 
+    // 暂时只对接余额支付(wallet) 
+    const channel = 'wallet'
+    // 对比余额是否不足 订单金额:orderInfo?.price-0
+    const balance = userAccount.value?.balance ? userAccount.value.balance-0 : 0
+    // const balanceShow = userAccount.value?.balance && userAccount.value?.balance > 0 ? (userAccount.value?.balance / 100.0).toFixed(2) : 0
+    if (balance < payState?.orderInfo?.price-0) {
+      // 余额不足
+      Confirm(t('common.confirmTitle'), '余额不足,是否前往充值?', {sureText: '立即前往'}).then(() => {
+        router.push({ path: '/personalRecharge' })
+      })
+    } else {
+      // 提交支付订单
+      let obj = {
+        id: payState.orderInfo.id,
+        channelCode: channel,
+        channelExtras: {},
+      };
+      const res = await payOrderSubmit(obj)
+      console.log('提交支付订单payOrderSubmit:', res)
+      Snackbar.warning('支付成功!')
+      emit('paySuccess')
+      userStore.getUserInfos()
+    }
+  }
+
+  // 创建订单&跳转
+  async function submitOrder() {
+    const data = await createOrder({
+      items: state.orderPayload.items,
+      couponId: state.orderPayload.couponId,
+      remark: state.orderPayload.remark,
+      deliveryType: addressState.value.deliveryType,
+      addressId: addressState.value.addressInfo.id, // 收件地址编号
+      pickUpStoreId: addressState.value.pickUpInfo.id, //自提门店编号
+      receiverName: addressState.value.receiverName, // 选择门店自提时,该字段为联系人名
+      receiverMobile: addressState.value.receiverMobile, // 选择门店自提时,该字段为联系人手机
+      pointStatus: state.pointStatus,
+      combinationActivityId: state.orderPayload.combinationActivityId,
+      combinationHeadId: state.orderPayload.combinationHeadId,
+      seckillActivityId: state.orderPayload.seckillActivityId,
+      pointActivityId: state.orderPayload.pointActivityId,
+    });
+    if (data.payOrderId && data.payOrderId > 0) payImmediately(data.payOrderId)
+    // 更新购物车列表,如果来自购物车
+    // if (state.orderPayload.items[0].cartId > 0) {
+    //   sheep.$store('cart').getList();
+    // }
+
+    // // 跳转到支付页面
+    // if (data.payOrderId && data.payOrderId > 0) {
+    //   sheep.$router.redirect('/pages/pay/index', {
+    //     id: data.payOrderId,
+    //   });
+    // } else {
+    //   sheep.$router.redirect('/pages/order/detail', {
+    //     id: data.id,
+    //   });
+    // }
+
+  }
+
+  // 检查库存 & 计算订单价格
+  async function getOrderInfo() {
+    // 计算价格
+    const data = await settlementOrder({
+      items: state.orderPayload.items,
+      couponId: state.orderPayload.couponId,
+      deliveryType: addressState.value.deliveryType,
+      addressId: addressState.value.addressInfo.id, // 收件地址编号
+      pickUpStoreId: addressState.value.pickUpInfo.id, //自提门店编号
+      receiverName: addressState.value.receiverName, // 选择门店自提时,该字段为联系人名
+      receiverMobile: addressState.value.receiverMobile, // 选择门店自提时,该字段为联系人手机
+      pointStatus: state.pointStatus,
+      combinationActivityId: state.orderPayload.combinationActivityId,
+      combinationHeadId: state.orderPayload.combinationHeadId,
+      seckillActivityId: state.orderPayload.seckillActivityId,
+      pointActivityId: state.orderPayload.pointActivityId,
+    });
+    state.orderInfo = data;
+    state.couponInfo = data.coupons || [];
+    // 设置收货地址
+    if (state.orderInfo.address) {
+      addressState.value.addressInfo = state.orderInfo.address;
+    }
+    return 0;
+  }
+
+  // 使用 watch 监听地址和配送方式的变化
+  watch(addressState, async (newAddress, oldAddress) => {
+    // 如果收货地址或配送方式有变化,则重新计算价格
+    if (
+      newAddress.addressInfo.id !== oldAddress.addressInfo.id ||
+      newAddress.deliveryType !== oldAddress.deliveryType
+    ) {
+      await getOrderInfo();
+    }
+  });
+
+  defineExpose({
+    onConfirm
+  })
+</script>
+
+<style lang="scss" scoped>
+  .score-img {
+    width: 36rpx;
+    height: 36rpx;
+    margin: 0 4rpx;
+  }
+
+  .order-item {
+    height: 80rpx;
+
+    .item-title {
+      font-size: 28rpx;
+      font-weight: 400;
+    }
+
+    .item-value {
+      font-size: 28rpx;
+      font-weight: 500;
+    }
+
+    .text-disabled {
+      color: #bbbbbb;
+    }
+
+    .item-icon {
+      color: #999;
+    }
+
+    .remark-input {
+      text-align: right;
+    }
+
+    .item-placeholder {
+      color: #999;
+      font-size: 26rpx;
+      text-align: right;
+    }
+  }
+
+  .total-box-footer {
+    height: 90rpx;
+
+    .total-num {
+      color: #333333;
+    }
+  }
+
+  .footer-box {
+    height: 100rpx;
+
+    .submit-btn {
+      width: 240rpx;
+      height: 70rpx;
+      font-size: 28rpx;
+      font-weight: 500;
+
+      .goto-pay-text {
+        line-height: 28rpx;
+      }
+    }
+
+    .cancel-btn {
+      width: 240rpx;
+      height: 80rpx;
+      font-size: 26rpx;
+      background-color: #e5e5e5;
+      color: #999;
+    }
+  }
+
+  .title {
+    font-size: 36rpx;
+    font-weight: bold;
+    color: #333333;
+  }
+
+  .subtitle {
+    font-size: 28rpx;
+    color: #999999;
+  }
+
+  .cicon-checkbox {
+    font-size: 36rpx;
+    color: var(--v-primary-base);
+  }
+
+  .cicon-box {
+    font-size: 36rpx;
+    color: #999999;
+  }
+</style>