Compare commits

...

8 Commits

Author SHA1 Message Date
wangfukang 4f735b58b3 1 2 days ago
wangfukang 67124e17ba 1 2 days ago
wangfukang 5864a94934 1 2 days ago
wangfukang 19787e19f6 1 2 days ago
wangfukang 5cdb8dcd33 1 2 days ago
wangfukang aed1d1c8db 1 2 days ago
wangfukang e829ed7453 1 2 days ago
wangfukang 83171d380f 1 2 days ago
  1. 4
      App.vue
  2. 31
      components/tab-bar/delivery.vue
  3. 1
      manifest.json
  4. 373
      package1/address/addAddress.vue
  5. 89
      package1/buyFood/buyFood.vue
  6. 16
      package2/group/groupBuyDetail.vue
  7. 127
      package2/group/groupBuyList.vue
  8. 467
      package2/group/groupBuySingle.vue
  9. 100
      package2/group/studentStoreList.vue
  10. 105
      package3/myCenter/problemFeedback.vue
  11. 127
      package3/shop/transferDeliveryOrderList.vue
  12. 65
      pages/index/index.vue
  13. 21
      utils/sharePath.js

4
App.vue

@ -1,11 +1,11 @@
<script>
import { getEnterShareTarget, resolveHomeShareTarget } from '@/utils/sharePath.js'
import { getEnterShareTarget, resolveHomeShareTarget, resolveMiniCodeSceneTarget } from '@/utils/sharePath.js'
export default {
onShow: function(options) {
const targetPath = getEnterShareTarget(options)
const targetPath = resolveMiniCodeSceneTarget(options && options.query && options.query.scene) || getEnterShareTarget(options)
if (targetPath) {
uni.setStorageSync('pendingShareTargetPath', targetPath)
} else if (resolveHomeShareTarget(options && options.query)) {

31
components/tab-bar/delivery.vue

@ -63,6 +63,9 @@
<uni-icons type="info" size="10"></uni-icons>
跑腿
</view>
<view class="transfer-delivery-btn" v-if="canTransferOrder" @tap="goTransferDeliveryOrders">
中转单
</view>
<view class="delivery-ranking-btn" @tap="goDeliveryRanking">
配送排行
</view>
@ -575,6 +578,10 @@
return '已上线,但还没有勾选参与派单的路线'
}
return '已准备好接收符合范围和佣金的订单'
},
canTransferOrder() {
const worker = uni.getStorageSync('worker')
return !!(worker && worker.canTransferOrder == 1)
}
},
filters: {
@ -762,6 +769,12 @@
url: '/package3/shop/ranking'
})
},
goTransferDeliveryOrders() {
if (!this.ensureWorkerRegistered()) return
uni.navigateTo({
url: '/package3/shop/transferDeliveryOrderList?role=worker'
})
},
goPartTimeRegister() {
if (!this.ensureLogin()) return
if (uni.getStorageSync('worker')) {
@ -1512,6 +1525,24 @@
flex-shrink: 0;
}
.transfer-delivery-btn {
height: 58rpx;
line-height: 58rpx;
padding: 0 22rpx;
border-radius: 999rpx;
background: linear-gradient(135deg, #fff2e9 0%, #ffd0a8 100%);
box-shadow: 0 10rpx 22rpx rgba(255, 128, 64, 0.14);
color: #9a4b13;
font-size: 22rpx;
font-weight: 900;
text-align: center;
flex-shrink: 0;
}
.transfer-delivery-btn:active {
transform: scale(0.96);
}
.delivery-ranking-btn:active {
transform: scale(0.96);
}

1
manifest.json

@ -84,6 +84,7 @@
"quickapp" : {},
"mp-weixin" : {
"appid" : "wx21a1caccef085db7",
"__usePrivacyCheck__" : true,
"setting" : {
"urlCheck" : false,
"postcss" : false,

373
package1/address/addAddress.vue

@ -11,22 +11,29 @@
<input class="form-input" type="number" maxlength="11" v-model="addressForm.receiverPhone"
@input="onReceiverPhoneInput" placeholder="手机号" placeholder-class="form-placeholder" />
</view>
<view class="form-row" @tap="openAreaPopup">
<view v-if="!isSocialArea" class="form-row" @tap="openAreaPopup">
<text class="form-label">楼座区域</text>
<text class="form-value" :class="{'form-placeholder-text': !areaTitleInput}">
{{areaTitleInput || (areaListLoading ? '加载中...' : '选择收货地址')}}
</text>
<uni-icons type="right" size="18" color="#999"></uni-icons>
</view>
<view class="form-row">
<view v-else class="form-row" @tap="openLocationPicker">
<text class="form-label">收货地址</text>
<text class="form-value" :class="{'form-placeholder-text': !addressForm.areaName}">
{{addressForm.areaName || '搜索/定位选择地址'}}
</text>
<uni-icons type="right" size="18" color="#999"></uni-icons>
</view>
<view v-if="!isSocialArea" class="form-row">
<text class="form-label">楼层</text>
<input class="form-input" type="number" v-model="addressForm.floor" @input="onFloorInput"
placeholder="所在楼层,仅限整数" placeholder-class="form-placeholder" />
</view>
<view class="form-row">
<text class="form-label">门牌号</text>
<text class="form-label">{{isSocialArea ? '门牌号' : '门牌号'}}</text>
<input class="form-input" type="text" maxlength="30" v-model="addressForm.roomNum"
@input="onRoomNumInput" placeholder="例:5号楼203室" placeholder-class="form-placeholder" />
@input="onRoomNumInput" :placeholder="isSocialArea ? '例:3号楼2单元301室' : '例:5号楼203室'" placeholder-class="form-placeholder" />
</view>
<view class="form-row form-row--switch">
<text class="form-label">设为默认地址</text>
@ -57,6 +64,21 @@
</scroll-view>
</view>
</uni-popup>
<view v-if="showPrivacyModal" class="privacy-mask">
<view class="privacy-dialog">
<view class="privacy-title">隐私授权提示</view>
<view class="privacy-content">
选择收货地址需要使用定位能力请先阅读并同意
<text class="privacy-link" @tap.stop="openPrivacyContract">{{privacyContractName}}</text>
后继续
</view>
<view class="privacy-actions">
<button class="privacy-btn privacy-btn--cancel" @tap="rejectPrivacyAuthorization">不同意</button>
<button id="agree-privacy-btn" class="privacy-btn privacy-btn--agree" open-type="agreePrivacyAuthorization"
@agreeprivacyauthorization="handleAgreePrivacyAuthorization">同意并继续</button>
</view>
</view>
</view>
</view>
</template>
@ -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;
}
</style>

89
package1/buyFood/buyFood.vue

@ -349,7 +349,7 @@
<view class="buy-bottom">
<view class="bottom-btn" @tap="submitPay">
<image class="pay-btn-gif" src="https://jewel-shop.oss-cn-beijing.aliyuncs.com/f7227759d693497a8beab87ff437a875.png" mode="aspectFit"></image>
<text>{{isStoreGroupOrder ? '立即支付-到店核销' : ('立即支付' + (nowMake && isPaotui == false ? '-即刻出餐' : ''))}} {{totalAmountCalc.toFixed(2)}}</text>
<text>{{bottomPayButtonText}}</text>
</view>
</view>
</view>
@ -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;

16
package2/group/groupBuyDetail.vue

@ -113,6 +113,22 @@
computed: {
businessTimeText() {
const takeaway = this.shopItem.shopTakeaway || {};
let list = takeaway.businessHourList;
if ((!list || !list.length) && takeaway.businessHours) {
try {
list = typeof takeaway.businessHours === 'string' ? JSON.parse(takeaway.businessHours) : takeaway.businessHours;
} catch (e) {
list = [];
}
}
if (list && list.length) {
const parts = list.filter(item => item && item.begin && item.end).map(item => {
const begin = String(item.begin).substring(0, 5);
const end = String(item.end).substring(0, 5);
return begin + '-' + end;
});
if (parts.length) return '周一到周日 ' + parts.join('、');
}
if (!takeaway.businessHourBegin || !takeaway.businessHourEnd) return '营业时间暂无';
return '周一到周日 ' + takeaway.businessHourBegin + '-' + takeaway.businessHourEnd;
},

127
package2/group/groupBuyList.vue

@ -108,6 +108,10 @@
<view class="shop-list">
<view class="shop-member" :class="{'shop-member-disabled': item.status != 1}" v-for="(item,index) in shopList" :key="item._renderKey" @tap="goDetail('shop',item)">
<view class="shop-disabled-mask" @tap.stop="buyingye" v-if="item.status != 1">
<view class="closed-badge">
<text class="closed-title">休息中</text>
<text class="closed-hours" v-if="getBusinessHoursText(item)">营业时间 {{getBusinessHoursText(item)}}</text>
</view>
</view>
<view class="shop-card-bg"></view>
<view class="shop-top">
@ -149,7 +153,10 @@
</view>
<view class="shop-content-bottom">
<view class="shop-deal">
<text v-if="item.subtitle != null">
<text v-if="hasStartingPrice(item)">
{{formatAmountText(item.startingPrice)}}元起送
</text>
<text v-else-if="item.subtitle != null">
{{item.subtitle}}
</text>
<text v-else>{{item._groupTip}}</text>
@ -795,6 +802,10 @@
if (isNaN(amount)) return '0'
return amount.toFixed(2).replace(/\.?0+$/, '')
},
hasStartingPrice(item) {
const amount = item ? parseFloat(item.startingPrice) : 0
return !isNaN(amount) && amount > 0
},
hasOpenKitchenVideo(item) {
return !!(item && item.openKitchenVideoUrl && String(item.openKitchenVideoUrl).trim())
},
@ -820,34 +831,82 @@
for (let i = 0; i < this.shopList.length; i++) {
if (this.shopList[i].shopTakeaway != '' && this.shopList[i].shopTakeaway != null) {
if (this.shopList[i].status == 1) {
let isEndTime = this.isWithinBusinessHours(this.shopList[i].shopTakeaway.businessHourBegin,this.shopList[i].shopTakeaway.businessHourEnd)
let isEndTime = this.isShopWithinBusinessHours(this.shopList[i].shopTakeaway)
this.shopList[i].status = isEndTime ? 1 : 0
}
}
}
},
//
isWithinBusinessHours(begin, end) {
//
const now = new Date();
const currentMinutes = now.getHours() * 60 + now.getMinutes();
// "HH:MM"
//
isShopWithinBusinessHours(takeaway) {
if (!takeaway) return false
const periods = this.parseBusinessHourPeriods(takeaway)
if (!periods.length) return false
const now = new Date()
const currentMinutes = now.getHours() * 60 + now.getMinutes()
for (let i = 0; i < periods.length; i++) {
if (this.isWithinBusinessHours(periods[i].begin, periods[i].end, currentMinutes)) {
return true
}
}
return false
},
parseBusinessHourPeriods(takeaway) {
let list = takeaway.businessHourList
if ((!list || !list.length) && takeaway.businessHours) {
try {
list = typeof takeaway.businessHours === 'string' ? JSON.parse(takeaway.businessHours) : takeaway.businessHours
} catch (e) {
list = []
}
}
if (list && list.length) {
return list.filter(item => item && item.begin && item.end).map(item => ({
begin: String(item.begin).substring(0, 5),
end: String(item.end).substring(0, 5)
}))
}
if (takeaway.businessHourBegin && takeaway.businessHourEnd) {
return [{
begin: String(takeaway.businessHourBegin).substring(0, 5),
end: String(takeaway.businessHourEnd).substring(0, 5)
}]
}
return []
},
getBusinessHoursText(item) {
const takeaway = item && item.shopTakeaway
if (!takeaway) return ''
const periods = this.parseBusinessHourPeriods(takeaway)
if (!periods.length) return ''
return periods.map(p => p.begin + '-' + p.end).join('、')
},
isWithinBusinessHours(begin, end, currentMinutes) {
const nowMinutes = currentMinutes != null ? currentMinutes : (() => {
const now = new Date()
return now.getHours() * 60 + now.getMinutes()
})()
const parseTime = (timeStr) => {
const [hours, minutes] = timeStr.split(':').map(Number);
return hours * 60 + minutes;
};
const startMinutes = parseTime(begin);
const endMinutes = parseTime(end);
//
return currentMinutes >= startMinutes && currentMinutes <= endMinutes;
const parts = String(timeStr || '00:00').split(':').map(Number)
return (parts[0] || 0) * 60 + (parts[1] || 0)
}
const startMinutes = parseTime(begin)
const endMinutes = parseTime(end)
if (startMinutes === endMinutes) return true
if (startMinutes < endMinutes) {
return nowMinutes >= startMinutes && nowMinutes <= endMinutes
}
//
return nowMinutes >= startMinutes || nowMinutes <= endMinutes
},
goDetail(type, item) {
console.log('数据',item)
if (type == 'shop') {
if (item && item.status != 1) {
this.buyingye()
return
}
if (!requireLoginToCurrentPage()) return
uni.navigateTo({
url: '/package2/group/groupBuySingle?type=shop&item=' + encodeURIComponent(JSON.stringify(item))
@ -1524,6 +1583,38 @@
z-index: 20;
border-radius: inherit;
background: rgba(255, 255, 255, 0.58);
display: flex;
align-items: center;
justify-content: center;
padding: 24rpx;
box-sizing: border-box;
}
.closed-badge {
max-width: 86%;
padding: 16rpx 28rpx;
border-radius: 24rpx;
background: rgba(255, 255, 255, 0.94);
box-shadow: 0 8rpx 24rpx rgba(19, 91, 70, 0.12);
display: flex;
flex-direction: column;
align-items: center;
}
.closed-title {
color: #19be6b;
font-size: 28rpx;
font-weight: 700;
line-height: 40rpx;
}
.closed-hours {
margin-top: 6rpx;
color: #666;
font-size: 22rpx;
line-height: 32rpx;
text-align: center;
word-break: break-all;
}
.shop-member-disabled {

467
package2/group/groupBuySingle.vue

@ -36,8 +36,13 @@
style="width:30rpx;height:30rpx;position: absolute;top: 0;left: 0;background-size: 100%;" />
</view>
<view class="shop-content" @tap="goDetail('shopDetail')">
<view class="shop-name">
{{shopItem.shopName}}
<view class="shop-name-row">
<view class="shop-name">
{{shopItem.shopName}}
</view>
<view class="shop-action-btn shop-share-action" @tap.stop="openShopSharePoster">
分享商家
</view>
</view>
<view class="shop-subtitle">
{{shopSubtitle}}
@ -132,8 +137,7 @@
<view class="catalog-menu-wrap" :style="{ top: navBarHeight + 'px' }">
<scroll-view scroll-y id="menuList" class="menu-scroll"
:style="{ height: stickyInnerHeight + 'px' }">
<view class="menu1" @tap="checkTab(index)" v-for="(item,index) in menuList" :key="index"
:style="{'border-top-right-radius':item.checked?'20rpx':'','border-bottom-right-radius':item.checked?'20rpx':'','color':item.checked ? (isStoreGroupOrder ? '#f0642f' : 'rgba(0, 35, 28, 1)') : (isStoreGroupOrder ? '#9b6a4c' : '#777'),'background':item.checked ? (isStoreGroupOrder ? 'linear-gradient(90deg, #fff4e8, #fff)' : '#fff') : ''}">
<view class="menu1" :class="{'menu1-active': item.checked}" @tap="checkTab(index)" v-for="(item,index) in menuList" :key="index">
<!-- <image class="menu-active-dot" v-if="item.checked" src="https://jewel-shop.oss-cn-beijing.aliyuncs.com/f7227759d693497a8beab87ff437a875.png"
mode="aspectFit"></image> -->
<view class="menu-name">{{item.categoryName}}</view>
@ -207,12 +211,12 @@
</view>
</view>
<uni-load-more :status="loadStatus" @change="onChange" />
<view style="width: 100%;height: 160rpx;"></view>
<view :style="{width: '100%', height: showStartingPriceTip ? '190rpx' : '160rpx'}"></view>
</view>
</view>
</view>
<view style="width: 100%;height: 160rpx;"></view>
<view class="bottom checkout-bar">
<view :style="{width: '100%', height: showStartingPriceTip ? '190rpx' : '160rpx'}"></view>
<view class="bottom checkout-bar" :class="{'checkout-bar--with-starting-price': showStartingPriceTip}">
<view class="bottom-left">
<view class="cart-bag-wrap" style="position: relative;width: 60rpx;height: 80rpx;margin-top: 20rpx;"
@tap="openPopup('car','','')">
@ -226,6 +230,9 @@
<view class="package-fee-tip" v-if="cartTotalPackageFeeNumber > 0">
餐盒费{{cartTotalPackageFee}}(堂食会去掉)
</view>
<view class="delivery-start-tip" v-if="showStartingPriceTip">
外卖{{startingPriceText}}元起送
</view>
</view>
</view>
<view class="bottom-right checkout-btn" @tap="submitCartCheckout">
@ -302,6 +309,9 @@
<view class="package-fee-tip" v-if="cartTotalPackageFeeNumber > 0">
含餐盒费{{cartTotalPackageFee}}
</view>
<view class="delivery-start-tip" v-if="showStartingPriceTip">
外卖{{startingPriceText}}元起送
</view>
</view>
</view>
<view class="bottom-right" @tap="submitCartCheckout">
@ -795,7 +805,23 @@
</view>
</view>
</uni-popup>
<uni-popup ref="shopSharePopup" background-color="transparent" @change="onBottomPopupChange">
<view class="share-poster-popup" @tap.stop>
<view class="share-popup-close" @tap="$refs.shopSharePopup.close()">
<uni-icons type="closeempty" size="22" color="#26433d"></uni-icons>
</view>
<view class="share-popup-title">商家分享码</view>
<view class="share-popup-sub">保存图片或让朋友扫码直接进入当前商家</view>
<view class="share-poster-preview">
<view class="share-loading" v-if="sharePosterLoading">海报生成中...</view>
<image v-else-if="sharePosterImage" class="share-poster-image" :src="sharePosterImage" mode="widthFix" show-menu-by-longpress></image>
</view>
<view class="share-save-btn" @tap="saveSharePoster">保存图片</view>
</view>
</uni-popup>
<common-loading />
<canvas canvas-id="shopShareCanvas" id="shopShareCanvas" class="share-canvas"
:style="{width: posterCanvasWidth + 'px', height: posterCanvasHeight + 'px'}"></canvas>
<view class="free-order-mask" v-if="freeOrderEffectVisible" @tap.stop="closeFreeOrderEffect">
<view class="free-order-card" @tap.stop>
<view class="free-order-rays"></view>
@ -908,7 +934,12 @@
bottomPopupOpenCount: 0,
autoWaitRuleKnown: false,
hasAutoOpenedWaitPopup: false,
areaKind: 1
areaKind: 1,
sharePosterLoading: false,
sharePosterImage: '',
shareQrcodeUrl: '',
posterCanvasWidth: 320,
posterCanvasHeight: 450
}
},
components: {
@ -921,6 +952,12 @@
isStoreGroupOrder() {
return this.orderScene === 'storeGroup' || this.shopItem.merchantType == 2;
},
showStartingPriceTip() {
return !this.isStoreGroupOrder && this.hasStartingPrice(this.shopItem);
},
startingPriceText() {
return this.formatMoney(this.shopItem.startingPrice).replace(/\.?0+$/, '');
},
shopSubtitle() {
if (this.isStoreGroupOrder) {
return this.isSocialArea
@ -2137,6 +2174,10 @@
let amount = parseFloat(value);
return (isNaN(amount) ? 0 : amount).toFixed(2);
},
hasStartingPrice(item) {
let amount = item ? parseFloat(item.startingPrice) : 0;
return !isNaN(amount) && amount > 0;
},
getSpecDisplayString(specs) {
if (!specs) return '';
let arr = [];
@ -2449,6 +2490,236 @@
});
}
},
openShopSharePoster() {
if (!this.shopItem || !this.shopItem.id) {
this.tui.toast('商家信息加载中,请稍后再试');
return;
}
this.sharePosterLoading = true;
this.sharePosterImage = '';
this.$refs.shopSharePopup.open('center');
this.generateShopSharePoster().catch((err) => {
this.sharePosterLoading = false;
this.tui.toast((err && err.message) || '生成分享图失败');
});
},
getShopShareQrcode() {
if (this.shareQrcodeUrl) return Promise.resolve(this.shareQrcodeUrl);
const scene = 's' + String(this.shopItem.id || '');
if (scene.length > 32) {
throw new Error('商家码参数过长,暂时无法生成');
}
return this.tui.request('/order/ow/getWechatQrcode', 'POST', {
scene: scene,
page: 'pages/index/index',
envVersion: 'release'
}, false, true).then((res) => {
if (res && res.code == 200 && res.result) {
this.shareQrcodeUrl = res.result;
return res.result;
}
throw new Error((res && res.message) || '小程序码生成失败');
});
},
getPosterImageInfo(src, required) {
if (!src) {
return required ? Promise.reject(new Error('图片加载失败')) : Promise.resolve(null);
}
return new Promise((resolve, reject) => {
uni.getImageInfo({
src: src,
success: resolve,
fail: () => {
if (required) {
reject(new Error('图片加载失败'));
} else {
resolve(null);
}
}
});
});
},
createRoundedPath(ctx, x, y, width, height, radius) {
const r = Math.min(radius, width / 2, height / 2);
ctx.beginPath();
ctx.moveTo(x + r, y);
ctx.lineTo(x + width - r, y);
ctx.arc(x + width - r, y + r, r, Math.PI * 1.5, Math.PI * 2);
ctx.lineTo(x + width, y + height - r);
ctx.arc(x + width - r, y + height - r, r, 0, Math.PI * 0.5);
ctx.lineTo(x + r, y + height);
ctx.arc(x + r, y + height - r, r, Math.PI * 0.5, Math.PI);
ctx.lineTo(x, y + r);
ctx.arc(x + r, y + r, r, Math.PI, Math.PI * 1.5);
ctx.closePath();
},
fillRoundRect(ctx, x, y, width, height, radius, color) {
this.createRoundedPath(ctx, x, y, width, height, radius);
ctx.setFillStyle(color);
ctx.fill();
},
drawRoundImage(ctx, imagePath, x, y, width, height, radius) {
if (!imagePath) return;
ctx.save();
this.createRoundedPath(ctx, x, y, width, height, radius);
ctx.clip();
ctx.drawImage(imagePath, x, y, width, height);
ctx.restore();
},
drawWrappedText(ctx, text, x, y, maxWidth, lineHeight, maxLines) {
const chars = String(text || '').split('');
let line = '';
let lines = [];
for (let i = 0; i < chars.length; i++) {
const testLine = line + chars[i];
const metrics = ctx.measureText ? ctx.measureText(testLine) : { width: testLine.length * 14 };
if (metrics.width > maxWidth && line) {
lines.push(line);
line = chars[i];
} else {
line = testLine;
}
if (lines.length >= maxLines) break;
}
if (line && lines.length < maxLines) lines.push(line);
if (lines.length > maxLines) lines = lines.slice(0, maxLines);
if (lines.length === maxLines && chars.join('').length > lines.join('').length) {
lines[maxLines - 1] = lines[maxLines - 1].slice(0, Math.max(0, lines[maxLines - 1].length - 1)) + '...';
}
for (let i = 0; i < lines.length; i++) {
ctx.fillText(lines[i], x, y + i * lineHeight);
}
return lines.length;
},
generateShopSharePoster() {
return this.getShopShareQrcode().then((qrcodeUrl) => {
return Promise.all([
this.getPosterImageInfo(qrcodeUrl, true),
this.getPosterImageInfo(this.shopItem.shopIcon, false)
]);
}).then(([qrcodeInfo, shopIconInfo]) => {
const width = uni.upx2px(640);
const height = uni.upx2px(900);
this.posterCanvasWidth = width;
this.posterCanvasHeight = height;
return new Promise((resolve, reject) => {
this.$nextTick(() => {
setTimeout(() => {
try {
const canvasScope = this.$scope || this;
const ctx = uni.createCanvasContext('shopShareCanvas', canvasScope);
const bg = ctx.createLinearGradient(0, 0, width, height);
bg.addColorStop(0, '#f8fff2');
bg.addColorStop(0.52, '#e8fff7');
bg.addColorStop(1, '#fff7df');
ctx.setFillStyle(bg);
ctx.fillRect(0, 0, width, height);
ctx.setFillStyle('rgba(227, 255, 150, 0.58)');
ctx.beginPath();
ctx.arc(width - uni.upx2px(88), uni.upx2px(112), uni.upx2px(104), 0, Math.PI * 2);
ctx.fill();
ctx.setFillStyle('rgba(166, 255, 234, 0.52)');
ctx.beginPath();
ctx.arc(uni.upx2px(74), height - uni.upx2px(120), uni.upx2px(130), 0, Math.PI * 2);
ctx.fill();
ctx.save();
ctx.translate(width - uni.upx2px(130), height - uni.upx2px(72));
ctx.rotate(-0.38);
ctx.setFillStyle('rgba(255, 184, 84, 0.22)');
this.fillRoundRect(ctx, -uni.upx2px(150), -uni.upx2px(34), uni.upx2px(300), uni.upx2px(68), uni.upx2px(34), 'rgba(255, 184, 84, 0.22)');
ctx.restore();
ctx.setFillStyle('rgba(12, 75, 62, 0.08)');
for (let i = 0; i < 9; i++) {
ctx.beginPath();
ctx.arc(uni.upx2px(70 + i * 56), uni.upx2px(760 + (i % 3) * 22), uni.upx2px(5), 0, Math.PI * 2);
ctx.fill();
}
this.fillRoundRect(ctx, uni.upx2px(28), uni.upx2px(28), width - uni.upx2px(56), height - uni.upx2px(56), uni.upx2px(36), '#ffffff');
this.fillRoundRect(ctx, uni.upx2px(54), uni.upx2px(54), uni.upx2px(172), uni.upx2px(54), uni.upx2px(27), '#e3ff96');
ctx.setFillStyle('#0c4b3e');
ctx.setFontSize(uni.upx2px(26));
ctx.setTextAlign('center');
ctx.fillText('半径里', uni.upx2px(140), uni.upx2px(91));
ctx.setTextAlign('left');
ctx.setFillStyle('#153a35');
ctx.setFontSize(uni.upx2px(58));
ctx.fillText('拼团更省钱', uni.upx2px(54), uni.upx2px(184));
ctx.setFillStyle('#6a7a75');
ctx.setFontSize(uni.upx2px(26));
ctx.fillText(this.isSocialArea ? '同城好友一起拼,优惠一起拿' : '约上同学室友一起拼,越拼越划算', uni.upx2px(58), uni.upx2px(236));
const iconX = uni.upx2px(58);
const iconY = uni.upx2px(288);
const iconSize = uni.upx2px(118);
this.fillRoundRect(ctx, iconX, iconY, iconSize, iconSize, uni.upx2px(24), '#f1faf6');
if (shopIconInfo && shopIconInfo.path) {
this.drawRoundImage(ctx, shopIconInfo.path, iconX, iconY, iconSize, iconSize, uni.upx2px(24));
}
ctx.setFillStyle('#173a34');
ctx.setFontSize(uni.upx2px(34));
const shopNameLines = this.drawWrappedText(ctx, this.shopItem.shopName || '半径里商家', uni.upx2px(198), uni.upx2px(328), uni.upx2px(350), uni.upx2px(42), 2);
ctx.setFillStyle('#ff6a2a');
ctx.setFontSize(uni.upx2px(24));
ctx.fillText('评分 ' + (this.shopItem.shopScore || 5), uni.upx2px(198), uni.upx2px(328 + shopNameLines * 42 + 26));
this.fillRoundRect(ctx, uni.upx2px(66), uni.upx2px(480), width - uni.upx2px(132), uni.upx2px(288), uni.upx2px(34), '#f7fffb');
this.fillRoundRect(ctx, uni.upx2px(106), uni.upx2px(518), uni.upx2px(212), uni.upx2px(212), uni.upx2px(30), '#ffffff');
this.drawRoundImage(ctx, qrcodeInfo.path, uni.upx2px(126), uni.upx2px(538), uni.upx2px(172), uni.upx2px(172), uni.upx2px(18));
ctx.setFillStyle('#153a35');
ctx.setFontSize(uni.upx2px(34));
ctx.fillText('扫码立即省钱', uni.upx2px(344), uni.upx2px(586));
ctx.setFillStyle('#70807b');
ctx.setFontSize(uni.upx2px(24));
this.drawWrappedText(ctx, '打开半径里小程序,直接查看本店拼团优惠。', uni.upx2px(344), uni.upx2px(636), uni.upx2px(190), uni.upx2px(34), 3);
this.fillRoundRect(ctx, uni.upx2px(112), uni.upx2px(798), width - uni.upx2px(224), uni.upx2px(58), uni.upx2px(29), '#153a35');
ctx.setFillStyle('#ffffff');
ctx.setFontSize(uni.upx2px(24));
ctx.setTextAlign('center');
ctx.fillText('长按保存,分享给朋友一起省', width / 2, uni.upx2px(836));
ctx.draw(false, () => {
setTimeout(() => {
uni.canvasToTempFilePath({
canvasId: 'shopShareCanvas',
width: width,
height: height,
destWidth: width * 2,
destHeight: height * 2,
success: (res) => {
this.sharePosterImage = res.tempFilePath;
this.sharePosterLoading = false;
resolve(res.tempFilePath);
},
fail: reject
}, canvasScope);
}, 120);
});
} catch (e) {
reject(e);
}
}, 80);
});
});
});
},
saveSharePoster() {
if (!this.sharePosterImage) {
this.tui.toast('分享图还在生成中');
return;
}
uni.saveImageToPhotosAlbum({
filePath: this.sharePosterImage,
success: () => {
this.tui.toast('已保存到相册', 1200, true);
},
fail: () => {
this.tui.toast('保存失败,请检查相册权限');
}
});
},
fetchCoupons() {
let userId = uni.getStorageSync('id');
if (!userId) return Promise.resolve([]);
@ -2636,9 +2907,20 @@
}
.shop-name-row {
display: flex;
align-items: center;
gap: 14rpx;
}
.shop-name {
flex: 1;
min-width: 0;
font-size: 32rpx;
font-weight: 900;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.shop-content-center {
@ -2718,6 +3000,99 @@
opacity: 0.86;
}
.shop-share-action {
background: linear-gradient(90deg, #fff5d6 0%, #e3ff96 100%);
color: #8a4b10;
border-color: rgba(255, 209, 108, 0.72);
}
.share-poster-popup {
position: relative;
width: 650rpx;
padding: 34rpx 26rpx 30rpx;
border-radius: 38rpx;
background:
radial-gradient(circle at 12% 4%, rgba(227, 255, 150, 0.48), rgba(227, 255, 150, 0) 190rpx),
linear-gradient(180deg, #ffffff 0%, #f7fffb 100%);
box-shadow: 0 24rpx 78rpx rgba(0, 35, 28, 0.18);
box-sizing: border-box;
text-align: center;
}
.share-popup-close {
position: absolute;
top: 20rpx;
right: 20rpx;
width: 52rpx;
height: 52rpx;
line-height: 52rpx;
border-radius: 50%;
background: rgba(243, 251, 247, 0.96);
}
.share-popup-title {
color: #153a35;
font-size: 34rpx;
font-weight: 900;
line-height: 46rpx;
}
.share-popup-sub {
margin-top: 8rpx;
color: #758681;
font-size: 22rpx;
font-weight: 700;
line-height: 32rpx;
}
.share-poster-preview {
width: 520rpx;
min-height: 660rpx;
margin: 26rpx auto 0;
border-radius: 32rpx;
background: #edf8f4;
overflow: hidden;
display: flex;
align-items: center;
justify-content: center;
}
.share-loading {
color: #61736e;
font-size: 26rpx;
font-weight: 800;
}
.share-poster-image {
width: 520rpx;
display: block;
}
.share-save-btn {
width: 440rpx;
height: 82rpx;
line-height: 82rpx;
margin: 28rpx auto 0;
border-radius: 999rpx;
background: linear-gradient(90deg, #e3ff96 0%, #a6ffea 100%);
color: #153a35;
font-size: 28rpx;
font-weight: 900;
box-shadow: 0 14rpx 28rpx rgba(98, 229, 190, 0.24);
}
.share-save-btn:active {
transform: scale(0.97);
}
.share-canvas {
position: fixed;
left: -9999px;
top: -9999px;
z-index: -1;
pointer-events: none;
}
.shop-deal1 {
flex-shrink: 0;
margin-left: 10rpx;
@ -3390,25 +3765,33 @@
.menu1 {
width: 160rpx;
height: 90rpx;
min-height: 88rpx;
position: relative;
text-align: center;
display: flex;
align-items: center;
justify-content: center;
padding: 0 16rpx;
padding: 10rpx 10rpx;
box-sizing: border-box;
line-height: 1.2;
color: #6f7b77;
background: transparent;
line-height: 1.18;
overflow: hidden;
}
.menu-name {
width: 100%;
max-height: 68rpx;
line-height: 34rpx;
width: 138rpx;
max-height: 64rpx;
color: inherit;
font-size: 23rpx;
font-weight: 700;
line-height: 31rpx;
letter-spacing: 0;
overflow: hidden;
text-overflow: ellipsis;
white-space: normal;
word-break: break-all;
overflow-wrap: break-word;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
@ -3815,9 +4198,11 @@
z-index: 50;
flex-shrink: 0;
width: 160rpx;
background: rgba(247, 255, 251, 0.95);
border-right: 1rpx solid #eee;
background: #f5f7f6;
border-right: 0;
border-top-left-radius: 34rpx;
border-bottom-left-radius: 34rpx;
overflow: hidden;
}
.catalog-goods {
@ -3827,17 +4212,38 @@
}
.menu-scroll {
background: rgba(247, 255, 251, 0.9);
background: #f5f7f6;
border-right: 0 !important;
flex-shrink: 0;
width: 160rpx;
font-weight: 700;
font-size: 28rpx;
}
.menu1 {
transition: transform 0.18s ease, background 0.18s ease, color 0.18s ease;
}
.menu1-active {
color: #173a34;
background: #fff;
border-radius: 0 24rpx 24rpx 0;
box-shadow: 0 10rpx 24rpx rgba(0, 35, 28, 0.05);
}
.menu1-active::before {
content: '';
position: absolute;
left: 0;
top: 50%;
width: 7rpx;
height: 36rpx;
border-radius: 0 8rpx 8rpx 0;
background: linear-gradient(180deg, #ffb84d 0%, #ff6a2a 100%);
transform: translateY(-50%);
}
.menu1-active .menu-name {
font-size: 24rpx;
font-weight: 900;
transition: transform 0.18s ease, background 0.18s ease;
}
.menu1:active {
@ -3986,6 +4392,10 @@
overflow: hidden;
}
.checkout-bar--with-starting-price {
min-height: 142rpx;
}
.cart-bag-wrap {
animation: bagBounce 2.2s ease-in-out infinite;
}
@ -4109,6 +4519,14 @@
line-height: 24rpx;
}
.delivery-start-tip {
margin-top: 2rpx;
color: #ff6f2c;
font-size: 20rpx;
font-weight: 900;
line-height: 26rpx;
}
.cart-item-price {
flex: 1;
display: flex;
@ -5233,6 +5651,15 @@
color: #9b6a4c;
}
.store-group-page .menu1-active {
color: #f0642f;
background: #fff;
}
.store-group-page .menu1-active::before {
background: linear-gradient(180deg, #ffbf6b 0%, #f0642f 100%);
}
.store-group-page .menu-active-dot {
filter: hue-rotate(148deg) saturate(1.5);
opacity: 0.9;

100
package2/group/studentStoreList.vue

@ -83,7 +83,10 @@
<view class="shop-card community-shop-card" :class="{'shop-card-disabled': item.status != 1}"
v-for="(item,index) in shopList" :key="index" @tap="goShop(item)">
<view @tap.stop="buyingye" v-if="item.status != 1" class="closed-mask">
<view class="closed-text">休息中</view>
<view class="closed-badge">
<text class="closed-title">休息中</text>
<text class="closed-hours" v-if="getBusinessHoursText(item)">营业时间 {{getBusinessHoursText(item)}}</text>
</view>
</view>
<view class="shop-card-bg"></view>
<view class="shop-top">
@ -452,19 +455,70 @@
judgeBusinessStatus() {
for (let i = 0; i < this.shopList.length; i++) {
const takeaway = this.shopList[i].shopTakeaway
if (takeaway && takeaway.businessHourBegin && takeaway.businessHourEnd && this.shopList[i].status == 1) {
this.shopList[i].status = this.isWithinBusinessHours(takeaway.businessHourBegin, takeaway.businessHourEnd) ? 1 : 0
if (takeaway && this.shopList[i].status == 1) {
this.shopList[i].status = this.isShopWithinBusinessHours(takeaway) ? 1 : 0
}
}
},
isWithinBusinessHours(begin, end) {
isShopWithinBusinessHours(takeaway) {
if (!takeaway) return false
const periods = this.parseBusinessHourPeriods(takeaway)
if (!periods.length) return false
const now = new Date()
const currentMinutes = now.getHours() * 60 + now.getMinutes()
for (let i = 0; i < periods.length; i++) {
if (this.isWithinBusinessHours(periods[i].begin, periods[i].end, currentMinutes)) {
return true
}
}
return false
},
parseBusinessHourPeriods(takeaway) {
let list = takeaway.businessHourList
if ((!list || !list.length) && takeaway.businessHours) {
try {
list = typeof takeaway.businessHours === 'string' ? JSON.parse(takeaway.businessHours) : takeaway.businessHours
} catch (e) {
list = []
}
}
if (list && list.length) {
return list.filter(item => item && item.begin && item.end).map(item => ({
begin: String(item.begin).substring(0, 5),
end: String(item.end).substring(0, 5)
}))
}
if (takeaway.businessHourBegin && takeaway.businessHourEnd) {
return [{
begin: String(takeaway.businessHourBegin).substring(0, 5),
end: String(takeaway.businessHourEnd).substring(0, 5)
}]
}
return []
},
getBusinessHoursText(item) {
const takeaway = item && item.shopTakeaway
if (!takeaway) return ''
const periods = this.parseBusinessHourPeriods(takeaway)
if (!periods.length) return ''
return periods.map(p => p.begin + '-' + p.end).join('、')
},
isWithinBusinessHours(begin, end, currentMinutes) {
const nowMinutes = currentMinutes != null ? currentMinutes : (() => {
const now = new Date()
return now.getHours() * 60 + now.getMinutes()
})()
const parseTime = (timeStr) => {
const parts = String(timeStr || '00:00').split(':').map(Number)
return (parts[0] || 0) * 60 + (parts[1] || 0)
}
return currentMinutes >= parseTime(begin) && currentMinutes <= parseTime(end)
const startMinutes = parseTime(begin)
const endMinutes = parseTime(end)
if (startMinutes === endMinutes) return true
if (startMinutes < endMinutes) {
return nowMinutes >= startMinutes && nowMinutes <= endMinutes
}
return nowMinutes >= startMinutes || nowMinutes <= endMinutes
},
switchTab(item) {
this.checkedTab = item.key
@ -551,6 +605,10 @@
})
},
goShop(item) {
if (item && item.status != 1) {
this.buyingye()
return
}
if (!requireLoginToCurrentPage()) return
uni.navigateTo({
url: '/package2/group/groupBuySingle?type=shop&orderScene=storeGroup&item=' + encodeURIComponent(JSON.stringify(item))
@ -1705,15 +1763,35 @@
display: flex;
align-items: center;
justify-content: center;
padding: 24rpx;
box-sizing: border-box;
}
.closed-text {
padding: 12rpx 30rpx;
border-radius: 999rpx;
background: rgba(255, 255, 255, 0.92);
.closed-badge {
max-width: 86%;
padding: 16rpx 28rpx;
border-radius: 24rpx;
background: rgba(255, 255, 255, 0.94);
box-shadow: 0 8rpx 24rpx rgba(92, 48, 21, 0.1);
display: flex;
flex-direction: column;
align-items: center;
}
.closed-title {
color: #ff8352;
font-size: 26rpx;
font-weight: 900;
font-size: 28rpx;
font-weight: 700;
line-height: 40rpx;
}
.closed-hours {
margin-top: 6rpx;
color: #666;
font-size: 22rpx;
line-height: 32rpx;
text-align: center;
word-break: break-all;
}
.rank-tag {

105
package3/myCenter/problemFeedback.vue

@ -11,6 +11,13 @@
</view>
</view>
<view class="content">
<view class="service-phone-card" @tap="callPlatformService">
<view>
<view class="service-phone-label">平台客服电话</view>
<view class="service-phone-value">{{servicePhoneLoading ? '正在获取客服热线...' : '遇到问题可联系平台客服'}}</view>
</view>
<view class="service-phone-action">联系客服</view>
</view>
<view class="list-head">
<text>我的反馈</text>
<text class="list-tip">按处理状态和时间排序</text>
@ -79,6 +86,8 @@
typeOptions: ['投诉', '吐槽', '建议', '其他'],
pictureList: [],
loading: false,
servicePhone: '',
servicePhoneLoading: false,
totalPages: 0,
feedbackList: [],
form: {
@ -97,6 +106,7 @@
onLoad() {
this.menuButtonInfo = uni.getMenuButtonBoundingClientRect ? uni.getMenuButtonBoundingClientRect() : {}
this.form.contactPhone = uni.getStorageSync('mobile') || uni.getStorageSync('phone') || ''
this.getPlatformServicePhone()
this.getFeedbackList()
},
computed: {
@ -116,11 +126,65 @@
},
getArea() {
try {
return uni.getStorageSync('area') ? JSON.parse(uni.getStorageSync('area')) : {}
const area = uni.getStorageSync('area')
if (!area) return {}
if (typeof area === 'object') return area
return JSON.parse(area)
} catch (e) {
return {}
}
},
getCurrentRegionId() {
const area = this.getArea()
return area && area.id ? area.id : ''
},
makeCall(phone) {
if (!phone) {
this.tui.toast('暂无联系电话')
return
}
uni.makePhoneCall({
phoneNumber: phone
})
},
getPlatformServicePhone(callAfterFetch = false) {
if (this.servicePhone) {
if (callAfterFetch) {
this.makeCall(this.servicePhone)
}
return
}
if (this.servicePhoneLoading) return
const regionId = this.getCurrentRegionId()
if (!regionId) {
if (callAfterFetch) {
this.tui.toast('请选择区域')
}
return
}
this.servicePhoneLoading = true
this.tui.request('/mall/admin/seckillGroup/customerPhone/get', 'GET', {
regionId: regionId
}, false, true).then((res) => {
this.servicePhoneLoading = false
if (res.code == 200 && res.result) {
this.servicePhone = res.result
if (callAfterFetch) {
this.makeCall(res.result)
}
} else if (callAfterFetch) {
this.tui.toast(res.message || '获取客服电话失败')
}
}).catch(() => {
this.servicePhoneLoading = false
if (callAfterFetch) {
this.tui.toast('获取客服电话失败')
}
})
},
callPlatformService() {
this.getPlatformServicePhone(true)
},
getFeedbackList() {
if (this.loading) return
this.loading = true
@ -305,6 +369,43 @@
padding: 0 24rpx 180rpx;
}
.service-phone-card {
display: flex;
align-items: center;
justify-content: space-between;
margin: 0 4rpx 24rpx;
padding: 24rpx 10rpx;
border-radius: 28rpx;
background: linear-gradient(135deg, #FFFFFF 0%, #ECFFF7 100%);
box-shadow: 0 12rpx 40rpx rgba(30, 80, 60, .06);
}
.service-phone-label {
color: #60706C;
font-size: 24rpx;
font-weight: 700;
}
.service-phone-value {
margin-top: 8rpx;
color: #00231C;
font-size: 26rpx;
font-weight: 700;
}
.service-phone-action {
min-width: 108rpx;
height: 56rpx;
padding: 0 24rpx;
border-radius: 999rpx;
background: #34C8A0;
color: #fff;
font-size: 24rpx;
font-weight: 900;
line-height: 56rpx;
text-align: center;
}
.feedback-popup,
.feedback-item {
border-radius: 28rpx;
@ -436,7 +537,7 @@
}
.feedback-scroll {
height: calc(100vh - 348rpx);
height: calc(100vh - 476rpx);
min-height: 520rpx;
}

127
package3/shop/transferDeliveryOrderList.vue

@ -3,11 +3,13 @@
<view class="fixed-head">
<view class="nav" :style="{'height': navHeight + 'px'}">
<view class="back" :style="{'top': backTop + 'px'}" @tap="back"><uni-icons type="left" size="26"></uni-icons></view>
<view class="title" :style="{'top': titleTop + 'px','height': capsuleHeight + 'px','line-height': capsuleHeight + 'px'}">中转配送</view>
<view class="title" :style="{'top': titleTop + 'px','height': capsuleHeight + 'px','line-height': capsuleHeight + 'px'}">{{isWorkerMode ? '中转单' : '中转配送'}}</view>
</view>
<view class="tabs">
<view class="tab" :class="{active: tab === 'wait'}" @tap="switchTab('wait')">待送达</view>
<view class="tab" :class="{active: tab === 'done'}" @tap="switchTab('done')">已送达</view>
<view class="tab" v-if="isWorkerMode" :class="{active: tab === 'wait'}" @tap="switchTab('wait')">待接单</view>
<view class="tab" v-if="isWorkerMode" :class="{active: tab === 'accepted'}" @tap="switchTab('accepted')">待送达中转点({{transferTodoCount}})</view>
<view class="tab" v-if="!isWorkerMode" :class="{active: tab === 'wait'}" @tap="switchTab('wait')">待送达</view>
<view class="tab" v-if="!isWorkerMode" :class="{active: tab === 'done'}" @tap="switchTab('done')">已送达</view>
</view>
</view>
<scroll-view class="list" :style="{'top': listTop + 'px'}" scroll-y @scrolltolower="loadMore">
@ -17,6 +19,13 @@
<view class="appointment-delivery-tag" v-if="item.appointmentDelivery == 1">预约配送</view>
<view class="code" v-if="item.numberCode">#{{item.numberCode}}</view>
</view>
<view class="row">
<view class="label">商家</view>
<view class="value phone-value" @tap.stop="makeCall(item.shopPhone)">
<text>{{item.shopName || '-'}}</text>
<text v-if="item.shopPhone" class="phone">{{item.shopPhone}}</text>
</view>
</view>
<view class="row">
<view class="label">中转点</view>
<view class="value">{{item.transferAddressName || item.shopName}}</view>
@ -37,13 +46,20 @@
<view class="label">取餐码</view>
<view class="value pickup-code">{{item.transferPickupCode || '配送员接单后生成'}}</view>
</view>
<view class="row">
<view class="row" v-if="!isWorkerMode">
<view class="label">配送员</view>
<view class="value phone-value" @tap.stop="makeCall(item.workerPhone)">
<text>{{item.workerName || '未分配'}}</text>
<text v-if="item.workerPhone" class="phone">{{item.workerPhone}}</text>
</view>
</view>
<view class="row" v-if="item.transferWorkerId">
<view class="label">中转员</view>
<view class="value phone-value" @tap.stop="makeCall(item.transferWorkerMobile)">
<text>{{item.transferWorkerName || '未分配'}}</text>
<text v-if="item.transferWorkerMobile" class="phone">{{item.transferWorkerMobile}}</text>
</view>
</view>
<view class="row">
<view class="label">用户</view>
<view class="value phone-value" @tap.stop="makeCall(item.receiverPhone)">
@ -58,12 +74,18 @@
<view class="arrive-info" v-if="item.transferArriveTime">
已送达中转点{{formatTime(item.transferArriveTime)}}
</view>
<view class="actions" v-else-if="isWorkerMode && tab === 'wait'">
<view class="main-btn" :class="{'main-btn-disabled': item.transferAccepting}" @tap.stop="transferAccept(index,item)">
{{item.transferAccepting ? '接单中...' : '接单'}}
</view>
</view>
<view class="actions" v-else>
<view class="photo-btn" @tap.stop="chooseImage(index)">拍照</view>
<view class="main-btn" @tap.stop="transferArrive(index,item)">送达中转点</view>
</view>
</view>
<view class="empty" v-if="list.length === 0 && loadStatus === 'nomore'">暂无中转配送单</view>
<uni-load-more :status="loadStatus" />
<view class="empty" v-if="list.length === 0 && loadStatus === 'nomore'">{{isWorkerMode ? (tab === 'accepted' ? '暂无待送达中转点订单' : '暂无待接中转单') : '暂无中转配送单'}}</view>
</scroll-view>
<uni-popup ref="productPopup" background-color="transparent">
<view class="product-popup-content">
@ -104,8 +126,13 @@
data() {
return {
tab: 'wait',
isWorkerMode: false,
currentWorker: {},
transferTodoCount: 0,
acceptingTransferId: '',
list: [],
productData: [],
isLoadingPage: false,
loadStatus: 'more',
totalPages: 1,
searchForm: {
@ -114,6 +141,9 @@
deliveryType: 1,
transferDelivery: 1,
transferArriveEmpty: true,
transferWorkerView: false,
transferWorkerAccepted: false,
transferWorkerId: '',
shopId: '',
regionId: ''
},
@ -124,6 +154,9 @@
listTop: 210
}
},
onLoad(options) {
this.isWorkerMode = !!(options && options.role === 'worker')
},
onShow() {
this.initNavMetrics()
this.resetList()
@ -165,27 +198,67 @@
this.totalPages = 1
this.list = []
this.loadStatus = 'more'
this.searchForm.transferArriveEmpty = this.tab === 'wait'
this.searchForm.shopId = uni.getStorageSync('shopId')
this.isLoadingPage = false
const area = uni.getStorageSync('area')
this.searchForm.regionId = area ? JSON.parse(area).id : ''
this.searchForm.transferWorkerView = this.isWorkerMode
if (this.isWorkerMode) {
this.currentWorker = uni.getStorageSync('worker') || {}
this.searchForm.shopId = ''
this.searchForm.transferWorkerId = this.currentWorker.workerId || ''
this.searchForm.transferArriveEmpty = true
this.searchForm.transferWorkerAccepted = this.tab === 'accepted'
} else {
this.searchForm.shopId = uni.getStorageSync('shopId')
this.searchForm.transferWorkerId = ''
this.searchForm.transferArriveEmpty = this.tab === 'wait'
this.searchForm.transferWorkerAccepted = false
}
this.getList()
},
loadMore() {
if (this.searchForm.pageNum >= this.totalPages) return
if (this.isLoadingPage || this.searchForm.pageNum >= this.totalPages) return
this.searchForm.pageNum++
this.getList()
},
getList() {
if (this.isLoadingPage) return
this.isLoadingPage = true
this.tui.request('/mall/delivery/pageTransferDelivery', 'POST', this.searchForm, false, false).then((res) => {
if (res.code == 200) {
const records = res.result.records || []
this.list = this.searchForm.pageNum == 1 ? records : this.list.concat(records)
this.totalPages = res.result.pages || 1
this.loadStatus = this.searchForm.pageNum >= this.totalPages ? 'nomore' : 'more'
if (this.isWorkerMode) {
if (this.tab === 'accepted') {
this.transferTodoCount = Number(res.result.total) || 0
} else {
this.getTransferTodoCount()
}
}
} else {
this.tui.toast(res.message)
}
this.isLoadingPage = false
}).catch(() => {
this.isLoadingPage = false
})
},
getTransferTodoCount() {
if (!this.isWorkerMode || !this.searchForm.transferWorkerId) {
this.transferTodoCount = 0
return
}
const params = Object.assign({}, this.searchForm, {
pageNum: 1,
pageSize: 1,
transferWorkerAccepted: true
})
this.tui.request('/mall/delivery/pageTransferDelivery', 'POST', params, false, false).then((res) => {
if (res.code == 200) {
this.transferTodoCount = Number((res.result || {}).total) || 0
}
})
},
chooseImage(index) {
@ -227,6 +300,43 @@
}
})
},
transferAccept(index, item) {
if (this.acceptingTransferId) return
const worker = this.currentWorker && this.currentWorker.workerId ? this.currentWorker : (uni.getStorageSync('worker') || {})
if (!worker.workerId) {
this.tui.toast('您还没有注册兼职')
return
}
this.acceptingTransferId = item.id
this.$set(this.list[index], 'transferAccepting', true)
uni.showLoading({
title: '接单中...',
mask: true
})
this.tui.request('/mall/delivery/transferAccept', 'POST', {
deliveryId: item.id,
workerId: worker.workerId,
workerName: worker.workerName || '',
workerMobile: worker.mobile || ''
}, false, true).then((res) => {
if (res.code == 200) {
this.list.splice(index, 1)
this.transferTodoCount += 1
this.tui.toast('接单成功')
} else {
this.tui.toast(res.message)
this.resetList()
}
}).catch(() => {
this.tui.toast('接单失败,请重试')
}).then(() => {
this.acceptingTransferId = ''
if (this.list[index] && this.list[index].id === item.id) {
this.$set(this.list[index], 'transferAccepting', false)
}
uni.hideLoading()
})
},
productDetail(item) {
const goodsList = item.goodsList || item.mallOrderGoodsList || item.orderGoodsList || []
this.productData = goodsList
@ -290,6 +400,7 @@
.photo-btn,.main-btn{flex:1;height:76rpx;line-height:76rpx;text-align:center;border-radius:38rpx;font-weight:700;}
.photo-btn{background:#f2f6f4;color:#0b9b73;}
.main-btn{background:linear-gradient(90deg,#e3ff96,#a6ffea);color:#00231c;}
.main-btn-disabled{opacity:.58;}
.empty{text-align:center;color:#8a9692;padding:80rpx 0;}
.product-popup-content {
width: 680rpx;

65
pages/index/index.vue

@ -1,4 +1,5 @@
<template>
<page-meta :page-style="areaPopupVisible ? 'overflow: hidden;' : ''"></page-meta>
<view class="page1" :class="{'is-sticky': lastScrollTop>stickyTrigger}">
<view class="swiper-container">
<view class="swiper-page" v-show="currentIndex == 0">
@ -466,8 +467,8 @@
</view>
</view> -->
<!-- 弹出区域选择 -->
<uni-popup ref="areaPopup" class="home-popup-layer" :is-mask-click="areaPopupCanClose" background-color="transparent">
<view class="campus-area-popup">
<uni-popup ref="areaPopup" class="home-popup-layer" :is-mask-click="areaPopupCanClose" background-color="transparent" @change="onAreaPopupChange">
<view class="campus-area-popup" @touchmove.stop>
<view class="area-popup-glow area-popup-glow-a"></view>
<view class="area-popup-glow area-popup-glow-b"></view>
<view class="campus-area-header">
@ -486,7 +487,7 @@
<input v-model="areaKeyword" confirm-type="search" placeholder="搜索学校 / 校区 / 区域"
placeholder-class="area-search-placeholder" />
</view>
<scroll-view scroll-y class="campus-area-list">
<scroll-view scroll-y class="campus-area-list" @touchmove.stop>
<view
class="campus-area-item"
:class="{ 'campus-area-item--suggest': suggestedArea && suggestedArea.id == item.id }"
@ -569,7 +570,7 @@
import myCenter from "@/components/tab-bar/myCenter.vue";
import homeGroupPending from "@/components/tab-bar/homeGroupPending.vue";
import { getIeRealNameAuditSetting } from '@/common/ieApi.js'
import { resolveHomeShareTarget, normalizeShareTargetPath } from '@/utils/sharePath.js'
import { resolveHomeShareTarget, normalizeShareTargetPath, resolveMiniCodeSceneTarget } from '@/utils/sharePath.js'
const HOME_AD_IMAGE_CACHE_KEY = 'home_ad_image_cache_v1'
const HOME_POPUP_AD_SHOWN_KEY_PREFIX = 'home_popup_ad_shown_v1'
@ -578,6 +579,7 @@
const homeAdImageDownloading = {}
let groupBuySubPackagePreloaded = false
let groupBuySubPackagePreloading = false
let groupBuySubPackagePromise = null
const HOME_MENU_ICON_URLS = {
food: 'https://jewel-shop.oss-cn-beijing.aliyuncs.com/553230c20dea4e5c85f1e396058a7493.png',
store: 'https://jewel-shop.oss-cn-beijing.aliyuncs.com/28b5cfec79d74871b0ce67743fb7619c.png',
@ -747,6 +749,7 @@
areaList: [],
areaKeyword: '',
areaPopupCanClose: false,
areaPopupVisible: false,
suggestedArea: null,
orderLists: [],
waimaiCount: 0,
@ -957,7 +960,7 @@
if (option && option.deliveryRuleEdited == 1) {
this.pendingDeliveryRuleConfirm = true
}
const shareTargetPath = resolveHomeShareTarget(option)
const shareTargetPath = resolveMiniCodeSceneTarget(option && option.scene) || resolveHomeShareTarget(option)
if (shareTargetPath) {
this.pendingShareTargetPath = shareTargetPath
uni.setStorageSync('pendingShareTargetPath', shareTargetPath)
@ -999,7 +1002,9 @@
}
},
onHide() {
this.areaPopupVisible = false
this.closeProductPopup()
clearTimeout(this._groupBuyPreloadTimer)
this.stopDeliveryTimeTimer()
},
onReady() {
@ -1009,13 +1014,19 @@
}, 500);
},
onUnload() {
this.areaPopupVisible = false
this.closeProductPopup()
clearTimeout(this._groupBuyPreloadTimer)
this.stopDeliveryTimeTimer()
},
onShareAppMessage() {
return this.buildHomeShareInfo()
},
methods: {
onAreaPopupChange(e) {
const detail = e && e.detail ? e.detail : e
this.areaPopupVisible = !!(detail && detail.show)
},
enableShareMenu() {
if (!uni.showShareMenu) return
uni.showShareMenu({
@ -1073,20 +1084,27 @@
}, 300)
},
preloadGroupBuyListPage() {
if (groupBuySubPackagePreloaded || groupBuySubPackagePreloading) return
if (groupBuySubPackagePreloaded) return Promise.resolve()
if (groupBuySubPackagePromise) return groupBuySubPackagePromise
// #ifdef MP-WEIXIN
if (typeof wx === 'undefined' || !wx.loadSubpackage) return
if (typeof wx === 'undefined' || !wx.loadSubpackage) return Promise.resolve()
groupBuySubPackagePreloading = true
wx.loadSubpackage({
root: 'package2',
success: () => {
groupBuySubPackagePreloaded = true
},
complete: () => {
groupBuySubPackagePreloading = false
}
groupBuySubPackagePromise = new Promise((resolve) => {
wx.loadSubpackage({
root: 'package2',
success: () => {
groupBuySubPackagePreloaded = true
},
complete: () => {
groupBuySubPackagePreloading = false
groupBuySubPackagePromise = null
resolve()
}
})
})
return groupBuySubPackagePromise
// #endif
return Promise.resolve()
},
normalizeShareTargetPath(value) {
return normalizeShareTargetPath(value)
@ -1125,6 +1143,13 @@
uni.removeStorageSync('authPendingTargetPath')
this.$nextTick(() => {
setTimeout(() => {
const pages = getCurrentPages ? getCurrentPages() : []
const currentPage = pages[pages.length - 1]
const currentRoute = currentPage && currentPage.route ? '/' + currentPage.route : ''
if (currentRoute && currentRoute !== '/pages/index/index') {
this.openingShareTargetPath = ''
return
}
uni.navigateTo({
url: targetPath,
fail: () => {
@ -1962,8 +1987,9 @@
url: '/package3/seckill/seckillGroup'
})
},
goPendingGroup() {
this.preloadGroupBuyListPage()
async goPendingGroup() {
clearTimeout(this._groupBuyPreloadTimer)
await this.preloadGroupBuyListPage()
uni.navigateTo({
url: '/package2/group/groupPendingList'
})
@ -1976,7 +2002,8 @@
url = '/package1/runErrand/runErrand'
break;
case '2':
this.preloadGroupBuyListPage()
clearTimeout(this._groupBuyPreloadTimer)
await this.preloadGroupBuyListPage()
url = '/package2/group/groupBuyList'
break;
case '3':
@ -2052,6 +2079,7 @@
if (this.$refs.areaPopup) {
this.$refs.areaPopup.close()
}
this.areaPopupVisible = false
this.tui.toast(fromManual ? '切换成功' : ('已定位到:' + (item.title || '当前区域')))
if (this.$refs.myCenterPage && this.$refs.myCenterPage.init) {
this.$refs.myCenterPage.init()
@ -2152,6 +2180,7 @@
this.suggestedArea = hit
}
}
this.areaPopupVisible = true
this.$refs.areaPopup.open();
} else {
this.tui.toast(res.message)

21
utils/sharePath.js

@ -33,6 +33,27 @@ export function buildGroupTargetPath(params) {
return targetPath
}
export function buildGroupShopTargetPath(shopId) {
if (!shopId) return ''
return '/package2/group/groupBuySingle?type=shop&shopId=' + encodeURIComponent(shopId)
}
export function resolveMiniCodeSceneTarget(scene) {
let value = String(scene || '').trim()
if (!value) return ''
try {
value = decodeURIComponent(value)
} catch (e) {}
if (!value) return ''
if (value.indexOf('groupShop:') === 0) {
return buildGroupShopTargetPath(value.replace('groupShop:', ''))
}
if (/^s[A-Za-z0-9_-]{1,31}$/.test(value)) {
return buildGroupShopTargetPath(value.slice(1))
}
return ''
}
export function normalizeShareTargetPath(value) {
let targetPath = String(value || '')
if (targetPath.indexOf('%2F') === 0 || targetPath.indexOf('%2f') === 0) {

Loading…
Cancel
Save