Explorar o código

feat:修改首页地图为高德地图

wangsisi hai 4 días
pai
achega
7f3c37b5c6

BIN=BIN
src/assets/images/more/medal-popup.png


+ 284 - 0
src/views/home/homeMap.vue

@@ -0,0 +1,284 @@
+<template>
+    <div id="map-container"></div>
+</template>
+
+<script setup>
+import { ref, onMounted, onUnmounted } from "vue";
+import AMapLoader from "@amap/amap-jsapi-loader";
+import eventBus from "@/api/eventBus";
+import { convertPointToArray } from "@/utils/index";
+import { deepClone } from "@/common/commonFun";
+import {base_img_url,base_img_url3} from "@/api/config";
+
+const map = ref(null);
+const mouseTool = ref(null);
+const selectedArea = ref(null);
+const selectedPoints = ref([]);
+const allPoints = ref([]);
+const defalutAllPoints = ref([])
+const pointList = ref([])
+
+const mapEventType = ref('rectangle')
+
+// 自定义圆点样式
+const createMarkerContent = (color = 'rgba(0, 0, 0, 0.4)') => {
+    return `
+        <div style="
+            width: 15px;
+            height: 15px;
+            background-color: ${color};
+            border: 1px solid #fff;
+            border-radius: 50%;
+        ">
+        </div>
+    `;
+}
+
+//高亮样式
+const createMarkerImg = () => {
+    return `
+        <img style="width: 38px;height: 38px;" src="${require('@/assets/images/map/status/active-icon.png')}">
+    `;
+}
+// 初始化地图
+const initMap = async () => {
+    try {
+        const AMap = await AMapLoader.load({
+            key: "41769169f0f157e13a197e7eb0dd7b5b", // 替换为你的高德地图 Key
+            version: "2.0", // SDK 版本
+            plugins: ["AMap.MouseTool","AMap.MarkerCluster"], // 加载 MouseTool 插件
+        }).then(async (AMap) => {
+            // 创建地图实例
+            map.value = new AMap.Map("map-container", {
+                zoom: 18, // 初始缩放级别
+                center: [113.6149629 ,23.58594117],
+                layers: [
+                    // 天地图卫星
+                    new AMap.TileLayer({
+                        tileSize: 256, // 瓦片大小
+                        getTileUrl: function(x, y, z) {
+                            // 天地图影像 WMTS 地址
+                            const s = Math.floor(Math.random() * 8); // 随机选择服务器节点
+                            return `https://t${s}.tianditu.gov.cn/img_w/wmts?SERVICE=WMTS&REQUEST=GetTile&VERSION=1.0.0&LAYER=img&STYLE=default&TILEMATRIXSET=w&FORMAT=tiles&TILEMATRIX=${z}&TILEROW=${y}&TILECOL=${x}&tk=e95115c454a663cd052d96019fd83840`;
+                        },
+                    }),
+                    //天地图路网
+                    new AMap.TileLayer({
+                        tileSize: 256, // 瓦片大小
+                        getTileUrl: function(x, y, z) {
+                            // 天地图影像 WMTS 地址
+                            const s = Math.floor(Math.random() * 8); // 随机选择服务器节点
+                            return `https://t${s}.tianditu.gov.cn/cva_w/wmts?SERVICE=WMTS&REQUEST=GetTile&VERSION=1.0.0&LAYER=cva&STYLE=default&TILEMATRIXSET=w&FORMAT=tiles&TILEMATRIX=${z}&TILEROW=${y}&TILECOL=${x}&tk=e95115c454a663cd052d96019fd83840`;
+                        },
+                    }),
+                    //正射影像
+                    new AMap.TileLayer({
+                        tileUrl: base_img_url + 'map/lby/[z]/[x]/[y].png',
+                        zIndex:2
+                    }),
+                    new AMap.TileLayer({
+                        tileUrl: base_img_url3 + 'map/lby/[z]/[x]/[y].png',
+                        zIndex:2
+                    })
+                ],
+            });
+
+            // // 初始化点聚合
+            // const cluster = new AMap.MarkerCluster(map, markers, {
+            //     gridSize: 60,          // 聚合网格像素大小
+            //     maxZoom: 15,           // 最大聚合级别
+            //     renderClusterMarker: customizeCluster // 自定义聚合样式
+            // });
+
+            // 初始化 MouseTool
+            mouseTool.value = new AMap.MouseTool(map.value);
+            
+            // startRectangleSelection()
+
+            // 监听框选完成事件
+            mouseTool.value.on("draw", (event) => {
+                if(mapEventType.value==='click') return
+                const { obj} = event;
+                if (obj instanceof AMap.Rectangle) {
+                    selectedArea.value = obj;
+                    map.value.remove(selectedArea.value);
+                    checkPointsInArea(); // 检查框选区域内的点位
+                }
+            });
+
+        });
+    } catch (error) {
+        console.error("地图加载失败:", error);
+    }
+};
+
+//重置数据
+const resetData = () =>{
+    allPoints.value = defalutAllPoints.value
+    allPoints.value.forEach(item =>{
+        item.icon.setContent(createMarkerContent(item.color))
+        item.icon.setOffset(new window.AMap.Pixel(-7, -7))
+        item.type = 'defalutIcon'
+    })
+}
+
+//初始化数据
+const initData = (data) =>{
+    allPoints.value = data
+    defalutAllPoints.value = []
+    totalList.value.forEach(item => map.value.remove(item.icon))
+    totalList.value = []
+    // allPoints.value.forEach(item =>{
+    //     item.icon = new window.AMap.Marker({
+    //         position: item.lngLat,
+    //         map: map.value,
+    //         content: createMarkerContent(item.color),
+    //         offset: new window.AMap.Pixel(-7, -7),
+    //         extData: { id: item.id, priority: item.status > item.noImg > item.wys } // 标记优先级
+    //     });
+    //     item.type = 'defalutIcon'
+
+    //     // 绑定点击事件
+    //     item.icon.on('click', () => {
+    //         mapEventType.value = 'click'
+    //         if(item.type === 'defalutIcon'){
+    //             item.icon.setContent(createMarkerImg()); 
+    //             item.icon.setOffset(new window.AMap.Pixel(-18, -18))
+    //             item.type = 'activeIcon'
+    //             const arr = pointList.value.filter(ele => ele.id === item.id)
+    //             if(arr.length===0){
+    //                 pointList.value.push(item)
+    //             }
+    //         }else{
+    //             item.icon.setContent(createMarkerContent(item.color)); 
+    //             item.icon.setOffset(new window.AMap.Pixel(-7, -7))
+    //             item.type = 'defalutIcon'
+    //         }
+
+    //         setTimeout(()=>{
+    //             mapEventType.value = 'draw'
+    //         },100)
+    //     });
+    //     defalutAllPoints.value.push(item)
+    // })
+    // 初始化点聚合
+    console.log('data',data);
+    const cluster = new window.AMap.MarkerCluster(map.value, data, {
+        gridSize: 60,          // 聚合网格像素大小
+        // maxZoom: 15,           // 最大聚合级别
+        // renderClusterMarker: customizeCluster // 自定义聚合样式
+    });
+    setTimeout(()=>{
+        map.value.setFitView(null, false, [0, 0, 0, 0]);
+    },100)
+}
+
+// 开始框选
+const startRectangleSelection = () => {
+    if (mouseTool.value) {
+        mouseTool.value.rectangle({
+            strokeColor: "#ffffff", // 框选区域边框颜色
+            strokeOpacity: 1, // 边框透明度
+            strokeWeight: 2, // 边框宽度
+            fillColor: "#000000", // 填充颜色
+            fillOpacity: 0.5, // 填充透明度
+        }); // 启用矩形框选工具
+        map.value.setDefaultCursor("crosshair");
+    }
+};
+
+ // 停止绘制矩形
+const stopDrawRectangle = () => {
+    mouseTool.value.close(); // 关闭 mouseTool
+    map.value.setDefaultCursor("default");
+};
+
+// 清除框选
+const clearSelection = () => {
+    if (selectedArea.value) {
+        map.value.remove(selectedArea.value); // 移除框选区域
+        selectedArea.value = null;
+    }
+    resetData()                                                                                                                                                                                                                                                                                                                                      
+    selectedPoints.value = []; // 清空选中的点位
+};
+
+// 检查框选区域内的点位
+const checkPointsInArea = () => {
+    if (!selectedArea.value) return;
+    const bounds = selectedArea.value.getBounds(); // 获取框选区域的边界
+    selectedPoints.value = allPoints.value.filter((point) => {
+        return bounds.contains(point.lngLat); // 判断点位是否在框选区域内
+    });
+    
+    selectedPoints.value.forEach(item =>{
+        if(item.type==='defalutIcon'){
+            item.icon.setContent(createMarkerImg())
+            item.icon.setOffset(new window.AMap.Pixel(-18, -18))
+            item.type = 'activeIcon'
+        }else{
+            item.icon.setContent(createMarkerContent(item.color))
+            item.icon.setOffset(new window.AMap.Pixel(-7, -7))
+            item.type = 'defalutIcon'
+        }
+    })
+};
+
+const speciesArr = ref([])
+const getList = () =>{
+    const params = {
+        farmId:sessionStorage.getItem('farmId'),
+        regionId:sessionStorage.getItem('regionId')
+    }
+    VE_API.variety.pointList(params).then(res =>{
+        allPoints.value = res.data.map(item =>{
+            const bgColor = speciesArr.value.filter(ele =>ele.speciesItemId === item.speciesItemId)
+            return{
+                ...item,
+                color:bgColor[0].color,
+                lngLat:convertPointToArray(item.point)
+            }
+        })
+        initData()
+    })
+}
+
+const totalList = ref([])
+
+function getSelectPoint(){
+    totalList.value = selectedPoints.value.concat(pointList.value)
+    const arr = totalList.value.filter(item =>item.type==='activeIcon')
+    return arr
+}
+
+defineExpose({getSelectPoint,initData})
+
+function getSpeciesList(arr){
+    speciesArr.value = arr
+    getList()
+}
+
+// 组件挂载时初始化地图
+onMounted(() => {
+    initMap()
+    eventBus.on('handle:clear',clearSelection)
+    eventBus.on('species:list',getSpeciesList)
+    eventBus.on('handle:start',startRectangleSelection)
+    eventBus.on('handle:end',stopDrawRectangle)
+});
+
+onUnmounted(()=>{
+    map.value.destroy()// 销毁地图实例以释放资源
+    eventBus.off('handle:clear',clearSelection)
+    eventBus.off('species:list',getSpeciesList)
+    eventBus.off('handle:start',startRectangleSelection)
+    eventBus.off('handle:end',stopDrawRectangle)
+})
+</script>
+
+<style scoped>
+#map-container {
+    width: 100%;
+    height: 100%;
+}
+</style>

