| | |
| | | <image class="cell-icon" src="/static/icon/ic_store@2x.png" mode="widthFix"></image> |
| | | <view class="store-cell-copy"> |
| | | <text class="cell-title">{{ selectedStore ? selectedStore.name : servicePointPlaceholder }}</text> |
| | | <view class="store-cell-copy-addr" v-if="selectedStore"> |
| | | <view class="store-cell-copy-addr1" v-if="selectedStore"> |
| | | <image src="/static/icon/home_ic_location3@2x.png" mode="widthFix"></image> |
| | | <text class="store-cell-subtitle">{{ selectedStore.address }}</text> |
| | | </view> |
| | |
| | | </view> |
| | | |
| | | <view v-else class="address-card section-card"> |
| | | <view class="address-row" @tap="openStorePopup"> |
| | | <view class="address-row" @click="openStorePopup"> |
| | | <view class="address-left"> |
| | | <view class="address-badge send">寄</view> |
| | | <view class="address-copy"> |
| | | <text class="address-title">寄件地址</text> |
| | | <text class="address-desc">请选择寄件服务点</text> |
| | | <text class="address-title" v-if="!sendStore">寄件地址</text> |
| | | <text class="address-desc" v-if="!sendStore">请选择寄件服务点</text> |
| | | <view v-if="sendStore" class="store-cell-copy-addr"> |
| | | <text class="store-cell-title">{{ sendStore.name }}</text> |
| | | <view class="store-cell-subtitle-container"> |
| | | <image src="/static/icon/home_ic_location3@2x.png" mode="widthFix"></image> |
| | | <text class="store-cell-subtitle">{{ sendStore.address }}</text> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | <u-icon name="arrow-right" size="20" color="#222222"></u-icon> |
| | | </view> |
| | | <view class="address-row no-border" @tap="openReceiveAddress"> |
| | | <view class="address-row no-border" @click="openReceiveAddress"> |
| | | <view class="address-left"> |
| | | <view class="address-badge receive">收</view> |
| | | <view class="address-copy"> |
| | | <text class="address-title">取件地址</text> |
| | | <text class="address-desc">请选择取件服务点或者其他地址</text> |
| | | <text class="address-title" v-if="!receiveStore && !receiveAddr">取件地址</text> |
| | | <text class="address-desc" v-if="!receiveStore && !receiveAddr">请选择取件服务点或者其他地址</text> |
| | | <view v-if="receiveStore" class="store-cell-copy-addr"> |
| | | <text class="store-cell-title">{{ receiveStore.name }}</text> |
| | | <view class="store-cell-subtitle-container"> |
| | | <image src="/static/icon/home_ic_location3@2x.png" mode="widthFix"></image> |
| | | <text class="store-cell-subtitle">{{ receiveStore.address }}</text> |
| | | </view> |
| | | </view> |
| | | <view v-if="receiveAddr" class="store-cell-copy-addr"> |
| | | <text class="store-cell-title">{{ receiveAddr.name }}</text> |
| | | <view class="store-cell-subtitle-container"> |
| | | <image src="/static/icon/home_ic_location3@2x.png" mode="widthFix"></image> |
| | | <text class="store-cell-subtitle">{{ receiveAddr.addr }}</text> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | <u-icon name="arrow-right" size="20" color="#222222"></u-icon> |
| | |
| | | </view> |
| | | </view> |
| | | |
| | | <view v-if="activeMode === 'city'" class="section-card service-time-card"> |
| | | <view v-if="activeMode === 'city' && amountData" class="section-card service-time-card"> |
| | | <view class="section-head"> |
| | | <text class="section-title">服务时效</text> |
| | | <text class="section-desc">(必选)</text> |
| | |
| | | v-for="item in serviceTimes" |
| | | :key="item.id" |
| | | class="service-time-item" |
| | | :class="{ active: selectedServiceTime === item.id }" |
| | | @tap="selectedServiceTime = item.id" |
| | | :class="{ active: isUrgent === item.id }" |
| | | @tap="isUrgent = item.id" |
| | | > |
| | | <text class="service-time-name"> |
| | | <text>标速达</text> |
| | | <text>(预计6小时内送达)</text> |
| | | <text>{{ item.name }}</text> |
| | | <text>(预计{{ item.serviceTime }}小时内送达)</text> |
| | | </text> |
| | | <text class="service-time-price">{{ item.price }}</text> |
| | | <text class="service-time-price">¥{{ item.price }}</text> |
| | | </view> |
| | | </view> |
| | | |
| | |
| | | <text class="unit-text">元</text> |
| | | </view> |
| | | </view> |
| | | <view v-if="insuranceFeeText" class="insurance-tip-row"> |
| | | <view v-if="amountData" class="insurance-tip-row"> |
| | | <text class="insurance-tip-label">物品保费:</text> |
| | | <text class="insurance-tip-value">{{ insuranceFeeText }}</text> |
| | | <text class="insurance-tip-value">¥{{ amountData.insuranceFee }}</text> |
| | | </view> |
| | | </view> |
| | | |
| | |
| | | <u-popup :show="showStorePopup" mode="bottom" round="24" :closeOnClickOverlay="true" @close="showStorePopup = false"> |
| | | <view class="store-popup-wrap"> |
| | | <view class="store-popup-head"> |
| | | <text class="store-popup-title">选择服务点</text> |
| | | <text class="store-popup-title">{{ storePopupType === 'receive' ? '选择取件服务点' : (activeMode === 'city' ? '选择寄件服务点' : '选择服务点') }}</text> |
| | | <view class="store-popup-close" @tap="showStorePopup = false"> |
| | | <u-icon name="close" size="28" color="#999999"></u-icon> |
| | | </view> |
| | |
| | | <text class="amount-row-value">{{ '¥' + item.unitPrice }}</text> |
| | | </view> |
| | | </view> |
| | | <view class="agreement-bar popup-agreement-bar"> |
| | | <image class="agreement-icon" src="/static/icon/ic_accept_sel@2x.png" mode="widthFix"></image> |
| | | <view class="agreement-bar popup-agreement-bar" @click="toggleAgreement"> |
| | | <image class="agreement-icon" :src="agreementChecked ? '/static/icon/ic_accept_sel@2x.png' : '/static/icon/ic_accept@2x.png'" mode="widthFix" /> |
| | | <text class="agreement-text">我已阅读并同意</text> |
| | | <text class="agreement-link">《用户服务协议》</text> |
| | | <text class="agreement-link" @tap="goRichText('protocol')">《用户服务协议》</text> |
| | | <text class="agreement-text">及</text> |
| | | <text class="agreement-link">《隐私政策》</text> |
| | | <text class="agreement-link" @tap="goRichText('privacy')">《隐私政策》</text> |
| | | </view> |
| | | <view class="bottom-action-row popup-action-row"> |
| | | <view class="total-wrap"> |
| | | <text class="total-label">总费用</text> |
| | | <text class="total-price">{{ amountData ? '¥' + amountData.totalPrice : '¥--' }}</text> |
| | | <text class="detail-text">明细</text> |
| | | <u-icon name="arrow-up" size="13" color="#7B7F86"></u-icon> |
| | | <view @click="showAmountPopup = false"> |
| | | <text class="detail-text">明细</text> |
| | | <u-icon name="arrow-up" size="13" color="#7B7F86"></u-icon> |
| | | </view> |
| | | </view> |
| | | <view class="submit-btn active-submit-btn" @click="createOrder">立即下单</view> |
| | | </view> |
| | |
| | | </u-popup> |
| | | |
| | | <view class="bottom-bar"> |
| | | <view class="agreement-bar"> |
| | | <image class="agreement-icon" :src="agreementChecked ? '/static/icon/ic_accept_sel@2x.png' : '/static/icon/ic_accept@2x.png'" mode="widthFix"></image> |
| | | <view class="agreement-bar" @click="toggleAgreement"> |
| | | <image class="agreement-icon" :src="agreementChecked ? '/static/icon/ic_accept_sel@2x.png' : '/static/icon/ic_accept@2x.png'" mode="widthFix" /> |
| | | <text class="agreement-text">我已阅读并同意</text> |
| | | <text class="agreement-link">《用户服务协议》</text> |
| | | <text class="agreement-link" @tap="goRichText('protocol')">《用户服务协议》</text> |
| | | <text class="agreement-text">及</text> |
| | | <text class="agreement-link">《隐私政策》</text> |
| | | <text class="agreement-link" @tap="goRichText('privacy')">《隐私政策》</text> |
| | | </view> |
| | | <view class="bottom-action-row"> |
| | | <view class="total-wrap"> |
| | | <text class="total-label">总费用</text> |
| | | <text class="total-price">{{ amountData ? '¥' + amountData.totalPrice : '¥--' }}</text> |
| | | <text class="detail-text detail-click" @tap.stop="openAmountPopup">明细</text> |
| | | <u-icon name="arrow-down" size="18" color="#999999"></u-icon> |
| | | <template v-if="amountData"> |
| | | <text class="detail-text detail-click" @tap.stop="openAmountPopup">明细</text> |
| | | <u-icon name="arrow-down" size="18" color="#999999"></u-icon> |
| | | </template> |
| | | </view> |
| | | <view class="submit-btn active-submit-btn" @click="createOrder">立即下单</view> |
| | | </view> |
| | | </view> |
| | | <!-- 选择服务点/地址 --> |
| | | <u-action-sheet |
| | | :show="showReceiveAddress" |
| | | @close="showReceiveAddress = false" |
| | | @select="caozuo" |
| | | :actions="actions" |
| | | :round="15" |
| | | cancelText="取消" /> |
| | | </view> |
| | | </template> |
| | | |
| | |
| | | tempSelectedGoodsIds: [1], |
| | | selectedGoodsIds: [1], |
| | | selectedLuggageId: 1, |
| | | selectedServiceTime: 1, |
| | | isUrgent: 0, |
| | | form: { |
| | | receiver: '', |
| | | mobile: '', |
| | |
| | | }, |
| | | amountData: null, |
| | | luggageTypes: [], |
| | | serviceTimes: [ |
| | | { id: 1, name: '标准达(预计6小时内送达)', price: '¥50' }, |
| | | { id: 2, name: '急速达(预计4小时内送达)', price: '¥70' } |
| | | ], |
| | | serviceTimes: [], |
| | | |
| | | storeList: [], |
| | | selectedStore: null, |
| | | sendStore: null, |
| | | receiveStore: null, |
| | | receiveAddr: null, |
| | | storePopupType: 'send', |
| | | storeForm: { |
| | | keyword: '', |
| | | page: 1, |
| | |
| | | }, |
| | | |
| | | goodsOptions: [], |
| | | uploadedImages: [] |
| | | uploadedImages: [], |
| | | |
| | | showReceiveAddress: false, |
| | | actions: [ |
| | | { name: '选择服务点' }, |
| | | { name: '选择地址簿' } |
| | | ] |
| | | } |
| | | }, |
| | | watch: { |
| | | 'form.insurance': { |
| | | handler() { |
| | | this.calculateLocalPrice() |
| | | } |
| | | }, |
| | | isUrgent: { |
| | | handler() { |
| | | if (this.activeMode === 'city') { |
| | | this.calculateRemotePrice() |
| | | } |
| | | } |
| | | } |
| | | }, |
| | |
| | | .map(item => item.name) |
| | | return labels.join('、') |
| | | }, |
| | | insuranceFeeText() { |
| | | if (!this.form.insurance) { |
| | | return '' |
| | | } |
| | | return '+10' |
| | | }, |
| | | totalPriceText() { |
| | | return '¥150.00' |
| | | } |
| | |
| | | this.getNearbyShopList() |
| | | this.getCategoryList() |
| | | this.getCitySizeList() |
| | | |
| | | uni.$on('updateAddress', (data) => { |
| | | console.log(data) |
| | | this.receiveAddr = data |
| | | }) |
| | | }, |
| | | methods: { |
| | | caozuo(e) { |
| | | var that = this; |
| | | if (e.name === '选择服务点') { |
| | | that.storePopupType = 'receive' |
| | | that.receiveStore = null |
| | | that.showStorePopup = true |
| | | } else if (e.name === '选择地址簿') { |
| | | that.receiveAddr = null |
| | | uni.navigateTo({ |
| | | url: '/pages/address/address?type=1' |
| | | }) |
| | | } |
| | | that.showReceiveAddress = false |
| | | }, |
| | | async uploadFiles(filePaths, maxCount = 9) { |
| | | if (!filePaths || filePaths.length === 0) { |
| | | return [] |
| | |
| | | }, |
| | | switchMode(mode) { |
| | | this.activeMode = mode |
| | | this.selectedStore = null |
| | | this.sendStore = null |
| | | this.receiveStore = null |
| | | this.receiveAddr = null |
| | | this.form.receiver = '' |
| | | this.form.mobile = '' |
| | | this.form.arriveTime = '' |
| | | this.form.pickupTime = '' |
| | | this.form.goodType = '' |
| | | this.form.goodTypeName = '' |
| | | this.form.insurance = '' |
| | | this.form.remark = '' |
| | | this.form.goodsImages = [] |
| | | this.amountData = null |
| | | this.uploadedImages = [] |
| | | this.luggageTypes.forEach(item => { |
| | | item.count = 0 |
| | | }) |
| | | }, |
| | | toggleAgreement() { |
| | | this.agreementChecked = !this.agreementChecked |
| | | }, |
| | | goRichText(type) { |
| | | uni.navigateTo({ |
| | | url: '/pages/rich-text/rich-text?type=' + type |
| | | }) |
| | | }, |
| | | openReceiveAddress() { |
| | | uni.showToast({ |
| | | title: '取件地址待接入', |
| | | icon: 'none' |
| | | }) |
| | | this.showReceiveAddress = true |
| | | }, |
| | | openAmountPopup() { |
| | | this.showAmountPopup = true |
| | | }, |
| | | openStorePopup() { |
| | | this.storePopupType = 'send' |
| | | this.tempSelectedStoreId = null |
| | | this.showStorePopup = true |
| | | }, |
| | | confirmStore() { |
| | | this.selectedStore = this.storeList.find(item => item.active) |
| | | const selected = this.storeList.find(item => item.active) |
| | | if (this.storePopupType === 'send') { |
| | | this.sendStore = selected |
| | | } else if (this.storePopupType === 'receive') { |
| | | this.receiveStore = selected |
| | | this.receiveAddr = null |
| | | this.calculateRemotePrice() |
| | | } else { |
| | | this.selectedStore = selected |
| | | this.calculateLocalPrice() |
| | | } |
| | | this.showStorePopup = false |
| | | this.calculateLocalPrice() |
| | | }, |
| | | confirmGoods() { |
| | | if (!this.goodsOptions.find(item => item.active)) { |
| | | uni.showToast({ title: '请选择物品信息', icon: 'none' }) |
| | | return |
| | | } |
| | | this.form.goodTypeName = this.goodsOptions.find(item => item.active)?.name || '' |
| | | this.form.goodType = this.goodsOptions.find(item => item.active)?.id || '' |
| | | this.showGoodsPopup = false |
| | |
| | | const day = String(date.getDate()).padStart(2, '0') |
| | | const hour = String(date.getHours()).padStart(2, '0') |
| | | const minute = String(date.getMinutes()).padStart(2, '0') |
| | | this.form.pickupTime = `${year}-${month}-${day} ${hour}:${minute}` |
| | | const pickupTime = `${year}-${month}-${day} ${hour}:${minute}` |
| | | if (this.form.arriveTime && new Date(pickupTime) <= new Date(this.form.arriveTime)) { |
| | | uni.showToast({ title: '预计取件时间必须大于预计到店时间', icon: 'none' }) |
| | | return |
| | | } |
| | | this.form.pickupTime = pickupTime |
| | | this.showPickupTimePicker = false |
| | | this.calculateLocalPrice() |
| | | }, |
| | |
| | | this.calculateLocalPrice() |
| | | }, |
| | | async calculateLocalPrice() { |
| | | if (this.activeMode === 'city') { |
| | | this.calculateRemotePrice() |
| | | } else { |
| | | this.calculateLocalPriceOnly() |
| | | } |
| | | }, |
| | | async calculateLocalPriceOnly() { |
| | | if (!this.selectedStore || !this.form.arriveTime || !this.form.pickupTime) { |
| | | return |
| | | } |
| | |
| | | item.unitPrice = item.unitPrice / 100 |
| | | }) |
| | | res.data.totalPrice = res.data.totalPrice / 100 |
| | | res.data.insuranceFee = res.data.insuranceFee / 100 |
| | | this.amountData = res.data |
| | | } |
| | | }, |
| | | async calculateRemotePrice() { |
| | | if (!this.sendStore || !this.form.arriveTime || !this.form.pickupTime) { |
| | | return |
| | | } |
| | | if (!this.receiveStore && !this.receiveAddr) { |
| | | return |
| | | } |
| | | const luggageList = this.luggageTypes |
| | | .filter(item => item.count > 0) |
| | | .map(item => ({ |
| | | categoryId: item.id, |
| | | quantity: item.count |
| | | })) |
| | | if (luggageList.length === 0) { |
| | | return |
| | | } |
| | | let fromLat = '' |
| | | let fromLgt = '' |
| | | let toLat = '' |
| | | let toLgt = '' |
| | | if (this.sendStore) { |
| | | fromLat = this.sendStore.latitude |
| | | fromLgt = this.sendStore.longitude |
| | | } |
| | | if (this.receiveStore) { |
| | | toLat = this.receiveStore.latitude |
| | | toLgt = this.receiveStore.longitude |
| | | } else if (this.receiveAddr) { |
| | | toLat = this.receiveAddr.latitude |
| | | toLgt = this.receiveAddr.longitude |
| | | } |
| | | const res = await this.$u.api.calculateRemotePrice({ |
| | | cityId: this.cityId, |
| | | fromLat: fromLat, |
| | | fromLgt: fromLgt, |
| | | toLat: toLat, |
| | | toLgt: toLgt, |
| | | urgent: this.isUrgent, |
| | | depositStartTime: this.form.arriveTime + ':00', |
| | | depositEndTime: this.form.pickupTime + ':00', |
| | | items: luggageList, |
| | | declaredAmount: this.form.insurance || 0 |
| | | }) |
| | | if (res.code === 200) { |
| | | res.data.itemList.forEach(item => { |
| | | item.unitPrice = item.unitPrice / 100 |
| | | }) |
| | | res.data.totalPrice = res.data.totalPrice / 100 |
| | | res.data.insuranceFee = res.data.insuranceFee / 100 |
| | | this.serviceTimes = [ |
| | | { id: 0, name: '标准达', serviceTime: res.data.standardHours, price: res.data.itemPrice / 100 }, |
| | | { id: 1, name: '急速达', serviceTime: res.data.urgentHours, price: (res.data.urgentFee + res.data.itemPrice) / 100 } |
| | | ] |
| | | this.amountData = res.data |
| | | } |
| | | }, |
| | | async createOrder() { |
| | | if (!this.selectedStore) { |
| | | uni.showToast({ title: '请选择门店', icon: 'none' }) |
| | | if (!this.agreementChecked) { |
| | | uni.showToast({ title: '请先阅读并同意用户服务协议及隐私政策', icon: 'none' }) |
| | | return |
| | | } |
| | | if (this.activeMode === 'local') { |
| | | if (!this.selectedStore) { |
| | | uni.showToast({ title: '请选择门店', icon: 'none' }) |
| | | return |
| | | } |
| | | } else { |
| | | if (!this.sendStore) { |
| | | uni.showToast({ title: '请选择寄件服务点', icon: 'none' }) |
| | | return |
| | | } |
| | | if (!this.receiveStore && !this.receiveAddr) { |
| | | uni.showToast({ title: '请选择取件地址', icon: 'none' }) |
| | | return |
| | | } |
| | | } |
| | | if (!this.form.arriveTime) { |
| | | uni.showToast({ title: '请选择预计到店时间', icon: 'none' }) |
| | |
| | | } |
| | | if (!this.form.pickupTime) { |
| | | uni.showToast({ title: '请选择预计取件时间', icon: 'none' }) |
| | | return |
| | | } |
| | | if (new Date(this.form.pickupTime) <= new Date(this.form.arriveTime)) { |
| | | uni.showToast({ title: '预计取件时间必须大于预计到店时间', icon: 'none' }) |
| | | return |
| | | } |
| | | const luggageList = this.luggageTypes |
| | |
| | | categoryId: item.categoryId, |
| | | quantity: item.quantity |
| | | })) |
| | | const res = await this.$u.api.createOrder({ |
| | | let orderParams = { |
| | | cityId: this.cityId, |
| | | declaredAmount: this.form.insurance || 0, |
| | | depositShopId: this.selectedStore.id, |
| | | expectedDepositTime: this.form.arriveTime + ':00', |
| | | expectedTakeTime: this.form.pickupTime + ':00', |
| | | goodType: this.form.goodType, |
| | |
| | | remark: this.form.remark, |
| | | takePhone: this.form.mobile, |
| | | takeUser: this.form.receiver, |
| | | type: this.activeMode === 'local' ? 0 : 1 |
| | | }) |
| | | type: this.activeMode === 'local' ? 0 : 1, |
| | | isUrgent: this.isUrgent |
| | | } |
| | | if (this.activeMode === 'local') { |
| | | orderParams.depositShopId = this.selectedStore.id |
| | | } else { |
| | | orderParams.depositShopId = this.sendStore.id |
| | | orderParams.fromShopId = this.sendStore.id |
| | | if (this.receiveStore) { |
| | | orderParams.toType = 0 |
| | | orderParams.toShopId = this.receiveStore.id |
| | | orderParams.takeLat = this.receiveStore.latitude |
| | | orderParams.takeLgt = this.receiveStore.longitude |
| | | orderParams.takeLocation = this.receiveStore.address |
| | | } else if (this.receiveAddr) { |
| | | orderParams.toType = 1 |
| | | orderParams.toAddrId = this.receiveAddr.id |
| | | orderParams.takeLat = this.receiveAddr.latitude |
| | | orderParams.takeLgt = this.receiveAddr.longitude |
| | | orderParams.takeLocation = this.receiveAddr.addr |
| | | } |
| | | } |
| | | const res = await this.$u.api.createOrder(orderParams) |
| | | if (res.code === 200) { |
| | | uni.showToast({ title: '下单成功', icon: 'success' }) |
| | | if (res.data) { |
| | | this.processPayment(res.data.response) |
| | | this.processPayment(res.data.response, res.data.orderId) |
| | | } |
| | | } |
| | | }, |
| | | processPayment(paymentData) { |
| | | processPayment(paymentData, orderId) { |
| | | uni.requestPayment({ |
| | | provider: 'wxpay', |
| | | timeStamp: paymentData.timeStamp || '', |
| | | nonceStr: paymentData.nonceStr || '', |
| | | package: paymentData.packageValue || '', |
| | | package: paymentData.package || '', |
| | | signType: paymentData.signType || 'MD5', |
| | | paySign: paymentData.paySign || '', |
| | | success: (res) => { |
| | | uni.showToast({ title: '支付成功', icon: 'success' }) |
| | | uni.navigateTo({ |
| | | url: '/pages/payment-success/payment-success?orderId=' + orderId |
| | | }); |
| | | }, |
| | | fail: (err) => { |
| | | if (err.errMsg.includes('cancel')) { |
| | |
| | | flex-direction: column; |
| | | justify-content: center; |
| | | } |
| | | |
| | | .store-cell-copy-addr { |
| | | |
| | | .store-cell-copy-addr1 { |
| | | display: flex; |
| | | align-items: baseline; |
| | | image { |
| | |
| | | margin-right: 4rpx; |
| | | } |
| | | } |
| | | |
| | | .store-cell-copy-addr { |
| | | display: flex; |
| | | flex-direction: column; |
| | | .store-cell-subtitle-container { |
| | | display: flex; |
| | | align-items: center; |
| | | image { |
| | | width: 24rpx; |
| | | height: 24rpx; |
| | | margin-right: 4rpx; |
| | | } |
| | | } |
| | | } |
| | | |
| | | .with-icon .cell-icon { |
| | | width: 40rpx; |