|
|
@@ -4,26 +4,49 @@
|
|
|
<text class="loading-text">加载中...</text>
|
|
|
</view>
|
|
|
<scroll-view v-else scroll-y class="content">
|
|
|
- <!-- Address Section -->
|
|
|
- <view class="section address-section" @click="goSelectAddress">
|
|
|
- <view v-if="selectedAddress" class="address-content">
|
|
|
+ <!-- Consignee Section -->
|
|
|
+ <view class="section address-section" @click="openConsigneePicker">
|
|
|
+ <view v-if="selectedConsignee" class="address-content">
|
|
|
<view class="address-top">
|
|
|
- <text class="receiver-name">{{ selectedAddress.receiverName }}</text>
|
|
|
- <text class="receiver-phone">{{ formatPhone(selectedAddress.phone) }}</text>
|
|
|
- <text v-if="selectedAddress.isDefault === 1" class="default-badge">默认</text>
|
|
|
+ <text class="receiver-name">{{ selectedConsignee.name }}</text>
|
|
|
+ <text class="receiver-phone">{{ formatPhone(selectedConsignee.phone) }}</text>
|
|
|
+ <text v-if="selectedConsignee.isDefault === 1" class="default-badge">默认</text>
|
|
|
</view>
|
|
|
- <text class="address-detail">
|
|
|
- {{ selectedAddress.province }}{{ selectedAddress.city }}{{ selectedAddress.district }}{{ selectedAddress.street }}
|
|
|
- </text>
|
|
|
+ <text v-if="selectedConsignee.idCard" class="address-detail">身份证: {{ maskField(selectedConsignee.idCard) }}</text>
|
|
|
+ <text v-if="selectedConsignee.ethnicity" class="address-detail">民族: {{ selectedConsignee.ethnicity }}</text>
|
|
|
+ <text v-if="selectedConsignee.bloodType" class="address-detail">血型: {{ selectedConsignee.bloodType }}</text>
|
|
|
<text class="address-arrow">›</text>
|
|
|
</view>
|
|
|
- <view v-else class="address-empty" @click.stop="goAddAddress">
|
|
|
+ <view v-else class="address-empty" @click.stop="goAddConsignee">
|
|
|
<text class="address-empty-icon">📍</text>
|
|
|
- <text class="address-empty-text">请添加收货地址</text>
|
|
|
+ <text class="address-empty-text">请选择收货人</text>
|
|
|
<text class="address-arrow">›</text>
|
|
|
</view>
|
|
|
</view>
|
|
|
|
|
|
+ <!-- Purchase Info Section (when product requires it) -->
|
|
|
+ <view class="section" v-if="requiredFields.length > 0">
|
|
|
+ <text class="section-title">购买信息</text>
|
|
|
+ <view
|
|
|
+ v-for="field in requiredFields"
|
|
|
+ :key="field.fieldKey"
|
|
|
+ class="purchase-field-row"
|
|
|
+ >
|
|
|
+ <text class="purchase-field-label">
|
|
|
+ {{ field.fieldName }}
|
|
|
+ <text v-if="field.isRequired" class="required-mark">*</text>
|
|
|
+ </text>
|
|
|
+ <input
|
|
|
+ v-model="purchaseForm[field.fieldKey]"
|
|
|
+ class="purchase-field-input"
|
|
|
+ :placeholder="'请输入' + field.fieldName + (field.isRequired ? '' : '(选填)')"
|
|
|
+ :disabled="!!field.prefillValue"
|
|
|
+ placeholder-style="color: #ccc; font-size: 26rpx;"
|
|
|
+ />
|
|
|
+ <text v-if="field.prefillValue" class="purchase-field-prefill">已填写</text>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+
|
|
|
<!-- Order Items Section -->
|
|
|
<view class="section">
|
|
|
<text class="section-title">商品信息</text>
|
|
|
@@ -159,13 +182,17 @@
|
|
|
|
|
|
<script>
|
|
|
import config from '@/config.js'
|
|
|
-import { getCouponList } from '../../../utils/api.js'
|
|
|
+import { getCouponList, consigneeList, getProductRequiredFields } from '../../../utils/api.js'
|
|
|
|
|
|
export default {
|
|
|
data() {
|
|
|
return {
|
|
|
items: [],
|
|
|
- selectedAddress: null,
|
|
|
+ selectedConsignee: null,
|
|
|
+ requiredFields: [], // 需要的购买信息字段
|
|
|
+ purchaseForm: {}, // 用户填写的购买信息
|
|
|
+ consigneeList: [],
|
|
|
+ showConsigneePicker: false,
|
|
|
freight: 0,
|
|
|
remark: '',
|
|
|
loading: false,
|
|
|
@@ -234,7 +261,8 @@ export default {
|
|
|
this.items = [productData]
|
|
|
this.calcAmount()
|
|
|
}
|
|
|
- this.loadDefaultAddress()
|
|
|
+ this.loadDefaultConsignee()
|
|
|
+ this.loadRequiredFields()
|
|
|
this.loadCoupons()
|
|
|
this.loadUserPoints()
|
|
|
},
|
|
|
@@ -286,55 +314,76 @@ export default {
|
|
|
if (!minSpend || minSpend <= 0) return '无门槛'
|
|
|
return '满' + (minSpend / 100).toFixed(2) + '元可用'
|
|
|
},
|
|
|
- loadDefaultAddress() {
|
|
|
+ async loadDefaultConsignee() {
|
|
|
this.loading = true
|
|
|
- var that = this
|
|
|
- uni.request({
|
|
|
- url: config.api('/api/user/address/list'),
|
|
|
- method: 'POST',
|
|
|
- data: {},
|
|
|
- header: {
|
|
|
- 'Content-Type': 'application/json',
|
|
|
- 'Authorization': 'Bearer ' + uni.getStorageSync('token')
|
|
|
- },
|
|
|
- success: function(res) {
|
|
|
- that.loading = false
|
|
|
- if (res.data && res.data.code === 200) {
|
|
|
- var list = res.data.data || []
|
|
|
- for (var i = 0; i < list.length; i++) {
|
|
|
- if (list[i].isDefault === 1) {
|
|
|
- that.selectedAddress = list[i]
|
|
|
- break
|
|
|
- }
|
|
|
- }
|
|
|
- if (!that.selectedAddress && list.length > 0) {
|
|
|
- that.selectedAddress = list[0]
|
|
|
+ try {
|
|
|
+ var res = await consigneeList()
|
|
|
+ if (res.code === 200) {
|
|
|
+ var list = res.data || []
|
|
|
+ this.consigneeList = list
|
|
|
+ for (var i = 0; i < list.length; i++) {
|
|
|
+ if (list[i].isDefault === 1) {
|
|
|
+ this.selectedConsignee = list[i]
|
|
|
+ this.autoFillPurchaseForm()
|
|
|
+ break
|
|
|
}
|
|
|
}
|
|
|
- },
|
|
|
- fail: function() {
|
|
|
- that.loading = false
|
|
|
+ if (!this.selectedConsignee && list.length > 0) {
|
|
|
+ this.selectedConsignee = list[0]
|
|
|
+ this.autoFillPurchaseForm()
|
|
|
+ }
|
|
|
}
|
|
|
- })
|
|
|
+ } catch (e) {
|
|
|
+ console.error(e)
|
|
|
+ } finally {
|
|
|
+ this.loading = false
|
|
|
+ }
|
|
|
},
|
|
|
- goSelectAddress() {
|
|
|
- var self = this
|
|
|
- uni.navigateTo({
|
|
|
- url: '/pages/shop/address/address?selectMode=1',
|
|
|
- events: {
|
|
|
- onAddressSelect: function(data) {
|
|
|
- self.selectedAddress = data
|
|
|
- }
|
|
|
- },
|
|
|
+ async loadRequiredFields() {
|
|
|
+ if (this.items.length === 0) return
|
|
|
+ var productId = this.items[0].productId
|
|
|
+ try {
|
|
|
+ var res = await getProductRequiredFields(productId)
|
|
|
+ if (res.code === 200) {
|
|
|
+ this.requiredFields = res.data || []
|
|
|
+ this.autoFillPurchaseForm()
|
|
|
+ }
|
|
|
+ } catch (e) {
|
|
|
+ console.error(e)
|
|
|
+ }
|
|
|
+ },
|
|
|
+ autoFillPurchaseForm() {
|
|
|
+ if (!this.selectedConsignee || this.requiredFields.length === 0) return
|
|
|
+ var form = {}
|
|
|
+ for (var i = 0; i < this.requiredFields.length; i++) {
|
|
|
+ var field = this.requiredFields[i]
|
|
|
+ if (field.prefillValue) {
|
|
|
+ form[field.fieldKey] = field.prefillValue
|
|
|
+ } else {
|
|
|
+ form[field.fieldKey] = ''
|
|
|
+ }
|
|
|
+ }
|
|
|
+ this.purchaseForm = form
|
|
|
+ },
|
|
|
+ openConsigneePicker() {
|
|
|
+ var that = this
|
|
|
+ if (this.consigneeList.length === 0) {
|
|
|
+ uni.showToast({ title: '暂无收货人,请先添加', icon: 'none' })
|
|
|
+ return
|
|
|
+ }
|
|
|
+ var items = this.consigneeList.map(function(c) {
|
|
|
+ return (c.name || '') + ' ' + (c.phone || '')
|
|
|
+ })
|
|
|
+ uni.showActionSheet({
|
|
|
+ itemList: items,
|
|
|
success: function(res) {
|
|
|
- res.eventChannel.on('onAddressSelect', function(data) {
|
|
|
- self.selectedAddress = data
|
|
|
- })
|
|
|
+ that.selectedConsignee = that.consigneeList[res.tapIndex]
|
|
|
+ that.autoFillPurchaseForm()
|
|
|
}
|
|
|
})
|
|
|
},
|
|
|
- goAddAddress() {
|
|
|
- uni.navigateTo({ url: '/pages/shop/address-edit/address-edit' })
|
|
|
+ goAddConsignee() {
|
|
|
+ uni.navigateTo({ url: '/pages/shop/consignee-edit/consignee-edit' })
|
|
|
},
|
|
|
loadUserPoints() {
|
|
|
var that = this
|
|
|
@@ -354,8 +403,8 @@ export default {
|
|
|
})
|
|
|
},
|
|
|
onSubmit() {
|
|
|
- if (!this.selectedAddress) {
|
|
|
- uni.showToast({ title: '请选择收货地址', icon: 'none' })
|
|
|
+ if (!this.selectedConsignee) {
|
|
|
+ uni.showToast({ title: '请选择收货人', icon: 'none' })
|
|
|
return
|
|
|
}
|
|
|
if (this.items.length === 0) {
|
|
|
@@ -374,14 +423,6 @@ export default {
|
|
|
}
|
|
|
orderItems.push(itemData)
|
|
|
}
|
|
|
- var addressSnapshot = JSON.stringify({
|
|
|
- receiverName: this.selectedAddress.receiverName,
|
|
|
- phone: this.selectedAddress.phone,
|
|
|
- province: this.selectedAddress.province,
|
|
|
- city: this.selectedAddress.city,
|
|
|
- district: this.selectedAddress.district,
|
|
|
- street: this.selectedAddress.street
|
|
|
- })
|
|
|
var that = this
|
|
|
uni.request({
|
|
|
url: config.api('/api/product/order/create'),
|
|
|
@@ -389,7 +430,8 @@ export default {
|
|
|
data: {
|
|
|
items: orderItems,
|
|
|
remark: this.remark,
|
|
|
- addressSnapshot: addressSnapshot,
|
|
|
+ consigneeId: that.selectedConsignee.id,
|
|
|
+ purchaseInfo: that.purchaseForm,
|
|
|
pointsUsed: that.pointsUsed
|
|
|
},
|
|
|
header: {
|
|
|
@@ -429,6 +471,13 @@ export default {
|
|
|
return phone.substr(0, 3) + '****' + phone.substr(7)
|
|
|
}
|
|
|
return phone
|
|
|
+ },
|
|
|
+ maskField(val) {
|
|
|
+ if (!val) return ''
|
|
|
+ if (val.length >= 8) {
|
|
|
+ return val.substr(0, 4) + '****' + val.substr(val.length - 4)
|
|
|
+ }
|
|
|
+ return val
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
@@ -854,4 +903,40 @@ export default {
|
|
|
text-decoration: line-through;
|
|
|
margin-left: 8rpx;
|
|
|
}
|
|
|
+
|
|
|
+/* ===== Purchase Info Fields ===== */
|
|
|
+.purchase-field-row {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ padding: 16rpx 0;
|
|
|
+ border-bottom: 1rpx solid #f5f5f5;
|
|
|
+}
|
|
|
+.purchase-field-row:last-child {
|
|
|
+ border-bottom: none;
|
|
|
+}
|
|
|
+.purchase-field-label {
|
|
|
+ font-size: 26rpx;
|
|
|
+ color: #333;
|
|
|
+ width: 160rpx;
|
|
|
+ flex-shrink: 0;
|
|
|
+}
|
|
|
+.required-mark {
|
|
|
+ color: #f56c6c;
|
|
|
+}
|
|
|
+.purchase-field-input {
|
|
|
+ flex: 1;
|
|
|
+ font-size: 26rpx;
|
|
|
+ color: #333;
|
|
|
+ text-align: right;
|
|
|
+ padding: 8rpx 0;
|
|
|
+}
|
|
|
+.purchase-field-input[disabled] {
|
|
|
+ background: transparent;
|
|
|
+ color: #999;
|
|
|
+}
|
|
|
+.purchase-field-prefill {
|
|
|
+ font-size: 22rpx;
|
|
|
+ color: #67c23a;
|
|
|
+ margin-left: 8rpx;
|
|
|
+}
|
|
|
</style>
|