+ 21 - 32
src/views/home/index.vue

@@ -4,12 +4,7 @@
         <div class="content">
             <navigation @handleTab="handleTab"></navigation>
             <div class="left yes-events">
-                <!-- <tool-list direction="left" :list="leftToolList" @handleActive="handleActiveLeft"></tool-list> -->
                 <component :is="components[currentComponent]" />
-                <!-- 箭头 -->
-                <!-- <div class="arrow" @click="handleShrink('left')">
-                    <el-icon class="icon" color="#141414"><DArrowLeft /></el-icon>
-                </div> -->
             </div>
             <!-- <div class="home-bottom">
                 <div class="garden-file yes-events" :class="{ isShrink: isShrink }">
@@ -38,11 +33,9 @@
                         </el-tabs>
                     </chart-box>
                 </div>
-                <!-- <tool-list direction="right" :list="rightToolList" @handleActive="handleActiveRight"></tool-list> -->
             </div>
 
             <!-- 图例 -->
-            <!-- <img class="legend yes-events" src="@/assets/images/home/legend-img.png" alt="" /> -->
             <div v-if="legendArr && legendArr.length" class="map-bg map-legend yes-events">
                 <div class="item" v-for="(legend, legendI) in legendArr" :key="legendI">
                     <span class="legend-block" :style="{background: legend.color}"></span>
