miniprogramme/pages/map/map.wxml

478 lines
22 KiB
Text
Raw Normal View History

2025-09-12 16:08:17 +08:00
<!--地图页面 - 高德地图集成版本-->
<view class="map-container" style="height: {{windowHeight}}px;">
<!-- 地图组件 - 腾讯地图实现 -->
<map
wx:if="{{latitude && longitude && amapReady}}"
id="main-map"
class="map"
latitude="{{latitude}}"
longitude="{{longitude}}"
scale="{{scale}}"
markers="{{markers}}"
show-location="false"
show-scale="false"
show-compass="false"
enable-zoom="true"
enable-scroll="true"
enable-rotate="false"
enable-3D="false"
enable-overlooking="false"
enable-traffic="false"
enable-satellite="{{enableSatellite}}"
enable-cluster="true"
bindmarkertap="onMarkerTap"
bindcallouttap="onMarkerTap"
bindregionchange="onMapRegionChange"
bindlongtap="onMapLongTap"
bindtap="onMapTap"
bindpoitap="onPoiTap"
circles="{{circles}}"
>
<cover-view slot="callout">
<block wx:for="{{markers}}" wx:key="*this">
<cover-view class="customCallout" marker-id="{{item.id}}">
<!-- 用户标注(好友/自己) -->
<block wx:if="{{item.type == 'friend' || item.type == 'mine'}}">
<cover-view class="callout-nickname">{{item.nickName}}</cover-view>
<cover-image class="callout-avatar" src="{{item.avatarUrl}}"></cover-image>
<cover-view class="callout-battery">{{item.battery}}%</cover-view>
</block>
<!-- 商家标注 -->
<block wx:elif="{{item.type == 'merchant'}}">
<cover-view class="callout-nickname">{{item.nickName}}</cover-view>
<cover-image class="callout-merchant-icon" src="/images/map/marker_{{item.merchantType}}.png"></cover-image>
</block>
</cover-view>
</block>
</cover-view>
</map>
<!-- 定位中占位图 -->
<view wx:elif="{{!amapReady || (!latitude && !longitude)}}" class="location-placeholder">
<view class="placeholder-content">
<view class="location-icon">📍</view>
<text class="placeholder-title">正在获取您的位置</text>
<text class="placeholder-subtitle">首次定位可能需要几秒钟...</text>
<view class="location-progress">
<view class="progress-bar">
<view class="progress-fill"></view>
</view>
<text class="progress-text">{{loadingText}}</text>
</view>
</view>
</view>
<!-- 左上角区域信息 - 完全透明 -->
<view class="location-info-minimal" wx:if="{{latitude && longitude && amapReady}}"
style="top: {{statusBarHeight + 12}}px; left: 16px;">
<text class="district-text-minimal">{{currentDistrict || '定位中...'}}</text>
<text class="city-text-minimal" wx:if="{{currentCity}}">{{currentCity}}</text>
</view>
<!-- 天气信息 - 紧贴用户头像下方 -->
<view class="weather-info-minimal" wx:if="{{latitude && longitude && amapReady && weatherInfo}}"
style="top: {{statusBarHeight + 60}}px; right: 16px;">
<text class="weather-icon-minimal {{weatherInfo.isCloudIcon ? 'cloud-icon' : ''}}">{{weatherInfo.icon || '☀️'}}</text>
<text class="weather-temp-minimal">{{weatherInfo.temperature}}°</text>
</view>
<!-- 用户头像 - 避开胶囊按钮,点击进入个人页面 -->
<view class="user-avatar" wx:if="{{latitude && longitude && amapReady}}"
style="top: {{statusBarHeight + 16}}px; right: {{16}}px;"
bindtap="openProfile">
<view class="avatar-container">
<text class="avatar-text">{{userInfo.nickname ? userInfo.nickname.charAt(0) : 'U'}}</text>
<view class="online-dot"></view>
<!-- 添加未读消息和好友请求的小红点 -->
<view class="notification-dot" wx:if="{{unreadMessageCount > 0 || friendRequestCount > 0}}"></view>
</view>
</view>
<!-- 右侧功能按钮组 -->
<view class="right-controls" wx:if="{{latitude && longitude && amapReady}}"
style="top: calc({{statusBarHeight + navBarHeight + 20}}px); right: 16px;">
<!-- 刷新按钮 -->
<view class="control-btn refresh-btn" bindtap="onRefresh">
<view class="btn-icon refresh-icon">🔄</view>
</view>
<!-- 图层切换按钮 -->
<view class="control-btn layer-btn" bindtap="toggleMapLayer">
<view class="btn-icon layer-icon">
<text wx:if="{{currentMapType === 'satellite'}}">🛰️</text>
<text wx:else>🗺️</text>
</view>
</view>
<!-- 附近的人按钮 -->
<view class="control-btn nearby-btn" bindtap="showNearbyPeople">
<view class="btn-icon nearby-icon">👀</view>
<view class="nearby-count" wx:if="{{nearbyCount > 0}}">{{nearbyCount}}</view>
</view>
</view>
<!-- 底部中间定位按钮 - 回到当前位置 -->
<view class="location-btn" wx:if="{{latitude && longitude && amapReady}}"
style="bottom: {{safeAreaBottom + 120}}px;"
bindtap="centerToUserLocation">
<view class="location-btn-inner">
<view class="location-icon">📍</view>
<view class="location-ripple"></view>
</view>
</view>
<!-- 用户详情卡片 - 修复数据绑定 -->
<view class="user-card {{selectedUser ? 'show' : ''}}" wx:if="{{selectedUser}}"
style="bottom: {{safeAreaBottom + 40}}px;">
<view class="card-backdrop"></view>
<view class="card-content">
<view class="card-header">
<view class="card-avatar">
<text class="card-avatar-text">{{selectedUser.nickname ? selectedUser.nickname.charAt(0) : 'U'}}</text>
</view>
<view class="card-info">
<text class="card-name">{{selectedUser.nickname || '用户'}}</text>
<text class="card-distance" wx:if="{{selectedUser.distance}}">距离 {{selectedUser.distance}}米</text>
<text class="card-distance" wx:else>位置信息</text>
<view class="card-status">
<view class="status-dot online"></view>
<text class="status-text">在线</text>
</view>
</view>
<view class="card-close" bindtap="closeUserCard">
<text class="close-icon">✕</text>
</view>
</view>
<view class="card-actions">
<view class="action-btn chat-btn" bindtap="startChat" data-userid="{{selectedUser.userId}}">
<view class="action-icon">💬</view>
<text class="action-text">聊天</text>
</view>
<view class="action-btn add-btn" bindtap="addFriend" data-userid="{{selectedUser.userId}}" wx:if="{{!selectedUser.isFriend}}">
<view class="action-icon"></view>
<text class="action-text">加好友</text>
</view>
<view class="action-btn profile-btn" bindtap="viewProfile" data-userid="{{selectedUser.userId}}">
<view class="action-icon">👤</view>
<text class="action-text">资料</text>
</view>
</view>
</view>
</view>
<!-- 加载提示 - 优化设计 -->
<view class="loading-overlay" wx:if="{{!amapReady || (!latitude && !longitude)}}">
<view class="loading-content">
<view class="loading-spinner">
<view class="spinner-ring"></view>
<view class="spinner-ring"></view>
<view class="spinner-ring"></view>
</view>
<text class="loading-text">{{loadingText}}</text>
<text class="loading-subtitle">正在连接位置服务...</text>
</view>
</view>
<!-- 🔥 底部浮动按钮组 - 好友和消息 -->
<view class="floating-buttons" style="bottom: {{safeAreaBottom + 40}}px;">
<!-- 好友按钮 -->
<view class="floating-btn friends-btn" bindtap="openFriends">
<view class="btn-icon">
<text class="icon-emoji">👥</text>
<view class="icon-glow"></view>
</view>
<text class="btn-label">好友</text>
<view class="notification-badge" wx:if="{{friendRequestCount > 0}}">
<text class="badge-text">{{friendRequestCount > 99 ? '99+' : friendRequestCount}}</text>
</view>
</view>
<!-- 消息按钮 -->
<view class="floating-btn messages-btn" bindtap="openMessages">
<view class="btn-icon">
<text class="icon-emoji">💬</text>
<view class="icon-glow"></view>
</view>
<text class="btn-label">消息</text>
<view class="notification-badge" wx:if="{{unreadMessageCount > 0}}">
<text class="badge-text">{{unreadMessageCount > 99 ? '99+' : unreadMessageCount}}</text>
</view>
</view>
<!-- 地点标记 -->
<view class="floating-btn messages-btn" bindtap="onOpenLocationMarkModal">
<view class="btn-icon">
<text class="icon-emoji">🏠</text>
<view class="icon-glow"></view>
</view>
<text class="btn-label">地点</text>
<view class="notification-badge" wx:if="{{unreadMessageCount > 0}}">
<text class="badge-text">{{unreadMessageCount > 99 ? '99+' : unreadMessageCount}}</text>
</view>
</view>
</view>
<!-- POI详情卡片最上层 -->
<view class="user-card {{poiDetail ? 'show' : ''}}" wx:if="{{poiDetail}}" style="bottom: {{safeAreaBottom + 60}}px; z-index:9999;">
<view class="card-backdrop"></view>
<view class="card-content">
<view class="card-header">
<view class="card-avatar">
<text class="card-avatar-text">POI</text>
</view>
<view class="card-info">
<text class="card-name">{{poiDetail.name || 'POI'}}</text>
<text class="card-distance">{{poiDetail.address || '详细地址未知'}}</text>
</view>
<view class="card-close" bindtap="closePoiCard">
<text class="close-icon">✕</text>
</view>
</view>
<view class="card-actions">
<view class="action-btn" bindtap="onPoiFavorite">
<view class="action-icon">⭐</view>
<text class="action-text">收藏</text>
</view>
<view class="action-btn" bindtap="onPoiNavigate">
<view class="action-icon">🧭</view>
<text class="action-text">导航</text>
</view>
<view class="action-btn" bindtap="onPoiRemind">
<view class="action-icon">⏰</view>
<text class="action-text">提醒</text>
</view>
</view>
</view>
</view>
<!-- POI提醒操作弹窗 -->
<view class="user-card show" wx:if="{{showPoiRemindModal}}" style="bottom: {{safeAreaBottom + 60}}px; z-index:10000;">
<view class="card-backdrop"></view>
<view class="card-content">
<view class="remind-title-block">
<text class="remind-title">地点设置</text>
<text class="remind-subtitle">到达或离开该地点时提醒你和好友</text>
</view>
<view class="card-header">
<view class="card-avatar">
<text class="card-avatar-text">POI</text>
</view>
<view class="card-info">
<!-- 名称可编辑 -->
<view class="card-name-editable">
<input class="card-name-input" value="{{lastPoiForRemind.name}}" placeholder="请输入地点名称" bindinput="onPoiRemindNameInput" />
<view class="card-name-tip">可点击编辑名称</view>
</view>
<text class="card-distance">{{lastPoiForRemind.address || '详细地址未知'}}</text>
</view>
<view class="card-actions-right">
<button class="remind-save-btn" size="mini" type="primary" bindtap="onSavePoiRemind">保存</button>
<view class="card-close" bindtap="closePoiRemindModal">
<text class="close-icon">✕</text>
</view>
</view>
</view>
<!-- 好友选择与提醒模式 -->
<view class="remind-friends-mode-row">
<view class="remind-friends-block">
<button class="friend-select-btn" size="mini" type="primary" bindtap="onOpenFriendSelectModal">选择好友</button>
<view class="remind-friends-avatars">
<block wx:for="{{[0,1,2,3,4]}}" wx:key="index">
<view class="friend-avatar-mini">
<image wx:if="{{poiRemindSelectedFriends[index] && poiRemindSelectedFriends[index].avatarUrl}}" src="{{poiRemindSelectedFriends[index].avatarUrl}}" class="avatar-img-mini" />
<text wx:elif="{{poiRemindSelectedFriends[index]}}" class="avatar-txt-mini">{{poiRemindSelectedFriends[index].nickname ? poiRemindSelectedFriends[index].nickname.charAt(0) : 'U'}}</text>
<text wx:else class="avatar-txt-mini avatar-placeholder">+</text>
</view>
</block>
</view>
</view>
<view class="remind-mode-block">
<checkbox-group class="remind-mode-checkbox" bindchange="onPoiRemindModeChange" value="{{poiRemindMode}}">
<label class="remind-mode-label">
<checkbox value="arrive" name="arrive" />到达
</label>
<label class="remind-mode-label">
<checkbox value="leave" name="leave" />离开
</label>
</checkbox-group>
</view>
</view>
<view class="remind-slider-block" style="flex-direction: column; align-items: stretch; gap: 8px;">
<view style="display: flex; align-items: center; justify-content: space-between;">
<text class="remind-label">提醒范围</text>
<text class="remind-value highlight">{{poiRemindRadius}} 米</text>
</view>
<slider min="100" max="1000" step="10" value="{{poiRemindRadius}}" show-value="false" bindchange="onPoiRemindRadiusChange" />
</view>
<view class="remind-desc">拖动滑块,地图上圆圈范围会实时变化</view>
</view>
</view>
<!-- 好友选择弹窗 -->
<view class="friend-select-modal" wx:if="{{showFriendSelectModal}}">
<view class="friend-select-content">
<view class="friend-select-header">
<view class="friend-select-title">选择好友最多3个</view>
<view class="friend-select-close" bindtap="onCloseFriendSelect">✕</view>
</view>
<!-- 已选好友横向展示 -->
<view class="friend-selected-row">
<block wx:for="{{[0,1,2]}}" wx:key="index">
<view class="friend-selected-avatar" wx:if="{{poiRemindSelectedFriends[index]}}" bindtap="onRemoveSelectedFriend" data-userid="{{poiRemindSelectedFriends[index].userId}}">
<image wx:if="{{poiRemindSelectedFriends[index].avatarUrl}}" src="{{poiRemindSelectedFriends[index].avatarUrl}}" class="avatar-img-mini" />
<text wx:else class="avatar-txt-mini">{{poiRemindSelectedFriends[index].nickname ? poiRemindSelectedFriends[index].nickname.charAt(0) : 'U'}}</text>
<view class="friend-selected-remove">✕</view>
</view>
<view class="friend-selected-avatar friend-selected-placeholder" wx:else>+</view>
</block>
</view>
<!-- 搜索框 -->
<input class="friend-search-input" placeholder="搜索昵称" value="{{friendSearchText}}" bindinput="onFriendSearchInput" />
<scroll-view scroll-y="true" class="friend-list-scroll">
<block wx:for="{{filteredFriendsList}}" wx:for-item="item" wx:key="userId">
<view class="friend-item {{item.selected ? 'selected' : ''}} {{poiRemindSelectedFriends.length>=3 && !item.selected ? 'disabled' : ''}}" bindtap="onToggleFriendSelect" data-userid="{{item.userId}}">
<image wx:if="{{item.avatarUrl}}" src="{{item.avatarUrl}}" class="friend-avatar" />
<view wx:else class="friend-avatar friend-avatar-txt">{{item.nickname ? item.nickname.charAt(0) : 'U'}}</view>
<text class="friend-nickname">{{item.nickname}}</text>
<view class="friend-check" wx:if="{{item.selected}}">✔</view>
</view>
</block>
</scroll-view>
<button class="friend-select-done" type="primary" size="mini" bindtap="onFriendSelectDone" disabled="{{poiRemindSelectedFriends.length===0}}">完成</button>
</view>
</view>
<!-- 地点标记弹窗(底部卡片) -->
<view class="location-mark-modal" wx:if="{{showLocationMarkModal}}">
<!-- 遮罩层,点击关闭 -->
<view class="location-mark-mask" bindtap="onCloseLocationMarkModal"></view>
<!-- 底部弹出卡片 -->
<view class="location-mark-sheet location-mark-sheet-animate">
<!-- 顶部手柄和关闭按钮 -->
<view class="location-mark-sheet-handle-row">
<view class="location-mark-sheet-handle"></view>
<view class="location-mark-sheet-close" bindtap="onCloseLocationMarkModal">✕</view>
</view>
<!-- 标题 -->
<view class="location-mark-sheet-title">地点标记</view>
<!-- 搜索框 -->
<view class="location-mark-search-row">
<text class="iconfont search-icon">🔍</text>
<input class="location-mark-search" placeholder="搜索收藏地址" value="{{locationMarkSearchText}}" bindinput="onLocationMarkSearchInput" />
<text wx:if="{{locationMarkSearchText}}" class="iconfont clear-icon" bindtap="onClearLocationMarkSearch">✕</text>
</view>
<!-- 地址列表 -->
<scroll-view scroll-y class="location-mark-list-sheet">
<block wx:for="{{filteredLocationMarkList}}" wx:for-item="item" wx:key="id">
<view class="location-mark-item-card" bindtap="onLocationMarkItemTap" data-item="{{item}}">
<view class="location-mark-item-main-opt">
<!-- 左侧主信息 -->
<view class="location-mark-item-info-opt">
<view class="location-mark-name-opt">{{item.name}}</view>
<view class="location-mark-coord-opt">{{item.lat}}, {{item.lng}}</view>
</view>
<!-- 右上操作按钮 -->
<view class="location-mark-actions-opt">
<!-- 左侧主信息
<button class="location-mark-edit-btn-opt" size="mini" bindtap="onEditLocationMarkName" data-id="{{item.id}}">
<text class="iconfont">✏️</text> 修改
</button>
-->
<button class="location-mark-del-btn-opt" size="mini" bindtap="onDeleteLocationMark" data-id="{{item.id}}" catchtap="onDeleteLocationMark">
<text class="iconfont">🗑️</text> 删除
</button>
</view>
</view>
<!-- 下方提醒好友和类型 -->
<view class="location-mark-meta-opt">
<view class="location-mark-friends-opt">
<block wx:for="{{item.remindFriends}}" wx:for-item="f" wx:key="userId">
<view class="location-mark-friend-avatar-opt">
<image wx:if="{{f.avatarUrl}}" src="{{f.avatarUrl}}" />
<text wx:else>{{f.nickname ? f.nickname.charAt(0) : 'U'}}</text>
</view>
</block>
<view class="location-mark-friend-btn-opt" bindtap="onSetRemindFriends" data-id="{{item.id}}" catchtap="onSetRemindFriends">+</view>
</view>
<view class="location-mark-type-tag-opt {{item.remindType}}" bindtap="onToggleRemindType" data-id="{{item.id}}" catchtap="onToggleRemindType">
{{item.remindType == 'arrive' ? '到达' : '离开'}}
</view>
</view>
</view>
</block>
<view wx:if="{{filteredLocationMarkList.length === 0}}" class="location-mark-empty">
<text class="iconfont empty-icon">📍</text>
<text>暂无收藏地址</text>
</view>
</scroll-view>
<!-- 加号浮动按钮 -->
<view class="location-mark-fab" bindtap="onAddLocationPlace">
<text class="location-mark-fab-icon">+</text>
</view>
</view>
</view>
<!-- POI周边列表弹窗 -->
<view class="poi-around-modal" wx:if="{{showPoiAroundModal}}">
<view class="poi-around-mask" bindtap="onClosePoiAroundModal"></view>
<view class="poi-around-sheet">
<!-- 右上角关闭按钮 -->
<view class="poi-around-close-x" bindtap="onClosePoiAroundModal">✕</view>
<view class="poi-around-title">附近地点</view>
<scroll-view scroll-y class="poi-around-list">
<block wx:for="{{poiAroundList}}" wx:for-item="item" wx:key="id">
<view class="poi-around-item" bindtap="onPoiAroundItemTap" data-item="{{item}}">
<view class="poi-around-name">{{item.name}}</view>
<view class="poi-around-address">{{item.address}}</view>
<view class="poi-around-distance">{{item.distanceStr}}</view>
</view>
</block>
<view wx:if="{{poiAroundList.length === 0}}" class="poi-around-empty">暂无数据</view>
</scroll-view>
<view class="poi-around-close-btn" bindtap="onClosePoiAroundModal">关闭</view>
</view>
</view>
<!-- 商家信息弹窗 -->
<view wx:if="{{merchantInfoModalVisible}}" class="merchant-info-modal">
<view class="merchant-info-card">
<button class="close-btn-abs" bindtap="closeMerchantInfo" aria-label="关闭">
<text class="close-icon">×</text>
</button>
<view class="merchant-info-header">
<text class="merchant-name">{{selectedMerchant.name}}</text>
</view>
<view class="merchant-info-row">
<text class="merchant-address">{{selectedMerchant.address}}</text>
</view>
<view class="merchant-info-row merchant-info-meta">
<text>距离:{{formatDistance(selectedMerchant.distance)}}</text>
<text>经纬度:{{selectedMerchant.latitude}}, {{selectedMerchant.longitude}}</text>
</view>
<view class="merchant-packages">
<block wx:for="{{selectedMerchant.packages}}" wx:key="title">
<view class="package-card">
<view class="package-card-header">
<text class="package-title">{{item.title}}</text>
<text class="package-price">{{item.price}}</text>
</view>
<text class="package-desc">{{item.desc}}</text>
</view>
</block>
</view>
<view class="merchant-actions">
<button class="action-btn action-fav" bindtap="onFavoriteMerchant">
<text wx:if="{{selectedMerchant.isFavorited}}">★ 已收藏</text>
<text wx:else>☆ 收藏</text>
</button>
<button class="action-btn action-nav" bindtap="onNavigateMerchant">去这里</button>
</view>
</view>
</view>
<!-- 🔧 开发调试按钮 - WebSocket测试 -->
<view class="debug-button" style="top: {{statusBarHeight + 60}}px; right: 16px;" bindtap="openWebSocketTest">
<text class="debug-icon">🔧</text>
</view>
</view>