From e829ed74532dc328224e01246b0095a1f77f8162 Mon Sep 17 00:00:00 2001 From: wangfukang <15630117759@163.com> Date: Sun, 20 Sep 2026 11:05:06 +0800 Subject: [PATCH] 1 --- package1/address/addAddress.vue | 373 ++++++++++++++++++++++++++++++-- package1/buyFood/buyFood.vue | 89 +++++++- 2 files changed, 445 insertions(+), 17 deletions(-) diff --git a/package1/address/addAddress.vue b/package1/address/addAddress.vue index 4f1e632..1179c82 100644 --- a/package1/address/addAddress.vue +++ b/package1/address/addAddress.vue @@ -11,22 +11,29 @@ - + 楼座区域 {{areaTitleInput || (areaListLoading ? '加载中...' : '选择收货地址')}} - + + 收货地址 + + {{addressForm.areaName || '搜索/定位选择地址'}} + + + + 楼层 - 门牌号 + {{isSocialArea ? '门牌号' : '门牌号'}} + @input="onRoomNumInput" :placeholder="isSocialArea ? '例:3号楼2单元301室' : '例:5号楼203室'" placeholder-class="form-placeholder" /> 设为默认地址 @@ -57,6 +64,21 @@ + + + 隐私授权提示 + + 选择收货地址需要使用定位能力。请先阅读并同意 + {{privacyContractName}} + 后继续。 + + + + + + + @@ -70,11 +92,17 @@ areaTitleInput: '', areaSearchInput: '', addressSubmitting: false, + showPrivacyModal: false, + privacyContractName: '《用户隐私保护指引》', + pendingPrivacyResolve: null, + pendingPrivacyReject: null, addressForm: { id: '', regionId: '', areaId: '', areaName: '', + addressLng: '', + addressLat: '', floor: '', roomNum: '', receiverName: '', @@ -84,6 +112,10 @@ } }, computed: { + isSocialArea() { + const area = this.getCurrentArea(); + return area && Number(area.areaKind) === 2; + }, filteredAreaList() { const keyword = (this.areaSearchInput || '').toString(); if (!keyword) return this.areaList; @@ -100,17 +132,25 @@ this.addressForm.id = id; this.initEditAddress(id); } - this.getAreaList().then(() => { - this.syncAreaTitle(); - }); + if (!this.isSocialArea) { + this.getAreaList().then(() => { + this.syncAreaTitle(); + }); + } }, methods: { - getCurrentRegionId() { + getCurrentArea() { try { const area = uni.getStorageSync('area'); - if (!area) return ''; - if (typeof area === 'object') return area.id ? String(area.id) : ''; - const areaInfo = JSON.parse(area); + if (!area) return null; + return typeof area === 'object' ? area : JSON.parse(area); + } catch (e) { + return null; + } + }, + getCurrentRegionId() { + try { + const areaInfo = this.getCurrentArea(); return areaInfo.id ? String(areaInfo.id) : ''; } catch (e) { return ''; @@ -136,6 +176,8 @@ regionId: item.regionId || this.getCurrentRegionId(), areaId: item.areaId || '', areaName: item.areaName || '', + addressLng: item.addressLng || '', + addressLat: item.addressLat || '', floor: item.floor || '', roomNum: item.roomNum || '', receiverName: item.receiverName || '', @@ -143,7 +185,7 @@ isDefault: item.isDefault || 0 }; this.areaTitleInput = item.areaName || ''; - this.syncAreaTitle(); + if (!this.isSocialArea) this.syncAreaTitle(); }, getAreaList() { if (this.areaListLoading) return Promise.resolve(this.areaList); @@ -166,6 +208,7 @@ if (match) this.areaTitleInput = match.title; }, openAreaPopup() { + if (this.isSocialArea) return; this.areaSearchInput = ''; this.getAreaList(); this.$refs.areaPopup.open(); @@ -180,6 +223,220 @@ this.areaTitleInput = item.title; this.closeAreaPopup(); }, + openLocationPicker() { + const openPicker = (option) => { + this.ensurePrivacyAuthorization().then(() => { + console.log('chooseLocation 入参:', option || {}); + uni.chooseLocation({ + ...(option || {}), + success: (res) => { + console.log('chooseLocation 成功:', res); + const name = (res.name || '').trim(); + const address = (res.address || '').trim(); + if (!name && !address) { + this.tui.toast('请选择有效地址'); + return; + } + this.applySelectedLocation({ + name: address && name && address.indexOf(name) === -1 ? address + name : (name || address), + latitude: res.latitude, + longitude: res.longitude + }); + }, + fail: (err) => { + console.log('chooseLocation 失败:', err); + const errMsg = err && err.errMsg ? String(err.errMsg) : ''; + if (errMsg.indexOf('cancel') > -1) { + return; + } + this.showLocationFailTip(err, '打开位置选择失败,可重新进入页面后再试'); + } + }); + }).catch(() => {}); + }; + const lat = this.toLocationNumber(this.addressForm.addressLat); + const lng = this.toLocationNumber(this.addressForm.addressLng); + if (this.isValidLocation(lat, lng)) { + openPicker({ + latitude: lat, + longitude: lng + }); + return; + } + this.ensurePrivacyAuthorization().then(() => this.getPickerCurrentLocation()).then((location) => { + openPicker(location); + }).catch(() => {}); + }, + applySelectedLocation(location) { + if (!location) return; + this.validateSocialAddressLocation(location.latitude, location.longitude).then(() => { + this.addressForm.areaId = ''; + this.addressForm.areaName = location.name || '当前位置'; + this.addressForm.addressLng = location.longitude; + this.addressForm.addressLat = location.latitude; + this.areaTitleInput = this.addressForm.areaName; + }).catch(() => {}); + }, + toLocationNumber(value) { + if (value === '' || value === null || value === undefined) return null; + const numberValue = Number(value); + return Number.isFinite(numberValue) ? numberValue : null; + }, + isValidLocation(latitude, longitude) { + const lat = this.toLocationNumber(latitude); + const lng = this.toLocationNumber(longitude); + return lat !== null && lng !== null && + lat >= -90 && lat <= 90 && + lng >= -180 && lng <= 180 && + !(lat === 0 && lng === 0); + }, + ensurePrivacyAuthorization() { + return new Promise((resolve, reject) => { + // #ifdef MP-WEIXIN + if (typeof wx !== 'undefined' && wx.getPrivacySetting) { + wx.getPrivacySetting({ + success: (res) => { + console.log('getPrivacySetting:', res); + if (!res || !res.needAuthorization) { + resolve(); + return; + } + this.privacyContractName = res.privacyContractName || '《用户隐私保护指引》'; + this.pendingPrivacyResolve = resolve; + this.pendingPrivacyReject = reject; + this.showPrivacyModal = true; + }, + fail: (err) => { + console.log('getPrivacySetting 失败:', err); + resolve(); + } + }); + return; + } + // #endif + resolve(); + }); + }, + openPrivacyContract() { + // #ifdef MP-WEIXIN + if (typeof wx !== 'undefined' && wx.openPrivacyContract) { + wx.openPrivacyContract({ + fail: () => { + this.tui.toast('打开隐私协议失败'); + } + }); + return; + } + // #endif + this.tui.toast('当前微信版本暂不支持查看'); + }, + handleAgreePrivacyAuthorization() { + this.showPrivacyModal = false; + const resolve = this.pendingPrivacyResolve; + this.pendingPrivacyResolve = null; + this.pendingPrivacyReject = null; + if (typeof resolve === 'function') resolve(); + }, + rejectPrivacyAuthorization() { + this.showPrivacyModal = false; + const reject = this.pendingPrivacyReject; + this.pendingPrivacyResolve = null; + this.pendingPrivacyReject = null; + this.tui.toast('请先同意隐私协议后再定位'); + if (typeof reject === 'function') reject(); + }, + getPickerCurrentLocation() { + return new Promise((resolve, reject) => { + uni.getLocation({ + type: 'gcj02', + isHighAccuracy: true, + highAccuracyExpireTime: 3000, + success: (res) => { + console.log('getLocation 成功:', res); + if (this.isValidLocation(res.latitude, res.longitude)) { + resolve({ + latitude: res.latitude, + longitude: res.longitude + }); + } else { + console.log('getLocation 返回无效坐标:', res); + resolve({}); + } + }, + fail: (err) => { + console.log('getLocation 失败:', err); + const errMsg = (err && err.errMsg) || ''; + const denied = errMsg.indexOf('auth deny') > -1 || + errMsg.indexOf('authorize') > -1 || + errMsg.indexOf('denied') > -1 || + errMsg.indexOf('permission') > -1; + if (!denied) { + this.showLocationFailTip(err, '定位失败,可手动搜索地址'); + resolve({}); + return; + } + uni.showModal({ + title: '需要定位权限', + content: '请选择允许定位后再选择收货地址', + confirmText: '去设置', + success: (modalRes) => { + if (modalRes.confirm) { + uni.openSetting({ + complete: () => reject(err) + }); + } else { + reject(err); + } + }, + fail: () => reject(err) + }); + } + }); + }); + }, + showLocationFailTip(err, fallbackText) { + const errMsg = err && err.errMsg ? String(err.errMsg) : ''; + if (errMsg) { + console.log('location fail:', errMsg); + } + if (errMsg.indexOf('privacy') > -1) { + this.tui.toast('请先同意隐私协议后再定位'); + return; + } + this.tui.toast(fallbackText || '定位失败,请重试'); + }, + validateSocialAddressLocation(lat, lng) { + return new Promise((resolve, reject) => { + const currentRegionId = this.getCurrentRegionId(); + const latitude = this.toLocationNumber(lat); + const longitude = this.toLocationNumber(lng); + if (!currentRegionId) { + this.tui.toast('请先选择区域'); + reject(); + return; + } + if (!this.isValidLocation(latitude, longitude)) { + this.tui.toast('地址坐标不合法,请重新选择'); + reject(); + return; + } + this.tui.request('/app/shopArea/matchByLocation', 'GET', { + lat: latitude, + lng: longitude + }, false, true).then((res) => { + const matched = res && res.code == 200 && res.result && res.result.id; + if (matched && String(res.result.id) === String(currentRegionId)) { + resolve(); + } else { + this.tui.toast('该地址不在当前区域配送范围内'); + reject(); + } + }).catch(() => { + this.tui.toast('地址范围校验失败,请稍后重试'); + reject(); + }); + }); + }, onFloorInput(e) { const value = (e.detail.value || '').toString(); const hasMinus = value.charAt(0) === '-'; @@ -205,13 +462,20 @@ }, submitAddress() { if (this.addressSubmitting) return; - if (!this.addressForm.areaId) return this.tui.toast('请选择楼座区域'); + const isSocial = this.isSocialArea; + if (!isSocial && !this.addressForm.areaId) return this.tui.toast('请选择楼座区域'); + if (isSocial && !this.addressForm.areaName) return this.tui.toast('请选择收货地址'); + if (isSocial && (!this.addressForm.addressLng || !this.addressForm.addressLat)) return this.tui.toast('请选择带定位的收货地址'); this.addressForm.floor = (this.addressForm.floor || '').toString(); this.addressForm.roomNum = (this.addressForm.roomNum || '').toString().trim(); this.addressForm.receiverName = (this.addressForm.receiverName || '').toString().trim(); this.addressForm.receiverPhone = (this.addressForm.receiverPhone || '').toString().replace(/\D/g, ''); - if (!this.addressForm.floor) return this.tui.toast('请填写所在楼层,楼层必须为整数数字'); - if (!/^-?\d+$/.test(this.addressForm.floor)) return this.tui.toast('楼层必须为整数'); + if (!isSocial && !this.addressForm.floor) return this.tui.toast('请填写所在楼层,楼层必须为整数数字'); + if (!isSocial && !/^-?\d+$/.test(this.addressForm.floor)) return this.tui.toast('楼层必须为整数'); + if (isSocial) { + this.addressForm.floor = ''; + this.addressForm.areaId = ''; + } if (!this.addressForm.roomNum) return this.tui.toast('请填写详细地址门牌号最多30字'); if (this.addressForm.roomNum.length > 30) return this.tui.toast('详细地址最多30字'); if (!this.addressForm.receiverName) return this.tui.toast('请填写收件人最多20字'); @@ -221,6 +485,15 @@ this.addressForm.userId = uni.getStorageSync('id'); this.addressForm.regionId = this.getCurrentRegionId(); if (!this.addressForm.regionId) return this.tui.toast('请先选择区域'); + if (isSocial) { + this.validateSocialAddressLocation(this.addressForm.addressLat, this.addressForm.addressLng) + .then(() => this.doSubmitAddress()) + .catch(() => {}); + return; + } + this.doSubmitAddress(); + }, + doSubmitAddress() { const url = this.addressForm.id ? "/app/userAddress/edit" : "/app/userAddress/save"; this.addressSubmitting = true; this.tui.request(url, "POST", this.addressForm, false, true).then((res) => { @@ -377,4 +650,74 @@ color: #999; text-align: center; } + + .privacy-mask { + position: fixed; + left: 0; + right: 0; + top: 0; + bottom: 0; + z-index: 9999; + display: flex; + align-items: center; + justify-content: center; + padding: 0 48rpx; + background: rgba(0, 0, 0, 0.45); + } + + .privacy-dialog { + width: 100%; + padding: 42rpx 34rpx 30rpx; + border-radius: 24rpx; + background: #fff; + box-sizing: border-box; + } + + .privacy-title { + font-size: 34rpx; + font-weight: 700; + color: #222; + text-align: center; + } + + .privacy-content { + margin-top: 28rpx; + font-size: 28rpx; + line-height: 1.7; + color: #555; + } + + .privacy-link { + color: #10b981; + } + + .privacy-actions { + display: flex; + gap: 20rpx; + margin-top: 36rpx; + } + + .privacy-btn { + flex: 1; + height: 80rpx; + line-height: 80rpx; + margin: 0; + padding: 0; + border-radius: 40rpx; + font-size: 28rpx; + } + + .privacy-btn::after { + border: 0; + } + + .privacy-btn--cancel { + color: #666; + background: #f5f5f5; + } + + .privacy-btn--agree { + color: #fff; + background: #10b981; + } \ No newline at end of file diff --git a/package1/buyFood/buyFood.vue b/package1/buyFood/buyFood.vue index 7a5bcaa..ba3da25 100644 --- a/package1/buyFood/buyFood.vue +++ b/package1/buyFood/buyFood.vue @@ -349,7 +349,7 @@ - {{isStoreGroupOrder ? '立即支付-到店核销' : ('立即支付' + (nowMake && isPaotui == false ? '-即刻出餐' : ''))}} {{totalAmountCalc.toFixed(2)}} + {{bottomPayButtonText}} @@ -808,6 +808,27 @@ }, minimumCommissionText() { return this.formatAmountText(this.getMinimumCommissionAmount()); + }, + startingPriceAmount() { + const amount = parseFloat(this.shopItem.startingPrice); + return isNaN(amount) ? 0 : amount; + }, + shouldCheckStartingPrice() { + return this.isPaotui && !this.isStoreGroupOrder && this.startingPriceAmount > 0; + }, + startingPriceCheckAmount() { + return this.goodsAmountCalc + this.packageFee; + }, + startingPriceMissingAmount() { + const amount = this.startingPriceAmount - this.startingPriceCheckAmount; + return amount > 0 ? amount : 0; + }, + bottomPayButtonText() { + if (this.shouldCheckStartingPrice && this.startingPriceMissingAmount > 0) { + return `外卖差${this.formatAmountText(this.startingPriceMissingAmount)}元起送`; + } + const prefix = this.isStoreGroupOrder ? '立即支付-到店核销' : ('立即支付' + (this.nowMake && this.isPaotui == false ? '-即刻出餐' : '')); + return `${prefix} ${this.totalAmountCalc.toFixed(2)}`; } }, watch: {}, @@ -945,6 +966,35 @@ if (isNaN(amount)) return '0'; return amount.toFixed(2).replace(/\.?0+$/, ''); }, + validateStartingPrice() { + if (!this.shouldCheckStartingPrice) return true; + if (this.startingPriceCheckAmount + 0.0001 >= this.startingPriceAmount) return true; + uni.showToast({ + title: `不满${this.formatAmountText(this.startingPriceAmount)}元起送条件`, + icon: 'none' + }); + setTimeout(() => { + this.backToGroupBuySingle(); + }, 1200); + return false; + }, + backToGroupBuySingle() { + const pages = typeof getCurrentPages === 'function' ? getCurrentPages() : []; + if (pages.length > 1) { + uni.navigateBack({ + delta: 1, + fail: () => this.redirectToGroupBuySingle() + }); + return; + } + this.redirectToGroupBuySingle(); + }, + redirectToGroupBuySingle() { + uni.redirectTo({ + url: '/package2/group/groupBuySingle?type=shop&item=' + + encodeURIComponent(JSON.stringify(this.shopItem || {})) + }); + }, syncAreaKind(areaObj) { let area = areaObj; if (!area) { @@ -1202,6 +1252,35 @@ }).catch(() => {}); } }, + ensureSocialAddressInCurrentRegion() { + if (!this.isSocialArea || !this.isPaotui) return Promise.resolve(true); + const address = this.formData.address || {}; + const lat = Number(address.addressLat); + const lng = Number(address.addressLng); + if (!Number.isFinite(lat) || !Number.isFinite(lng)) { + this.tui.toast('请选择带定位的收货地址'); + return Promise.resolve(false); + } + const regionId = this.getCurrentRegionId(); + if (!regionId) { + this.tui.toast('请先选择区域'); + return Promise.resolve(false); + } + return this.tui.request('/app/shopArea/matchByLocation', 'GET', { + lat, + lng + }, false, true).then((res) => { + const matched = res && res.code == 200 && res.result && res.result.id; + if (matched && String(res.result.id) === String(regionId)) { + return true; + } + this.tui.toast('收货地址不在当前区域配送范围内'); + return false; + }).catch(() => { + this.tui.toast('地址范围校验失败,请稍后重试'); + return false; + }); + }, handleSelectAddress(address) { this.formData.address = address; uni.setStorageSync('selectedAddress', address); @@ -1239,6 +1318,9 @@ if (this.isCreatingOrder) return; let isJoiningFaceToFace = this.isGroupBuy && this.groupItem && this.groupItem.groupId && this.groupItem .isFaceToFace; + if (!this.validateStartingPrice()) { + return; + } if (this.isPaotui && !(await this.ensureDeliveryAvailable())) { return; } @@ -1247,6 +1329,9 @@ this.$refs.warnPopup.open(); return; } + if (this.isPaotui && this.isSocialArea && !isJoiningFaceToFace && !(await this.ensureSocialAddressInCurrentRegion())) { + return; + } if (this.isSocialArea && this.isPaotui) { this.applySocialDeliveryDefaults(); @@ -1551,7 +1636,7 @@ if (deliveryType === 1 && !isJoiningFaceToFace) { payload.addressId = this.formData.address ? this.formData.address.id : null; payload.getAreaId = this.shopItem.shopArea || null; - payload.putAreaId = this.formData.address ? this.formData.address.areaId : null; + payload.putAreaId = this.isSocialArea ? null : (this.formData.address ? this.formData.address.areaId : null); if (this.shopItem.supportTransferDelivery == 1) { payload.transferDelivery = 1; payload.transferAddressId = this.shopItem.transferAddressId;