| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624 |
- <template>
- <view class="map_container">
- <view id="MapContainer" style="width: 100%;height: 300px;position: relative;"></view>
- </view>
- </template>
- <script>
- export default {
- name: "TdMap",
- props: {
- allowSelection: {
- type: Boolean,
- default: false
- },
- showCoordinates: {
- type: Boolean,
- default: false
- },
- mapKey: {
- type: [String, Number],
- default: 0
- }
- },
- mounted() {
- console.log('【地图组件挂载】开始初始化');
- },
- beforeDestroy() {
- // 清理资源
- this.$emit('destroy-map');
- }
- };
- </script>
- <script module="randerJSMap" lang="renderjs">
- export default {
- data() {
- return {
- mapRef: null,
- markerRef: null,
- labelRef: null,
- mapLoaded: false,
- initPromise: null,
- lastDataHash: '',
- storageCheckTimer: null,
- lastStorageDataStr: ''
- };
- },
- methods: {
- /**
- * 获取地图容器
- */
- getMapContainer() {
- return new Promise((resolve) => {
- let container = document.getElementById('MapContainer');
- if (container) {
- console.log('【容器获取】成功', container);
- return resolve(container);
- }
- // APP端兜底
- uni.createSelectorQuery().in(this.$ownerInstance)
- .select('#MapContainer')
- .boundingClientRect((rect) => {
- if (rect) {
- container = document.querySelector('#MapContainer');
- resolve(container);
- } else {
- resolve(null);
- }
- }).exec();
- });
- },
- /**
- * 直接从storage获取地图数据
- */
- getMapDataFromStorage() {
- try {
- console.log('【RenderJS开始获取storage数据】');
- const storageData = uni.getStorageSync('address');
- console.log('【RenderJS从storage获取的原始数据】', storageData);
- console.log('【RenderJS数据类型】', typeof storageData);
- let parsedData = null;
- if (storageData) {
- // 如果是字符串,尝试解析为JSON
- if (typeof storageData === 'string') {
- try {
- parsedData = JSON.parse(storageData);
- console.log('【RenderJS解析后的数据】', parsedData);
- } catch (parseError) {
- console.error('【RenderJS JSON解析失败】', parseError);
- return this.getDefaultMapData();
- }
- } else {
- parsedData = storageData;
- }
- // 验证数据格式
- const isValidData = parsedData &&
- typeof parsedData === 'object' &&
- (typeof parsedData.lng !== 'undefined' || parsedData.longitude) &&
- (typeof parsedData.lat !== 'undefined' || parsedData.latitude);
- console.log('【RenderJS数据验证结果】', isValidData);
- if (isValidData) {
- // 支持多种字段名
- const lng = parsedData.lng || parsedData.longitude || 112.55;
- const lat = parsedData.lat || parsedData.latitude || 37.87;
- const address = parsedData.address || parsedData.name || parsedData.location || '未知位置';
- return {
- lng: Number(lng),
- lat: Number(lat),
- address: address,
- isValid: true
- };
- } else {
- console.warn('【RenderJS】storage数据格式无效,使用默认值');
- console.warn('【RenderJS无效数据详情】', parsedData);
- return this.getDefaultMapData();
- }
- } else {
- console.log('【RenderJS】storage中无address数据,使用默认值');
- return this.getDefaultMapData();
- }
- } catch (error) {
- console.error('【RenderJS】从storage获取数据失败', error);
- return this.getDefaultMapData();
- }
- },
- /**
- * 获取默认地图数据
- */
- getDefaultMapData() {
- return {
- lng: 112.55,
- lat: 37.87,
- address: '山西省太原市',
- isValid: false
- };
- },
- /**
- * 创建标记和标签
- */
- createMarkerWithLabel(lngLat, address) {
- try {
- const T = window.T;
- if (!this.mapLoaded || !lngLat || !this.mapRef) {
- console.error('【标点失败】前置条件不足');
- return;
- }
- // 1. 移除旧标记
- this.clearMarkers();
- // 2. 创建自定义图标(红色标记)
- const icon = new T.Icon({
- iconUrl: this.generateMarkerIcon('#ff0000'),
- iconSize: new T.Point(30, 30),
- iconAnchor: new T.Point(15, 30)
- });
- // 3. 创建标记(使用T.Marker类)
- this.markerRef = new T.Marker(lngLat, {
- icon: icon,
- title: address || '未知位置',
- draggable: false
- });
- // 4. 创建文本标签
- this.labelRef = new T.Label({
- text: address || '未知位置',
- position: lngLat,
- style: {
- color: '#333333',
- backgroundColor: 'rgba(255, 255, 255, 0.9)',
- borderColor: '#ff0000',
- borderWidth: 1,
- borderRadius: 4,
- fontSize: '14px',
- fontWeight: '500',
- padding: '6px 10px'
- },
- offset: new T.Point(0, -40) // 向上偏移
- });
- // 5. 添加到地图
- this.mapRef.addOverLay(this.markerRef);
- this.mapRef.addOverLay(this.labelRef);
- console.log('【创建标记】成功', {
- lng: lngLat.getLng(),
- lat: lngLat.getLat(),
- address
- });
- // 6. 调整地图视图
- this.mapRef.setViewport([lngLat]);
- // 7. 标记点击事件
- this.markerRef.addEventListener('click', (e) => {
- const coord = {
- lng: lngLat.getLng().toFixed(6),
- lat: lngLat.getLat().toFixed(6)
- };
- const message = `地址:${address}\n坐标:${coord.lng}, ${coord.lat}`;
- uni.showModal({
- title: '位置信息',
- content: message,
- showCancel: false,
- confirmText: '确定'
- });
- });
- // 8. 标签点击事件
- this.labelRef.addEventListener('click', (e) => {
- e.stopPropagation();
- uni.showToast({
- title: `地址:${address}`,
- icon: 'none',
- duration: 3000
- });
- });
- } catch (error) {
- console.error('【创建标记异常】', error);
- // 备用方案:使用默认标记
- this.createDefaultMarker(lngLat, address);
- }
- },
- /**
- * 生成标记图标
- */
- generateMarkerIcon(color) {
- const size = 30;
- const canvas = document.createElement('canvas');
- canvas.width = size;
- canvas.height = size;
- const ctx = canvas.getContext('2d');
- // 外圆(白色边框)
- ctx.beginPath();
- ctx.arc(size / 2, size / 2, size / 2 - 1, 0, Math.PI * 2);
- ctx.fillStyle = '#ffffff';
- ctx.fill();
- // 内圆(红色)
- ctx.beginPath();
- ctx.arc(size / 2, size / 2, size / 2 - 4, 0, Math.PI * 2);
- ctx.fillStyle = color;
- ctx.fill();
- return canvas.toDataURL();
- },
- /**
- * 创建默认标记(备用方案)
- */
- createDefaultMarker(lngLat, address) {
- try {
- const T = window.T;
- // 清除旧标记
- this.clearMarkers();
- // 创建默认标记
- this.markerRef = new T.Marker(lngLat, {
- title: address || '未知位置'
- });
- // 设置默认图标
- this.markerRef.setIcon(new T.Icon.Default());
- // 添加到地图
- this.mapRef.addOverLay(this.markerRef);
- // 标记点击事件
- this.markerRef.addEventListener('click', (e) => {
- const coord = {
- lng: lngLat.getLng().toFixed(6),
- lat: lngLat.getLat().toFixed(6)
- };
- const message = `地址:${address}\n坐标:${coord.lng}, ${coord.lat}`;
- uni.showModal({
- title: '位置信息',
- content: message,
- showCancel: false,
- confirmText: '确定'
- });
- });
- console.log('【创建默认标记】成功');
- } catch (error) {
- console.error('【创建默认标记失败】', error);
- }
- },
- /**
- * 清除所有标记
- */
- clearMarkers() {
- try {
- if (this.markerRef && this.mapRef) {
- this.mapRef.removeOverLay(this.markerRef);
- this.markerRef = null;
- }
- if (this.labelRef && this.mapRef) {
- this.mapRef.removeOverLay(this.labelRef);
- this.labelRef = null;
- }
- } catch (error) {
- console.warn('清除标记时出错', error);
- }
- },
- /**
- * 初始化地图
- */
- async initMap() {
- try {
- console.log('【初始化地图】开始');
- const T = window.T;
- if (!T || !T.Map) {
- throw new Error('天地图API未正确加载');
- }
- // 获取容器
- const container = await this.getMapContainer();
- if (!container) {
- throw new Error('地图容器获取失败');
- }
- // 直接从storage获取位置数据
- const mapData = this.getMapDataFromStorage();
- console.log('【初始化地图使用的数据】', mapData);
- const center = new T.LngLat(Number(mapData.lng), Number(mapData.lat));
- console.log('【地图中心点】', center);
- // 创建地图实例
- this.mapRef = new T.Map(container);
- this.mapRef.centerAndZoom(center, 16);
- this.mapRef.enableScrollWheelZoom();
- this.mapLoaded = true;
- console.log('【初始化地图】成功');
- // 创建标记和标签
- setTimeout(() => {
- this.createMarkerWithLabel(center, mapData.address);
- }, 500);
- } catch (error) {
- console.error('【初始化失败】', error);
- this.mapLoaded = false;
- uni.showToast({
- title: '地图初始化失败',
- icon: 'error',
- duration: 3000
- });
- }
- },
- /**
- * 确保API加载完成
- */
- async ensureAPILoaded() {
- if (window.T && window.T.Map && window.T.LngLat) {
- console.log('【API已加载】');
- return Promise.resolve();
- }
- return new Promise((resolve, reject) => {
- console.log('【开始加载API】');
- // 检查是否已经在加载中
- const existingScript = document.querySelector('script[src*="tianditu.gov.cn"]');
- if (existingScript) {
- console.log('【API已在加载中】');
- const checkInterval = setInterval(() => {
- if (window.T && window.T.Map && window.T.LngLat) {
- clearInterval(checkInterval);
- console.log('【API加载完成】');
- resolve();
- }
- }, 100);
- setTimeout(() => {
- clearInterval(checkInterval);
- reject(new Error('API加载超时'));
- }, 10000);
- return;
- }
- // 加载天地图4.0 API
- const script = document.createElement('script');
- script.src = 'https://api.tianditu.gov.cn/api?v=4.0&tk=9bcf63358817bfb878e8236de8bf1423';
- script.onload = () => {
- console.log('【API脚本加载完成】');
- const checkT = setInterval(() => {
- if (window.T && window.T.Map && window.T.LngLat) {
- clearInterval(checkT);
- console.log('【T对象初始化完成】');
- resolve();
- }
- }, 50);
- setTimeout(() => {
- clearInterval(checkT);
- reject(new Error('T对象初始化超时'));
- }, 5000);
- };
- script.onerror = (err) => {
- console.error('API加载失败', err);
- reject(new Error('API加载失败'));
- };
- document.head.appendChild(script);
- });
- },
- /**
- * 更新地图标记(从storage获取最新数据)
- */
- updateMapFromStorage() {
- if (!this.mapLoaded || !this.mapRef) {
- console.warn('【更新被跳过】地图未加载');
- return;
- }
- // 获取最新数据
- const mapData = this.getMapDataFromStorage();
- console.log('【更新地图使用的数据】', mapData);
- // 生成数据哈希,避免重复更新
- const dataHash = JSON.stringify(mapData);
- if (dataHash === this.lastDataHash) {
- console.log('【数据未变化,跳过更新】');
- return;
- }
- this.lastDataHash = dataHash;
- // 验证坐标有效性
- const isValidLng = typeof mapData.lng !== 'undefined' && !isNaN(Number(mapData.lng)) && mapData.lng !== null;
- const isValidLat = typeof mapData.lat !== 'undefined' && !isNaN(Number(mapData.lat)) && mapData.lat !== null;
- console.log('【坐标验证】', {
- isValidLng,
- isValidLat,
- lng: mapData.lng,
- lat: mapData.lat
- });
- if (!isValidLng || !isValidLat) {
- console.warn('无效坐标,使用默认值');
- const defaultLngLat = new window.T.LngLat(112.55, 37.87);
- this.createMarkerWithLabel(defaultLngLat, mapData.address || '默认位置');
- return;
- }
- // 创建新标记
- const targetLngLat = new window.T.LngLat(Number(mapData.lng), Number(mapData.lat));
- // 移动地图中心
- this.mapRef.panTo(targetLngLat);
- setTimeout(() => {
- this.createMarkerWithLabel(targetLngLat, mapData.address || '未知位置');
- }, 300);
- },
- /**
- * 设置storage监听
- */
- setupStorageListener() {
- // 监听uni-app自定义事件
- uni.$on('storageChange', (data) => {
- console.log('【RenderJS收到storage变化事件】', data);
- if (data && data.key === 'address') {
- this.updateMapFromStorage();
- }
- });
- // 定时检查(兼容性更好)
- this.storageCheckTimer = setInterval(() => {
- this.checkStorageUpdate();
- }, 2000);
- },
- /**
- * 检查storage是否更新
- */
- checkStorageUpdate() {
- try {
- const currentData = uni.getStorageSync('address');
- const currentStr = JSON.stringify(currentData);
- // 首次检查时初始化lastStorageData
- if (!this.lastStorageDataStr) {
- this.lastStorageDataStr = currentStr;
- return;
- }
- if (currentStr !== this.lastStorageDataStr) {
- console.log('【storage数据已更新】', currentData);
- this.lastStorageDataStr = currentStr;
- this.updateMapFromStorage();
- }
- } catch (error) {
- console.error('检查storage更新失败', error);
- }
- }
- },
- mounted() {
- console.log('【RenderJS】开始加载');
- // 延迟初始化
- setTimeout(async () => {
- try {
- await this.ensureAPILoaded();
- await this.initMap();
- // 初始化成功后设置监听
- this.setupStorageListener();
- // 保存初始数据用于比较
- try {
- this.lastStorageDataStr = JSON.stringify(uni.getStorageSync('address'));
- } catch (error) {
- this.lastStorageDataStr = '';
- }
- } catch (error) {
- console.error('【初始化失败】', error);
- uni.showToast({
- title: '地图加载失败',
- icon: 'error',
- duration: 3000
- });
- }
- }, 500);
- },
- beforeDestroy() {
- // 清理资源
- if (this.mapRef) {
- this.clearMarkers();
- this.mapRef = null;
- }
- // 清理定时器
- if (this.storageCheckTimer) {
- clearInterval(this.storageCheckTimer);
- this.storageCheckTimer = null;
- }
- // 移除事件监听
- uni.$off('storageChange');
- this.mapLoaded = false;
- console.log('【RenderJS】组件销毁');
- }
- };
- </script>
- <style scoped lang="scss">
- .map_container {
- position: relative;
- width: 100%;
- height: 300px;
- z-index: 1;
- overflow: hidden;
- background: #f0f0f0;
- border-radius: 8px;
- border: 1px solid #e0e0e0;
- }
- #MapContainer {
- position: relative;
- z-index: 9999;
- display: block;
- width: 100% !important;
- height: 100% !important;
- padding: 0 !important;
- margin: 0 !important;
- border: none !important;
- border-radius: 8px;
- }
- /* 自定义样式 */
- ::v-deep .tdt-label {
- cursor: pointer;
- transition: all 0.3s ease;
- &:hover {
- background-color: rgba(255, 255, 255, 1) !important;
- border-color: #ff4444 !important;
- box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2) !important;
- }
- }
- ::v-deep .tdt-marker-icon {
- cursor: pointer;
- transition: transform 0.2s;
- &:hover {
- transform: scale(1.2);
- }
- }
- </style>
|