Ver código fonte

修改标题

shuhao 2 semanas atrás
pai
commit
fbfce24d81

+ 20 - 0
src/api/modules/info.js

@@ -0,0 +1,20 @@
+const config = require("../config")
+
+module.exports = {
+    contactsList: {
+        url: config.base_url + "farm_contacts/list",
+        type: "get",
+    },
+    buyList: {
+        url: config.base_url + "z_farm_buy/list",
+        type: "get",
+    },
+    videoList: {
+        url: config.base_url + "z_farm_fine_view/list",
+        type: "get",
+    },
+    saveCustom: {
+        url: config.base_url + "z_farm_buy/save ",
+        type: "post",
+    }
+}

BIN
src/assets/images/map/logo.jpg


BIN
src/assets/images/map/point-bg-active.png


BIN
src/assets/images/map/point-bg.png


+ 13 - 3
src/views/home/homeMap.vue

@@ -13,9 +13,10 @@ import { platformModifierKeyOnly } from "ol/events/condition";
 import { newPoint } from "@/utils/map.js";
 import { Circle, Fill, Stroke, Style, Text } from "ol/style.js";
 import { Cluster, Vector as VectorSource } from "ol/source.js";
-import { boundingExtent } from "ol/extent.js";
+import { boundingExtent, getCenter } from "ol/extent.js";
 import RegionLayer from "./map/regionLayer";
 import eventBus from "@/api/eventBus";
+import MockFarmLayer from "./map/mockFarmLayer";
 
 import { onMounted, ref } from "vue";
 import { useStore } from "vuex";
@@ -34,6 +35,7 @@ const ROLE = store.state.home.userRole;
 
 let kmap = null;
 const areaRef = ref(null);
+let mockFarmLayer = null;
 let dragBox;
 
 const currentTree = ref(null)