@@ -69,13 +62,12 @@
             </div>
         </div>
     </div>
-    <div ref="mapRef" class="bottom-map"></div>
+    <custom-map class="bottom-map" ref="mapRef"></custom-map>
 
     <!-- 图片弹窗 -->
     <PicturePreview :imageUrl="urls" :curIndex="urlsIndex"></PicturePreview>
     <album-carousel></album-carousel>
     <PdfDialog title="果园报告"></PdfDialog>
-
 </template>
 
 <script setup>
@@ -86,51 +78,53 @@ import PicturePreview from "@/components/PicturePreview.vue";
 import fnHeader from "@/components/fnHeader.vue";
 import navigation from "@/components/navigation.vue";
 import chartBox from "@/components/chartBox.vue";
-import toolList from "@/components/toolList.vue";
 import fileBar from "@/components/fileBar.vue";
-import HomeMap from "./map/homeMap";
 import homePage from "./components/homePage.vue";
 import adoptList from "./components/adoptList.vue";
 import clientList from "./components/clientList.vue";
+import customMap from "./homeMap.vue";
 import applyList from "./components/applyList.vue";
 import addressList from "./components/addressList.vue";
+import { convertPointToArray } from "@/utils/index";
 import { useRouter } from "vue-router";
-import SamplePointLayer from "./map/samplePointLayer";
 import { useStore } from "vuex";
