syle:更新样式
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<view class="map-container">
|
||||
<view class="map-container" :class="{ 'full-width': props.fullWidth }" :style="{ '--map-height': props.customHeight || '78vh' }">
|
||||
<!-- 地图容器 -->
|
||||
<view class="map-wrapper">
|
||||
<!-- 使用小程序原生地图组件 -->
|
||||
@@ -7,10 +7,10 @@
|
||||
:markers="mapMarkers" :scale="mapZoom" :show-location="true" @regionchange="onMapRegionChange"
|
||||
@markertap="onMapMarkerTap" @callouttap="onCalloutTap" @updated="onMapUpdated" @error="onMapError">
|
||||
<!-- 覆盖在地图上的可点击控件(使用 cover-view 以兼容小程序原生组件层级) -->
|
||||
<cover-view class="index-swiper">
|
||||
<image src="/static/index_swiper.png" class="index-swiper" mode="aspectFit"></image>
|
||||
<cover-view class="index-swiper" v-if="!props.hideControls" @tap="handleJoinTap">
|
||||
<cover-image src="/static/index_swiper.png" class="index-swiper-img" mode="aspectFit"></cover-image>
|
||||
</cover-view>
|
||||
<cover-view class="map-side-controls">
|
||||
<cover-view class="map-side-controls" v-if="!props.hideControls">
|
||||
<cover-view class="side-btn service" @tap="handleService">
|
||||
<cover-image class="side-icon" src="/static/customer-service.png"></cover-image>
|
||||
<!-- <cover-view class="side-text">客服</cover-view> -->
|
||||
@@ -100,6 +100,18 @@
|
||||
enableMarkers: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
customHeight: {
|
||||
type: String,
|
||||
default: '' // 自定义高度,如 '48vh', '400rpx' 等
|
||||
},
|
||||
hideControls: {
|
||||
type: Boolean,
|
||||
default: false // 是否隐藏侧边控制按钮
|
||||
},
|
||||
fullWidth: {
|
||||
type: Boolean,
|
||||
default: false // 是否全宽显示(去掉 margin 和固定宽度)
|
||||
}
|
||||
})
|
||||
|
||||
@@ -122,13 +134,23 @@
|
||||
const mapMarkers = ref([]) // 用于地图组件的markers
|
||||
const mapContext = ref(null) // 地图上下文
|
||||
|
||||
// 验证坐标有效性
|
||||
const isValidCoordinate = (lat, lng) => {
|
||||
const latitude = parseFloat(lat)
|
||||
const longitude = parseFloat(lng)
|
||||
return !isNaN(latitude) && !isNaN(longitude) &&
|
||||
latitude >= -90 && latitude <= 90 &&
|
||||
longitude >= -180 && longitude <= 180 &&
|
||||
!(latitude === 0 && longitude === 0) // 排除 0,0 这种无效坐标
|
||||
}
|
||||
|
||||
// 方法
|
||||
const updateMapMarkers = () => {
|
||||
const markers = []
|
||||
// 中心 marker(始终存在,使用传入中心坐标或 userLocation)
|
||||
const centerLng = Number(mapCenter.value.longitude || (props.userLocation && props.userLocation.longitude))
|
||||
const centerLat = Number(mapCenter.value.latitude || (props.userLocation && props.userLocation.latitude))
|
||||
if (!isNaN(centerLng) && !isNaN(centerLat)) {
|
||||
if (!isNaN(centerLng) && !isNaN(centerLat) && isValidCoordinate(centerLat, centerLng)) {
|
||||
markers.push({
|
||||
id: 999999, // 固定 id 作为中心点
|
||||
latitude: centerLat,
|
||||
@@ -146,19 +168,25 @@
|
||||
// 可选:周边位置点
|
||||
if (props.enableMarkers && props.filteredPositions && props.filteredPositions.length > 0) {
|
||||
props.filteredPositions.forEach((pos, index) => {
|
||||
if (pos.longitude && pos.latitude) {
|
||||
if (pos.longitude && pos.latitude && isValidCoordinate(pos.latitude, pos.longitude)) {
|
||||
const lat = parseFloat(pos.latitude)
|
||||
const lng = parseFloat(pos.longitude)
|
||||
if (lat >= -90 && lat <= 90 && lng >= -180 && lng <= 180) {
|
||||
markers.push({
|
||||
id: index + 1,
|
||||
latitude: lat,
|
||||
longitude: lng,
|
||||
width: 24,
|
||||
height: 24,
|
||||
title: pos.name
|
||||
})
|
||||
iconPath: '/static/map.png',
|
||||
width: 30,
|
||||
height: 30,
|
||||
callout: {
|
||||
content: pos.name,
|
||||
fontSize: 12,
|
||||
borderRadius: 8,
|
||||
bgColor: '#ffffff',
|
||||
padding: 8,
|
||||
display: 'BYCLICK'
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -248,20 +276,22 @@
|
||||
|
||||
// 标记点点击事件
|
||||
const onMapMarkerTap = (e) => {
|
||||
const markerId = e.markerId
|
||||
const marker = mapMarkers.value.find(item => item.id === markerId)
|
||||
const markerId = e.detail?.markerId || e.markerId
|
||||
|
||||
if (marker) {
|
||||
if (markerId === 0) { // 用户位置标记
|
||||
// 中心点标记
|
||||
if (markerId === 999999) {
|
||||
uni.showToast({
|
||||
title: '这是您的位置',
|
||||
title: '这是您的当前位置',
|
||||
icon: 'none'
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
if (marker.position) {
|
||||
emit('markerTap', marker.position)
|
||||
// 查找对应的位置信息
|
||||
if (props.filteredPositions && props.filteredPositions.length > 0) {
|
||||
const position = props.filteredPositions[markerId - 1]
|
||||
if (position) {
|
||||
emit('markerTap', position)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -301,6 +331,12 @@ const handleSearch = () => {
|
||||
})
|
||||
}
|
||||
|
||||
const handleJoinTap = () => {
|
||||
uni.navigateTo({
|
||||
url: '/pages/join/index'
|
||||
})
|
||||
}
|
||||
|
||||
const handleScan = () => {
|
||||
emit('scan')
|
||||
}
|
||||
@@ -366,11 +402,17 @@ const handleSearch = () => {
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
width: 94vw;
|
||||
height: 78vh;
|
||||
height: var(--map-height, 78vh);
|
||||
margin: 20rpx;
|
||||
border-radius: 20rpx;
|
||||
overflow: hidden;
|
||||
|
||||
&.full-width {
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
.map-wrapper {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
@@ -511,14 +553,21 @@ const handleSearch = () => {
|
||||
}
|
||||
|
||||
.index-swiper {
|
||||
width: 92vw;
|
||||
height: 180rpx;
|
||||
margin-top: 20rpx;
|
||||
width: 90vw;
|
||||
height: 120rpx;
|
||||
border-radius: 20rpx;
|
||||
z-index: 1000;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
// top: 10rpx;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
right: 0;
|
||||
|
||||
&-img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border-radius: 20rpx;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -166,6 +166,22 @@
|
||||
"navigationBarTextStyle": "black"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/position/detail",
|
||||
"style": {
|
||||
"navigationBarTitleText": "附近设备详情",
|
||||
"navigationBarBackgroundColor": "#D1FFE1",
|
||||
"navigationBarTextStyle": "black"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/join/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "合作加盟",
|
||||
"navigationBarBackgroundColor": "#ffffff",
|
||||
"navigationBarTextStyle": "black"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/waiting/index",
|
||||
"style": {
|
||||
|
||||
@@ -382,6 +382,33 @@
|
||||
// 提交租借订单
|
||||
const submitRentOrder = async (payWay) => {
|
||||
try {
|
||||
// --- 第一步:先请求订阅消息(必须在用户点击的同步上下文中)---
|
||||
if (payWay === 'wx-score-pay') {
|
||||
console.log('准备请求订阅消息(在异步操作之前),时间:', new Date().toLocaleTimeString());
|
||||
try {
|
||||
await new Promise((resolve, reject) => {
|
||||
uni.requestSubscribeMessage({
|
||||
tmplIds: ['o7OMTIcHnFBR7mvsggxFtdt8FfIgSl-v0swVUefGx6w'],
|
||||
success: (subscribeRes) => {
|
||||
console.log('订阅消息success回调,时间:', new Date()
|
||||
.toLocaleTimeString(), subscribeRes);
|
||||
resolve(subscribeRes);
|
||||
},
|
||||
fail: (subscribeErr) => {
|
||||
console.log('订阅消息fail回调,时间:', new Date().toLocaleTimeString(),
|
||||
subscribeErr);
|
||||
// 订阅失败不影响主流程
|
||||
resolve(subscribeErr);
|
||||
}
|
||||
});
|
||||
});
|
||||
console.log('订阅消息完成,时间:', new Date().toLocaleTimeString());
|
||||
} catch (subscribeError) {
|
||||
console.log('订阅消息异常', subscribeError);
|
||||
}
|
||||
}
|
||||
// --- 订阅消息请求完成 ---
|
||||
|
||||
uni.showLoading({
|
||||
title: '处理中'
|
||||
})
|
||||
@@ -394,7 +421,7 @@
|
||||
|
||||
// 获取后端返回的订单信息
|
||||
const order = rentResult.data
|
||||
console.log('订单信息',order);
|
||||
console.log('订单信息', order);
|
||||
|
||||
// // --- 统一:先更新订单套餐信息 ---
|
||||
// try {
|
||||
@@ -455,6 +482,8 @@
|
||||
const payResult = await initiateWeChatScorePayment(res);
|
||||
// 成功则跳转等待页,轮询在等待页处理
|
||||
if (payResult.errCode == '0') {
|
||||
console.log('支付分授权成功,准备跳转,时间:', new Date().toLocaleTimeString());
|
||||
// 直接跳转(订阅消息已经在前面完成了)
|
||||
uni.redirectTo({
|
||||
url: `/pages/waiting/index?orderNo=${order.orderNo}&deviceId=${deviceId.value}`
|
||||
});
|
||||
|
||||
@@ -223,8 +223,8 @@
|
||||
transition: all 0.3s;
|
||||
|
||||
&.active {
|
||||
background: #E3F2FD;
|
||||
color: #1976D2;
|
||||
background: #E8F5EE;
|
||||
color: #3EAB64;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -342,7 +342,7 @@
|
||||
.submit-btn {
|
||||
width: 100%;
|
||||
height: 88rpx;
|
||||
background: #1976D2;
|
||||
background: #3EAB64;
|
||||
color: #fff;
|
||||
border-radius: 44rpx;
|
||||
font-size: 32rpx;
|
||||
|
||||
@@ -212,6 +212,14 @@
|
||||
}
|
||||
const res = await getNoticeTextData(parasm);
|
||||
noticeText.value = res.data.noticeContent;
|
||||
|
||||
// 将通知内容存储到本地缓存
|
||||
try {
|
||||
uni.setStorageSync('noticeContent', res.data.noticeContent);
|
||||
console.log('通知内容已缓存:', res.data.noticeContent);
|
||||
} catch (e) {
|
||||
console.warn('缓存通知内容失败:', e);
|
||||
}
|
||||
}
|
||||
|
||||
// 距离格式化函数
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
<template>
|
||||
<view class="join-page">
|
||||
<!-- 使用 web-view 嵌入外部网页 -->
|
||||
<web-view :src="webUrl" @message="handleMessage" @error="handleError"></web-view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import {
|
||||
ref,
|
||||
onMounted
|
||||
} from 'vue'
|
||||
|
||||
// 外部网页地址
|
||||
const webUrl = ref('https://joininvestment.gxfs123.com/')
|
||||
|
||||
// 处理来自 web-view 的消息(如果外部网页有通过 postMessage 发送消息)
|
||||
const handleMessage = (e) => {
|
||||
console.log('收到来自 web-view 的消息:', e)
|
||||
// 可以根据消息内容进行相应处理
|
||||
}
|
||||
|
||||
// 处理 web-view 加载错误
|
||||
const handleError = (e) => {
|
||||
console.error('web-view 加载错误:', e)
|
||||
uni.showToast({
|
||||
title: '页面加载失败',
|
||||
icon: 'none',
|
||||
duration: 2000
|
||||
})
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
console.log('招商页面加载,外部网址:', webUrl.value)
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.join-page {
|
||||
width: 100%;
|
||||
height: 100vh;
|
||||
overflow: hidden;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
</view> -->
|
||||
|
||||
<view class="section">
|
||||
<view class="banner-card">
|
||||
<view class="banner-card" @click="navigateTo('/pages/join/index')">
|
||||
<image class="banner-image" src="/static/userCenter_swiper.png" mode="aspectFill"></image>
|
||||
</view>
|
||||
<!-- <view class="section-title">常用服务</view> -->
|
||||
@@ -63,21 +63,21 @@
|
||||
</view>
|
||||
<view class="list-item" @click="navigateTo('/pages/feedback/index')">
|
||||
<view class="left">
|
||||
<image class="icon" src="/static/complaint.png" mode="aspectFit"></image>
|
||||
<image class="icon" src="/static/suggess.png" mode="aspectFit"></image>
|
||||
<text class="title">投诉与建议</text>
|
||||
</view>
|
||||
<uv-icon name="arrow-right" size="16" color="#c8c8c8"></uv-icon>
|
||||
</view>
|
||||
<view class="list-item" @click="navigateTo('/pages/legal/agreement')">
|
||||
<view class="left">
|
||||
<image class="icon" src="/static/peopleInWork.png" mode="aspectFit"></image>
|
||||
<image class="icon" src="/static/business-licence.png" mode="aspectFit"></image>
|
||||
<text class="title">营业资质</text>
|
||||
</view>
|
||||
<uv-icon name="arrow-right" size="16" color="#c8c8c8"></uv-icon>
|
||||
</view>
|
||||
<view class="list-item" @click="navigateTo('/pages/serve/bagCheck/index')">
|
||||
<view class="list-item" @click="navigateTo('/pages/join/index')">
|
||||
<view class="left">
|
||||
<image class="icon" src="/static/other_device.png" mode="aspectFit"></image>
|
||||
<image class="icon" src="/static/peopleInWork.png" mode="aspectFit"></image>
|
||||
<text class="title">合作加盟</text>
|
||||
</view>
|
||||
<uv-icon name="arrow-right" size="16" color="#c8c8c8"></uv-icon>
|
||||
@@ -128,9 +128,13 @@
|
||||
wxLogin,
|
||||
getUserInfo
|
||||
} from '../../util/index.js';
|
||||
import { uploadUserAvatar } from '../../config/user.js'
|
||||
import { URL } from '../../config/url.js'
|
||||
// 设置页执行退出登录,此页不再直接调用
|
||||
import {
|
||||
uploadUserAvatar
|
||||
} from '../../config/user.js'
|
||||
import {
|
||||
URL
|
||||
} from '../../config/url.js'
|
||||
// 设置页执行退出登录,此页不再直接调用
|
||||
|
||||
// 响应式状态
|
||||
const userInfo = ref({});
|
||||
@@ -289,7 +293,10 @@
|
||||
getUserProfile()
|
||||
// #endif
|
||||
// #ifndef MP-WEIXIN
|
||||
uni.showToast({ title: '请在微信小程序中使用此功能', icon: 'none' })
|
||||
uni.showToast({
|
||||
title: '请在微信小程序中使用此功能',
|
||||
icon: 'none'
|
||||
})
|
||||
// #endif
|
||||
};
|
||||
|
||||
@@ -303,21 +310,36 @@
|
||||
}
|
||||
const avatarLocalPath = e?.detail?.avatarUrl
|
||||
if (!avatarLocalPath) {
|
||||
uni.showToast({ title: '未选择头像', icon: 'none' })
|
||||
uni.showToast({
|
||||
title: '未选择头像',
|
||||
icon: 'none'
|
||||
})
|
||||
return
|
||||
}
|
||||
uni.showLoading({ title: '上传中...', mask: true })
|
||||
uni.showLoading({
|
||||
title: '上传中...',
|
||||
mask: true
|
||||
})
|
||||
const uploadRes = await uploadUserAvatar(avatarLocalPath)
|
||||
const serverAvatar = uploadRes?.data?.url || uploadRes?.url || uploadRes?.data || ''
|
||||
if (serverAvatar) {
|
||||
userInfo.value = { ...userInfo.value, avatar: serverAvatar }
|
||||
userInfo.value = {
|
||||
...userInfo.value,
|
||||
avatar: serverAvatar
|
||||
}
|
||||
uni.setStorageSync('userInfo', userInfo.value)
|
||||
}
|
||||
uni.showToast({ title: '头像已更新', icon: 'success' })
|
||||
uni.showToast({
|
||||
title: '头像已更新',
|
||||
icon: 'success'
|
||||
})
|
||||
await getInfo()
|
||||
} catch (err) {
|
||||
console.error('选择/上传头像失败:', err)
|
||||
uni.showToast({ title: '头像更新失败', icon: 'none' })
|
||||
uni.showToast({
|
||||
title: '头像更新失败',
|
||||
icon: 'none'
|
||||
})
|
||||
} finally {
|
||||
uni.hideLoading()
|
||||
}
|
||||
@@ -425,7 +447,10 @@
|
||||
try {
|
||||
const avatarUrl = wxUserInfo?.avatarUrl
|
||||
if (!avatarUrl) {
|
||||
uni.showToast({ title: '未获取到头像地址', icon: 'none' })
|
||||
uni.showToast({
|
||||
title: '未获取到头像地址',
|
||||
icon: 'none'
|
||||
})
|
||||
return
|
||||
}
|
||||
// 下载微信头像为本地临时文件
|
||||
@@ -447,14 +472,23 @@
|
||||
// 直接使用返回的头像地址(如果有),并刷新用户信息
|
||||
const serverAvatar = uploadRes?.data?.url || uploadRes?.url || uploadRes?.data || ''
|
||||
if (serverAvatar) {
|
||||
userInfo.value = { ...userInfo.value, avatar: serverAvatar }
|
||||
userInfo.value = {
|
||||
...userInfo.value,
|
||||
avatar: serverAvatar
|
||||
}
|
||||
uni.setStorageSync('userInfo', userInfo.value)
|
||||
}
|
||||
uni.showToast({ title: '头像已更新', icon: 'success' })
|
||||
uni.showToast({
|
||||
title: '头像已更新',
|
||||
icon: 'success'
|
||||
})
|
||||
await getInfo()
|
||||
} catch (error) {
|
||||
console.error('头像上传失败:', error)
|
||||
uni.showToast({ title: '头像上传失败', icon: 'none' })
|
||||
uni.showToast({
|
||||
title: '头像上传失败',
|
||||
icon: 'none'
|
||||
})
|
||||
} finally {
|
||||
uni.hideLoading()
|
||||
}
|
||||
@@ -491,7 +525,7 @@
|
||||
return phone.replace(/(\d{3})\d{4}(\d{4})/, '$1****$2');
|
||||
}
|
||||
|
||||
// 退出登录移动至设置页
|
||||
// 退出登录移动至设置页
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@@ -556,8 +590,10 @@
|
||||
background: transparent;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
opacity: 0; /* 保持可点击但不可见 */
|
||||
opacity: 0;
|
||||
/* 保持可点击但不可见 */
|
||||
}
|
||||
|
||||
/* #endif */
|
||||
|
||||
.user-text {
|
||||
@@ -612,7 +648,7 @@
|
||||
margin-top: 20rpx;
|
||||
margin: 0 20rpx 20rpx 20rpx;
|
||||
background-color: #ffffff;
|
||||
flex:1;
|
||||
flex: 1;
|
||||
// border-top: 1rpx solid #f0f0f0;
|
||||
// border-bottom: 1rpx solid #f0f0f0;
|
||||
border-radius: 20rpx;
|
||||
@@ -665,7 +701,7 @@
|
||||
color: #999999;
|
||||
font-size: 22rpx;
|
||||
|
||||
.link-box{
|
||||
.link-box {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
@@ -10,20 +10,22 @@
|
||||
<view class="info-card">
|
||||
<view class="info-row">
|
||||
<view class="info-col">
|
||||
<view class="info-value">{{ getUsedTimeNumber() }}</view>
|
||||
<view class="info-label">{{ getUsedTimeUnit() }}</view>
|
||||
<view class="info-value-wrapper">
|
||||
<text class="info-value-large">{{ getUsedTimeDisplay().number }}</text>
|
||||
<text class="info-value-unit">{{ getUsedTimeDisplay().unit }}</text>
|
||||
</view>
|
||||
<view class="info-label">{{ getUsedTimeLabel() }}</view>
|
||||
</view>
|
||||
<view class="info-col">
|
||||
<view class="info-value">{{ getOrderFee() }}</view>
|
||||
<view class="info-label">{{ isOrderCompleted() ? '订单金额' : '订单金额' }}</view>
|
||||
<view class="info-value-wrapper">
|
||||
<text class="info-value-large">{{ getOrderFee() }}</text>
|
||||
<text class="info-value-unit">元</text>
|
||||
</view>
|
||||
<view class="info-col">
|
||||
<view class="info-value">{{ orderInfo.packagePrice || '10' }}</view>
|
||||
<view class="info-label">元</view>
|
||||
<view class="info-label">订单金额</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="fee-rule">
|
||||
计费规则:5.0元/60分钟 前15分钟内归还免费 不足60分钟按60分钟计费 封顶99元 持续计费至99元视为买断
|
||||
{{ feeRuleText }}
|
||||
</view>
|
||||
</view>
|
||||
|
||||
@@ -88,7 +90,7 @@
|
||||
<template v-if="isOrderCompleted()">
|
||||
<view class="bottom-icon-btn" @click="handleWithdraw" v-if="!orderInfo.isWithdrawn && orderInfo.refundAmount > 0">
|
||||
<image src="/static/suggess.png" class="icon" mode="aspectFit"></image>
|
||||
<text>费用申请</text>
|
||||
<text>费用申诉</text>
|
||||
</view>
|
||||
<view class="bottom-icon-btn" @click="contactService">
|
||||
<image src="/static/customer-service.png" class="icon" mode="aspectFit"></image>
|
||||
@@ -145,7 +147,8 @@
|
||||
withdrawStatus: 'waiting',
|
||||
isWithdrawn: false,
|
||||
positionName: '',
|
||||
returnPosition: ''
|
||||
returnPosition: '',
|
||||
expressReturnStart: null
|
||||
},
|
||||
timer: null,
|
||||
statusCheckTimer: null,
|
||||
@@ -156,7 +159,8 @@
|
||||
expressThresholdSeconds: 180,
|
||||
countdownRemaining: 0,
|
||||
showExpressAction: false,
|
||||
countdownTimer: null
|
||||
countdownTimer: null,
|
||||
feeRuleText: '5.0元/60分钟 前15分钟内归还免费 不足60分钟按60分钟计费 封顶99元 持续计费至99元视为买断'
|
||||
}
|
||||
},
|
||||
onLoad(options) {
|
||||
@@ -164,6 +168,17 @@
|
||||
|
||||
this.isPageActive = true
|
||||
|
||||
// 从缓存读取通知内容(计费规则)
|
||||
try {
|
||||
const cachedNotice = uni.getStorageSync('noticeContent')
|
||||
if (cachedNotice) {
|
||||
console.log('从缓存读取计费规则:', cachedNotice)
|
||||
this.feeRuleText = cachedNotice
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn('读取缓存通知内容失败:', e)
|
||||
}
|
||||
|
||||
this.orderInfo.orderId = options.orderId || ''
|
||||
this.deviceId = options.deviceNo || options.deviceId || ''
|
||||
|
||||
@@ -185,11 +200,11 @@
|
||||
uni.$on('orderCompleted', this.handleOrderCompleted)
|
||||
|
||||
// 获取系统配置
|
||||
this.loadSystemConfig().then(() => {
|
||||
if (this.orderInfo.orderStatus === 'in_used') {
|
||||
this.startExpressCountdown()
|
||||
}
|
||||
})
|
||||
// this.loadSystemConfig().then(() => {
|
||||
// if (this.orderInfo.orderStatus === 'in_used') {
|
||||
// this.startExpressCountdown()
|
||||
// }
|
||||
// })
|
||||
},
|
||||
onShow() {
|
||||
this.isPageActive = true
|
||||
@@ -313,28 +328,70 @@
|
||||
})
|
||||
},
|
||||
|
||||
// 获取使用时长的数字部分(用于显示)
|
||||
getUsedTimeNumber() {
|
||||
const usedTime = this.orderInfo.usedTime || '0分钟'
|
||||
// 提取第一个数字(如 "1小时5分钟" -> "1")
|
||||
const match = usedTime.match(/(\d+)/)
|
||||
return match ? match[1] : '0'
|
||||
// 获取使用时长显示信息
|
||||
getUsedTimeDisplay() {
|
||||
let usedTime = this.orderInfo.usedTime
|
||||
|
||||
// 如果 usedTime 为空,通过开始时间和结束时间计算
|
||||
if (usedTime === '0分钟' && this.orderInfo.startTime && this.orderInfo.endTime) {
|
||||
const startMs = this.parseStartTimeToMs(this.orderInfo.startTime)
|
||||
const endMs = this.parseStartTimeToMs(this.orderInfo.endTime)
|
||||
|
||||
if (!isNaN(startMs) && !isNaN(endMs)) {
|
||||
const diffMs = endMs - startMs
|
||||
const totalMinutes = Math.floor(diffMs / (1000 * 60))
|
||||
|
||||
// 格式化为 "X小时X分钟" 或 "X分钟"
|
||||
if (totalMinutes >= 60) {
|
||||
const hours = Math.floor(totalMinutes / 60)
|
||||
const minutes = totalMinutes % 60
|
||||
usedTime = minutes > 0 ? `${hours}小时${minutes}分钟` : `${hours}小时`
|
||||
} else {
|
||||
usedTime = `${totalMinutes}分钟`
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 如果还是没有值,使用默认值
|
||||
if (!usedTime) {
|
||||
usedTime = '0分钟'
|
||||
}
|
||||
|
||||
// 解析时长字符串,例如 "1小时5分钟" 或 "5分钟"
|
||||
const hourMatch = usedTime.match(/(\d+)小时/)
|
||||
const minuteMatch = usedTime.match(/(\d+)分钟/)
|
||||
|
||||
let displayNumber = ''
|
||||
let displayUnit = ''
|
||||
|
||||
if (hourMatch && minuteMatch) {
|
||||
// 有小时也有分钟,如 "1小时5分钟"
|
||||
displayNumber = `${hourMatch[1]}`
|
||||
displayUnit = `小时${minuteMatch[1]}分钟`
|
||||
} else if (hourMatch) {
|
||||
// 只有小时,如 "1小时"
|
||||
displayNumber = hourMatch[1]
|
||||
displayUnit = '小时'
|
||||
} else if (minuteMatch) {
|
||||
// 只有分钟,如 "5分钟"
|
||||
displayNumber = minuteMatch[1]
|
||||
displayUnit = '分钟'
|
||||
} else {
|
||||
// 默认情况
|
||||
displayNumber = '0'
|
||||
displayUnit = '分钟'
|
||||
}
|
||||
|
||||
return {
|
||||
number: displayNumber,
|
||||
unit: displayUnit
|
||||
}
|
||||
},
|
||||
|
||||
// 获取使用时长的单位部分
|
||||
getUsedTimeUnit() {
|
||||
if (this.isOrderCompleted()) {
|
||||
return '使用时长'
|
||||
}
|
||||
const usedTime = this.orderInfo.usedTime || '0分钟'
|
||||
// 如果是完整的时长字符串,返回"已使用"
|
||||
// 否则提取单位
|
||||
if (usedTime.includes('小时')) {
|
||||
return '小时'
|
||||
} else if (usedTime.includes('分钟')) {
|
||||
return '分钟'
|
||||
}
|
||||
return '已使用'
|
||||
// 获取使用时长标签文本
|
||||
getUsedTimeLabel() {
|
||||
// 使用中状态显示"已使用",已完成状态显示"使用时长"
|
||||
return this.orderInfo.orderStatus === 'in_used' ? '已使用' : '使用时长'
|
||||
},
|
||||
|
||||
// 获取订单费用(不含单位)
|
||||
@@ -347,13 +404,22 @@
|
||||
// 加载系统配置
|
||||
async loadSystemConfig() {
|
||||
try {
|
||||
// 优先使用订单数据中的 expressReturnStart(小时转秒)
|
||||
if (this.orderInfo.expressReturnStart && typeof this.orderInfo.expressReturnStart === 'number' && this.orderInfo.expressReturnStart > 0) {
|
||||
this.expressThresholdSeconds = this.orderInfo.expressReturnStart * 3600
|
||||
console.log('使用订单配置的快递归还阈值:', this.orderInfo.expressReturnStart, '小时 =>', this.expressThresholdSeconds, '秒')
|
||||
} else {
|
||||
// 如果订单数据中没有,则使用系统配置
|
||||
const res = await getSystemConfig()
|
||||
if (res && res.code === 200 && res.data && typeof res.data.expressReturnCountdownSeconds === 'number') {
|
||||
const seconds = res.data.expressReturnCountdownSeconds
|
||||
if (seconds > 0) {
|
||||
this.expressThresholdSeconds = seconds
|
||||
console.log('使用系统配置的快递归还阈值:', seconds, '秒')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (this.orderInfo.orderStatus === 'in_used' && this.orderInfo.startTime) {
|
||||
this.recomputeExpressCountdownFromStartTime()
|
||||
}
|
||||
@@ -553,6 +619,15 @@
|
||||
this.orderInfo.positionName = orderData.positionName || orderData.positionLocation || ''
|
||||
this.orderInfo.returnPosition = orderData.returnPosition || orderData.positionName || orderData.positionLocation || ''
|
||||
|
||||
// 保存快递归还开始时间(小时为单位)
|
||||
this.orderInfo.expressReturnStart = orderData.expressReturnStart || null
|
||||
|
||||
// 如果有有效的 expressReturnStart,立即更新倒计时阈值(小时转秒)
|
||||
if (this.orderInfo.expressReturnStart && typeof this.orderInfo.expressReturnStart === 'number' && this.orderInfo.expressReturnStart > 0) {
|
||||
this.expressThresholdSeconds = this.orderInfo.expressReturnStart * 3600
|
||||
console.log('从订单数据更新快递归还阈值:', this.orderInfo.expressReturnStart, '小时 =>', this.expressThresholdSeconds, '秒')
|
||||
}
|
||||
|
||||
if (orderData.deviceNo && !this.deviceId) {
|
||||
this.deviceId = orderData.deviceNo
|
||||
}
|
||||
@@ -843,11 +918,24 @@
|
||||
flex: 1;
|
||||
text-align: center;
|
||||
|
||||
.info-value {
|
||||
font-size: 40rpx;
|
||||
.info-value-wrapper {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
justify-content: center;
|
||||
margin-bottom: 8rpx;
|
||||
|
||||
.info-value-large {
|
||||
font-size: 48rpx;
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
margin-bottom: 8rpx;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.info-value-unit {
|
||||
font-size: 24rpx;
|
||||
color: #666;
|
||||
margin-left: 4rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.info-label {
|
||||
|
||||
@@ -0,0 +1,330 @@
|
||||
<template>
|
||||
<view class="position-detail-page">
|
||||
<!-- 顶部设备柜图示 -->
|
||||
<view class="device-illustration">
|
||||
<image src="/static/device-info.png" class="device-img" mode="aspectFit"></image>
|
||||
</view>
|
||||
|
||||
<!-- 场地信息卡片 -->
|
||||
<view class="info-card">
|
||||
<!-- 场地名称 -->
|
||||
<view class="position-name">{{ positionInfo.name || '加载中...' }}</view>
|
||||
|
||||
<!-- 地址信息 -->
|
||||
<view class="info-item" v-if="positionInfo.location">
|
||||
<image src="/static/device-location.png" class="item-icon" mode="aspectFit"></image>
|
||||
<text class="item-text">{{ positionInfo.location }}</text>
|
||||
</view>
|
||||
|
||||
<!-- 营业时间 -->
|
||||
<view class="info-item" v-if="positionInfo.workTime && positionInfo.workTime !== '0'">
|
||||
<image src="/static/device-time.png" class="item-icon" mode="aspectFit"></image>
|
||||
<text class="item-text">营业时间:{{ positionInfo.workTime }}</text>
|
||||
</view>
|
||||
|
||||
<!-- 计费信息 -->
|
||||
<view class="info-item">
|
||||
<image src="/static/device-price.png" class="item-icon" mode="aspectFit"></image>
|
||||
<text class="item-text">计费:{{ pricingText }}</text>
|
||||
</view>
|
||||
|
||||
<!-- 按钮组 -->
|
||||
<view class="button-group">
|
||||
<view style="display: flex;flex-direction: row;gap: 10rpx;">
|
||||
<view class="status-btn" v-if="isRentable">可租借</view>
|
||||
<view class="status-btn" v-if="isReturnable">可归还</view>
|
||||
</view>
|
||||
|
||||
<view class="nav-btn" @click.stop="navigateToPosition">导航去这</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 底部操作按钮 -->
|
||||
<view class="footer-actions">
|
||||
<button class="action-btn btn-outline" @click="reportError">设备报错</button>
|
||||
<button class="action-btn btn-primary" @click="scanCode">扫码使用</button>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import {
|
||||
ref,
|
||||
computed,
|
||||
onMounted
|
||||
} from 'vue'
|
||||
import {
|
||||
onLoad
|
||||
} from '@dcloudio/uni-app'
|
||||
import {
|
||||
URL
|
||||
} from '../../config/url.js'
|
||||
|
||||
const positionInfo = ref({})
|
||||
const positionId = ref('')
|
||||
|
||||
const isRentable = computed(() => {
|
||||
if (typeof positionInfo.value?.canRent !== 'undefined') {
|
||||
return !!positionInfo.value.canRent
|
||||
}
|
||||
return String(positionInfo.value?.status || '').toLowerCase() === 'online'
|
||||
})
|
||||
|
||||
const isReturnable = computed(() => {
|
||||
if (typeof positionInfo.value?.canReturn !== 'undefined') {
|
||||
return !!positionInfo.value.canReturn
|
||||
}
|
||||
return String(positionInfo.value?.status || '').toLowerCase() === 'online'
|
||||
})
|
||||
|
||||
const pricingText = computed(() => {
|
||||
// 这里可以根据实际的计费规则来显示
|
||||
// 默认显示一个通用的计费说明
|
||||
return '5元/小时,36元/24小时,总计¥899元'
|
||||
})
|
||||
|
||||
onLoad(async (options) => {
|
||||
if (options.positionId) {
|
||||
positionId.value = options.positionId
|
||||
await loadPositionDetail()
|
||||
}
|
||||
})
|
||||
|
||||
const loadPositionDetail = async () => {
|
||||
try {
|
||||
uni.showLoading({
|
||||
title: '加载中...'
|
||||
})
|
||||
|
||||
const res = await uni.request({
|
||||
url: `${URL}/device/position/app/list`,
|
||||
method: 'GET',
|
||||
header: {
|
||||
'Authorization': 'Bearer ' + uni.getStorageSync('token'),
|
||||
'Clientid': uni.getStorageSync('client_id')
|
||||
}
|
||||
})
|
||||
|
||||
if (res.statusCode === 200 && res.data?.code === 200) {
|
||||
const positions = res.data.rows || []
|
||||
const position = positions.find(p => p.positionId === positionId.value)
|
||||
if (position) {
|
||||
positionInfo.value = position
|
||||
} else {
|
||||
uni.showToast({
|
||||
title: '场地不存在',
|
||||
icon: 'none'
|
||||
})
|
||||
}
|
||||
} else if (res.statusCode === 401 || res.data?.code === 401 || res.data?.code === 40101) {
|
||||
uni.reLaunch({
|
||||
url: '/pages/login/index'
|
||||
})
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('加载场地详情失败:', e)
|
||||
uni.showToast({
|
||||
title: '加载失败',
|
||||
icon: 'none'
|
||||
})
|
||||
} finally {
|
||||
uni.hideLoading()
|
||||
}
|
||||
}
|
||||
|
||||
const navigateToPosition = () => {
|
||||
if (!positionInfo.value.latitude || !positionInfo.value.longitude) {
|
||||
uni.showToast({
|
||||
title: '该场地坐标信息异常',
|
||||
icon: 'none'
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
const latitude = parseFloat(positionInfo.value.latitude)
|
||||
const longitude = parseFloat(positionInfo.value.longitude)
|
||||
|
||||
// 验证坐标有效性
|
||||
if (isNaN(latitude) || isNaN(longitude) ||
|
||||
latitude < -90 || latitude > 90 ||
|
||||
longitude < -180 || longitude > 180 ||
|
||||
(latitude === 0 && longitude === 0)) {
|
||||
uni.showToast({
|
||||
title: '该场地坐标信息异常',
|
||||
icon: 'none'
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
uni.openLocation({
|
||||
latitude,
|
||||
longitude,
|
||||
name: positionInfo.value.name,
|
||||
address: positionInfo.value.location
|
||||
})
|
||||
}
|
||||
|
||||
const reportError = () => {
|
||||
uni.navigateTo({
|
||||
url: '/pages/feedback/index'
|
||||
})
|
||||
}
|
||||
|
||||
const scanCode = () => {
|
||||
uni.scanCode({
|
||||
scanType: ['qrCode', 'barCode'],
|
||||
success: (res) => {
|
||||
console.log('扫码结果:', res)
|
||||
// 处理扫码结果,跳转到设备详情页
|
||||
if (res.result) {
|
||||
// 假设二维码内容是设备号
|
||||
uni.navigateTo({
|
||||
url: `/pages/device/detail?deviceNo=${res.result}`
|
||||
})
|
||||
}
|
||||
},
|
||||
fail: (err) => {
|
||||
console.error('扫码失败:', err)
|
||||
uni.showToast({
|
||||
title: '扫码失败',
|
||||
icon: 'none'
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.position-detail-page {
|
||||
min-height: 100vh;
|
||||
background: linear-gradient(180deg, #D1FFE1 0%, #F5F9F7 100%);
|
||||
padding-bottom: 200rpx;
|
||||
}
|
||||
|
||||
.device-illustration {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 40rpx 0 30rpx;
|
||||
|
||||
.device-img {
|
||||
width: 480rpx;
|
||||
height: 480rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.info-card {
|
||||
background: #ffffff;
|
||||
margin: 0 32rpx;
|
||||
border-radius: 32rpx;
|
||||
padding: 44rpx 36rpx 36rpx;
|
||||
box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.06);
|
||||
|
||||
.position-name {
|
||||
font-size: 44rpx;
|
||||
font-weight: 700;
|
||||
color: #1A1A1A;
|
||||
margin-bottom: 32rpx;
|
||||
line-height: 1.3;
|
||||
}
|
||||
|
||||
.info-item {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
margin-bottom: 20rpx;
|
||||
|
||||
.item-icon {
|
||||
width: 32rpx;
|
||||
height: 32rpx;
|
||||
margin-right: 12rpx;
|
||||
margin-top: 2rpx;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.item-text {
|
||||
flex: 1;
|
||||
font-size: 28rpx;
|
||||
color: #666666;
|
||||
line-height: 1.6;
|
||||
}
|
||||
}
|
||||
|
||||
.button-group {
|
||||
display: flex;
|
||||
gap: 12rpx;
|
||||
margin-top: 36rpx;
|
||||
justify-content: space-between;
|
||||
flex-wrap: wrap;
|
||||
|
||||
.status-btn {
|
||||
padding: 12rpx 28rpx;
|
||||
border-radius: 40rpx;
|
||||
font-size: 26rpx;
|
||||
font-weight: 500;
|
||||
border: 2rpx solid #3EAB64;
|
||||
color: #3EAB64;
|
||||
background: #ffffff;
|
||||
}
|
||||
|
||||
.nav-btn {
|
||||
padding: 12rpx 28rpx;
|
||||
border-radius: 40rpx;
|
||||
font-size: 26rpx;
|
||||
font-weight: 500;
|
||||
border: 2rpx solid #3EAB64;
|
||||
color: #ffffff;
|
||||
background: #3EAB64;
|
||||
|
||||
&:active {
|
||||
opacity: 0.8;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.footer-actions {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
background: #ffffff;
|
||||
padding: 24rpx 32rpx;
|
||||
padding-bottom: calc(24rpx + env(safe-area-inset-bottom));
|
||||
box-shadow: 0 -2rpx 16rpx rgba(0, 0, 0, 0.08);
|
||||
display: flex;
|
||||
gap: 16rpx;
|
||||
|
||||
.action-btn {
|
||||
flex: 1;
|
||||
height: 96rpx;
|
||||
border-radius: 48rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 32rpx;
|
||||
font-weight: 600;
|
||||
border: none;
|
||||
|
||||
&.btn-outline {
|
||||
border: 2rpx solid #3EAB64;
|
||||
color: #3EAB64;
|
||||
background: #ffffff;
|
||||
}
|
||||
|
||||
&.btn-primary {
|
||||
background: #3EAB64;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
&:active {
|
||||
opacity: 0.85;
|
||||
transform: scale(0.98);
|
||||
}
|
||||
|
||||
&::after {
|
||||
border: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,9 +1,14 @@
|
||||
<template>
|
||||
<view class="search-page">
|
||||
<!-- <view class="map-wrap">
|
||||
<view class="map-wrap">
|
||||
<MapComponent :userLocation="userLocation" :filteredPositions="filteredPositions"
|
||||
@mapCenterChange="onMapCenterChange" />
|
||||
</view> -->
|
||||
:enableMarkers="true" :customHeight="'100%'" :hideControls="true" :fullWidth="true"
|
||||
@mapCenterChange="onMapCenterChange" @relocate="init" @markerTap="goToPositionDetail" />
|
||||
<!-- 定位按钮 -->
|
||||
<view class="relocate-btn" @click="init">
|
||||
<image src="/static/location.png" class="relocate-icon" mode="aspectFit"></image>
|
||||
</view>
|
||||
</view>
|
||||
<view class="list-wrap">
|
||||
<view class="panel">
|
||||
<view class="filter-tabs">
|
||||
@@ -11,8 +16,9 @@
|
||||
<view class="tab" :class="{ active: activeTab === 'return' }" @click="setTab('return')">可归还</view>
|
||||
</view>
|
||||
<scroll-view class="list-scroll" scroll-y="true">
|
||||
<view class="card" :class="{ available: isRentable(item) }"
|
||||
v-for="(item, index) in filteredPositions" :key="item.positionId || index">
|
||||
<view class="card" :class="{ available: isRentable(item), invalid: !isValidCoordinate(item.latitude, item.longitude) }"
|
||||
v-for="(item, index) in filteredPositions" :key="item.positionId || index"
|
||||
@click="goToPositionDetail(item)">
|
||||
<view class="thumb"></view>
|
||||
<view class="info">
|
||||
<view class="row top">
|
||||
@@ -25,6 +31,9 @@
|
||||
<view class="row meta" v-if="item.workTime && item.workTime !== '0'">
|
||||
<text class="time">营业时间:{{ item.workTime }}</text>
|
||||
</view>
|
||||
<view class="row meta" v-if="!isValidCoordinate(item.latitude, item.longitude)">
|
||||
<text class="time" style="color: #ff6b6b;">坐标信息异常</text>
|
||||
</view>
|
||||
<view class="tags">
|
||||
<view class="tag rent" v-if="isRentable(item)">可租借</view>
|
||||
<view class="tag return" v-if="isReturnable(item)">可归还</view>
|
||||
@@ -32,10 +41,12 @@
|
||||
</view>
|
||||
<view class="actions">
|
||||
|
||||
<view class="nav" @click.stop="navigateToPosition(item)">
|
||||
<view class="nav"
|
||||
:class="{ disabled: !isValidCoordinate(item.latitude, item.longitude) }"
|
||||
@click.stop="navigateToPosition(item)">
|
||||
<image src="/static/location.png" class="action-icon" mode="aspectFit"></image>
|
||||
</view>
|
||||
<view class="distance" v-if="item.distance">{{ item.distance }}</view>
|
||||
<view class="distance" v-if="item.distance && isValidCoordinate(item.latitude, item.longitude)">{{ item.distance }}</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
@@ -101,9 +112,18 @@
|
||||
}
|
||||
}
|
||||
|
||||
const isValidCoordinate = (lat, lng) => {
|
||||
const latitude = parseFloat(lat)
|
||||
const longitude = parseFloat(lng)
|
||||
return !isNaN(latitude) && !isNaN(longitude) &&
|
||||
latitude >= -90 && latitude <= 90 &&
|
||||
longitude >= -180 && longitude <= 180 &&
|
||||
!(latitude === 0 && longitude === 0) // 排除 0,0 这种无效坐标
|
||||
}
|
||||
|
||||
const calculateDistances = (center) => {
|
||||
positionList.value.forEach(item => {
|
||||
if (item.longitude && item.latitude) {
|
||||
if (item.longitude && item.latitude && isValidCoordinate(item.latitude, item.longitude)) {
|
||||
try {
|
||||
const distanceInMeters = calculateDistanceSync(
|
||||
center.latitude,
|
||||
@@ -113,10 +133,14 @@
|
||||
)
|
||||
item.distance = formatDistance(distanceInMeters)
|
||||
item.distanceInMeters = distanceInMeters
|
||||
} catch (_) {}
|
||||
} catch (_) {
|
||||
item.distanceInMeters = 999999
|
||||
}
|
||||
} else {
|
||||
item.distanceInMeters = 999999
|
||||
}
|
||||
})
|
||||
positionList.value.sort((a, b) => (a.distanceInMeters || 999000) - (b.distanceInMeters || 999000))
|
||||
positionList.value.sort((a, b) => (a.distanceInMeters || 999999) - (b.distanceInMeters || 999999))
|
||||
applyFilter()
|
||||
}
|
||||
|
||||
@@ -180,6 +204,13 @@
|
||||
}
|
||||
|
||||
const navigateToPosition = (position) => {
|
||||
if (!isValidCoordinate(position.latitude, position.longitude)) {
|
||||
uni.showToast({
|
||||
title: '该位置坐标无效',
|
||||
icon: 'none'
|
||||
})
|
||||
return
|
||||
}
|
||||
const latitude = parseFloat(position.latitude)
|
||||
const longitude = parseFloat(position.longitude)
|
||||
uni.openLocation({
|
||||
@@ -190,6 +221,19 @@
|
||||
})
|
||||
}
|
||||
|
||||
const goToPositionDetail = (position) => {
|
||||
if (!position.positionId) {
|
||||
uni.showToast({
|
||||
title: '场地信息异常',
|
||||
icon: 'none'
|
||||
})
|
||||
return
|
||||
}
|
||||
uni.navigateTo({
|
||||
url: `/pages/position/detail?positionId=${position.positionId}`
|
||||
})
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
init()
|
||||
})
|
||||
@@ -204,13 +248,39 @@
|
||||
|
||||
.map-wrap {
|
||||
flex: 0 0 48vh;
|
||||
padding: 20rpx 20rpx 0 20rpx;
|
||||
padding: 0;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
|
||||
.relocate-btn {
|
||||
position: absolute;
|
||||
right: 20rpx;
|
||||
bottom: 20rpx;
|
||||
width: 72rpx;
|
||||
height: 72rpx;
|
||||
background: rgba(255, 255, 255, 0.96);
|
||||
border-radius: 36rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
box-shadow: 0 6rpx 20rpx rgba(0, 0, 0, 0.12);
|
||||
border: 2rpx solid #e0e0e0;
|
||||
z-index: 100;
|
||||
|
||||
&:active {
|
||||
transform: scale(0.95);
|
||||
}
|
||||
|
||||
.relocate-icon {
|
||||
width: 44rpx;
|
||||
height: 44rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.list-wrap {
|
||||
flex: 1;
|
||||
background: transparent;
|
||||
margin-top: -20rpx;
|
||||
padding: 0 20rpx 20rpx;
|
||||
|
||||
.panel {
|
||||
@@ -247,7 +317,7 @@
|
||||
}
|
||||
|
||||
.list-scroll {
|
||||
height: calc(52vh - 88rpx);
|
||||
height: calc(48vh - 140rpx);
|
||||
padding: 6rpx 4rpx 12rpx 4rpx;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
@@ -269,6 +339,12 @@
|
||||
background: #F6FBF8;
|
||||
}
|
||||
|
||||
&.invalid {
|
||||
opacity: 0.6;
|
||||
border-color: #FFE5E5;
|
||||
background: #FFF9F9;
|
||||
}
|
||||
|
||||
.thumb {
|
||||
width: 120rpx;
|
||||
height: 120rpx;
|
||||
@@ -353,6 +429,11 @@
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: #3EAB64;
|
||||
|
||||
&.disabled {
|
||||
background: #F5F5F5;
|
||||
opacity: 0.5;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
After Width: | Height: | Size: 4.7 KiB |
|
After Width: | Height: | Size: 19 KiB |
|
After Width: | Height: | Size: 7.4 KiB |
|
After Width: | Height: | Size: 9.4 KiB |
|
After Width: | Height: | Size: 6.7 KiB |
|
Before Width: | Height: | Size: 7.0 KiB |
|
Before Width: | Height: | Size: 6.7 KiB |