|
|
@@ -0,0 +1,825 @@
|
|
|
+<template>
|
|
|
+ <view class="container">
|
|
|
+ <view class="top"></view>
|
|
|
+ <view class="head">
|
|
|
+ <view class="handle">
|
|
|
+ <view class="head_left">
|
|
|
+ <span class="cuIcon-back" @click="back"></span>
|
|
|
+ </view>
|
|
|
+ <span class="center">选择检测站</span>
|
|
|
+ <span class="submit-btn" @click="submit">确定</span>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+
|
|
|
+ <!-- 地图容器 -->
|
|
|
+ <view id="mapContainer" class="map-container"></view>
|
|
|
+
|
|
|
+ <!-- 搜索功能区域 -->
|
|
|
+ <view class="search-panel" v-if="showSearch">
|
|
|
+ <view class="search-input-group">
|
|
|
+ <input class="search-input" v-model="searchKeyword" placeholder="输入关键词搜索地点" @confirm="handleSearch"
|
|
|
+ @input="onSearchInput" />
|
|
|
+ <button class="search-btn" @click="handleSearch">搜索</button>
|
|
|
+ </view>
|
|
|
+
|
|
|
+ <view class="search-results" v-if="dataTips.length > 0">
|
|
|
+ <view class="result-header">
|
|
|
+ <text>搜索结果 ({{ dataTips.length }} 个)</text>
|
|
|
+ <text class="clear-results" @click="clearSearchResults">清除搜索结果</text>
|
|
|
+ </view>
|
|
|
+ <scroll-view class="results-list" scroll-y>
|
|
|
+ <view v-for="(result, index) in dataTips" :key="index"
|
|
|
+ class="result-item"
|
|
|
+ @click="selectSearchResult(result)"
|
|
|
+ :class="{'selected': result.check}">
|
|
|
+ <view class="left">
|
|
|
+ <view class="result-name">
|
|
|
+ <text v-show="result.check" class="selected-tag">已选</text>
|
|
|
+ {{ result.name }}
|
|
|
+ </view>
|
|
|
+ <view class="result-address">{{ result.address }}</view>
|
|
|
+ </view>
|
|
|
+ <view class="right">
|
|
|
+ <image src="/static/coupon/danxuan.png" mode="" v-if="result.check"></image>
|
|
|
+ <image src="/static/product/round.png" mode="" v-else></image>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+ </scroll-view>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script>
|
|
|
+ export default {
|
|
|
+ props: {
|
|
|
+ initialPosition: {
|
|
|
+ type: Object,
|
|
|
+ default: () => ({
|
|
|
+ lnglat: null,
|
|
|
+ address: ''
|
|
|
+ })
|
|
|
+ },
|
|
|
+ allowSelection: {
|
|
|
+ type: Boolean,
|
|
|
+ default: true
|
|
|
+ },
|
|
|
+ showCoordinates: {
|
|
|
+ type: Boolean,
|
|
|
+ default: true
|
|
|
+ },
|
|
|
+ showSearch: {
|
|
|
+ type: Boolean,
|
|
|
+ default: true
|
|
|
+ }
|
|
|
+ },
|
|
|
+ data() {
|
|
|
+ return {
|
|
|
+ tk: '9bcf63358817bfb878e8236de8bf1423',
|
|
|
+ searchKeyword: '',
|
|
|
+ dataTips: [], // 存储:已选检测站 + 搜索结果(合并去重)
|
|
|
+ selectedResult: null,
|
|
|
+ renderjsReady: false,
|
|
|
+ merchantId: uni.getStorageSync('login_user_info')?.merchantId || '',
|
|
|
+ originalSelected: [] // 新增:存储原始已选检测站(用于去重和保留)
|
|
|
+ };
|
|
|
+ },
|
|
|
+ mounted() {
|
|
|
+ console.log('组件mounted');
|
|
|
+ // 1. 先获取已选检测站(保留原有数据)
|
|
|
+ this.getShopList();
|
|
|
+ // 2. 等待renderjs初始化
|
|
|
+ setTimeout(() => {
|
|
|
+ this.waitForRenderjs();
|
|
|
+ }, 1000);
|
|
|
+ },
|
|
|
+ methods: {
|
|
|
+ // 核心:获取已选检测站(保留到originalSelected,避免被搜索覆盖)
|
|
|
+ getShopList() {
|
|
|
+ if (!this.merchantId) {
|
|
|
+ console.warn('商户ID不存在');
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ this.$http.get(`/ts/localTsMerchantLinkInspection/list?merchantId=${this.merchantId}`).then(res => {
|
|
|
+ if (res.data.code == 200) {
|
|
|
+ // 存储原始已选数据(用于去重)
|
|
|
+ this.originalSelected = res.data.result.map(item => ({
|
|
|
+ ...item,
|
|
|
+ check: true, // 强制设置为已选
|
|
|
+ lonlat: `${item.inspectionStationLongitude},${item.inspectionStationLatitude}`,
|
|
|
+ address: item.detailedAddress,
|
|
|
+ name: item.inspectionStationName,
|
|
|
+ // 新增:用ID作为唯一标识(避免重复添加)
|
|
|
+ uniqueKey: item.id || `${item.inspectionStationLongitude}-${item.inspectionStationLatitude}`
|
|
|
+ }));
|
|
|
+ // 初始数据 = 原始已选数据
|
|
|
+ this.dataTips = [...this.originalSelected];
|
|
|
+ console.log(this.dataTips)
|
|
|
+ // 自动定位第一个已选检测站(传入isInit=true,不切换check状态)
|
|
|
+ if (this.dataTips.length > 0) {
|
|
|
+ this.selectSearchResult(this.dataTips[0], true); // 修改:添加isInit参数
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }).catch(err => {
|
|
|
+ console.error('获取已选检测站失败:', err);
|
|
|
+ });
|
|
|
+ },
|
|
|
+
|
|
|
+ // 等待renderjs就绪
|
|
|
+ waitForRenderjs() {
|
|
|
+ if (this.renderjsReady) return;
|
|
|
+
|
|
|
+ const checkInterval = setInterval(() => {
|
|
|
+ if (this.$refs && this.$refs.renderjsMap) {
|
|
|
+ clearInterval(checkInterval);
|
|
|
+ this.renderjsReady = true;
|
|
|
+ console.log('renderjs就绪');
|
|
|
+
|
|
|
+ if (this.initialPosition && this.isValidPosition(this.initialPosition)) {
|
|
|
+ this.setMapCenterFromProps();
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }, 100);
|
|
|
+
|
|
|
+ setTimeout(() => {
|
|
|
+ clearInterval(checkInterval);
|
|
|
+ console.warn('等待renderjs超时');
|
|
|
+ }, 10000);
|
|
|
+ },
|
|
|
+
|
|
|
+ setMapCenterFromProps() {
|
|
|
+ const mapData = {
|
|
|
+ lng: this.initialPosition.lng ||
|
|
|
+ (this.initialPosition.lnglat && this.initialPosition.lnglat.lng) ||
|
|
|
+ (this.initialPosition.lnglat && this.initialPosition.lnglat.longitude),
|
|
|
+ lat: this.initialPosition.lat ||
|
|
|
+ (this.initialPosition.lnglat && this.initialPosition.lnglat.lat) ||
|
|
|
+ (this.initialPosition.lnglat && this.initialPosition.lnglat.latitude),
|
|
|
+ address: this.initialPosition.address || ''
|
|
|
+ };
|
|
|
+
|
|
|
+ this.callRenderjsMethod('updateMapWithMarker', mapData);
|
|
|
+ },
|
|
|
+
|
|
|
+ callRenderjsMethod(methodName, data) {
|
|
|
+ if (!this.renderjsReady) {
|
|
|
+ console.warn('renderjs未就绪,延迟执行');
|
|
|
+ setTimeout(() => {
|
|
|
+ this.callRenderjsMethod(methodName, data);
|
|
|
+ }, 300);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ try {
|
|
|
+ if (this.$refs.renderjsMap) {
|
|
|
+ this.$refs.renderjsMap.callMethod(methodName, data);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ const ownerInstance = this.$ownerInstance;
|
|
|
+ if (ownerInstance && ownerInstance.callMethod) {
|
|
|
+ ownerInstance.callMethod('callRenderjsMethod', {
|
|
|
+ method: methodName,
|
|
|
+ data: data
|
|
|
+ });
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ console.error('无法调用renderjs方法');
|
|
|
+
|
|
|
+ } catch (error) {
|
|
|
+ console.error(`调用renderjs方法 ${methodName} 失败:`, error);
|
|
|
+ setTimeout(() => {
|
|
|
+ if (this.renderjsReady) {
|
|
|
+ this.callRenderjsMethod(methodName, data);
|
|
|
+ }
|
|
|
+ }, 500);
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+ // 选择检测站:新增isInit参数,区分初始化和用户点击
|
|
|
+ selectSearchResult(result, isInit = false) {
|
|
|
+ console.log('选择搜索结果:', result);
|
|
|
+
|
|
|
+ // 只有非初始化场景,才切换check状态(用户点击时)
|
|
|
+ if (!isInit) {
|
|
|
+ result.check = !result.check;
|
|
|
+ this.selectedResult = result.check ? result : null;
|
|
|
+ } else {
|
|
|
+ // 初始化场景:强制保持check=true,仅定位不切换
|
|
|
+ result.check = true;
|
|
|
+ this.selectedResult = result;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 提取经纬度并定位
|
|
|
+ const coords = this.extractCoordinates(result);
|
|
|
+ if (!coords) {
|
|
|
+ uni.showToast({
|
|
|
+ title: '无法获取位置坐标',
|
|
|
+ icon: 'none'
|
|
|
+ });
|
|
|
+ console.error('无法解析坐标:', result);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ const { lng, lat } = coords;
|
|
|
+ const mapData = {
|
|
|
+ lng: lng,
|
|
|
+ lat: lat,
|
|
|
+ address: result.address || result.name,
|
|
|
+ name: result.name,
|
|
|
+ fromSearch: true
|
|
|
+ };
|
|
|
+
|
|
|
+ this.callRenderjsMethod('updateMapWithMarker', mapData);
|
|
|
+ console.log('地图已定位到:', lng, lat, result.name);
|
|
|
+ },
|
|
|
+
|
|
|
+ // 核心提交:带上原有已选 + 当前新选
|
|
|
+ submit() {
|
|
|
+ // 过滤所有选中的检测站(包括原有已选和当前新选)
|
|
|
+ const selectedItems = this.dataTips.filter(item => item.check);
|
|
|
+ if (selectedItems.length === 0) {
|
|
|
+ uni.showToast({
|
|
|
+ title: '请至少选择一个检测站',
|
|
|
+ icon: 'none'
|
|
|
+ });
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 构造提交数据(兼容原有已选和新选)
|
|
|
+ const submitData = selectedItems.map(item => {
|
|
|
+ const locationParts = item.lonlat ? item.lonlat.split(",") : [];
|
|
|
+ return {
|
|
|
+ // 原有已选数据带id,新选数据可能没有,保留id(便于后端更新/去重)
|
|
|
+ id: item.id || '',
|
|
|
+ country: item.adcode || item.country || '',
|
|
|
+ detailedAddress: item.address || item.detailedAddress || '',
|
|
|
+ inspectionStationName: item.name || item.inspectionStationName || '',
|
|
|
+ locality: item.district || item.locality || '',
|
|
|
+ inspectionStationLongitude: item.inspectionStationLongitude || (locationParts[0] || null),
|
|
|
+ inspectionStationLatitude: item.inspectionStationLatitude || (locationParts[1] || null),
|
|
|
+ merchantId: this.merchantId
|
|
|
+ };
|
|
|
+ });
|
|
|
+
|
|
|
+ // 调用保存接口(后端需支持:存在id则更新,不存在则新增)
|
|
|
+ this.$http.post("/ts/localTsMerchantLinkInspection/add", submitData).then(res => {
|
|
|
+ console.log('保存检测站结果:', res);
|
|
|
+ if (res.data.code == 200) {
|
|
|
+ uni.showToast({
|
|
|
+ icon: 'none',
|
|
|
+ title: '保存成功(包含原有已选)'
|
|
|
+ });
|
|
|
+ this.back();
|
|
|
+ } else {
|
|
|
+ uni.showToast({
|
|
|
+ icon: 'none',
|
|
|
+ title: '保存失败:' + (res.data.msg || '未知错误')
|
|
|
+ });
|
|
|
+ }
|
|
|
+ }).catch(err => {
|
|
|
+ console.error('保存检测站失败:', err);
|
|
|
+ uni.showToast({
|
|
|
+ icon: 'none',
|
|
|
+ title: '网络错误,请重试'
|
|
|
+ });
|
|
|
+ });
|
|
|
+ },
|
|
|
+
|
|
|
+ // 提取坐标信息
|
|
|
+ extractCoordinates(item) {
|
|
|
+ let lng, lat;
|
|
|
+
|
|
|
+ if (item.lonlat) {
|
|
|
+ const coords = item.lonlat.split(',');
|
|
|
+ if (coords.length >= 2) {
|
|
|
+ lng = parseFloat(coords[0]);
|
|
|
+ lat = parseFloat(coords[1]);
|
|
|
+ }
|
|
|
+ } else if (item.longitude && item.latitude) {
|
|
|
+ lng = item.longitude;
|
|
|
+ lat = item.latitude;
|
|
|
+ } else if (item.lng && item.lat) {
|
|
|
+ lng = item.lng;
|
|
|
+ lat = item.lat;
|
|
|
+ } else if (item.inspectionStationLongitude && item.inspectionStationLatitude) {
|
|
|
+ lng = item.inspectionStationLongitude;
|
|
|
+ lat = item.inspectionStationLatitude;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (lng !== undefined && lat !== undefined &&
|
|
|
+ !isNaN(lng) && !isNaN(lat) &&
|
|
|
+ lng >= -180 && lng <= 180 &&
|
|
|
+ lat >= -90 && lat <= 90) {
|
|
|
+ return { lng, lat };
|
|
|
+ }
|
|
|
+
|
|
|
+ console.warn('无法提取有效坐标:', item);
|
|
|
+ return null;
|
|
|
+ },
|
|
|
+
|
|
|
+ back() {
|
|
|
+ uni.navigateBack({
|
|
|
+ delta: 1
|
|
|
+ });
|
|
|
+ },
|
|
|
+
|
|
|
+ isValidPosition(position) {
|
|
|
+ const lng = position.lng ||
|
|
|
+ (position.lnglat && (position.lnglat.lng || position.lnglat.longitude));
|
|
|
+ const lat = position.lat ||
|
|
|
+ (position.lnglat && (position.lnglat.lat || position.lnglat.latitude));
|
|
|
+ return lng && lat && !isNaN(parseFloat(lng)) && !isNaN(parseFloat(lat));
|
|
|
+ },
|
|
|
+
|
|
|
+ // 搜索:合并搜索结果与原有已选(去重)
|
|
|
+ handleSearch() {
|
|
|
+ if (!this.searchKeyword.trim()) {
|
|
|
+ uni.showToast({
|
|
|
+ title: '请输入搜索关键词',
|
|
|
+ icon: 'none'
|
|
|
+ });
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ this.searchByRestAPI(this.searchKeyword);
|
|
|
+ },
|
|
|
+
|
|
|
+ searchByRestAPI(keyword) {
|
|
|
+ let cityCode = uni.getStorageSync('cityCode') || '140100';
|
|
|
+ let postStr = {
|
|
|
+ "keyWord": keyword,
|
|
|
+ "queryType": 12,
|
|
|
+ "start": 0,
|
|
|
+ "count": 10,
|
|
|
+ "specify": "156" + cityCode
|
|
|
+ };
|
|
|
+
|
|
|
+ uni.request({
|
|
|
+ url: `https://api.tianditu.gov.cn/v2/search?postStr=${encodeURIComponent(JSON.stringify(postStr))}&type=query&tk=${this.tk}`,
|
|
|
+ success: (res) => {
|
|
|
+ if (res.statusCode === 200 && res.data) {
|
|
|
+ // 处理搜索结果:添加唯一标识,避免与原有已选重复
|
|
|
+ const searchResults = res.data.pois.map(item => ({
|
|
|
+ ...item,
|
|
|
+ location: item.lonlat,
|
|
|
+ check: false, // 新搜索结果默认未选中
|
|
|
+ detailedAddress: item.address,
|
|
|
+ inspectionStationName: item.name,
|
|
|
+ locality: item.district || '',
|
|
|
+ // 唯一标识:用经纬度拼接(避免重复添加同一检测站)
|
|
|
+ uniqueKey: `${item.lonlat}`
|
|
|
+ }));
|
|
|
+
|
|
|
+ // 合并:原有已选 + 新搜索结果(去重)
|
|
|
+ const allData = [...this.originalSelected];
|
|
|
+ searchResults.forEach(searchItem => {
|
|
|
+ // 去重:判断uniqueKey是否已存在
|
|
|
+ const isDuplicate = allData.some(
|
|
|
+ item => item.uniqueKey === searchItem.uniqueKey
|
|
|
+ );
|
|
|
+ if (!isDuplicate) {
|
|
|
+ allData.push(searchItem);
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ // 更新显示列表(保留原有已选)
|
|
|
+ this.dataTips = allData;
|
|
|
+
|
|
|
+ } else {
|
|
|
+ uni.showToast({
|
|
|
+ title: '搜索失败,请重试',
|
|
|
+ icon: 'none'
|
|
|
+ });
|
|
|
+ }
|
|
|
+ },
|
|
|
+ fail: (error) => {
|
|
|
+ console.error('搜索请求失败:', error);
|
|
|
+ uni.showToast({
|
|
|
+ title: '网络错误,请重试',
|
|
|
+ icon: 'none'
|
|
|
+ });
|
|
|
+ }
|
|
|
+ });
|
|
|
+ },
|
|
|
+
|
|
|
+ onSearchInput(e) {
|
|
|
+ this.searchKeyword = e.detail.value;
|
|
|
+ },
|
|
|
+
|
|
|
+ // 清除搜索结果:只清除新搜索的,保留原有已选
|
|
|
+ clearSearchResults() {
|
|
|
+ this.searchKeyword = '';
|
|
|
+ this.selectedResult = null;
|
|
|
+ // 恢复为原始已选数据(清除新搜索结果)
|
|
|
+ this.dataTips = [...this.originalSelected];
|
|
|
+ }
|
|
|
+ }
|
|
|
+ };
|
|
|
+</script>
|
|
|
+
|
|
|
+<!-- renderjs模块不变 -->
|
|
|
+<script module="renderjsMap" lang="renderjs">
|
|
|
+ let mapInstance = null;
|
|
|
+ let markerInstance = null;
|
|
|
+ let labelInstance = null;
|
|
|
+
|
|
|
+ export default {
|
|
|
+ data() {
|
|
|
+ return {
|
|
|
+ mapLoaded: false,
|
|
|
+ lastDataHash: ''
|
|
|
+ };
|
|
|
+ },
|
|
|
+ methods: {
|
|
|
+ updateMapWithMarker(mapData) {
|
|
|
+ console.log('【RenderJS】updateMapWithMarker 被调用', mapData);
|
|
|
+
|
|
|
+ if (!this.mapLoaded || !mapInstance) {
|
|
|
+ console.warn('地图未加载,延迟执行');
|
|
|
+ setTimeout(() => {
|
|
|
+ this.updateMapWithMarker(mapData);
|
|
|
+ }, 300);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ const dataHash = JSON.stringify(mapData);
|
|
|
+ if (dataHash === this.lastDataHash) {
|
|
|
+ console.log('数据未变化,跳过更新');
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ this.lastDataHash = dataHash;
|
|
|
+
|
|
|
+ const lng = Number(mapData.lng);
|
|
|
+ const lat = Number(mapData.lat);
|
|
|
+ const address = mapData.address || mapData.name || '未知位置';
|
|
|
+
|
|
|
+ if (isNaN(lng) || isNaN(lat)) {
|
|
|
+ console.error('坐标无效:', mapData);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ const targetLngLat = new window.T.LngLat(lng, lat);
|
|
|
+ mapInstance.panTo(targetLngLat);
|
|
|
+
|
|
|
+ if (mapData.fromSearch) {
|
|
|
+ mapInstance.setZoom(15);
|
|
|
+ }
|
|
|
+
|
|
|
+ this.createMarker(targetLngLat, address, mapData.name || '');
|
|
|
+ },
|
|
|
+ clearMarkers() {
|
|
|
+ if (markerInstance && mapInstance) {
|
|
|
+ mapInstance.removeOverLay(markerInstance);
|
|
|
+ markerInstance = null;
|
|
|
+ }
|
|
|
+ if (labelInstance && mapInstance) {
|
|
|
+ mapInstance.removeOverLay(labelInstance);
|
|
|
+ labelInstance = null;
|
|
|
+ }
|
|
|
+ },
|
|
|
+ createMarker(lngLat, address, name) {
|
|
|
+ try {
|
|
|
+ const T = window.T;
|
|
|
+ this.clearMarkers();
|
|
|
+
|
|
|
+ const iconUrl = this.generateMarkerIcon('#ff0000');
|
|
|
+ const icon = new T.Icon({
|
|
|
+ iconUrl: iconUrl,
|
|
|
+ iconSize: new T.Point(30, 30),
|
|
|
+ iconAnchor: new T.Point(15, 30)
|
|
|
+ });
|
|
|
+
|
|
|
+ markerInstance = new T.Marker(lngLat, {
|
|
|
+ icon: icon,
|
|
|
+ title: name || address
|
|
|
+ });
|
|
|
+
|
|
|
+ labelInstance = new T.Label({
|
|
|
+ text: address,
|
|
|
+ position: lngLat,
|
|
|
+ style: {
|
|
|
+ color: '#333',
|
|
|
+ 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)
|
|
|
+ });
|
|
|
+
|
|
|
+ mapInstance.addOverLay(markerInstance);
|
|
|
+ mapInstance.addOverLay(labelInstance);
|
|
|
+
|
|
|
+ markerInstance.addEventListener('click', () => {
|
|
|
+ this.sendToVue('showLocationInfo', {
|
|
|
+ lng: lngLat.getLng().toFixed(6),
|
|
|
+ lat: lngLat.getLat().toFixed(6),
|
|
|
+ address: address,
|
|
|
+ name: name
|
|
|
+ });
|
|
|
+ });
|
|
|
+
|
|
|
+ console.log('标记创建成功');
|
|
|
+ } catch (error) {
|
|
|
+ console.error('创建标记失败:', error);
|
|
|
+ }
|
|
|
+ },
|
|
|
+ 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();
|
|
|
+ },
|
|
|
+ async initMap() {
|
|
|
+ try {
|
|
|
+ console.log('【RenderJS】开始初始化地图');
|
|
|
+ await this.loadTiandituAPI();
|
|
|
+
|
|
|
+ const container = document.getElementById('mapContainer');
|
|
|
+ if (!container) throw new Error('地图容器不存在');
|
|
|
+
|
|
|
+ const T = window.T;
|
|
|
+ mapInstance = new T.Map(container);
|
|
|
+ const center = new T.LngLat(112.55, 37.87);
|
|
|
+ mapInstance.centerAndZoom(center, 12);
|
|
|
+
|
|
|
+ const vecLayer = new T.TileLayer(
|
|
|
+ `https://t{s}.tianditu.gov.cn/vec_c/wmts?tk=9bcf63358817bfb878e8236de8bf1423`, {
|
|
|
+ subdomains: ['0', '1', '2', '3', '4', '5', '6', '7']
|
|
|
+ }
|
|
|
+ );
|
|
|
+ const cvaLayer = new T.TileLayer(
|
|
|
+ `https://t{s}.tianditu.gov.cn/cva_c/wmts?tk=9bcf63358817bfb878e8236de8bf1423`, {
|
|
|
+ subdomains: ['0', '1', '2', '3', '4', '5', '6', '7']
|
|
|
+ }
|
|
|
+ );
|
|
|
+
|
|
|
+ mapInstance.addLayer(vecLayer);
|
|
|
+ mapInstance.addLayer(cvaLayer);
|
|
|
+ mapInstance.addControl(new T.Control.Zoom());
|
|
|
+ mapInstance.addEventListener('click', (e) => this.handleMapClick(e));
|
|
|
+
|
|
|
+ this.mapLoaded = true;
|
|
|
+ console.log('【RenderJS】地图初始化完成');
|
|
|
+ } catch (error) {
|
|
|
+ console.error('【RenderJS】地图初始化失败:', error);
|
|
|
+ }
|
|
|
+ },
|
|
|
+ loadTiandituAPI() {
|
|
|
+ return new Promise((resolve, reject) => {
|
|
|
+ if (window.T && window.T.Map) {
|
|
|
+ resolve();
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ const script = document.createElement('script');
|
|
|
+ script.src = 'https://api.tianditu.gov.cn/api?v=4.0&tk=9bcf63358817bfb878e8236de8bf1423';
|
|
|
+ script.onload = () => {
|
|
|
+ const checkInterval = setInterval(() => {
|
|
|
+ if (window.T && window.T.Map) {
|
|
|
+ clearInterval(checkInterval);
|
|
|
+ resolve();
|
|
|
+ }
|
|
|
+ }, 100);
|
|
|
+ setTimeout(() => {
|
|
|
+ clearInterval(checkInterval);
|
|
|
+ reject(new Error('T对象初始化超时'));
|
|
|
+ }, 5000);
|
|
|
+ };
|
|
|
+ script.onerror = reject;
|
|
|
+ document.head.appendChild(script);
|
|
|
+ });
|
|
|
+ },
|
|
|
+ handleMapClick(e) {
|
|
|
+ if (!this.mapLoaded) return;
|
|
|
+ const lng = e.lnglat.lng;
|
|
|
+ const lat = e.lnglat.lat;
|
|
|
+ const mapData = { lng, lat, address: '点击位置' };
|
|
|
+ this.updateMapWithMarker(mapData);
|
|
|
+ this.sendToVue('onMapClick', { lng, lat });
|
|
|
+ },
|
|
|
+ sendToVue(method, data) {
|
|
|
+ try {
|
|
|
+ if (this.$ownerInstance && this.$ownerInstance.callMethod) {
|
|
|
+ this.$ownerInstance.callMethod(method, data);
|
|
|
+ }
|
|
|
+ } catch (error) {
|
|
|
+ console.error('发送消息到Vue失败:', error);
|
|
|
+ }
|
|
|
+ },
|
|
|
+ cleanupMap() {
|
|
|
+ this.clearMarkers();
|
|
|
+ if (mapInstance) {
|
|
|
+ try { mapInstance.destroy(); } catch (e) {}
|
|
|
+ mapInstance = null;
|
|
|
+ }
|
|
|
+ this.mapLoaded = false;
|
|
|
+ }
|
|
|
+ },
|
|
|
+ mounted() {
|
|
|
+ console.log('【RenderJS】模块挂载');
|
|
|
+ setTimeout(() => this.initMap(), 500);
|
|
|
+ },
|
|
|
+ beforeDestroy() {
|
|
|
+ console.log('【RenderJS】模块销毁');
|
|
|
+ this.cleanupMap();
|
|
|
+ }
|
|
|
+ };
|
|
|
+</script>
|
|
|
+
|
|
|
+<style scoped lang="scss">
|
|
|
+ .top {
|
|
|
+ width: 100%;
|
|
|
+ height: var(--status-bar-height);
|
|
|
+ background: linear-gradient(87deg, #FFFFFF 0%, #A3CDFF 100%), linear-gradient(360deg, #FFFFFF 0%, rgba(255, 255, 255, 0) 100%);
|
|
|
+ }
|
|
|
+
|
|
|
+ .head {
|
|
|
+ width: 100%;
|
|
|
+ background: #ffffff;
|
|
|
+
|
|
|
+ .handle {
|
|
|
+ height: 112rpx;
|
|
|
+ display: flex;
|
|
|
+ justify-content: space-between;
|
|
|
+ align-items: center;
|
|
|
+ padding: 0px 24rpx;
|
|
|
+ position: relative;
|
|
|
+
|
|
|
+ .head_left {
|
|
|
+ font-size: 40rpx;
|
|
|
+ }
|
|
|
+
|
|
|
+ .center {
|
|
|
+ position: absolute;
|
|
|
+ left: 50%;
|
|
|
+ top: 50%;
|
|
|
+ transform: translate(-50%, -50%);
|
|
|
+ font-size: 30rpx;
|
|
|
+ font-weight: 600;
|
|
|
+ }
|
|
|
+
|
|
|
+ .submit-btn {
|
|
|
+ display: flex;
|
|
|
+ width: 104rpx;
|
|
|
+ height: 49rpx;
|
|
|
+ background: #2D88F4;
|
|
|
+ border-radius: 8rpx;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: center;
|
|
|
+ font-size: 28rpx;
|
|
|
+ color: #FFFFFF;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .container {
|
|
|
+ width: 100%;
|
|
|
+ height: 100vh;
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ position: relative;
|
|
|
+ }
|
|
|
+
|
|
|
+ .map-container {
|
|
|
+ width: 100%;
|
|
|
+ height: 300px;
|
|
|
+ background-color: #f8f8f8;
|
|
|
+ position: relative;
|
|
|
+ }
|
|
|
+
|
|
|
+ .search-panel {
|
|
|
+ margin-top: 20rpx;
|
|
|
+ padding: 20rpx;
|
|
|
+ background-color: #f8f9fa;
|
|
|
+ border-radius: 8rpx;
|
|
|
+ flex: 1;
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ }
|
|
|
+
|
|
|
+ .search-input-group {
|
|
|
+ display: flex;
|
|
|
+ gap: 10rpx;
|
|
|
+ margin-bottom: 20rpx;
|
|
|
+ }
|
|
|
+
|
|
|
+ .search-input {
|
|
|
+ flex: 1;
|
|
|
+ height: 70rpx;
|
|
|
+ padding: 0 20rpx;
|
|
|
+ border: 1px solid #ddd;
|
|
|
+ border-radius: 8rpx;
|
|
|
+ background-color: white;
|
|
|
+ font-size: 28rpx;
|
|
|
+ }
|
|
|
+
|
|
|
+ .search-btn {
|
|
|
+ height: 70rpx;
|
|
|
+ padding: 0 20rpx;
|
|
|
+ background-color: #1890ff;
|
|
|
+ color: white;
|
|
|
+ border-radius: 8rpx;
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: center;
|
|
|
+ font-size: 28rpx;
|
|
|
+ border: none;
|
|
|
+ }
|
|
|
+
|
|
|
+ .search-results {
|
|
|
+ border: 1px solid #e8e8e8;
|
|
|
+ border-radius: 8rpx;
|
|
|
+ flex: 1;
|
|
|
+ background-color: white;
|
|
|
+ }
|
|
|
+
|
|
|
+ .result-header {
|
|
|
+ display: flex;
|
|
|
+ justify-content: space-between;
|
|
|
+ align-items: center;
|
|
|
+ padding: 20rpx;
|
|
|
+ border-bottom: 1px solid #f0f0f0;
|
|
|
+ font-weight: bold;
|
|
|
+ font-size: 28rpx;
|
|
|
+ }
|
|
|
+
|
|
|
+ .clear-results {
|
|
|
+ color: #1890ff;
|
|
|
+ font-size: 24rpx;
|
|
|
+ }
|
|
|
+
|
|
|
+ .results-list {
|
|
|
+ max-height: 700rpx;
|
|
|
+ }
|
|
|
+
|
|
|
+ .result-item {
|
|
|
+ padding: 20rpx;
|
|
|
+ border-bottom: 1px solid #f5f5f5;
|
|
|
+ cursor: pointer;
|
|
|
+ display: flex;
|
|
|
+ justify-content: space-between;
|
|
|
+ align-items: center;
|
|
|
+
|
|
|
+ &.selected {
|
|
|
+ background-color: #e6f7ff;
|
|
|
+ border-left: 4rpx solid #1890ff;
|
|
|
+ }
|
|
|
+
|
|
|
+ .left {
|
|
|
+ flex: 1;
|
|
|
+ margin-right: 20rpx;
|
|
|
+ }
|
|
|
+
|
|
|
+ .selected-tag {
|
|
|
+ display: inline-flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: center;
|
|
|
+ width: 80rpx;
|
|
|
+ height: 43rpx;
|
|
|
+ background: rgba(253,73,18,0.05);
|
|
|
+ border-radius: 4rpx ;
|
|
|
+ font-size: 24rpx;
|
|
|
+ color: #FD4912;
|
|
|
+ margin-right: 12rpx;
|
|
|
+ }
|
|
|
+
|
|
|
+ .right {
|
|
|
+ image {
|
|
|
+ width: 40rpx;
|
|
|
+ height: 40rpx;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .result-name {
|
|
|
+ font-weight: bold;
|
|
|
+ margin-bottom: 5rpx;
|
|
|
+ color: #333;
|
|
|
+ font-size: 28rpx;
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ }
|
|
|
+
|
|
|
+ .result-address {
|
|
|
+ font-size: 24rpx;
|
|
|
+ color: #666;
|
|
|
+ }
|
|
|
+</style>
|