-import BlueRegionLayer from "./map/blueRegionLayer";
 import eventBus from "@/api/eventBus";
 import AlbumCarousel from "./album_compoents/albumCarousel.vue";
-// import compareDialog from "./album_compoents/compareDialog.vue";
-// import album from "./album/index.vue";
 import PdfDialog from "../../components/PdfDialog";
 let store = useStore();
 const components = {
     homePage,
 };
 
-let homeMap = new HomeMap();
-let samplePointLayer = null;
-let blueRegionLayer = null;
 const router = useRouter();
-const mapRef = ref();
+const mapRef = ref(null);
 
 const activeName = ref('认养列表')
 onMounted(() => {
-    homeMap.initMap("POINT(113.61448114737868 23.585550924763083)", mapRef.value);
-    // homeMap.initMap(store.getters.userinfo.location, mapRef.value);
-    samplePointLayer = new SamplePointLayer(homeMap.kmap);
-    blueRegionLayer = new BlueRegionLayer(homeMap.kmap);
-
-    // getFarmLog()
-
     //区域切换监听事件
     eventBus.on("area:id", areaId);
+
+    getPointList()
 });
 
 onUnmounted(() => {
     eventBus.off("area:id", areaId);
 });
 
+const pointList = ref([])
+const getPointList = () =>{
+    VE_API.variety.pointList({ farmId:766, selectAll:1 }).then(({ data }) => {
+        const arr = data.map(item =>{
+            return{
+                ...item,
+                lngLat:convertPointToArray(item.point)
+            }
+        })
+        if(mapRef.value){
+            mapRef.value.initData(arr)
+        }
+    })
+}
 
 const blueList = ref([]);
 const getBlueRegionList = (callback) => {
@@ -162,7 +156,7 @@ const getBlueRegionList = (callback) => {
                 color,
             };
         });
-        blueRegionLayer.initData(blueList.value);
+        // blueRegionLayer.initData(blueList.value);
         callback && callback();
     });
 };
