fix;修复bug

This commit is contained in:
2025-11-08 16:00:44 +08:00
parent 85702d8d08
commit 50a711bba8
13 changed files with 248 additions and 188 deletions
+7 -6
View File
@@ -17,18 +17,19 @@
</cover-view> </cover-view>
<cover-view class="map-side-controls" v-if="!props.hideControls && !props.hideMapOverlays"> <cover-view class="map-side-controls" v-if="!props.hideControls && !props.hideMapOverlays">
<cover-view class="side-btn locate" @tap="handleRelocate">
<cover-image class="side-icon" src="/static/location.png"></cover-image>
<!-- <cover-view class="side-text">定位</cover-view> -->
</cover-view>
<cover-view class="side-btn service" @tap="handleService"> <cover-view class="side-btn service" @tap="handleService">
<cover-image class="side-icon" src="/static/customer-service.png"></cover-image> <cover-image class="side-icon" src="/static/customer-service.png"></cover-image>
<!-- <cover-view class="side-text">客服</cover-view> --> <!-- <cover-view class="side-text">客服</cover-view> -->
</cover-view> </cover-view>
<cover-view class="side-btn search" @tap="handleSearch"> <cover-view class="side-btn search" @tap="handleSearch">
<cover-image class="side-icon" src="/static/search-icon.png"></cover-image> <cover-image class="side-icon" src="/static/other_device.png"></cover-image>
<!-- <cover-view class="side-text">搜索</cover-view> --> <!-- <cover-view class="side-text">搜索</cover-view> -->
</cover-view> </cover-view>
<cover-view class="side-btn locate" @tap="handleRelocate">
<cover-image class="side-icon" src="/static/location.png"></cover-image>
<!-- <cover-view class="side-text">定位</cover-view> -->
</cover-view>
</cover-view> </cover-view>
</map> </map>
@@ -530,7 +531,7 @@ const handleSearch = () => {
flex-direction: row; flex-direction: row;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
box-shadow: 0 6rpx 20rpx rgba(0, 0, 0, 0.12); // box-shadow: 0 6rpx 20rpx rgba(0, 0, 0, 0.12);
padding: 20rpx; padding: 20rpx;
border: 2rpx solid #e0e0e0; border: 2rpx solid #e0e0e0;
+29 -13
View File
@@ -16,6 +16,9 @@
<text class="highlight">{{ $t('order.depositFree') }}</text> <text class="highlight">{{ $t('order.depositFree') }}</text>
</view> </view>
</view> </view>
<view class="payment-badge whitelist" v-else-if="order.payWay == 'wx_global_pay'">
<text class="badge-text">{{ $t('order.whitelistOrder') }}</text>
</view>
<view class="payment-badge member" v-else-if="order.payWay == 'wx_member_pay'"> <view class="payment-badge member" v-else-if="order.payWay == 'wx_member_pay'">
<text class="badge-text">{{ $t('order.memberOrder') }}</text> <text class="badge-text">{{ $t('order.memberOrder') }}</text>
</view> </view>
@@ -59,10 +62,22 @@
<!-- 订单底部 --> <!-- 订单底部 -->
<view class="order-footer"> <view class="order-footer">
<view class="footer-left"> <view class="footer-left">
<view v-if="isInUse" class="renting"><text class="dot"></text>{{ $t('order.renting') }}</view> <view v-if="isInUse" class="renting">
<image src="/static/order_time.png" mode="aspectFit" class="icon-time"></image>
{{ $t('order.renting') }}
</view>
<view v-else-if="isFinished" class="meta"> <view v-else-if="isFinished" class="meta">
<view class="meta-item"><text class="dot"></text>{{ usedDurationText }}</view> <view class="meta-item">
<view class="meta-item"><text class="currency"></text>{{ displayAmount }}</view> <image src="/static/order_time.png" mode="aspectFit" class="icon-time"></image>
{{ usedDurationText }}
</view>
<view class="meta-item">
<image src="/static/order_price.png" mode="aspectFit" class="icon-price"></image>
{{ displayAmount }}
</view>
</view>
<view v-else-if="isCancelled" class="cancelled">
{{ $t('order.orderCancelled') }}
</view> </view>
</view> </view>
@@ -127,6 +142,7 @@
const isWaitingForPayment = computed(() => normalizedStatus.value === 'waiting_for_payment'); const isWaitingForPayment = computed(() => normalizedStatus.value === 'waiting_for_payment');
const isInUse = computed(() => normalizedStatus.value === 'in_used'); const isInUse = computed(() => normalizedStatus.value === 'in_used');
const isFinished = computed(() => normalizedStatus.value === 'used_done'); const isFinished = computed(() => normalizedStatus.value === 'used_done');
const isCancelled = computed(() => normalizedStatus.value === 'order_cancelled');
const titleText = computed(() => $t('order.rentFan')); const titleText = computed(() => $t('order.rentFan'));
@@ -202,16 +218,15 @@
border-radius: 0; border-radius: 0;
white-space: nowrap; white-space: nowrap;
&.wx-score { }
&.member { }
&.deposit { }
.badge-icon { width: 32rpx; height: 26rpx; margin-right: 8rpx; } .badge-icon { width: 32rpx; height: 26rpx; margin-right: 8rpx; }
.badge-text { font-size: 22rpx; color: #07c160; font-weight: 500; background: transparent; } .badge-text { font-size: 22rpx; color: #07c160; font-weight: 500; background: transparent; }
.divider { margin: 0 6rpx; color: #07c160; } .divider { margin: 0 6rpx; color: #07c160; }
} }
// 不同支付方式的文字颜色 // 不同支付方式的文字颜色
.payment-badge.whitelist {
.badge-text, .divider { color: #FF9800; }
}
.payment-badge.member { .payment-badge.member {
.badge-text, .divider { color: #1976D2; } .badge-text, .divider { color: #1976D2; }
} }
@@ -333,12 +348,13 @@
background: #fafafa; background: #fafafa;
border-top: 1rpx solid #f0f0f0; border-top: 1rpx solid #f0f0f0;
.footer-left { display: flex; align-items: center; } .footer-left { display: flex; align-items: center; }
.renting { font-size: 26rpx; color: #333; display: flex; align-items: center; } .renting { font-size: 26rpx; color: #333; display: flex; align-items: center; }
.meta { display: flex; align-items: center; } .meta { display: flex; align-items: center; }
.meta-item { font-size: 26rpx; color: #333; display: flex; align-items: center; margin-right: 28rpx; } .meta-item { font-size: 26rpx; color: #333; display: flex; align-items: center; margin-right: 28rpx; }
.dot { width: 16rpx; height: 16rpx; background: #000; border-radius: 50%; margin-right: 12rpx; display: inline-block; } .cancelled { font-size: 26rpx; color: #999; }
.currency { margin-right: 4rpx; color: #333; } .icon-time { width: 32rpx; height: 32rpx; margin-right: 8rpx; }
.icon-price { width: 32rpx; height: 32rpx; margin-right: 8rpx; }
.actions { .actions {
display: flex; display: flex;
+9
View File
@@ -27,3 +27,12 @@ export const getCommonByBrand = (brandName) => {
}) })
} }
// 查询激活的且临近关闭时间最近的活动
export const getActiveActivity = () => {
return request({
url: '/app/activity/nearest-active',
method: 'get',
hideLoading: true
})
}
+2 -2
View File
@@ -1,5 +1,5 @@
export const URL = "https://my.gxfs123.com/api" //正式服务器 // export const URL = "https://my.gxfs123.com/api" //正式服务器
// export const URL = "https://fansdev.gxfs123.com/api" //测试服务器 export const URL = "https://fansdev.gxfs123.com/api" //测试服务器
// export const URL = "http://192.168.5.149:8080" //本地调试 // export const URL = "http://192.168.5.149:8080" //本地调试
// export const URL = "http://127.0.0.1:8080" //本地调试 // export const URL = "http://127.0.0.1:8080" //本地调试
+1
View File
@@ -188,6 +188,7 @@ export default {
confirmReturn: 'Confirm to return device?', confirmReturn: 'Confirm to return device?',
wxPayScore: 'WeChat Pay Score', wxPayScore: 'WeChat Pay Score',
depositFree: 'Deposit-free', depositFree: 'Deposit-free',
whitelistOrder: 'Whitelist Order',
memberOrder: 'Member Order', memberOrder: 'Member Order',
wxPay: 'WeChat Pay', wxPay: 'WeChat Pay',
depositPay: 'Deposit Pay', depositPay: 'Deposit Pay',
+1
View File
@@ -188,6 +188,7 @@ export default {
confirmReturn: '确认归还设备?', confirmReturn: '确认归还设备?',
wxPayScore: '微信支付分', wxPayScore: '微信支付分',
depositFree: '免押租借', depositFree: '免押租借',
whitelistOrder: '白名单订单',
memberOrder: '会员订单', memberOrder: '会员订单',
wxPay: '微信支付', wxPay: '微信支付',
depositPay: '押金租借', depositPay: '押金租借',
+63 -94
View File
@@ -23,16 +23,16 @@
<text class="card-title">{{ $t('device.pricingRules') }}</text> <text class="card-title">{{ $t('device.pricingRules') }}</text>
</view> </view>
<view class="pricing-banner"> <view class="pricing-banner">
<view class="pricing-main"> <view class="pricing-main">
<text class="price-symbol">¥</text> <text class="price-symbol">¥</text>
<text class="price">{{ deviceFeeConfig.maxHourPrice || '5.00' }}</text> <text class="price">{{ deviceFeeConfig.maxHourPrice || '5.00' }}</text>
<text class="unit">/{{ $t('time.hour') }}</text> <text class="unit">/{{ getPriceUnit() }}</text>
</view>
<view class="cap-badge">
<text class="cap-text">{{ deviceInfo.depositAmount || '99' }}{{ $t('device.capLimit') }}</text>
</view>
</view> </view>
<view class="cap-badge">
<text class="cap-text">{{ deviceInfo.depositAmount || '99' }}{{ $t('device.capLimit') }}</text>
</view>
</view>
<view class="pricing-info"> <view class="pricing-info">
<view class="info-icon"> <view class="info-icon">
@@ -111,8 +111,7 @@
onMounted onMounted
} from 'vue' } from 'vue'
import { import {
onLoad, onLoad
onShow
} from '@dcloudio/uni-app' } from '@dcloudio/uni-app'
import { import {
getDeviceInfo, getDeviceInfo,
@@ -120,14 +119,9 @@
} from '@/config/api/device.js' } from '@/config/api/device.js'
import { import {
getOrderByOrderNoScore, getOrderByOrderNoScore,
getOrderByOrderNoScorePayStatus,
getOrderByOrderNo, getOrderByOrderNo,
updateOrderPackage,
cancelOrder cancelOrder
} from '@/config/api/order.js' } from '@/config/api/order.js'
import {
URL
} from "@/config/url.js"
import { import {
initiateWeChatScorePayment, initiateWeChatScorePayment,
getUserInfo, getUserInfo,
@@ -147,7 +141,6 @@
const deviceFeeConfig = ref({}) const deviceFeeConfig = ref({})
const positionInfo = ref({}) const positionInfo = ref({})
const deviceLocation = ref('一号教学楼大厅') const deviceLocation = ref('一号教学楼大厅')
const batteryLevel = ref(95)
const hasActiveOrder = ref(false) const hasActiveOrder = ref(false)
const deviceStatus = reactive({ const deviceStatus = reactive({
text: $t('device.available'), text: $t('device.available'),
@@ -164,7 +157,6 @@
uni.setStorageSync('deviceId', options.deviceNo) uni.setStorageSync('deviceId', options.deviceNo)
} else { } else {
deviceId.value = uni.getStorageSync('deviceId') deviceId.value = uni.getStorageSync('deviceId')
// uni.removeStorageSync('deviceId')
} }
await checkOrderStatus() await checkOrderStatus()
await fetchDeviceInfo() await fetchDeviceInfo()
@@ -178,10 +170,6 @@
await fetchDeviceInfo() await fetchDeviceInfo()
}) })
// onShow(async () => {
// await fetchDeviceInfo()
// })
const checkUserPhone = async () => { const checkUserPhone = async () => {
try { try {
const userInfoRes = await getUserInfo() const userInfoRes = await getUserInfo()
@@ -318,15 +306,14 @@
} }
} }
if (deviceInfo.value.feeConfig) { if (deviceInfo.value.feeConfig) {
deviceFeeConfig.value = JSON.parse(deviceInfo.value.feeConfig)[0] || {} deviceFeeConfig.value = JSON.parse(deviceInfo.value.feeConfig)[0] || {}
console.log('deviceFeeConfig', deviceFeeConfig.value); console.log('deviceFeeConfig', deviceFeeConfig.value);
} else { } else {
deviceFeeConfig.value = { deviceFeeConfig.value = {
maxHourPrice: '5.00', maxHourPrice: '5.00',
}
discount.value = '99.00'
} }
}
} }
@@ -395,14 +382,23 @@
submitRentOrder(payWay) submitRentOrder(payWay)
} }
const selectedPkg = reactive({ // 获取价格单位文本
time: '1小时', const getPriceUnit = () => {
price: '5' // 按分钟计费
}) if (deviceInfo.value && deviceInfo.value.feeType === 'minute') {
const depositAmount = ref('99.00') return '分钟'
}
// 按小时计费(默认)
return $t('time.hour')
}
// 计算计费单位时间(分钟) // 计算计费单位时间(分钟)
const getBillingUnitMinutes = () => { const getBillingUnitMinutes = () => {
// 按分钟计费时,单位为1分钟
if (deviceInfo.value && deviceInfo.value.feeType === 'minute') {
return 1
}
// 按小时计费
if (!deviceFeeConfig.value || !deviceFeeConfig.value.hourPrice) return 60 if (!deviceFeeConfig.value || !deviceFeeConfig.value.hourPrice) return 60
const hourPrice = parseFloat(deviceFeeConfig.value.hourPrice) const hourPrice = parseFloat(deviceFeeConfig.value.hourPrice)
// hourPrice 为 0.5 时表示 30 分钟,为 1 时表示 60 分钟 // hourPrice 为 0.5 时表示 30 分钟,为 1 时表示 60 分钟
@@ -413,8 +409,14 @@
const getBillingUnitPrice = () => { const getBillingUnitPrice = () => {
if (!deviceFeeConfig.value || !deviceFeeConfig.value.maxHourPrice) return '5' if (!deviceFeeConfig.value || !deviceFeeConfig.value.maxHourPrice) return '5'
const maxHourPrice = parseFloat(deviceFeeConfig.value.maxHourPrice) const maxHourPrice = parseFloat(deviceFeeConfig.value.maxHourPrice)
// 按分钟计费时,直接返回每分钟价格
if (deviceInfo.value && deviceInfo.value.feeType === 'minute') {
return maxHourPrice.toFixed(2)
}
// 按小时计费
const hourPrice = parseFloat(deviceFeeConfig.value.hourPrice || 1) const hourPrice = parseFloat(deviceFeeConfig.value.hourPrice || 1)
// maxHourPrice 是1小时的价格,需要根据 hourPrice 换算
const unitPrice = maxHourPrice * hourPrice const unitPrice = maxHourPrice * hourPrice
return unitPrice.toFixed(2) return unitPrice.toFixed(2)
} }
@@ -425,21 +427,18 @@
return parseInt(positionInfo.value.freeRentTime) return parseInt(positionInfo.value.freeRentTime)
} }
// 计算24小时封顶价格
const get24HourCapPrice = () => {
if (!deviceFeeConfig.value || !deviceFeeConfig.value.maxHourPrice || !deviceFeeConfig.value.maxHour)
return '45.00'
const maxHourPrice = parseFloat(deviceFeeConfig.value.maxHourPrice)
const maxHour = parseFloat(deviceFeeConfig.value.maxHour)
return (maxHourPrice * maxHour).toFixed(2)
}
// 生成计费说明文本 // 生成计费说明文本
const getPricingInfoText = () => { const getPricingInfoText = () => {
const unitMinutes = getBillingUnitMinutes()
const unitPrice = getBillingUnitPrice() const unitPrice = getBillingUnitPrice()
const maxHourPrice = deviceFeeConfig.value.maxHourPrice || '5' const maxHourPrice = deviceFeeConfig.value.maxHourPrice || '5'
// 按分钟计费
if (deviceInfo.value && deviceInfo.value.feeType === 'minute') {
return `${unitPrice}元/分钟`
}
// 按小时计费
const unitMinutes = getBillingUnitMinutes()
if (unitMinutes === 30) { if (unitMinutes === 30) {
return `${unitPrice}元/${unitMinutes}分钟,${maxHourPrice}元/小时` return `${unitPrice}元/${unitMinutes}分钟,${maxHourPrice}元/小时`
} else { } else {
@@ -453,6 +452,12 @@
const unitMinutes = getBillingUnitMinutes() const unitMinutes = getBillingUnitMinutes()
const depositAmount = deviceInfo.value.depositAmount || '99' const depositAmount = deviceInfo.value.depositAmount || '99'
// 按分钟计费
if (deviceInfo.value && deviceInfo.value.feeType === 'minute') {
return `${freeMinutes}分钟内归还免费,不足${unitMinutes}分钟按${unitMinutes}分钟计费,封顶${depositAmount}元,持续计费至${depositAmount}元视为买断`
}
// 按小时计费
return `${freeMinutes}分钟内归还免费,不足${unitMinutes}分钟按${unitMinutes}分钟计费,封顶${depositAmount}元,持续计费至${depositAmount}元视为买断` return `${freeMinutes}分钟内归还免费,不足${unitMinutes}分钟按${unitMinutes}分钟计费,封顶${depositAmount}元,持续计费至${depositAmount}元视为买断`
} }
@@ -496,54 +501,23 @@
throw new Error(rentResult.msg || $t('device.rentFailed')) throw new Error(rentResult.msg || $t('device.rentFailed'))
} }
// 获取后端返回的订单信息 // 获取后端返回的订单信息
const order = rentResult.data const order = rentResult.data
console.log('订单信息', order); console.log('订单信息', order);
// // --- 统一:先更新订单套餐信息 --- if (payWay == 'wx-pay') {
// try { // 当支付方式为押金支付时
// let packageTimeMinutes = 0;
// if (selectedPkg.time.includes('小时')) {
// packageTimeMinutes = parseInt(selectedPkg.time) * 60;
// } else if (selectedPkg.time.includes('分钟')) {
// packageTimeMinutes = parseInt(selectedPkg.time);
// } else {
// packageTimeMinutes = parseInt(selectedPkg.time) * 60; // 默认按小时处理
// }
// const updateRes = await updateOrderPackage({
// orderId: order.orderId,
// packageTime: packageTimeMinutes,
// packagePrice: parseFloat(selectedPkg.price)
// });
// if (updateRes.code !== 200) {
// console.warn("更新订单套餐信息失败:", updateRes.msg);
// // 这里可以选择是否提示用户或阻止流程,当前不阻止
// } else {
// console.log("订单套餐信息已提前更新");
// }
// } catch (updateError) {
// console.error("更新订单套餐信息时出错:", updateError);
// // 即使更新失败,也继续流程
// }
// --- 套餐信息更新结束 ---
if (payWay == 'wx-pay') {
//当支付方式为押金支付时
uni.hideLoading() uni.hideLoading()
const res = await getOrderByOrderNo(order.orderNo); const res = await getOrderByOrderNo(order.orderNo);
console.log(res); console.log(res);
// --- 新增:计算总金额 ---
const deposit = parseFloat(order.depositAmount); const deposit = parseFloat(order.depositAmount);
const packagePrice = parseFloat(order.unitPrice); const packagePrice = parseFloat(order.unitPrice);
const totalAmount = deposit.toFixed(2); const totalAmount = deposit.toFixed(2);
// --- 计算结束 ---
uni.hideLoading() // 跳转到订单支付页面
// 跳转到订单支付页面,传递订单ID、套餐信息和总金额
uni.redirectTo({ uni.redirectTo({
url: `/pages/order/payment?orderId=${order.orderId}&packageTimeHours=${selectedPkg.time.replace('小时', '')}&packagePrice=${packagePrice}&totalAmount=${totalAmount}&depositAmount=${depositAmount.value}${deviceInfo.value && deviceInfo.value.feeConfig ? '&feeConfig=' + encodeURIComponent(deviceInfo.value.feeConfig) : ''}` url: `/pages/order/payment?orderId=${order.orderId}&packagePrice=${packagePrice}&totalAmount=${totalAmount}&depositAmount=${deposit}${deviceInfo.value && deviceInfo.value.feeConfig ? '&feeConfig=' + encodeURIComponent(deviceInfo.value.feeConfig) : ''}`
}) })
} else if (payWay == 'wx-score-pay') { } else if (payWay == 'wx-score-pay') {
@@ -915,15 +889,10 @@
margin-right: 8rpx; margin-right: 8rpx;
} }
.credit-text { .credit-text {
font-size: 24rpx; font-size: 24rpx;
color: #999; color: #999;
}
.divider {
margin: 0 8rpx;
color: #ddd;
}
}
} }
} }
+24 -66
View File
@@ -1,21 +1,19 @@
<template> <template>
<view class="help-container"> <view class="help-container">
<!-- 常见问题 --> <!-- 常见问题 -->
<view class="faq-list"> <view class="faq-section">
<view <uv-collapse :border="false">
class="faq-item" <uv-collapse-item
v-for="(item, index) in faqList" v-for="(item, index) in faqList"
:key="index" :key="index"
@click="toggleFaq(index)" :title="$t(item.question)"
> :name="index"
<view class="faq-header"> >
<text class="question">{{ $t(item.question) }}</text> <view class="answer-content">
<view class="arrow" :class="{ open: item.isOpen }"></view> <text class="answer-text">{{ $t(item.answer) }}</text>
</view> </view>
<view class="answer" v-show="item.isOpen"> </uv-collapse-item>
{{ $t(item.answer) }} </uv-collapse>
</view>
</view>
</view> </view>
<!-- 联系客服 --> <!-- 联系客服 -->
@@ -43,10 +41,7 @@ export default {
data() { data() {
return { return {
HELP_CONTENT, HELP_CONTENT,
faqList: HELP_CONTENT.FAQ_LIST.map(item => ({ faqList: HELP_CONTENT.FAQ_LIST,
...item,
isOpen: false
})),
customerPhone: HELP_CONTENT.CONTACT.PHONE.VALUE // 默认客服电话 customerPhone: HELP_CONTENT.CONTACT.PHONE.VALUE // 默认客服电话
} }
}, },
@@ -59,9 +54,6 @@ export default {
this.customerPhone = getCustomerPhone() this.customerPhone = getCustomerPhone()
}, },
methods: { methods: {
toggleFaq(index) {
this.faqList[index].isOpen = !this.faqList[index].isOpen
},
makePhoneCall() { makePhoneCall() {
uni.makePhoneCall({ uni.makePhoneCall({
phoneNumber: this.customerPhone phoneNumber: this.customerPhone
@@ -77,55 +69,22 @@ export default {
background: #f8f8f8; background: #f8f8f8;
padding: 30rpx; padding: 30rpx;
.faq-list { .faq-section {
background: #fff; background: #fff;
border-radius: 20rpx; border-radius: 20rpx;
padding: 20rpx;
margin-bottom: 30rpx; margin-bottom: 30rpx;
box-shadow: 0 4rpx 16rpx rgba(0,0,0,0.04); box-shadow: 0 4rpx 16rpx rgba(0,0,0,0.04);
overflow: hidden;
.faq-item { .answer-content {
border-bottom: 1rpx solid #f5f5f5; padding: 20rpx 30rpx 30rpx;
background: #f9f9f9;
&:last-child { .answer-text {
border-bottom: none;
}
.faq-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 30rpx 20rpx;
.question {
font-size: 30rpx;
color: #333;
flex: 1;
padding-right: 20rpx;
}
.arrow {
width: 16rpx;
height: 16rpx;
border-right: 4rpx solid #999;
border-bottom: 4rpx solid #999;
transform: rotate(45deg);
transition: all 0.3s;
&.open {
transform: rotate(-135deg);
}
}
}
.answer {
font-size: 28rpx; font-size: 28rpx;
color: #666; color: #666;
line-height: 1.6; line-height: 1.8;
padding: 0 20rpx 30rpx; display: block;
background: #f9f9f9;
border-radius: 10rpx;
margin: 0 20rpx 20rpx;
} }
} }
} }
@@ -145,7 +104,7 @@ export default {
font-weight: 500; font-weight: 500;
margin-bottom: 20rpx; margin-bottom: 20rpx;
padding-bottom: 8rpx; padding-bottom: 8rpx;
width:fit-content; width: fit-content;
&::after { &::after {
z-index: -1; z-index: -1;
@@ -156,8 +115,7 @@ export default {
width: 88%; width: 88%;
height: 16rpx; height: 16rpx;
border-radius: 20rpx; border-radius: 20rpx;
// background: transparent; background: #07C160;
background: #07C160; // 底部高亮(微信绿)
} }
} }
+109 -4
View File
@@ -22,7 +22,7 @@
<MapComponent v-if="!isLoading && userLocation" ref="mapRef" :userLocation="userLocation" <MapComponent v-if="!isLoading && userLocation" ref="mapRef" :userLocation="userLocation"
:positionList="positionList" :filteredPositions="filteredPositions" :searchKeyword="searchKeyword" :positionList="positionList" :filteredPositions="filteredPositions" :searchKeyword="searchKeyword"
:enableMarkers="true" :enableMarkers="true"
:hideMapOverlays="showGuidePopup || showNoticePopup" :hideMapOverlays="showGuidePopup || showNoticePopup || showActivityPopup"
@relocate="handleRelocate" @scan="handleScan" @showList="showLocationList" @markerTap="selectPosition" @relocate="handleRelocate" @scan="handleScan" @showList="showLocationList" @markerTap="selectPosition"
@mapCenterChange="onMapCenterChange" /> @mapCenterChange="onMapCenterChange" />
@@ -150,6 +150,24 @@
</view> </view>
</view> </view>
</uv-popup> </uv-popup>
<!-- 活动弹窗居中弹出主要展示海报 -->
<uv-popup ref="activityPopup" mode="center" round="16" :overlay="true" :closeOnClickOverlay="false" :safeAreaInsetBottom="false">
<view class="activity-popup" v-if="activityData">
<!-- 活动海报图片 -->
<view class="activity-poster-wrapper">
<image
:src="activityData.imageUrl || activityData.posterUrl"
mode="widthFix"
class="activity-poster"
></image>
</view>
<!-- 关闭按钮 -->
<view class="activity-close-btn" @click="closeActivityPopup">
<uv-icon name="close-circle-fill" size="40" color="#ffffff"></uv-icon>
</view>
</view>
</uv-popup>
</view> </view>
</template> </template>
@@ -176,7 +194,8 @@
getPotionsDetail getPotionsDetail
} from '../../config/api/order.js' } from '../../config/api/order.js'
import { import {
getNoticeTextData getNoticeTextData,
getActiveActivity
} from '../../config/api/system.js' } from '../../config/api/system.js'
// 导入地图工具函数 // 导入地图工具函数
import { import {
@@ -214,6 +233,8 @@ const showLocationPopup = ref(false)
const isRelocating = ref(false) // 防抖标志:是否正在重新定位 const isRelocating = ref(false) // 防抖标志:是否正在重新定位
const showGuidePopup = ref(false) // 使用指南弹窗显示状态 const showGuidePopup = ref(false) // 使用指南弹窗显示状态
const showNoticePopup = ref(false) // 通知详情弹窗显示状态 const showNoticePopup = ref(false) // 通知详情弹窗显示状态
const showActivityPopup = ref(false) // 活动弹窗显示状态
const activityData = ref(null) // 活动数据
// 导航栏高度相关 // 导航栏高度相关
const statusBarHeight = ref(0) const statusBarHeight = ref(0)
@@ -267,6 +288,31 @@ const statusBarHeight = ref(0)
console.warn('缓存通知内容失败:', e); console.warn('缓存通知内容失败:', e);
} }
} }
// 查询最近的活动
const checkActiveActivity = async () => {
try {
// 检查是否已经显示过活动弹窗(本次会话)
const hasShownActivity = uni.getStorageSync('hasShownActivityInSession');
if (hasShownActivity) {
console.log('本次会话已显示过活动弹窗,跳过');
return;
}
const res = await getActiveActivity();
if (res.code === 200 && res.data) {
activityData.value = res.data;
// 延迟一下显示,避免与其他弹窗冲突
setTimeout(() => {
openActivityPopup();
// 标记本次会话已显示过活动弹窗
uni.setStorageSync('hasShownActivityInSession', true);
}, 500);
}
} catch (e) {
console.warn('查询活动失败:', e);
}
}
// 距离格式化函数 // 距离格式化函数
const formatDistance = (distanceInMeters) => { const formatDistance = (distanceInMeters) => {
@@ -282,6 +328,7 @@ const statusBarHeight = ref(0)
const mapRef = ref(null) const mapRef = ref(null)
const guidePopup = ref(null) const guidePopup = ref(null)
const noticePopup = ref(null) const noticePopup = ref(null)
const activityPopup = ref(null)
// 计算属性 // 计算属性
const searchPlaceholder = computed(() => { const searchPlaceholder = computed(() => {
@@ -344,8 +391,8 @@ const noticePopup = ref(null)
// 2. 加载场地列表 // 2. 加载场地列表
await loadPositions() await loadPositions()
// 3. 检查是否需要显示使用指南(可以根据用户行为记录来决定) // 3. 查询活动并显示弹窗
// 这里暂时默认显示,后续可以根据用户使用情况优化 await checkActiveActivity()
} catch (error) { } catch (error) {
console.error('初始化失败:', error) console.error('初始化失败:', error)
@@ -843,6 +890,21 @@ const closeNoticePopup = () => {
noticePopup.value && typeof noticePopup.value.close === 'function' && noticePopup.value.close() noticePopup.value && typeof noticePopup.value.close === 'function' && noticePopup.value.close()
} catch (e) {} } catch (e) {}
} }
// 活动弹窗控制
const openActivityPopup = () => {
try {
showActivityPopup.value = true
activityPopup.value && typeof activityPopup.value.open === 'function' && activityPopup.value.open()
} catch (e) {}
}
const closeActivityPopup = () => {
try {
showActivityPopup.value = false
activityPopup.value && typeof activityPopup.value.close === 'function' && activityPopup.value.close()
} catch (e) {}
}
</script> </script>
<script> <script>
@@ -1673,4 +1735,47 @@ const closeNoticePopup = () => {
.notice-actions { .notice-actions {
margin-top: 20rpx; margin-top: 20rpx;
} }
/* 活动弹窗样式 - 简洁海报模式 */
.activity-popup {
position: relative;
width: 600rpx;
max-width: 80vw;
background: transparent;
border-radius: 16rpx;
overflow: visible;
}
.activity-poster-wrapper {
width: 100%;
border-radius: 16rpx;
overflow: hidden;
background: #fff;
box-shadow: 0 8rpx 32rpx rgba(0, 0, 0, 0.15);
}
.activity-poster {
width: 100%;
height: auto;
display: block;
vertical-align: middle;
}
.activity-close-btn {
position: absolute;
bottom: -100rpx;
left: 50%;
transform: translateX(-50%);
width: 80rpx;
height: 80rpx;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
transition: all 0.3s ease;
&:active {
transform: translateX(-50%) scale(0.9);
}
}
</style> </style>
+3 -3
View File
@@ -56,7 +56,7 @@
<view class="nav" <view class="nav"
:class="{ disabled: !isValidCoordinate(item.latitude, item.longitude) }" :class="{ disabled: !isValidCoordinate(item.latitude, item.longitude) }"
@click.stop="navigateToPosition(item)"> @click.stop="navigateToPosition(item)">
<image src="/static/location.png" class="action-icon" mode="aspectFit"></image> <image src="/static/luxian.png" class="action-icon" mode="aspectFit"></image>
</view> </view>
<view class="distance" v-if="item.distance && isValidCoordinate(item.latitude, item.longitude)">{{ item.distance }}</view> <view class="distance" v-if="item.distance && isValidCoordinate(item.latitude, item.longitude)">{{ item.distance }}</view>
</view> </view>
@@ -295,7 +295,7 @@
.relocate-btn { .relocate-btn {
position: absolute; position: absolute;
right: 20rpx; right: 20rpx;
bottom: 20rpx; bottom: 30rpx;
width: 72rpx; width: 72rpx;
height: 72rpx; height: 72rpx;
background: rgba(255, 255, 255, 0.96); background: rgba(255, 255, 255, 0.96);
@@ -303,7 +303,7 @@
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
box-shadow: 0 6rpx 20rpx rgba(0, 0, 0, 0.12); // box-shadow: 0 6rpx 20rpx rgba(0, 0, 0, 0.12);
border: 2rpx solid #e0e0e0; border: 2rpx solid #e0e0e0;
z-index: 100; z-index: 100;
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 355 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.2 KiB