feat(order): 实现客户订单创建与确认功能
- 新增客户订单创建页面,简化表单字段并优化用户体验 - 实现订单确认页面,展示订单详情、客户信息和收货地址 - 添加订单状态显示和支付跳转功能 - 创建订单相关API接口封装 - 优化路由配置,支持商品链接码访问 - 添加页面标题组件和首页跳转逻辑
This commit is contained in:
24
src/App.vue
24
src/App.vue
@@ -1,14 +1,19 @@
|
|||||||
<template>
|
<template>
|
||||||
<el-container>
|
<!-- 客户页面:商品详情页,不显示管理导航 -->
|
||||||
|
<div v-if="isCustomerPage" class="customer-layout">
|
||||||
|
<router-view />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 管理页面:显示完整导航 -->
|
||||||
|
<el-container v-else>
|
||||||
<el-header>
|
<el-header>
|
||||||
<div class="header-content">
|
<div class="header-content">
|
||||||
<h1>MT Pay</h1>
|
<h1>MT Pay 管理系统</h1>
|
||||||
<el-menu
|
<el-menu
|
||||||
mode="horizontal"
|
mode="horizontal"
|
||||||
:default-active="activeIndex"
|
:default-active="activeIndex"
|
||||||
router
|
router
|
||||||
>
|
>
|
||||||
<el-menu-item index="/">商品详情</el-menu-item>
|
|
||||||
<el-menu-item index="/create-order">创建订单</el-menu-item>
|
<el-menu-item index="/create-order">创建订单</el-menu-item>
|
||||||
<el-menu-item index="/query">订单查询</el-menu-item>
|
<el-menu-item index="/query">订单查询</el-menu-item>
|
||||||
<el-menu-item index="/manage/product">商品管理</el-menu-item>
|
<el-menu-item index="/manage/product">商品管理</el-menu-item>
|
||||||
@@ -26,6 +31,13 @@ import { computed } from 'vue'
|
|||||||
import { useRoute } from 'vue-router'
|
import { useRoute } from 'vue-router'
|
||||||
|
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
|
|
||||||
|
// 判断是否为客户页面(商品详情页)
|
||||||
|
const isCustomerPage = computed(() => {
|
||||||
|
return route.meta?.isCustomerPage === true ||
|
||||||
|
route.path.startsWith('/product/')
|
||||||
|
})
|
||||||
|
|
||||||
const activeIndex = computed(() => route.path)
|
const activeIndex = computed(() => route.path)
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -62,5 +74,11 @@ const activeIndex = computed(() => route.path)
|
|||||||
padding: 20px;
|
padding: 20px;
|
||||||
min-height: calc(100vh - 60px);
|
min-height: calc(100vh - 60px);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 客户页面布局:全屏显示,无导航栏 */
|
||||||
|
.customer-layout {
|
||||||
|
min-height: 100vh;
|
||||||
|
background-color: #f5f7fa;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
|||||||
33
src/api/order.js
Normal file
33
src/api/order.js
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
import request from './request'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建客户订单
|
||||||
|
*/
|
||||||
|
export function createCustomerOrder(data) {
|
||||||
|
return request({
|
||||||
|
url: '/order',
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据订单号获取订单详情
|
||||||
|
*/
|
||||||
|
export function getOrderByOrderNo(orderNo) {
|
||||||
|
return request({
|
||||||
|
url: `/order/${orderNo}`,
|
||||||
|
method: 'get'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据ID获取订单详情
|
||||||
|
*/
|
||||||
|
export function getOrderById(id) {
|
||||||
|
return request({
|
||||||
|
url: `/order/id/${id}`,
|
||||||
|
method: 'get'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
39
src/components/PageHeader.vue
Normal file
39
src/components/PageHeader.vue
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
<template>
|
||||||
|
<div class="page-header">
|
||||||
|
<h2>{{ title }}</h2>
|
||||||
|
<p v-if="description" class="description">{{ description }}</p>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
defineProps({
|
||||||
|
title: {
|
||||||
|
type: String,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
description: {
|
||||||
|
type: String,
|
||||||
|
default: ''
|
||||||
|
}
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.page-header {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-header h2 {
|
||||||
|
margin: 0;
|
||||||
|
font-size: 20px;
|
||||||
|
font-weight: 500;
|
||||||
|
color: #303133;
|
||||||
|
}
|
||||||
|
|
||||||
|
.description {
|
||||||
|
margin: 8px 0 0 0;
|
||||||
|
font-size: 14px;
|
||||||
|
color: #909399;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
@@ -16,13 +16,26 @@ const routes = [
|
|||||||
name: 'ProductDetail',
|
name: 'ProductDetail',
|
||||||
component: ProductDetail,
|
component: ProductDetail,
|
||||||
// 支持商品ID(数字)或链接码(32位字符串)
|
// 支持商品ID(数字)或链接码(32位字符串)
|
||||||
props: true
|
props: true,
|
||||||
|
meta: { isCustomerPage: true } // 标记为客户页面
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/product/link/:linkCode',
|
||||||
|
name: 'ProductDetailByLinkCode',
|
||||||
|
component: ProductDetail,
|
||||||
|
props: true,
|
||||||
|
meta: { isCustomerPage: true } // 标记为客户页面
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/create-order',
|
path: '/create-order',
|
||||||
name: 'CreateOrder',
|
name: 'CreateOrder',
|
||||||
component: CreateOrder
|
component: CreateOrder
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: '/order/confirm',
|
||||||
|
name: 'OrderConfirm',
|
||||||
|
component: () => import('../views/OrderConfirm.vue')
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: '/checkout',
|
path: '/checkout',
|
||||||
name: 'Checkout',
|
name: 'Checkout',
|
||||||
@@ -55,5 +68,11 @@ const router = createRouter({
|
|||||||
routes
|
routes
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// 添加路由守卫,用于调试
|
||||||
|
router.beforeEach((to, from, next) => {
|
||||||
|
console.log('路由导航:', from.path, '->', to.path)
|
||||||
|
next()
|
||||||
|
})
|
||||||
|
|
||||||
export default router
|
export default router
|
||||||
|
|
||||||
|
|||||||
@@ -3,11 +3,11 @@
|
|||||||
<el-card>
|
<el-card>
|
||||||
<template #header>
|
<template #header>
|
||||||
<div class="card-header">
|
<div class="card-header">
|
||||||
<span>创建支付订单</span>
|
<span>填写订单信息</span>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<!-- 商品信息展示卡片 -->
|
<!-- 商品信息展示 -->
|
||||||
<el-card v-if="productInfo" class="product-info-card" shadow="never">
|
<el-card v-if="productInfo" class="product-info-card" shadow="never">
|
||||||
<template #header>
|
<template #header>
|
||||||
<div class="product-card-header">
|
<div class="product-card-header">
|
||||||
@@ -23,24 +23,10 @@
|
|||||||
/>
|
/>
|
||||||
<div class="product-info-details">
|
<div class="product-info-details">
|
||||||
<div class="product-info-name">{{ productInfo.name }}</div>
|
<div class="product-info-name">{{ productInfo.name }}</div>
|
||||||
<div class="product-info-subtitle">{{ productInfo.subtitle }}</div>
|
|
||||||
<!-- SKU信息 -->
|
|
||||||
<div class="product-info-sku" v-if="productInfo.sku">
|
<div class="product-info-sku" v-if="productInfo.sku">
|
||||||
<span class="sku-label">商品编码:</span>
|
<span class="sku-label">SKU:</span>
|
||||||
<span class="sku-value">{{ productInfo.sku }}</span>
|
<span class="sku-value">{{ productInfo.sku }}</span>
|
||||||
</div>
|
</div>
|
||||||
<!-- 规格信息 -->
|
|
||||||
<div class="product-info-specs" v-if="productInfo.specs && productInfo.specs.length > 0">
|
|
||||||
<div
|
|
||||||
v-for="(spec, index) in productInfo.specs"
|
|
||||||
:key="index"
|
|
||||||
class="product-spec-item"
|
|
||||||
>
|
|
||||||
<span class="spec-label">{{ spec.name }}:</span>
|
|
||||||
<span class="spec-value">{{ spec.label }}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<!-- 价格和数量 -->
|
|
||||||
<div class="product-info-price">
|
<div class="product-info-price">
|
||||||
<span class="price-label">单价:</span>
|
<span class="price-label">单价:</span>
|
||||||
<span class="price-value">{{ productInfo.currency }} {{ formatPrice(productInfo.price) }}</span>
|
<span class="price-value">{{ productInfo.currency }} {{ formatPrice(productInfo.price) }}</span>
|
||||||
@@ -58,252 +44,116 @@
|
|||||||
ref="formRef"
|
ref="formRef"
|
||||||
:model="form"
|
:model="form"
|
||||||
:rules="rules"
|
:rules="rules"
|
||||||
label-width="150px"
|
label-width="120px"
|
||||||
label-position="left"
|
label-position="left"
|
||||||
>
|
>
|
||||||
<el-form-item label="商户订单号" prop="merchantTransactionId">
|
<el-divider>客户信息</el-divider>
|
||||||
|
|
||||||
|
<el-form-item label="客户姓名" prop="customerName">
|
||||||
<el-input
|
<el-input
|
||||||
v-model="form.merchantTransactionId"
|
v-model="form.customerName"
|
||||||
placeholder="请输入商户订单号(全局唯一)"
|
placeholder="请输入客户姓名"
|
||||||
clearable
|
|
||||||
/>
|
|
||||||
<el-button
|
|
||||||
type="primary"
|
|
||||||
link
|
|
||||||
@click="generateOrderIdLocal"
|
|
||||||
style="margin-left: 10px"
|
|
||||||
>
|
|
||||||
自动生成
|
|
||||||
</el-button>
|
|
||||||
</el-form-item>
|
|
||||||
|
|
||||||
<el-form-item label="交易金额" prop="amount">
|
|
||||||
<el-input-number
|
|
||||||
v-model="form.amount"
|
|
||||||
:precision="2"
|
|
||||||
:min="0.01"
|
|
||||||
:max="999999.99"
|
|
||||||
placeholder="请输入交易金额"
|
|
||||||
style="width: 100%"
|
|
||||||
/>
|
|
||||||
</el-form-item>
|
|
||||||
|
|
||||||
<el-form-item label="交易币种" prop="currency">
|
|
||||||
<el-select v-model="form.currency" placeholder="请选择币种" style="width: 100%">
|
|
||||||
<el-option label="USD - 美元" value="USD" />
|
|
||||||
<el-option label="EUR - 欧元" value="EUR" />
|
|
||||||
<el-option label="GBP - 英镑" value="GBP" />
|
|
||||||
<el-option label="CNY - 人民币" value="CNY" />
|
|
||||||
<el-option label="JPY - 日元" value="JPY" />
|
|
||||||
</el-select>
|
|
||||||
</el-form-item>
|
|
||||||
|
|
||||||
<el-form-item label="交易类型" prop="paymentType">
|
|
||||||
<el-radio-group v-model="form.paymentType">
|
|
||||||
<el-radio label="SALE">直接付款</el-radio>
|
|
||||||
<el-radio label="AUTH">预授权</el-radio>
|
|
||||||
</el-radio-group>
|
|
||||||
</el-form-item>
|
|
||||||
|
|
||||||
<el-form-item label="商户用户ID" prop="merchantUserId">
|
|
||||||
<el-input
|
|
||||||
v-model="form.merchantUserId"
|
|
||||||
placeholder="请输入商户用户ID(可选)"
|
|
||||||
clearable
|
clearable
|
||||||
/>
|
/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
|
||||||
<el-form-item label="结果重定向URL" prop="shopperResultUrl">
|
<el-form-item label="客户电话" prop="customerPhone">
|
||||||
<el-input
|
<el-input
|
||||||
v-model="form.shopperResultUrl"
|
v-model="form.customerPhone"
|
||||||
placeholder="支付完成后跳转的URL"
|
placeholder="请输入客户电话"
|
||||||
|
clearable
|
||||||
/>
|
/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
|
||||||
<el-form-item label="取消重定向URL" prop="shopperCancelUrl">
|
<el-form-item label="客户邮箱" prop="customerEmail">
|
||||||
<el-input
|
<el-input
|
||||||
v-model="form.shopperCancelUrl"
|
v-model="form.customerEmail"
|
||||||
placeholder="取消支付后跳转的URL"
|
placeholder="请输入客户邮箱(可选)"
|
||||||
/>
|
clearable
|
||||||
</el-form-item>
|
|
||||||
|
|
||||||
<el-divider>风控信息</el-divider>
|
|
||||||
|
|
||||||
<el-form-item label="客户姓名" prop="riskInfo.customer.firstName">
|
|
||||||
<el-input
|
|
||||||
v-model="form.riskInfo.customer.firstName"
|
|
||||||
placeholder="名"
|
|
||||||
style="width: 48%"
|
|
||||||
/>
|
|
||||||
<el-input
|
|
||||||
v-model="form.riskInfo.customer.lastName"
|
|
||||||
placeholder="姓"
|
|
||||||
style="width: 48%; margin-left: 4%"
|
|
||||||
/>
|
|
||||||
</el-form-item>
|
|
||||||
|
|
||||||
<el-form-item label="客户邮箱" prop="riskInfo.customer.email">
|
|
||||||
<el-input
|
|
||||||
v-model="form.riskInfo.customer.email"
|
|
||||||
placeholder="请输入邮箱"
|
|
||||||
/>
|
|
||||||
</el-form-item>
|
|
||||||
|
|
||||||
<el-form-item label="客户电话" prop="riskInfo.customer.phone">
|
|
||||||
<el-input
|
|
||||||
v-model="form.riskInfo.customer.phone"
|
|
||||||
placeholder="请输入电话"
|
|
||||||
/>
|
|
||||||
</el-form-item>
|
|
||||||
|
|
||||||
<el-divider>商品信息</el-divider>
|
|
||||||
|
|
||||||
<el-form-item label="商品名称" prop="riskInfo.goods.0.name">
|
|
||||||
<el-input
|
|
||||||
v-model="form.riskInfo.goods[0].name"
|
|
||||||
placeholder="请输入商品名称"
|
|
||||||
/>
|
|
||||||
</el-form-item>
|
|
||||||
|
|
||||||
<el-form-item label="商品描述" prop="riskInfo.goods.0.description">
|
|
||||||
<el-input
|
|
||||||
v-model="form.riskInfo.goods[0].description"
|
|
||||||
type="textarea"
|
|
||||||
:rows="2"
|
|
||||||
placeholder="请输入商品描述"
|
|
||||||
/>
|
|
||||||
</el-form-item>
|
|
||||||
|
|
||||||
<el-form-item label="商品单价" prop="riskInfo.goods.0.averageUnitPrice">
|
|
||||||
<el-input-number
|
|
||||||
v-model="form.riskInfo.goods[0].averageUnitPrice"
|
|
||||||
:precision="2"
|
|
||||||
:min="0.01"
|
|
||||||
style="width: 100%"
|
|
||||||
/>
|
|
||||||
</el-form-item>
|
|
||||||
|
|
||||||
<el-form-item label="商品数量" prop="riskInfo.goods.0.number">
|
|
||||||
<el-input-number
|
|
||||||
v-model="form.riskInfo.goods[0].number"
|
|
||||||
:min="1"
|
|
||||||
style="width: 100%"
|
|
||||||
/>
|
/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
|
||||||
<el-divider>收货地址</el-divider>
|
<el-divider>收货地址</el-divider>
|
||||||
|
|
||||||
<el-form-item label="收货人姓名" prop="riskInfo.shipping.firstName">
|
<el-form-item label="收货人姓名" prop="shippingName">
|
||||||
<el-input
|
<el-input
|
||||||
v-model="form.riskInfo.shipping.firstName"
|
v-model="form.shippingName"
|
||||||
placeholder="名"
|
placeholder="请输入收货人姓名"
|
||||||
style="width: 48%"
|
clearable
|
||||||
/>
|
|
||||||
<el-input
|
|
||||||
v-model="form.riskInfo.shipping.lastName"
|
|
||||||
placeholder="姓"
|
|
||||||
style="width: 48%; margin-left: 4%"
|
|
||||||
/>
|
/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
|
||||||
<el-form-item label="街道地址" prop="riskInfo.shipping.street">
|
<el-form-item label="收货人电话" prop="shippingPhone">
|
||||||
<el-input
|
<el-input
|
||||||
v-model="form.riskInfo.shipping.street"
|
v-model="form.shippingPhone"
|
||||||
placeholder="请输入街道地址"
|
placeholder="请输入收货人电话"
|
||||||
|
clearable
|
||||||
/>
|
/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
|
||||||
<el-form-item label="城市" prop="riskInfo.shipping.city">
|
<el-form-item label="收货国家" prop="shippingCountry">
|
||||||
<el-input
|
<el-select v-model="form.shippingCountry" placeholder="请选择国家" style="width: 100%">
|
||||||
v-model="form.riskInfo.shipping.city"
|
<el-option label="中国 (CN)" value="CN" />
|
||||||
placeholder="请输入城市"
|
<el-option label="美国 (US)" value="US" />
|
||||||
style="width: 48%"
|
<el-option label="马来西亚 (MY)" value="MY" />
|
||||||
/>
|
<el-option label="菲律宾 (PH)" value="PH" />
|
||||||
<el-input
|
<el-option label="泰国 (TH)" value="TH" />
|
||||||
v-model="form.riskInfo.shipping.state"
|
<el-option label="越南 (VN)" value="VN" />
|
||||||
placeholder="州/省(可选)"
|
<el-option label="新加坡 (SG)" value="SG" />
|
||||||
style="width: 48%; margin-left: 4%"
|
<el-option label="英国 (GB)" value="GB" />
|
||||||
/>
|
<el-option label="德国 (DE)" value="DE" />
|
||||||
</el-form-item>
|
<el-option label="法国 (FR)" value="FR" />
|
||||||
|
|
||||||
<el-form-item label="邮编" prop="riskInfo.shipping.postcode">
|
|
||||||
<el-input
|
|
||||||
v-model="form.riskInfo.shipping.postcode"
|
|
||||||
placeholder="请输入邮编"
|
|
||||||
style="width: 48%"
|
|
||||||
/>
|
|
||||||
<el-select
|
|
||||||
v-model="form.riskInfo.shipping.country"
|
|
||||||
placeholder="国家"
|
|
||||||
style="width: 48%; margin-left: 4%"
|
|
||||||
>
|
|
||||||
<el-option label="US - 美国" value="US" />
|
|
||||||
<el-option label="CN - 中国" value="CN" />
|
|
||||||
<el-option label="GB - 英国" value="GB" />
|
|
||||||
<el-option label="DE - 德国" value="DE" />
|
|
||||||
<el-option label="FR - 法国" value="FR" />
|
|
||||||
</el-select>
|
</el-select>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
|
||||||
<el-divider>账单地址</el-divider>
|
<el-form-item label="收货城市" prop="shippingCity">
|
||||||
|
|
||||||
<el-form-item label="账单人姓名" prop="riskInfo.billing.firstName">
|
|
||||||
<el-input
|
<el-input
|
||||||
v-model="form.riskInfo.billing.firstName"
|
v-model="form.shippingCity"
|
||||||
placeholder="名"
|
placeholder="请输入收货城市"
|
||||||
style="width: 48%"
|
style="width: 48%"
|
||||||
|
clearable
|
||||||
/>
|
/>
|
||||||
<el-input
|
<el-input
|
||||||
v-model="form.riskInfo.billing.lastName"
|
v-model="form.shippingState"
|
||||||
placeholder="姓"
|
|
||||||
style="width: 48%; margin-left: 4%"
|
|
||||||
/>
|
|
||||||
</el-form-item>
|
|
||||||
|
|
||||||
<el-form-item label="账单街道地址" prop="riskInfo.billing.street">
|
|
||||||
<el-input
|
|
||||||
v-model="form.riskInfo.billing.street"
|
|
||||||
placeholder="请输入街道地址"
|
|
||||||
/>
|
|
||||||
</el-form-item>
|
|
||||||
|
|
||||||
<el-form-item label="账单城市" prop="riskInfo.billing.city">
|
|
||||||
<el-input
|
|
||||||
v-model="form.riskInfo.billing.city"
|
|
||||||
placeholder="请输入城市"
|
|
||||||
style="width: 48%"
|
|
||||||
/>
|
|
||||||
<el-input
|
|
||||||
v-model="form.riskInfo.billing.state"
|
|
||||||
placeholder="州/省(可选)"
|
placeholder="州/省(可选)"
|
||||||
style="width: 48%; margin-left: 4%"
|
style="width: 48%; margin-left: 4%"
|
||||||
|
clearable
|
||||||
/>
|
/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
|
||||||
<el-form-item label="账单邮编" prop="riskInfo.billing.postcode">
|
<el-form-item label="街道地址" prop="shippingStreet">
|
||||||
<el-input
|
<el-input
|
||||||
v-model="form.riskInfo.billing.postcode"
|
v-model="form.shippingStreet"
|
||||||
placeholder="请输入邮编"
|
type="textarea"
|
||||||
style="width: 48%"
|
:rows="2"
|
||||||
|
placeholder="请输入详细街道地址"
|
||||||
|
clearable
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
<el-form-item label="邮编" prop="shippingPostcode">
|
||||||
|
<el-input
|
||||||
|
v-model="form.shippingPostcode"
|
||||||
|
placeholder="请输入邮编(可选)"
|
||||||
|
clearable
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
<el-form-item label="订单备注" prop="remark">
|
||||||
|
<el-input
|
||||||
|
v-model="form.remark"
|
||||||
|
type="textarea"
|
||||||
|
:rows="3"
|
||||||
|
placeholder="请输入订单备注(可选)"
|
||||||
|
clearable
|
||||||
/>
|
/>
|
||||||
<el-select
|
|
||||||
v-model="form.riskInfo.billing.country"
|
|
||||||
placeholder="国家"
|
|
||||||
style="width: 48%; margin-left: 4%"
|
|
||||||
>
|
|
||||||
<el-option label="US - 美国" value="US" />
|
|
||||||
<el-option label="CN - 中国" value="CN" />
|
|
||||||
<el-option label="GB - 英国" value="GB" />
|
|
||||||
<el-option label="DE - 德国" value="DE" />
|
|
||||||
<el-option label="FR - 法国" value="FR" />
|
|
||||||
</el-select>
|
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
|
||||||
<el-form-item>
|
<el-form-item>
|
||||||
<el-button type="primary" @click="submitForm" :loading="loading">
|
<el-button type="primary" size="large" @click="submitForm" :loading="loading" style="width: 200px">
|
||||||
创建订单并支付
|
提交订单
|
||||||
</el-button>
|
</el-button>
|
||||||
<el-button @click="resetForm">重置</el-button>
|
<el-button @click="goBack" style="margin-left: 10px">返回</el-button>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
</el-card>
|
</el-card>
|
||||||
@@ -311,11 +161,11 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { ref, reactive, onMounted, computed } from 'vue'
|
import { ref, reactive, onMounted } from 'vue'
|
||||||
import { useRouter, useRoute } from 'vue-router'
|
import { useRouter, useRoute } from 'vue-router'
|
||||||
import { ElMessage } from 'element-plus'
|
import { ElMessage } from 'element-plus'
|
||||||
import { createPaymentOrder } from '../api/payment'
|
import { createCustomerOrder } from '../api/order'
|
||||||
import { generateOrderId, formatAmount } from '../utils/helpers'
|
import { formatAmount } from '../utils/helpers'
|
||||||
|
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
@@ -324,80 +174,54 @@ const loading = ref(false)
|
|||||||
const productInfo = ref(null)
|
const productInfo = ref(null)
|
||||||
|
|
||||||
const form = reactive({
|
const form = reactive({
|
||||||
merchantTransactionId: '',
|
customerName: '',
|
||||||
amount: '',
|
customerPhone: '',
|
||||||
currency: 'USD',
|
customerEmail: '',
|
||||||
paymentType: 'SALE',
|
shippingName: '',
|
||||||
merchantUserId: '',
|
shippingPhone: '',
|
||||||
shopperResultUrl: `${window.location.origin}/result`,
|
shippingCountry: '',
|
||||||
shopperCancelUrl: `${window.location.origin}/result`,
|
shippingState: '',
|
||||||
riskInfo: {
|
shippingCity: '',
|
||||||
customer: {
|
shippingStreet: '',
|
||||||
firstName: '',
|
shippingPostcode: '',
|
||||||
lastName: '',
|
remark: ''
|
||||||
email: '',
|
|
||||||
phone: '',
|
|
||||||
registerTime: new Date().toISOString().replace(/[-:]/g, '').split('.')[0],
|
|
||||||
registerIp: '',
|
|
||||||
registerTerminal: 'PC',
|
|
||||||
registerRange: '1',
|
|
||||||
orderTime: new Date().toISOString().replace(/[-:]/g, '').split('.')[0],
|
|
||||||
orderIp: '',
|
|
||||||
orderCountry: 'US'
|
|
||||||
},
|
|
||||||
goods: [{
|
|
||||||
name: '',
|
|
||||||
description: '',
|
|
||||||
averageUnitPrice: '',
|
|
||||||
number: '1',
|
|
||||||
virtualProduct: 'N'
|
|
||||||
}],
|
|
||||||
shipping: {
|
|
||||||
firstName: '',
|
|
||||||
lastName: '',
|
|
||||||
street: '',
|
|
||||||
city: '',
|
|
||||||
state: '',
|
|
||||||
postcode: '',
|
|
||||||
country: 'US'
|
|
||||||
},
|
|
||||||
billing: {
|
|
||||||
firstName: '',
|
|
||||||
lastName: '',
|
|
||||||
street: '',
|
|
||||||
city: '',
|
|
||||||
state: '',
|
|
||||||
postcode: '',
|
|
||||||
country: 'US'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
|
|
||||||
const rules = {
|
const rules = {
|
||||||
merchantTransactionId: [
|
customerName: [
|
||||||
{ required: true, message: '请输入商户订单号', trigger: 'blur' }
|
{ required: true, message: '请输入客户姓名', trigger: 'blur' }
|
||||||
],
|
],
|
||||||
amount: [
|
customerPhone: [
|
||||||
{ required: true, message: '请输入交易金额', trigger: 'blur' }
|
{ required: true, message: '请输入客户电话', trigger: 'blur' },
|
||||||
|
{ pattern: /^[0-9+\-\s()]+$/, message: '请输入有效的电话号码', trigger: 'blur' }
|
||||||
],
|
],
|
||||||
currency: [
|
customerEmail: [
|
||||||
{ required: true, message: '请选择交易币种', trigger: 'change' }
|
{ type: 'email', message: '请输入有效的邮箱地址', trigger: 'blur' }
|
||||||
],
|
],
|
||||||
paymentType: [
|
shippingName: [
|
||||||
{ required: true, message: '请选择交易类型', trigger: 'change' }
|
{ required: true, message: '请输入收货人姓名', trigger: 'blur' }
|
||||||
],
|
],
|
||||||
shopperResultUrl: [
|
shippingPhone: [
|
||||||
{ required: true, message: '请输入结果重定向URL', trigger: 'blur' }
|
{ required: true, message: '请输入收货人电话', trigger: 'blur' },
|
||||||
|
{ pattern: /^[0-9+\-\s()]+$/, message: '请输入有效的电话号码', trigger: 'blur' }
|
||||||
],
|
],
|
||||||
shopperCancelUrl: [
|
shippingCountry: [
|
||||||
{ required: true, message: '请输入取消重定向URL', trigger: 'blur' }
|
{ required: true, message: '请选择收货国家', trigger: 'change' }
|
||||||
|
],
|
||||||
|
shippingCity: [
|
||||||
|
{ required: true, message: '请输入收货城市', trigger: 'blur' }
|
||||||
|
],
|
||||||
|
shippingStreet: [
|
||||||
|
{ required: true, message: '请输入街道地址', trigger: 'blur' }
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
const generateOrderIdLocal = () => {
|
// 返回上一页
|
||||||
form.merchantTransactionId = generateOrderId()
|
const goBack = () => {
|
||||||
|
router.back()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 提交订单
|
||||||
const submitForm = async () => {
|
const submitForm = async () => {
|
||||||
if (!formRef.value) return
|
if (!formRef.value) return
|
||||||
|
|
||||||
@@ -407,34 +231,40 @@ const submitForm = async () => {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!productInfo.value) {
|
||||||
|
ElMessage.error('商品信息缺失,请重新选择商品')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
loading.value = true
|
loading.value = true
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// 格式化数据
|
// 构建订单请求数据
|
||||||
const requestData = {
|
const orderData = {
|
||||||
...form,
|
productId: productInfo.value.id,
|
||||||
amount: form.amount.toFixed(2),
|
skuId: productInfo.value.skuId,
|
||||||
signType: 'MD5',
|
quantity: productInfo.value.quantity,
|
||||||
language: 'zh',
|
customerName: form.customerName,
|
||||||
threeDSecure: 'N',
|
customerPhone: form.customerPhone,
|
||||||
riskInfo: {
|
customerEmail: form.customerEmail || null,
|
||||||
...form.riskInfo,
|
shippingName: form.shippingName,
|
||||||
goods: form.riskInfo.goods.map(g => ({
|
shippingPhone: form.shippingPhone,
|
||||||
...g,
|
shippingCountry: form.shippingCountry,
|
||||||
averageUnitPrice: g.averageUnitPrice.toFixed(2),
|
shippingState: form.shippingState || null,
|
||||||
number: g.number.toString()
|
shippingCity: form.shippingCity,
|
||||||
}))
|
shippingStreet: form.shippingStreet,
|
||||||
}
|
shippingPostcode: form.shippingPostcode || null,
|
||||||
|
remark: form.remark || null
|
||||||
}
|
}
|
||||||
|
|
||||||
const response = await createPaymentOrder(requestData)
|
const response = await createCustomerOrder(orderData)
|
||||||
|
|
||||||
if (response.code === '0000') {
|
if (response.code === '0000' && response.data) {
|
||||||
ElMessage.success('订单创建成功')
|
ElMessage.success('订单创建成功')
|
||||||
// 跳转到收银台页面
|
// 跳转到订单确认页面
|
||||||
router.push({
|
router.push({
|
||||||
path: '/checkout',
|
path: '/order/confirm',
|
||||||
query: { token: response.data.token }
|
query: { orderNo: response.data.orderNo }
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
ElMessage.error(response.message || '创建订单失败')
|
ElMessage.error(response.message || '创建订单失败')
|
||||||
@@ -448,12 +278,6 @@ const submitForm = async () => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const resetForm = () => {
|
|
||||||
if (!formRef.value) return
|
|
||||||
formRef.value.resetFields()
|
|
||||||
generateOrderIdLocal()
|
|
||||||
}
|
|
||||||
|
|
||||||
// 格式化价格
|
// 格式化价格
|
||||||
const formatPrice = (price) => {
|
const formatPrice = (price) => {
|
||||||
return formatAmount(price)
|
return formatAmount(price)
|
||||||
@@ -466,33 +290,24 @@ onMounted(() => {
|
|||||||
const data = JSON.parse(decodeURIComponent(route.query.data))
|
const data = JSON.parse(decodeURIComponent(route.query.data))
|
||||||
if (data.product) {
|
if (data.product) {
|
||||||
productInfo.value = data.product
|
productInfo.value = data.product
|
||||||
|
|
||||||
// 自动填充表单
|
|
||||||
form.amount = (data.product.price * data.product.quantity).toFixed(2)
|
|
||||||
form.currency = data.product.currency || 'USD'
|
|
||||||
form.riskInfo.goods[0].name = data.product.name
|
|
||||||
form.riskInfo.goods[0].description = `${data.product.subtitle} ${data.product.specs.map(s => `${s.name}:${s.label}`).join(';')}`
|
|
||||||
form.riskInfo.goods[0].averageUnitPrice = data.product.price.toFixed(2)
|
|
||||||
form.riskInfo.goods[0].number = data.product.quantity.toString()
|
|
||||||
// 如果有SKU,添加到商品描述中
|
|
||||||
if (data.product.sku) {
|
|
||||||
form.riskInfo.goods[0].sku = data.product.sku
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('解析商品信息失败:', error)
|
console.error('解析商品信息失败:', error)
|
||||||
|
ElMessage.error('商品信息解析失败')
|
||||||
|
router.push('/')
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
ElMessage.error('缺少商品信息')
|
||||||
|
router.push('/')
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
// 初始化时生成订单号
|
|
||||||
generateOrderIdLocal()
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.create-order {
|
.create-order {
|
||||||
max-width: 1000px;
|
max-width: 1000px;
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
|
padding: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.card-header {
|
.card-header {
|
||||||
@@ -546,12 +361,6 @@ generateOrderIdLocal()
|
|||||||
line-height: 1.5;
|
line-height: 1.5;
|
||||||
}
|
}
|
||||||
|
|
||||||
.product-info-subtitle {
|
|
||||||
font-size: 14px;
|
|
||||||
color: #909399;
|
|
||||||
line-height: 1.5;
|
|
||||||
}
|
|
||||||
|
|
||||||
.product-info-sku {
|
.product-info-sku {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
@@ -572,34 +381,6 @@ generateOrderIdLocal()
|
|||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
color: #303133;
|
color: #303133;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
font-family: 'Courier New', monospace;
|
|
||||||
background: #fff;
|
|
||||||
padding: 2px 8px;
|
|
||||||
border-radius: 4px;
|
|
||||||
border: 1px solid #dcdfe6;
|
|
||||||
}
|
|
||||||
|
|
||||||
.product-info-specs {
|
|
||||||
display: flex;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
gap: 15px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.product-spec-item {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.spec-label {
|
|
||||||
font-size: 14px;
|
|
||||||
color: #909399;
|
|
||||||
}
|
|
||||||
|
|
||||||
.spec-value {
|
|
||||||
font-size: 14px;
|
|
||||||
color: #303133;
|
|
||||||
font-weight: 500;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.product-info-price {
|
.product-info-price {
|
||||||
@@ -619,18 +400,6 @@ generateOrderIdLocal()
|
|||||||
color: #606266;
|
color: #606266;
|
||||||
}
|
}
|
||||||
|
|
||||||
.price-value {
|
|
||||||
font-size: 14px;
|
|
||||||
color: #303133;
|
|
||||||
font-weight: 500;
|
|
||||||
}
|
|
||||||
|
|
||||||
.quantity-value {
|
|
||||||
font-size: 14px;
|
|
||||||
color: #303133;
|
|
||||||
font-weight: 500;
|
|
||||||
}
|
|
||||||
|
|
||||||
.total-label {
|
.total-label {
|
||||||
margin-left: auto;
|
margin-left: auto;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
@@ -642,4 +411,3 @@ generateOrderIdLocal()
|
|||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
|||||||
59
src/views/Home.vue
Normal file
59
src/views/Home.vue
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
<template>
|
||||||
|
<div class="home">
|
||||||
|
<el-card>
|
||||||
|
<template #header>
|
||||||
|
<div class="card-header">
|
||||||
|
<h2>欢迎使用 MT Pay 支付系统</h2>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<div class="welcome-content">
|
||||||
|
<p>正在跳转到商品管理页面...</p>
|
||||||
|
<el-button type="primary" @click="goToProductManage">
|
||||||
|
立即前往商品管理
|
||||||
|
</el-button>
|
||||||
|
</div>
|
||||||
|
</el-card>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { onMounted } from 'vue'
|
||||||
|
import { useRouter } from 'vue-router'
|
||||||
|
|
||||||
|
const router = useRouter()
|
||||||
|
|
||||||
|
const goToProductManage = () => {
|
||||||
|
router.push('/manage/product')
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
// 自动跳转到商品管理页面
|
||||||
|
setTimeout(() => {
|
||||||
|
router.push('/manage/product')
|
||||||
|
}, 1000)
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.home {
|
||||||
|
max-width: 800px;
|
||||||
|
margin: 50px auto;
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-header {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.welcome-content {
|
||||||
|
text-align: center;
|
||||||
|
padding: 40px 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.welcome-content p {
|
||||||
|
font-size: 16px;
|
||||||
|
color: #666;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
330
src/views/OrderConfirm.vue
Normal file
330
src/views/OrderConfirm.vue
Normal file
@@ -0,0 +1,330 @@
|
|||||||
|
<template>
|
||||||
|
<div class="order-confirm">
|
||||||
|
<el-card v-if="orderLoading">
|
||||||
|
<el-skeleton :rows="10" animated />
|
||||||
|
</el-card>
|
||||||
|
|
||||||
|
<template v-else-if="order">
|
||||||
|
<el-card>
|
||||||
|
<template #header>
|
||||||
|
<div class="card-header">
|
||||||
|
<span>订单确认</span>
|
||||||
|
<el-tag :type="getStatusType(order.status)" size="large">
|
||||||
|
{{ getStatusText(order.status) }}
|
||||||
|
</el-tag>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<!-- 订单信息 -->
|
||||||
|
<div class="order-info-section">
|
||||||
|
<h3>订单信息</h3>
|
||||||
|
<el-descriptions :column="2" border>
|
||||||
|
<el-descriptions-item label="订单号">{{ order.orderNo }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="订单金额">
|
||||||
|
<span class="order-amount">{{ order.currency }} {{ formatPrice(order.totalAmount) }}</span>
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="商品名称" :span="2">{{ order.productName }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="SKU名称" :span="2">{{ order.skuName }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="购买数量">{{ order.quantity }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="单价">{{ order.currency }} {{ formatPrice(order.unitPrice) }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="创建时间" :span="2">{{ formatDateTime(order.createTime) }}</el-descriptions-item>
|
||||||
|
</el-descriptions>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 客户信息 -->
|
||||||
|
<div class="order-info-section">
|
||||||
|
<h3>客户信息</h3>
|
||||||
|
<el-descriptions :column="2" border>
|
||||||
|
<el-descriptions-item label="客户姓名">{{ order.customerName }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="客户电话">{{ order.customerPhone }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="客户邮箱" :span="2">
|
||||||
|
{{ order.customerEmail || '未填写' }}
|
||||||
|
</el-descriptions-item>
|
||||||
|
</el-descriptions>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 收货地址 -->
|
||||||
|
<div class="order-info-section">
|
||||||
|
<h3>收货地址</h3>
|
||||||
|
<el-descriptions :column="2" border>
|
||||||
|
<el-descriptions-item label="收货人">{{ order.shippingName }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="收货电话">{{ order.shippingPhone }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="收货地址" :span="2">
|
||||||
|
{{ formatShippingAddress(order) }}
|
||||||
|
</el-descriptions-item>
|
||||||
|
</el-descriptions>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 订单备注 -->
|
||||||
|
<div class="order-info-section" v-if="order.remark">
|
||||||
|
<h3>订单备注</h3>
|
||||||
|
<p class="order-remark">{{ order.remark }}</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 操作按钮 -->
|
||||||
|
<div class="action-buttons">
|
||||||
|
<el-button
|
||||||
|
v-if="order.paymentStatus === 'UNPAID'"
|
||||||
|
type="primary"
|
||||||
|
size="large"
|
||||||
|
@click="handlePay"
|
||||||
|
:loading="payLoading"
|
||||||
|
style="width: 200px"
|
||||||
|
>
|
||||||
|
<el-icon><Money /></el-icon>
|
||||||
|
立即支付
|
||||||
|
</el-button>
|
||||||
|
<el-button @click="goBack" style="margin-left: 10px">返回</el-button>
|
||||||
|
</div>
|
||||||
|
</el-card>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<el-card v-else>
|
||||||
|
<el-empty description="订单不存在">
|
||||||
|
<el-button type="primary" @click="goBack">返回</el-button>
|
||||||
|
</el-empty>
|
||||||
|
</el-card>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { ref, onMounted } from 'vue'
|
||||||
|
import { useRouter, useRoute } from 'vue-router'
|
||||||
|
import { ElMessage } from 'element-plus'
|
||||||
|
import { Money } from '@element-plus/icons-vue'
|
||||||
|
import { getOrderByOrderNo } from '../api/order'
|
||||||
|
import { createPaymentOrder } from '../api/payment'
|
||||||
|
import { formatAmount } from '../utils/helpers'
|
||||||
|
import { generateOrderId } from '../utils/helpers'
|
||||||
|
|
||||||
|
const router = useRouter()
|
||||||
|
const route = useRoute()
|
||||||
|
const orderLoading = ref(true)
|
||||||
|
const payLoading = ref(false)
|
||||||
|
const order = ref(null)
|
||||||
|
|
||||||
|
// 获取订单状态类型
|
||||||
|
const getStatusType = (status) => {
|
||||||
|
const statusMap = {
|
||||||
|
'PENDING': 'warning',
|
||||||
|
'PAID': 'success',
|
||||||
|
'SHIPPED': 'info',
|
||||||
|
'COMPLETED': 'success',
|
||||||
|
'CANCELLED': 'info'
|
||||||
|
}
|
||||||
|
return statusMap[status] || 'info'
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取订单状态文本
|
||||||
|
const getStatusText = (status) => {
|
||||||
|
const statusMap = {
|
||||||
|
'PENDING': '待支付',
|
||||||
|
'PAID': '已支付',
|
||||||
|
'SHIPPED': '已发货',
|
||||||
|
'COMPLETED': '已完成',
|
||||||
|
'CANCELLED': '已取消'
|
||||||
|
}
|
||||||
|
return statusMap[status] || status
|
||||||
|
}
|
||||||
|
|
||||||
|
// 格式化价格
|
||||||
|
const formatPrice = (price) => {
|
||||||
|
return formatAmount(price)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 格式化日期时间
|
||||||
|
const formatDateTime = (dateTime) => {
|
||||||
|
if (!dateTime) return '-'
|
||||||
|
const date = new Date(dateTime)
|
||||||
|
return date.toLocaleString('zh-CN')
|
||||||
|
}
|
||||||
|
|
||||||
|
// 格式化收货地址
|
||||||
|
const formatShippingAddress = (order) => {
|
||||||
|
const parts = []
|
||||||
|
if (order.shippingCountry) parts.push(order.shippingCountry)
|
||||||
|
if (order.shippingState) parts.push(order.shippingState)
|
||||||
|
if (order.shippingCity) parts.push(order.shippingCity)
|
||||||
|
if (order.shippingStreet) parts.push(order.shippingStreet)
|
||||||
|
if (order.shippingPostcode) parts.push(`邮编:${order.shippingPostcode}`)
|
||||||
|
return parts.join(' ')
|
||||||
|
}
|
||||||
|
|
||||||
|
// 返回上一页
|
||||||
|
const goBack = () => {
|
||||||
|
router.back()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 加载订单详情
|
||||||
|
const loadOrder = async () => {
|
||||||
|
const orderNo = route.query.orderNo
|
||||||
|
if (!orderNo) {
|
||||||
|
ElMessage.error('订单号不能为空')
|
||||||
|
router.push('/')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
orderLoading.value = true
|
||||||
|
try {
|
||||||
|
const response = await getOrderByOrderNo(orderNo)
|
||||||
|
if (response.code === '0000' && response.data) {
|
||||||
|
order.value = response.data
|
||||||
|
} else {
|
||||||
|
ElMessage.error(response.message || '获取订单信息失败')
|
||||||
|
order.value = null
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取订单信息失败:', error)
|
||||||
|
ElMessage.error('获取订单信息失败')
|
||||||
|
order.value = null
|
||||||
|
} finally {
|
||||||
|
orderLoading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理支付
|
||||||
|
const handlePay = async () => {
|
||||||
|
if (!order.value) {
|
||||||
|
ElMessage.error('订单信息不存在')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (order.value.paymentStatus !== 'UNPAID') {
|
||||||
|
ElMessage.warning('订单已支付或已取消')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
payLoading.value = true
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 构建支付请求数据
|
||||||
|
const paymentData = {
|
||||||
|
merchantTransactionId: generateOrderId(),
|
||||||
|
amount: order.value.totalAmount.toFixed(2),
|
||||||
|
currency: order.value.currency,
|
||||||
|
paymentType: 'SALE',
|
||||||
|
merchantUserId: '',
|
||||||
|
shopperResultUrl: `${window.location.origin}/result`,
|
||||||
|
shopperCancelUrl: `${window.location.origin}/result`,
|
||||||
|
signType: 'MD5',
|
||||||
|
language: 'zh',
|
||||||
|
threeDSecure: 'N',
|
||||||
|
riskInfo: {
|
||||||
|
customer: {
|
||||||
|
firstName: order.value.customerName.split(' ')[0] || order.value.customerName,
|
||||||
|
lastName: order.value.customerName.split(' ').slice(1).join(' ') || '',
|
||||||
|
email: order.value.customerEmail || '',
|
||||||
|
phone: order.value.customerPhone,
|
||||||
|
registerTime: new Date().toISOString().replace(/[-:]/g, '').split('.')[0],
|
||||||
|
registerIp: '',
|
||||||
|
registerTerminal: 'PC',
|
||||||
|
registerRange: '1',
|
||||||
|
orderTime: new Date(order.value.createTime).toISOString().replace(/[-:]/g, '').split('.')[0],
|
||||||
|
orderIp: '',
|
||||||
|
orderCountry: order.value.shippingCountry || 'US'
|
||||||
|
},
|
||||||
|
goods: [{
|
||||||
|
name: order.value.productName,
|
||||||
|
description: order.value.skuName,
|
||||||
|
sku: order.value.skuName,
|
||||||
|
averageUnitPrice: order.value.unitPrice.toFixed(2),
|
||||||
|
number: order.value.quantity.toString(),
|
||||||
|
virtualProduct: 'N'
|
||||||
|
}],
|
||||||
|
shipping: {
|
||||||
|
firstName: order.value.shippingName.split(' ')[0] || order.value.shippingName,
|
||||||
|
lastName: order.value.shippingName.split(' ').slice(1).join(' ') || '',
|
||||||
|
street: order.value.shippingStreet,
|
||||||
|
city: order.value.shippingCity,
|
||||||
|
state: order.value.shippingState || '',
|
||||||
|
postcode: order.value.shippingPostcode || '',
|
||||||
|
country: order.value.shippingCountry
|
||||||
|
},
|
||||||
|
billing: {
|
||||||
|
firstName: order.value.shippingName.split(' ')[0] || order.value.shippingName,
|
||||||
|
lastName: order.value.shippingName.split(' ').slice(1).join(' ') || '',
|
||||||
|
street: order.value.shippingStreet,
|
||||||
|
city: order.value.shippingCity,
|
||||||
|
state: order.value.shippingState || '',
|
||||||
|
postcode: order.value.shippingPostcode || '',
|
||||||
|
country: order.value.shippingCountry
|
||||||
|
}
|
||||||
|
},
|
||||||
|
notificationUrl: `${window.location.origin}/api/callback/pingpong`
|
||||||
|
}
|
||||||
|
|
||||||
|
const response = await createPaymentOrder(paymentData)
|
||||||
|
|
||||||
|
if (response.code === '0000' && response.data && response.data.token) {
|
||||||
|
ElMessage.success('正在跳转到支付页面...')
|
||||||
|
// 跳转到收银台页面
|
||||||
|
router.push({
|
||||||
|
path: '/checkout',
|
||||||
|
query: { token: response.data.token }
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
ElMessage.error(response.message || '创建支付订单失败')
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('创建支付订单失败:', error)
|
||||||
|
ElMessage.error(error.response?.data?.message || '创建支付订单失败,请稍后重试')
|
||||||
|
} finally {
|
||||||
|
payLoading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
loadOrder()
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.order-confirm {
|
||||||
|
max-width: 1000px;
|
||||||
|
margin: 0 auto;
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.order-info-section {
|
||||||
|
margin-bottom: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.order-info-section h3 {
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #303133;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
padding-bottom: 10px;
|
||||||
|
border-bottom: 2px solid #409eff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.order-amount {
|
||||||
|
font-size: 20px;
|
||||||
|
font-weight: 700;
|
||||||
|
color: #f56c6c;
|
||||||
|
}
|
||||||
|
|
||||||
|
.order-remark {
|
||||||
|
padding: 15px;
|
||||||
|
background: #f5f7fa;
|
||||||
|
border-radius: 6px;
|
||||||
|
color: #606266;
|
||||||
|
line-height: 1.6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.action-buttons {
|
||||||
|
margin-top: 30px;
|
||||||
|
text-align: center;
|
||||||
|
padding-top: 20px;
|
||||||
|
border-top: 1px solid #e4e7ed;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
Reference in New Issue
Block a user