feat(order): 支持东南亚多国地址格式的订单创建功能

- 实现动态地址表单,根据选择国家显示对应字段
- 添加新加坡组屋号、单元号,马来西亚州属,菲律宾Barangay等特殊字段
- 集成泰国双语地址、越南省市坊三级地址格式支持
- 优化订单确认页地址展示,按从小到大格式排列
- 添加邮编格式验证和自动匹配城市功能
- 实现SKU货币自动识别国家并预设字段
- 重构README文档结构和项目说明信息
This commit is contained in:
2025-12-24 11:20:34 +08:00
parent 2dfd0c13a8
commit bd6b7b3b79
4 changed files with 507 additions and 263 deletions

View File

@@ -1,130 +0,0 @@
# 商品详情页说明
## 页面功能
### 1. 商品展示区域
- **左侧大图**:支持点击放大预览
- **缩略图列表**:点击切换主图
- **图片预览**Element Plus图片预览功能
### 2. 商品信息区域
- **商品标题**:大字体,醒目展示
- **商品副标题**:补充说明信息
- **价格展示**
- 现价(大字体,红色高亮)
- 原价(删除线,灰色)
- 节省金额提示
### 3. 规格选择
- **多规格支持**:颜色、尺寸等
- **可视化选择**:标签式选择,选中高亮
- **必选验证**:支付前验证所有规格已选择
### 4. 数量选择
- **数量输入框**:支持增减
- **库存显示**:实时显示库存信息
- **库存限制**:超过库存时禁用
### 5. 服务保障
- **7天无理由退货**
- **正品保证**
- **极速发货**
### 6. 操作按钮
- **立即购买**:橙色渐变按钮
- **立即支付**:红色渐变按钮(主要支付按钮)
- **悬停效果**:按钮有阴影和上浮动画
### 7. 商品详情标签页
- **商品详情**HTML格式的商品描述
- **规格参数**:表格形式展示参数
- **用户评价**:评价列表展示
## 样式特点
### 参考主流电商设计
1. **价格突出**:大字体、红色、渐变背景
2. **图片展示**:大图+缩略图,支持预览
3. **按钮设计**:渐变背景、阴影效果、悬停动画
4. **卡片布局**:圆角、阴影、层次分明
5. **响应式设计**:移动端自适应
### 颜色方案
- **主色调**:红色系(#f56c6c, #ff4757
- **辅助色**:橙色系(#ff9500, #ff6b00
- **背景色**:浅灰色(#f5f7fa
- **文字色**:深灰色(#333, #666
## 支付流程
1. 用户选择商品规格和数量
2. 点击"立即支付"按钮
3. 系统自动生成订单号
4. 调用后端API创建支付订单
5. 跳转到收银台页面完成支付
## 数据格式
### 商品数据结构
```javascript
{
id: '1',
name: '商品名称',
subtitle: '商品副标题',
price: 20.00,
originalPrice: 25.00,
currency: 'USD',
stock: 100,
images: ['图片URL数组'],
specs: [
{
name: '规格名称',
options: [
{ label: '选项标签', value: '选项值' }
]
}
],
description: 'HTML格式的商品描述',
params: [
{ name: '参数名', value: '参数值' }
],
reviews: [
{
user: '用户名',
rating: 5,
date: '日期',
content: '评价内容'
}
]
}
```
## 路由配置
- **路径**`/``/product/:id`
- **组件**`ProductDetail.vue`
- **参数**`id` - 商品ID可选
## 使用示例
### 访问商品详情页
```
http://localhost:3000/
http://localhost:3000/product/1
```
### 从其他页面跳转
```javascript
router.push('/product/1')
router.push({ path: '/product', params: { id: '1' } })
```
## 扩展建议
1. **商品列表页**:创建商品列表,点击跳转到详情页
2. **购物车功能**:添加购物车按钮和功能
3. **商品推荐**:在详情页下方展示相关商品
4. **图片轮播**:主图支持自动轮播
5. **视频展示**:支持商品视频播放
6. **分享功能**:添加商品分享功能

130
README.md
View File

@@ -1,6 +1,6 @@
# MT Pay Frontend
PingPong支付系统前端页面Vue3
电商支付系统前端Vue 3 + Element Plus
## 技术栈
@@ -10,6 +10,23 @@ PingPong支付系统前端页面Vue3
- Axios
- Vite
## 快速开始
### 安装依赖
```bash
npm install
```
### 启动开发服务器
```bash
npm run dev
```
### 构建生产版本
```bash
npm run build
```
## 项目结构
```
@@ -17,99 +34,50 @@ MTKJPAY-FRONT/
├── src/
│ ├── api/ # API接口
│ │ ├── request.js # Axios封装
│ │ ── payment.js # 支付相关API
│ │ ── product.js # 商品API
│ │ └── order.js # 订单API
│ ├── router/ # 路由配置
│ │ └── index.js
│ ├── views/ # 页面组件
│ │ ├── CreateOrder.vue # 创建订单页面
│ │ ├── Checkout.vue # 收银台页面
│ │ ├── PaymentResult.vue # 支付结果页面
│ │ └── OrderQuery.vue # 订单查询页
│ │ ├── ProductDetail.vue # 商品详情页
│ │ ├── CreateOrder.vue # 创建订单页(支持东南亚地址)
│ │ ├── OrderConfirm.vue # 订单确认页
│ │ └── OrderQuery.vue # 订单查询页
│ ├── utils/ # 工具函数
│ │ ├── helpers.js # 辅助函数
│ │ └── countryConfig.js # 国家配置
│ ├── App.vue # 根组件
│ └── main.js # 入口文件
── index.html # HTML模板
├── vite.config.js # Vite配置
└── package.json # 项目配置
── index.html # HTML模板
```
## 安装依赖
## 核心功能
```bash
npm install
```
- 商品详情展示支持多图、SKU选择、货币切换
- 订单创建(动态地址表单,根据国家显示不同字段)
- 订单确认(显示货币转换信息)
- PayPal支付集成
## 开发运行
## 地址表单
```bash
npm run dev
```
支持东南亚国家地址格式:
- 新加坡:组屋号、单元号
- 马来西亚:州属
- 菲律宾Barangay
- 泰国:泰文地址(双语)
- 越南:省/市/郡/区/坊
访问http://localhost:3000
根据SKU货币自动识别国家动态显示对应字段。
## 构建生产版本
```bash
npm run build
```
## 功能说明
### 1. 创建订单页面 (/)
- 填写订单信息(金额、币种、交易类型等)
- 填写风控信息(客户信息、商品信息、地址信息等)
- 提交后跳转到收银台
### 2. 收银台页面 (/checkout)
- 集成PingPong支付SDK
- 用户完成支付操作
- 支付完成后跳转到结果页面
### 3. 支付结果页面 (/result)
- 显示支付结果
- 显示订单详情
- 提供继续支付和查询订单操作
### 4. 订单查询页面 (/query)
- 根据商户订单号查询订单状态
- 显示订单详情
- 支持继续支付(如果订单未完成)
## 配置说明
### API代理配置
`vite.config.js` 中配置了API代理
## API配置
`src/api/request.js` 中配置后端API地址
```javascript
server: {
proxy: {
'/api': {
target: 'http://localhost:8080', // 后端服务地址
changeOrigin: true
}
}
}
const service = axios.create({
baseURL: 'http://localhost:8082/api',
timeout: 10000
})
```
### PingPong SDK模式
`Checkout.vue` 中配置SDK模式
```javascript
mode: 'sandbox' // 根据环境修改sandbox/test/build
```
## 注意事项
1. **后端服务**确保后端服务已启动默认端口8080
2. **CORS配置**如果跨域需要后端配置CORS
3. **SDK模式**:生产环境需要将 `mode` 改为 `build`
4. **重定向URL**创建订单时的重定向URL会自动设置为当前域名
## 开发建议
1. 根据实际需求调整表单字段
2. 根据业务需求添加更多验证规则
3. 优化用户体验和错误提示
4. 添加加载状态和错误处理
## 许可证
MIT

View File

@@ -74,11 +74,12 @@
</el-form-item>
<el-divider>收货地址</el-divider>
<el-alert v-if="currentCountryConfig" :title="`地址格式:${currentCountryConfig.addressFormat}`" type="info" :closable="false" style="margin-bottom: 20px" />
<el-form-item label="收货人姓名" prop="shippingName">
<el-input
v-model="form.shippingName"
placeholder="请输入收货人姓名"
placeholder="请输入收货人姓名(需与证件一致,支持当地语言+英文)"
clearable
/>
</el-form-item>
@@ -86,42 +87,172 @@
<el-form-item label="收货人电话" prop="shippingPhone">
<el-input
v-model="form.shippingPhone"
placeholder="请输入收货人电话"
:placeholder="currentCountryConfig ? `请输入收货人电话(国际区号:${currentCountryConfig.phoneCode}` : '请输入收货人电话(带国际区号)'"
clearable
/>
</el-form-item>
<el-form-item label="收货国家" prop="shippingCountry">
<el-select v-model="form.shippingCountry" placeholder="请选择国家" style="width: 100%">
<el-option label="中国 (CN)" value="CN" />
<el-option label="美国 (US)" value="US" />
<el-option label="新加坡 (SG)" value="SG" />
<el-option label="马来西亚 (MY)" value="MY" />
<el-option label="菲律宾 (PH)" value="PH" />
<el-option label="泰国 (TH)" value="TH" />
<el-option label="越南 (VN)" value="VN" />
<el-option label="新加坡 (SG)" value="SG" />
<el-option label="中国 (CN)" value="CN" />
<el-option label="美国 (US)" value="US" />
<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 label="收货城市" prop="shippingCity">
<!-- 详细地址1门牌号街道楼栋- 所有国家都显示 -->
<el-form-item v-if="showField('shippingAddressLine1')" :label="getFieldLabel('shippingAddressLine1')" prop="shippingAddressLine1">
<el-input
v-model="form.shippingAddressLine1"
type="textarea"
:rows="2"
placeholder="请输入门牌号、街道、楼栋"
clearable
/>
</el-form-item>
<!-- 详细地址2楼层单元号可选 -->
<el-form-item v-if="showField('shippingAddressLine2')" :label="getFieldLabel('shippingAddressLine2')" prop="shippingAddressLine2">
<el-input
v-model="form.shippingAddressLine2"
placeholder="请输入楼层、单元号(可选)"
clearable
/>
</el-form-item>
<!-- 新加坡组屋号和单元号 -->
<template v-if="form.shippingCountry === 'SG'">
<el-form-item :label="getFieldLabel('shippingBlockNumber')" prop="shippingBlockNumber">
<el-input
v-model="form.shippingBlockNumber"
placeholder="例如Blk 123"
clearable
/>
</el-form-item>
<el-form-item :label="getFieldLabel('shippingUnitNumber')" prop="shippingUnitNumber">
<el-input
v-model="form.shippingUnitNumber"
placeholder="例如:#01-234"
clearable
/>
</el-form-item>
</template>
<!-- 菲律宾Barangay -->
<el-form-item v-if="form.shippingCountry === 'PH'" :label="getFieldLabel('shippingBarangay')" prop="shippingBarangay">
<el-input
v-model="form.shippingBarangay"
placeholder="请输入Barangay社区编号"
clearable
/>
</el-form-item>
<!-- 泰国泰文地址 -->
<el-form-item v-if="form.shippingCountry === 'TH'" :label="getFieldLabel('shippingAddressThai')" prop="shippingAddressThai">
<el-input
v-model="form.shippingAddressThai"
type="textarea"
:rows="2"
placeholder="请输入泰文地址(支持双语)"
clearable
/>
</el-form-item>
<!-- 越南// -->
<template v-if="form.shippingCountry === 'VN'">
<el-form-item :label="getFieldLabel('shippingProvince')" prop="shippingProvince">
<el-input
v-model="form.shippingProvince"
placeholder="请输入省 (Tỉnh)"
clearable
/>
</el-form-item>
<el-form-item :label="getFieldLabel('shippingDistrict')" prop="shippingDistrict">
<el-input
v-model="form.shippingDistrict"
placeholder="请输入市/郡 (Thành phố/Huyện)"
clearable
/>
</el-form-item>
<el-form-item :label="getFieldLabel('shippingWard')" prop="shippingWard">
<el-input
v-model="form.shippingWard"
placeholder="请输入区/坊 (Quận/Phường)"
clearable
/>
</el-form-item>
</template>
<!-- 马来西亚州属 -->
<el-form-item v-if="form.shippingCountry === 'MY'" :label="getFieldLabel('shippingStateMalaysia')" prop="shippingStateMalaysia">
<el-input
v-model="form.shippingStateMalaysia"
placeholder="例如Selangor雪兰莪"
clearable
/>
</el-form-item>
<!-- 泰国行政区域/Tambon -->
<el-form-item v-if="form.shippingCountry === 'TH' && showField('shippingAdministrativeArea')" :label="getFieldLabel('shippingAdministrativeArea')" prop="shippingAdministrativeArea">
<el-input
v-model="form.shippingAdministrativeArea"
placeholder="请输入区 (Tambon)"
clearable
/>
</el-form-item>
<!-- 城市和州/通用字段 -->
<el-form-item label="城市/城镇" prop="shippingCity">
<el-input
v-model="form.shippingCity"
placeholder="请输入收货城市"
:placeholder="form.shippingCountry === 'TH' ? '请输入县 (Amphoe)' : '请输入城市/城镇'"
style="width: 48%"
clearable
/>
<el-input
v-if="form.shippingCountry !== 'VN' && form.shippingCountry !== 'MY'"
v-model="form.shippingState"
placeholder="州/省(可选)"
:placeholder="form.shippingCountry === 'TH' ? '府 (Changwat)' : form.shippingCountry === 'PH' ? '省 (Province)' : '州/省(可选)'"
style="width: 48%; margin-left: 4%"
clearable
/>
</el-form-item>
<el-form-item label="街道地址" prop="shippingStreet">
<!-- 邮编 -->
<el-form-item label="邮政编码" prop="shippingPostcode">
<el-input
v-model="form.shippingPostcode"
:placeholder="currentCountryConfig ? `请输入邮编(${currentCountryConfig.postcodeLength}位数字)` : '请输入邮编'"
clearable
style="width: 48%"
>
<template #append v-if="postcodeMatching">
<el-icon class="is-loading"><el-icon-loading /></el-icon>
</template>
</el-input>
<span v-if="currentCountryConfig" style="margin-left: 10px; color: #909399; font-size: 12px">
{{ currentCountryConfig.name }}邮编为{{ currentCountryConfig.postcodeLength }}位数字
</span>
</el-form-item>
<!-- 楼层/单元/代收点补充信息 -->
<el-form-item label="楼层/单元/代收点" prop="shippingFloorUnit">
<el-input
v-model="form.shippingFloorUnit"
placeholder="楼层、单元号或代收点信息(可选)"
clearable
/>
</el-form-item>
<!-- 兼容旧字段街道地址如果新字段为空使用旧字段 -->
<el-form-item v-if="!form.shippingAddressLine1" label="街道地址" prop="shippingStreet">
<el-input
v-model="form.shippingStreet"
type="textarea"
@@ -131,14 +262,6 @@
/>
</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"
@@ -161,17 +284,19 @@
</template>
<script setup>
import { ref, reactive, onMounted } from 'vue'
import { ref, reactive, computed, watch, onMounted } from 'vue'
import { useRouter, useRoute } from 'vue-router'
import { ElMessage } from 'element-plus'
import { createCustomerOrder } from '../api/order'
import { formatAmount } from '../utils/helpers'
import { getCountryConfig, getCountryByCurrency, validatePostcode, getRequiredFields } from '../utils/countryConfig'
const router = useRouter()
const route = useRoute()
const formRef = ref()
const loading = ref(false)
const productInfo = ref(null)
const postcodeMatching = ref(false) // 邮编匹配中
const form = reactive({
customerName: '',
@@ -184,38 +309,225 @@ const form = reactive({
shippingCity: '',
shippingStreet: '',
shippingPostcode: '',
// 东南亚地址扩展字段
shippingAddressLine1: '',
shippingAddressLine2: '',
shippingAdministrativeArea: '',
shippingBlockNumber: '',
shippingUnitNumber: '',
shippingBarangay: '',
shippingAddressThai: '',
shippingProvince: '',
shippingDistrict: '',
shippingWard: '',
shippingStateMalaysia: '',
shippingFloorUnit: '',
remark: ''
})
const rules = {
customerName: [
{ required: true, message: '请输入客户姓名', trigger: 'blur' }
],
customerPhone: [
{ required: true, message: '请输入客户电话', trigger: 'blur' },
{ pattern: /^[0-9+\-\s()]+$/, message: '请输入有效的电话号码', trigger: 'blur' }
],
customerEmail: [
{ type: 'email', message: '请输入有效的邮箱地址', trigger: 'blur' }
],
shippingName: [
{ required: true, message: '请输入收货人姓名', trigger: 'blur' }
],
shippingPhone: [
{ required: true, message: '请输入收货人电话', trigger: 'blur' },
{ pattern: /^[0-9+\-\s()]+$/, message: '请输入有效的电话号码', trigger: 'blur' }
],
shippingCountry: [
{ required: true, message: '请选择收货国家', trigger: 'change' }
],
shippingCity: [
{ required: true, message: '请输入收货城市', trigger: 'blur' }
],
shippingStreet: [
{ required: true, message: '请输入街道地址', trigger: 'blur' }
]
// 当前国家配置
const currentCountryConfig = computed(() => {
return getCountryConfig(form.shippingCountry)
})
// 是否显示特定字段
const showField = (fieldName) => {
if (!currentCountryConfig.value) {
// 默认显示基础字段
return ['shippingName', 'shippingPhone', 'shippingCountry', 'shippingCity',
'shippingState', 'shippingStreet', 'shippingPostcode'].includes(fieldName)
}
const specialFields = currentCountryConfig.value.specialFields || []
const requiredFields = currentCountryConfig.value.requiredFields || []
// 如果是特殊字段,只在对应国家显示
if (specialFields.includes(fieldName)) {
return true
}
// 如果是必填字段,显示
if (requiredFields.includes(fieldName)) {
return true
}
// 基础字段始终显示
const baseFields = ['shippingName', 'shippingPhone', 'shippingCountry',
'shippingCity', 'shippingState', 'shippingStreet',
'shippingPostcode', 'shippingAddressLine1', 'shippingAddressLine2']
if (baseFields.includes(fieldName)) {
return true
}
// 越南特殊字段
if (form.shippingCountry === 'VN') {
return ['shippingProvince', 'shippingDistrict', 'shippingWard'].includes(fieldName)
}
return false
}
// 获取字段标签
const getFieldLabel = (fieldName) => {
if (currentCountryConfig.value && currentCountryConfig.value.fieldLabels) {
return currentCountryConfig.value.fieldLabels[fieldName] || fieldName
}
return fieldName
}
// 监听国家变化,清空相关字段
watch(() => form.shippingCountry, (newCountry, oldCountry) => {
if (newCountry !== oldCountry) {
// 清空国家特定字段
form.shippingStateMalaysia = ''
form.shippingBarangay = ''
form.shippingBlockNumber = ''
form.shippingUnitNumber = ''
form.shippingAddressThai = ''
form.shippingProvince = ''
form.shippingDistrict = ''
form.shippingWard = ''
form.shippingAdministrativeArea = ''
// 更新电话区号提示
if (currentCountryConfig.value) {
form.shippingPhone = currentCountryConfig.value.phoneCode + ' '
}
}
})
// 监听邮编变化,自动匹配城市
watch(() => form.shippingPostcode, async (newPostcode) => {
if (!newPostcode || !form.shippingCountry) return
// 验证邮编格式
if (!validatePostcode(form.shippingCountry, newPostcode)) {
return
}
// TODO: 调用后端API匹配城市/区域
// 这里先预留接口,后续实现
postcodeMatching.value = true
try {
// const result = await matchPostcode(form.shippingCountry, newPostcode)
// if (result && result.city) {
// form.shippingCity = result.city
// if (result.state) {
// form.shippingState = result.state
// }
// }
} catch (error) {
console.error('邮编匹配失败:', error)
} finally {
postcodeMatching.value = false
}
})
// 动态验证规则
const getRules = () => {
const baseRules = {
customerName: [
{ required: true, message: '请输入客户姓名', trigger: 'blur' }
],
customerPhone: [
{ required: true, message: '请输入客户电话', trigger: 'blur' },
{ pattern: /^[0-9+\-\s()]+$/, message: '请输入有效的电话号码', trigger: 'blur' }
],
customerEmail: [
{ type: 'email', message: '请输入有效的邮箱地址', trigger: 'blur' }
],
shippingName: [
{ required: true, message: '请输入收货人姓名', trigger: 'blur' }
],
shippingPhone: [
{ required: true, message: '请输入收货人电话', trigger: 'blur' },
{ pattern: /^[0-9+\-\s()]+$/, message: '请输入有效的电话号码', trigger: 'blur' }
],
shippingCountry: [
{ required: true, message: '请选择收货国家', trigger: 'change' }
],
shippingCity: [
{ required: true, message: '请输入收货城市', trigger: 'blur' }
],
shippingStreet: [
{ required: true, message: '请输入街道地址', trigger: 'blur' }
]
}
// 根据国家配置添加必填字段验证
if (currentCountryConfig.value) {
const requiredFields = getRequiredFields(form.shippingCountry)
if (requiredFields.includes('shippingAddressLine1')) {
baseRules.shippingAddressLine1 = [
{ required: true, message: '请输入详细地址1', trigger: 'blur' }
]
}
if (requiredFields.includes('shippingPostcode')) {
baseRules.shippingPostcode = [
{ required: true, message: '请输入邮编', trigger: 'blur' },
{
validator: (rule, value, callback) => {
if (value && !validatePostcode(form.shippingCountry, value)) {
callback(new Error(`邮编格式不正确,应为${currentCountryConfig.value.postcodeLength}位数字`))
} else {
callback()
}
},
trigger: 'blur'
}
]
}
if (requiredFields.includes('shippingBlockNumber')) {
baseRules.shippingBlockNumber = [
{ required: true, message: '请输入组屋号', trigger: 'blur' }
]
}
if (requiredFields.includes('shippingUnitNumber')) {
baseRules.shippingUnitNumber = [
{ required: true, message: '请输入单元号', trigger: 'blur' }
]
}
if (requiredFields.includes('shippingBarangay')) {
baseRules.shippingBarangay = [
{ required: true, message: '请输入Barangay社区编号', trigger: 'blur' }
]
}
if (requiredFields.includes('shippingStateMalaysia')) {
baseRules.shippingStateMalaysia = [
{ required: true, message: '请输入州属', trigger: 'blur' }
]
}
if (requiredFields.includes('shippingProvince')) {
baseRules.shippingProvince = [
{ required: true, message: '请输入省', trigger: 'blur' }
]
}
if (requiredFields.includes('shippingDistrict')) {
baseRules.shippingDistrict = [
{ required: true, message: '请输入市/郡', trigger: 'blur' }
]
}
if (requiredFields.includes('shippingWard')) {
baseRules.shippingWard = [
{ required: true, message: '请输入区/坊', trigger: 'blur' }
]
}
}
return baseRules
}
const rules = computed(() => getRules())
// 返回上一页
const goBack = () => {
router.back()
@@ -252,8 +564,21 @@ const submitForm = async () => {
shippingCountry: form.shippingCountry,
shippingState: form.shippingState || null,
shippingCity: form.shippingCity,
shippingStreet: form.shippingStreet,
shippingStreet: form.shippingStreet || form.shippingAddressLine1, // 兼容旧字段
shippingPostcode: form.shippingPostcode || null,
// 东南亚地址扩展字段
shippingAddressLine1: form.shippingAddressLine1 || null,
shippingAddressLine2: form.shippingAddressLine2 || null,
shippingAdministrativeArea: form.shippingAdministrativeArea || null,
shippingBlockNumber: form.shippingBlockNumber || null,
shippingUnitNumber: form.shippingUnitNumber || null,
shippingBarangay: form.shippingBarangay || null,
shippingAddressThai: form.shippingAddressThai || null,
shippingProvince: form.shippingProvince || null,
shippingDistrict: form.shippingDistrict || null,
shippingWard: form.shippingWard || null,
shippingStateMalaysia: form.shippingStateMalaysia || null,
shippingFloorUnit: form.shippingFloorUnit || null,
remark: form.remark || null
}
@@ -290,6 +615,17 @@ onMounted(() => {
const data = JSON.parse(decodeURIComponent(route.query.data))
if (data.product) {
productInfo.value = data.product
// 根据SKU的货币代码自动设置国家
if (data.product.currency) {
const countryCode = getCountryByCurrency(data.product.currency)
if (countryCode) {
form.shippingCountry = countryCode
const config = getCountryConfig(countryCode)
if (config) {
form.shippingPhone = config.phoneCode + ' '
}
}
}
}
} catch (error) {
console.error('解析商品信息失败:', error)

View File

@@ -87,7 +87,50 @@
<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) }}
<div class="shipping-address-detail">
<div v-if="order.shippingAddressLine1" class="address-line">
<strong>详细地址1</strong>{{ order.shippingAddressLine1 }}
</div>
<div v-if="order.shippingAddressLine2" class="address-line">
<strong>详细地址2</strong>{{ order.shippingAddressLine2 }}
</div>
<!-- 从JSON字段中读取特殊字段 -->
<template v-if="order.shippingSpecialFields">
<div v-if="order.shippingSpecialFields.blockNumber" class="address-line">
<strong>组屋号</strong>{{ order.shippingSpecialFields.blockNumber }}
</div>
<div v-if="order.shippingSpecialFields.unitNumber" class="address-line">
<strong>单元号</strong>{{ order.shippingSpecialFields.unitNumber }}
</div>
<div v-if="order.shippingSpecialFields.barangay" class="address-line">
<strong>Barangay</strong>{{ order.shippingSpecialFields.barangay }}
</div>
<div v-if="order.shippingSpecialFields.addressThai" class="address-line">
<strong>泰文地址</strong>{{ order.shippingSpecialFields.addressThai }}
</div>
<div v-if="order.shippingSpecialFields.province" class="address-line">
<strong></strong>{{ order.shippingSpecialFields.province }}
</div>
<div v-if="order.shippingSpecialFields.district" class="address-line">
<strong>/</strong>{{ order.shippingSpecialFields.district }}
</div>
<div v-if="order.shippingSpecialFields.ward" class="address-line">
<strong>/</strong>{{ order.shippingSpecialFields.ward }}
</div>
<div v-if="order.shippingSpecialFields.stateMalaysia" class="address-line">
<strong>州属</strong>{{ order.shippingSpecialFields.stateMalaysia }}
</div>
<div v-if="order.shippingSpecialFields.administrativeArea" class="address-line">
<strong>行政区域</strong>{{ order.shippingSpecialFields.administrativeArea }}
</div>
<div v-if="order.shippingSpecialFields.floorUnit" class="address-line">
<strong>楼层/单元/代收点</strong>{{ order.shippingSpecialFields.floorUnit }}
</div>
</template>
<div class="address-line">
{{ formatShippingAddress(order) }}
</div>
</div>
</el-descriptions-item>
</el-descriptions>
</div>
@@ -199,15 +242,29 @@ const formatDateTime = (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)
// 详细地址1或街道地址
if (order.shippingAddressLine1) {
parts.push(order.shippingAddressLine1)
} else if (order.shippingStreet) {
parts.push(order.shippingStreet)
}
if (order.shippingCity) parts.push(order.shippingCity)
if (order.shippingStreet) parts.push(order.shippingStreet)
// 根据国家显示不同的州/省字段从JSON字段中读取
if (order.shippingSpecialFields && order.shippingSpecialFields.stateMalaysia) {
parts.push(order.shippingSpecialFields.stateMalaysia)
} else if (order.shippingState) {
parts.push(order.shippingState)
}
if (order.shippingCountry) parts.push(order.shippingCountry)
if (order.shippingPostcode) parts.push(`邮编:${order.shippingPostcode}`)
return parts.join(' ')
return parts.length > 0 ? parts.join('') : '地址信息不完整'
}
// 返回上一页
@@ -523,6 +580,19 @@ onMounted(() => {
.rate-locked-info {
margin-top: 10px;
font-size: 13px;
}
.shipping-address-detail {
line-height: 1.8;
}
.address-line {
margin-bottom: 4px;
}
.address-line strong {
color: #606266;
margin-right: 8px;
color: #909399;
display: flex;
align-items: center;