@@ -89,10 +91,17 @@ const stopBoxSelect = () => {
 
 let treeClusterLayer;
 onMounted(() => {
-    let level = 16;
+    let level = 14;
     let coordinate = util.wktCastGeom(store.getters.userinfo.location).getFirstCoordinate();
     kmap = new KMap.Map(areaRef.value, level, coordinate[0], coordinate[1], null, 1, 22, "img", true, true);
     regionLayer = new RegionLayer(kmap);
+    mockFarmLayer = new MockFarmLayer(kmap)
+    VE_API.info.contactsList().then(res => {
+      if(res.code ===0){
+        mockFarmLayer.setData(res.data)
+      }
+    })
+
 });
 let styleCache = {};
 let listenKey;
@@ -311,7 +320,8 @@ function clearCluster() {
 
 function zoomToFeatures(features) {
     const extent = boundingExtent(features.map((r) => r.getGeometry().getCoordinates()));
-    kmap.getView().fit(extent, { duration: 500, padding: [200, 250, 120, 250] });
+    const center = getCenter(extent);
+    kmap.setCenter2(center);
 }
 
 function resetCurrentTree() {

+ 414 - 0
src/views/home/map/mockFarmLayer.js

@@ -0,0 +1,414 @@
+import Style from "ol/style/Style";
+import Photo from "ol-ext/style/Photo";
+import { newPolymerFeature, newPoint } from "@/common/util";
+import Icon from "ol/style/Icon";
+import { Cluster, Vector as VectorSource } from "ol/source.js";
+import { Vector } from "ol/layer.js";
+import * as KMap from '@/utils/ol-map/KMap';
+import { Fill, Text, Circle, Stroke } from "ol/style.js";
+import { boundingExtent } from 'ol/extent.js';
+import { toLonLat } from 'ol/proj';
+import CircleStyle from 'ol/style/Circle.js';
+import {base_img_url2} from "@/api/config.js"
+import eventBus from "@/api/eventBus";
+import router from '@/router'
+/**
+ * 行政区县级天气点位数据
+ */
+class MockFarmLayer {
+    constructor(kmap) {
+        let that = this
+        this.nameStyleCache = {}
+        this.cloudFilenameCache = {}
+        this.statusTitleStyleCache = {}
+        this.textBgStyleCache = {}
+        this.bgStyleCache = {}
+        this.typeInfoStyleCache = {}
+        this.pointStyle = new Style({
+            renderer: function (coordinates, state) {
+                let ctx = state.context;
+                ctx.strokeStyle = 'black'; // 边框颜色
+                ctx.lineWidth = 2; // 边框宽度
+                ctx.fillStyle = 'rgba(29,28,28,0.8)'; // 填充颜色
+                ctx.beginPath();
+                ctx.ellipse(coordinates[0], coordinates[1], 7 * state.pixelRatio, 2 * state.pixelRatio, 0, 0, 2 * Math.PI);// 绘制椭圆
+                ctx.fill();// 填充椭圆
+                ctx.stroke();// 绘制椭圆边框
+            },
+            zIndex:-1
+        });
+        that.initLayer(kmap)
+
+        // 点击点位
+        this.currentPoint = null
+        // eventBus.on("MockFarmLayer:click", function ({event, feature}) {
+        //     if (that.currentPoint && that.currentPoint.get("id") != "10117") {
+        //         that.currentPoint.set("selected", false)
+        //     }
+        //     // that.currentPoint = feature
+        //     const f = that.selectFeature(feature)
+        //     that.currentPoint = f
+        //     f.set("selected", true)
+        // })
+
+        // eventBus.on("MockFarmLayer:resetPoint", function () {
+        //     if (that.currentPoint && that.currentPoint.get("id") != "10117") {
+        //         that.currentPoint.set("selected", false)
+        //     }
+        // })
+    }
+
+
+    /*************************************************************************************************
+     图层初始化的函数
+     *************************************************************************************************/
+    initLayer(kmap){
+        let that = this;
+        this.vectorStyle = new KMap.VectorStyle()
+        this.clusterSource = new VectorSource({})
+        this.clusterLayer = new Vector({
+            source: new Cluster({
+                distance: 80,
+                source: this.clusterSource,
+            }),
+            name: "MockFarmLayer",
+            minZoom: 9,
+            maxZoom: 15,
+            zIndex: 1001,
+            style: function (feature) {
+                let f = that.selectFeature(feature)
+                const isCurrent = f.get("selected");
+                const isHigh = f.get("isHigh") || f.get("id") == 10117 || f.get("isDemo");
+                let imgType = f.get("pz") ? f.get("pz").slice(0, 2) : "荔枝";
+                console.log('imgType', imgType);
+                let img = f.get("id")==10117 ? require("@/assets/images/map/logo.jpg") : `https://birdseye-img.sysuimars.com/temp/pz/${imgType}.png`;
+                // let name = f.get("farmName");
+                let name = f.get("yield");
+                let id = f.get("mapId");
+                let typeImg = require("@/assets/images/foster-home/tree-item.png");
+                // let styles = [that.cloudStyle(img, isCurrent), that.pointBgStyle(name, isCurrent), that.nameStyle(id, name, isCurrent)];
+                let styles = [that.cloudStyle(img, isCurrent, isHigh), that.pointBgStyle(name, isCurrent, isHigh)];
+                that.setProperties(feature,f)
+                // let text = f.get("pz");
+                let text = f.get("pz") ? that.firstVariety(f.get("pz")) : "荔枝";
+                let fontColor = (isCurrent || isHigh) ? "#fff" : "#000"
+                const yieldVal = f.get("yield") ? (f.get("yield")+"吨") : "--"
+                const price = "7.5"
+                // if(name==='HS.89215'){
+                //     name = ''
+                // }
+
+                // const startColor = isCurrent ? "#140D00" : "#00000099"
+                // const endColor = isCurrent ? "#7A4E00" : "#00000099"
+                const startColor = (isCurrent || isHigh) ? "#140D00" : "#FFD489"
+                const endColor = (isCurrent || isHigh) ? "#7A4E00" : "#FFD489"
+
+                // styles.push(that.typeInfoStyle(typeImg, "", "15", isCurrent));
+                const textOffsetY = (isCurrent || isHigh) ? -120 : -100
+                // styles.push(that.statusTitleStyle(text, -16, textOffsetY, fontColor,"#ffffff00", 14));
+                // styles.push(that.statusTitleStyle(text, -16, textOffsetY, fontColor,"#ffffff00", 14));
+                // styles.push(that.statusTitleStyle(price, 28, textOffsetY, "#FFD489","#ffffff00", 18));
+                // styles.push(that.statusTitleStyle("元/斤", 56, textOffsetY, "#FFFFFF66","#ffffff00", 10));
+
+                styles.push(that.textBgStyle(startColor, endColor, yieldVal+text+"123", isCurrent, isHigh));
+                styles.push(that.statusTitleStyle(text + "   " + yieldVal, 0, textOffsetY, fontColor,"#ffffff00", 14));
+                //  const zoom = kmap.getView().getZoom();
+                //  if (zoom > 9) {
+                //   }
+                return styles;
+            }
+        });
+        kmap.addLayer(this.clusterLayer);
+
+        // kmap.getView().on('change:resolution', function() {
+        //     const zoom = kmap.getView().getZoom();
+        //     const clusterSource = that.clusterLayer.getSource();
+        //     console.log('zoom', zoom);
+        //     if (zoom < 9) {
+        //         clusterSource.setDistance(10); // 缩放级别小于7时,聚合距离为10
+        //     } else {
+        //         clusterSource.setDistance(80); // 缩放级别大于等于7时,聚合距离为80
+        //     }
+        // }.bind(this));
+
+    }
+
+    firstVariety(pz) {
+      const match = pz.match(/[^、,。\s]+/);
+      return match ? match[0] : '';
+    }
+    /*************************************************************************************************
+     加载数据相关的函数
+     *************************************************************************************************/
+    setData(data){
+        this.clusterSource.clear()
+        let features = []
+        for(let item of data){
+            // if(item.name==='HS.89215'){
+            //     item.name = ''
+            //     item.wkt = 'POINT(113.5081595 23.5320866)'
+            //     console.log('GZ.88209',item);
+            // }
+            if (item.geom) {
+                item.wkt = item.geom
+                try {
+                    features.push(newPoint(item,"wkt","mock_farm_data"))
+                } catch (err) {
+                    console.log('err', err);
+                }
+            }
+        }
+        this.clusterSource.addFeatures(features)
+    }
+    /*************************************************************************************************
+        样式相关的函数
+     *************************************************************************************************/
+    /**
+     *
+     * @param startColor '#ffc91a'
+     * @param endColor '#d2a106'
+     * @returns {Style}
+     */
+    textBgStyle(startColor, endColor, text = "", isCurrent, isHigh) {
+        let key = startColor + endColor + text + isCurrent + isHigh; // 缓存键加入文字内容
+        let style = this.textBgStyleCache[key];
+        if (!style) {
+            style = new Style({
+                renderer: function (coordinates, state) {
+                    let ctx = state.context;
+
+                    // 1. 先测量文字宽度
+                    ctx.font = "14px Arial"; // 设置字体样式(需与最终显示文字的样式一致)
+                    const textWidth = ctx.measureText(text).width;
+
+                    // 2. 动态计算矩形宽度(基础宽度 + 文字宽度)
+                    const padding = 16 * state.pixelRatio; // 左右内边距
+                    const dynamicWidth = textWidth + padding * 2 + 26; // 总宽度 = 文字宽度 + 内边距
+
+                    // 3. 矩形参数(高度和圆角不变)
+                    const x = coordinates[0];
+                    const y = (isCurrent||isHigh) ? (coordinates[1] - 120 * state.pixelRatio) : (coordinates[1] - 100 * state.pixelRatio);
+                    const height = 40 * state.pixelRatio;
+                    const cornerRadius = 20 * state.pixelRatio;
+
+                    // 4. 创建渐变(根据动态宽度调整)
+                    const gradient = ctx.createLinearGradient(
+                        x - dynamicWidth / 2, y,
+                        x + dynamicWidth / 2, y
+                    );
+                    gradient.addColorStop(0, startColor);
+                    gradient.addColorStop(1, endColor);
+
+                    // 5. 绘制圆角矩形(宽度用 dynamicWidth)
+                    ctx.beginPath();
+                    ctx.moveTo(x - dynamicWidth / 2 + cornerRadius, y - height / 2);
+                    ctx.lineTo(x + dynamicWidth / 2 - cornerRadius, y - height / 2);
+                    ctx.arc(x + dynamicWidth / 2 - cornerRadius, y - height / 2 + cornerRadius,
+                        cornerRadius, -Math.PI / 2, 0);
+                    ctx.lineTo(x + dynamicWidth / 2, y + height / 2 - cornerRadius);
+                    ctx.arc(x + dynamicWidth / 2 - cornerRadius, y + height / 2 - cornerRadius,
+                        cornerRadius, 0, Math.PI / 2);
+                    ctx.lineTo(x - dynamicWidth / 2 + cornerRadius, y + height / 2);
+                    ctx.arc(x - dynamicWidth / 2 + cornerRadius, y + height / 2 - cornerRadius,
+                        cornerRadius, Math.PI / 2, Math.PI);
+                    ctx.lineTo(x - dynamicWidth / 2, y - height / 2 + cornerRadius);
+                    ctx.arc(x - dynamicWidth / 2 + cornerRadius, y - height / 2 + cornerRadius,
+                        cornerRadius, Math.PI, -Math.PI / 2);
+                    ctx.closePath();
+                    ctx.fillStyle = gradient;
+                    ctx.fill();
+                },
+                zIndex: 2
+            });
+            this.textBgStyleCache[key] = style;
+        }
+        return style;
+    }
+
+
+    statusTitleStyle(statusName, offsetX, offsetY,color,strokeColor, fontSize){
+        const key = statusName + "-" + offsetY
+        let style = this.statusTitleStyleCache[key]
+        if (!style) {
+            style = new Style({
+                text: new Text({
+                    text: statusName,
+                    offsetX: offsetX,
+                    offsetY: offsetY,
+                    font: `bold ${fontSize}px sans-serif`,
+                    fill: new Fill({ color }), // 字体颜色
+                    stroke: new Stroke({ color: strokeColor }), // 字体颜色
+                }),
+                zIndex:3
+            });
+            this.statusTitleStyleCache[key] = style
+        }
+        return style
+    }
+    /**
+     * 农场名称样式
+     * @param id
+     * @param name
+     * @returns {Style}
+     */
+    nameStyle(id, name, isCurrent, isHigh) {
+        const key = name + isCurrent + isHigh
+        let nameStyle = this.nameStyleCache[key]
+        if (!nameStyle) {
+            nameStyle = new Style({
+                text: new Text({
+                    text: name,
+                    offsetX: 0,
+                    offsetY: 8,
+                    font: `bold ${isCurrent||isHigh ? 22 : 16}px sans-serif`,
+                    fill: new Fill({ color: isCurrent||isHigh ? "#CB8713" : "#1D1D1D" }), // 字体颜色
+                    stroke: new Stroke({ width: 1.3,color: "#fff" }), // 字体颜色
+                }),
+            });
+            this.nameStyleCache[key] = nameStyle
+        }
+        return nameStyle
+    }
+
+    /**
+     * 农场照片样式
+     * @param cloudFilename
+     * @returns {Style}
+     */
+    cloudStyle(cloudFilename, isCurrent, isHigh){
+        let that= this
+        const key = cloudFilename + isCurrent + isHigh
+        let cloudStyle = this.cloudFilenameCache[key]
+        if (!cloudStyle) {
+            let cloudUrl = `${cloudFilename}?imageView2/1/w/100`
+            cloudStyle = new Style({
+                image: new Photo({
+                    src: cloudUrl,
+                    radius: isHigh ? 39 : (isCurrent ? 36 : 30),
+                    crop: true,
+                    displacement: [0, isCurrent||isHigh ? 58 : 42],
+                    stroke: new Stroke({
+                        width: 0,
+                        color: "#fdfcfc00",
+                    }),
+                    onload: function () {
+                        that.clusterLayer &&
+                        that.clusterLayer.changed();
+                    },
+                }),
+                zIndex: isHigh ? 10 : 0
+            })
+            this.cloudFilenameCache[key] = cloudStyle
+        }
+        return cloudStyle
+    }
+
+    /**
+     * 点位背景样式
+     * @returns {Style}
+     */
+    pointBgStyle(name, isCurrent, isHigh){
+        const key = name + isCurrent + isHigh
+        let bgStyle = this.bgStyleCache[key]
+        if (!bgStyle) {
+            bgStyle = new Style({
+                image: new Photo({
+                    src: isCurrent||isHigh ? require("@/assets/images/map/point-bg-active.png") : require("@/assets/images/map/point-bg.png"),
+                    radius: isHigh ? 55 : (isCurrent ? 50 : 36),
+                    shadow: 0,
+                    crop: false,
+                    displacement: [0, isCurrent||isHigh ? 52 : 38],
+                    stroke: new Stroke({
+                        width: 0,
+                        color: "#fdfcfc00",
+                    }),
+                }),
+                zIndex: isHigh ? 11 : 0
+            });
+            this.bgStyleCache[key] = bgStyle
+        }
+        return bgStyle
+    }
+
+    /**
+     * 品种图片加文字样式
+     * @returns {Style}
+     */
+    typeInfoStyle(img, name, price, isCurrent, isHigh){
+         let key = img + name + price + isCurrent+isHigh; // 缓存键
+        let typeStyle = this.typeInfoStyleCache[key]
+        if (!typeStyle) {
+            // 1. 创建一个临时的 Canvas 上下文用于测量文字宽度
+            const canvas = document.createElement("canvas");
+            const ctx = canvas.getContext("2d");
+
+            // 2. 设置字体样式(需与最终显示文字的样式一致)
+            ctx.font = "14px Arial"; // 假设文字样式为 14px Arial
+
+            // 3. 计算 name 和 price 的总宽度(包括间距)
+            const nameWidth = ctx.measureText(name).width;
+            const priceWidth = ctx.measureText(price).width;
+            const totalTextWidth = nameWidth + priceWidth + 70; // 10px 作为间距
+
+            // 4. 动态计算图片的横向偏移量(使其位于最左侧)
+            const imageRadius = 14; // 图片半径(与 Photo 的 radius 一致)
+            const offsetX = - (totalTextWidth / 2 + imageRadius + 5); // 5px 额外间距
+            typeStyle = new Style({
+                image: new Photo({
+                    src: img,
+                    kind: "circle",
+                    radius: 14,
+                    shadow: 0,
+                    crop: true,
+                    displacement: [offsetX, isCurrent||isHigh ? 124 : 100],
+                    stroke: new Stroke({
+                        width: 0,
+                        color: "#fdfcfc00",
+                    }),
+                }),
+                zIndex: 4
+            });
+            this.typeInfoStyleCache[key] = typeStyle
+        }
+        return typeStyle
+    }
+
+    /*************************************************************************************************
+     其他函数
+     *************************************************************************************************/
+    setProperties(feature,f) {
+        let disease = f.get("disease");
+        let cloudFilename = f.get("cloudFilename");
+        let mockFarmId = f.get("mockFarmId");
+        let id = f.get("mapId");
+        let targetSampleId = f.get("targetSampleId");
+        feature.set("id",id)
+        feature.set("mockFarmId",mockFarmId)
+        feature.set("targetSampleId",targetSampleId)
+    }
+    selectFeature(feature) {
+        let fs = feature.get("features");
+        if (fs.length === 1) {
+            return fs[0];
+        } else {
+            // 优先返回有 isHight 的项
+            for (let item of fs) {
+                if (item.get("isHight") || item.get("id") == 10117 || item.get("isDemo")) {
+                    return item;
+                }
+            }
+            // 其次返回有 disease 的项
+            for (let item of fs) {
+                if (item.get("disease")) {
+                    return item;
+                }
+            }
+            // 如果都没有,返回第一个项
+            return fs[0];
+        }
+    }
+
+}
+
+export default MockFarmLayer;
+// new MockFarmLayer()