| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825 |
- <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>
|