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;