@@ -182,7 +176,6 @@ function toggleLegend({colorObj}) {
 const handleTab = async ({ name, id, isUpdate, params, legend, colorObj }) => {
     eventBus.emit("changePointType", {legend, colorObj})
     legendArr.value = colorObj?.list
-    console.log('name, id, isUpdate, params', name, id, isUpdate, params, legend, colorObj);
     tabName.value = name;
     tabId.value = id;
     if (id === 0) {
@@ -194,7 +187,6 @@ const handleTab = async ({ name, id, isUpdate, params, legend, colorObj }) => {
 function areaId({ areaId, farmId }) {
     organId.value = farmId;
     regionId.value = areaId;
-    samplePointLayer.initData(farmId, areaId);
     if (tabId.value === 0) {
         getBlueRegionList();
     }
@@ -203,10 +195,7 @@ function areaId({ areaId, farmId }) {
 const urls = ref([]);
 const urlsIndex = ref(0);
 
-
 const currentComponent = ref("homePage");
-
-
 </script>
 
 <style lang="scss" scoped>

+ 102 - 33
src/views/more/medalPage.vue

@@ -9,34 +9,45 @@
                 </div>
             </div>
             <div class="medal-list">
-                <div class="medal-item" v-for="(item,index) in 20" :key="index">
-                    <div class="btn" :class="{active:index===0}">{{index===0?'已置顶':'设为置顶'}}</div>
+                <div class="medal-item" v-for="(item, index) in 5" :key="index" @click="handleMedal">
+                    <div class="btn" :class="{ active: index === 0 }">{{ index === 0 ? "已置顶" : "设为置顶" }}</div>
                     <img class="medal" src="@/assets/images/more/medal.png" alt="" />
                     <div class="icon-name">水源洁净</div>
                 </div>
             </div>
         </div>
     </div>
+    <Popup v-model:show="showPopup" class="medal-popup" overlay-class="medal-overlay" closeable>
+        <div class="btn" :class="{ active: false }">设为置顶</div>
+        <img class="medal" src="@/assets/images/more/medal-popup.png" alt="" />
+        <div class="icon-name">水源洁净</div>
+        <div class="desc">
+            评分规则评分规则评分规则评分规则评分规则评分规则评分规则评分规则评分规则评分规则评分规则评分规则
+        </div>
+    </Popup>
 </template>
 
 <script setup>
 import fnHeader from "@/components/fnHeader.vue";
-import {ref} from 'vue'
-import { Popup } from 'vant';
+import { ref } from "vue";
+import { Popup } from "vant";
 import { useRouter } from "vue-router";
 const router = useRouter();
 
 const list = ref([
     {
-        isDefalut:1
-    }
-])
+        isDefalut: 1,
+    },
+]);
 
-const showPopup = ref(false)
+const showPopup = ref(true);
+const handleMedal = () => {
+    showPopup.value = true;
+};
 
-const goBack = () =>{
-    router.go('-1')
-}
+const goBack = () => {
+    router.go("-1");
+};
 </script>
 
 <style lang="scss" scoped>
@@ -52,7 +63,7 @@ const goBack = () =>{
         height: calc(100% - 74px);
         padding: 16px 20px 0 20px;
         box-sizing: border-box;
-        .tool{
+        .tool {
             padding-bottom: 24px;
             border-bottom: 1px solid #444444;
             margin-bottom: 24px;
@@ -74,14 +85,14 @@ const goBack = () =>{
                 }
             }
         }
-        .medal-list{
+        .medal-list {
             width: 100%;
             height: calc(100% - 65px - 24px);
             overflow-y: auto;
             display: flex;
             align-content: flex-start;
             flex-wrap: wrap;
-            .medal-item{
+            .medal-item {
                 width: 13.7%;
                 height: 220px;
                 border-radius: 8px;
@@ -92,14 +103,15 @@ const goBack = () =>{
                 align-items: center;
                 flex-direction: column;
                 justify-content: center;
+                cursor: pointer;
                 &.medal-item:nth-child(7n) {
                     margin-right: 0;
                 }
-                .icon-name{
+                .icon-name {
                     margin-top: 18px;
                     position: relative;
-                    &::before{
-                        content: '';
+                    &::before {
+                        content: "";
                         position: absolute;
                         top: -16px;
                         width: 60px;
@@ -108,25 +120,82 @@ const goBack = () =>{
                         background: rgba(204, 204, 204, 0.1);
                     }
                 }
-                .btn{
-                    position: absolute;
-                    cursor: pointer;
-                    right: 8px;
-                    top: 8px;
-                    background: rgba(0, 0, 0, 0.6);
-                    border-radius: 4px;
-                    padding: 4px 8px;
-                    color: #FFD489;
-                    &.active{
-                        background:#FFD489;
-                        color: #000;
-                    }
-                }
+                // .btn {
+                //     position: absolute;
+                //     cursor: pointer;
+                //     right: 8px;
+                //     top: 8px;
+                //     background: rgba(0, 0, 0, 0.6);
+                //     border-radius: 4px;
+                //     padding: 4px 8px;
+                //     color: #ffd489;
+                //     &.active {
+                //         background: #ffd489;
+                //         color: #000;
+                //     }
+                // }
             }
         }
     }
 }
+.btn {
+    position: absolute;
+    cursor: pointer;
+    right: 8px;
+    top: 8px;
+    background: rgba(0, 0, 0, 0.6);
+    border-radius: 4px;
+    padding: 4px 8px;
+    color: #ffd489;
+    &.active {
+        background: #ffd489;
+        color: #000;
+    }
+}
+// .medal-overlay {
+//     background: rgba(0, 0, 0, 0.8);
+// }
 .medal-popup {
-    background: transparent;
+    background: rgba(255, 255, 255, 0.2);
+    padding: 70px 24px 24px 24px;
+    border-radius: 5px;
+    display: flex;
+    align-items: center;
+    flex-direction: column;
+    .btn {
+        position: absolute;
+        left: 24px;
+        top: 24px;
+        right: auto;
+        font-size: 16px;
+        padding: 8px 16px;
+    }
+    .medal {
+        width: 180px;
+        height: 180px;
+    }
+    .icon-name {
+        margin-top: 40px;
+        font-size: 28px;
+        color: #fff;
+        position: relative;
+        &::before {
+            content: "";
+            position: absolute;
+            top: -28px;
+            left: -18px;
+            width: 155px;
+            height: 14px;
+            border-radius: 50%;
+            background: rgba(204, 204, 204, 0.1);
+        }
+    }
+    .desc {
+        margin-top: 34px;
+        width: 500px;
+        font-size: 19px;
+        color: rgba(255, 255, 255, 0.6);
+        text-align: center;
+    }
 }
-</style>
+</style>