wewe
This commit is contained in:
parent
5de7384ddb
commit
99d0d21d7f
@ -56,7 +56,7 @@
|
|||||||
<view class="shop-list" v-if="searchType == 1">
|
<view class="shop-list" v-if="searchType == 1">
|
||||||
<view class="shop-item" v-for="(item, index) in shopList" :key="item.id" @click="goShopDetail(item)">
|
<view class="shop-item" v-for="(item, index) in shopList" :key="item.id" @click="goShopDetail(item)">
|
||||||
<view class="shop-image">
|
<view class="shop-image">
|
||||||
<image :src="item.main_image_url"></image>
|
<image :src="item.main_image_url" mode="aspectFit"></image>
|
||||||
<view class="shop-tag flex">
|
<view class="shop-tag flex">
|
||||||
<view class="tag danger" v-if="item.is_hot_selling == 1">
|
<view class="tag danger" v-if="item.is_hot_selling == 1">
|
||||||
<text class="iconfont icon-tubiaoshangshengqushi"></text>热销
|
<text class="iconfont icon-tubiaoshangshengqushi"></text>热销
|
||||||
@ -79,7 +79,7 @@
|
|||||||
{{ item.name }}
|
{{ item.name }}
|
||||||
</view>
|
</view>
|
||||||
<view class="sku-description">
|
<view class="sku-description">
|
||||||
{{ item.description.details }}
|
{{ item.description.sub_title }}
|
||||||
</view>
|
</view>
|
||||||
<view class="sku-rating">
|
<view class="sku-rating">
|
||||||
<text class="iconfont icon-xingxing"></text>
|
<text class="iconfont icon-xingxing"></text>
|
||||||
@ -251,6 +251,7 @@ export default {
|
|||||||
item.description = parseDescription(item.description)
|
item.description = parseDescription(item.description)
|
||||||
return item
|
return item
|
||||||
})
|
})
|
||||||
|
console.log(this.shopList )
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
async userIsLogin() {
|
async userIsLogin() {
|
||||||
|
|||||||
@ -29,12 +29,8 @@
|
|||||||
<text class="picker-close" @click="hideAddressPicker">×</text>
|
<text class="picker-close" @click="hideAddressPicker">×</text>
|
||||||
</view>
|
</view>
|
||||||
<scroll-view scroll-y class="picker-content">
|
<scroll-view scroll-y class="picker-content">
|
||||||
<view
|
<view class="picker-item" v-for="(item, index) in addressList" :key="item.id || index"
|
||||||
class="picker-item"
|
@click="chooseAddress(item)">
|
||||||
v-for="(item, index) in addressList"
|
|
||||||
:key="item.id || index"
|
|
||||||
@click="chooseAddress(item)"
|
|
||||||
>
|
|
||||||
<view class="picker-item-header">
|
<view class="picker-item-header">
|
||||||
<text class="picker-receiver">{{ item.receiver_name }}</text>
|
<text class="picker-receiver">{{ item.receiver_name }}</text>
|
||||||
<text class="picker-phone">{{ item.phone }}</text>
|
<text class="picker-phone">{{ item.phone }}</text>
|
||||||
@ -63,7 +59,7 @@
|
|||||||
<view class="goods-list">
|
<view class="goods-list">
|
||||||
<view class="goods-item" v-for="(item, index) in orderItems" :key="index">
|
<view class="goods-item" v-for="(item, index) in orderItems" :key="index">
|
||||||
<view class="goods-image">
|
<view class="goods-image">
|
||||||
<image :src="item.product_image || item.main_image_url" mode="aspectFill"></image>
|
<image :src="item.product_image_url" mode="aspectFill"></image>
|
||||||
</view>
|
</view>
|
||||||
<view class="goods-info">
|
<view class="goods-info">
|
||||||
<view class="goods-name">{{ item.product_name || item.name }}</view>
|
<view class="goods-name">{{ item.product_name || item.name }}</view>
|
||||||
@ -83,13 +79,8 @@
|
|||||||
<!-- 订单备注 -->
|
<!-- 订单备注 -->
|
||||||
<view class="remark-section">
|
<view class="remark-section">
|
||||||
<view class="section-title">订单备注</view>
|
<view class="section-title">订单备注</view>
|
||||||
<textarea
|
<textarea class="remark-input" v-model="orderRemark" placeholder="选填,对本次购买的说明(如:请尽快发货)" maxlength="200"
|
||||||
class="remark-input"
|
auto-height></textarea>
|
||||||
v-model="orderRemark"
|
|
||||||
placeholder="选填,对本次购买的说明(如:请尽快发货)"
|
|
||||||
maxlength="200"
|
|
||||||
auto-height
|
|
||||||
></textarea>
|
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- 价格明细 -->
|
<!-- 价格明细 -->
|
||||||
@ -130,7 +121,7 @@ import request from '@/api/request.js';
|
|||||||
import { fetchAddressList } from '@/api/address.js';
|
import { fetchAddressList } from '@/api/address.js';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
orderItems: [], // 订单商品列表
|
orderItems: [], // 订单商品列表
|
||||||
selectedAddress: null, // 选中的收货地址
|
selectedAddress: null, // 选中的收货地址
|
||||||
@ -293,7 +284,7 @@ export default {
|
|||||||
|
|
||||||
// 调用创建订单接口
|
// 调用创建订单接口
|
||||||
const result = await request('xcx/order', 'POST', orderData);
|
const result = await request('xcx/order', 'POST', orderData);
|
||||||
|
|
||||||
uni.showToast({
|
uni.showToast({
|
||||||
title: '订单提交成功',
|
title: '订单提交成功',
|
||||||
icon: 'success',
|
icon: 'success',
|
||||||
@ -301,24 +292,35 @@ export default {
|
|||||||
});
|
});
|
||||||
// 发起微信支付
|
// 发起微信支付
|
||||||
wx.requestPayment({
|
wx.requestPayment({
|
||||||
timeStamp: result.timeStamp,
|
timeStamp: result.time_stamp,
|
||||||
nonceStr: result.nonceStr,
|
nonceStr: result.nonce_str,
|
||||||
package: result.package,
|
package: result.package,
|
||||||
signType: result.signType,
|
signType: result.sign_type,
|
||||||
paySign: result.paySign,
|
paySign: result.pay_sign,
|
||||||
success: () => {
|
success: () => {
|
||||||
console.log('支付成功');
|
// console.log('支付成功');
|
||||||
|
// // 跳转到订单详情或订单列表页面
|
||||||
|
// setTimeout(() => {
|
||||||
|
// // 根据实际路由调整
|
||||||
|
// uni.redirectTo({
|
||||||
|
// url: `/pages/order/detail?id=${result.order_id || result.id}`
|
||||||
|
// });
|
||||||
|
// }, 2000);
|
||||||
|
},
|
||||||
|
complete: () => {
|
||||||
|
console.log('支付xxx');
|
||||||
// 跳转到订单详情或订单列表页面
|
// 跳转到订单详情或订单列表页面
|
||||||
setTimeout(() => {
|
// setTimeout(() => {
|
||||||
// 根据实际路由调整
|
// 根据实际路由调整
|
||||||
uni.redirectTo({
|
uni.redirectTo({
|
||||||
url: `/pages/order/detail?id=${result.order_id || result.id}`
|
url: `/pages/order/detail?id=${result.order_id || result.id}`
|
||||||
});
|
});
|
||||||
}, 2000);
|
// }, 2000);
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('提交订单失败:', error);
|
console.error('提交订单失败:', error);
|
||||||
@ -655,6 +657,7 @@ export default {
|
|||||||
from {
|
from {
|
||||||
transform: translateY(100%);
|
transform: translateY(100%);
|
||||||
}
|
}
|
||||||
|
|
||||||
to {
|
to {
|
||||||
transform: translateY(0);
|
transform: translateY(0);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,16 +1,375 @@
|
|||||||
<template>
|
<template>
|
||||||
<view class="order-detail-page">
|
<view class="order-detail-page">
|
||||||
<view class="detail-content">
|
<view class="detail-content" v-if="orderInfo">
|
||||||
<text class="placeholder-text">订单详情页面开发中...</text>
|
<view class="card">
|
||||||
|
<view class="section-title">
|
||||||
|
<text>订单概览</text>
|
||||||
|
<text class="status-tag" :class="statusClass">{{ statusText }}</text>
|
||||||
|
</view>
|
||||||
|
<view class="row">
|
||||||
|
<text class="label">订单编号</text>
|
||||||
|
<text class="value">{{ orderInfo.orderNo }}</text>
|
||||||
|
</view>
|
||||||
|
<view class="row">
|
||||||
|
<text class="label">创建时间</text>
|
||||||
|
<text class="value">{{ orderInfo.createdAt }}</text>
|
||||||
|
</view>
|
||||||
|
<view class="row amount-row">
|
||||||
|
<text class="label">应付金额</text>
|
||||||
|
<text class="amount">¥{{ orderInfo.payableAmount }}</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="card warning-card" v-if="showPaymentWarning">
|
||||||
|
<view class="warning-title">
|
||||||
|
<text>付款提醒</text>
|
||||||
|
<text class="countdown" v-if="countdownText">{{ countdownText }}</text>
|
||||||
|
</view>
|
||||||
|
<text class="warning-text">
|
||||||
|
订单生成后需在15分钟内完成支付,逾期将自动取消。
|
||||||
|
</text>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="card">
|
||||||
|
<view class="section-title">
|
||||||
|
<text>商品信息</text>
|
||||||
|
<text class="sub-info">共{{ totalQuantity }}件</text>
|
||||||
|
</view>
|
||||||
|
<view v-if="orderInfo.products.length" class="product-list">
|
||||||
|
<view class="product-item" v-for="item in orderInfo.products" :key="item.id">
|
||||||
|
<image class="product-img" :src="item.image" mode="aspectFill" />
|
||||||
|
<view class="product-info">
|
||||||
|
<view class="product-name-row">
|
||||||
|
<text class="product-name">{{ item.name }}</text>
|
||||||
|
<text class="product-tag" v-if="item.isHot">热销</text>
|
||||||
|
<text class="product-tag limited" v-if="item.isLimited">限量</text>
|
||||||
|
</view>
|
||||||
|
<text class="product-sku" v-if="item.skuName">{{ item.skuName }}</text>
|
||||||
|
<text class="product-desc" v-if="item.desc">{{ item.desc }}</text>
|
||||||
|
<view class="product-meta">
|
||||||
|
<text class="product-price">¥{{ item.price }}</text>
|
||||||
|
<text class="product-qty">x{{ item.quantity }}</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<view class="empty-tip" v-else>暂无商品信息</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="card">
|
||||||
|
<view class="section-title">
|
||||||
|
<text>配送信息</text>
|
||||||
|
<text class="sub-info">{{ shippingStatusText }}</text>
|
||||||
|
</view>
|
||||||
|
<view class="row">
|
||||||
|
<text class="label">收件人</text>
|
||||||
|
<text class="value">{{ orderInfo.delivery.receiverName }}</text>
|
||||||
|
</view>
|
||||||
|
<view class="row">
|
||||||
|
<text class="label">联系电话</text>
|
||||||
|
<text class="value">{{ orderInfo.delivery.phone }}</text>
|
||||||
|
</view>
|
||||||
|
<view class="row">
|
||||||
|
<text class="label">收货地址</text>
|
||||||
|
<text class="value address">{{ orderInfo.delivery.fullAddress }}</text>
|
||||||
|
</view>
|
||||||
|
<view class="row">
|
||||||
|
<text class="label">物流公司</text>
|
||||||
|
<text class="value">{{ orderInfo.delivery.logisticsCompany }}</text>
|
||||||
|
</view>
|
||||||
|
<view class="row">
|
||||||
|
<text class="label">运单号</text>
|
||||||
|
<text class="value">{{ orderInfo.delivery.trackingNumber }}</text>
|
||||||
|
</view>
|
||||||
|
<view class="row">
|
||||||
|
<text class="label">预计送达</text>
|
||||||
|
<text class="value">{{ orderInfo.delivery.estimatedDelivery }}</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="card">
|
||||||
|
<view class="section-title">
|
||||||
|
<text>金额明细</text>
|
||||||
|
</view>
|
||||||
|
<view class="row">
|
||||||
|
<text class="label">商品总额</text>
|
||||||
|
<text class="value">¥{{ orderInfo.originalAmount }}</text>
|
||||||
|
</view>
|
||||||
|
<view class="row">
|
||||||
|
<text class="label">优惠券抵扣</text>
|
||||||
|
<text class="value">-¥{{ orderInfo.couponAmount }}</text>
|
||||||
|
</view>
|
||||||
|
<view class="row" v-if="orderInfo.pointsUsed">
|
||||||
|
<text class="label">积分抵扣({{ orderInfo.pointsUsed }}积分)</text>
|
||||||
|
<text class="value">-¥{{ orderInfo.pointsAmount }}</text>
|
||||||
|
</view>
|
||||||
|
<view class="row total-row">
|
||||||
|
<text class="label">实付金额</text>
|
||||||
|
<text class="amount">¥{{ orderInfo.payableAmount }}</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="action-bar" v-if="showPaymentWarning">
|
||||||
|
<button class="pay-btn" :disabled="isPaying" @tap="handleRepay">
|
||||||
|
{{ isPaying ? '正在唤起支付...' : '重新发起支付' }}
|
||||||
|
</button>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="empty-block" v-else>
|
||||||
|
<text class="placeholder-text">正在加载订单详情...</text>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
import request from '@/api/request.js';
|
||||||
|
|
||||||
|
const STATUS_META = {
|
||||||
|
1: { text: '待支付', class: 'status-pending' },
|
||||||
|
2: { text: '支付失败', class: 'status-failed' },
|
||||||
|
3: { text: '待发货', class: 'status-delivering' },
|
||||||
|
4: { text: '待收货', class: 'status-receiving' },
|
||||||
|
5: { text: '已完成', class: 'status-success' },
|
||||||
|
6: { text: '已取消', class: 'status-cancel' },
|
||||||
|
7: { text: '已退款', class: 'status-refund' },
|
||||||
|
default: { text: '未知状态', class: 'status-default' }
|
||||||
|
};
|
||||||
|
|
||||||
|
const SHIPPING_STATUS_META = {
|
||||||
|
0: '待发货',
|
||||||
|
1: '已发货',
|
||||||
|
2: '运输中',
|
||||||
|
3: '派送中',
|
||||||
|
4: '已签收'
|
||||||
|
};
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
orderId: '',
|
||||||
|
orderInfo: null,
|
||||||
|
payCountdown: 0,
|
||||||
|
countdownTimer: null,
|
||||||
|
isPaying: false
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
countdownText() {
|
||||||
|
if (this.payCountdown <= 0) return '';
|
||||||
|
const minutes = Math.floor(this.payCountdown / 60);
|
||||||
|
const seconds = this.payCountdown % 60;
|
||||||
|
return `${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}`;
|
||||||
|
},
|
||||||
|
statusClass() {
|
||||||
|
return this.orderStatusMeta.class;
|
||||||
|
},
|
||||||
|
statusText() {
|
||||||
|
return this.orderStatusMeta.text;
|
||||||
|
},
|
||||||
|
orderStatusMeta() {
|
||||||
|
if (!this.orderInfo) return STATUS_META.default;
|
||||||
|
return STATUS_META[this.orderInfo.status] || STATUS_META.default;
|
||||||
|
},
|
||||||
|
showPaymentWarning() {
|
||||||
|
return !!this.orderInfo && this.orderInfo.status === 1;
|
||||||
|
},
|
||||||
|
totalQuantity() {
|
||||||
|
if (!this.orderInfo) return 0;
|
||||||
|
return this.orderInfo.products.reduce((acc, item) => acc + Number(item.quantity || 0), 0);
|
||||||
|
},
|
||||||
|
shippingStatusText() {
|
||||||
|
if (!this.orderInfo) return '--';
|
||||||
|
const code = this.orderInfo.delivery.shippingStatus;
|
||||||
|
return SHIPPING_STATUS_META[code] || '物流信息更新中';
|
||||||
|
}
|
||||||
|
},
|
||||||
onLoad(options) {
|
onLoad(options) {
|
||||||
console.log('订单ID:', options.id);
|
this.orderId = options.id || '';
|
||||||
// TODO: 加载订单详情数据
|
this.loadOrderDetail(this.orderId);
|
||||||
|
},
|
||||||
|
onUnload() {
|
||||||
|
this.clearCountdown();
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
async loadOrderDetail(orderId) {
|
||||||
|
try {
|
||||||
|
const res = await request(`xcx/order/${orderId}`, 'get');
|
||||||
|
const parsed = this.formatOrderInfo(res || {});
|
||||||
|
this.orderInfo = parsed;
|
||||||
|
this.initCountdown(parsed.createdAt, parsed.status);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取订单详情失败', error);
|
||||||
|
const mockData = this.formatOrderInfo({
|
||||||
|
order_id: orderId || 'PO202411230001',
|
||||||
|
status: 1,
|
||||||
|
created_at: '2024-11-23 10:00',
|
||||||
|
total_amount: 688,
|
||||||
|
coupon_amount: 50,
|
||||||
|
payable_amount: 638,
|
||||||
|
points_amount: 0,
|
||||||
|
points_used: 0,
|
||||||
|
product_list: [
|
||||||
|
{
|
||||||
|
product_id: 'P001',
|
||||||
|
product_name: '至臻香水 50ml',
|
||||||
|
sku_name: '清新花香调',
|
||||||
|
price: 488,
|
||||||
|
quantity: 1,
|
||||||
|
product_main_image_url: 'https://dummyimage.com/120x120',
|
||||||
|
product_description: '灵感源于花园的香氛体验'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
delivery_info: {
|
||||||
|
receiver_name: '张三',
|
||||||
|
phone: '138****6666',
|
||||||
|
province: '上海市',
|
||||||
|
city: '上海市',
|
||||||
|
district: '浦东新区',
|
||||||
|
detail_address: '世纪大道100号',
|
||||||
|
logistics_company: '顺丰速运',
|
||||||
|
tracking_number: 'SF123456789',
|
||||||
|
estimated_delivery: '2024-11-24 18:00',
|
||||||
|
shipping_status: 0
|
||||||
|
}
|
||||||
|
});
|
||||||
|
this.orderInfo = mockData;
|
||||||
|
this.initCountdown(mockData.createdAt, mockData.status);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async handleRepay() {
|
||||||
|
if (!this.orderInfo || this.isPaying) return;
|
||||||
|
this.isPaying = true;
|
||||||
|
try {
|
||||||
|
const payload = {
|
||||||
|
order_id: this.orderInfo.orderNo,
|
||||||
|
pay_amount: this.orderInfo.payableAmount
|
||||||
|
};
|
||||||
|
// TODO: 根据实际支付接口调整请求方式/参数
|
||||||
|
const result = await request('xcx/order/repay/' + this.orderId, 'post');
|
||||||
|
// 发起微信支付
|
||||||
|
wx.requestPayment({
|
||||||
|
timeStamp: result.time_stamp,
|
||||||
|
nonceStr: result.nonce_str,
|
||||||
|
package: result.package,
|
||||||
|
signType: result.sign_type,
|
||||||
|
paySign: result.pay_sign,
|
||||||
|
success: () => {
|
||||||
|
// console.log('支付成功');
|
||||||
|
// // 跳转到订单详情或订单列表页面
|
||||||
|
// setTimeout(() => {
|
||||||
|
// // 根据实际路由调整
|
||||||
|
// uni.redirectTo({
|
||||||
|
// url: `/pages/order/detail?id=${result.order_id || result.id}`
|
||||||
|
// });
|
||||||
|
// }, 2000);
|
||||||
|
},
|
||||||
|
complete: () => {
|
||||||
|
console.log('支付xxx');
|
||||||
|
this.loadOrderDetail(this.orderId);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.error('重新发起支付失败', error);
|
||||||
|
uni.showToast({ title: error.message || '发起支付失败', icon: 'none' });
|
||||||
|
} finally {
|
||||||
|
this.isPaying = false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
initCountdown(createdAt, statusCode = 1) {
|
||||||
|
if (statusCode !== 1) {
|
||||||
|
this.clearCountdown();
|
||||||
|
this.payCountdown = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const createdTime = new Date((createdAt || '').replace(/-/g, '/')).getTime();
|
||||||
|
if (Number.isNaN(createdTime)) {
|
||||||
|
this.payCountdown = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const deadline = createdTime + 15 * 60 * 1000;
|
||||||
|
const diff = Math.max(0, Math.floor((deadline - Date.now()) / 1000));
|
||||||
|
this.payCountdown = diff;
|
||||||
|
this.clearCountdown();
|
||||||
|
if (diff > 0) {
|
||||||
|
this.countdownTimer = setInterval(() => {
|
||||||
|
if (this.payCountdown <= 1) {
|
||||||
|
this.payCountdown = 0;
|
||||||
|
this.clearCountdown();
|
||||||
|
this.orderInfo.status = 6;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.payCountdown -= 1;
|
||||||
|
}, 1000);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
formatOrderInfo(raw = {}) {
|
||||||
|
const statusCode = this.normalizeStatus(raw.status);
|
||||||
|
const products = this.formatProducts(raw.product_list);
|
||||||
|
const delivery = this.formatDeliveryInfo(raw.delivery_info);
|
||||||
|
return {
|
||||||
|
orderNo: raw.order_id || raw.orderNo || '--',
|
||||||
|
status: statusCode,
|
||||||
|
createdAt: raw.created_at || raw.createdAt || raw.createTime || '--',
|
||||||
|
originalAmount: this.formatAmount(raw.total_amount ?? raw.amount),
|
||||||
|
couponAmount: this.formatAmount(raw.coupon_amount),
|
||||||
|
payableAmount: this.formatAmount(raw.payable_amount ?? raw.payableAmount ?? raw.amount),
|
||||||
|
pointsAmount: this.formatAmount(raw.points_amount),
|
||||||
|
pointsUsed: Number(raw.points_used) || 0,
|
||||||
|
products,
|
||||||
|
delivery
|
||||||
|
};
|
||||||
|
},
|
||||||
|
formatProducts(list = []) {
|
||||||
|
if (!Array.isArray(list)) return [];
|
||||||
|
return list.map((item) => ({
|
||||||
|
id: item.product_id || item.sku_id || item.product_name,
|
||||||
|
name: item.product_name || '--',
|
||||||
|
skuName: item.sku_name || '',
|
||||||
|
desc: item.product_description || '',
|
||||||
|
price: this.formatAmount(item.price ?? item.original_price),
|
||||||
|
quantity: item.quantity || 0,
|
||||||
|
image: item.product_main_image_url || item.category_image_url || '',
|
||||||
|
isHot: Number(item.is_hot_selling) === 1,
|
||||||
|
isLimited: Number(item.is_limited) === 1
|
||||||
|
}));
|
||||||
|
},
|
||||||
|
formatDeliveryInfo(info = {}) {
|
||||||
|
const province = info.province || '';
|
||||||
|
const city = info.city || '';
|
||||||
|
const district = info.district || '';
|
||||||
|
const detail = info.detail_address || '';
|
||||||
|
const fullAddress = [province, city, district, detail].filter(Boolean).join('');
|
||||||
|
const status = Number(info.shipping_status);
|
||||||
|
return {
|
||||||
|
receiverName: info.receiver_name || '--',
|
||||||
|
phone: info.phone || '--',
|
||||||
|
fullAddress: fullAddress || '--',
|
||||||
|
logisticsCompany: info.logistics_company || '--',
|
||||||
|
trackingNumber: info.tracking_number || '--',
|
||||||
|
estimatedDelivery: info.estimated_delivery || '--',
|
||||||
|
shippingStatus: Number.isNaN(status) ? 0 : status,
|
||||||
|
shippedAt: info.shipped_at || '',
|
||||||
|
deliveredAt: info.delivered_at || ''
|
||||||
|
};
|
||||||
|
},
|
||||||
|
formatAmount(value) {
|
||||||
|
const num = Number(value);
|
||||||
|
return Number.isFinite(num) ? num.toFixed(2) : '0.00';
|
||||||
|
},
|
||||||
|
normalizeStatus(status) {
|
||||||
|
const code = Number(status);
|
||||||
|
if (Number.isNaN(code) || code <= 0) return 1;
|
||||||
|
return code;
|
||||||
|
},
|
||||||
|
clearCountdown() {
|
||||||
|
if (this.countdownTimer) {
|
||||||
|
clearInterval(this.countdownTimer);
|
||||||
|
this.countdownTimer = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
@ -19,17 +378,261 @@ export default {
|
|||||||
.order-detail-page {
|
.order-detail-page {
|
||||||
min-height: 100vh;
|
min-height: 100vh;
|
||||||
background-color: #f5f5f5;
|
background-color: #f5f5f5;
|
||||||
display: flex;
|
padding: 32rpx;
|
||||||
align-items: center;
|
box-sizing: border-box;
|
||||||
justify-content: center;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.detail-content {
|
.detail-content {
|
||||||
padding: 40rpx;
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 24rpx;
|
||||||
}
|
}
|
||||||
|
|
||||||
.placeholder-text {
|
.card {
|
||||||
|
background-color: #fff;
|
||||||
|
border-radius: 16rpx;
|
||||||
|
padding: 32rpx;
|
||||||
|
box-shadow: 0 8rpx 20rpx rgba(0, 0, 0, 0.06);
|
||||||
|
}
|
||||||
|
|
||||||
|
.section-title {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
font-size: 30rpx;
|
||||||
|
font-weight: 600;
|
||||||
|
margin-bottom: 24rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sub-info {
|
||||||
|
font-size: 24rpx;
|
||||||
|
color: #888;
|
||||||
|
font-weight: 400;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-tag {
|
||||||
|
padding: 6rpx 20rpx;
|
||||||
|
border-radius: 999rpx;
|
||||||
|
font-size: 24rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-pending {
|
||||||
|
background-color: #fff5e5;
|
||||||
|
color: #f57c00;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-failed {
|
||||||
|
background-color: #ffe5e5;
|
||||||
|
color: #d32f2f;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-delivering,
|
||||||
|
.status-receiving {
|
||||||
|
background-color: #e8f4ff;
|
||||||
|
color: #1976d2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-success {
|
||||||
|
background-color: #e5f9f0;
|
||||||
|
color: #22a46d;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-cancel {
|
||||||
|
background-color: #f0f0f0;
|
||||||
|
color: #777;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-refund {
|
||||||
|
background-color: #f0e8ff;
|
||||||
|
color: #7b1fa2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-default {
|
||||||
|
background-color: #ececec;
|
||||||
|
color: #666;
|
||||||
|
}
|
||||||
|
|
||||||
|
.row {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 16rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.label {
|
||||||
|
width: 160rpx;
|
||||||
|
font-size: 26rpx;
|
||||||
|
color: #666;
|
||||||
|
}
|
||||||
|
|
||||||
|
.value {
|
||||||
|
flex: 1;
|
||||||
font-size: 28rpx;
|
font-size: 28rpx;
|
||||||
|
color: #111;
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
.amount-row {
|
||||||
|
margin-top: 12rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.amount {
|
||||||
|
font-size: 40rpx;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #d81e06;
|
||||||
|
}
|
||||||
|
|
||||||
|
.total-row .amount {
|
||||||
|
font-size: 44rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.warning-card {
|
||||||
|
background: linear-gradient(135deg, #fff6e6, #ffe8d1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.warning-title {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
font-size: 28rpx;
|
||||||
|
font-weight: 600;
|
||||||
|
margin-bottom: 16rpx;
|
||||||
|
color: #b34700;
|
||||||
|
}
|
||||||
|
|
||||||
|
.countdown {
|
||||||
|
font-size: 26rpx;
|
||||||
|
color: #d81e06;
|
||||||
|
}
|
||||||
|
|
||||||
|
.warning-text {
|
||||||
|
font-size: 26rpx;
|
||||||
|
color: #8c4a00;
|
||||||
|
line-height: 1.6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.address {
|
||||||
|
text-align: right;
|
||||||
|
word-break: break-all;
|
||||||
|
}
|
||||||
|
|
||||||
|
.product-list {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 24rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.product-item {
|
||||||
|
display: flex;
|
||||||
|
gap: 24rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.product-img {
|
||||||
|
width: 140rpx;
|
||||||
|
height: 140rpx;
|
||||||
|
border-radius: 12rpx;
|
||||||
|
background-color: #f6f6f6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.product-info {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 8rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.product-name-row {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 12rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.product-name {
|
||||||
|
font-size: 30rpx;
|
||||||
|
color: #111;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.product-tag {
|
||||||
|
font-size: 20rpx;
|
||||||
|
color: #f57c00;
|
||||||
|
background-color: #fff4e0;
|
||||||
|
padding: 2rpx 12rpx;
|
||||||
|
border-radius: 999rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.product-tag.limited {
|
||||||
|
color: #c2185b;
|
||||||
|
background-color: #ffe1ec;
|
||||||
|
}
|
||||||
|
|
||||||
|
.product-sku {
|
||||||
|
font-size: 24rpx;
|
||||||
|
color: #666;
|
||||||
|
}
|
||||||
|
|
||||||
|
.product-desc {
|
||||||
|
font-size: 24rpx;
|
||||||
color: #999;
|
color: #999;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.product-meta {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
margin-top: 8rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.product-price {
|
||||||
|
font-size: 30rpx;
|
||||||
|
color: #d81e06;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.product-qty {
|
||||||
|
font-size: 26rpx;
|
||||||
|
color: #555;
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty-tip {
|
||||||
|
padding: 40rpx 0;
|
||||||
|
text-align: center;
|
||||||
|
color: #999;
|
||||||
|
font-size: 28rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty-block {
|
||||||
|
min-height: 400rpx;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
color: #999;
|
||||||
|
font-size: 28rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.action-bar {
|
||||||
|
position: sticky;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
padding: 24rpx 32rpx 48rpx;
|
||||||
|
background: linear-gradient(180deg, rgba(245, 245, 245, 0), #f5f5f5 40%, #f5f5f5);
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pay-btn {
|
||||||
|
width: 100%;
|
||||||
|
border: none;
|
||||||
|
border-radius: 999rpx;
|
||||||
|
background-image: linear-gradient(120deg, #ff8a00, #ff4d4f);
|
||||||
|
color: #fff;
|
||||||
|
font-size: 32rpx;
|
||||||
|
font-weight: 600;
|
||||||
|
box-shadow: 0 12rpx 20rpx rgba(255, 77, 79, 0.25);
|
||||||
|
}
|
||||||
|
|
||||||
|
.pay-btn:disabled {
|
||||||
|
opacity: 0.6;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@ -30,7 +30,7 @@
|
|||||||
<!-- 商品名称 -->
|
<!-- 商品名称 -->
|
||||||
<view class="product-name">{{ productInfo.name || '玫瑰香水盲盒' }}</view>
|
<view class="product-name">{{ productInfo.name || '玫瑰香水盲盒' }}</view>
|
||||||
|
|
||||||
<view class="product-sub-title">{{ productInfo.description.sub_title || '玫瑰香水盲盒' }}</view>
|
<!-- <view class="product-sub-title">{{ productInfo.description.sub_title || '玫瑰香水盲盒' }}</view> -->
|
||||||
<!-- 评分和喜欢 -->
|
<!-- 评分和喜欢 -->
|
||||||
<view class="product-rating">
|
<view class="product-rating">
|
||||||
<text class="iconfont icon-xingxing star-icon"></text>
|
<text class="iconfont icon-xingxing star-icon"></text>
|
||||||
@ -51,9 +51,8 @@
|
|||||||
|
|
||||||
<!-- 商品描述 -->
|
<!-- 商品描述 -->
|
||||||
<view class="product-description">
|
<view class="product-description">
|
||||||
<!-- {{ productInfo.description.details ||
|
{{ productInfo.description.sub_title ||
|
||||||
'经典玫瑰香调,优雅女神范,持久留香8小时。采用法国进口玫瑰精油,层次丰富,前调清新,中调浓郁,后调温暖。适合日常使用,也是送礼的绝佳选择。' }} -->
|
'经典玫瑰香调,优雅女神范,持久留香8小时。采用法国进口玫瑰精油,层次丰富,前调清新,中调浓郁,后调温暖。适合日常使用,也是送礼的绝佳选择。' }}
|
||||||
<rich-text :nodes="productInfo.description.details"></rich-text>
|
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
@ -139,6 +138,9 @@
|
|||||||
<view class="content-section">
|
<view class="content-section">
|
||||||
<!-- 商品详情 -->
|
<!-- 商品详情 -->
|
||||||
<view class="tab-content" v-if="activeTab === 'detail'">
|
<view class="tab-content" v-if="activeTab === 'detail'">
|
||||||
|
<view class="detail-section">
|
||||||
|
<rich-text :nodes="productInfo.description.details"></rich-text>
|
||||||
|
</view>
|
||||||
<view class="detail-section">
|
<view class="detail-section">
|
||||||
<view class="section-title">香调层次</view>
|
<view class="section-title">香调层次</view>
|
||||||
<view class="fragrance-notes">
|
<view class="fragrance-notes">
|
||||||
@ -393,6 +395,7 @@ export default {
|
|||||||
this.productInfo.scent_level = JSON.parse(productInfo.scent_level);
|
this.productInfo.scent_level = JSON.parse(productInfo.scent_level);
|
||||||
this.productInfo.attr = JSON.parse(productInfo.attr);
|
this.productInfo.attr = JSON.parse(productInfo.attr);
|
||||||
this.productInfo.description = parseDescription(productInfo.description);
|
this.productInfo.description = parseDescription(productInfo.description);
|
||||||
|
console.log(this.productInfo.description)
|
||||||
// 处理SKU列表
|
// 处理SKU列表
|
||||||
if (productInfo.skus && Array.isArray(productInfo.skus) && productInfo.skus.length > 0) {
|
if (productInfo.skus && Array.isArray(productInfo.skus) && productInfo.skus.length > 0) {
|
||||||
this.skuList = productInfo.skus;
|
this.skuList = productInfo.skus;
|
||||||
|
|||||||
@ -9,7 +9,7 @@
|
|||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
<view class="item-image">
|
<view class="item-image">
|
||||||
<image :src="item.product_image || item.main_image_url" mode="aspectFill"></image>
|
<image :src="item.product_image_url" mode="aspectFill"></image>
|
||||||
</view>
|
</view>
|
||||||
<view class="item-info">
|
<view class="item-info">
|
||||||
<view class="item-title">{{ item.product_name || item.name }}</view>
|
<view class="item-title">{{ item.product_name || item.name }}</view>
|
||||||
@ -252,7 +252,8 @@ export default {
|
|||||||
product_id: item.product_id,
|
product_id: item.product_id,
|
||||||
sku_id: item.sku_id,
|
sku_id: item.sku_id,
|
||||||
quantity: item.quantity || item.count,
|
quantity: item.quantity || item.count,
|
||||||
price: item.price || item.sku_price
|
price: item.price || item.sku_price,
|
||||||
|
product_image_url: item.product_image_url
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// 跳转到订单页面,根据实际路由调整
|
// 跳转到订单页面,根据实际路由调整
|
||||||
|
|||||||
@ -19,11 +19,11 @@ export function parseDescription(value) {
|
|||||||
try {
|
try {
|
||||||
const parsed = JSON.parse(value)
|
const parsed = JSON.parse(value)
|
||||||
if (typeof parsed === 'string') {
|
if (typeof parsed === 'string') {
|
||||||
return { details: parsed, sub_title: '' }
|
return { details: parsed, sub_title: parsed }
|
||||||
}
|
}
|
||||||
return { ...fallback, ...(parsed || {}) }
|
return { ...fallback, ...(parsed || {}) }
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return { details: value, sub_title: '' }
|
return { details: value, sub_title: value }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return { ...fallback, ...value }
|
return { ...fallback, ...value }
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user