| | |
| | | </view> |
| | | </view> |
| | | <!-- 活动介绍 --> |
| | | <view class="wrapper bg-white radius24" v-if="activityData.content"> |
| | | <view class="wrapper bg-white radius24" v-if="activityData.content || activityData.describe"> |
| | | <view class="info-title-box tc pt30 fb f30"> |
| | | <view class="info-title">活动介绍</view> |
| | | </view> |
| | | <view class="activity-desc p30" v-html="activityData.content"></view> |
| | | <view class="activity-desc p30" v-html="activityData.content||activityData.describe"></view> |
| | | <!-- <view class="activity-desc p30" v-else>{{ activityData.describe }}</view> --> |
| | | </view> |
| | | </view> |
| | | <view class="detail-footer"> |
| | |
| | | import Share from './popup/share.vue'; |
| | | import Album from './popup/album.vue'; // 相册 |
| | | import uniPopup from '@/components/uni-popup.vue'; |
| | | import utils from '@/common/utils.js'; |
| | | export default { |
| | | components: { |
| | | RegForm, |
| | |
| | | longitude: 0, |
| | | isVerify: false, // 签到成功弹窗 |
| | | in_radius: false, // 用户是否在活动地点限定范围内 |
| | | user_verify: 0, // 核销标识,只有扫核销码进来值才是1 |
| | | } |
| | | }, |
| | | onLoad(e) { |
| | | /*活动id*/ |
| | | if (typeof e.activity_id != 'undefined') { |
| | | this.activity_id = e.activity_id; |
| | | } else { |
| | | let scene = utils.getSceneData(e); |
| | | let scene = utils.getSceneData(e); |
| | | if (scene && typeof scene === 'object' && Object.keys(scene).length > 0) { |
| | | this.activity_id = scene.activity_id; |
| | | this.user_verify = scene.user_verify; |
| | | } else { |
| | | this.activity_id = e.activity_id; |
| | | } |
| | | }, |
| | | onShow() { |
| | | this.getUserLocation(); |
| | | this.getData(); |
| | | }, |
| | | onUnload() { |
| | | this.stopWatchingLocation(); |
| | | }, |
| | | methods: { |
| | | // 获取用户坐标并判断在不在活动范围内 |
| | | getUserLocation() { |
| | | let self = this; |
| | | uni.getLocation({ |
| | | type: 'gcj02', |
| | | success: (res) => { |
| | | self.latitude = res.latitude; |
| | | self.longitude = res.longitude; |
| | | // 计算距离 |
| | | const distance = this.calculateDistance( |
| | | res.latitude, res.longitude, |
| | | parseFloat(self.activityData.latitude), parseFloat(self.activityData.longitude) |
| | | ); |
| | | if (distance <= self.activityData.radius) { |
| | | self.in_radius = true; |
| | | } |
| | | }, |
| | | fail: (err) => { |
| | | console.error('获取位置失败:', err); |
| | | uni.showToast({ |
| | | title:'获取位置失败' + err, |
| | | icon: 'none' |
| | | }); |
| | | } |
| | | }); |
| | | }, |
| | | |
| | | /*获取数据*/ |
| | | getData() { |
| | | let self = this; |
| | |
| | | function(res) { |
| | | self.activityData = res.data.detail; |
| | | self.userList = res.data.userList; |
| | | if (self.activityData.radius > 0) { |
| | | self.getUserLocation(); |
| | | // 当活动设置了签到范围并且是扫核销码进来的 |
| | | if (self.activityData.radius > 0 && self.user_verify) { |
| | | self.initializeWithLocation(); |
| | | } |
| | | self.loadding = false; |
| | | uni.hideLoading(); |
| | | } |
| | | ); |
| | | }, |
| | | |
| | | // 初始化并等待位置信息 |
| | | async initializeWithLocation() { |
| | | try { |
| | | |
| | | // 等待获取第一个有效位置 |
| | | const location = await this.startWatchingLocation(); |
| | | |
| | | console.log('获得初始位置:', location); |
| | | |
| | | // 检查用户是否在活动范围 |
| | | this.updateInRadius(); |
| | | |
| | | } catch (error) { |
| | | console.error('初始化失败:', error); |
| | | uni.showToast({ |
| | | title: '位置获取失败,请重新扫码试试', |
| | | icon: 'none' |
| | | }); |
| | | } |
| | | }, |
| | | |
| | | // 修改后的开始监听位置方法,返回 Promise |
| | | startWatchingLocation() { |
| | | return new Promise(async (resolve, reject) => { |
| | | try { |
| | | // 存储 resolve 和 reject 以便在位置回调中使用 |
| | | this.locationResolve = resolve; |
| | | this.locationReject = reject; |
| | | |
| | | // 申请定位权限 |
| | | await this.authorizeLocation(); |
| | | |
| | | // 开始位置更新 |
| | | await this.startLocationUpdate(); |
| | | |
| | | // 监听位置变化 |
| | | this.watchLocationChange(); |
| | | |
| | | // 设置超时处理 |
| | | setTimeout(() => { |
| | | if (this.locationReject) { |
| | | this.locationReject(new Error('位置获取超时')); |
| | | this.locationResolve = null; |
| | | this.locationReject = null; |
| | | } |
| | | }, 10000); // 10秒超时 |
| | | |
| | | } catch (error) { |
| | | reject(error); |
| | | } |
| | | }); |
| | | }, |
| | | |
| | | // 授权定位 |
| | | authorizeLocation() { |
| | | return new Promise((resolve, reject) => { |
| | | uni.authorize({ |
| | | scope: 'scope.userLocation', |
| | | success: resolve, |
| | | fail: (err) => { |
| | | uni.showModal({ |
| | | title: '提示', |
| | | content: '需要您授权位置信息,请前往设置页开启权限', |
| | | success: (modalRes) => { |
| | | if (modalRes.confirm) { |
| | | uni.openSetting({ |
| | | success: (openRes) => { |
| | | if (openRes.authSetting[ |
| | | 'scope.userLocation' |
| | | ]) { |
| | | resolve(); |
| | | } else { |
| | | reject(new Error( |
| | | '用户未授权位置权限')); |
| | | } |
| | | } |
| | | }); |
| | | } else { |
| | | reject(new Error('用户取消授权')); |
| | | } |
| | | } |
| | | }); |
| | | } |
| | | }); |
| | | }); |
| | | }, |
| | | |
| | | // 开启位置更新 |
| | | startLocationUpdate() { |
| | | return new Promise((resolve, reject) => { |
| | | uni.startLocationUpdate({ |
| | | success: resolve, |
| | | fail: reject |
| | | }); |
| | | }); |
| | | }, |
| | | |
| | | // 监听位置变化 |
| | | watchLocationChange() { |
| | | if (this._locationChangeCallback) { |
| | | uni.offLocationChange(this._locationChangeCallback); |
| | | } |
| | | |
| | | this._locationChangeCallback = (res) => { |
| | | console.log('位置变化:', res); |
| | | this.latitude = res.latitude; |
| | | this.longitude = res.longitude; |
| | | |
| | | // 如果是第一次获取位置,解析 Promise |
| | | if (this.locationResolve) { |
| | | this.locationResolve({ |
| | | latitude: res.latitude, |
| | | longitude: res.longitude, |
| | | accuracy: res.accuracy |
| | | }); |
| | | this.locationResolve = null; |
| | | this.locationReject = null; |
| | | } |
| | | |
| | | // 位置更新后处理 |
| | | this.updateInRadius(); |
| | | }; |
| | | |
| | | uni.onLocationChange(this._locationChangeCallback); |
| | | }, |
| | | |
| | | // 检查是否在签到范围 |
| | | updateInRadius() { |
| | | let self = this; |
| | | // 如果不是扫码进来的或者没有设置活动范围 |
| | | if (!self.user_verify || !self.activityData.radius) { |
| | | return; |
| | | } |
| | | // 计算距离 |
| | | const distance = this.calculateDistance( |
| | | parseFloat(self.latitude), |
| | | parseFloat(self.longitude), |
| | | parseFloat(self.activityData.latitude), |
| | | parseFloat(self.activityData.longitude) |
| | | ); |
| | | if (distance <= self.activityData.radius) { |
| | | self.in_radius = true; |
| | | } |
| | | }, |
| | | |
| | | // 停止监听位置 |
| | | stopWatchingLocation() { |
| | | if (this._locationChangeCallback) { |
| | | uni.offLocationChange(this._locationChangeCallback); |
| | | this._locationChangeCallback = null; |
| | | } |
| | | |
| | | // 清理 Promise 相关状态 |
| | | if (this.locationReject) { |
| | | this.locationReject(new Error('位置监听已停止')); |
| | | this.locationResolve = null; |
| | | this.locationReject = null; |
| | | } |
| | | |
| | | uni.stopLocationUpdate(); |
| | | }, |
| | | |
| | | // 获取用户坐标并判断在不在活动范围内(弃用) |
| | | // getUserLocation() { |
| | | // let self = this; |
| | | // uni.getLocation({ |
| | | // type: 'gcj02', |
| | | // success: (res) => { |
| | | // self.latitude = res.latitude; |
| | | // self.longitude = res.longitude; |
| | | // // 计算距离 |
| | | // const distance = this.calculateDistance( |
| | | // res.latitude, res.longitude, |
| | | // parseFloat(self.activityData.latitude), parseFloat(self.activityData.longitude) |
| | | // ); |
| | | // if (distance <= self.activityData.radius) { |
| | | // self.in_radius = true; |
| | | // } |
| | | // }, |
| | | // fail: (err) => { |
| | | // console.error('获取位置失败:', err); |
| | | // uni.showToast({ |
| | | // title: '获取位置失败' + err, |
| | | // icon: 'none' |
| | | // }); |
| | | // } |
| | | // }); |
| | | // }, |
| | | |
| | | onReg() { |
| | | this.isOpenReg = true; |
| | | this.$refs.regForm.activityData = this.activityData; |
| | | if (this.activityData.fee > 0) { |
| | | this.isOpenReg = true; |
| | | } else { |
| | | this.$refs.regForm.onSubmit(); |
| | | } |
| | | }, |
| | | |
| | | closeRegFunc(e) { |
| | |
| | | let self = this; |
| | | if (!self.in_radius && self.activityData.radius > 0) { |
| | | uni.showModal({ |
| | | title: '签到失败', |
| | | content: `您距离活动地点太远,无法签到`, |
| | | showCancel: false |
| | | title: '签到失败', |
| | | content: `您距离活动地点太远,无法签到`, |
| | | showCancel: false |
| | | }); |
| | | return false; |
| | | } else { |
| | | self.doVierfy(); |
| | | } |
| | | }, |
| | | |
| | | |
| | | // 执行签到 |
| | | doVierfy() { |
| | | let self = this; |
| | |
| | | } |
| | | ); |
| | | }, |
| | | |
| | | |
| | | // 计算坐标距离 |
| | | calculateDistance(lat1, lng1, lat2, lng2) { |
| | | const R = 6371000; // 地球半径(米) |
| | | const toRadians = (degree) => degree * (Math.PI / 180); |
| | | |
| | | const φ1 = toRadians(lat1); |
| | | const φ2 = toRadians(lat2); |
| | | const Δφ = toRadians(lat2 - lat1); |
| | | const Δλ = toRadians(lng2 - lng1); |
| | | |
| | | const a = Math.sin(Δφ / 2) * Math.sin(Δφ / 2) + |
| | | Math.cos(φ1) * Math.cos(φ2) * |
| | | Math.sin(Δλ / 2) * Math.sin(Δλ / 2); |
| | | const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)); |
| | | |
| | | const distance = R * c; |
| | | return Math.round(distance * 100) / 100; // 保留两位小数 |
| | | const R = 6371000; // 地球半径(米) |
| | | const toRadians = (degree) => degree * (Math.PI / 180); |
| | | |
| | | const φ1 = toRadians(lat1); |
| | | const φ2 = toRadians(lat2); |
| | | const Δφ = toRadians(lat2 - lat1); |
| | | const Δλ = toRadians(lng2 - lng1); |
| | | |
| | | const a = Math.sin(Δφ / 2) * Math.sin(Δφ / 2) + |
| | | Math.cos(φ1) * Math.cos(φ2) * |
| | | Math.sin(Δλ / 2) * Math.sin(Δλ / 2); |
| | | const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)); |
| | | |
| | | const distance = R * c; |
| | | return Math.round(distance * 100) / 100; // 保留两位小数 |
| | | }, |
| | | |
| | | // 打开相册 |
| | |
| | | } |
| | | }); |
| | | }, |
| | | |
| | | |
| | | gotoSupplier() { |
| | | this.gotoPage('pages/shop/shop?shop_supplier_id=' + this.activityData.visit_supplier_id); |
| | | }, |
| | | |
| | | } |
| | | } |
| | | </script> |
| | |
| | | height: 120rpx; |
| | | } |
| | | } |
| | | |
| | | |
| | | .status-verify { |
| | | position: absolute; |
| | | bottom: 200rpx; |
| | |
| | | border-radius: 60rpx 0 0 60rpx; |
| | | font-size: 28rpx; |
| | | padding: 6rpx 20rpx; |
| | | |
| | | |
| | | .iconfont { |
| | | margin-right: 10rpx; |
| | | } |