miniprogramme/pages/map/map.wxml
2025-09-12 16:08:17 +08:00

478 lines
No EOL
22 KiB
Text
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!--地图页面 - 高德地图集成版本-->
<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>