You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
772 lines
17 KiB
772 lines
17 KiB
<template>
|
|
<view class="confirm-container">
|
|
<scroll-view class="content-scroll" scroll-y>
|
|
<!-- 收货地址 -->
|
|
<view class="address-section">
|
|
<!-- 有地址时显示 -->
|
|
<view class="section-header" @click="selectAddress" v-if="address.name">
|
|
<view class="address-info">
|
|
<view class="user-info">
|
|
<text class="username">{{ address.name }}</text>
|
|
<text class="phone">{{ formatPhone(address.phone) }}</text>
|
|
<text class="default-tag" v-if="address.isDefault">默认地址</text>
|
|
</view>
|
|
<view class="address-detail">
|
|
<text>{{ address.fullAddress }}</text>
|
|
</view>
|
|
</view>
|
|
<uni-icons type="right" size="16" color="#c0c4cc" />
|
|
</view>
|
|
|
|
<!-- 没有地址时显示 -->
|
|
<view class="empty-address" @click="selectAddress" v-else>
|
|
<view class="empty-address-content">
|
|
<uni-icons type="location" size="24" color="#c0c4cc" />
|
|
<view class="empty-address-text">
|
|
<text class="empty-title">请选择收货地址</text>
|
|
<text class="empty-tip">点击添加收货地址信息</text>
|
|
</view>
|
|
</view>
|
|
<uni-icons type="right" size="16" color="#c0c4cc" />
|
|
</view>
|
|
</view>
|
|
|
|
<!-- 预约日期 -->
|
|
<view class="date-section">
|
|
<view class="section-title">
|
|
<text>预约日期</text>
|
|
<text class="required">*</text>
|
|
</view>
|
|
<view class="date-picker-container">
|
|
<uni-datetime-picker
|
|
v-model="selectedDate"
|
|
type="date"
|
|
:clear-icon="false"
|
|
placeholder="请选择预约日期"
|
|
@change="onDateChange"
|
|
>
|
|
<view class="date-input">
|
|
<text class="date-text" :class="{ placeholder: !selectedDate }">
|
|
{{ selectedDate || "请选择预约日期" }}
|
|
</text>
|
|
<uni-icons type="calendar" size="18" color="#c0c4cc" />
|
|
</view>
|
|
</uni-datetime-picker>
|
|
</view>
|
|
</view>
|
|
|
|
<!-- 商品信息 -->
|
|
<view class="goods-section">
|
|
<view class="section-title">
|
|
<text>IP资产供应商:{{ supplier || "苏州XXX博物馆" }}</text>
|
|
</view>
|
|
<view class="goods-card" v-for="item in 4">
|
|
<image
|
|
class="goods-image"
|
|
:src="goodsInfo.image || '/static/image/goods-default.jpg'"
|
|
mode="aspectFill"
|
|
/>
|
|
<view class="goods-info">
|
|
<text class="goods-name">{{
|
|
goodsInfo.name || "食在苏州 | 世界美食之都巡礼+实物探真"
|
|
}}</text>
|
|
<text class="goods-desc">{{
|
|
goodsInfo.desc || "商品规格信息描述"
|
|
}}</text>
|
|
<text class="goods-price">¥{{ goodsInfo.price || "699.00" }}</text>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
|
|
<!-- 履约方式 -->
|
|
<!-- <view class="delivery-section">
|
|
<view class="section-row" @click="selectDeliveryMethod">
|
|
<text class="section-label">履约方式</text>
|
|
<view class="section-value">
|
|
<text :class="{ placeholder: !deliveryMethod }">
|
|
{{ deliveryMethod || "预约发货+到店核销 >" }}
|
|
</text>
|
|
</view>
|
|
</view>
|
|
</view> -->
|
|
|
|
<!-- 备注 -->
|
|
<view class="note-section">
|
|
<view class="section-row">
|
|
<text class="section-label">备注</text>
|
|
<view class="note-input">
|
|
<input
|
|
v-model="note"
|
|
placeholder="选填"
|
|
placeholder-class="placeholder"
|
|
maxlength="200"
|
|
/>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
|
|
<!-- 费用明细 -->
|
|
<view class="cost-section">
|
|
<view class="cost-item">
|
|
<text class="cost-label">商品金额</text>
|
|
<text class="cost-value">¥{{ goodsInfo.price || "699.00" }}</text>
|
|
</view>
|
|
<view class="cost-item">
|
|
<text class="cost-label">运费</text>
|
|
<text class="cost-value">{{ shipping || "预约发货时计算" }}</text>
|
|
</view>
|
|
<view class="cost-item">
|
|
<text class="cost-label">优惠券</text>
|
|
<text class="cost-value coupon">{{ coupon || "无可用 >" }}</text>
|
|
</view>
|
|
</view>
|
|
<view class="submit-section-content" style="height: 230rpx"> </view>
|
|
</scroll-view>
|
|
|
|
<!-- 底部提交区域 -->
|
|
<view
|
|
class=" "
|
|
style="position: fixed; width: 100%; bottom: 0; z-index: 98"
|
|
>
|
|
<view class="submit-section">
|
|
<view class="total-price">
|
|
<text class="total-amount">¥{{ totalAmount || "699.00" }}</text>
|
|
</view>
|
|
<button class="submit-btn" @click="submitOrder">提交订单</button>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
</template>
|
|
|
|
<script>
|
|
export default {
|
|
data() {
|
|
return {
|
|
// 收货地址信息
|
|
address: {
|
|
name: "",
|
|
phone: "",
|
|
fullAddress: "",
|
|
isDefault: false,
|
|
},
|
|
// 选中的日期
|
|
selectedDate: "",
|
|
// 商品信息
|
|
goodsInfo: {
|
|
id: "",
|
|
name: "",
|
|
desc: "",
|
|
price: "",
|
|
image: "",
|
|
},
|
|
// 供应商
|
|
supplier: "",
|
|
// 履约方式
|
|
deliveryMethod: "",
|
|
// 备注
|
|
note: "",
|
|
// 运费
|
|
shipping: "",
|
|
// 优惠券
|
|
coupon: "",
|
|
// 总金额
|
|
totalAmount: "",
|
|
};
|
|
},
|
|
|
|
onLoad(options) {
|
|
// 接收页面参数
|
|
if (options.goodsId) {
|
|
this.loadGoodsInfo(options.goodsId);
|
|
}
|
|
if (options.goodsName) {
|
|
this.goodsInfo.name = decodeURIComponent(options.goodsName);
|
|
}
|
|
this.loadDefaultAddress();
|
|
|
|
// 监听地址选择事件
|
|
uni.$on("addressSelected", this.handleAddressSelected);
|
|
},
|
|
|
|
onShow() {
|
|
// 从地址选择页面返回时,优先检查用户选择的地址
|
|
const hasSelectedAddress = this.checkSelectedAddress();
|
|
// 如果没有选择地址且当前也没有地址,才加载默认地址
|
|
if (!hasSelectedAddress && !this.address.id) {
|
|
this.loadDefaultAddress();
|
|
}
|
|
},
|
|
|
|
onUnload() {
|
|
// 移除事件监听
|
|
uni.$off("addressSelected", this.handleAddressSelected);
|
|
},
|
|
|
|
methods: {
|
|
// 返回上一页
|
|
goBack() {
|
|
uni.navigateBack();
|
|
},
|
|
|
|
// 处理地址选择事件
|
|
handleAddressSelected(data) {
|
|
if (data.fromPage === "orderConfirm") {
|
|
this.updateAddressInfo(data.address);
|
|
console.log("地址已更新:", this.address);
|
|
}
|
|
},
|
|
|
|
// 更新地址信息的统一方法
|
|
updateAddressInfo(addressData) {
|
|
this.address = {
|
|
id: addressData.id,
|
|
name: addressData.name,
|
|
phone: addressData.tel,
|
|
fullAddress: addressData.address || this.getFullAddress(addressData),
|
|
isDefault: addressData.is_default == 1,
|
|
};
|
|
},
|
|
|
|
// 检查storage中的选中地址
|
|
checkSelectedAddress() {
|
|
try {
|
|
const selectedAddress = uni.getStorageSync("selectedAddress");
|
|
if (selectedAddress) {
|
|
this.updateAddressInfo(selectedAddress);
|
|
// 清除storage
|
|
uni.removeStorageSync("selectedAddress");
|
|
return true; // 表示找到了选中的地址
|
|
}
|
|
return false;
|
|
} catch (error) {
|
|
console.error("读取选中地址失败:", error);
|
|
return false;
|
|
}
|
|
},
|
|
|
|
// 获取完整地址
|
|
getFullAddress(addressData) {
|
|
const province = addressData.province_text || "";
|
|
const city = addressData.city_text || "";
|
|
const district = addressData.district_text || "";
|
|
const detail = addressData.detail_addr || "";
|
|
return `${province}${city}${district}${detail}`;
|
|
},
|
|
|
|
// 格式化手机号
|
|
formatPhone(phone) {
|
|
if (!phone) return "";
|
|
return phone.replace(/(\d{3})\d{4}(\d{4})/, "$1****$2");
|
|
},
|
|
|
|
// 选择收货地址
|
|
selectAddress() {
|
|
uni.navigateTo({
|
|
url: "/subPackages/user/travelerList?from=orderConfirm",
|
|
});
|
|
},
|
|
|
|
// 日期变化回调
|
|
onDateChange(date) {
|
|
this.selectedDate = date;
|
|
console.log("选择的日期:", date);
|
|
},
|
|
|
|
// 选择履约方式
|
|
selectDeliveryMethod() {
|
|
const itemList = ["预约发货+到店核销", "预约发货+快递配送", "到店自提"];
|
|
|
|
uni.showActionSheet({
|
|
itemList: itemList,
|
|
success: (res) => {
|
|
this.deliveryMethod = itemList[res.tapIndex];
|
|
console.log("选择的履约方式:", this.deliveryMethod);
|
|
},
|
|
fail: (err) => {
|
|
console.log("取消选择履约方式");
|
|
},
|
|
});
|
|
},
|
|
|
|
// 加载商品信息
|
|
async loadGoodsInfo(goodsId) {
|
|
try {
|
|
// 这里调用实际的API接口获取商品详情
|
|
const goodsData = await this.getGoodsDetail(goodsId);
|
|
this.goodsInfo = goodsData;
|
|
this.supplier = goodsData.supplier;
|
|
this.totalAmount = goodsData.price;
|
|
} catch (error) {
|
|
console.error("加载商品信息失败:", error);
|
|
}
|
|
},
|
|
|
|
// 加载默认地址
|
|
async loadDefaultAddress() {
|
|
try {
|
|
// 这里调用实际的API接口获取默认地址
|
|
const addressData = await this.getDefaultAddress();
|
|
if (addressData && !this.address.id) {
|
|
// 只有当前没有地址时才设置默认地址
|
|
this.updateAddressInfo(addressData);
|
|
}
|
|
} catch (error) {
|
|
console.error("加载默认地址失败:", error);
|
|
}
|
|
},
|
|
|
|
// 提交订单
|
|
async submitOrder() {
|
|
// 验证必填项
|
|
if (!this.selectedDate) {
|
|
uni.showToast({
|
|
title: "请选择预约日期",
|
|
icon: "none",
|
|
});
|
|
return;
|
|
}
|
|
|
|
if (!this.deliveryMethod) {
|
|
uni.showToast({
|
|
title: "请选择履约方式",
|
|
icon: "none",
|
|
});
|
|
return;
|
|
}
|
|
|
|
if (!this.address.name) {
|
|
uni.showToast({
|
|
title: "请选择收货地址",
|
|
icon: "none",
|
|
});
|
|
return;
|
|
}
|
|
|
|
try {
|
|
uni.showLoading({
|
|
title: "提交中...",
|
|
});
|
|
|
|
const orderData = {
|
|
goodsId: this.goodsInfo.id,
|
|
addressId: this.address.id,
|
|
deliveryDate: this.selectedDate,
|
|
deliveryMethod: this.deliveryMethod,
|
|
note: this.note,
|
|
totalAmount: this.totalAmount,
|
|
};
|
|
|
|
const result = await this.createOrder(orderData);
|
|
|
|
uni.hideLoading();
|
|
|
|
if (result.success) {
|
|
uni.showToast({
|
|
title: "订单提交成功",
|
|
icon: "success",
|
|
});
|
|
|
|
// 跳转到订单详情或订单列表
|
|
setTimeout(() => {
|
|
uni.redirectTo({
|
|
url: "/subPackages/orderQy/detail?orderId=" + result.orderId,
|
|
});
|
|
}, 1500);
|
|
}
|
|
} catch (error) {
|
|
uni.hideLoading();
|
|
console.error("提交订单失败:", error);
|
|
uni.showToast({
|
|
title: "提交失败,请重试",
|
|
icon: "none",
|
|
});
|
|
}
|
|
},
|
|
|
|
// API接口 - 获取商品详情
|
|
async getGoodsDetail(goodsId) {
|
|
// 模拟API数据
|
|
return new Promise((resolve) => {
|
|
setTimeout(() => {
|
|
resolve({
|
|
id: goodsId,
|
|
name: "食在苏州 | 世界美食之都巡礼+实物探真",
|
|
desc: "商品规格信息描述",
|
|
price: "699.00",
|
|
image: "/static/image/goods-sample.jpg",
|
|
supplier: "苏州XXX博物馆",
|
|
});
|
|
}, 500);
|
|
});
|
|
},
|
|
|
|
// API接口 - 获取默认地址
|
|
async getDefaultAddress() {
|
|
// 模拟API数据
|
|
return new Promise((resolve) => {
|
|
setTimeout(() => {
|
|
resolve({
|
|
id: 1,
|
|
name: "XX先生",
|
|
tel: "18312341234",
|
|
address: "江苏省苏州市姑苏区XXXXXX",
|
|
province_text: "江苏省",
|
|
city_text: "苏州市",
|
|
district_text: "姑苏区",
|
|
detail_addr: "XXXXXX",
|
|
is_default: 1,
|
|
});
|
|
}, 500);
|
|
});
|
|
},
|
|
|
|
// API接口 - 创建订单
|
|
async createOrder(orderData) {
|
|
// 模拟API调用
|
|
return new Promise((resolve) => {
|
|
setTimeout(() => {
|
|
resolve({
|
|
success: true,
|
|
orderId: "EQ" + Date.now(),
|
|
});
|
|
}, 1000);
|
|
});
|
|
},
|
|
},
|
|
};
|
|
</script>
|
|
|
|
<style lang="scss" scoped>
|
|
// 主题色彩变量
|
|
$primary-color: #667eea;
|
|
$secondary-color: #f8f9fa;
|
|
$text-primary: #2d3748;
|
|
$text-secondary: #718096;
|
|
$text-muted: #a0aec0;
|
|
$border-color: #e2e8f0;
|
|
$success-color: #48bb78;
|
|
$warning-color: #ed8936;
|
|
$danger-color: #f56565;
|
|
$bg-light: #f7fafc;
|
|
|
|
.confirm-container {
|
|
height: 100vh;
|
|
background-color: $bg-light;
|
|
display: flex;
|
|
flex-direction: column;
|
|
}
|
|
|
|
.content-scroll {
|
|
flex: 1;
|
|
padding: 20rpx;
|
|
width: 710rpx;
|
|
}
|
|
|
|
// 通用section样式
|
|
.section-header,
|
|
.section-row {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
padding: 24rpx 30rpx;
|
|
background-color: #ffffff;
|
|
border-radius: 16rpx;
|
|
margin-bottom: 20rpx;
|
|
}
|
|
|
|
.section-title {
|
|
font-size: 28rpx;
|
|
color: $text-primary;
|
|
font-weight: 600;
|
|
margin-bottom: 16rpx;
|
|
|
|
.required {
|
|
color: $danger-color;
|
|
margin-left: 4rpx;
|
|
}
|
|
}
|
|
|
|
// 收货地址
|
|
.address-section {
|
|
margin-bottom: 24rpx;
|
|
}
|
|
|
|
.address-info {
|
|
flex: 1;
|
|
}
|
|
|
|
.user-info {
|
|
display: flex;
|
|
align-items: center;
|
|
margin-bottom: 12rpx;
|
|
gap: 16rpx;
|
|
}
|
|
|
|
.username {
|
|
font-size: 28rpx;
|
|
color: $text-primary;
|
|
font-weight: 600;
|
|
}
|
|
|
|
.phone {
|
|
font-size: 24rpx;
|
|
color: $text-secondary;
|
|
}
|
|
|
|
.default-tag {
|
|
font-size: 20rpx;
|
|
color: $primary-color;
|
|
background-color: rgba(102, 126, 234, 0.1);
|
|
padding: 4rpx 12rpx;
|
|
border-radius: 12rpx;
|
|
}
|
|
|
|
.address-detail {
|
|
font-size: 26rpx;
|
|
color: $text-secondary;
|
|
line-height: 1.4;
|
|
}
|
|
|
|
// 空地址状态
|
|
.empty-address {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
padding: 24rpx 30rpx;
|
|
background-color: #ffffff;
|
|
border-radius: 16rpx;
|
|
margin-bottom: 20rpx;
|
|
border: 2rpx dashed $border-color;
|
|
transition: all 0.3s ease;
|
|
|
|
&:active {
|
|
background-color: rgba(102, 126, 234, 0.02);
|
|
border-color: $primary-color;
|
|
}
|
|
}
|
|
|
|
.empty-address-content {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 16rpx;
|
|
}
|
|
|
|
.empty-address-text {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 6rpx;
|
|
}
|
|
|
|
.empty-title {
|
|
font-size: 28rpx;
|
|
color: $text-primary;
|
|
font-weight: 500;
|
|
}
|
|
|
|
.empty-tip {
|
|
font-size: 24rpx;
|
|
color: $text-muted;
|
|
}
|
|
|
|
// 预约日期
|
|
.date-section {
|
|
margin-bottom: 24rpx;
|
|
}
|
|
|
|
.date-picker-container {
|
|
background-color: #ffffff;
|
|
border-radius: 16rpx;
|
|
padding: 24rpx 30rpx;
|
|
}
|
|
|
|
.date-input {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
}
|
|
|
|
.date-text {
|
|
font-size: 28rpx;
|
|
color: $text-primary;
|
|
|
|
&.placeholder {
|
|
color: $text-muted;
|
|
}
|
|
}
|
|
|
|
// 商品信息
|
|
.goods-section {
|
|
margin-bottom: 24rpx;
|
|
}
|
|
|
|
.goods-card {
|
|
display: flex;
|
|
background-color: #ffffff;
|
|
border-radius: 16rpx;
|
|
padding: 24rpx;
|
|
gap: 20rpx;
|
|
}
|
|
|
|
.goods-image {
|
|
width: 120rpx;
|
|
height: 120rpx;
|
|
border-radius: 12rpx;
|
|
flex-shrink: 0;
|
|
}
|
|
|
|
.goods-info {
|
|
flex: 1;
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 12rpx;
|
|
}
|
|
|
|
.goods-name {
|
|
font-size: 26rpx;
|
|
color: $text-primary;
|
|
font-weight: 600;
|
|
line-height: 1.4;
|
|
}
|
|
|
|
.goods-desc {
|
|
font-size: 24rpx;
|
|
color: $text-secondary;
|
|
line-height: 1.3;
|
|
}
|
|
|
|
.goods-price {
|
|
font-size: 32rpx;
|
|
color: $danger-color;
|
|
font-weight: 600;
|
|
margin-top: auto;
|
|
}
|
|
|
|
// 履约方式
|
|
.delivery-section {
|
|
margin-bottom: 24rpx;
|
|
}
|
|
|
|
.section-label {
|
|
font-size: 28rpx;
|
|
color: $text-primary;
|
|
font-weight: 500;
|
|
}
|
|
|
|
.section-value {
|
|
display: flex;
|
|
align-items: center;
|
|
|
|
text {
|
|
font-size: 26rpx;
|
|
color: $text-primary;
|
|
|
|
&.placeholder {
|
|
color: $text-muted;
|
|
}
|
|
}
|
|
}
|
|
|
|
// 备注
|
|
.note-section {
|
|
margin-bottom: 24rpx;
|
|
|
|
.section-row {
|
|
align-items: flex-start;
|
|
}
|
|
}
|
|
|
|
.note-input {
|
|
flex: 1;
|
|
margin-left: 40rpx;
|
|
|
|
input {
|
|
font-size: 26rpx;
|
|
color: $text-primary;
|
|
text-align: right;
|
|
width: 100%;
|
|
}
|
|
|
|
.placeholder {
|
|
color: $text-muted;
|
|
}
|
|
}
|
|
|
|
// 费用明细
|
|
.cost-section {
|
|
background-color: #ffffff;
|
|
border-radius: 16rpx;
|
|
padding: 24rpx 30rpx;
|
|
margin-bottom: 24rpx;
|
|
}
|
|
|
|
.cost-item {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
padding: 16rpx 0;
|
|
|
|
&:not(:last-child) {
|
|
border-bottom: 1px solid $border-color;
|
|
}
|
|
}
|
|
|
|
.cost-label {
|
|
font-size: 26rpx;
|
|
color: $text-primary;
|
|
}
|
|
|
|
.cost-value {
|
|
font-size: 26rpx;
|
|
color: $text-primary;
|
|
|
|
&.coupon {
|
|
color: $text-muted;
|
|
}
|
|
}
|
|
|
|
// 底部提交区域
|
|
.submit-section {
|
|
flex-shrink: 0; // 防止被压缩
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
padding: 24rpx 30rpx;
|
|
background-color: #ffffff;
|
|
border-top: 1px solid $border-color;
|
|
box-shadow: 0 -2rpx 12rpx rgba(0, 0, 0, 0.04);
|
|
|
|
// 添加安全区域适配
|
|
padding-bottom: calc(24rpx + env(safe-area-inset-bottom));
|
|
}
|
|
|
|
.submit-section-content {
|
|
padding-bottom: calc(24rpx + env(safe-area-inset-bottom));
|
|
}
|
|
|
|
.total-price {
|
|
flex: 1;
|
|
}
|
|
|
|
.total-amount {
|
|
font-size: 36rpx;
|
|
color: $danger-color;
|
|
font-weight: 600;
|
|
font-family: "SF Mono", "Monaco", "Cascadia Code", monospace;
|
|
}
|
|
|
|
.submit-btn {
|
|
background-color: $text-primary;
|
|
color: #ffffff;
|
|
border: none;
|
|
border-radius: 32rpx;
|
|
padding: 4rpx 48rpx;
|
|
font-size: 28rpx;
|
|
font-weight: 600;
|
|
transition: all 0.3s ease;
|
|
|
|
&:active {
|
|
transform: scale(0.95);
|
|
background-color: rgba(45, 55, 72, 0.8);
|
|
}
|
|
}
|
|
</style>
|
|
|