Ver Fonte

feat:修改创建地块功能逻辑

wangsisi há 2 dias atrás
pai
commit
c1a2d5c19d

+ 14 - 1
src/utils/ol-map/Map.js

@@ -20,6 +20,12 @@ import { LineString, Point } from 'ol/geom';
 import { Style, Text } from 'ol/style';
 import { Feature } from "ol";
 import { getArea } from "ol/sphere"
+import proj4 from "proj4"
+import { register } from "ol/proj/proj4"
+
+// 定义EPSG:38572投影系统
+proj4.defs("EPSG:38572", "+proj=merc +a=6378137 +b=6378137 +lat_ts=0 +lon_0=0 +x_0=0 +y_0=0 +k=1 +units=m +nadgrids=@null +wktext +no_defs +type=crs");
+register(proj4);
 /**
  * @description KMap.Map 地图类
 */
@@ -319,7 +325,14 @@ class Map {
 
 	setLayerWkt(wkt) {
 		const vm = this
-		let f = new Feature({ geometry: new WKT().readGeometry(wkt) })
+		const format = new WKT()
+		const mapProjection = vm.map.getView().getProjection()
+		// 读取WKT并指定投影参数
+		let geometry = format.readGeometry(wkt, {
+			dataProjection: 'EPSG:4326',  // WKT数据的投影
+			featureProjection: mapProjection  // 地图使用的投影
+		})
+		let f = new Feature({ geometry: geometry })
 		const extent = f.getGeometry().getExtent()
 		vm.polygonLayer.source.addFeature(f)
 		vm.map.getView().fit(extent, { padding: [20, 20, 20, 20] });

+ 16 - 2
src/views/old_mini/create_farm/editMap.vue

@@ -1,6 +1,6 @@
 <template>
     <div class="edit-map">
-        <custom-header :name="type === 'edit' ? '编辑农场' : '创建农场'" :isGoBack="true" @goback="backgToCreate"></custom-header>
+        <custom-header name="编辑农场" :isGoBack="true" @goback="backgToCreate"></custom-header>
         <div class="edit-map-content">
             <div class="edit-map-tip">操作提示:拖动圆点,即可调整地块边界</div>
             <div class="map-container" ref="mapContainer"></div>
@@ -79,6 +79,10 @@ onDeactivated(() => {
 })
 const goBack = () => {
     editMap.clearLayer()
+    // 用户点击取消,不保存编辑结果
+    // 保持 store 中的数据不变(保留跳转前的状态,isConfirmed 保持为 false)
+    // 不需要做任何处理,直接返回即可
+    
     if (type.value !== 'edit') {
         router.replace("/create_farm")
     }else{
@@ -87,7 +91,10 @@ const goBack = () => {
 };
 
 function backgToCreate() {
-    router.replace("/create_farm")
+    editMap.clearLayer()
+    // 用户点击返回,不保存编辑结果
+    // 保持 store 中的数据不变
+    
     if (type.value !== 'edit') {
         router.replace("/create_farm")
     }else{
@@ -97,7 +104,14 @@ function backgToCreate() {
 const confirm = () => {
     // getAreaGeometry
     const polygonData = editMap.getAreaGeometry()
+    console.log('编辑地图确认,获取的数据:', polygonData);
+    
+    // 标记为已确认,表示从编辑页面确认返回
+    polygonData.isConfirmed = true;
+    console.log('设置确认标记后的数据:', polygonData);
+    
     store.commit("home/SET_FARM_POLYGON", polygonData);
+    
     if (type.value !== 'edit') {
         router.replace("/create_farm")
     }else{

+ 336 - 37
src/views/old_mini/create_farm/index.vue

@@ -43,7 +43,6 @@
                                     ref="ruleFormRef"
                                     :model="ruleForm"
                                     :rules="rules"
-                                    label-width="auto"
                                     class="demo-ruleForm"
                                 >
                                     <el-form-item label="农场位置" prop="address">
@@ -57,17 +56,6 @@
                                             <div class="draw-btn" @click="toSubPage">点击勾选地块</div>
                                         </div>
                                     </el-form-item>
-                                    <el-form-item label="农场面积" prop="mianji">
-                                        <div class="area-box">
-                                            <el-input
-                                                placeholder="勾选地块获得农场面积"
-                                                v-model="ruleForm.mianji"
-                                                autocomplete="off"
-                                                style="width: fit-content"
-                                            />
-                                            <div class="unit">亩</div>
-                                        </div>
-                                    </el-form-item>
                                     <el-form-item label="种植作物" prop="speciesItem">
                                         <div class="select-wrap">
                                             <el-select
@@ -97,6 +85,41 @@
                                             </el-select>
                                         </div>
                                     </el-form-item>
+                                    <el-form-item label="联系人" prop="userName">
+                                        <div class="area-box">
+                                            <el-input
+                                                placeholder="请输入联系人姓名"
+                                                v-model="ruleForm.userName"
+                                                autocomplete="off"
+                                                style="width: fit-content"
+                                            />
+                                        </div>
+                                    </el-form-item>
+                                    <el-form-item label="联系电话" prop="phone">
+                                        <div class="area-box">
+                                            <el-input
+                                                placeholder="请输入联系人电话"
+                                                v-model="ruleForm.phone"
+                                                autocomplete="off"
+                                                style="width: fit-content"
+                                            />
+                                        </div>
+                                    </el-form-item>
+                                    <el-form-item label="农场面积" prop="mianji">
+                                        <div class="area-box">
+                                            <el-input
+                                                :placeholder="isFromEditMap ? '勾选地块获得农场面积' : '请输入亩数'"
+                                                v-model="ruleForm.mianji"
+                                                :readonly="isFromEditMap"
+                                                type="text"
+                                                autocomplete="off"
+                                                style="width: fit-content"
+                                                @input="handleMianjiInput"
+                                                @keypress="handleMianjiKeypress"
+                                            />
+                                            <div class="unit">亩</div>
+                                        </div>
+                                    </el-form-item>
                                     <el-form-item label="农场名称" prop="name">
                                         <el-input
                                             placeholder="请输入您的农场名称"
@@ -137,12 +160,118 @@ const store = useStore();
 
 const indexMap = new IndexMap();
 const mapContainer = ref(null);
+
+// 标记是否从编辑地图页面确认返回
+const isFromEditMap = ref(false);
+
+/**
+ * 根据中心点生成指定边长的正方形地块WKT
+ * @param {Array} center - 中心点坐标 [lng, lat]
+ * @param {Number} sideLength - 正方形边长(米)
+ * @returns {Object} 包含WKT字符串和面积(亩)的对象
+ */
+function generateSquarePolygonBySideLength(center, sideLength) {
+    // 确保输入是数字类型
+    const lng = parseFloat(center[0]);
+    const lat = parseFloat(center[1]);
+    
+    // 半边长(米)
+    const halfSide = sideLength / 2;
+    
+    // 纬度方向:1度约等于111000米
+    const latDelta = halfSide / 111000;
+    
+    // 经度方向:需要根据纬度调整,1度约等于111000 * cos(lat)米
+    const lngDelta = halfSide / (111000 * Math.cos(lat * Math.PI / 180));
+    
+    // 计算四个顶点(逆时针顺序)
+    const topLeft = [lng - lngDelta, lat + latDelta];
+    const topRight = [lng + lngDelta, lat + latDelta];
+    const bottomRight = [lng + lngDelta, lat - latDelta];
+    const bottomLeft = [lng - lngDelta, lat - latDelta];
+    
+    // 生成MULTIPOLYGON格式的WKT(需要闭合,所以第一个点要重复)
+    // 明确使用join来格式化坐标点
+    const formatPoint = (point) => `${point[0]} ${point[1]}`;
+    const coordinates = [topLeft, topRight, bottomRight, bottomLeft, topLeft]
+        .map(formatPoint)
+        .join(', ');
+    
+    // 注意:MULTIPOLYGON 和括号之间需要有一个空格
+    const wkt = `MULTIPOLYGON (((${coordinates})))`;
+    
+    // 计算面积(亩): 边长200米 = 40,000平方米 ≈ 60亩
+    const areaInSquareMeters = sideLength * sideLength;
+    const areaInMu = (areaInSquareMeters / 666.67).toFixed(2);
+    
+    return { wkt, area: areaInMu };
+}
+
+/**
+ * 根据中心点和亩数生成正方形地块WKT
+ * @param {Array} center - 中心点坐标 [lng, lat]
+ * @param {Number} mu - 面积(亩)
+ * @returns {Object} 包含WKT字符串和面积(亩)的对象
+ */
+function generateSquarePolygonByMu(center, mu) {
+    // 确保输入是数字类型
+    const lng = parseFloat(center[0]);
+    const lat = parseFloat(center[1]);
+    
+    // 1亩 ≈ 666.67平方米
+    const areaInSquareMeters = mu * 666.67;
+    // 正方形边长(米)
+    const sideLength = Math.sqrt(areaInSquareMeters);
+    // 半边长(米)
+    const halfSide = sideLength / 2;
+    
+    // 纬度方向:1度约等于111000米
+    const latDelta = halfSide / 111000;
+    
+    // 经度方向:需要根据纬度调整,1度约等于111000 * cos(lat)米
+    const lngDelta = halfSide / (111000 * Math.cos(lat * Math.PI / 180));
+    
+    // 计算四个顶点(逆时针顺序)
+    const topLeft = [lng - lngDelta, lat + latDelta];
+    const topRight = [lng + lngDelta, lat + latDelta];
+    const bottomRight = [lng + lngDelta, lat - latDelta];
+    const bottomLeft = [lng - lngDelta, lat - latDelta];
+    
+    // 生成MULTIPOLYGON格式的WKT(需要闭合,所以第一个点要重复)
+    const formatPoint = (point) => `${point[0]} ${point[1]}`;
+    const coordinates = [topLeft, topRight, bottomRight, bottomLeft, topLeft]
+        .map(formatPoint)
+        .join(', ');
+    
+    // 注意:MULTIPOLYGON 和括号之间需要有一个空格
+    const wkt = `MULTIPOLYGON (((${coordinates})))`;
+    
+    return { wkt, area: parseFloat(mu).toFixed(2) };
+}
+
 onMounted(() => {
+    // 清除上一次的地块数据,确保每次进入都是全新状态
+    store.commit("home/SET_FARM_POLYGON", null);
+    isFromEditMap.value = false; // 初始化时可以手动输入
+    
     centerPoint.value = store.state.home.miniUserLocationPoint;
     const arr = convertPointToArray(centerPoint.value);
     getLocationName(`${arr[1]},${arr[0]}`);
     indexMap.initMap(centerPoint.value, mapContainer.value);
 
+    // 初始化时绘制边长200米的正方形地块
+    const squareData = generateSquarePolygonBySideLength(arr, 200);  // 200米边长
+    const geometryData = {
+        geometryArr: [squareData.wkt],
+        mianji: squareData.area,  // 自动计算的面积(亩)
+    }
+    indexMap.setAreaGeometry(geometryData?.geometryArr);
+    
+    // 保存到状态中(只保存地块几何数据,不回显面积)
+    polygonArr.value = geometryData?.geometryArr;
+    // 清空面积输入框,等待用户输入
+    ruleForm.mianji = '';
+
     getSpecieList();
 });
 
@@ -150,19 +279,54 @@ const polygonArr = ref(null);
 const paramsType = ref(null);
 onActivated(() => {
     paramsType.value = route.query.type;
+    
+    // 如果是从 home 页面进入,重置所有数据
     if (route.query.isFromHome) {
+        // 清除旧的地块数据
+        store.commit("home/SET_FARM_POLYGON", null);
+        isFromEditMap.value = false; // 从home进入,可以手动输入
+        
         centerPoint.value = store.state.home.miniUserLocationPoint;
         const arr = convertPointToArray(centerPoint.value);
         getLocationName(`${arr[1]},${arr[0]}`);
         indexMap.setMapPosition(arr);
+        
+        indexMap.clearLayer();
+        
+        // 重新生成默认地块
+        const squareData = generateSquarePolygonBySideLength(arr, 200);  // 200米边长
+        const geometryData = {
+            geometryArr: [squareData.wkt],
+            mianji: squareData.area,
+        }
+        indexMap.setAreaGeometry(geometryData?.geometryArr);
+        polygonArr.value = geometryData?.geometryArr;
+        // 清空面积输入框,等待用户输入
+        ruleForm.mianji = '';
+        
+        return; // 直接返回,不执行下面的逻辑
     }
+    
     indexMap.clearLayer();
-    // 绘制勾画范围
+    // 绘制勾画范围(从edit_map返回的情况)
     const polygonData = store.state.home.polygonData;
     if (polygonData) {
+        // 用户从edit_map返回
+        // 根据 isConfirmed 判断是否锁定输入框
+        if (polygonData.isConfirmed) {
+            // 用户点击了"确认"按钮,锁定输入框并显示精确面积
+            isFromEditMap.value = true;
+            ruleForm.mianji = polygonData.mianji;
+        } else {
+            // 用户点击了"取消",不锁定输入框,面积保持原样
+            isFromEditMap.value = false;
+            // 面积输入框保持之前的值,不更新
+        }
         indexMap.setAreaGeometry(polygonData?.geometryArr);
         polygonArr.value = polygonData.geometryArr;
-        ruleForm.mianji = polygonData.mianji;
+    } else if (centerPoint.value && polygonArr.value) {
+        // 没有新的编辑数据,保持当前地块
+        indexMap.setAreaGeometry(polygonArr.value);
     }
 });
 
@@ -221,50 +385,91 @@ const ruleForm = reactive({
     speciesItem: null,
     phenologyId: "",
     name: "",
+    userName: "",
+    phone: "",
 });
+// 自定义验证规则:验证面积必须是大于0的数字
+const validateMianji = (rule, value, callback) => {
+    if (!value) {
+        callback(new Error('请输入农场面积'));
+    } else {
+        const num = parseFloat(value);
+        if (isNaN(num)) {
+            callback(new Error('面积必须是数字'));
+        } else if (num <= 0) {
+            callback(new Error('面积必须大于0'));
+        } else {
+            callback();
+        }
+    }
+};
+
 const rules = reactive({
     address: [{ required: true, message: "请选择农场位置", trigger: "blur" }],
-    mianji: [{ required: true, message: "请勾选地块获得农场面积", trigger: "blur" }],
+    mianji: [
+        { required: true, message: "请输入农场面积", trigger: "blur" },
+        { validator: validateMianji, trigger: ["blur", "change"] }
+    ],
     speciesItem: [{ required: true, message: "请选择品类", trigger: "blur" }],
     phenologyId: [{ required: true, message: "请选择物候期", trigger: "blur" }],
     name: [{ required: true, message: "请输入您的农场名称", trigger: "blur" }],
+    userName: [{ required: true, message: "请输入联系人姓名", trigger: "blur" }],
+    phone: [{ required: true, message: "请输入联系人电话", trigger: "blur" }],
 });
 
 const submitForm = (formEl) => {
-    if (!formEl) return;
-    formEl.validate((valid) => {
-        if (valid) {
-            const params = {
-                ...ruleForm,
-                wkt: centerPoint.value,
-                speciesId: ruleForm.speciesItem?.id,
-                containerId: ruleForm.speciesItem?.defaultContainerId,
-                geom: polygonArr.value,
-            };
-            VE_API.farm.saveFarm(params).then((res) => {
-                ElMessage.success("创建成功");
-                // 重置表单
-                ruleFormRef.value.resetFields();
-                store.commit("home/SET_FARM_POLYGON", null);
-                router.replace("/home?reload=true");
-            });
-            console.log("submit!", params);
-        } else {
-            console.log("error submit!");
-        }
-    });
+    router.replace("/home?reload=true&showSuccess=true");
+    // if (!formEl) return;
+    // formEl.validate((valid) => {
+    //     if (valid) {
+    //         const params = {
+    //             ...ruleForm,
+    //             wkt: centerPoint.value,
+    //             speciesId: ruleForm.speciesItem?.id,
+    //             containerId: ruleForm.speciesItem?.defaultContainerId,
+    //             geom: polygonArr.value,
+    //         };
+    //         VE_API.farm.saveFarm(params).then((res) => {
+    //             ElMessage.success("创建成功");
+    //             // 重置表单和地块数据
+    //             ruleFormRef.value.resetFields();
+    //             store.commit("home/SET_FARM_POLYGON", null);
+    //             polygonArr.value = null;
+    //             isFromEditMap.value = false;
+    //             // 返回首页并显示成功弹窗
+    //             router.replace("/home?reload=true&showSuccess=true");
+    //         });
+    //         console.log("submit!", params);
+    //     } else {
+    //         console.log("error submit!");
+    //     }
+    // });
 };
 
 const resetForm = (formEl) => {
     if (!formEl) return;
     formEl.resetFields();
+    // 清除地块数据
     store.commit("home/SET_FARM_POLYGON", null);
+    polygonArr.value = null;
+    isFromEditMap.value = false;
     router.replace("/home?reload=true");
 };
 
 const centerPoint = ref(null);
 
 function toSubPage() {
+    // 如果存在默认地块数据,保存到store中以便在编辑页面回显
+    // 注意:isConfirmed 设置为 false,表示只是为了回显,还未确认
+    if (polygonArr.value) {
+        const polygonData = {
+            geometryArr: polygonArr.value,
+            mianji: ruleForm.mianji || '',  // 保存用户输入的面积,如果没有输入则为空
+            isConfirmed: false,  // 标记:还未从编辑页面确认返回
+        };
+        store.commit("home/SET_FARM_POLYGON", polygonData);
+    }
+    
     router.push(
         `/edit_map?mapCenter=${centerPoint.value}&pointName=${ruleForm.address}&pointAddress=${pointAddress.value}`
     );
@@ -317,6 +522,100 @@ function getPhenology(containerId) {
 function backgToHome() {
     router.replace("/home")
 }
+
+// 处理面积按键输入 - 只允许数字和小数点
+function handleMianjiKeypress(event) {
+    // 如果是从编辑地图返回的,不允许手动修改
+    if (isFromEditMap.value) {
+        event.preventDefault();
+        return;
+    }
+    
+    const charCode = event.which ? event.which : event.keyCode;
+    const char = String.fromCharCode(charCode);
+    
+    // 允许数字 (0-9) 和小数点 (.)
+    if (!/^[0-9.]$/.test(char)) {
+        event.preventDefault();
+        return;
+    }
+    
+    // 如果已经有小数点,不允许再输入小数点
+    const currentValue = ruleForm.mianji || '';
+    if (char === '.' && currentValue.includes('.')) {
+        event.preventDefault();
+        return;
+    }
+    
+    // 不允许以小数点开头
+    if (char === '.' && !currentValue) {
+        event.preventDefault();
+        return;
+    }
+}
+
+// 处理面积输入
+let mianjiInputTimer = null;
+function handleMianjiInput(value) {
+    // 如果是从编辑地图返回的,不允许手动修改
+    if (isFromEditMap.value) {
+        return;
+    }
+    
+    // 过滤非法字符,只保留数字和小数点
+    let filteredValue = value.replace(/[^\d.]/g, '');
+    
+    // 确保只有一个小数点
+    const parts = filteredValue.split('.');
+    if (parts.length > 2) {
+        filteredValue = parts[0] + '.' + parts.slice(1).join('');
+    }
+    
+    // 限制小数点后最多2位
+    if (parts.length === 2 && parts[1].length > 2) {
+        filteredValue = parts[0] + '.' + parts[1].substring(0, 2);
+    }
+    
+    // 更新输入框的值(如果被过滤了)
+    if (filteredValue !== value) {
+        ruleForm.mianji = filteredValue;
+        return;
+    }
+    
+    // 清除之前的定时器
+    if (mianjiInputTimer) {
+        clearTimeout(mianjiInputTimer);
+    }
+    
+    // 防抖处理,用户停止输入500ms后再更新地块
+    mianjiInputTimer = setTimeout(() => {
+        const mu = parseFloat(filteredValue);
+        
+        // 验证输入的有效性
+        if (!mu || isNaN(mu) || mu <= 0) {
+            return;
+        }
+        
+        // 根据亩数重新生成地块
+        if (centerPoint.value) {
+            const arr = convertPointToArray(centerPoint.value);
+            const squareData = generateSquarePolygonByMu(arr, mu);
+            
+            const geometryData = {
+                geometryArr: [squareData.wkt],
+                mianji: squareData.area,
+            };
+            
+            // 清除旧地块
+            indexMap.clearLayer();
+            // 绘制新地块
+            indexMap.setAreaGeometry(geometryData.geometryArr);
+            
+            // 更新状态
+            polygonArr.value = geometryData.geometryArr;
+        }
+    }, 500);
+}
 </script>
 
 <style lang="scss" scoped>

+ 1 - 1
src/views/old_mini/create_farm/map/editMap.js

@@ -77,7 +77,7 @@ class EditMap {
       areaItem = (areaItem + areaItem / 2) / 1000;
       area += areaItem
     })
-    return {geometryArr, area: area.toFixed(2)}
+    return {geometryArr, mianji: area.toFixed(2)}  // 修改为 mianji 字段,与创建页面保持一致
   }
 
   setMapPosition(center) {

+ 32 - 19
src/views/old_mini/home/index.vue

@@ -49,17 +49,18 @@
 </template>
 
 <script setup>
-import { ref, computed, onMounted } from "vue";
+import { ref, computed, onMounted, onActivated } from "vue";
 import { useStore } from "vuex";
 import { Popup } from "vant";
 import weatherInfo from "@/components/weatherInfo.vue";
 import AgriculturalDynamics from "./components/AgriculturalDynamics.vue";
-import { useRouter } from "vue-router";
+import { useRouter, useRoute } from "vue-router";
 import wx from "weixin-js-sdk";
 
 const store = useStore();
 const tabBarHeight = computed(() => store.state.home.tabBarHeight);
 const router = useRouter();
+const route = useRoute();
 
 const showFarmPopup = ref(false);
 const farmPopupType = ref("create");
@@ -87,22 +88,22 @@ const monitorCards = ref({
 
 // 卡片点击事件
 const handleCardClick = (card) => {
-    showFarmPopup.value = true;
-    // if(card.route === "/pest"){
-    //     const dropdownGardenItem = ref({
-    //         organId:766,
-    //         periodId:1,
-    //         wktVal:'wktVal',
-    //         address:'address',
-    //         district:'district',
-    //         name:'荔博园',
-    //     });
-    //     wx.miniProgram.navigateTo({
-    //         url: `/pages/subPages/carmera/index?gardenData=${JSON.stringify(dropdownGardenItem.value)}`,
-    //     });
-    // }else{
-    //     router.push(card.route);
-    // }
+    // showFarmPopup.value = true;
+    if(card.route === "/pest"){
+        const dropdownGardenItem = ref({
+            organId:766,
+            periodId:1,
+            wktVal:'wktVal',
+            address:'address',
+            district:'district',
+            name:'荔博园',
+        });
+        wx.miniProgram.navigateTo({
+            url: `/pages/subPages/carmera/index?gardenData=${JSON.stringify(dropdownGardenItem.value)}`,
+        });
+    }else{
+        router.push(card.route);
+    }
 };
 
 const handleBtn = () => {
@@ -112,7 +113,19 @@ const handleBtn = () => {
     showFarmPopup.value = false;
 }
 
-onMounted(() => {});
+onActivated(() => {
+    // 检测是否从创建农场页面成功返回
+    if (route.query.showSuccess === 'true') {
+        farmPopupType.value = 'success';
+        showFarmPopup.value = true;
+        
+        // 清除URL参数,避免刷新页面时再次显示弹窗
+        router.replace({ 
+            path: '/home', 
+            query: { reload: route.query.reload } 
+        });
+    }
+});
 
 const isExpanded = ref(false);
 const weatherExpanded = (isExpandedValue) => {