Quellcode durchsuchen

修改高斯工具显示,新增度和度分秒转换

qyx vor 2 Tagen
Ursprung
Commit
3d747a6741
59 geänderte Dateien mit 7494 neuen und 2549 gelöschten Zeilen
  1. 99
    0
      oa-back/ruoyi-calculate/src/main/java/com/ruoyi/web/calculate/controller/CmcDMSToDegreeController.java
  2. 99
    0
      oa-back/ruoyi-calculate/src/main/java/com/ruoyi/web/calculate/controller/CmcDegreeToDMSController.java
  3. 83
    0
      oa-back/ruoyi-calculate/src/main/java/com/ruoyi/web/calculate/controller/CmcDegreeToRadianController.java
  4. 0
    24
      oa-back/ruoyi-calculate/src/main/java/com/ruoyi/web/calculate/controller/CmcGaussBandChangeController.java
  5. 0
    23
      oa-back/ruoyi-calculate/src/main/java/com/ruoyi/web/calculate/controller/CmcGaussNegativeController.java
  6. 0
    22
      oa-back/ruoyi-calculate/src/main/java/com/ruoyi/web/calculate/controller/CmcGaussPositiveController.java
  7. 0
    21
      oa-back/ruoyi-calculate/src/main/java/com/ruoyi/web/calculate/controller/CmcGeodeticToSpatialController.java
  8. 0
    19
      oa-back/ruoyi-calculate/src/main/java/com/ruoyi/web/calculate/controller/CmcSpatialToGeodeticContoller.java
  9. 0
    23
      oa-back/ruoyi-calculate/src/main/java/com/ruoyi/web/calculate/controller/CmcUTMBandChangeContorller.java
  10. 0
    22
      oa-back/ruoyi-calculate/src/main/java/com/ruoyi/web/calculate/controller/CmcUTMNegativeController.java
  11. 0
    21
      oa-back/ruoyi-calculate/src/main/java/com/ruoyi/web/calculate/controller/CmcUTMPositiveController.java
  12. 107
    0
      oa-back/ruoyi-calculate/src/main/java/com/ruoyi/web/calculate/domain/CmcDMSToDegree.java
  13. 120
    0
      oa-back/ruoyi-calculate/src/main/java/com/ruoyi/web/calculate/domain/CmcDegreeToDMS.java
  14. 120
    0
      oa-back/ruoyi-calculate/src/main/java/com/ruoyi/web/calculate/domain/CmcDegreeToRadian.java
  15. 32
    0
      oa-back/ruoyi-calculate/src/main/java/com/ruoyi/web/calculate/service/ICmcDMSToDegreeService.java
  16. 32
    0
      oa-back/ruoyi-calculate/src/main/java/com/ruoyi/web/calculate/service/ICmcDegreeToDMSService.java
  17. 32
    0
      oa-back/ruoyi-calculate/src/main/java/com/ruoyi/web/calculate/service/ICmcDegreeToRadianService.java
  18. 281
    0
      oa-back/ruoyi-calculate/src/main/java/com/ruoyi/web/calculate/service/impl/CmcDMSToDegreeServiceImpl.java
  19. 280
    0
      oa-back/ruoyi-calculate/src/main/java/com/ruoyi/web/calculate/service/impl/CmcDegreeToDMSServiceImpl.java
  20. 271
    0
      oa-back/ruoyi-calculate/src/main/java/com/ruoyi/web/calculate/service/impl/CmcDegreeToRadianServiceImpl.java
  21. 133
    44
      oa-back/ruoyi-calculate/src/main/java/com/ruoyi/web/calculate/service/impl/CmcGaussBandChangeServiceImpl.java
  22. 135
    42
      oa-back/ruoyi-calculate/src/main/java/com/ruoyi/web/calculate/service/impl/CmcGaussNegativeServiceImpl.java
  23. 133
    43
      oa-back/ruoyi-calculate/src/main/java/com/ruoyi/web/calculate/service/impl/CmcGaussPositiveServiceImpl.java
  24. 177
    45
      oa-back/ruoyi-calculate/src/main/java/com/ruoyi/web/calculate/service/impl/CmcGeodeticToSpatialServiceImpl.java
  25. 178
    44
      oa-back/ruoyi-calculate/src/main/java/com/ruoyi/web/calculate/service/impl/CmcSpatialToGeodeticServiceImpl.java
  26. 177
    42
      oa-back/ruoyi-calculate/src/main/java/com/ruoyi/web/calculate/service/impl/CmcUTMBandChangeServicelmpl.java
  27. 175
    40
      oa-back/ruoyi-calculate/src/main/java/com/ruoyi/web/calculate/service/impl/CmcUTMNegativeServiceImpl.java
  28. 133
    40
      oa-back/ruoyi-calculate/src/main/java/com/ruoyi/web/calculate/service/impl/CmcUTMPositiveServiceImpl.java
  29. 0
    125
      oa-back/ruoyi-calculate/src/main/java/com/ruoyi/web/calculate/vo/CmcGaussBandChangeTemplate.java
  30. 0
    76
      oa-back/ruoyi-calculate/src/main/java/com/ruoyi/web/calculate/vo/CmcGaussNegativeTemplate.java
  31. 0
    124
      oa-back/ruoyi-calculate/src/main/java/com/ruoyi/web/calculate/vo/CmcGaussPositiveTemplate.java
  32. 0
    88
      oa-back/ruoyi-calculate/src/main/java/com/ruoyi/web/calculate/vo/CmcGeodeticToSpatialTemplate.java
  33. 0
    66
      oa-back/ruoyi-calculate/src/main/java/com/ruoyi/web/calculate/vo/CmcSpatialToGeodeticTemplate.java
  34. 0
    101
      oa-back/ruoyi-calculate/src/main/java/com/ruoyi/web/calculate/vo/CmcUTMBandChangeTemplate.java
  35. 0
    98
      oa-back/ruoyi-calculate/src/main/java/com/ruoyi/web/calculate/vo/CmcUTMNegativeTemplate.java
  36. 0
    109
      oa-back/ruoyi-calculate/src/main/java/com/ruoyi/web/calculate/vo/CmcUTMPositiveTemplate.java
  37. 32
    0
      oa-ui/src/api/calculate/DMStodegree.js
  38. 32
    0
      oa-ui/src/api/calculate/degreetoDMS.js
  39. 32
    0
      oa-ui/src/api/calculate/degreetoradian.js
  40. 0
    9
      oa-ui/src/api/calculate/gaussbandchange.js
  41. 0
    9
      oa-ui/src/api/calculate/gaussnegative.js
  42. 0
    9
      oa-ui/src/api/calculate/gausspositive.js
  43. 0
    9
      oa-ui/src/api/calculate/geodetictospatial.js
  44. 0
    9
      oa-ui/src/api/calculate/spatialtogeodetic.js
  45. 0
    9
      oa-ui/src/api/calculate/utmbandchange.js
  46. 0
    9
      oa-ui/src/api/calculate/utmnegative.js
  47. 0
    9
      oa-ui/src/api/calculate/utmpositive.js
  48. 27
    0
      oa-ui/src/views/calculate/index.vue
  49. 596
    0
      oa-ui/src/views/calculate/tools/DMStodegree.vue
  50. 592
    0
      oa-ui/src/views/calculate/tools/degreetoDMS.vue
  51. 564
    0
      oa-ui/src/views/calculate/tools/degreetoradian.vue
  52. 351
    167
      oa-ui/src/views/calculate/tools/gaussbandchange.vue
  53. 335
    169
      oa-ui/src/views/calculate/tools/gaussnegative.vue
  54. 370
    109
      oa-ui/src/views/calculate/tools/gausspositive.vue
  55. 382
    140
      oa-ui/src/views/calculate/tools/geodetictospatial.vue
  56. 353
    119
      oa-ui/src/views/calculate/tools/spatialtogeodetic.vue
  57. 342
    154
      oa-ui/src/views/calculate/tools/utmbandchange.vue
  58. 323
    138
      oa-ui/src/views/calculate/tools/utmnegative.vue
  59. 366
    179
      oa-ui/src/views/calculate/tools/utmpositive.vue

+ 99
- 0
oa-back/ruoyi-calculate/src/main/java/com/ruoyi/web/calculate/controller/CmcDMSToDegreeController.java Datei anzeigen

@@ -0,0 +1,99 @@
1
+package com.ruoyi.web.calculate.controller;
2
+
3
+import java.util.ArrayList;
4
+import java.util.List;
5
+import javax.servlet.http.HttpServletResponse;
6
+
7
+import org.springframework.beans.factory.annotation.Autowired;
8
+import org.springframework.web.bind.annotation.GetMapping;
9
+import org.springframework.web.bind.annotation.PostMapping;
10
+import org.springframework.web.bind.annotation.RequestBody;
11
+import org.springframework.web.bind.annotation.RequestMapping;
12
+import org.springframework.web.bind.annotation.RequestParam;
13
+import org.springframework.web.bind.annotation.RestController;
14
+import org.springframework.web.multipart.MultipartFile;
15
+
16
+import com.ruoyi.common.annotation.Log;
17
+import com.ruoyi.common.core.controller.BaseController;
18
+import com.ruoyi.common.core.domain.AjaxResult;
19
+import com.ruoyi.common.enums.BusinessType;
20
+import com.ruoyi.web.calculate.domain.CmcDMSToDegree;
21
+import com.ruoyi.web.calculate.service.ICmcDMSToDegreeService;
22
+import com.ruoyi.common.utils.poi.ExcelUtil;
23
+
24
+/**
25
+ * 度分秒转度 Controller
26
+ * 
27
+ * @author ruoyi
28
+ * @date 2026-06-12
29
+ */
30
+@RestController
31
+@RequestMapping("/calculate/DMSToDegree")
32
+public class CmcDMSToDegreeController extends BaseController
33
+{
34
+    @Autowired
35
+    private ICmcDMSToDegreeService cmcDMSToDegreeService;
36
+
37
+    /**
38
+     * 导入 Excel 数据
39
+     */
40
+    @PostMapping("/import")
41
+    public AjaxResult importExcel(@RequestParam("file") MultipartFile file)
42
+    {
43
+        try {
44
+            // 检查文件是否为空
45
+            if (file.isEmpty()) {
46
+                return AjaxResult.error("请选择要导入的文件!");
47
+            }
48
+
49
+            // 检查文件格式
50
+            String fileName = file.getOriginalFilename();
51
+            if (fileName == null || (!fileName.endsWith(".xls") && !fileName.endsWith(".xlsx"))) {
52
+                return AjaxResult.error("请上传 Excel 文件(.xls 或.xlsx 格式)!");
53
+            }
54
+
55
+            // 调用 Service 层解析 Excel 并返回数据
56
+            return cmcDMSToDegreeService.importExcelData(file);
57
+
58
+        } catch (Exception e) {
59
+            e.printStackTrace();
60
+            return AjaxResult.error("导入失败:" + e.getMessage());
61
+        }
62
+    }
63
+
64
+    /**
65
+     * 导出度分秒转度列表
66
+     */
67
+    @Log(title = "度分秒转度", businessType = BusinessType.EXPORT)
68
+    @PostMapping("/export")
69
+    public void export(HttpServletResponse response, @RequestBody List<CmcDMSToDegree> dataList)
70
+    {
71
+        if (dataList == null || dataList.isEmpty()) {
72
+            throw new RuntimeException("没有需要导出的数据");
73
+        }
74
+        
75
+        List<CmcDMSToDegree> exportList = new ArrayList<>();
76
+        for (CmcDMSToDegree item : dataList) {
77
+            CmcDMSToDegree exportItem = new CmcDMSToDegree();   
78
+            exportItem.setPointNumber(item.getPointNumber());
79
+            exportItem.setLongitudeDMS(item.getLongitudeDMS());
80
+            exportItem.setLatitudeDMS(item.getLatitudeDMS());
81
+            exportItem.setLongitude(item.getLongitude());
82
+            exportItem.setLatitude(item.getLatitude());
83
+            exportList.add(exportItem);
84
+        }
85
+        
86
+        ExcelUtil<CmcDMSToDegree> util = new ExcelUtil<CmcDMSToDegree>(CmcDMSToDegree.class);
87
+        util.exportExcel(response, exportList, "度分秒转度结果");
88
+    }
89
+
90
+    /**
91
+     * 执行度分秒转度(仅计算,不保存)
92
+     */
93
+    @PostMapping("/calculate")
94
+    public AjaxResult calculate(@RequestBody List<CmcDMSToDegree> dataList)     
95
+    {
96
+        List<CmcDMSToDegree> result = cmcDMSToDegreeService.calculateDMSToDegree(dataList);
97
+        return success(result);
98
+    }
99
+}

+ 99
- 0
oa-back/ruoyi-calculate/src/main/java/com/ruoyi/web/calculate/controller/CmcDegreeToDMSController.java Datei anzeigen

@@ -0,0 +1,99 @@
1
+package com.ruoyi.web.calculate.controller;
2
+
3
+import java.util.ArrayList;
4
+import java.util.List;
5
+import javax.servlet.http.HttpServletResponse;
6
+
7
+import org.springframework.beans.factory.annotation.Autowired;
8
+import org.springframework.web.bind.annotation.GetMapping;
9
+import org.springframework.web.bind.annotation.PostMapping;
10
+import org.springframework.web.bind.annotation.RequestBody;
11
+import org.springframework.web.bind.annotation.RequestMapping;
12
+import org.springframework.web.bind.annotation.RequestParam;
13
+import org.springframework.web.bind.annotation.RestController;
14
+import org.springframework.web.multipart.MultipartFile;
15
+
16
+import com.ruoyi.common.annotation.Log;
17
+import com.ruoyi.common.core.controller.BaseController;
18
+import com.ruoyi.common.core.domain.AjaxResult;
19
+import com.ruoyi.common.enums.BusinessType;
20
+import com.ruoyi.web.calculate.domain.CmcDegreeToDMS;
21
+import com.ruoyi.web.calculate.service.ICmcDegreeToDMSService;
22
+import com.ruoyi.common.utils.poi.ExcelUtil;
23
+
24
+/**
25
+ * 角度度分秒转换 Controller
26
+ * 
27
+ * @author ruoyi
28
+ * @date 2026-06-12
29
+ */
30
+@RestController
31
+@RequestMapping("/calculate/degreeToDMS")
32
+public class CmcDegreeToDMSController extends BaseController
33
+{
34
+    @Autowired
35
+    private ICmcDegreeToDMSService cmcDegreeToDMS;
36
+
37
+    /**
38
+     * 导入 Excel 数据
39
+     */
40
+    @PostMapping("/import")
41
+    public AjaxResult importExcel(@RequestParam("file") MultipartFile file)
42
+    {
43
+        try {
44
+            // 检查文件是否为空
45
+            if (file.isEmpty()) {
46
+                return AjaxResult.error("请选择要导入的文件!");
47
+            }
48
+
49
+            // 检查文件格式
50
+            String fileName = file.getOriginalFilename();
51
+            if (fileName == null || (!fileName.endsWith(".xls") && !fileName.endsWith(".xlsx"))) {
52
+                return AjaxResult.error("请上传 Excel 文件(.xls 或.xlsx 格式)!");
53
+            }
54
+
55
+            // 调用 Service 层解析 Excel 并返回数据
56
+            return cmcDegreeToDMS.importExcelData(file);
57
+
58
+        } catch (Exception e) {
59
+            e.printStackTrace();
60
+            return AjaxResult.error("导入失败:" + e.getMessage());
61
+        }
62
+    }
63
+
64
+    /**
65
+     * 导出角度度分秒转换列表
66
+     */
67
+    @Log(title = "度转度分秒", businessType = BusinessType.EXPORT)
68
+    @PostMapping("/export")
69
+    public void export(HttpServletResponse response, @RequestBody List<CmcDegreeToDMS> dataList)
70
+    {
71
+        if (dataList == null || dataList.isEmpty()) {
72
+            throw new RuntimeException("没有需要导出的数据");
73
+        }
74
+        
75
+        List<CmcDegreeToDMS> exportList = new ArrayList<>();
76
+        for (CmcDegreeToDMS item : dataList) {
77
+            CmcDegreeToDMS exportItem = new CmcDegreeToDMS();
78
+            exportItem.setPointNumber(item.getPointNumber());
79
+            exportItem.setLongitude(item.getLongitude());
80
+            exportItem.setLatitude(item.getLatitude());
81
+            exportItem.setLongitudeDMS(item.getLongitudeDMS());
82
+            exportItem.setLatitudeDMS(item.getLatitudeDMS());
83
+            exportList.add(exportItem);
84
+        }
85
+        
86
+        ExcelUtil<CmcDegreeToDMS> util = new ExcelUtil<CmcDegreeToDMS>(CmcDegreeToDMS.class);
87
+        util.exportExcel(response, exportList, "度转度分秒结果");
88
+    }
89
+
90
+    /**
91
+     * 执行角度度分秒转换(仅计算,不保存)
92
+     */
93
+    @PostMapping("/calculate")
94
+    public AjaxResult calculate(@RequestBody List<CmcDegreeToDMS> dataList)
95
+    {
96
+        List<CmcDegreeToDMS> result = cmcDegreeToDMS.calculateDegreeToDMS(dataList);
97
+        return success(result);
98
+    }
99
+}

+ 83
- 0
oa-back/ruoyi-calculate/src/main/java/com/ruoyi/web/calculate/controller/CmcDegreeToRadianController.java Datei anzeigen

@@ -0,0 +1,83 @@
1
+package com.ruoyi.web.calculate.controller;
2
+
3
+import java.util.ArrayList;
4
+import java.util.List;
5
+
6
+import javax.servlet.http.HttpServletResponse;
7
+
8
+import org.springframework.beans.factory.annotation.Autowired;
9
+import org.springframework.web.bind.annotation.PostMapping;
10
+import org.springframework.web.bind.annotation.RequestBody;
11
+import org.springframework.web.bind.annotation.RequestMapping;
12
+import org.springframework.web.bind.annotation.RequestParam;
13
+import org.springframework.web.bind.annotation.RestController;
14
+import org.springframework.web.multipart.MultipartFile;
15
+
16
+import com.ruoyi.common.annotation.Log;
17
+import com.ruoyi.common.core.domain.AjaxResult;
18
+import com.ruoyi.common.enums.BusinessType;
19
+import com.ruoyi.common.utils.poi.ExcelUtil;
20
+import com.ruoyi.web.calculate.domain.CmcDMSToDegree;
21
+import com.ruoyi.web.calculate.domain.CmcDegreeToRadian;
22
+import com.ruoyi.web.calculate.service.ICmcDegreeToRadianService;
23
+
24
+/**
25
+ * 角度度转弧度控制器
26
+ * 
27
+ * @author ruoyi
28
+ * @date 2026-06-17
29
+ */
30
+@RestController
31
+@RequestMapping("/calculate/degreeToRadian")
32
+public class CmcDegreeToRadianController {
33
+
34
+    @Autowired
35
+    private ICmcDegreeToRadianService cmcDegreeToRadianService;
36
+
37
+    /**
38
+     * 导入Excel数据
39
+     */
40
+    @PostMapping("/import")
41
+    public AjaxResult importExcel(@RequestParam("file") MultipartFile file) {
42
+        return cmcDegreeToRadianService.importExcelData(file);
43
+    }
44
+
45
+    /**
46
+     * 导出角度度转弧度列表
47
+     */
48
+    @Log(title = "角度度转弧度", businessType = BusinessType.EXPORT)
49
+    @PostMapping("/export")
50
+    public void export(HttpServletResponse response, @RequestBody List<CmcDegreeToRadian> dataList)
51
+    {
52
+        if (dataList == null || dataList.isEmpty()) {
53
+            throw new RuntimeException("没有需要导出的数据");
54
+        }
55
+        
56
+        List<CmcDegreeToRadian> exportList = new ArrayList<>();
57
+        for (CmcDegreeToRadian item : dataList) {
58
+            CmcDegreeToRadian exportItem = new CmcDegreeToRadian();   
59
+            exportItem.setPointNumber(item.getPointNumber());
60
+            exportItem.setLongitude(item.getLongitude());
61
+            exportItem.setLatitude(item.getLatitude());
62
+            exportItem.setLongitudeRadian(item.getLongitudeRadian());
63
+            exportItem.setLatitudeRadian(item.getLatitudeRadian());
64
+            exportList.add(exportItem);
65
+        }
66
+        
67
+        ExcelUtil<CmcDegreeToRadian> util = new ExcelUtil<CmcDegreeToRadian>(CmcDegreeToRadian.class);
68
+        util.exportExcel(response, exportList, "角度度转弧度结果");
69
+    }
70
+
71
+    /**
72
+     * 执行角度度转弧度计算
73
+     */
74
+    @PostMapping("/calculate")
75
+    public AjaxResult calculateDegreeToRadian(@RequestBody List<CmcDegreeToRadian> dataList) {
76
+        try {
77
+            List<CmcDegreeToRadian> results = cmcDegreeToRadianService.calculateDegreeToRadian(dataList);
78
+            return AjaxResult.success("计算完成", results);
79
+        } catch (Exception e) {
80
+            return AjaxResult.error("计算失败:" + e.getMessage());
81
+        }
82
+    }
83
+}

+ 0
- 24
oa-back/ruoyi-calculate/src/main/java/com/ruoyi/web/calculate/controller/CmcGaussBandChangeController.java Datei anzeigen

@@ -19,7 +19,6 @@ import com.ruoyi.common.enums.BusinessType;
19 19
 import com.ruoyi.common.utils.poi.ExcelUtil;
20 20
 import com.ruoyi.web.calculate.domain.CmcGaussBandChange;
21 21
 import com.ruoyi.web.calculate.service.ICmcGaussBandChangeService;
22
-import com.ruoyi.web.calculate.vo.CmcGaussBandChangeTemplate;
23 22
 
24 23
 @RestController
25 24
 @RequestMapping("/calculate/gaussBandChange")
@@ -96,27 +95,4 @@ public class CmcGaussBandChangeController {
96 95
         return AjaxResult.success(result);
97 96
     }
98 97
 
99
-    @PostMapping("/template")
100
-    public void downloadTemplate(HttpServletResponse response) {
101
-        List<CmcGaussBandChangeTemplate> templateList = new ArrayList<>();
102
-
103
-        // 添加示例数据
104
-        CmcGaussBandChangeTemplate example = new CmcGaussBandChangeTemplate();
105
-        example.setPointNumber("示例点1");
106
-        example.setCoordinateSystem("WGS84");
107
-        example.setGaussX(3421806.699);
108
-        example.setGaussY(34460881.36);
109
-        example.setBand(1);
110
-        example.setBandwidth(3);
111
-        example.setNewBand(2);
112
-        example.setNewBandwidth(6);
113
-        example.setLongitudePosition("E");
114
-        example.setLatitudePosition("N");
115
-        templateList.add(example);
116
-
117
-        ExcelUtil<CmcGaussBandChangeTemplate> util = new ExcelUtil<CmcGaussBandChangeTemplate>(
118
-                CmcGaussBandChangeTemplate.class);
119
-        util.exportExcel(response, templateList, "高斯换带模板");
120
-    }
121
-
122 98
 }

+ 0
- 23
oa-back/ruoyi-calculate/src/main/java/com/ruoyi/web/calculate/controller/CmcGaussNegativeController.java Datei anzeigen

@@ -19,7 +19,6 @@ import com.ruoyi.common.enums.BusinessType;
19 19
 import com.ruoyi.common.utils.poi.ExcelUtil;
20 20
 import com.ruoyi.web.calculate.domain.CmcGaussNegative;
21 21
 import com.ruoyi.web.calculate.service.ICmcGaussNegativeService;
22
-import com.ruoyi.web.calculate.vo.CmcGaussNegativeTemplate;
23 22
 
24 23
 
25 24
 @RestController
@@ -98,26 +97,4 @@ public class CmcGaussNegativeController
98 97
         List<CmcGaussNegative> result = cmcGaussNegativeService.calculateGaussNegative(dataList);
99 98
         return AjaxResult.success(result);
100 99
     }
101
-
102
-
103
-
104
-    @PostMapping("/template")
105
-    public void downloadTemplate(HttpServletResponse response) {
106
-        List<CmcGaussNegativeTemplate> templateList = new ArrayList<>();
107
-
108
-        // 添加示例数据
109
-        CmcGaussNegativeTemplate example = new CmcGaussNegativeTemplate();
110
-        example.setPointNumber("示例点1");
111
-        example.setCoordinateSystem("WGS84");
112
-        example.setGaussX(3421806.699);
113
-        example.setGaussY(34460881.36);
114
-        example.setLongitudePosition("E");
115
-        example.setLatitudePosition("N");
116
-        templateList.add(example);
117
-
118
-        ExcelUtil<CmcGaussNegativeTemplate> util = new ExcelUtil<CmcGaussNegativeTemplate>(CmcGaussNegativeTemplate.class);
119
-        util.exportExcel(response, templateList, "高斯反算模板");
120
-    }
121
-
122
-
123 100
 }

+ 0
- 22
oa-back/ruoyi-calculate/src/main/java/com/ruoyi/web/calculate/controller/CmcGaussPositiveController.java Datei anzeigen

@@ -22,7 +22,6 @@ import com.ruoyi.common.core.domain.AjaxResult;
22 22
 import com.ruoyi.common.enums.BusinessType;
23 23
 import com.ruoyi.web.calculate.domain.CmcGaussPositive;
24 24
 import com.ruoyi.web.calculate.service.ICmcGaussPositiveService;
25
-import com.ruoyi.web.calculate.vo.CmcGaussPositiveTemplate;
26 25
 import com.ruoyi.common.utils.poi.ExcelUtil;
27 26
 import com.ruoyi.common.core.page.TableDataInfo;
28 27
 
@@ -108,25 +107,4 @@ public class CmcGaussPositiveController extends BaseController
108 107
         List<CmcGaussPositive> result = cmcGaussPositiveService.calculateGaussPositive(dataList);
109 108
         return success(result);
110 109
     }
111
-
112
-    @PostMapping("/template")
113
-    public void downloadTemplate(HttpServletResponse response) {
114
-        List<CmcGaussPositiveTemplate> templateList = new ArrayList<>();
115
-
116
-        // 添加示例数据
117
-        CmcGaussPositiveTemplate example = new CmcGaussPositiveTemplate();
118
-        example.setPointNumber("示例点1");
119
-        example.setCoordinateSystem("WGS84");
120
-        example.setLongitude(103.3734281951);
121
-        example.setLongitudePosition("E");
122
-        example.setLatitude(29.29315636);
123
-        example.setLatitudePosition("N");
124
-        example.setBand(35);
125
-        example.setBandwidth(3);
126
-        templateList.add(example);
127
-
128
-        ExcelUtil<CmcGaussPositiveTemplate> util = new ExcelUtil<CmcGaussPositiveTemplate>(CmcGaussPositiveTemplate.class);
129
-        util.exportExcel(response, templateList, "高斯正算模板");
130
-    }
131
-
132 110
 }

+ 0
- 21
oa-back/ruoyi-calculate/src/main/java/com/ruoyi/web/calculate/controller/CmcGeodeticToSpatialController.java Datei anzeigen

@@ -22,7 +22,6 @@ import com.ruoyi.common.core.domain.AjaxResult;
22 22
 import com.ruoyi.common.enums.BusinessType;
23 23
 import com.ruoyi.web.calculate.domain.CmcGeodeticToSpatial;
24 24
 import com.ruoyi.web.calculate.service.ICmcGeodeticToSpatialService;
25
-import com.ruoyi.web.calculate.vo.CmcGeodeticToSpatialTemplate;
26 25
 import com.ruoyi.common.utils.poi.ExcelUtil;
27 26
 import com.ruoyi.common.core.page.TableDataInfo;
28 27
 
@@ -107,24 +106,4 @@ public class CmcGeodeticToSpatialController extends BaseController
107 106
         List<CmcGeodeticToSpatial> result = cmcGeodeticToSpatialService.calculateGeodeticToSpatial(dataList);
108 107
         return success(result);
109 108
     }
110
-
111
-    @PostMapping("/template")
112
-    public void downloadTemplate(HttpServletResponse response) {
113
-        List<CmcGeodeticToSpatialTemplate> templateList = new ArrayList<>();
114
-
115
-        // 添加示例数据
116
-        CmcGeodeticToSpatialTemplate example = new CmcGeodeticToSpatialTemplate();
117
-        example.setPointNumber("示例点 1");
118
-        example.setCoordinateSystem("WGS84");
119
-        example.setLongitude(103.3734281951);
120
-        example.setLongitudePosition("E");
121
-        example.setLatitude(29.29315636);
122
-        example.setLatitudePosition("N");
123
-        example.setHeight(500.0);
124
-        templateList.add(example);
125
-
126
-        ExcelUtil<CmcGeodeticToSpatialTemplate> util = new ExcelUtil<CmcGeodeticToSpatialTemplate>(CmcGeodeticToSpatialTemplate.class);
127
-        util.exportExcel(response, templateList, "大地坐标转空间坐标模板");
128
-    }
129
-
130 109
 }

+ 0
- 19
oa-back/ruoyi-calculate/src/main/java/com/ruoyi/web/calculate/controller/CmcSpatialToGeodeticContoller.java Datei anzeigen

@@ -22,7 +22,6 @@ import com.ruoyi.common.core.domain.AjaxResult;
22 22
 import com.ruoyi.common.enums.BusinessType;
23 23
 import com.ruoyi.web.calculate.domain.CmcSpatialToGeodetic;
24 24
 import com.ruoyi.web.calculate.service.ICmcSpatialToGeodeticService;
25
-import com.ruoyi.web.calculate.vo.CmcSpatialToGeodeticTemplate;
26 25
 import com.ruoyi.common.utils.poi.ExcelUtil;
27 26
 import com.ruoyi.common.core.page.TableDataInfo;
28 27
 
@@ -107,22 +106,4 @@ public class CmcSpatialToGeodeticContoller extends BaseController
107 106
         List<CmcSpatialToGeodetic> result = cmcSpatialToGeodeticService.calculateSpatialToGeodetic(dataList);
108 107
         return success(result);
109 108
     }
110
-
111
-    @PostMapping("/template")
112
-    public void downloadTemplate(HttpServletResponse response) {
113
-        List<CmcSpatialToGeodeticTemplate> templateList = new ArrayList<>();
114
-
115
-        // 添加示例数据
116
-        CmcSpatialToGeodeticTemplate example = new CmcSpatialToGeodeticTemplate();
117
-        example.setPointNumber("示例点 1");
118
-        example.setCoordinateSystem("WGS84");
119
-        example.setSpatialX(-2267767.268);
120
-        example.setSpatialY(4895887.566);
121
-        example.setSpatialZ(3279474.822);
122
-        templateList.add(example);
123
-
124
-        ExcelUtil<CmcSpatialToGeodeticTemplate> util = new ExcelUtil<CmcSpatialToGeodeticTemplate>(CmcSpatialToGeodeticTemplate.class);
125
-        util.exportExcel(response, templateList, "空间转大地模板");
126
-    }
127
-
128 109
 }

+ 0
- 23
oa-back/ruoyi-calculate/src/main/java/com/ruoyi/web/calculate/controller/CmcUTMBandChangeContorller.java Datei anzeigen

@@ -19,7 +19,6 @@ import com.ruoyi.common.enums.BusinessType;
19 19
 import com.ruoyi.common.utils.poi.ExcelUtil;
20 20
 import com.ruoyi.web.calculate.domain.CmcUTMBandChange;
21 21
 import com.ruoyi.web.calculate.service.ICmcUTMBandChangeService;
22
-import com.ruoyi.web.calculate.vo.CmcUTMBandChangeTemplate;
23 22
 
24 23
 @RestController
25 24
 @RequestMapping("/calculate/utmBandChange")
@@ -93,26 +92,4 @@ public class CmcUTMBandChangeContorller {
93 92
         List<CmcUTMBandChange> result = cmcUTMBandChangeService.calculateUTMBandChange(dataList);
94 93
         return AjaxResult.success(result);
95 94
     }
96
-
97
-    @PostMapping("/template")
98
-    public void downloadTemplate(HttpServletResponse response) {
99
-        List<CmcUTMBandChangeTemplate> templateList = new ArrayList<>();
100
-
101
-        // 添加示例数据
102
-        CmcUTMBandChangeTemplate example = new CmcUTMBandChangeTemplate();
103
-        example.setPointNumber("示例点1");
104
-        example.setCoordinateSystem("WGS84");
105
-        example.setUtmX(3421806.699);
106
-        example.setUtmY(4460881.36);
107
-        example.setBand(49);
108
-        example.setNewBand(50);
109
-        example.setLongitudePosition("E");
110
-        example.setLatitudePosition("N");
111
-        templateList.add(example);
112
-
113
-        ExcelUtil<CmcUTMBandChangeTemplate> util = new ExcelUtil<CmcUTMBandChangeTemplate>(
114
-                CmcUTMBandChangeTemplate.class);
115
-        util.exportExcel(response, templateList, "UTM换带模板");
116
-    }
117
-
118 95
 }

+ 0
- 22
oa-back/ruoyi-calculate/src/main/java/com/ruoyi/web/calculate/controller/CmcUTMNegativeController.java Datei anzeigen

@@ -19,7 +19,6 @@ import com.ruoyi.common.core.domain.AjaxResult;
19 19
 import com.ruoyi.common.enums.BusinessType;
20 20
 import com.ruoyi.web.calculate.domain.CmcUTMNegative;
21 21
 import com.ruoyi.web.calculate.service.ICmcUTMNegativeService;
22
-import com.ruoyi.web.calculate.vo.CmcUTMNegativeTemplate;
23 22
 import com.ruoyi.common.utils.poi.ExcelUtil;
24 23
 
25 24
 /**
@@ -102,25 +101,4 @@ public class CmcUTMNegativeController extends BaseController
102 101
         List<CmcUTMNegative> result = cmcUTMNagativeService.calculateUTMNagative(dataList);
103 102
         return success(result);
104 103
     }
105
-
106
-    @PostMapping("/template")
107
-    public void downloadTemplate(HttpServletResponse response) {
108
-        List<CmcUTMNegativeTemplate> templateList = new ArrayList<>();
109
-
110
-        // 添加示例数据
111
-        CmcUTMNegativeTemplate example = new CmcUTMNegativeTemplate();
112
-        example.setPointNumber("示例点1");
113
-        example.setCoordinateSystem("WGS84");
114
-        example.setUtmX(500000.1234);
115
-        example.setUtmY(3248765.4321);
116
-        example.setBand(48);
117
-        example.setLongitudePosition("东");
118
-        example.setLatitudePosition("北");
119
-        
120
-        templateList.add(example); // 示例数据
121
-        
122
-        ExcelUtil<CmcUTMNegativeTemplate> util = new ExcelUtil<CmcUTMNegativeTemplate>(CmcUTMNegativeTemplate.class);
123
-        util.exportExcel(response, templateList, "UTM反算模板");
124
-    }
125
-
126 104
 }

+ 0
- 21
oa-back/ruoyi-calculate/src/main/java/com/ruoyi/web/calculate/controller/CmcUTMPositiveController.java Datei anzeigen

@@ -22,7 +22,6 @@ import com.ruoyi.common.core.domain.AjaxResult;
22 22
 import com.ruoyi.common.enums.BusinessType;
23 23
 import com.ruoyi.web.calculate.domain.CmcUTMPositive;
24 24
 import com.ruoyi.web.calculate.service.ICmcUTMPositiveService;
25
-import com.ruoyi.web.calculate.vo.CmcUTMPositiveTemplate;
26 25
 import com.ruoyi.common.utils.poi.ExcelUtil;
27 26
 import com.ruoyi.common.core.page.TableDataInfo;
28 27
 
@@ -107,24 +106,4 @@ public class CmcUTMPositiveController extends BaseController
107 106
         List<CmcUTMPositive> result = cmcUTMPositiveService.calculateUTMPositive(dataList);
108 107
         return success(result);
109 108
     }
110
-
111
-    @PostMapping("/template")
112
-    public void downloadTemplate(HttpServletResponse response) {
113
-        List<CmcUTMPositiveTemplate> templateList = new ArrayList<>();
114
-
115
-        // 添加示例数据
116
-        CmcUTMPositiveTemplate example = new CmcUTMPositiveTemplate();
117
-        example.setPointNumber("示例点1");
118
-        example.setCoordinateSystem("WGS84");
119
-        example.setLongitude(103.3734281951);
120
-        example.setLongitudePosition("E");
121
-        example.setLatitude(29.29315636);
122
-        example.setLatitudePosition("N");
123
-        example.setBand(48);
124
-        templateList.add(example);
125
-
126
-        ExcelUtil<CmcUTMPositiveTemplate> util = new ExcelUtil<CmcUTMPositiveTemplate>(CmcUTMPositiveTemplate.class);
127
-        util.exportExcel(response, templateList, "UTM正算模板");
128
-    }
129
-
130 109
 }

+ 107
- 0
oa-back/ruoyi-calculate/src/main/java/com/ruoyi/web/calculate/domain/CmcDMSToDegree.java Datei anzeigen

@@ -0,0 +1,107 @@
1
+package com.ruoyi.web.calculate.domain;
2
+
3
+import java.math.BigDecimal;
4
+import java.math.RoundingMode;
5
+
6
+import org.apache.commons.lang3.builder.ToStringBuilder;
7
+import org.apache.commons.lang3.builder.ToStringStyle;
8
+import com.ruoyi.common.annotation.Excel;
9
+import com.ruoyi.common.annotation.Excel.ColumnType;
10
+import com.ruoyi.common.core.domain.BaseEntity;
11
+
12
+/**
13
+ * 角度度分秒转换对象 cmc_degree_to_dms
14
+ * 
15
+ * @author qyx
16
+ * @date 2026-06-11
17
+ */
18
+public class CmcDMSToDegree extends BaseEntity {
19
+    private static final long serialVersionUID = 1L;
20
+
21
+    /** 主键ID */
22
+    private Long id;
23
+
24
+    /** 待求点号 */
25
+    @Excel(name = "待求点")
26
+    private String pointNumber;
27
+
28
+    /** 经度 */
29
+    @Excel(name = "经度(度分秒)", cellType = ColumnType.NUMERIC, numberFormat = "0.000000000")
30
+    private Double longitudeDMS;
31
+
32
+    /** 纬度 */
33
+    @Excel(name = "纬度(度分秒)", cellType = ColumnType.NUMERIC, numberFormat = "0.000000000")
34
+    private Double latitudeDMS;
35
+    
36
+    /** 经度 */
37
+    @Excel(name = "经度(度)", cellType = ColumnType.NUMERIC, numberFormat = "0.000000000")
38
+    private Double longitude;
39
+
40
+    /** 纬度 */
41
+    @Excel(name = "纬度(度)", cellType = ColumnType.NUMERIC, numberFormat = "0.000000000")
42
+    private Double latitude;
43
+
44
+    public void setId(Long id) {
45
+        this.id = id;
46
+    }
47
+
48
+    public Long getId() {
49
+        return id;
50
+    }
51
+
52
+    public void setPointNumber(String pointNumber) {
53
+        this.pointNumber = pointNumber;
54
+    }
55
+
56
+    public String getPointNumber() {
57
+        return pointNumber;
58
+    }
59
+
60
+    public void setLongitude(Double longitude) {
61
+        this.longitude = longitude;
62
+    }
63
+
64
+    public Double getLongitude() {
65
+        return longitude;
66
+    }
67
+
68
+    public void setLatitude(Double latitude) {
69
+        this.latitude = latitude;
70
+    }
71
+
72
+    public Double getLatitude() {
73
+        return latitude;
74
+    }
75
+
76
+    public void setLongitudeDMS(Double longitudeDMS) {
77
+        this.longitudeDMS = longitudeDMS;
78
+    }
79
+
80
+    public Double getLongitudeDMS() {
81
+        return longitudeDMS;
82
+    }
83
+
84
+    public void setLatitudeDMS(Double latitudeDMS) {
85
+        this.latitudeDMS = latitudeDMS;
86
+    }
87
+
88
+    public Double getLatitudeDMS() {
89
+        return latitudeDMS;
90
+    }
91
+
92
+    @Override
93
+    public String toString() {
94
+        return new ToStringBuilder(this, ToStringStyle.MULTI_LINE_STYLE)
95
+                .append("id", getId())
96
+                .append("pointNumber", getPointNumber())
97
+                .append("longitude", getLongitude())
98
+                .append("latitude", getLatitude())
99
+                .append("longitudeDMS", getLongitudeDMS())
100
+                .append("latitudeDMS", getLatitudeDMS())
101
+                .append("createTime", getCreateTime())
102
+                .append("createBy", getCreateBy())
103
+                .append("updateTime", getUpdateTime())
104
+                .append("updateBy", getUpdateBy())
105
+                .toString();
106
+    }
107
+}

+ 120
- 0
oa-back/ruoyi-calculate/src/main/java/com/ruoyi/web/calculate/domain/CmcDegreeToDMS.java Datei anzeigen

@@ -0,0 +1,120 @@
1
+package com.ruoyi.web.calculate.domain;
2
+
3
+import java.math.BigDecimal;
4
+import java.math.RoundingMode;
5
+
6
+import org.apache.commons.lang3.builder.ToStringBuilder;
7
+import org.apache.commons.lang3.builder.ToStringStyle;
8
+import com.ruoyi.common.annotation.Excel;
9
+import com.ruoyi.common.annotation.Excel.ColumnType;
10
+import com.ruoyi.common.core.domain.BaseEntity;
11
+
12
+/**
13
+ * 角度度分秒转换对象 cmc_degree_to_dms
14
+ * 
15
+ * @author qyx
16
+ * @date 2026-06-11
17
+ */
18
+public class CmcDegreeToDMS extends BaseEntity
19
+{
20
+    private static final long serialVersionUID = 1L;
21
+
22
+    /** 主键ID */
23
+    private Long id;
24
+
25
+    /** 待求点号 */
26
+    @Excel(name = "待求点")
27
+    private String pointNumber;
28
+
29
+    /** 经度 */
30
+    @Excel(name = "经度(度)", cellType = ColumnType.NUMERIC, numberFormat = "0.000000000")
31
+    private Double longitude;
32
+
33
+    /** 纬度 */
34
+    @Excel(name = "纬度(度)", cellType = ColumnType.NUMERIC, numberFormat = "0.000000000")
35
+    private Double latitude;
36
+
37
+    /** 经度 */
38
+    @Excel(name = "经度(度分秒)", cellType = ColumnType.NUMERIC, numberFormat = "0.000000000")
39
+    private Double longitudeDMS;
40
+
41
+    /** 纬度 */
42
+    @Excel(name = "纬度(度分秒)", cellType = ColumnType.NUMERIC, numberFormat = "0.000000000")
43
+    private Double latitudeDMS;
44
+
45
+    public void setId(Long id) 
46
+    {
47
+        this.id = id;
48
+    }
49
+
50
+    public Long getId() 
51
+    {
52
+        return id;
53
+    }
54
+    
55
+    public void setPointNumber(String pointNumber) 
56
+    {
57
+        this.pointNumber = pointNumber;
58
+    }
59
+
60
+    public String getPointNumber() 
61
+    {
62
+        return pointNumber;
63
+    }
64
+    
65
+    public void setLongitude(Double longitude) 
66
+    {
67
+        this.longitude = longitude;
68
+    }
69
+
70
+    public Double getLongitude() 
71
+    {
72
+        return longitude;
73
+    }
74
+    
75
+    public void setLatitude(Double latitude) 
76
+    {
77
+        this.latitude = latitude;
78
+    }
79
+
80
+    public Double getLatitude() 
81
+    {
82
+        return latitude;
83
+    }
84
+
85
+    public void setLongitudeDMS(Double longitudeDMS) 
86
+    {
87
+        this.longitudeDMS = longitudeDMS;
88
+    }
89
+
90
+    public Double getLongitudeDMS() 
91
+    {
92
+        return longitudeDMS;
93
+    }
94
+    
95
+    public void setLatitudeDMS(Double latitudeDMS) 
96
+    {
97
+        this.latitudeDMS = latitudeDMS;
98
+    }
99
+
100
+    public Double getLatitudeDMS() 
101
+    {
102
+        return latitudeDMS;
103
+    }
104
+
105
+    @Override
106
+    public String toString() {
107
+        return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)
108
+            .append("id", getId())
109
+            .append("pointNumber", getPointNumber())
110
+            .append("longitude", getLongitude())
111
+            .append("latitude", getLatitude())
112
+            .append("longitudeDMS", getLongitudeDMS())
113
+            .append("latitudeDMS", getLatitudeDMS())
114
+            .append("createTime", getCreateTime())
115
+            .append("createBy", getCreateBy())
116
+            .append("updateTime", getUpdateTime())
117
+            .append("updateBy", getUpdateBy())
118
+            .toString();
119
+    }
120
+}

+ 120
- 0
oa-back/ruoyi-calculate/src/main/java/com/ruoyi/web/calculate/domain/CmcDegreeToRadian.java Datei anzeigen

@@ -0,0 +1,120 @@
1
+package com.ruoyi.web.calculate.domain;
2
+
3
+import java.math.BigDecimal;
4
+import java.math.RoundingMode;
5
+
6
+import org.apache.commons.lang3.builder.ToStringBuilder;
7
+import org.apache.commons.lang3.builder.ToStringStyle;
8
+import com.ruoyi.common.annotation.Excel;
9
+import com.ruoyi.common.annotation.Excel.ColumnType;
10
+import com.ruoyi.common.core.domain.BaseEntity;
11
+
12
+/**
13
+ * 角度度转弧度对象 cmc_degree_to_radian
14
+ * 
15
+ * @author qyx
16
+ * @date 2026-06-17
17
+ */
18
+public class CmcDegreeToRadian extends BaseEntity
19
+{
20
+    private static final long serialVersionUID = 1L;
21
+
22
+    /** 主键ID */
23
+    private Long id;
24
+
25
+    /** 待求点号 */
26
+    @Excel(name = "待求点")
27
+    private String pointNumber;
28
+
29
+    /** 经度(度) */
30
+    @Excel(name = "经度(度)", cellType = ColumnType.NUMERIC, numberFormat = "0.000000000")
31
+    private Double longitude;
32
+
33
+    /** 纬度(度) */
34
+    @Excel(name = "纬度(度)", cellType = ColumnType.NUMERIC, numberFormat = "0.000000000")
35
+    private Double latitude;
36
+
37
+    /** 经度(弧度) */
38
+    @Excel(name = "经度(弧度)", cellType = ColumnType.NUMERIC, numberFormat = "0.000000000000")
39
+    private Double longitudeRadian;
40
+
41
+    /** 纬度(弧度) */
42
+    @Excel(name = "纬度(弧度)", cellType = ColumnType.NUMERIC, numberFormat = "0.000000000000")
43
+    private Double latitudeRadian;
44
+
45
+    public void setId(Long id) 
46
+    {
47
+        this.id = id;
48
+    }
49
+
50
+    public Long getId() 
51
+    {
52
+        return id;
53
+    }
54
+    
55
+    public void setPointNumber(String pointNumber) 
56
+    {
57
+        this.pointNumber = pointNumber;
58
+    }
59
+
60
+    public String getPointNumber() 
61
+    {
62
+        return pointNumber;
63
+    }
64
+    
65
+    public void setLongitude(Double longitude) 
66
+    {
67
+        this.longitude = longitude;
68
+    }
69
+
70
+    public Double getLongitude() 
71
+    {
72
+        return longitude;
73
+    }
74
+    
75
+    public void setLatitude(Double latitude) 
76
+    {
77
+        this.latitude = latitude;
78
+    }
79
+
80
+    public Double getLatitude() 
81
+    {
82
+        return latitude;
83
+    }
84
+
85
+    public void setLongitudeRadian(Double longitudeRadian) 
86
+    {
87
+        this.longitudeRadian = longitudeRadian;
88
+    }
89
+
90
+    public Double getLongitudeRadian() 
91
+    {
92
+        return longitudeRadian;
93
+    }
94
+    
95
+    public void setLatitudeRadian(Double latitudeRadian) 
96
+    {
97
+        this.latitudeRadian = latitudeRadian;
98
+    }
99
+
100
+    public Double getLatitudeRadian() 
101
+    {
102
+        return latitudeRadian;
103
+    }
104
+
105
+    @Override
106
+    public String toString() {
107
+        return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)
108
+            .append("id", getId())
109
+            .append("pointNumber", getPointNumber())
110
+            .append("longitude", getLongitude())
111
+            .append("latitude", getLatitude())
112
+            .append("longitudeRadian", getLongitudeRadian())
113
+            .append("latitudeRadian", getLatitudeRadian())
114
+            .append("createTime", getCreateTime())
115
+            .append("createBy", getCreateBy())
116
+            .append("updateTime", getUpdateTime())
117
+            .append("updateBy", getUpdateBy())
118
+            .toString();
119
+    }
120
+} 

+ 32
- 0
oa-back/ruoyi-calculate/src/main/java/com/ruoyi/web/calculate/service/ICmcDMSToDegreeService.java Datei anzeigen

@@ -0,0 +1,32 @@
1
+package com.ruoyi.web.calculate.service;
2
+
3
+import java.util.List;
4
+
5
+import org.springframework.web.multipart.MultipartFile;
6
+
7
+import com.ruoyi.common.core.domain.AjaxResult;
8
+import com.ruoyi.web.calculate.domain.CmcDMSToDegree;
9
+
10
+/**
11
+ * 度分秒转度服务接口
12
+ * 
13
+ * @author qyx
14
+ * @date 2026-06-12
15
+ */
16
+public interface ICmcDMSToDegreeService {
17
+    /**
18
+     * 导入Excel数据
19
+     * 
20
+     * @param file Excel文件
21
+     * @return 导入结果
22
+     */
23
+    public AjaxResult importExcelData(MultipartFile file);
24
+
25
+    /**
26
+     * 执行度分秒转度计算
27
+     * 
28
+     * @param dataList 计算参数列表
29
+     * @return 计算结果
30
+     */
31
+    public List<CmcDMSToDegree> calculateDMSToDegree(List<CmcDMSToDegree> dataList);
32
+}

+ 32
- 0
oa-back/ruoyi-calculate/src/main/java/com/ruoyi/web/calculate/service/ICmcDegreeToDMSService.java Datei anzeigen

@@ -0,0 +1,32 @@
1
+package com.ruoyi.web.calculate.service;
2
+
3
+import java.util.List;
4
+
5
+import org.springframework.web.multipart.MultipartFile;
6
+
7
+import com.ruoyi.common.core.domain.AjaxResult;
8
+import com.ruoyi.web.calculate.domain.CmcDegreeToDMS;
9
+
10
+/**
11
+ * 角度度分秒转换服务接口
12
+ * 
13
+ * @author qyx
14
+ * @date 2026-06-12
15
+ */
16
+public interface ICmcDegreeToDMSService {
17
+    /**
18
+     * 导入Excel数据
19
+     * 
20
+     * @param file Excel文件
21
+     * @return 导入结果
22
+     */
23
+    public AjaxResult importExcelData(MultipartFile file);
24
+
25
+    /**
26
+     * 执行角度度分秒转换计算
27
+     * 
28
+     * @param dataList 计算参数列表
29
+     * @return 计算结果
30
+     */
31
+    public List<CmcDegreeToDMS> calculateDegreeToDMS(List<CmcDegreeToDMS> dataList);
32
+}

+ 32
- 0
oa-back/ruoyi-calculate/src/main/java/com/ruoyi/web/calculate/service/ICmcDegreeToRadianService.java Datei anzeigen

@@ -0,0 +1,32 @@
1
+package com.ruoyi.web.calculate.service;
2
+
3
+import java.util.List;
4
+
5
+import org.springframework.web.multipart.MultipartFile;
6
+
7
+import com.ruoyi.common.core.domain.AjaxResult;
8
+import com.ruoyi.web.calculate.domain.CmcDegreeToRadian;
9
+
10
+/**
11
+ * 角度度弧度服务接口
12
+ * 
13
+ * @author qyx
14
+ * @date 2026-06-17
15
+ */
16
+public interface ICmcDegreeToRadianService {
17
+    /**
18
+     * 导入Excel数据
19
+     * 
20
+     * @param file Excel文件
21
+     * @return 导入结果
22
+     */
23
+    public AjaxResult importExcelData(MultipartFile file);
24
+
25
+    /**
26
+     * 执行角度转弧度计算
27
+     * 
28
+     * @param dataList 计算参数列表
29
+     * @return 计算结果
30
+     */
31
+    public List<CmcDegreeToRadian> calculateDegreeToRadian(List<CmcDegreeToRadian> dataList);
32
+}

+ 281
- 0
oa-back/ruoyi-calculate/src/main/java/com/ruoyi/web/calculate/service/impl/CmcDMSToDegreeServiceImpl.java Datei anzeigen

@@ -0,0 +1,281 @@
1
+package com.ruoyi.web.calculate.service.impl;
2
+
3
+import java.math.BigDecimal;
4
+import java.math.RoundingMode;
5
+import java.util.ArrayList;
6
+import java.util.HashMap;
7
+import java.util.List;
8
+import java.util.Map;
9
+
10
+import javax.servlet.http.HttpServletResponse;
11
+
12
+import org.springframework.beans.factory.annotation.Autowired;
13
+import org.springframework.stereotype.Service;
14
+import org.springframework.transaction.annotation.Transactional;
15
+import org.springframework.web.multipart.MultipartFile;
16
+
17
+import com.ruoyi.common.core.domain.AjaxResult;
18
+import com.ruoyi.common.utils.poi.ExcelUtil;
19
+import com.ruoyi.web.calculate.domain.CmcDMSToDegree;
20
+import com.ruoyi.web.calculate.service.ICmcDMSToDegreeService;
21
+import org.apache.poi.ss.usermodel.Cell;
22
+import org.apache.poi.ss.usermodel.CellType;
23
+import org.apache.poi.ss.usermodel.DateUtil;
24
+import org.apache.poi.ss.usermodel.Row;
25
+import org.apache.poi.ss.usermodel.Sheet;
26
+import org.apache.poi.ss.usermodel.Workbook;
27
+import org.apache.poi.ss.usermodel.WorkbookFactory;
28
+
29
+/**
30
+ * 度分秒转度服务实现
31
+ * 
32
+ * @author ruoyi
33
+ * @date 2026-06-12
34
+ */
35
+@Service
36
+public class CmcDMSToDegreeServiceImpl implements ICmcDMSToDegreeService {
37
+
38
+    /**
39
+     * 导入Excel数据
40
+     */
41
+    @Override
42
+    @Transactional(rollbackFor = Exception.class)
43
+    public AjaxResult importExcelData(MultipartFile file) {
44
+        try {
45
+            // 获取Excel表头信息
46
+            List<String> excelColumns = getExcelColumns(file);
47
+
48
+            // 返回原始Excel数据格式(使用表头作为key)
49
+            List<Map<String, Object>> rawData = readRawExcelData(file, excelColumns);
50
+
51
+            // 检查是否有数据
52
+            if (rawData == null || rawData.isEmpty()) {
53
+                return AjaxResult.error("Excel文件中没有数据!");
54
+            }
55
+
56
+            // 构建返回结果
57
+            Map<String, Object> result = new HashMap<>();
58
+            result.put("total", rawData.size());
59
+            result.put("validCount", rawData.size());
60
+            result.put("data", rawData);
61
+            result.put("columns", excelColumns); // 返回Excel表头
62
+
63
+            String message = String.format("导入成功!共解析%d条数据", rawData.size());
64
+            return AjaxResult.success(message, result);
65
+
66
+        } catch (Exception e) {
67
+            e.printStackTrace();
68
+            return AjaxResult.error("导入失败:" + e.getMessage());
69
+        }
70
+    }
71
+    
72
+    /**
73
+     * 获取Excel表头信息
74
+     */
75
+    private List<String> getExcelColumns(MultipartFile file) {
76
+        List<String> columns = new ArrayList<>();
77
+        try {
78
+            Workbook workbook = WorkbookFactory.create(file.getInputStream());
79
+            Sheet sheet = workbook.getSheetAt(0);
80
+            Row headerRow = sheet.getRow(0); // 第一行为表头
81
+            
82
+            if (headerRow != null) {
83
+                int lastCellNum = headerRow.getLastCellNum();
84
+                for (int i = 0; i < lastCellNum; i++) {
85
+                    Cell cell = headerRow.getCell(i);
86
+                    String cellValue = null;
87
+                    if (cell != null) {
88
+                        cellValue = getCellStringValue(cell);
89
+                    }
90
+                    if (cellValue != null && !cellValue.trim().isEmpty()) {
91
+                        columns.add(cellValue.trim());
92
+                    } else {
93
+                        columns.add("列" + (i + 1));
94
+                    }
95
+                }
96
+            }
97
+            workbook.close();
98
+        } catch (Exception e) {
99
+            e.printStackTrace();
100
+        }
101
+        return columns;
102
+    }
103
+    
104
+    /**
105
+     * 获取单元格的字符串值(处理多种类型)
106
+     */
107
+    private String getCellStringValue(Cell cell) {
108
+        if (cell == null) {
109
+            return null;
110
+        }
111
+        switch (cell.getCellType()) {
112
+            case STRING:
113
+                return cell.getStringCellValue();
114
+            case NUMERIC:
115
+                if (DateUtil.isCellDateFormatted(cell)) {
116
+                    return cell.getLocalDateTimeCellValue().toString();
117
+                } else {
118
+                    double value = cell.getNumericCellValue();
119
+                    if (value == Math.floor(value)) {
120
+                        return String.valueOf((long) value);
121
+                    }
122
+                    return String.valueOf(value);
123
+                }
124
+            case BOOLEAN:
125
+                return String.valueOf(cell.getBooleanCellValue());
126
+            case FORMULA:
127
+                try {
128
+                    return cell.getStringCellValue();
129
+                } catch (Exception e) {
130
+                    try {
131
+                        return String.valueOf(cell.getNumericCellValue());
132
+                    } catch (Exception ex) {
133
+                        return cell.getCellFormula();
134
+                    }
135
+                }
136
+            default:
137
+                return null;
138
+        }
139
+    }
140
+    
141
+    /**
142
+     * 读取原始Excel数据(使用表头作为key)
143
+     */
144
+    private List<Map<String, Object>> readRawExcelData(MultipartFile file, List<String> columns) {
145
+        List<Map<String, Object>> dataList = new ArrayList<>();
146
+        try {
147
+            Workbook workbook = WorkbookFactory.create(file.getInputStream());
148
+            Sheet sheet = workbook.getSheetAt(0);
149
+            int rows = sheet.getLastRowNum();
150
+            
151
+            // 从第二行开始读取数据(第一行为表头)
152
+            for (int i = 1; i <= rows; i++) {
153
+                Row row = sheet.getRow(i);
154
+                if (row == null || isRowEmpty(row)) {
155
+                    continue;
156
+                }
157
+                
158
+                Map<String, Object> rowMap = new HashMap<>();
159
+                int lastCellNum = Math.min(row.getLastCellNum(), columns.size());
160
+                for (int j = 0; j < lastCellNum; j++) {
161
+                    Cell cell = row.getCell(j);
162
+                    Object value = getCellValue(cell);
163
+                    rowMap.put(columns.get(j), value);
164
+                }
165
+                dataList.add(rowMap);
166
+            }
167
+            workbook.close();
168
+        } catch (Exception e) {
169
+            e.printStackTrace();
170
+        }
171
+        return dataList;
172
+    }
173
+    
174
+    /**
175
+     * 判断行是否为空
176
+     */
177
+    private boolean isRowEmpty(Row row) {
178
+        if (row == null) {
179
+            return true;
180
+        }
181
+        for (int i = row.getFirstCellNum(); i < row.getLastCellNum(); i++) {
182
+            Cell cell = row.getCell(i);
183
+            if (cell != null && cell.getCellType() != CellType.BLANK) {
184
+                // 使用getCellValue方法处理多种类型,避免数字类型调用getStringCellValue抛出异常
185
+                Object value = getCellValue(cell);
186
+                if (value != null) {
187
+                    String strValue = String.valueOf(value).trim();
188
+                    if (!strValue.isEmpty()) {
189
+                        return false;
190
+                    }
191
+                }
192
+            }
193
+        }
194
+        return true;
195
+    }
196
+    
197
+    /**
198
+     * 获取单元格值
199
+     */
200
+    private Object getCellValue(Cell cell) {
201
+        if (cell == null) {
202
+            return null;
203
+        }
204
+        switch (cell.getCellType()) {
205
+            case STRING:
206
+                return cell.getStringCellValue();
207
+            case NUMERIC:
208
+                if (DateUtil.isCellDateFormatted(cell)) {
209
+                    return cell.getLocalDateTimeCellValue();
210
+                } else {
211
+                    // 避免科学计数法
212
+                    double value = cell.getNumericCellValue();
213
+                    if (value == Math.floor(value)) {
214
+                        return (long) value;
215
+                    }
216
+                    return value;
217
+                }
218
+            case BOOLEAN:
219
+                return cell.getBooleanCellValue();
220
+            case FORMULA:
221
+                try {
222
+                    return cell.getStringCellValue();
223
+                } catch (Exception e) {
224
+                    return cell.getNumericCellValue();
225
+                }
226
+            default:
227
+                return null;
228
+        }
229
+    }
230
+
231
+    /**
232
+     * 执行度分秒转度计算
233
+     */
234
+    @Override
235
+    public List<CmcDMSToDegree> calculateDMSToDegree(List<CmcDMSToDegree> dataList) {
236
+        List<CmcDMSToDegree> resultList = new ArrayList<>();
237
+        for (CmcDMSToDegree item : dataList) {
238
+            try {
239
+                CmcDMSToDegree result = calculateDMSToDegreeSingle(item);
240
+                resultList.add(result);
241
+            } catch (Exception e) {
242
+                e.printStackTrace();
243
+                // 计算失败时,返回带有错误信息的对象
244
+                CmcDMSToDegree errorResult = new CmcDMSToDegree();
245
+                errorResult.setPointNumber(item.getPointNumber());
246
+                errorResult.setLongitude(-1.0);
247
+                errorResult.setLatitude(-1.0);
248
+                resultList.add(errorResult);
249
+            }
250
+        }
251
+
252
+        return resultList;
253
+    }
254
+
255
+    /**
256
+     * 单条度分秒转度计算(待实现)
257
+     */
258
+    private CmcDMSToDegree calculateDMSToDegreeSingle(CmcDMSToDegree cmcDMSToDegree) {
259
+        double longitudeDegrees = cmcDMSToDegree.getLongitudeDMS();
260
+        double latitudeDegrees = cmcDMSToDegree.getLatitudeDMS();
261
+
262
+        // 度的整数部分
263
+        double longitudeDegreesInt = Math.floor(longitudeDegrees + 1.0E-12);
264
+        // 分的计算
265
+        double minutes = Math.floor(100 * (longitudeDegrees - longitudeDegreesInt) + 1.0E-12) / 60;
266
+        // 秒的计算
267
+        double seconds = (longitudeDegrees * 100 - Math.floor(longitudeDegrees * 100 + 1.0E-12)) / 36;
268
+
269
+        double longitude = longitudeDegreesInt + minutes + seconds;
270
+        cmcDMSToDegree.setLongitude(longitude);
271
+
272
+        // 纬度的整数部分
273
+        double latitudeDegreesInt = Math.floor(latitudeDegrees + 1.0E-12);
274
+        minutes = Math.floor(100 * (latitudeDegrees - latitudeDegreesInt) + 1.0E-12) / 60;
275
+        seconds = (latitudeDegrees * 100 - Math.floor(latitudeDegrees * 100 + 1.0E-12)) / 36;
276
+        double latitude = latitudeDegreesInt + minutes + seconds;
277
+        cmcDMSToDegree.setLatitude(latitude);
278
+        
279
+        return cmcDMSToDegree;
280
+    }
281
+}

+ 280
- 0
oa-back/ruoyi-calculate/src/main/java/com/ruoyi/web/calculate/service/impl/CmcDegreeToDMSServiceImpl.java Datei anzeigen

@@ -0,0 +1,280 @@
1
+package com.ruoyi.web.calculate.service.impl;
2
+
3
+import java.math.BigDecimal;
4
+import java.math.RoundingMode;
5
+import java.util.ArrayList;
6
+import java.util.HashMap;
7
+import java.util.List;
8
+import java.util.Map;
9
+
10
+import javax.servlet.http.HttpServletResponse;
11
+
12
+import org.springframework.beans.factory.annotation.Autowired;
13
+import org.springframework.stereotype.Service;
14
+import org.springframework.transaction.annotation.Transactional;
15
+import org.springframework.web.multipart.MultipartFile;
16
+
17
+import com.ruoyi.common.core.domain.AjaxResult;
18
+import com.ruoyi.common.utils.poi.ExcelUtil;
19
+import com.ruoyi.web.calculate.domain.CmcDegreeToDMS;
20
+import com.ruoyi.web.calculate.service.ICmcDegreeToDMSService;
21
+import org.apache.poi.ss.usermodel.Cell;
22
+import org.apache.poi.ss.usermodel.CellType;
23
+import org.apache.poi.ss.usermodel.DateUtil;
24
+import org.apache.poi.ss.usermodel.Row;
25
+import org.apache.poi.ss.usermodel.Sheet;
26
+import org.apache.poi.ss.usermodel.Workbook;
27
+import org.apache.poi.ss.usermodel.WorkbookFactory;
28
+
29
+/**
30
+ * 角度度分秒转换服务实现
31
+ * 
32
+ * @author ruoyi
33
+ * @date 2026-06-12
34
+ */
35
+@Service
36
+public class CmcDegreeToDMSServiceImpl implements ICmcDegreeToDMSService {
37
+
38
+    /**
39
+     * 导入Excel数据
40
+     */
41
+    @Override
42
+    @Transactional(rollbackFor = Exception.class)
43
+    public AjaxResult importExcelData(MultipartFile file) {
44
+        try {
45
+            // 获取Excel表头信息
46
+            List<String> excelColumns = getExcelColumns(file);
47
+
48
+            // 返回原始Excel数据格式(使用表头作为key)
49
+            List<Map<String, Object>> rawData = readRawExcelData(file, excelColumns);
50
+
51
+            // 检查是否有数据
52
+            if (rawData == null || rawData.isEmpty()) {
53
+                return AjaxResult.error("Excel文件中没有数据!");
54
+            }
55
+
56
+            // 构建返回结果
57
+            Map<String, Object> result = new HashMap<>();
58
+            result.put("total", rawData.size());
59
+            result.put("validCount", rawData.size());
60
+            result.put("data", rawData);
61
+            result.put("columns", excelColumns); // 返回Excel表头
62
+
63
+            String message = String.format("导入成功!共解析%d条数据", rawData.size());
64
+            return AjaxResult.success(message, result);
65
+
66
+        } catch (Exception e) {
67
+            e.printStackTrace();
68
+            return AjaxResult.error("导入失败:" + e.getMessage());
69
+        }
70
+    }
71
+    
72
+    /**
73
+     * 获取Excel表头信息
74
+     */
75
+    private List<String> getExcelColumns(MultipartFile file) {
76
+        List<String> columns = new ArrayList<>();
77
+        try {
78
+            Workbook workbook = WorkbookFactory.create(file.getInputStream());
79
+            Sheet sheet = workbook.getSheetAt(0);
80
+            Row headerRow = sheet.getRow(0); // 第一行为表头
81
+            
82
+            if (headerRow != null) {
83
+                int lastCellNum = headerRow.getLastCellNum();
84
+                for (int i = 0; i < lastCellNum; i++) {
85
+                    Cell cell = headerRow.getCell(i);
86
+                    String cellValue = null;
87
+                    if (cell != null) {
88
+                        cellValue = getCellStringValue(cell);
89
+                    }
90
+                    if (cellValue != null && !cellValue.trim().isEmpty()) {
91
+                        columns.add(cellValue.trim());
92
+                    } else {
93
+                        columns.add("列" + (i + 1));
94
+                    }
95
+                }
96
+            }
97
+            workbook.close();
98
+        } catch (Exception e) {
99
+            e.printStackTrace();
100
+        }
101
+        return columns;
102
+    }
103
+    
104
+    /**
105
+     * 获取单元格的字符串值(处理多种类型)
106
+     */
107
+    private String getCellStringValue(Cell cell) {
108
+        if (cell == null) {
109
+            return null;
110
+        }
111
+        switch (cell.getCellType()) {
112
+            case STRING:
113
+                return cell.getStringCellValue();
114
+            case NUMERIC:
115
+                if (DateUtil.isCellDateFormatted(cell)) {
116
+                    return cell.getLocalDateTimeCellValue().toString();
117
+                } else {
118
+                    double value = cell.getNumericCellValue();
119
+                    if (value == Math.floor(value)) {
120
+                        return String.valueOf((long) value);
121
+                    }
122
+                    return String.valueOf(value);
123
+                }
124
+            case BOOLEAN:
125
+                return String.valueOf(cell.getBooleanCellValue());
126
+            case FORMULA:
127
+                try {
128
+                    return cell.getStringCellValue();
129
+                } catch (Exception e) {
130
+                    try {
131
+                        return String.valueOf(cell.getNumericCellValue());
132
+                    } catch (Exception ex) {
133
+                        return cell.getCellFormula();
134
+                    }
135
+                }
136
+            default:
137
+                return null;
138
+        }
139
+    }
140
+    
141
+    /**
142
+     * 读取原始Excel数据(使用表头作为key)
143
+     */
144
+    private List<Map<String, Object>> readRawExcelData(MultipartFile file, List<String> columns) {
145
+        List<Map<String, Object>> dataList = new ArrayList<>();
146
+        try {
147
+            Workbook workbook = WorkbookFactory.create(file.getInputStream());
148
+            Sheet sheet = workbook.getSheetAt(0);
149
+            int rows = sheet.getLastRowNum();
150
+            
151
+            // 从第二行开始读取数据(第一行为表头)
152
+            for (int i = 1; i <= rows; i++) {
153
+                Row row = sheet.getRow(i);
154
+                if (row == null || isRowEmpty(row)) {
155
+                    continue;
156
+                }
157
+                
158
+                Map<String, Object> rowMap = new HashMap<>();
159
+                int lastCellNum = Math.min(row.getLastCellNum(), columns.size());
160
+                for (int j = 0; j < lastCellNum; j++) {
161
+                    Cell cell = row.getCell(j);
162
+                    Object value = getCellValue(cell);
163
+                    rowMap.put(columns.get(j), value);
164
+                }
165
+                dataList.add(rowMap);
166
+            }
167
+            workbook.close();
168
+        } catch (Exception e) {
169
+            e.printStackTrace();
170
+        }
171
+        return dataList;
172
+    }
173
+    
174
+    /**
175
+     * 判断行是否为空
176
+     */
177
+    private boolean isRowEmpty(Row row) {
178
+        if (row == null) {
179
+            return true;
180
+        }
181
+        for (int i = row.getFirstCellNum(); i < row.getLastCellNum(); i++) {
182
+            Cell cell = row.getCell(i);
183
+            if (cell != null && cell.getCellType() != CellType.BLANK) {
184
+                // 使用getCellValue方法处理多种类型,避免数字类型调用getStringCellValue抛出异常
185
+                Object value = getCellValue(cell);
186
+                if (value != null) {
187
+                    String strValue = String.valueOf(value).trim();
188
+                    if (!strValue.isEmpty()) {
189
+                        return false;
190
+                    }
191
+                }
192
+            }
193
+        }
194
+        return true;
195
+    }
196
+    
197
+    /**
198
+     * 获取单元格值
199
+     */
200
+    private Object getCellValue(Cell cell) {
201
+        if (cell == null) {
202
+            return null;
203
+        }
204
+        switch (cell.getCellType()) {
205
+            case STRING:
206
+                return cell.getStringCellValue();
207
+            case NUMERIC:
208
+                if (DateUtil.isCellDateFormatted(cell)) {
209
+                    return cell.getLocalDateTimeCellValue();
210
+                } else {
211
+                    // 避免科学计数法
212
+                    double value = cell.getNumericCellValue();
213
+                    if (value == Math.floor(value)) {
214
+                        return (long) value;
215
+                    }
216
+                    return value;
217
+                }
218
+            case BOOLEAN:
219
+                return cell.getBooleanCellValue();
220
+            case FORMULA:
221
+                try {
222
+                    return cell.getStringCellValue();
223
+                } catch (Exception e) {
224
+                    return cell.getNumericCellValue();
225
+                }
226
+            default:
227
+                return null;
228
+        }
229
+    }
230
+
231
+    /**
232
+     * 执行角度度分秒转换计算
233
+     */
234
+    @Override
235
+    public List<CmcDegreeToDMS> calculateDegreeToDMS(List<CmcDegreeToDMS> dataList) {
236
+        List<CmcDegreeToDMS> resultList = new ArrayList<>();
237
+        for (CmcDegreeToDMS item : dataList) {
238
+            try {
239
+                CmcDegreeToDMS result = calculateDegreeToDMSSingle(item);
240
+                resultList.add(result);
241
+            } catch (Exception e) {
242
+                e.printStackTrace();
243
+                // 计算失败时,返回带有错误信息的对象
244
+                CmcDegreeToDMS errorResult = new CmcDegreeToDMS();
245
+                errorResult.setPointNumber(item.getPointNumber());
246
+                errorResult.setLongitudeDMS(-1.0);
247
+                errorResult.setLatitudeDMS(-1.0);
248
+                resultList.add(errorResult);
249
+            }
250
+        }
251
+
252
+        return resultList;
253
+    }
254
+
255
+    /**
256
+     * 单条角度度分秒转换计算
257
+     */
258
+    private CmcDegreeToDMS calculateDegreeToDMSSingle(CmcDegreeToDMS cmcDegreeToDMS) {
259
+        
260
+        double longitude = cmcDegreeToDMS.getLongitude();
261
+        double latitude = cmcDegreeToDMS.getLatitude();
262
+
263
+        // 1. 提取度
264
+        double degrees = Math.floor(longitude + 1.0E-13);
265
+        // 2. 提取分
266
+        double minutesRaw = 60 * (longitude - degrees);
267
+        double minutes = Math.floor(minutesRaw + 1.0E-13) / 100;
268
+        // 3. 提取秒
269
+        double secondsRaw = 60 * (60 * longitude - Math.floor(60 * longitude + 1.0E-13)) + 1.0E-13;
270
+        double seconds = secondsRaw / 10000;
271
+        cmcDegreeToDMS.setLongitudeDMS(degrees + minutes + seconds);
272
+
273
+        double latitudeDegrees = Math.floor(latitude + 1.0E-13);
274
+        double latitudeMinutes = Math.floor(60 * (latitude - latitudeDegrees) + 1.0E-13) / 100;
275
+        double latitudeSeconds = 60 * (60 * (latitude - latitudeDegrees) - Math.floor(60 * (latitude - latitudeDegrees) + 1.0E-13)) + 1.0E-13;
276
+        latitudeSeconds = latitudeSeconds / 10000;
277
+        cmcDegreeToDMS.setLatitudeDMS(latitudeDegrees + latitudeMinutes + latitudeSeconds);
278
+        return cmcDegreeToDMS;
279
+    }
280
+}

+ 271
- 0
oa-back/ruoyi-calculate/src/main/java/com/ruoyi/web/calculate/service/impl/CmcDegreeToRadianServiceImpl.java Datei anzeigen

@@ -0,0 +1,271 @@
1
+package com.ruoyi.web.calculate.service.impl;
2
+
3
+import java.math.BigDecimal;
4
+import java.math.RoundingMode;
5
+import java.util.ArrayList;
6
+import java.util.HashMap;
7
+import java.util.List;
8
+import java.util.Map;
9
+
10
+import javax.servlet.http.HttpServletResponse;
11
+
12
+import org.springframework.beans.factory.annotation.Autowired;
13
+import org.springframework.stereotype.Service;
14
+import org.springframework.transaction.annotation.Transactional;
15
+import org.springframework.web.multipart.MultipartFile;
16
+
17
+import com.ruoyi.common.core.domain.AjaxResult;
18
+import com.ruoyi.common.utils.poi.ExcelUtil;
19
+import com.ruoyi.web.calculate.domain.CmcDegreeToRadian;
20
+import com.ruoyi.web.calculate.service.ICmcDegreeToRadianService;
21
+import org.apache.poi.ss.usermodel.Cell;
22
+import org.apache.poi.ss.usermodel.CellType;
23
+import org.apache.poi.ss.usermodel.DateUtil;
24
+import org.apache.poi.ss.usermodel.Row;
25
+import org.apache.poi.ss.usermodel.Sheet;
26
+import org.apache.poi.ss.usermodel.Workbook;
27
+import org.apache.poi.ss.usermodel.WorkbookFactory;
28
+
29
+/**
30
+ * 角度度转弧度服务实现
31
+ * 
32
+ * @author ruoyi
33
+ * @date 2026-06-17
34
+ */
35
+@Service
36
+public class CmcDegreeToRadianServiceImpl implements ICmcDegreeToRadianService {
37
+
38
+    /**
39
+     * 导入Excel数据
40
+     */
41
+    @Override
42
+    @Transactional(rollbackFor = Exception.class)
43
+    public AjaxResult importExcelData(MultipartFile file) {
44
+        try {
45
+            // 获取Excel表头信息
46
+            List<String> excelColumns = getExcelColumns(file);
47
+
48
+            // 返回原始Excel数据格式(使用表头作为key)
49
+            List<Map<String, Object>> rawData = readRawExcelData(file, excelColumns);
50
+
51
+            // 检查是否有数据
52
+            if (rawData == null || rawData.isEmpty()) {
53
+                return AjaxResult.error("Excel文件中没有数据!");
54
+            }
55
+
56
+            // 构建返回结果
57
+            Map<String, Object> result = new HashMap<>();
58
+            result.put("total", rawData.size());
59
+            result.put("validCount", rawData.size());
60
+            result.put("data", rawData);
61
+            result.put("columns", excelColumns); // 返回Excel表头
62
+
63
+            String message = String.format("导入成功!共解析%d条数据", rawData.size());
64
+            return AjaxResult.success(message, result);
65
+
66
+        } catch (Exception e) {
67
+            e.printStackTrace();
68
+            return AjaxResult.error("导入失败:" + e.getMessage());
69
+        }
70
+    }
71
+    
72
+    /**
73
+     * 获取Excel表头信息
74
+     */
75
+    private List<String> getExcelColumns(MultipartFile file) {
76
+        List<String> columns = new ArrayList<>();
77
+        try {
78
+            Workbook workbook = WorkbookFactory.create(file.getInputStream());
79
+            Sheet sheet = workbook.getSheetAt(0);
80
+            Row headerRow = sheet.getRow(0); // 第一行为表头
81
+            
82
+            if (headerRow != null) {
83
+                int lastCellNum = headerRow.getLastCellNum();
84
+                for (int i = 0; i < lastCellNum; i++) {
85
+                    Cell cell = headerRow.getCell(i);
86
+                    String cellValue = null;
87
+                    if (cell != null) {
88
+                        cellValue = getCellStringValue(cell);
89
+                    }
90
+                    if (cellValue != null && !cellValue.trim().isEmpty()) {
91
+                        columns.add(cellValue.trim());
92
+                    } else {
93
+                        columns.add("列" + (i + 1));
94
+                    }
95
+                }
96
+            }
97
+            workbook.close();
98
+        } catch (Exception e) {
99
+            e.printStackTrace();
100
+        }
101
+        return columns;
102
+    }
103
+    
104
+    /**
105
+     * 获取单元格的字符串值(处理多种类型)
106
+     */
107
+    private String getCellStringValue(Cell cell) {
108
+        if (cell == null) {
109
+            return null;
110
+        }
111
+        switch (cell.getCellType()) {
112
+            case STRING:
113
+                return cell.getStringCellValue();
114
+            case NUMERIC:
115
+                if (DateUtil.isCellDateFormatted(cell)) {
116
+                    return cell.getLocalDateTimeCellValue().toString();
117
+                } else {
118
+                    double value = cell.getNumericCellValue();
119
+                    if (value == Math.floor(value)) {
120
+                        return String.valueOf((long) value);
121
+                    }
122
+                    return String.valueOf(value);
123
+                }
124
+            case BOOLEAN:
125
+                return String.valueOf(cell.getBooleanCellValue());
126
+            case FORMULA:
127
+                try {
128
+                    return cell.getStringCellValue();
129
+                } catch (Exception e) {
130
+                    try {
131
+                        return String.valueOf(cell.getNumericCellValue());
132
+                    } catch (Exception ex) {
133
+                        return cell.getCellFormula();
134
+                    }
135
+                }
136
+            default:
137
+                return null;
138
+        }
139
+    }
140
+    
141
+    /**
142
+     * 读取原始Excel数据(使用表头作为key)
143
+     */
144
+    private List<Map<String, Object>> readRawExcelData(MultipartFile file, List<String> columns) {
145
+        List<Map<String, Object>> dataList = new ArrayList<>();
146
+        try {
147
+            Workbook workbook = WorkbookFactory.create(file.getInputStream());
148
+            Sheet sheet = workbook.getSheetAt(0);
149
+            int rows = sheet.getLastRowNum();
150
+            
151
+            // 从第二行开始读取数据(第一行为表头)
152
+            for (int i = 1; i <= rows; i++) {
153
+                Row row = sheet.getRow(i);
154
+                if (row == null || isRowEmpty(row)) {
155
+                    continue;
156
+                }
157
+                
158
+                Map<String, Object> rowMap = new HashMap<>();
159
+                int lastCellNum = Math.min(row.getLastCellNum(), columns.size());
160
+                for (int j = 0; j < lastCellNum; j++) {
161
+                    Cell cell = row.getCell(j);
162
+                    Object value = getCellValue(cell);
163
+                    rowMap.put(columns.get(j), value);
164
+                }
165
+                dataList.add(rowMap);
166
+            }
167
+            workbook.close();
168
+        } catch (Exception e) {
169
+            e.printStackTrace();
170
+        }
171
+        return dataList;
172
+    }
173
+    
174
+    /**
175
+     * 判断行是否为空
176
+     */
177
+    private boolean isRowEmpty(Row row) {
178
+        if (row == null) {
179
+            return true;
180
+        }
181
+        for (int i = row.getFirstCellNum(); i < row.getLastCellNum(); i++) {
182
+            Cell cell = row.getCell(i);
183
+            if (cell != null && cell.getCellType() != CellType.BLANK) {
184
+                // 使用getCellValue方法处理多种类型,避免数字类型调用getStringCellValue抛出异常
185
+                Object value = getCellValue(cell);
186
+                if (value != null) {
187
+                    String strValue = String.valueOf(value).trim();
188
+                    if (!strValue.isEmpty()) {
189
+                        return false;
190
+                    }
191
+                }
192
+            }
193
+        }
194
+        return true;
195
+    }
196
+    
197
+    /**
198
+     * 获取单元格值
199
+     */
200
+    private Object getCellValue(Cell cell) {
201
+        if (cell == null) {
202
+            return null;
203
+        }
204
+        switch (cell.getCellType()) {
205
+            case STRING:
206
+                return cell.getStringCellValue();
207
+            case NUMERIC:
208
+                if (DateUtil.isCellDateFormatted(cell)) {
209
+                    return cell.getLocalDateTimeCellValue();
210
+                } else {
211
+                    // 避免科学计数法
212
+                    double value = cell.getNumericCellValue();
213
+                    if (value == Math.floor(value)) {
214
+                        return (long) value;
215
+                    }
216
+                    return value;
217
+                }
218
+            case BOOLEAN:
219
+                return cell.getBooleanCellValue();
220
+            case FORMULA:
221
+                try {
222
+                    return cell.getStringCellValue();
223
+                } catch (Exception e) {
224
+                    return cell.getNumericCellValue();
225
+                }
226
+            default:
227
+                return null;
228
+        }
229
+    }
230
+
231
+    /**
232
+     * 执行角度度转弧度计算
233
+     */
234
+    @Override
235
+    public List<CmcDegreeToRadian> calculateDegreeToRadian(List<CmcDegreeToRadian> dataList) {
236
+        List<CmcDegreeToRadian> resultList = new ArrayList<>();
237
+        for (CmcDegreeToRadian item : dataList) {
238
+            try {
239
+                CmcDegreeToRadian result = calculateDegreeToRadianSingle(item);
240
+                resultList.add(result);
241
+            } catch (Exception e) {
242
+                e.printStackTrace();
243
+                // 计算失败时,返回带有错误信息的对象
244
+                CmcDegreeToRadian errorResult = new CmcDegreeToRadian();
245
+                errorResult.setPointNumber(item.getPointNumber());
246
+                errorResult.setLongitudeRadian(-1.0);
247
+                errorResult.setLatitudeRadian(-1.0);
248
+                resultList.add(errorResult);
249
+            }
250
+        }
251
+
252
+        return resultList;
253
+    }
254
+
255
+    /**
256
+     * 单条角度度转弧度计算(待实现)
257
+     */
258
+    private CmcDegreeToRadian calculateDegreeToRadianSingle(CmcDegreeToRadian cmcDegreeToRadian) {
259
+        double longitude = cmcDegreeToRadian.getLongitude();
260
+        double latitude = cmcDegreeToRadian.getLatitude();
261
+
262
+        // 转换为弧度
263
+        double longitudeRadian = Math.toRadians(longitude);
264
+        double latitudeRadian = Math.toRadians(latitude);
265
+        
266
+        cmcDegreeToRadian.setLongitudeRadian(longitudeRadian);
267
+        cmcDegreeToRadian.setLatitudeRadian(latitudeRadian);
268
+
269
+        return cmcDegreeToRadian;
270
+    }
271
+}

+ 133
- 44
oa-back/ruoyi-calculate/src/main/java/com/ruoyi/web/calculate/service/impl/CmcGaussBandChangeServiceImpl.java Datei anzeigen

@@ -16,9 +16,15 @@ import org.springframework.web.multipart.MultipartFile;
16 16
 
17 17
 import com.ruoyi.common.core.domain.AjaxResult;
18 18
 import com.ruoyi.common.utils.poi.ExcelUtil;
19
-// import com.ruoyi.web.calculate.mapper.CmcGaussPositiveMapper;
20 19
 import com.ruoyi.web.calculate.domain.CmcGaussBandChange;
21 20
 import com.ruoyi.web.calculate.service.ICmcGaussBandChangeService;
21
+import org.apache.poi.ss.usermodel.Cell;
22
+import org.apache.poi.ss.usermodel.CellType;
23
+import org.apache.poi.ss.usermodel.DateUtil;
24
+import org.apache.poi.ss.usermodel.Row;
25
+import org.apache.poi.ss.usermodel.Sheet;
26
+import org.apache.poi.ss.usermodel.Workbook;
27
+import org.apache.poi.ss.usermodel.WorkbookFactory;
22 28
 
23 29
 /**
24 30
  * 高斯换带服务实现
@@ -53,6 +59,9 @@ public class CmcGaussBandChangeServiceImpl implements ICmcGaussBandChangeService
53 59
     @Transactional(rollbackFor = Exception.class)
54 60
     public AjaxResult importExcelData(MultipartFile file) {
55 61
         try {
62
+            // 获取Excel表头信息
63
+            List<String> excelColumns = getExcelColumns(file);
64
+
56 65
             // 使用若依的ExcelUtil工具类
57 66
             ExcelUtil<CmcGaussBandChange> util = new ExcelUtil<CmcGaussBandChange>(CmcGaussBandChange.class);
58 67
             List<CmcGaussBandChange> dataList = util.importExcel(file.getInputStream());
@@ -62,54 +71,17 @@ public class CmcGaussBandChangeServiceImpl implements ICmcGaussBandChangeService
62 71
                 return AjaxResult.error("Excel文件中没有数据!");
63 72
             }
64 73
 
65
-            // 验证数据并转换为前端需要的格式
66
-            List<Map<String, Object>> validData = new ArrayList<>();
67
-            int successCount = 0;
68
-            StringBuilder errorMsg = new StringBuilder();
69
-
70
-            for (int i = 0; i < dataList.size(); i++) {
71
-                CmcGaussBandChange item = dataList.get(i);
72
-
73
-                try {
74
-                    // 转换为前端需要的Map格式
75
-                    Map<String, Object> rowMap = new HashMap<>();
76
-                    rowMap.put("pointNumber", item.getPointNumber());
77
-                    rowMap.put("coordinateSystem", item.getCoordinateSystem());
78
-                    rowMap.put("gaussX", item.getGaussX());
79
-                    rowMap.put("gaussY", item.getGaussY());
80
-                    rowMap.put("band", item.getBand());
81
-                    rowMap.put("bandWidth", item.getBandwidth());
82
-                    rowMap.put("newBand", item.getNewBand());
83
-                    rowMap.put("newBandWidth", item.getNewBandwidth());
84
-                    rowMap.put("longitudePosition", item.getLongitudePosition());
85
-                    rowMap.put("latitudePosition", item.getLatitudePosition());
86
-                    rowMap.put("projectionHeight", item.getProjectionHeight());
87
-                    rowMap.put("rowNum", i + 2); // Excel中的行号
88
-
89
-                    validData.add(rowMap);
90
-                    successCount++;
91
-
92
-                } catch (Exception e) {
93
-                    errorMsg.append(String.format("第%d行数据验证失败:%s;", i + 2, e.getMessage()));
94
-                }
95
-            }
74
+            // 返回原始Excel数据格式(使用表头作为key)
75
+            List<Map<String, Object>> rawData = readRawExcelData(file, excelColumns);
96 76
 
97 77
             // 构建返回结果
98 78
             Map<String, Object> result = new HashMap<>();
99 79
             result.put("total", dataList.size());
100
-            result.put("validCount", successCount);
101
-            result.put("data", validData);
102
-
103
-            if (successCount == 0) {
104
-                return AjaxResult.error("没有有效数据!" + errorMsg.toString());
105
-            }
106
-
107
-            String message = String.format("导入成功!共解析%d条数据,有效数据%d条", dataList.size(), successCount);
108
-            if (errorMsg.length() > 0) {
109
-                message += ";部分数据验证失败:" + errorMsg.toString();
110
-                return AjaxResult.success(message, result);
111
-            }
80
+            result.put("validCount", dataList.size());
81
+            result.put("data", rawData);
82
+            result.put("columns", excelColumns); // 返回Excel表头
112 83
 
84
+            String message = String.format("导入成功!共解析%d条数据", dataList.size());
113 85
             return AjaxResult.success(message, result);
114 86
 
115 87
         } catch (Exception e) {
@@ -117,6 +89,123 @@ public class CmcGaussBandChangeServiceImpl implements ICmcGaussBandChangeService
117 89
             return AjaxResult.error("导入失败:" + e.getMessage());
118 90
         }
119 91
     }
92
+    
93
+    /**
94
+     * 获取Excel表头信息
95
+     */
96
+    private List<String> getExcelColumns(MultipartFile file) {
97
+        List<String> columns = new ArrayList<>();
98
+        try {
99
+            Workbook workbook = WorkbookFactory.create(file.getInputStream());
100
+            Sheet sheet = workbook.getSheetAt(0);
101
+            Row headerRow = sheet.getRow(0); // 第一行为表头
102
+            
103
+            if (headerRow != null) {
104
+                for (int i = 0; i < headerRow.getPhysicalNumberOfCells(); i++) {
105
+                    Cell cell = headerRow.getCell(i);
106
+                    if (cell != null) {
107
+                        String cellValue = cell.getStringCellValue();
108
+                        if (cellValue != null && !cellValue.trim().isEmpty()) {
109
+                            columns.add(cellValue.trim());
110
+                        }
111
+                    }
112
+                }
113
+            }
114
+            workbook.close();
115
+        } catch (Exception e) {
116
+            e.printStackTrace();
117
+        }
118
+        return columns;
119
+    }
120
+    
121
+    /**
122
+     * 读取原始Excel数据(使用表头作为key)
123
+     */
124
+    private List<Map<String, Object>> readRawExcelData(MultipartFile file, List<String> columns) {
125
+        List<Map<String, Object>> dataList = new ArrayList<>();
126
+        try {
127
+            Workbook workbook = WorkbookFactory.create(file.getInputStream());
128
+            Sheet sheet = workbook.getSheetAt(0);
129
+            int rows = sheet.getLastRowNum();
130
+            
131
+            // 从第二行开始读取数据(第一行为表头)
132
+            for (int i = 1; i <= rows; i++) {
133
+                Row row = sheet.getRow(i);
134
+                if (row == null || isRowEmpty(row)) {
135
+                    continue;
136
+                }
137
+                
138
+                Map<String, Object> rowMap = new HashMap<>();
139
+                for (int j = 0; j < columns.size(); j++) {
140
+                    Cell cell = row.getCell(j);
141
+                    Object value = getCellValue(cell);
142
+                    rowMap.put(columns.get(j), value);
143
+                }
144
+                dataList.add(rowMap);
145
+            }
146
+            workbook.close();
147
+        } catch (Exception e) {
148
+            e.printStackTrace();
149
+        }
150
+        return dataList;
151
+    }
152
+    
153
+    /**
154
+     * 判断行是否为空
155
+     */
156
+    private boolean isRowEmpty(Row row) {
157
+        if (row == null) {
158
+            return true;
159
+        }
160
+        for (int i = row.getFirstCellNum(); i < row.getLastCellNum(); i++) {
161
+            Cell cell = row.getCell(i);
162
+            if (cell != null && cell.getCellType() != CellType.BLANK) {
163
+                // 使用getCellValue方法处理多种类型,避免数字类型调用getStringCellValue抛出异常
164
+                Object value = getCellValue(cell);
165
+                if (value != null) {
166
+                    String strValue = String.valueOf(value).trim();
167
+                    if (!strValue.isEmpty()) {
168
+                        return false;
169
+                    }
170
+                }
171
+            }
172
+        }
173
+        return true;
174
+    }
175
+    
176
+    /**
177
+     * 获取单元格值
178
+     */
179
+    private Object getCellValue(Cell cell) {
180
+        if (cell == null) {
181
+            return null;
182
+        }
183
+        switch (cell.getCellType()) {
184
+            case STRING:
185
+                return cell.getStringCellValue();
186
+            case NUMERIC:
187
+                if (DateUtil.isCellDateFormatted(cell)) {
188
+                    return cell.getLocalDateTimeCellValue();
189
+                } else {
190
+                    // 避免科学计数法
191
+                    double value = cell.getNumericCellValue();
192
+                    if (value == Math.floor(value)) {
193
+                        return (long) value;
194
+                    }
195
+                    return value;
196
+                }
197
+            case BOOLEAN:
198
+                return cell.getBooleanCellValue();
199
+            case FORMULA:
200
+                try {
201
+                    return cell.getStringCellValue();
202
+                } catch (Exception e) {
203
+                    return cell.getNumericCellValue();
204
+                }
205
+            default:
206
+                return null;
207
+        }
208
+    }
120 209
 
121 210
     /**
122 211
      * 执行高斯反算

+ 135
- 42
oa-back/ruoyi-calculate/src/main/java/com/ruoyi/web/calculate/service/impl/CmcGaussNegativeServiceImpl.java Datei anzeigen

@@ -16,9 +16,15 @@ import org.springframework.web.multipart.MultipartFile;
16 16
 
17 17
 import com.ruoyi.common.core.domain.AjaxResult;
18 18
 import com.ruoyi.common.utils.poi.ExcelUtil;
19
-// import com.ruoyi.web.calculate.mapper.CmcGaussPositiveMapper;
20 19
 import com.ruoyi.web.calculate.domain.CmcGaussNegative;
21 20
 import com.ruoyi.web.calculate.service.ICmcGaussNegativeService;
21
+import org.apache.poi.ss.usermodel.Cell;
22
+import org.apache.poi.ss.usermodel.CellType;
23
+import org.apache.poi.ss.usermodel.DateUtil;
24
+import org.apache.poi.ss.usermodel.Row;
25
+import org.apache.poi.ss.usermodel.Sheet;
26
+import org.apache.poi.ss.usermodel.Workbook;
27
+import org.apache.poi.ss.usermodel.WorkbookFactory;
22 28
 
23 29
 /**
24 30
  * 高斯反算服务实现
@@ -53,6 +59,9 @@ public class CmcGaussNegativeServiceImpl implements ICmcGaussNegativeService {
53 59
     @Transactional(rollbackFor = Exception.class)
54 60
     public AjaxResult importExcelData(MultipartFile file) {
55 61
         try {
62
+            // 获取Excel表头信息
63
+            List<String> excelColumns = getExcelColumns(file);
64
+
56 65
             // 使用若依的ExcelUtil工具类
57 66
             ExcelUtil<CmcGaussNegative> util = new ExcelUtil<CmcGaussNegative>(CmcGaussNegative.class);
58 67
             List<CmcGaussNegative> dataList = util.importExcel(file.getInputStream());
@@ -62,50 +71,17 @@ public class CmcGaussNegativeServiceImpl implements ICmcGaussNegativeService {
62 71
                 return AjaxResult.error("Excel文件中没有数据!");
63 72
             }
64 73
 
65
-            // 验证数据并转换为前端需要的格式
66
-            List<Map<String, Object>> validData = new ArrayList<>();
67
-            int successCount = 0;
68
-            StringBuilder errorMsg = new StringBuilder();
69
-
70
-            for (int i = 0; i < dataList.size(); i++) {
71
-                CmcGaussNegative item = dataList.get(i);
72
-
73
-                try {
74
-                    // 转换为前端需要的Map格式
75
-                    Map<String, Object> rowMap = new HashMap<>();
76
-                    rowMap.put("pointNumber", item.getPointNumber());
77
-                    rowMap.put("coordinateSystem", item.getCoordinateSystem());
78
-                    rowMap.put("gaussX", item.getGaussX());
79
-                    rowMap.put("gaussY", item.getGaussY());
80
-                    rowMap.put("longitudePosition", item.getLongitudePosition());
81
-                    rowMap.put("latitudePosition", item.getLatitudePosition());
82
-                    rowMap.put("projectionHeight", item.getProjectionHeight());
83
-                    rowMap.put("rowNum", i + 2); // Excel中的行号
84
-
85
-                    validData.add(rowMap);
86
-                    successCount++;
87
-
88
-                } catch (Exception e) {
89
-                    errorMsg.append(String.format("第%d行数据验证失败:%s;", i + 2, e.getMessage()));
90
-                }
91
-            }
74
+            // 返回原始Excel数据格式(使用表头作为key)
75
+            List<Map<String, Object>> rawData = readRawExcelData(file, excelColumns);
92 76
 
93 77
             // 构建返回结果
94 78
             Map<String, Object> result = new HashMap<>();
95 79
             result.put("total", dataList.size());
96
-            result.put("validCount", successCount);
97
-            result.put("data", validData);
98
-
99
-            if (successCount == 0) {
100
-                return AjaxResult.error("没有有效数据!" + errorMsg.toString());
101
-            }
102
-
103
-            String message = String.format("导入成功!共解析%d条数据,有效数据%d条", dataList.size(), successCount);
104
-            if (errorMsg.length() > 0) {
105
-                message += ";部分数据验证失败:" + errorMsg.toString();
106
-                return AjaxResult.success(message, result);
107
-            }
80
+            result.put("validCount", dataList.size());
81
+            result.put("data", rawData);
82
+            result.put("columns", excelColumns); // 返回Excel表头
108 83
 
84
+            String message = String.format("导入成功!共解析%d条数据", dataList.size());
109 85
             return AjaxResult.success(message, result);
110 86
 
111 87
         } catch (Exception e) {
@@ -113,6 +89,123 @@ public class CmcGaussNegativeServiceImpl implements ICmcGaussNegativeService {
113 89
             return AjaxResult.error("导入失败:" + e.getMessage());
114 90
         }
115 91
     }
92
+    
93
+    /**
94
+     * 获取Excel表头信息
95
+     */
96
+    private List<String> getExcelColumns(MultipartFile file) {
97
+        List<String> columns = new ArrayList<>();
98
+        try {
99
+            Workbook workbook = WorkbookFactory.create(file.getInputStream());
100
+            Sheet sheet = workbook.getSheetAt(0);
101
+            Row headerRow = sheet.getRow(0); // 第一行为表头
102
+            
103
+            if (headerRow != null) {
104
+                for (int i = 0; i < headerRow.getPhysicalNumberOfCells(); i++) {
105
+                    Cell cell = headerRow.getCell(i);
106
+                    if (cell != null) {
107
+                        String cellValue = cell.getStringCellValue();
108
+                        if (cellValue != null && !cellValue.trim().isEmpty()) {
109
+                            columns.add(cellValue.trim());
110
+                        }
111
+                    }
112
+                }
113
+            }
114
+            workbook.close();
115
+        } catch (Exception e) {
116
+            e.printStackTrace();
117
+        }
118
+        return columns;
119
+    }
120
+    
121
+    /**
122
+     * 读取原始Excel数据(使用表头作为key)
123
+     */
124
+    private List<Map<String, Object>> readRawExcelData(MultipartFile file, List<String> columns) {
125
+        List<Map<String, Object>> dataList = new ArrayList<>();
126
+        try {
127
+            Workbook workbook = WorkbookFactory.create(file.getInputStream());
128
+            Sheet sheet = workbook.getSheetAt(0);
129
+            int rows = sheet.getLastRowNum();
130
+            
131
+            // 从第二行开始读取数据(第一行为表头)
132
+            for (int i = 1; i <= rows; i++) {
133
+                Row row = sheet.getRow(i);
134
+                if (row == null || isRowEmpty(row)) {
135
+                    continue;
136
+                }
137
+                
138
+                Map<String, Object> rowMap = new HashMap<>();
139
+                for (int j = 0; j < columns.size(); j++) {
140
+                    Cell cell = row.getCell(j);
141
+                    Object value = getCellValue(cell);
142
+                    rowMap.put(columns.get(j), value);
143
+                }
144
+                dataList.add(rowMap);
145
+            }
146
+            workbook.close();
147
+        } catch (Exception e) {
148
+            e.printStackTrace();
149
+        }
150
+        return dataList;
151
+    }
152
+    
153
+    /**
154
+     * 判断行是否为空
155
+     */
156
+    private boolean isRowEmpty(Row row) {
157
+        if (row == null) {
158
+            return true;
159
+        }
160
+        for (int i = row.getFirstCellNum(); i < row.getLastCellNum(); i++) {
161
+            Cell cell = row.getCell(i);
162
+            if (cell != null && cell.getCellType() != CellType.BLANK) {
163
+                // 使用getCellValue方法处理多种类型,避免数字类型调用getStringCellValue抛出异常
164
+                Object value = getCellValue(cell);
165
+                if (value != null) {
166
+                    String strValue = String.valueOf(value).trim();
167
+                    if (!strValue.isEmpty()) {
168
+                        return false;
169
+                    }
170
+                }
171
+            }
172
+        }
173
+        return true;
174
+    }
175
+    
176
+    /**
177
+     * 获取单元格值
178
+     */
179
+    private Object getCellValue(Cell cell) {
180
+        if (cell == null) {
181
+            return null;
182
+        }
183
+        switch (cell.getCellType()) {
184
+            case STRING:
185
+                return cell.getStringCellValue();
186
+            case NUMERIC:
187
+                if (DateUtil.isCellDateFormatted(cell)) {
188
+                    return cell.getLocalDateTimeCellValue();
189
+                } else {
190
+                    // 避免科学计数法
191
+                    double value = cell.getNumericCellValue();
192
+                    if (value == Math.floor(value)) {
193
+                        return (long) value;
194
+                    }
195
+                    return value;
196
+                }
197
+            case BOOLEAN:
198
+                return cell.getBooleanCellValue();
199
+            case FORMULA:
200
+                try {
201
+                    return cell.getStringCellValue();
202
+                } catch (Exception e) {
203
+                    return cell.getNumericCellValue();
204
+                }
205
+            default:
206
+                return null;
207
+        }
208
+    }
116 209
 
117 210
     /**
118 211
      * 执行高斯反算
@@ -151,8 +244,8 @@ public class CmcGaussNegativeServiceImpl implements ICmcGaussNegativeService {
151 244
             String latitudePosition = cmcGaussNegative.getLatitudePosition(); // 纬度位置(北/南)
152 245
             double projectionHeight = cmcGaussNegative.getProjectionHeight(); // 投影面高程
153 246
             String yStr = String.valueOf(Math.abs((long) gaussY));
154
-            int band = Integer.parseInt(yStr.substring(0, 2));
155
-            int bandwidth = band > 30 ? 3 : 6;
247
+            int band = cmcGaussNegative.getBand();
248
+            int bandwidth = cmcGaussNegative.getBandwidth();
156 249
 
157 250
             double x = latitudePosition.equals("S") ? 10000000 - gaussX : gaussX;
158 251
             double y = gaussY > 1.0E+7 ? gaussY - (int) Math.floor(gaussY / 1.0E+6) * 1.0E+6 - 500000 : gaussY - 500000;

+ 133
- 43
oa-back/ruoyi-calculate/src/main/java/com/ruoyi/web/calculate/service/impl/CmcGaussPositiveServiceImpl.java Datei anzeigen

@@ -7,8 +7,6 @@ import java.util.HashMap;
7 7
 import java.util.List;
8 8
 import java.util.Map;
9 9
 
10
-import javax.servlet.http.HttpServletResponse;
11
-
12 10
 import org.springframework.beans.factory.annotation.Autowired;
13 11
 import org.springframework.stereotype.Service;
14 12
 import org.springframework.transaction.annotation.Transactional;
@@ -19,6 +17,13 @@ import com.ruoyi.common.utils.poi.ExcelUtil;
19 17
 // import com.ruoyi.web.calculate.mapper.CmcGaussPositiveMapper;
20 18
 import com.ruoyi.web.calculate.domain.CmcGaussPositive;
21 19
 import com.ruoyi.web.calculate.service.ICmcGaussPositiveService;
20
+import org.apache.poi.ss.usermodel.Cell;
21
+import org.apache.poi.ss.usermodel.CellType;
22
+import org.apache.poi.ss.usermodel.DateUtil;
23
+import org.apache.poi.ss.usermodel.Row;
24
+import org.apache.poi.ss.usermodel.Sheet;
25
+import org.apache.poi.ss.usermodel.Workbook;
26
+import org.apache.poi.ss.usermodel.WorkbookFactory;
22 27
 
23 28
 /**
24 29
  * 高斯正算服务实现
@@ -56,6 +61,9 @@ public class CmcGaussPositiveServiceImpl implements ICmcGaussPositiveService
56 61
     public AjaxResult importExcelData(MultipartFile file)
57 62
     {
58 63
         try {
64
+            // 获取Excel表头信息
65
+            List<String> excelColumns = getExcelColumns(file);
66
+
59 67
             // 使用若依的ExcelUtil工具类
60 68
             ExcelUtil<CmcGaussPositive> util = new ExcelUtil<CmcGaussPositive>(CmcGaussPositive.class);
61 69
             List<CmcGaussPositive> dataList = util.importExcel(file.getInputStream());
@@ -65,52 +73,17 @@ public class CmcGaussPositiveServiceImpl implements ICmcGaussPositiveService
65 73
                 return AjaxResult.error("Excel文件中没有数据!");
66 74
             }
67 75
 
68
-            // 验证数据并转换为前端需要的格式
69
-            List<Map<String, Object>> validData = new ArrayList<>();
70
-            int successCount = 0;
71
-            StringBuilder errorMsg = new StringBuilder();
72
-
73
-            for (int i = 0; i < dataList.size(); i++) {
74
-                CmcGaussPositive item = dataList.get(i);
75
-
76
-                try {
77
-                    // 转换为前端需要的Map格式
78
-                    Map<String, Object> rowMap = new HashMap<>();
79
-                    rowMap.put("pointNumber", item.getPointNumber());
80
-                    rowMap.put("coordinateSystem", item.getCoordinateSystem());
81
-                    rowMap.put("longitude", item.getLongitude());
82
-                    rowMap.put("longitudePosition", item.getLongitudePosition());
83
-                    rowMap.put("latitude", item.getLatitude());
84
-                    rowMap.put("latitudePosition", item.getLatitudePosition());
85
-                    rowMap.put("band", item.getBand());
86
-                    rowMap.put("bandwidth", item.getBandwidth());
87
-                    rowMap.put("projectionHeight", item.getProjectionHeight());
88
-                    rowMap.put("rowNum", i + 2); // Excel中的行号
89
-
90
-                    validData.add(rowMap);
91
-                    successCount++;
92
-
93
-                } catch (Exception e) {
94
-                    errorMsg.append(String.format("第%d行数据验证失败:%s;", i + 2, e.getMessage()));
95
-                }
96
-            }
76
+            // 返回原始Excel数据格式(使用表头作为key)
77
+            List<Map<String, Object>> rawData = readRawExcelData(file, excelColumns);
97 78
 
98 79
             // 构建返回结果
99 80
             Map<String, Object> result = new HashMap<>();
100 81
             result.put("total", dataList.size());
101
-            result.put("validCount", successCount);
102
-            result.put("data", validData);
103
-
104
-            if (successCount == 0) {
105
-                return AjaxResult.error("没有有效数据!" + errorMsg.toString());
106
-            }
107
-
108
-            String message = String.format("导入成功!共解析%d条数据,有效数据%d条", dataList.size(), successCount);
109
-            if (errorMsg.length() > 0) {
110
-                message += ";部分数据验证失败:" + errorMsg.toString();
111
-                return AjaxResult.success(message, result);
112
-            }
82
+            result.put("validCount", dataList.size());
83
+            result.put("data", rawData);
84
+            result.put("columns", excelColumns); // 返回Excel表头
113 85
 
86
+            String message = String.format("导入成功!共解析%d条数据", dataList.size());
114 87
             return AjaxResult.success(message, result);
115 88
 
116 89
         } catch (Exception e) {
@@ -118,6 +91,123 @@ public class CmcGaussPositiveServiceImpl implements ICmcGaussPositiveService
118 91
             return AjaxResult.error("导入失败:" + e.getMessage());
119 92
         }
120 93
     }
94
+    
95
+    /**
96
+     * 读取原始Excel数据(使用表头作为key)
97
+     */
98
+    private List<Map<String, Object>> readRawExcelData(MultipartFile file, List<String> columns) {
99
+        List<Map<String, Object>> dataList = new ArrayList<>();
100
+        try {
101
+            Workbook workbook = WorkbookFactory.create(file.getInputStream());
102
+            Sheet sheet = workbook.getSheetAt(0);
103
+            int rows = sheet.getLastRowNum();
104
+            
105
+            // 从第二行开始读取数据(第一行为表头)
106
+            for (int i = 1; i <= rows; i++) {
107
+                Row row = sheet.getRow(i);
108
+                if (row == null || isRowEmpty(row)) {
109
+                    continue;
110
+                }
111
+                
112
+                Map<String, Object> rowMap = new HashMap<>();
113
+                for (int j = 0; j < columns.size(); j++) {
114
+                    Cell cell = row.getCell(j);
115
+                    Object value = getCellValue(cell);
116
+                    rowMap.put(columns.get(j), value);
117
+                }
118
+                dataList.add(rowMap);
119
+            }
120
+            workbook.close();
121
+        } catch (Exception e) {
122
+            e.printStackTrace();
123
+        }
124
+        return dataList;
125
+    }
126
+    
127
+    /**
128
+     * 获取单元格值
129
+     */
130
+    private Object getCellValue(Cell cell) {
131
+        if (cell == null) {
132
+            return null;
133
+        }
134
+        switch (cell.getCellType()) {
135
+            case STRING:
136
+                return cell.getStringCellValue();
137
+            case NUMERIC:
138
+                if (DateUtil.isCellDateFormatted(cell)) {
139
+                    return cell.getLocalDateTimeCellValue();
140
+                } else {
141
+                    // 避免科学计数法
142
+                    double value = cell.getNumericCellValue();
143
+                    if (value == Math.floor(value)) {
144
+                        return (long) value;
145
+                    }
146
+                    return value;
147
+                }
148
+            case BOOLEAN:
149
+                return cell.getBooleanCellValue();
150
+            case FORMULA:
151
+                try {
152
+                    return cell.getStringCellValue();
153
+                } catch (Exception e) {
154
+                    return cell.getNumericCellValue();
155
+                }
156
+            default:
157
+                return null;
158
+        }
159
+    }
160
+    
161
+    /**
162
+     * 判断行是否为空
163
+     */
164
+    private boolean isRowEmpty(Row row) {
165
+        if (row == null) {
166
+            return true;
167
+        }
168
+        for (int i = row.getFirstCellNum(); i < row.getLastCellNum(); i++) {
169
+            Cell cell = row.getCell(i);
170
+            if (cell != null && cell.getCellType() != CellType.BLANK) {
171
+                // 使用getCellValue方法处理多种类型,避免数字类型调用getStringCellValue抛出异常
172
+                Object value = getCellValue(cell);
173
+                if (value != null) {
174
+                    String strValue = String.valueOf(value).trim();
175
+                    if (!strValue.isEmpty()) {
176
+                        return false;
177
+                    }
178
+                }
179
+            }
180
+        }
181
+        return true;
182
+    }
183
+    
184
+    /**
185
+     * 获取Excel表头信息
186
+     */
187
+    private List<String> getExcelColumns(MultipartFile file) {
188
+        List<String> columns = new ArrayList<>();
189
+        try {
190
+            org.apache.poi.ss.usermodel.Workbook workbook = org.apache.poi.ss.usermodel.WorkbookFactory.create(file.getInputStream());
191
+            org.apache.poi.ss.usermodel.Sheet sheet = workbook.getSheetAt(0);
192
+            org.apache.poi.ss.usermodel.Row headerRow = sheet.getRow(0); // 第一行为表头
193
+            
194
+            if (headerRow != null) {
195
+                for (int i = 0; i < headerRow.getPhysicalNumberOfCells(); i++) {
196
+                    org.apache.poi.ss.usermodel.Cell cell = headerRow.getCell(i);
197
+                    if (cell != null) {
198
+                        String cellValue = cell.getStringCellValue();
199
+                        if (cellValue != null && !cellValue.trim().isEmpty()) {
200
+                            columns.add(cellValue.trim());
201
+                        }
202
+                    }
203
+                }
204
+            }
205
+            workbook.close();
206
+        } catch (Exception e) {
207
+            e.printStackTrace();
208
+        }
209
+        return columns;
210
+    }
121 211
 
122 212
     /**
123 213
      * 执行高斯正算

+ 177
- 45
oa-back/ruoyi-calculate/src/main/java/com/ruoyi/web/calculate/service/impl/CmcGeodeticToSpatialServiceImpl.java Datei anzeigen

@@ -18,6 +18,13 @@ import com.ruoyi.common.core.domain.AjaxResult;
18 18
 import com.ruoyi.common.utils.poi.ExcelUtil;
19 19
 import com.ruoyi.web.calculate.domain.CmcGeodeticToSpatial;
20 20
 import com.ruoyi.web.calculate.service.ICmcGeodeticToSpatialService;
21
+import org.apache.poi.ss.usermodel.Cell;
22
+import org.apache.poi.ss.usermodel.CellType;
23
+import org.apache.poi.ss.usermodel.DateUtil;
24
+import org.apache.poi.ss.usermodel.Row;
25
+import org.apache.poi.ss.usermodel.Sheet;
26
+import org.apache.poi.ss.usermodel.Workbook;
27
+import org.apache.poi.ss.usermodel.WorkbookFactory;
21 28
 
22 29
 /**
23 30
  * 大地坐标转空间直角坐标服务实现
@@ -52,59 +59,25 @@ public class CmcGeodeticToSpatialServiceImpl implements ICmcGeodeticToSpatialSer
52 59
     @Transactional(rollbackFor = Exception.class)
53 60
     public AjaxResult importExcelData(MultipartFile file) {
54 61
         try {
55
-            // 使用若依的ExcelUtil工具类
56
-            ExcelUtil<CmcGeodeticToSpatial> util = new ExcelUtil<CmcGeodeticToSpatial>(CmcGeodeticToSpatial.class);
57
-            List<CmcGeodeticToSpatial> dataList = util.importExcel(file.getInputStream());
62
+            // 获取Excel表头信息
63
+            List<String> excelColumns = getExcelColumns(file);
64
+
65
+            // 返回原始Excel数据格式(使用表头作为key)
66
+            List<Map<String, Object>> rawData = readRawExcelData(file, excelColumns);
58 67
 
59 68
             // 检查是否有数据
60
-            if (dataList == null || dataList.isEmpty()) {
69
+            if (rawData == null || rawData.isEmpty()) {
61 70
                 return AjaxResult.error("Excel文件中没有数据!");
62 71
             }
63 72
 
64
-            // 验证数据并转换为前端需要的格式
65
-            List<Map<String, Object>> validData = new ArrayList<>();
66
-            int successCount = 0;
67
-            StringBuilder errorMsg = new StringBuilder();
68
-
69
-            for (int i = 0; i < dataList.size(); i++) {
70
-                CmcGeodeticToSpatial item = dataList.get(i);
71
-
72
-                try {
73
-                    // 转换为前端需要的Map格式
74
-                    Map<String, Object> rowMap = new HashMap<>();
75
-                    rowMap.put("pointNumber", item.getPointNumber());
76
-                    rowMap.put("coordinateSystem", item.getCoordinateSystem());
77
-                    rowMap.put("longitude", item.getLongitude());
78
-                    rowMap.put("longitudePosition", item.getLongitudePosition());
79
-                    rowMap.put("latitude", item.getLatitude());
80
-                    rowMap.put("latitudePosition", item.getLatitudePosition());
81
-                    rowMap.put("height", item.getHeight());
82
-                    rowMap.put("rowNum", i + 2); // Excel中的行号
83
-
84
-                    validData.add(rowMap);
85
-                    successCount++;
86
-
87
-                } catch (Exception e) {
88
-                    errorMsg.append(String.format("第%d行数据验证失败:%s;", i + 2, e.getMessage()));
89
-                }
90
-            }
91
-
92 73
             // 构建返回结果
93 74
             Map<String, Object> result = new HashMap<>();
94
-            result.put("total", dataList.size());
95
-            result.put("validCount", successCount);
96
-            result.put("data", validData);
97
-
98
-            if (successCount == 0) {
99
-                return AjaxResult.error("没有有效数据!" + errorMsg.toString());
100
-            }
101
-
102
-            String message = String.format("导入成功!共解析%d条数据,有效数据%d条", dataList.size(), successCount);
103
-            if (errorMsg.length() > 0) {
104
-                message += ";部分数据验证失败:" + errorMsg.toString();
105
-                return AjaxResult.success(message, result);
106
-            }
75
+            result.put("total", rawData.size());
76
+            result.put("validCount", rawData.size());
77
+            result.put("data", rawData);
78
+            result.put("columns", excelColumns); // 返回Excel表头
107 79
 
80
+            String message = String.format("导入成功!共解析%d条数据", rawData.size());
108 81
             return AjaxResult.success(message, result);
109 82
 
110 83
         } catch (Exception e) {
@@ -112,6 +85,165 @@ public class CmcGeodeticToSpatialServiceImpl implements ICmcGeodeticToSpatialSer
112 85
             return AjaxResult.error("导入失败:" + e.getMessage());
113 86
         }
114 87
     }
88
+    
89
+    /**
90
+     * 获取Excel表头信息
91
+     */
92
+    private List<String> getExcelColumns(MultipartFile file) {
93
+        List<String> columns = new ArrayList<>();
94
+        try {
95
+            Workbook workbook = WorkbookFactory.create(file.getInputStream());
96
+            Sheet sheet = workbook.getSheetAt(0);
97
+            Row headerRow = sheet.getRow(0); // 第一行为表头
98
+            
99
+            if (headerRow != null) {
100
+                int lastCellNum = headerRow.getLastCellNum();
101
+                for (int i = 0; i < lastCellNum; i++) {
102
+                    Cell cell = headerRow.getCell(i);
103
+                    String cellValue = null;
104
+                    if (cell != null) {
105
+                        cellValue = getCellStringValue(cell);
106
+                    }
107
+                    if (cellValue != null && !cellValue.trim().isEmpty()) {
108
+                        columns.add(cellValue.trim());
109
+                    } else {
110
+                        columns.add("列" + (i + 1));
111
+                    }
112
+                }
113
+            }
114
+            workbook.close();
115
+        } catch (Exception e) {
116
+            e.printStackTrace();
117
+        }
118
+        return columns;
119
+    }
120
+    
121
+    /**
122
+     * 获取单元格的字符串值(处理多种类型)
123
+     */
124
+    private String getCellStringValue(Cell cell) {
125
+        if (cell == null) {
126
+            return null;
127
+        }
128
+        switch (cell.getCellType()) {
129
+            case STRING:
130
+                return cell.getStringCellValue();
131
+            case NUMERIC:
132
+                if (DateUtil.isCellDateFormatted(cell)) {
133
+                    return cell.getLocalDateTimeCellValue().toString();
134
+                } else {
135
+                    double value = cell.getNumericCellValue();
136
+                    if (value == Math.floor(value)) {
137
+                        return String.valueOf((long) value);
138
+                    }
139
+                    return String.valueOf(value);
140
+                }
141
+            case BOOLEAN:
142
+                return String.valueOf(cell.getBooleanCellValue());
143
+            case FORMULA:
144
+                try {
145
+                    return cell.getStringCellValue();
146
+                } catch (Exception e) {
147
+                    try {
148
+                        return String.valueOf(cell.getNumericCellValue());
149
+                    } catch (Exception ex) {
150
+                        return cell.getCellFormula();
151
+                    }
152
+                }
153
+            default:
154
+                return null;
155
+        }
156
+    }
157
+    
158
+    /**
159
+     * 读取原始Excel数据(使用表头作为key)
160
+     */
161
+    private List<Map<String, Object>> readRawExcelData(MultipartFile file, List<String> columns) {
162
+        List<Map<String, Object>> dataList = new ArrayList<>();
163
+        try {
164
+            Workbook workbook = WorkbookFactory.create(file.getInputStream());
165
+            Sheet sheet = workbook.getSheetAt(0);
166
+            int rows = sheet.getLastRowNum();
167
+            
168
+            // 从第二行开始读取数据(第一行为表头)
169
+            for (int i = 1; i <= rows; i++) {
170
+                Row row = sheet.getRow(i);
171
+                if (row == null || isRowEmpty(row)) {
172
+                    continue;
173
+                }
174
+                
175
+                Map<String, Object> rowMap = new HashMap<>();
176
+                int lastCellNum = Math.min(row.getLastCellNum(), columns.size());
177
+                for (int j = 0; j < lastCellNum; j++) {
178
+                    Cell cell = row.getCell(j);
179
+                    Object value = getCellValue(cell);
180
+                    rowMap.put(columns.get(j), value);
181
+                }
182
+                dataList.add(rowMap);
183
+            }
184
+            workbook.close();
185
+        } catch (Exception e) {
186
+            e.printStackTrace();
187
+        }
188
+        return dataList;
189
+    }
190
+    
191
+    /**
192
+     * 判断行是否为空
193
+     */
194
+    private boolean isRowEmpty(Row row) {
195
+        if (row == null) {
196
+            return true;
197
+        }
198
+        for (int i = row.getFirstCellNum(); i < row.getLastCellNum(); i++) {
199
+            Cell cell = row.getCell(i);
200
+            if (cell != null && cell.getCellType() != CellType.BLANK) {
201
+                // 使用getCellValue方法处理多种类型,避免数字类型调用getStringCellValue抛出异常
202
+                Object value = getCellValue(cell);
203
+                if (value != null) {
204
+                    String strValue = String.valueOf(value).trim();
205
+                    if (!strValue.isEmpty()) {
206
+                        return false;
207
+                    }
208
+                }
209
+            }
210
+        }
211
+        return true;
212
+    }
213
+    
214
+    /**
215
+     * 获取单元格值
216
+     */
217
+    private Object getCellValue(Cell cell) {
218
+        if (cell == null) {
219
+            return null;
220
+        }
221
+        switch (cell.getCellType()) {
222
+            case STRING:
223
+                return cell.getStringCellValue();
224
+            case NUMERIC:
225
+                if (DateUtil.isCellDateFormatted(cell)) {
226
+                    return cell.getLocalDateTimeCellValue();
227
+                } else {
228
+                    // 避免科学计数法
229
+                    double value = cell.getNumericCellValue();
230
+                    if (value == Math.floor(value)) {
231
+                        return (long) value;
232
+                    }
233
+                    return value;
234
+                }
235
+            case BOOLEAN:
236
+                return cell.getBooleanCellValue();
237
+            case FORMULA:
238
+                try {
239
+                    return cell.getStringCellValue();
240
+                } catch (Exception e) {
241
+                    return cell.getNumericCellValue();
242
+                }
243
+            default:
244
+                return null;
245
+        }
246
+    }
115 247
 
116 248
     /**
117 249
      * 执行大地坐标转空间直角坐标计算

+ 178
- 44
oa-back/ruoyi-calculate/src/main/java/com/ruoyi/web/calculate/service/impl/CmcSpatialToGeodeticServiceImpl.java Datei anzeigen

@@ -18,6 +18,13 @@ import com.ruoyi.common.core.domain.AjaxResult;
18 18
 import com.ruoyi.common.utils.poi.ExcelUtil;
19 19
 import com.ruoyi.web.calculate.domain.CmcSpatialToGeodetic;
20 20
 import com.ruoyi.web.calculate.service.ICmcSpatialToGeodeticService;
21
+import org.apache.poi.ss.usermodel.Cell;
22
+import org.apache.poi.ss.usermodel.CellType;
23
+import org.apache.poi.ss.usermodel.DateUtil;
24
+import org.apache.poi.ss.usermodel.Row;
25
+import org.apache.poi.ss.usermodel.Sheet;
26
+import org.apache.poi.ss.usermodel.Workbook;
27
+import org.apache.poi.ss.usermodel.WorkbookFactory;
21 28
 
22 29
 /**
23 30
  * 空间直角坐标转大地坐标服务实现
@@ -52,57 +59,25 @@ public class CmcSpatialToGeodeticServiceImpl implements ICmcSpatialToGeodeticSer
52 59
     @Transactional(rollbackFor = Exception.class)
53 60
     public AjaxResult importExcelData(MultipartFile file) {
54 61
         try {
55
-            // 使用若依的ExcelUtil工具类
56
-            ExcelUtil<CmcSpatialToGeodetic> util = new ExcelUtil<CmcSpatialToGeodetic>(CmcSpatialToGeodetic.class);
57
-            List<CmcSpatialToGeodetic> dataList = util.importExcel(file.getInputStream());
62
+            // 获取Excel表头信息
63
+            List<String> excelColumns = getExcelColumns(file);
64
+
65
+            // 返回原始Excel数据格式(使用表头作为key)
66
+            List<Map<String, Object>> rawData = readRawExcelData(file, excelColumns);
58 67
 
59 68
             // 检查是否有数据
60
-            if (dataList == null || dataList.isEmpty()) {
69
+            if (rawData == null || rawData.isEmpty()) {
61 70
                 return AjaxResult.error("Excel文件中没有数据!");
62 71
             }
63 72
 
64
-            // 验证数据并转换为前端需要的格式
65
-            List<Map<String, Object>> validData = new ArrayList<>();
66
-            int successCount = 0;
67
-            StringBuilder errorMsg = new StringBuilder();
68
-
69
-            for (int i = 0; i < dataList.size(); i++) {
70
-                CmcSpatialToGeodetic item = dataList.get(i);
71
-
72
-                try {
73
-                    // 转换为前端需要的Map格式
74
-                    Map<String, Object> rowMap = new HashMap<>();
75
-                    rowMap.put("pointNumber", item.getPointNumber());
76
-                    rowMap.put("coordinateSystem", item.getCoordinateSystem());
77
-                    rowMap.put("spatialX", item.getSpatialX());
78
-                    rowMap.put("spatialY", item.getSpatialY());
79
-                    rowMap.put("spatialZ", item.getSpatialZ());
80
-                    rowMap.put("rowNum", i + 2); // Excel中的行号
81
-
82
-                    validData.add(rowMap);
83
-                    successCount++;
84
-
85
-                } catch (Exception e) {
86
-                    errorMsg.append(String.format("第%d行数据验证失败:%s;", i + 2, e.getMessage()));
87
-                }
88
-            }
89
-
90 73
             // 构建返回结果
91 74
             Map<String, Object> result = new HashMap<>();
92
-            result.put("total", dataList.size());
93
-            result.put("validCount", successCount);
94
-            result.put("data", validData);
95
-
96
-            if (successCount == 0) {
97
-                return AjaxResult.error("没有有效数据!" + errorMsg.toString());
98
-            }
99
-
100
-            String message = String.format("导入成功!共解析%d条数据,有效数据%d条", dataList.size(), successCount);
101
-            if (errorMsg.length() > 0) {
102
-                message += ";部分数据验证失败:" + errorMsg.toString();
103
-                return AjaxResult.success(message, result);
104
-            }
75
+            result.put("total", rawData.size());
76
+            result.put("validCount", rawData.size());
77
+            result.put("data", rawData);
78
+            result.put("columns", excelColumns); // 返回Excel表头
105 79
 
80
+            String message = String.format("导入成功!共解析%d条数据", rawData.size());
106 81
             return AjaxResult.success(message, result);
107 82
 
108 83
         } catch (Exception e) {
@@ -110,6 +85,165 @@ public class CmcSpatialToGeodeticServiceImpl implements ICmcSpatialToGeodeticSer
110 85
             return AjaxResult.error("导入失败:" + e.getMessage());
111 86
         }
112 87
     }
88
+    
89
+    /**
90
+     * 获取Excel表头信息
91
+     */
92
+    private List<String> getExcelColumns(MultipartFile file) {
93
+        List<String> columns = new ArrayList<>();
94
+        try {
95
+            Workbook workbook = WorkbookFactory.create(file.getInputStream());
96
+            Sheet sheet = workbook.getSheetAt(0);
97
+            Row headerRow = sheet.getRow(0); // 第一行为表头
98
+            
99
+            if (headerRow != null) {
100
+                int lastCellNum = headerRow.getLastCellNum();
101
+                for (int i = 0; i < lastCellNum; i++) {
102
+                    Cell cell = headerRow.getCell(i);
103
+                    String cellValue = null;
104
+                    if (cell != null) {
105
+                        cellValue = getCellStringValue(cell);
106
+                    }
107
+                    if (cellValue != null && !cellValue.trim().isEmpty()) {
108
+                        columns.add(cellValue.trim());
109
+                    } else {
110
+                        columns.add("列" + (i + 1));
111
+                    }
112
+                }
113
+            }
114
+            workbook.close();
115
+        } catch (Exception e) {
116
+            e.printStackTrace();
117
+        }
118
+        return columns;
119
+    }
120
+    
121
+    /**
122
+     * 获取单元格的字符串值(处理多种类型)
123
+     */
124
+    private String getCellStringValue(Cell cell) {
125
+        if (cell == null) {
126
+            return null;
127
+        }
128
+        switch (cell.getCellType()) {
129
+            case STRING:
130
+                return cell.getStringCellValue();
131
+            case NUMERIC:
132
+                if (DateUtil.isCellDateFormatted(cell)) {
133
+                    return cell.getLocalDateTimeCellValue().toString();
134
+                } else {
135
+                    double value = cell.getNumericCellValue();
136
+                    if (value == Math.floor(value)) {
137
+                        return String.valueOf((long) value);
138
+                    }
139
+                    return String.valueOf(value);
140
+                }
141
+            case BOOLEAN:
142
+                return String.valueOf(cell.getBooleanCellValue());
143
+            case FORMULA:
144
+                try {
145
+                    return cell.getStringCellValue();
146
+                } catch (Exception e) {
147
+                    try {
148
+                        return String.valueOf(cell.getNumericCellValue());
149
+                    } catch (Exception ex) {
150
+                        return cell.getCellFormula();
151
+                    }
152
+                }
153
+            default:
154
+                return null;
155
+        }
156
+    }
157
+    
158
+    /**
159
+     * 读取原始Excel数据(使用表头作为key)
160
+     */
161
+    private List<Map<String, Object>> readRawExcelData(MultipartFile file, List<String> columns) {
162
+        List<Map<String, Object>> dataList = new ArrayList<>();
163
+        try {
164
+            Workbook workbook = WorkbookFactory.create(file.getInputStream());
165
+            Sheet sheet = workbook.getSheetAt(0);
166
+            int rows = sheet.getLastRowNum();
167
+            
168
+            // 从第二行开始读取数据(第一行为表头)
169
+            for (int i = 1; i <= rows; i++) {
170
+                Row row = sheet.getRow(i);
171
+                if (row == null || isRowEmpty(row)) {
172
+                    continue;
173
+                }
174
+                
175
+                Map<String, Object> rowMap = new HashMap<>();
176
+                int lastCellNum = Math.min(row.getLastCellNum(), columns.size());
177
+                for (int j = 0; j < lastCellNum; j++) {
178
+                    Cell cell = row.getCell(j);
179
+                    Object value = getCellValue(cell);
180
+                    rowMap.put(columns.get(j), value);
181
+                }
182
+                dataList.add(rowMap);
183
+            }
184
+            workbook.close();
185
+        } catch (Exception e) {
186
+            e.printStackTrace();
187
+        }
188
+        return dataList;
189
+    }
190
+    
191
+    /**
192
+     * 判断行是否为空
193
+     */
194
+    private boolean isRowEmpty(Row row) {
195
+        if (row == null) {
196
+            return true;
197
+        }
198
+        for (int i = row.getFirstCellNum(); i < row.getLastCellNum(); i++) {
199
+            Cell cell = row.getCell(i);
200
+            if (cell != null && cell.getCellType() != CellType.BLANK) {
201
+                // 使用getCellValue方法处理多种类型,避免数字类型调用getStringCellValue抛出异常
202
+                Object value = getCellValue(cell);
203
+                if (value != null) {
204
+                    String strValue = String.valueOf(value).trim();
205
+                    if (!strValue.isEmpty()) {
206
+                        return false;
207
+                    }
208
+                }
209
+            }
210
+        }
211
+        return true;
212
+    }
213
+    
214
+    /**
215
+     * 获取单元格值
216
+     */
217
+    private Object getCellValue(Cell cell) {
218
+        if (cell == null) {
219
+            return null;
220
+        }
221
+        switch (cell.getCellType()) {
222
+            case STRING:
223
+                return cell.getStringCellValue();
224
+            case NUMERIC:
225
+                if (DateUtil.isCellDateFormatted(cell)) {
226
+                    return cell.getLocalDateTimeCellValue();
227
+                } else {
228
+                    // 避免科学计数法
229
+                    double value = cell.getNumericCellValue();
230
+                    if (value == Math.floor(value)) {
231
+                        return (long) value;
232
+                    }
233
+                    return value;
234
+                }
235
+            case BOOLEAN:
236
+                return cell.getBooleanCellValue();
237
+            case FORMULA:
238
+                try {
239
+                    return cell.getStringCellValue();
240
+                } catch (Exception e) {
241
+                    return cell.getNumericCellValue();
242
+                }
243
+            default:
244
+                return null;
245
+        }
246
+    }
113 247
 
114 248
     /**
115 249
      * 执行空间直角坐标转大地坐标计算
@@ -177,7 +311,7 @@ public class CmcSpatialToGeodeticServiceImpl implements ICmcSpatialToGeodeticSer
177 311
                            Math.floor(((B * 60 - Math.floor(B * 60)) * 60 + Math.pow(10, -11)) * Math.pow(10, 6))
178 312
                         / Math.pow(10, 10);
179 313
         String longitudePosition = spatialY < 0 ? "W" : "E";
180
-        String latitudePosition = spatialX < 0 ? "S" : "N";
314
+        String latitudePosition = spatialZ < 0 ? "S" : "N";
181 315
         double height = Math.sqrt(Math.pow(spatialX, 2) + Math.pow(spatialY, 2) + Math.pow(spatialZ + t, 2)) - N;
182 316
         cmcSpatialToGeodetic.setLongitude(longitude);
183 317
         cmcSpatialToGeodetic.setLatitude(latitude);

+ 177
- 42
oa-back/ruoyi-calculate/src/main/java/com/ruoyi/web/calculate/service/impl/CmcUTMBandChangeServicelmpl.java Datei anzeigen

@@ -18,6 +18,13 @@ import com.ruoyi.common.core.domain.AjaxResult;
18 18
 import com.ruoyi.common.utils.poi.ExcelUtil;
19 19
 import com.ruoyi.web.calculate.domain.CmcUTMBandChange;
20 20
 import com.ruoyi.web.calculate.service.ICmcUTMBandChangeService;
21
+import org.apache.poi.ss.usermodel.Cell;
22
+import org.apache.poi.ss.usermodel.CellType;
23
+import org.apache.poi.ss.usermodel.DateUtil;
24
+import org.apache.poi.ss.usermodel.Row;
25
+import org.apache.poi.ss.usermodel.Sheet;
26
+import org.apache.poi.ss.usermodel.Workbook;
27
+import org.apache.poi.ss.usermodel.WorkbookFactory;
21 28
 
22 29
 /**
23 30
  * UTM换带服务实现
@@ -50,8 +57,12 @@ public class CmcUTMBandChangeServicelmpl implements ICmcUTMBandChangeService {
50 57
      */
51 58
     @Override
52 59
     @Transactional(rollbackFor = Exception.class)
53
-    public AjaxResult importExcelData(MultipartFile file) {
60
+    public AjaxResult importExcelData(MultipartFile file)
61
+    {
54 62
         try {
63
+            // 获取Excel表头信息
64
+            List<String> excelColumns = getExcelColumns(file);
65
+
55 66
             // 使用若依的ExcelUtil工具类
56 67
             ExcelUtil<CmcUTMBandChange> util = new ExcelUtil<CmcUTMBandChange>(CmcUTMBandChange.class);
57 68
             List<CmcUTMBandChange> dataList = util.importExcel(file.getInputStream());
@@ -61,52 +72,17 @@ public class CmcUTMBandChangeServicelmpl implements ICmcUTMBandChangeService {
61 72
                 return AjaxResult.error("Excel文件中没有数据!");
62 73
             }
63 74
 
64
-            // 验证数据并转换为前端需要的格式
65
-            List<Map<String, Object>> validData = new ArrayList<>();
66
-            int successCount = 0;
67
-            StringBuilder errorMsg = new StringBuilder();
68
-
69
-            for (int i = 0; i < dataList.size(); i++) {
70
-                CmcUTMBandChange item = dataList.get(i);
71
-
72
-                try {
73
-                    // 转换为前端需要的Map格式
74
-                    Map<String, Object> rowMap = new HashMap<>();
75
-                    rowMap.put("pointNumber", item.getPointNumber());
76
-                    rowMap.put("coordinateSystem", item.getCoordinateSystem());
77
-                    rowMap.put("utmX", item.getUtmX());
78
-                    rowMap.put("utmY", item.getUtmY());
79
-                    rowMap.put("band", item.getBand());
80
-                    rowMap.put("newBand", item.getNewBand());
81
-                    rowMap.put("longitudePosition", item.getLongitudePosition());
82
-                    rowMap.put("latitudePosition", item.getLatitudePosition());
83
-                    rowMap.put("projectionHeight", item.getProjectionHeight());
84
-                    rowMap.put("rowNum", i + 2); // Excel中的行号
85
-
86
-                    validData.add(rowMap);
87
-                    successCount++;
88
-
89
-                } catch (Exception e) {
90
-                    errorMsg.append(String.format("第%d行数据验证失败:%s;", i + 2, e.getMessage()));
91
-                }
92
-            }
75
+            // 返回原始Excel数据格式(使用表头作为key)
76
+            List<Map<String, Object>> rawData = readRawExcelData(file, excelColumns);
93 77
 
94 78
             // 构建返回结果
95 79
             Map<String, Object> result = new HashMap<>();
96 80
             result.put("total", dataList.size());
97
-            result.put("validCount", successCount);
98
-            result.put("data", validData);
99
-
100
-            if (successCount == 0) {
101
-                return AjaxResult.error("没有有效数据!" + errorMsg.toString());
102
-            }
103
-
104
-            String message = String.format("导入成功!共解析%d条数据,有效数据%d条", dataList.size(), successCount);
105
-            if (errorMsg.length() > 0) {
106
-                message += ";部分数据验证失败:" + errorMsg.toString();
107
-                return AjaxResult.success(message, result);
108
-            }
81
+            result.put("validCount", dataList.size());
82
+            result.put("data", rawData);
83
+            result.put("columns", excelColumns); // 返回Excel表头
109 84
 
85
+            String message = String.format("导入成功!共解析%d条数据", dataList.size());
110 86
             return AjaxResult.success(message, result);
111 87
 
112 88
         } catch (Exception e) {
@@ -114,6 +90,165 @@ public class CmcUTMBandChangeServicelmpl implements ICmcUTMBandChangeService {
114 90
             return AjaxResult.error("导入失败:" + e.getMessage());
115 91
         }
116 92
     }
93
+    
94
+    /**
95
+     * 获取Excel表头信息
96
+     */
97
+    private List<String> getExcelColumns(MultipartFile file) {
98
+        List<String> columns = new ArrayList<>();
99
+        try {
100
+            org.apache.poi.ss.usermodel.Workbook workbook = org.apache.poi.ss.usermodel.WorkbookFactory.create(file.getInputStream());
101
+            org.apache.poi.ss.usermodel.Sheet sheet = workbook.getSheetAt(0);
102
+            org.apache.poi.ss.usermodel.Row headerRow = sheet.getRow(0); // 第一行为表头
103
+            
104
+            if (headerRow != null) {
105
+                int lastCellNum = headerRow.getLastCellNum();
106
+                for (int i = 0; i < lastCellNum; i++) {
107
+                    org.apache.poi.ss.usermodel.Cell cell = headerRow.getCell(i);
108
+                    String cellValue = null;
109
+                    if (cell != null) {
110
+                        cellValue = getCellStringValue(cell);
111
+                    }
112
+                    if (cellValue != null && !cellValue.trim().isEmpty()) {
113
+                        columns.add(cellValue.trim());
114
+                    } else {
115
+                        columns.add("列" + (i + 1));
116
+                    }
117
+                }
118
+            }
119
+            workbook.close();
120
+        } catch (Exception e) {
121
+            e.printStackTrace();
122
+        }
123
+        return columns;
124
+    }
125
+    
126
+    /**
127
+     * 获取单元格的字符串值(处理多种类型)
128
+     */
129
+    private String getCellStringValue(org.apache.poi.ss.usermodel.Cell cell) {
130
+        if (cell == null) {
131
+            return null;
132
+        }
133
+        switch (cell.getCellType()) {
134
+            case STRING:
135
+                return cell.getStringCellValue();
136
+            case NUMERIC:
137
+                if (DateUtil.isCellDateFormatted(cell)) {
138
+                    return cell.getLocalDateTimeCellValue().toString();
139
+                } else {
140
+                    double value = cell.getNumericCellValue();
141
+                    if (value == Math.floor(value)) {
142
+                        return String.valueOf((long) value);
143
+                    }
144
+                    return String.valueOf(value);
145
+                }
146
+            case BOOLEAN:
147
+                return String.valueOf(cell.getBooleanCellValue());
148
+            case FORMULA:
149
+                try {
150
+                    return cell.getStringCellValue();
151
+                } catch (Exception e) {
152
+                    try {
153
+                        return String.valueOf(cell.getNumericCellValue());
154
+                    } catch (Exception ex) {
155
+                        return cell.getCellFormula();
156
+                    }
157
+                }
158
+            default:
159
+                return null;
160
+        }
161
+    }
162
+    
163
+    /**
164
+     * 读取原始Excel数据(使用表头作为key)
165
+     */
166
+    private List<Map<String, Object>> readRawExcelData(MultipartFile file, List<String> columns) {
167
+        List<Map<String, Object>> dataList = new ArrayList<>();
168
+        try {
169
+            Workbook workbook = WorkbookFactory.create(file.getInputStream());
170
+            Sheet sheet = workbook.getSheetAt(0);
171
+            int rows = sheet.getLastRowNum();
172
+            
173
+            // 从第二行开始读取数据(第一行为表头)
174
+            for (int i = 1; i <= rows; i++) {
175
+                Row row = sheet.getRow(i);
176
+                if (row == null || isRowEmpty(row)) {
177
+                    continue;
178
+                }
179
+                
180
+                Map<String, Object> rowMap = new HashMap<>();
181
+                int lastCellNum = Math.min(row.getLastCellNum(), columns.size());
182
+                for (int j = 0; j < lastCellNum; j++) {
183
+                    Cell cell = row.getCell(j);
184
+                    Object value = getCellValue(cell);
185
+                    rowMap.put(columns.get(j), value);
186
+                }
187
+                dataList.add(rowMap);
188
+            }
189
+            workbook.close();
190
+        } catch (Exception e) {
191
+            e.printStackTrace();
192
+        }
193
+        return dataList;
194
+    }
195
+    
196
+    /**
197
+     * 判断行是否为空
198
+     */
199
+    private boolean isRowEmpty(Row row) {
200
+        if (row == null) {
201
+            return true;
202
+        }
203
+        for (int i = row.getFirstCellNum(); i < row.getLastCellNum(); i++) {
204
+            Cell cell = row.getCell(i);
205
+            if (cell != null && cell.getCellType() != CellType.BLANK) {
206
+                // 使用getCellValue方法处理多种类型,避免数字类型调用getStringCellValue抛出异常
207
+                Object value = getCellValue(cell);
208
+                if (value != null) {
209
+                    String strValue = String.valueOf(value).trim();
210
+                    if (!strValue.isEmpty()) {
211
+                        return false;
212
+                    }
213
+                }
214
+            }
215
+        }
216
+        return true;
217
+    }
218
+    
219
+    /**
220
+     * 获取单元格值
221
+     */
222
+    private Object getCellValue(Cell cell) {
223
+        if (cell == null) {
224
+            return null;
225
+        }
226
+        switch (cell.getCellType()) {
227
+            case STRING:
228
+                return cell.getStringCellValue();
229
+            case NUMERIC:
230
+                if (DateUtil.isCellDateFormatted(cell)) {
231
+                    return cell.getLocalDateTimeCellValue();
232
+                } else {
233
+                    // 避免科学计数法
234
+                    double value = cell.getNumericCellValue();
235
+                    if (value == Math.floor(value)) {
236
+                        return (long) value;
237
+                    }
238
+                    return value;
239
+                }
240
+            case BOOLEAN:
241
+                return cell.getBooleanCellValue();
242
+            case FORMULA:
243
+                try {
244
+                    return cell.getStringCellValue();
245
+                } catch (Exception e) {
246
+                    return cell.getNumericCellValue();
247
+                }
248
+            default:
249
+                return null;
250
+        }
251
+    }
117 252
 
118 253
     /**
119 254
      * 执行UTM换带计算

+ 175
- 40
oa-back/ruoyi-calculate/src/main/java/com/ruoyi/web/calculate/service/impl/CmcUTMNegativeServiceImpl.java Datei anzeigen

@@ -16,6 +16,13 @@ import com.ruoyi.common.core.domain.AjaxResult;
16 16
 import com.ruoyi.common.utils.poi.ExcelUtil;
17 17
 import com.ruoyi.web.calculate.domain.CmcUTMNegative;
18 18
 import com.ruoyi.web.calculate.service.ICmcUTMNegativeService;
19
+import org.apache.poi.ss.usermodel.Cell;
20
+import org.apache.poi.ss.usermodel.CellType;
21
+import org.apache.poi.ss.usermodel.DateUtil;
22
+import org.apache.poi.ss.usermodel.Row;
23
+import org.apache.poi.ss.usermodel.Sheet;
24
+import org.apache.poi.ss.usermodel.Workbook;
25
+import org.apache.poi.ss.usermodel.WorkbookFactory;
19 26
 
20 27
 /**
21 28
  * UTM反算服务实现
@@ -54,6 +61,9 @@ public class CmcUTMNegativeServiceImpl implements ICmcUTMNegativeService
54 61
     public AjaxResult importExcelData(MultipartFile file)
55 62
     {
56 63
         try {
64
+            // 获取Excel表头信息
65
+            List<String> excelColumns = getExcelColumns(file);
66
+
57 67
             // 使用若依的ExcelUtil工具类
58 68
             ExcelUtil<CmcUTMNegative> util = new ExcelUtil<CmcUTMNegative>(CmcUTMNegative.class);
59 69
             List<CmcUTMNegative> dataList = util.importExcel(file.getInputStream());
@@ -63,51 +73,17 @@ public class CmcUTMNegativeServiceImpl implements ICmcUTMNegativeService
63 73
                 return AjaxResult.error("Excel文件中没有数据!");
64 74
             }
65 75
 
66
-            // 验证数据并转换为前端需要的格式
67
-            List<Map<String, Object>> validData = new ArrayList<>();
68
-            int successCount = 0;
69
-            StringBuilder errorMsg = new StringBuilder();
70
-
71
-            for (int i = 0; i < dataList.size(); i++) {
72
-                CmcUTMNegative item = dataList.get(i);
73
-
74
-                try {
75
-                    // 转换为前端需要的Map格式
76
-                    Map<String, Object> rowMap = new HashMap<>();
77
-                    rowMap.put("pointNumber", item.getPointNumber());
78
-                    rowMap.put("coordinateSystem", item.getCoordinateSystem());
79
-                    rowMap.put("utmX", item.getUtmX());
80
-                    rowMap.put("utmY", item.getUtmY());
81
-                    rowMap.put("band", item.getBand());
82
-                    rowMap.put("projectionHeight", item.getProjectionHeight());
83
-                    rowMap.put("longitudePosition", item.getLongitudePosition());
84
-                    rowMap.put("latitudePosition", item.getLatitudePosition());
85
-                    rowMap.put("rowNum", i + 2); // Excel中的行号
86
-
87
-                    validData.add(rowMap); 
88
-                    successCount++;
89
-
90
-                } catch (Exception e) {
91
-                    errorMsg.append(String.format("第%d行数据验证失败:%s;", i + 2, e.getMessage()));
92
-                }
93
-            }
76
+            // 返回原始Excel数据格式(使用表头作为key)
77
+            List<Map<String, Object>> rawData = readRawExcelData(file, excelColumns);
94 78
 
95 79
             // 构建返回结果
96 80
             Map<String, Object> result = new HashMap<>();
97 81
             result.put("total", dataList.size());
98
-            result.put("validCount", successCount);
99
-            result.put("data", validData);
100
-
101
-            if (successCount == 0) {
102
-                return AjaxResult.error("没有有效数据!" + errorMsg.toString());
103
-            }
104
-
105
-            String message = String.format("导入成功!共解析%d条数据,有效数据%d条", dataList.size(), successCount);
106
-            if (errorMsg.length() > 0) {
107
-                message += ";部分数据验证失败:" + errorMsg.toString();
108
-                return AjaxResult.success(message, result);
109
-            }
82
+            result.put("validCount", dataList.size());
83
+            result.put("data", rawData);
84
+            result.put("columns", excelColumns); // 返回Excel表头
110 85
 
86
+            String message = String.format("导入成功!共解析%d条数据", dataList.size());
111 87
             return AjaxResult.success(message, result);
112 88
 
113 89
         } catch (Exception e) {
@@ -115,6 +91,165 @@ public class CmcUTMNegativeServiceImpl implements ICmcUTMNegativeService
115 91
             return AjaxResult.error("导入失败:" + e.getMessage());
116 92
         }
117 93
     }
94
+    
95
+    /**
96
+     * 获取Excel表头信息
97
+     */
98
+    private List<String> getExcelColumns(MultipartFile file) {
99
+        List<String> columns = new ArrayList<>();
100
+        try {
101
+            org.apache.poi.ss.usermodel.Workbook workbook = org.apache.poi.ss.usermodel.WorkbookFactory.create(file.getInputStream());
102
+            org.apache.poi.ss.usermodel.Sheet sheet = workbook.getSheetAt(0);
103
+            org.apache.poi.ss.usermodel.Row headerRow = sheet.getRow(0); // 第一行为表头
104
+            
105
+            if (headerRow != null) {
106
+                int lastCellNum = headerRow.getLastCellNum();
107
+                for (int i = 0; i < lastCellNum; i++) {
108
+                    org.apache.poi.ss.usermodel.Cell cell = headerRow.getCell(i);
109
+                    String cellValue = null;
110
+                    if (cell != null) {
111
+                        cellValue = getCellStringValue(cell);
112
+                    }
113
+                    if (cellValue != null && !cellValue.trim().isEmpty()) {
114
+                        columns.add(cellValue.trim());
115
+                    } else {
116
+                        columns.add("列" + (i + 1));
117
+                    }
118
+                }
119
+            }
120
+            workbook.close();
121
+        } catch (Exception e) {
122
+            e.printStackTrace();
123
+        }
124
+        return columns;
125
+    }
126
+    
127
+    /**
128
+     * 获取单元格的字符串值(处理多种类型)
129
+     */
130
+    private String getCellStringValue(org.apache.poi.ss.usermodel.Cell cell) {
131
+        if (cell == null) {
132
+            return null;
133
+        }
134
+        switch (cell.getCellType()) {
135
+            case STRING:
136
+                return cell.getStringCellValue();
137
+            case NUMERIC:
138
+                if (DateUtil.isCellDateFormatted(cell)) {
139
+                    return cell.getLocalDateTimeCellValue().toString();
140
+                } else {
141
+                    double value = cell.getNumericCellValue();
142
+                    if (value == Math.floor(value)) {
143
+                        return String.valueOf((long) value);
144
+                    }
145
+                    return String.valueOf(value);
146
+                }
147
+            case BOOLEAN:
148
+                return String.valueOf(cell.getBooleanCellValue());
149
+            case FORMULA:
150
+                try {
151
+                    return cell.getStringCellValue();
152
+                } catch (Exception e) {
153
+                    try {
154
+                        return String.valueOf(cell.getNumericCellValue());
155
+                    } catch (Exception ex) {
156
+                        return cell.getCellFormula();
157
+                    }
158
+                }
159
+            default:
160
+                return null;
161
+        }
162
+    }
163
+    
164
+    /**
165
+     * 读取原始Excel数据(使用表头作为key)
166
+     */
167
+    private List<Map<String, Object>> readRawExcelData(MultipartFile file, List<String> columns) {
168
+        List<Map<String, Object>> dataList = new ArrayList<>();
169
+        try {
170
+            Workbook workbook = WorkbookFactory.create(file.getInputStream());
171
+            Sheet sheet = workbook.getSheetAt(0);
172
+            int rows = sheet.getLastRowNum();
173
+            
174
+            // 从第二行开始读取数据(第一行为表头)
175
+            for (int i = 1; i <= rows; i++) {
176
+                Row row = sheet.getRow(i);
177
+                if (row == null || isRowEmpty(row)) {
178
+                    continue;
179
+                }
180
+                
181
+                Map<String, Object> rowMap = new HashMap<>();
182
+                int lastCellNum = Math.min(row.getLastCellNum(), columns.size());
183
+                for (int j = 0; j < lastCellNum; j++) {
184
+                    Cell cell = row.getCell(j);
185
+                    Object value = getCellValue(cell);
186
+                    rowMap.put(columns.get(j), value);
187
+                }
188
+                dataList.add(rowMap);
189
+            }
190
+            workbook.close();
191
+        } catch (Exception e) {
192
+            e.printStackTrace();
193
+        }
194
+        return dataList;
195
+    }
196
+    
197
+    /**
198
+     * 判断行是否为空
199
+     */
200
+    private boolean isRowEmpty(Row row) {
201
+        if (row == null) {
202
+            return true;
203
+        }
204
+        for (int i = row.getFirstCellNum(); i < row.getLastCellNum(); i++) {
205
+            Cell cell = row.getCell(i);
206
+            if (cell != null && cell.getCellType() != CellType.BLANK) {
207
+                // 使用getCellValue方法处理多种类型,避免数字类型调用getStringCellValue抛出异常
208
+                Object value = getCellValue(cell);
209
+                if (value != null) {
210
+                    String strValue = String.valueOf(value).trim();
211
+                    if (!strValue.isEmpty()) {
212
+                        return false;
213
+                    }
214
+                }
215
+            }
216
+        }
217
+        return true;
218
+    }
219
+    
220
+    /**
221
+     * 获取单元格值
222
+     */
223
+    private Object getCellValue(Cell cell) {
224
+        if (cell == null) {
225
+            return null;
226
+        }
227
+        switch (cell.getCellType()) {
228
+            case STRING:
229
+                return cell.getStringCellValue();
230
+            case NUMERIC:
231
+                if (DateUtil.isCellDateFormatted(cell)) {
232
+                    return cell.getLocalDateTimeCellValue();
233
+                } else {
234
+                    // 避免科学计数法
235
+                    double value = cell.getNumericCellValue();
236
+                    if (value == Math.floor(value)) {
237
+                        return (long) value;
238
+                    }
239
+                    return value;
240
+                }
241
+            case BOOLEAN:
242
+                return cell.getBooleanCellValue();
243
+            case FORMULA:
244
+                try {
245
+                    return cell.getStringCellValue();
246
+                } catch (Exception e) {
247
+                    return cell.getNumericCellValue();
248
+                }
249
+            default:
250
+                return null;
251
+        }
252
+    }
118 253
 
119 254
     /**
120 255
      * 执行UTM反算

+ 133
- 40
oa-back/ruoyi-calculate/src/main/java/com/ruoyi/web/calculate/service/impl/CmcUTMPositiveServiceImpl.java Datei anzeigen

@@ -18,6 +18,13 @@ import com.ruoyi.common.core.domain.AjaxResult;
18 18
 import com.ruoyi.common.utils.poi.ExcelUtil;
19 19
 import com.ruoyi.web.calculate.domain.CmcUTMPositive;
20 20
 import com.ruoyi.web.calculate.service.ICmcUTMPositiveService;
21
+import org.apache.poi.ss.usermodel.Cell;
22
+import org.apache.poi.ss.usermodel.CellType;
23
+import org.apache.poi.ss.usermodel.DateUtil;
24
+import org.apache.poi.ss.usermodel.Row;
25
+import org.apache.poi.ss.usermodel.Sheet;
26
+import org.apache.poi.ss.usermodel.Workbook;
27
+import org.apache.poi.ss.usermodel.WorkbookFactory;
21 28
 
22 29
 /**
23 30
  * UTM正算服务实现
@@ -55,6 +62,9 @@ public class CmcUTMPositiveServiceImpl implements ICmcUTMPositiveService
55 62
     public AjaxResult importExcelData(MultipartFile file)
56 63
     {
57 64
         try {
65
+            // 获取Excel表头信息
66
+            List<String> excelColumns = getExcelColumns(file);
67
+
58 68
             // 使用若依的ExcelUtil工具类
59 69
             ExcelUtil<CmcUTMPositive> util = new ExcelUtil<CmcUTMPositive>(CmcUTMPositive.class);
60 70
             List<CmcUTMPositive> dataList = util.importExcel(file.getInputStream());
@@ -64,51 +74,17 @@ public class CmcUTMPositiveServiceImpl implements ICmcUTMPositiveService
64 74
                 return AjaxResult.error("Excel文件中没有数据!");
65 75
             }
66 76
 
67
-            // 验证数据并转换为前端需要的格式
68
-            List<Map<String, Object>> validData = new ArrayList<>();
69
-            int successCount = 0;
70
-            StringBuilder errorMsg = new StringBuilder();
71
-
72
-            for (int i = 0; i < dataList.size(); i++) {
73
-                CmcUTMPositive item = dataList.get(i);
74
-
75
-                try {
76
-                    // 转换为前端需要的Map格式
77
-                    Map<String, Object> rowMap = new HashMap<>();
78
-                    rowMap.put("pointNumber", item.getPointNumber());
79
-                    rowMap.put("coordinateSystem", item.getCoordinateSystem());
80
-                    rowMap.put("longitude", item.getLongitude());
81
-                    rowMap.put("longitudePosition", item.getLongitudePosition());
82
-                    rowMap.put("latitude", item.getLatitude());
83
-                    rowMap.put("latitudePosition", item.getLatitudePosition());
84
-                    rowMap.put("band", item.getBand());
85
-                    rowMap.put("projectionHeight", item.getProjectionHeight());
86
-                    rowMap.put("rowNum", i + 2); // Excel中的行号
87
-
88
-                    validData.add(rowMap);
89
-                    successCount++;
90
-
91
-                } catch (Exception e) {
92
-                    errorMsg.append(String.format("第%d行数据验证失败:%s;", i + 2, e.getMessage()));
93
-                }
94
-            }
77
+            // 返回原始Excel数据格式(使用表头作为key)
78
+            List<Map<String, Object>> rawData = readRawExcelData(file, excelColumns);
95 79
 
96 80
             // 构建返回结果
97 81
             Map<String, Object> result = new HashMap<>();
98 82
             result.put("total", dataList.size());
99
-            result.put("validCount", successCount);
100
-            result.put("data", validData);
101
-
102
-            if (successCount == 0) {
103
-                return AjaxResult.error("没有有效数据!" + errorMsg.toString());
104
-            }
105
-
106
-            String message = String.format("导入成功!共解析%d条数据,有效数据%d条", dataList.size(), successCount);
107
-            if (errorMsg.length() > 0) {
108
-                message += ";部分数据验证失败:" + errorMsg.toString();
109
-                return AjaxResult.success(message, result);
110
-            }
83
+            result.put("validCount", dataList.size());
84
+            result.put("data", rawData);
85
+            result.put("columns", excelColumns); // 返回Excel表头
111 86
 
87
+            String message = String.format("导入成功!共解析%d条数据", dataList.size());
112 88
             return AjaxResult.success(message, result);
113 89
 
114 90
         } catch (Exception e) {
@@ -116,6 +92,123 @@ public class CmcUTMPositiveServiceImpl implements ICmcUTMPositiveService
116 92
             return AjaxResult.error("导入失败:" + e.getMessage());
117 93
         }
118 94
     }
95
+    
96
+    /**
97
+     * 获取Excel表头信息
98
+     */
99
+    private List<String> getExcelColumns(MultipartFile file) {
100
+        List<String> columns = new ArrayList<>();
101
+        try {
102
+            org.apache.poi.ss.usermodel.Workbook workbook = org.apache.poi.ss.usermodel.WorkbookFactory.create(file.getInputStream());
103
+            org.apache.poi.ss.usermodel.Sheet sheet = workbook.getSheetAt(0);
104
+            org.apache.poi.ss.usermodel.Row headerRow = sheet.getRow(0); // 第一行为表头
105
+            
106
+            if (headerRow != null) {
107
+                for (int i = 0; i < headerRow.getPhysicalNumberOfCells(); i++) {
108
+                    org.apache.poi.ss.usermodel.Cell cell = headerRow.getCell(i);
109
+                    if (cell != null) {
110
+                        String cellValue = cell.getStringCellValue();
111
+                        if (cellValue != null && !cellValue.trim().isEmpty()) {
112
+                            columns.add(cellValue.trim());
113
+                        }
114
+                    }
115
+                }
116
+            }
117
+            workbook.close();
118
+        } catch (Exception e) {
119
+            e.printStackTrace();
120
+        }
121
+        return columns;
122
+    }
123
+    
124
+    /**
125
+     * 读取原始Excel数据(使用表头作为key)
126
+     */
127
+    private List<Map<String, Object>> readRawExcelData(MultipartFile file, List<String> columns) {
128
+        List<Map<String, Object>> dataList = new ArrayList<>();
129
+        try {
130
+            Workbook workbook = WorkbookFactory.create(file.getInputStream());
131
+            Sheet sheet = workbook.getSheetAt(0);
132
+            int rows = sheet.getLastRowNum();
133
+            
134
+            // 从第二行开始读取数据(第一行为表头)
135
+            for (int i = 1; i <= rows; i++) {
136
+                Row row = sheet.getRow(i);
137
+                if (row == null || isRowEmpty(row)) {
138
+                    continue;
139
+                }
140
+                
141
+                Map<String, Object> rowMap = new HashMap<>();
142
+                for (int j = 0; j < columns.size(); j++) {
143
+                    Cell cell = row.getCell(j);
144
+                    Object value = getCellValue(cell);
145
+                    rowMap.put(columns.get(j), value);
146
+                }
147
+                dataList.add(rowMap);
148
+            }
149
+            workbook.close();
150
+        } catch (Exception e) {
151
+            e.printStackTrace();
152
+        }
153
+        return dataList;
154
+    }
155
+    
156
+    /**
157
+     * 判断行是否为空
158
+     */
159
+    private boolean isRowEmpty(Row row) {
160
+        if (row == null) {
161
+            return true;
162
+        }
163
+        for (int i = row.getFirstCellNum(); i < row.getLastCellNum(); i++) {
164
+            Cell cell = row.getCell(i);
165
+            if (cell != null && cell.getCellType() != CellType.BLANK) {
166
+                // 使用getCellValue方法处理多种类型,避免数字类型调用getStringCellValue抛出异常
167
+                Object value = getCellValue(cell);
168
+                if (value != null) {
169
+                    String strValue = String.valueOf(value).trim();
170
+                    if (!strValue.isEmpty()) {
171
+                        return false;
172
+                    }
173
+                }
174
+            }
175
+        }
176
+        return true;
177
+    }
178
+    
179
+    /**
180
+     * 获取单元格值
181
+     */
182
+    private Object getCellValue(Cell cell) {
183
+        if (cell == null) {
184
+            return null;
185
+        }
186
+        switch (cell.getCellType()) {
187
+            case STRING:
188
+                return cell.getStringCellValue();
189
+            case NUMERIC:
190
+                if (DateUtil.isCellDateFormatted(cell)) {
191
+                    return cell.getLocalDateTimeCellValue();
192
+                } else {
193
+                    // 避免科学计数法
194
+                    double value = cell.getNumericCellValue();
195
+                    if (value == Math.floor(value)) {
196
+                        return (long) value;
197
+                    }
198
+                    return value;
199
+                }
200
+            case BOOLEAN:
201
+                return cell.getBooleanCellValue();
202
+            case FORMULA:
203
+                try {
204
+                    return cell.getStringCellValue();
205
+                } catch (Exception e) {
206
+                    return cell.getNumericCellValue();
207
+                }
208
+            default:
209
+                return null;
210
+        }
211
+    }
119 212
 
120 213
     /**
121 214
      * 执行UTM正算

+ 0
- 125
oa-back/ruoyi-calculate/src/main/java/com/ruoyi/web/calculate/vo/CmcGaussBandChangeTemplate.java Datei anzeigen

@@ -1,125 +0,0 @@
1
-package com.ruoyi.web.calculate.vo;
2
-
3
-import java.math.BigDecimal;
4
-import java.math.RoundingMode;
5
-
6
-import com.ruoyi.common.annotation.Excel;
7
-import com.ruoyi.common.annotation.Excel.ColumnType;
8
-
9
-public class CmcGaussBandChangeTemplate {
10
-
11
-    @Excel(name = "待求点")
12
-    private String pointNumber;
13
-
14
-    @Excel(name = "坐标系")
15
-    private String coordinateSystem;
16
-
17
-    @Excel(name = "高斯坐标x", cellType = ColumnType.NUMERIC)
18
-    private Double gaussX;
19
-
20
-    @Excel(name = "高斯坐标y", cellType = ColumnType.NUMERIC)
21
-    private Double gaussY;
22
-
23
-     /** 带号 */
24
-    @Excel(name = "带号", cellType = ColumnType.NUMERIC)
25
-    private Integer band;
26
-
27
-    /** 带宽 */
28
-    @Excel(name = "带宽", cellType = ColumnType.NUMERIC)
29
-    private Integer bandwidth;
30
-
31
-    /** 新带号 */
32
-    @Excel(name = "新带号", cellType = ColumnType.NUMERIC)
33
-    private Integer newBand;
34
-
35
-    /** 新带宽 */
36
-    @Excel(name = "新带宽", cellType = ColumnType.NUMERIC)
37
-    private Integer newBandwidth;
38
-
39
-    @Excel(name = "经度位置")
40
-    private String longitudePosition;
41
-
42
-    @Excel(name = "纬度位置")
43
-    private String latitudePosition;
44
-
45
-    public void setPointNumber(String pointNumber) {
46
-        this.pointNumber = pointNumber;
47
-    }
48
-
49
-    public String getPointNumber() {
50
-        return pointNumber;
51
-    }
52
-
53
-    public void setCoordinateSystem(String coordinateSystem) {
54
-        this.coordinateSystem = coordinateSystem;
55
-    }
56
-
57
-    public String getCoordinateSystem() {
58
-        return coordinateSystem;
59
-    }
60
-
61
-    public void setGaussX(Double gaussX) {
62
-        this.gaussX = gaussX;
63
-    }
64
-
65
-    public Double getGaussX() {
66
-        return gaussX;
67
-    }
68
-
69
-    public void setGaussY(Double gaussY) {
70
-        this.gaussY = gaussY;
71
-    }
72
-
73
-    public Double getGaussY() {
74
-        return gaussY;
75
-    }
76
-
77
-    public void setBand(Integer band) {
78
-        this.band = band;
79
-    }
80
-
81
-    public Integer getBand() {
82
-        return band;
83
-    }
84
-    
85
-    public void setBandwidth(Integer bandwidth) {
86
-        this.bandwidth = bandwidth;
87
-    }
88
-
89
-    public Integer getBandwidth() {
90
-        return bandwidth;
91
-    }
92
-    
93
-    public void setNewBand(Integer newBand) {
94
-        this.newBand = newBand;
95
-    }
96
-
97
-    public Integer getNewBand() {
98
-        return newBand;
99
-    }
100
-    
101
-    public void setNewBandwidth(Integer newBandwidth) {
102
-        this.newBandwidth = newBandwidth;
103
-    }
104
-
105
-    public Integer getNewBandwidth() {
106
-        return newBandwidth;
107
-    }
108
-
109
-    public void setLongitudePosition(String longitudePosition) {
110
-        this.longitudePosition = longitudePosition;
111
-    }
112
-
113
-    public String getLongitudePosition() {
114
-        return longitudePosition;
115
-    }
116
-
117
-    public void setLatitudePosition(String latitudePosition) {
118
-        this.latitudePosition = latitudePosition;
119
-    }
120
-
121
-    public String getLatitudePosition() {
122
-        return latitudePosition;
123
-    }
124
-
125
-}

+ 0
- 76
oa-back/ruoyi-calculate/src/main/java/com/ruoyi/web/calculate/vo/CmcGaussNegativeTemplate.java Datei anzeigen

@@ -1,76 +0,0 @@
1
-package com.ruoyi.web.calculate.vo;
2
-
3
-import java.math.BigDecimal;
4
-import java.math.RoundingMode;
5
-
6
-import com.ruoyi.common.annotation.Excel;
7
-import com.ruoyi.common.annotation.Excel.ColumnType;
8
-
9
-public class CmcGaussNegativeTemplate {
10
-
11
-    @Excel(name = "待求点")
12
-    private String pointNumber;
13
-
14
-    @Excel(name = "坐标系")
15
-    private String coordinateSystem;
16
-
17
-    @Excel(name = "高斯坐标x", cellType = ColumnType.NUMERIC)
18
-    private Double gaussX;
19
-
20
-    @Excel(name = "高斯坐标y", cellType = ColumnType.NUMERIC)
21
-    private Double gaussY;
22
-
23
-    @Excel(name = "经度位置")
24
-    private String longitudePosition;
25
-
26
-    @Excel(name = "纬度位置")
27
-    private String latitudePosition;
28
-
29
-    public void setPointNumber(String pointNumber) {
30
-        this.pointNumber = pointNumber;
31
-    }
32
-
33
-    public String getPointNumber() {
34
-        return pointNumber;
35
-    }
36
-
37
-    public void setCoordinateSystem(String coordinateSystem) {
38
-        this.coordinateSystem = coordinateSystem;
39
-    }
40
-
41
-    public String getCoordinateSystem() {
42
-        return coordinateSystem;
43
-    }
44
-
45
-    public void setGaussX(Double gaussX) {
46
-        this.gaussX = gaussX;
47
-    }
48
-
49
-    public Double getGaussX() {
50
-        return gaussX;
51
-    }
52
-
53
-    public void setGaussY(Double gaussY) {
54
-        this.gaussY = gaussY;
55
-    }
56
-
57
-    public Double getGaussY() {
58
-        return gaussY;
59
-    }
60
-    public void setLongitudePosition(String longitudePosition) {
61
-        this.longitudePosition = longitudePosition;
62
-    }
63
-
64
-    public String getLongitudePosition() {
65
-        return longitudePosition;
66
-    }
67
-
68
-    public void setLatitudePosition(String latitudePosition) {
69
-        this.latitudePosition = latitudePosition;
70
-    }
71
-
72
-    public String getLatitudePosition() {
73
-        return latitudePosition;
74
-    }
75
-
76
-}

+ 0
- 124
oa-back/ruoyi-calculate/src/main/java/com/ruoyi/web/calculate/vo/CmcGaussPositiveTemplate.java Datei anzeigen

@@ -1,124 +0,0 @@
1
-package com.ruoyi.web.calculate.vo;
2
-
3
-import java.math.BigDecimal;
4
-import java.math.RoundingMode;
5
-
6
-import com.ruoyi.common.annotation.Excel;
7
-import com.ruoyi.common.annotation.Excel.ColumnType;
8
-
9
-public class CmcGaussPositiveTemplate {
10
-
11
-    @Excel(name = "待求点")
12
-    private String pointNumber;
13
-
14
-    @Excel(name = "坐标系")
15
-    private String coordinateSystem;
16
-
17
-    @Excel(name = "大地经度", cellType = ColumnType.NUMERIC)
18
-    private Double longitude;
19
-
20
-    @Excel(name = "经度位置")
21
-    private String longitudePosition;
22
-
23
-    @Excel(name = "大地纬度", cellType = ColumnType.NUMERIC)
24
-    private Double latitude;
25
-
26
-    @Excel(name = "纬度位置")
27
-    private String latitudePosition;
28
-
29
-    @Excel(name = "带号", cellType = ColumnType.NUMERIC)
30
-    private Integer band;
31
-
32
-    @Excel(name = "带宽", cellType = ColumnType.NUMERIC)
33
-    private Integer bandwidth;
34
-
35
-    public void setPointNumber(String pointNumber) 
36
-    {
37
-        this.pointNumber = pointNumber;
38
-    }
39
-
40
-    public String getPointNumber() 
41
-    {
42
-        return pointNumber;
43
-    }
44
-    public void setCoordinateSystem(String coordinateSystem) 
45
-    {
46
-        this.coordinateSystem = coordinateSystem;
47
-    }
48
-
49
-    public String getCoordinateSystem() 
50
-    {
51
-        return coordinateSystem;
52
-    }
53
-     public void setLongitude(Double longitude) 
54
-    {
55
-        // 大地经度保留10位小数
56
-        if (longitude != null) {
57
-            BigDecimal bd = new BigDecimal(longitude);
58
-            this.longitude = bd.setScale(10, RoundingMode.HALF_UP).doubleValue();
59
-        } else {
60
-            this.longitude = null;
61
-        }
62
-    }
63
-
64
-    public Double getLongitude() 
65
-    {
66
-        return longitude;
67
-    }
68
-    
69
-    public void setLongitudePosition(String longitudePosition) 
70
-    {
71
-        this.longitudePosition = longitudePosition;
72
-    }
73
-
74
-    public String getLongitudePosition() 
75
-    {
76
-        return longitudePosition;
77
-    }
78
-    
79
-    public void setLatitude(Double latitude) 
80
-    {
81
-        // 大地纬度保留10位小数
82
-        if (latitude != null) {
83
-            BigDecimal bd = new BigDecimal(latitude);
84
-            this.latitude = bd.setScale(10, RoundingMode.HALF_UP).doubleValue();
85
-        } else {
86
-            this.latitude = null;
87
-        }
88
-    }
89
-
90
-    public Double getLatitude() 
91
-    {
92
-        return latitude;
93
-    }
94
-    
95
-    public void setLatitudePosition(String latitudePosition) 
96
-    {
97
-        this.latitudePosition = latitudePosition;
98
-    }
99
-
100
-    public String getLatitudePosition() 
101
-    {
102
-        return latitudePosition;
103
-    }
104
-    
105
-    public void setBand(Integer band) 
106
-    {
107
-        this.band = band;
108
-    }
109
-
110
-    public Integer getBand() 
111
-    {
112
-        return band;
113
-    }
114
-    
115
-    public void setBandwidth(Integer bandwidth) 
116
-    {
117
-        this.bandwidth = bandwidth;
118
-    }
119
-
120
-    public Integer getBandwidth() 
121
-    {
122
-        return bandwidth;
123
-    }
124
-}

+ 0
- 88
oa-back/ruoyi-calculate/src/main/java/com/ruoyi/web/calculate/vo/CmcGeodeticToSpatialTemplate.java Datei anzeigen

@@ -1,88 +0,0 @@
1
-package com.ruoyi.web.calculate.vo;
2
-
3
-import com.ruoyi.common.annotation.Excel;
4
-import com.ruoyi.common.annotation.Excel.ColumnType;
5
-
6
-/**
7
- * 大地坐标转空间直角坐标模板
8
- */
9
-public class CmcGeodeticToSpatialTemplate {
10
-
11
-    @Excel(name = "待求点")
12
-    private String pointNumber;
13
-
14
-    @Excel(name = "坐标系")
15
-    private String coordinateSystem;
16
-
17
-    @Excel(name = "大地经度", cellType = ColumnType.NUMERIC)
18
-    private Double longitude;
19
-
20
-    @Excel(name = "经度位置")
21
-    private String longitudePosition;
22
-
23
-    @Excel(name = "大地纬度", cellType = ColumnType.NUMERIC)
24
-    private Double latitude;
25
-
26
-    @Excel(name = "纬度位置")
27
-    private String latitudePosition;
28
-
29
-    @Excel(name = "大地高", cellType = ColumnType.NUMERIC)
30
-    private Double height;
31
-
32
-    public void setPointNumber(String pointNumber) {
33
-        this.pointNumber = pointNumber;
34
-    }
35
-
36
-    public String getPointNumber() {
37
-        return pointNumber;
38
-    }
39
-
40
-    public void setCoordinateSystem(String coordinateSystem) {
41
-        this.coordinateSystem = coordinateSystem;
42
-    }
43
-
44
-    public String getCoordinateSystem() {
45
-        return coordinateSystem;
46
-    }
47
-
48
-    public void setLongitude(Double longitude) {
49
-        this.longitude = longitude;
50
-    }
51
-
52
-    public Double getLongitude() {
53
-        return longitude;
54
-    }
55
-
56
-    public void setLongitudePosition(String longitudePosition) {
57
-        this.longitudePosition = longitudePosition;
58
-    }
59
-
60
-    public String getLongitudePosition() {
61
-        return longitudePosition;
62
-    }
63
-
64
-    public void setLatitude(Double latitude) {
65
-        this.latitude = latitude;
66
-    }
67
-
68
-    public Double getLatitude() {
69
-        return latitude;
70
-    }
71
-
72
-    public void setLatitudePosition(String latitudePosition) {
73
-        this.latitudePosition = latitudePosition;
74
-    }
75
-
76
-    public String getLatitudePosition() {
77
-        return latitudePosition;
78
-    }
79
-
80
-    public void setHeight(Double height) {
81
-        this.height = height;
82
-    }
83
-
84
-    public Double getHeight() {
85
-        return height;
86
-    }
87
-
88
-}

+ 0
- 66
oa-back/ruoyi-calculate/src/main/java/com/ruoyi/web/calculate/vo/CmcSpatialToGeodeticTemplate.java Datei anzeigen

@@ -1,66 +0,0 @@
1
-package com.ruoyi.web.calculate.vo;
2
-
3
-import com.ruoyi.common.annotation.Excel;
4
-import com.ruoyi.common.annotation.Excel.ColumnType;
5
-
6
-/**
7
- * 空间直角坐标转大地坐标模板
8
- */
9
-public class CmcSpatialToGeodeticTemplate {
10
-
11
-    @Excel(name = "待求点")
12
-    private String pointNumber;
13
-
14
-    @Excel(name = "坐标系")
15
-    private String coordinateSystem;
16
-
17
-    @Excel(name = "空间坐标X", cellType = ColumnType.NUMERIC)
18
-    private Double spatialX;
19
-
20
-    @Excel(name = "空间坐标Y", cellType = ColumnType.NUMERIC)
21
-    private Double spatialY;
22
-
23
-    @Excel(name = "空间坐标Z", cellType = ColumnType.NUMERIC)
24
-    private Double spatialZ;
25
-
26
-    public void setPointNumber(String pointNumber) {
27
-        this.pointNumber = pointNumber;
28
-    }
29
-
30
-    public String getPointNumber() {
31
-        return pointNumber;
32
-    }
33
-
34
-    public void setCoordinateSystem(String coordinateSystem) {
35
-        this.coordinateSystem = coordinateSystem;
36
-    }
37
-
38
-    public String getCoordinateSystem() {
39
-        return coordinateSystem;
40
-    }
41
-
42
-    public void setSpatialX(Double spatialX) {
43
-        this.spatialX = spatialX;
44
-    }
45
-
46
-    public Double getSpatialX() {
47
-        return spatialX;
48
-    }
49
-
50
-    public void setSpatialY(Double spatialY) {
51
-        this.spatialY = spatialY;
52
-    }
53
-
54
-    public Double getSpatialY() {
55
-        return spatialY;
56
-    }
57
-
58
-    public void setSpatialZ(Double spatialZ) {
59
-        this.spatialZ = spatialZ;
60
-    }
61
-
62
-    public Double getSpatialZ() {
63
-        return spatialZ;
64
-    }
65
-
66
-}

+ 0
- 101
oa-back/ruoyi-calculate/src/main/java/com/ruoyi/web/calculate/vo/CmcUTMBandChangeTemplate.java Datei anzeigen

@@ -1,101 +0,0 @@
1
-package com.ruoyi.web.calculate.vo;
2
-
3
-import java.math.BigDecimal;
4
-import java.math.RoundingMode;
5
-
6
-import com.ruoyi.common.annotation.Excel;
7
-import com.ruoyi.common.annotation.Excel.ColumnType;
8
-
9
-public class CmcUTMBandChangeTemplate {
10
-
11
-    @Excel(name = "待求点")
12
-    private String pointNumber;
13
-
14
-    @Excel(name = "坐标系")
15
-    private String coordinateSystem;
16
-
17
-    @Excel(name = "UTM坐标x", cellType = ColumnType.NUMERIC)
18
-    private Double utmX;
19
-
20
-    @Excel(name = "UTM坐标y", cellType = ColumnType.NUMERIC)
21
-    private Double utmY;
22
-
23
-    /** 带号 */
24
-    @Excel(name = "带号", cellType = ColumnType.NUMERIC)
25
-    private Integer band;
26
-
27
-    /** 新带号 */
28
-    @Excel(name = "新带号", cellType = ColumnType.NUMERIC)
29
-    private Integer newBand;
30
-
31
-    @Excel(name = "经度位置")
32
-    private String longitudePosition;
33
-
34
-    @Excel(name = "纬度位置")
35
-    private String latitudePosition;
36
-
37
-    public void setPointNumber(String pointNumber) {
38
-        this.pointNumber = pointNumber;
39
-    }
40
-
41
-    public String getPointNumber() {
42
-        return pointNumber;
43
-    }
44
-
45
-    public void setCoordinateSystem(String coordinateSystem) {
46
-        this.coordinateSystem = coordinateSystem;
47
-    }
48
-
49
-    public String getCoordinateSystem() {
50
-        return coordinateSystem;
51
-    }
52
-
53
-    public void setUtmX(Double utmX) {
54
-        this.utmX = utmX;
55
-    }
56
-
57
-    public Double getUtmX() {
58
-        return utmX;
59
-    }
60
-
61
-    public void setUtmY(Double utmY) {
62
-        this.utmY = utmY;
63
-    }
64
-
65
-    public Double getUtmY() {
66
-        return utmY;
67
-    }
68
-
69
-    public void setBand(Integer band) {
70
-        this.band = band;
71
-    }
72
-
73
-    public Integer getBand() {
74
-        return band;
75
-    }
76
-    
77
-    public void setNewBand(Integer newBand) {
78
-        this.newBand = newBand;
79
-    }
80
-
81
-    public Integer getNewBand() {
82
-        return newBand;
83
-    }
84
-
85
-    public void setLongitudePosition(String longitudePosition) {
86
-        this.longitudePosition = longitudePosition;
87
-    }
88
-
89
-    public String getLongitudePosition() {
90
-        return longitudePosition;
91
-    }
92
-
93
-    public void setLatitudePosition(String latitudePosition) {
94
-        this.latitudePosition = latitudePosition;
95
-    }
96
-
97
-    public String getLatitudePosition() {
98
-        return latitudePosition;
99
-    }
100
-
101
-}

+ 0
- 98
oa-back/ruoyi-calculate/src/main/java/com/ruoyi/web/calculate/vo/CmcUTMNegativeTemplate.java Datei anzeigen

@@ -1,98 +0,0 @@
1
-package com.ruoyi.web.calculate.vo;
2
-
3
-import com.ruoyi.common.annotation.Excel;
4
-import com.ruoyi.common.annotation.Excel.ColumnType;
5
-
6
-public class CmcUTMNegativeTemplate {
7
-
8
-    @Excel(name = "待求点")
9
-    private String pointNumber;
10
-
11
-    @Excel(name = "坐标系")
12
-    private String coordinateSystem;
13
-
14
-    @Excel(name = "UTM坐标x", cellType = ColumnType.NUMERIC)
15
-    private Double utmX;
16
-
17
-    @Excel(name = "UTM坐标y", cellType = ColumnType.NUMERIC)
18
-    private Double utmY;
19
-
20
-    @Excel(name = "带号", cellType = ColumnType.NUMERIC)
21
-    private Integer band;
22
-
23
-    @Excel(name = "经度位置")
24
-    private String longitudePosition;
25
-
26
-    @Excel(name = "纬度位置")
27
-    private String latitudePosition;
28
-
29
-    public void setPointNumber(String pointNumber) 
30
-    {
31
-        this.pointNumber = pointNumber;
32
-    }
33
-
34
-    public String getPointNumber() 
35
-    {
36
-        return pointNumber;
37
-    }
38
-    
39
-    public void setCoordinateSystem(String coordinateSystem) 
40
-    {
41
-        this.coordinateSystem = coordinateSystem;
42
-    }
43
-
44
-    public String getCoordinateSystem() 
45
-    {
46
-        return coordinateSystem;
47
-    }
48
-    
49
-    public void setUtmX(Double utmX) 
50
-    {
51
-        this.utmX = utmX;
52
-    }
53
-
54
-    public Double getUtmX() 
55
-    {
56
-        return utmX;
57
-    }
58
-    
59
-    public void setUtmY(Double utmY) 
60
-    {
61
-        this.utmY = utmY;
62
-    }
63
-
64
-    public Double getUtmY() 
65
-    {
66
-        return utmY;
67
-    }
68
-    
69
-    public void setBand(Integer band) 
70
-    {
71
-        this.band = band;
72
-    }
73
-
74
-    public Integer getBand() 
75
-    {
76
-        return band;
77
-    }
78
-    
79
-    public void setLongitudePosition(String longitudePosition) 
80
-    {
81
-        this.longitudePosition = longitudePosition;
82
-    }
83
-
84
-    public String getLongitudePosition() 
85
-    {
86
-        return longitudePosition;
87
-    }
88
-    
89
-    public void setLatitudePosition(String latitudePosition) 
90
-    {
91
-        this.latitudePosition = latitudePosition;
92
-    }
93
-
94
-    public String getLatitudePosition() 
95
-    {
96
-        return latitudePosition;
97
-    }
98
-}

+ 0
- 109
oa-back/ruoyi-calculate/src/main/java/com/ruoyi/web/calculate/vo/CmcUTMPositiveTemplate.java Datei anzeigen

@@ -1,109 +0,0 @@
1
-package com.ruoyi.web.calculate.vo;
2
-
3
-import java.math.BigDecimal;
4
-import java.math.RoundingMode;
5
-
6
-import com.ruoyi.common.annotation.Excel;
7
-import com.ruoyi.common.annotation.Excel.ColumnType;
8
-
9
-public class CmcUTMPositiveTemplate {
10
-
11
-    @Excel(name = "待求点")
12
-    private String pointNumber;
13
-
14
-    @Excel(name = "坐标系")
15
-    private String coordinateSystem;
16
-
17
-    @Excel(name = "大地经度", cellType = ColumnType.NUMERIC)
18
-    private Double longitude;
19
-
20
-    @Excel(name = "经度位置")
21
-    private String longitudePosition;
22
-
23
-    @Excel(name = "大地纬度", cellType = ColumnType.NUMERIC)
24
-    private Double latitude;
25
-
26
-    @Excel(name = "纬度位置")
27
-    private String latitudePosition;
28
-
29
-    @Excel(name = "带号", cellType = ColumnType.NUMERIC)
30
-    private Integer band;
31
-
32
-    public void setPointNumber(String pointNumber) 
33
-    {
34
-        this.pointNumber = pointNumber;
35
-    }
36
-
37
-    public String getPointNumber() 
38
-    {
39
-        return pointNumber;
40
-    }
41
-    public void setCoordinateSystem(String coordinateSystem) 
42
-    {
43
-        this.coordinateSystem = coordinateSystem;
44
-    }
45
-
46
-    public String getCoordinateSystem() 
47
-    {
48
-        return coordinateSystem;
49
-    }
50
-     public void setLongitude(Double longitude) 
51
-    {
52
-        if (longitude != null) {
53
-            BigDecimal bd = new BigDecimal(longitude);
54
-            this.longitude = bd.setScale(10, RoundingMode.HALF_UP).doubleValue();
55
-        } else {
56
-            this.longitude = null;
57
-        }
58
-    }
59
-
60
-    public Double getLongitude() 
61
-    {
62
-        return longitude;
63
-    }
64
-    
65
-    public void setLongitudePosition(String longitudePosition) 
66
-    {
67
-        this.longitudePosition = longitudePosition;
68
-    }
69
-
70
-    public String getLongitudePosition() 
71
-    {
72
-        return longitudePosition;
73
-    }
74
-    
75
-    public void setLatitude(Double latitude) 
76
-    {
77
-        if (latitude != null) {
78
-            BigDecimal bd = new BigDecimal(latitude);
79
-            this.latitude = bd.setScale(10, RoundingMode.HALF_UP).doubleValue();
80
-        } else {
81
-            this.latitude = null;
82
-        }
83
-    }
84
-
85
-    public Double getLatitude() 
86
-    {
87
-        return latitude;
88
-    }
89
-    
90
-    public void setLatitudePosition(String latitudePosition) 
91
-    {
92
-        this.latitudePosition = latitudePosition;
93
-    }
94
-
95
-    public String getLatitudePosition() 
96
-    {
97
-        return latitudePosition;
98
-    }
99
-    
100
-    public void setBand(Integer band) 
101
-    {
102
-        this.band = band;
103
-    }
104
-
105
-    public Integer getBand() 
106
-    {
107
-        return band;
108
-    }
109
-}

+ 32
- 0
oa-ui/src/api/calculate/DMStodegree.js Datei anzeigen

@@ -0,0 +1,32 @@
1
+import request from '@/utils/request'
2
+
3
+// 执行度分秒转度
4
+export function calculate(data) {
5
+    return request({
6
+        url: `/calculate/DMSToDegree/calculate`,
7
+        method: 'post',
8
+        data: data
9
+    })
10
+}
11
+
12
+// 导入 Excel
13
+export function importExcel(data) {
14
+    return request({
15
+        url: `/calculate/DMSToDegree/import`,
16
+        method: 'post',
17
+        data: data,
18
+        headers: {
19
+            'Content-Type': 'multipart/form-data'
20
+        }
21
+    })
22
+}
23
+
24
+// 导出度分秒转度结果
25
+export function exportExcel(data) {
26
+    return request({
27
+        url: `/calculate/DMSToDegree/export`,
28
+        method: 'post',
29
+        data: data,
30
+        responseType: 'blob'
31
+    })
32
+}

+ 32
- 0
oa-ui/src/api/calculate/degreetoDMS.js Datei anzeigen

@@ -0,0 +1,32 @@
1
+import request from '@/utils/request'
2
+
3
+// 执行角度度分秒转换
4
+export function calculate(data) {
5
+    return request({
6
+        url: `/calculate/degreeToDMS/calculate`,
7
+        method: 'post',
8
+        data: data
9
+    })
10
+}
11
+
12
+// 导入 Excel
13
+export function importExcel(data) {
14
+    return request({
15
+        url: `/calculate/degreeToDMS/import`,
16
+        method: 'post',
17
+        data: data,
18
+        headers: {
19
+            'Content-Type': 'multipart/form-data'
20
+        }
21
+    })
22
+}
23
+
24
+// 导出角度度分秒转换结果
25
+export function exportExcel(data) {
26
+    return request({
27
+        url: `/calculate/degreeToDMS/export`,
28
+        method: 'post',
29
+        data: data,
30
+        responseType: 'blob'
31
+    })
32
+}

+ 32
- 0
oa-ui/src/api/calculate/degreetoradian.js Datei anzeigen

@@ -0,0 +1,32 @@
1
+import request from '@/utils/request'
2
+
3
+// 执行角度弧度转换
4
+export function calculate(data) {
5
+    return request({
6
+        url: `/calculate/degreeToRadian/calculate`,
7
+        method: 'post',
8
+        data: data
9
+    })
10
+}
11
+
12
+// 导入 Excel
13
+export function importExcel(data) {
14
+    return request({
15
+        url: `/calculate/degreeToRadian/import`,
16
+        method: 'post',
17
+        data: data,
18
+        headers: {
19
+            'Content-Type': 'multipart/form-data'
20
+        }
21
+    })
22
+}
23
+
24
+// 导出角度弧度转换结果
25
+export function exportExcel(data) {
26
+    return request({
27
+        url: `/calculate/degreeToRadian/export`,
28
+        method: 'post',
29
+        data: data,
30
+        responseType: 'blob'
31
+    })
32
+}

+ 0
- 9
oa-ui/src/api/calculate/gaussbandchange.js Datei anzeigen

@@ -31,12 +31,3 @@ export function exportExcel(data) {
31 31
         responseType: 'blob'
32 32
     })
33 33
 }
34
-
35
-// 下载模板
36
-export function downloadTemplate() {
37
-    return request({
38
-        url: '/calculate/gaussBandChange/template',
39
-        method: 'get',
40
-        responseType: 'blob'
41
-    })
42
-}

+ 0
- 9
oa-ui/src/api/calculate/gaussnegative.js Datei anzeigen

@@ -31,12 +31,3 @@ export function exportExcel(data) {
31 31
         responseType: 'blob'
32 32
     })
33 33
 }
34
-
35
-// 下载模板
36
-export function downloadTemplate() {
37
-    return request({
38
-        url: '/calculate/gaussNegative/template',
39
-        method: 'get',
40
-        responseType: 'blob'
41
-    })
42
-}

+ 0
- 9
oa-ui/src/api/calculate/gausspositive.js Datei anzeigen

@@ -31,12 +31,3 @@ export function exportExcel(data) {
31 31
         responseType: 'blob'
32 32
     })
33 33
 }
34
-
35
-// 下载模板
36
-export function downloadTemplate() {
37
-    return request({
38
-        url: '/calculate/gaussPositive/template',
39
-        method: 'get',
40
-        responseType: 'blob'
41
-    })
42
-}

+ 0
- 9
oa-ui/src/api/calculate/geodetictospatial.js Datei anzeigen

@@ -29,13 +29,4 @@ export function exportExcel(data) {
29 29
         data: data,
30 30
         responseType: 'blob'
31 31
     })
32
-}
33
-
34
-// 下载模板
35
-export function downloadTemplate() {
36
-    return request({
37
-        url: '/calculate/geodeticToSpatial/template',
38
-        method: 'get',
39
-        responseType: 'blob'
40
-    })
41 32
 }

+ 0
- 9
oa-ui/src/api/calculate/spatialtogeodetic.js Datei anzeigen

@@ -29,13 +29,4 @@ export function exportExcel(data) {
29 29
         data: data,
30 30
         responseType: 'blob'
31 31
     })
32
-}
33
-
34
-// 下载模板
35
-export function downloadTemplate() {
36
-    return request({
37
-        url: '/calculate/spatialToGeodetic/template',
38
-        method: 'get',
39
-        responseType: 'blob'
40
-    })
41 32
 }

+ 0
- 9
oa-ui/src/api/calculate/utmbandchange.js Datei anzeigen

@@ -30,13 +30,4 @@ export function exportExcel(data) {
30 30
         data: data,
31 31
         responseType: 'blob'
32 32
     })
33
-}
34
-
35
-// 下载模板
36
-export function downloadTemplate() {
37
-    return request({
38
-        url: '/calculate/utmBandChange/template',
39
-        method: 'get',
40
-        responseType: 'blob'
41
-    })
42 33
 }

+ 0
- 9
oa-ui/src/api/calculate/utmnegative.js Datei anzeigen

@@ -31,12 +31,3 @@ export function exportExcel(data) {
31 31
         responseType: 'blob'
32 32
     })
33 33
 }
34
-
35
-// 下载模板
36
-export function downloadTemplate() {
37
-    return request({
38
-        url: '/calculate/utmNegative/template',
39
-        method: 'get',
40
-        responseType: 'blob'
41
-    })
42
-}

+ 0
- 9
oa-ui/src/api/calculate/utmpositive.js Datei anzeigen

@@ -30,13 +30,4 @@ export function exportExcel(data) {
30 30
         data: data,
31 31
         responseType: 'blob'
32 32
     })
33
-}
34
-
35
-// 下载模板
36
-export function downloadTemplate() {
37
-    return request({
38
-        url: '/calculate/utmPositive/template',
39
-        method: 'get',
40
-        responseType: 'blob'
41
-    })
42 33
 }

+ 27
- 0
oa-ui/src/views/calculate/index.vue Datei anzeigen

@@ -53,6 +53,9 @@ import UTMNegativeTool from './tools/utmnegative.vue'
53 53
 import UTMBandChangeTool from './tools/utmbandchange.vue'
54 54
 import GeodeticToSpatialTool from './tools/geodetictospatial.vue'
55 55
 import SpatialToGeodeticTool from './tools/spatialtogeodetic.vue'
56
+import DegreeToDSTool from './tools/degreetoDMS.vue'
57
+import DMStodegreeTool from './tools/DMStodegree.vue'
58
+import DegreeToRadianTool from './tools/degreetoradian.vue'
56 59
 
57 60
 export default {
58 61
   name: 'CalculationTools',
@@ -65,6 +68,9 @@ export default {
65 68
     UTMBandChangeTool,
66 69
     GeodeticToSpatialTool,
67 70
     SpatialToGeodeticTool,
71
+    DegreeToDSTool, 
72
+    DMStodegreeTool,
73
+    DegreeToRadianTool,
68 74
   },
69 75
   data() {
70 76
     return {
@@ -128,7 +134,28 @@ export default {
128 134
               id: 'spatial-to-geodetic',
129 135
               name: '空间坐标转大地坐标工具',
130 136
               component: 'SpatialToGeodeticTool'
137
+            }
138
+          ]
139
+        },
140
+        {
141
+          id: 'degreesandDMS-category',
142
+          name: '度和度分秒转换工具',
143
+          children: [
144
+            {
145
+              id: 'degreetodms',
146
+              name: '度转度分秒工具',
147
+              component: 'DegreeToDSTool'
148
+            },
149
+            {
150
+              id: 'dmstodegree',
151
+              name: '度分秒转度工具',
152
+              component: 'DMStodegreeTool'
131 153
             },
154
+            {
155
+              id: 'degreetoradian',
156
+              name: '度转弧度工具',
157
+              component: 'DegreeToRadianTool'
158
+            }
132 159
           ]
133 160
         },
134 161
         {

+ 596
- 0
oa-ui/src/views/calculate/tools/DMStodegree.vue Datei anzeigen

@@ -0,0 +1,596 @@
1
+<!-- 度分秒转度工具组件 -->
2
+<template>
3
+  <div class="dms-to-degree-tool">
4
+    <!-- 操作按钮区 -->
5
+    <div class="tool-header">
6
+      <div class="card-header-buttons">
7
+        <el-button type="primary" @click="handleImport" size="small" icon="el-icon-upload">
8
+          导入 Excel
9
+        </el-button>
10
+
11
+        <el-button 
12
+          type="warning" 
13
+          @click="handleCalculate" 
14
+          size="small" 
15
+          :disabled="!importedData || importedData.length === 0 || calculating"
16
+          :loading="calculating"
17
+          icon="el-icon-caret-right">
18
+          {{ calculating ? '计算中...' : '执行计算' }}
19
+        </el-button>
20
+        <el-button 
21
+          type="danger" 
22
+          @click="clearAll" 
23
+          size="small" 
24
+          :disabled="(!importedData || importedData.length === 0) && calculationResults.length === 0"
25
+          plain
26
+          icon="el-icon-delete">
27
+          清空全部
28
+        </el-button>
29
+      </div>
30
+    </div>
31
+
32
+    <!-- 数据展示区域 - 合并显示导入数据和计算结果 -->
33
+    <div v-if="displayData.length > 0" class="result-section">
34
+      <div class="section-header">
35
+        <span class="section-title">
36
+          <i class="el-icon-tickets"></i>
37
+          {{ calculationResults.length > 0 ? '计算结果' : '导入数据' }}
38
+        </span>
39
+        <span class="section-info">
40
+          共 {{ displayData.length }} 条数据
41
+        </span>
42
+        <el-button v-if="calculationResults.length > 0" type="text" size="small" @click="handleExport" icon="el-icon-download">
43
+          导出结果
44
+        </el-button>
45
+      </div>
46
+      
47
+      <el-table :data="displayData" style="width: 100%" border stripe>
48
+        <el-table-column prop="pointNumber" label="待求点号" width="120" align="center"></el-table-column>
49
+        <el-table-column prop="longitudeDMS" label="经度(° ′ ″)" width="200" align="center">
50
+          <template slot-scope="scope">
51
+            <span>{{ formatNumber(scope.row.longitudeDMS, 9) }}</span>
52
+          </template>
53
+        </el-table-column>
54
+        <el-table-column prop="latitudeDMS" label="纬度(° ′ ″)" width="200" align="center">
55
+          <template slot-scope="scope">
56
+            <span>{{ formatNumber(scope.row.latitudeDMS, 9) }}</span>
57
+          </template>
58
+        </el-table-column>
59
+        <el-table-column prop="longitude" label="经度(°)" width="180" align="center">
60
+          <template slot-scope="scope">
61
+            <span v-if="scope.row.longitude" class="result-value">{{ formatNumber(scope.row.longitude, 9) }}</span>
62
+            <span v-else class="empty-cell">-</span>
63
+          </template>
64
+        </el-table-column>
65
+        <el-table-column prop="latitude" label="纬度(°)" width="180" align="center">
66
+          <template slot-scope="scope">
67
+            <span v-if="scope.row.latitude" class="result-value">{{ formatNumber(scope.row.latitude, 9) }}</span>
68
+            <span v-else class="empty-cell">-</span>
69
+          </template>
70
+        </el-table-column>
71
+      </el-table>
72
+    </div>
73
+    
74
+    <!-- 空状态 -->
75
+    <div v-else class="empty-state">
76
+      <i class="el-icon-document"></i>
77
+      <p>暂无数据</p>
78
+      <p class="empty-tip">请先导入 Excel 数据</p>
79
+    </div>
80
+    
81
+    <!-- 导入 Excel 对话框 -->
82
+    <el-dialog title="导入 Excel" :visible.sync="importDialogVisible" width="550px" @close="resetImport">
83
+      <div style="text-align: center;">
84
+        <el-upload
85
+          class="upload-area"
86
+          :action="''"
87
+          :auto-upload="false"
88
+          :on-change="handleFileChange"
89
+          :show-file-list="true"
90
+          accept=".xlsx,.xls"
91
+          drag
92
+          ref="upload">
93
+          <i class="el-icon-upload"></i>
94
+          <div class="el-upload__text">将 Excel 文件拖到此处,或<em>点击选择</em></div>
95
+          <div class="el-upload__tip" slot="tip">
96
+            支持.xlsx 和.xls 格式文件
97
+          </div>
98
+        </el-upload>
99
+      </div>
100
+      
101
+      <span slot="footer" class="dialog-footer">
102
+        <el-button @click="importDialogVisible = false">取消</el-button>
103
+        <el-button type="primary" @click="submitImport" :loading="importLoading" :disabled="!importFile">
104
+          确认导入
105
+        </el-button>
106
+      </span>
107
+    </el-dialog>
108
+    
109
+    <!-- 字段映射对话框 -->
110
+    <el-dialog title="字段映射" :visible.sync="mappingDialogVisible" width="500px">
111
+      <div class="mapping-tips">
112
+        <el-alert
113
+          title="字段映射说明"
114
+          type="info"
115
+          :closable="false"
116
+          show-icon>
117
+          请将Excel中的列映射到对应的坐标字段。
118
+        </el-alert>
119
+      </div>
120
+      
121
+      <div class="mapping-section">
122
+        <div class="mapping-row">
123
+          <label class="mapping-label">点号字段</label>
124
+          <el-select v-model="fieldMapping.pointNumber" class="mapping-select" size="small">
125
+            <el-option label="请选择" value=""></el-option>
126
+            <el-option v-for="col in excelColumns" :key="col" :label="col" :value="col"></el-option>
127
+          </el-select>
128
+        </div>
129
+        <div class="mapping-row">
130
+          <label class="mapping-label">经度(度分秒)字段</label>
131
+          <el-select v-model="fieldMapping.longitudeDMS" class="mapping-select" size="small">
132
+            <el-option label="请选择" value=""></el-option>
133
+            <el-option v-for="col in excelColumns" :key="col" :label="col" :value="col"></el-option>
134
+          </el-select>
135
+        </div>
136
+        <div class="mapping-row">
137
+          <label class="mapping-label">纬度(度分秒)字段</label>
138
+          <el-select v-model="fieldMapping.latitudeDMS" class="mapping-select" size="small">
139
+            <el-option label="请选择" value=""></el-option>
140
+            <el-option v-for="col in excelColumns" :key="col" :label="col" :value="col"></el-option>
141
+          </el-select>
142
+        </div>
143
+      </div>
144
+      
145
+      <span slot="footer" class="dialog-footer">
146
+        <el-button @click="cancelMapping">取消</el-button>
147
+        <el-button type="primary" @click="confirmMapping" :disabled="!validateMapping">
148
+          确认映射
149
+        </el-button>
150
+      </span>
151
+    </el-dialog>
152
+  </div>
153
+</template>
154
+
155
+<script>
156
+import { calculate, importExcel, exportExcel, downloadTemplate } from '@/api/calculate/DMStodegree'
157
+
158
+export default {
159
+  name: 'DMSToDegreeTool',
160
+  data() {
161
+    return {
162
+      importedData: [],        // 导入的原始数据
163
+      calculationResults: [],  // 计算结果
164
+      importDialogVisible: false,
165
+      importLoading: false,
166
+      importFile: null,
167
+      calculating: false,
168
+      
169
+      // 字段映射相关
170
+      mappingDialogVisible: false,
171
+      excelColumns: [],
172
+      fieldMapping: {
173
+        pointNumber: '',
174
+        longitudeDMS: '',
175
+        latitudeDMS: ''
176
+      },
177
+      rawImportedData: []
178
+    }
179
+  },
180
+  methods: {
181
+    // 打开导入对话框 
182
+    handleImport() {
183
+      this.importDialogVisible = true
184
+    },
185
+    
186
+    // 文件选择变化
187
+    handleFileChange(file) {
188
+      this.importFile = file.raw
189
+    },
190
+    
191
+    // 提交导入
192
+    async submitImport() {
193
+      if (!this.importFile) {
194
+        this.$message.warning('请选择文件')
195
+        return
196
+      }
197
+      
198
+      this.importLoading = true
199
+      
200
+      const formData = new FormData()
201
+      formData.append('file', this.importFile)
202
+      
203
+      try {
204
+        const response = await importExcel(formData)
205
+        
206
+        if (response.code === 200) {
207
+          const result = response.data
208
+          
209
+          // 获取Excel列名
210
+          this.excelColumns = result.columns || this.extractColumns(result.data)
211
+          
212
+          // 保存原始数据
213
+          this.rawImportedData = result.data || []
214
+          
215
+          // 清空之前的计算结果
216
+          this.calculationResults = []
217
+          
218
+          // 自动匹配字段
219
+          this.autoMatchFields()
220
+          
221
+          // 显示字段映射对话框
222
+          this.mappingDialogVisible = true
223
+          
224
+          this.$message.success(`${response.msg}`)
225
+          this.importDialogVisible = false
226
+          this.importLoading = false
227
+        } else {
228
+          this.$message.error(response.msg)
229
+          this.importLoading = false
230
+        }
231
+      } catch (error) {
232
+        this.$message.error('导入失败')
233
+        this.importLoading = false
234
+      }
235
+    },
236
+    
237
+    // 提取列名
238
+    extractColumns(data) {
239
+      if (!data || data.length === 0) return []
240
+      return Object.keys(data[0])
241
+    },
242
+    
243
+    // 自动匹配字段
244
+    autoMatchFields() {
245
+      this.resetFieldMapping()
246
+      
247
+      const keywordMap = {
248
+        pointNumber: ['点号', '待求点', '点', '编号', '序号', 'ID'],
249
+        longitudeDMS: ['经度(度分秒)', '经度度分秒', '经度 DMS', 'lonDMS', 'LongitudeDMS'],
250
+        latitudeDMS: ['纬度(度分秒)', '纬度度分秒', '纬度 DMS', 'latDMS', 'LatitudeDMS']
251
+      }
252
+      
253
+      this.excelColumns.forEach(col => {
254
+        const lowerCol = col.toLowerCase()
255
+        Object.keys(keywordMap).forEach(field => {
256
+          if (!this.fieldMapping[field]) {
257
+            keywordMap[field].forEach(keyword => {
258
+              if (col.includes(keyword) || lowerCol.includes(keyword.toLowerCase())) {
259
+                this.fieldMapping[field] = col
260
+              }
261
+            })
262
+          }
263
+        })
264
+      })
265
+    },
266
+    
267
+    // 确认字段映射
268
+    confirmMapping() {
269
+      if (!this.validateMapping) {
270
+        this.$message.warning('请完成所有必填字段的映射')
271
+        return
272
+      }
273
+      
274
+      // 根据映射转换数据
275
+      this.importedData = this.rawImportedData.map(item => ({
276
+        pointNumber: item[this.fieldMapping.pointNumber],
277
+        longitudeDMS: item[this.fieldMapping.longitudeDMS],
278
+        latitudeDMS: item[this.fieldMapping.latitudeDMS]
279
+      }))
280
+      
281
+      this.mappingDialogVisible = false
282
+      this.resetFieldMapping()
283
+    },
284
+    
285
+    // 取消字段映射
286
+    cancelMapping() {
287
+      this.mappingDialogVisible = false
288
+      this.resetFieldMapping()
289
+      this.rawImportedData = []
290
+    },
291
+    
292
+    // 重置字段映射
293
+    resetFieldMapping() {
294
+      this.fieldMapping = {
295
+        pointNumber: '',
296
+        longitudeDMS: '',
297
+        latitudeDMS: ''
298
+      }
299
+    },
300
+    
301
+    // 下载模板
302
+    handleDownloadTemplate() {
303
+      this.download('/calculate/DMSToDegree/template', {}, '度分秒转度模板.xlsx')
304
+    },
305
+    
306
+    // 执行计算
307
+    async handleCalculate() {
308
+      if (!this.importedData || this.importedData.length === 0) {
309
+        this.$message.warning('没有数据可计算')
310
+        return
311
+      }
312
+      
313
+      this.calculating = true
314
+      
315
+      // 构建请求参数
316
+      const requestData = this.importedData.map(item => ({
317
+        pointNumber: item.pointNumber,
318
+        longitudeDMS: parseFloat(item.longitudeDMS),
319
+        latitudeDMS: parseFloat(item.latitudeDMS),
320
+        longitudePosition: item.longitudePosition,
321
+        latitudePosition: item.latitudePosition
322
+      }))
323
+      
324
+      const response = await calculate(requestData)
325
+      
326
+      if (response.code === 200) {
327
+        const results = response.data || []
328
+        
329
+        // 更新计算结果
330
+        this.calculationResults = results.map(item => ({
331
+          pointNumber: item.pointNumber,
332
+          longitudeDMS: item.longitudeDMS,
333
+          latitudeDMS: item.latitudeDMS,
334
+          longitudePosition: item.longitudePosition,
335
+          latitudePosition: item.latitudePosition,
336
+          longitude: item.longitude,
337
+          latitude: item.latitude
338
+        }))
339
+        
340
+        const successCount = results.filter(r => r.longitude !== undefined && r.longitude !== -1).length
341
+        const failCount = results.length - successCount
342
+        
343
+        this.$message.success(`计算完成,成功 ${successCount} 条${failCount > 0 ? `,失败 ${failCount} 条` : ''}`)
344
+      } else {
345
+        this.$message.error(response.msg)
346
+      }
347
+      
348
+      this.calculating = false
349
+    },
350
+    
351
+    // 导出结果
352
+    async handleExport() {
353
+      if (this.calculationResults.length === 0) {
354
+        this.$message.warning('没有结果可导出')
355
+        return
356
+      }
357
+      
358
+      const response = await exportExcel(this.calculationResults)
359
+      
360
+      const blob = new Blob([response], { 
361
+        type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
362
+      })
363
+      
364
+      const url = window.URL.createObjectURL(blob)
365
+      const link = document.createElement('a')
366
+      link.href = url
367
+      link.download = `度分秒转度结果_${new Date().getTime()}.xlsx`
368
+      link.click()
369
+      window.URL.revokeObjectURL(url)
370
+    },
371
+    
372
+    // 清空全部
373
+    clearAll() {
374
+      if ((!this.importedData || this.importedData.length === 0) && this.calculationResults.length === 0) {
375
+        return
376
+      }
377
+      
378
+      this.$confirm('确定清空所有数据吗?此操作不可恢复!', '警告', {
379
+        confirmButtonText: '确定',
380
+        cancelButtonText: '取消',
381
+        type: 'warning'
382
+      }).then(() => {
383
+        this.importedData = []
384
+        this.calculationResults = []
385
+        this.$message.success('已清空所有数据')
386
+      }).catch(() => {})
387
+    },
388
+    
389
+    // 重置导入状态
390
+    resetImport() {
391
+      this.importFile = null
392
+      if (this.$refs.upload) {
393
+        this.$refs.upload.clearFiles()
394
+      }
395
+    },
396
+    
397
+    // 格式化数字显示
398
+    formatNumber(value, decimals = 3) {
399
+      if (value === null || value === undefined) return '--'
400
+      return Number(value).toFixed(decimals)
401
+    }
402
+  },
403
+  
404
+  computed: {
405
+    // 验证字段映射
406
+    validateMapping() {
407
+      if (!this.fieldMapping) {
408
+        return false
409
+      }
410
+      return this.fieldMapping.pointNumber && 
411
+             this.fieldMapping.longitudeDMS && 
412
+             this.fieldMapping.latitudeDMS
413
+    },
414
+    
415
+    // 显示数据:优先显示计算结果,否则显示导入数据
416
+    displayData() {
417
+      if (this.calculationResults.length > 0) {
418
+        return this.calculationResults.map(item => ({
419
+          pointNumber: item.pointNumber,
420
+          longitudeDMS: item.longitudeDMS,
421
+          latitudeDMS: item.latitudeDMS,
422
+          longitude: item.longitude,
423
+          latitude: item.latitude
424
+        }))
425
+      } else if (this.importedData.length > 0) {
426
+        return this.importedData.map(item => ({
427
+          pointNumber: item.pointNumber,
428
+          longitudeDMS: item.longitudeDMS,
429
+          latitudeDMS: item.latitudeDMS,
430
+          longitude: undefined,
431
+          latitude: undefined
432
+        }))
433
+      }
434
+      return []
435
+    }
436
+  }
437
+}
438
+</script>
439
+
440
+<style scoped>
441
+.dms-to-degree-tool {
442
+  width: 100%;
443
+}
444
+
445
+.tool-header {
446
+  margin-bottom: 20px;
447
+}
448
+
449
+.card-header-buttons {
450
+  display: flex;
451
+  gap: 10px;
452
+  flex-wrap: wrap;
453
+}
454
+
455
+.result-section {
456
+  margin-top: 10px;
457
+}
458
+
459
+.section-header {
460
+  padding: 12px 16px;
461
+  background: #f5f7fa;
462
+  border-radius: 8px 8px 0 0;
463
+  border: 1px solid #e4e7ed;
464
+  border-bottom: none;
465
+  display: flex;
466
+  justify-content: space-between;
467
+  align-items: center;
468
+}
469
+
470
+.section-title {
471
+  font-size: 14px;
472
+  font-weight: bold;
473
+  color: #303133;
474
+}
475
+
476
+.section-title i {
477
+  margin-right: 8px;
478
+  color: #11a983;
479
+}
480
+
481
+.section-info {
482
+  font-size: 12px;
483
+  color: #909399;
484
+}
485
+
486
+.result-value {
487
+  color: #409eff;
488
+  font-family: monospace;
489
+  font-weight: 500;
490
+}
491
+
492
+.empty-state {
493
+  text-align: center;
494
+  padding: 60px 20px;
495
+  background: #fff;
496
+  border-radius: 8px;
497
+  border: 1px solid #e4e7ed;
498
+  color: #909399;
499
+}
500
+
501
+.empty-state i {
502
+  font-size: 64px;
503
+  margin-bottom: 16px;
504
+}
505
+
506
+.empty-tip {
507
+  font-size: 12px;
508
+  margin-top: 12px;
509
+  color: #c0c4cc;
510
+}
511
+
512
+.import-tips {
513
+  margin-bottom: 20px;
514
+}
515
+
516
+.template-info {
517
+  margin-top: 12px;
518
+}
519
+
520
+.template-info ul {
521
+  margin: 8px 0;
522
+  padding-left: 20px;
523
+}
524
+
525
+.template-info li {
526
+  margin: 4px 0;
527
+  font-size: 13px;
528
+}
529
+
530
+.upload-area {
531
+  margin-top: 10px;
532
+}
533
+
534
+.preview-info {
535
+  margin-bottom: 15px;
536
+  padding: 10px;
537
+  background: #f5f7fa;
538
+  border-radius: 4px;
539
+  font-size: 14px;
540
+  color: #606266;
541
+}
542
+
543
+.dialog-footer {
544
+  width: 100%;
545
+  display: flex;
546
+  justify-content: flex-end;
547
+  gap: 10px;
548
+}
549
+
550
+.template-download-wrapper {
551
+  margin-top: 12px;
552
+  display: flex;
553
+  align-items: center;
554
+  justify-content: flex-end;
555
+  gap: 10px;
556
+  padding-top: 12px;
557
+  border-top: 1px dashed #e4e7ed;
558
+}
559
+
560
+/* 字段映射对话框样式 */
561
+.mapping-tips {
562
+  margin-bottom: 20px;
563
+}
564
+
565
+.mapping-section {
566
+  background: #f9fafb;
567
+  border-radius: 8px;
568
+  padding: 16px;
569
+}
570
+
571
+.mapping-row {
572
+  display: flex;
573
+  align-items: center;
574
+  margin-bottom: 12px;
575
+}
576
+
577
+.mapping-row:last-child {
578
+  margin-bottom: 0;
579
+}
580
+
581
+.mapping-label {
582
+  width: 140px;
583
+  font-weight: 500;
584
+  color: #606266;
585
+}
586
+
587
+.mapping-select {
588
+  flex: 1;
589
+  min-width: 200px;
590
+}
591
+
592
+.empty-cell {
593
+  color: #C0C4CC;
594
+  font-style: italic;
595
+}
596
+</style>

+ 592
- 0
oa-ui/src/views/calculate/tools/degreetoDMS.vue Datei anzeigen

@@ -0,0 +1,592 @@
1
+<!-- 角度度分秒转换工具组件 -->
2
+<template>
3
+  <div class="degree-to-dms-tool">
4
+    <!-- 操作按钮区 -->
5
+    <div class="tool-header">
6
+      <div class="card-header-buttons">
7
+        <el-button type="primary" @click="handleImport" size="small" icon="el-icon-upload">
8
+          导入 Excel
9
+        </el-button>
10
+
11
+        <el-button 
12
+          type="warning" 
13
+          @click="handleCalculate" 
14
+          size="small" 
15
+          :disabled="!importedData || importedData.length === 0 || calculating"
16
+          :loading="calculating"
17
+          icon="el-icon-caret-right">
18
+          {{ calculating ? '计算中...' : '执行计算' }}
19
+        </el-button>
20
+        <el-button 
21
+          type="danger" 
22
+          @click="clearAll" 
23
+          size="small" 
24
+          :disabled="(!importedData || importedData.length === 0) && calculationResults.length === 0"
25
+          plain
26
+          icon="el-icon-delete">
27
+          清空全部
28
+        </el-button>
29
+      </div>
30
+    </div>
31
+
32
+    <!-- 数据展示区域 - 合并显示导入数据和计算结果 -->
33
+    <div v-if="displayData.length > 0" class="result-section">
34
+      <div class="section-header">
35
+        <span class="section-title">
36
+          <i class="el-icon-tickets"></i>
37
+          {{ calculationResults.length > 0 ? '计算结果' : '导入数据' }}
38
+        </span>
39
+        <span class="section-info">
40
+          共 {{ displayData.length }} 条数据
41
+        </span>
42
+        <el-button v-if="calculationResults.length > 0" type="text" size="small" @click="handleExport" icon="el-icon-download">
43
+          导出结果
44
+        </el-button>
45
+      </div>
46
+      
47
+      <el-table :data="displayData" style="width: 100%" border stripe>
48
+        <el-table-column prop="pointNumber" label="待求点号" width="120" align="center"></el-table-column>
49
+        <el-table-column prop="longitude" label="经度(°)" width="150" align="center">
50
+          <template slot-scope="scope">
51
+            <span>{{ formatNumber(scope.row.longitude, 9) }}</span>
52
+          </template>
53
+        </el-table-column>
54
+        <el-table-column prop="latitude" label="纬度(°)" width="150" align="center">
55
+          <template slot-scope="scope">
56
+            <span>{{ formatNumber(scope.row.latitude, 9) }}</span>
57
+          </template>
58
+        </el-table-column>
59
+        <el-table-column prop="longitudeDMS" label="经度(° ′ ″)" width="200" align="center">
60
+          <template slot-scope="scope">
61
+            <span v-if="scope.row.longitudeDMS" class="result-value">{{ formatNumber(scope.row.longitudeDMS, 7) }}</span>
62
+            <span v-else class="empty-cell">-</span>
63
+          </template>
64
+        </el-table-column>
65
+        <el-table-column prop="latitudeDMS" label="纬度(° ′ ″)" width="200" align="center">
66
+          <template slot-scope="scope">
67
+            <span v-if="scope.row.latitudeDMS" class="result-value">{{ formatNumber(scope.row.latitudeDMS, 7) }}</span>
68
+            <span v-else class="empty-cell">-</span>
69
+          </template>
70
+        </el-table-column>
71
+      </el-table>
72
+    </div>
73
+    
74
+    <!-- 空状态 -->
75
+    <div v-else class="empty-state">
76
+      <i class="el-icon-document"></i>
77
+      <p>暂无数据</p>
78
+      <p class="empty-tip">请先导入 Excel 数据</p>
79
+    </div>
80
+    
81
+    <!-- 导入 Excel 对话框 -->
82
+    <el-dialog title="导入 Excel" :visible.sync="importDialogVisible" width="550px" @close="resetImport">
83
+      <div style="text-align: center;">
84
+        <el-upload
85
+          class="upload-area"
86
+          :action="''"
87
+          :auto-upload="false"
88
+          :on-change="handleFileChange"
89
+          :show-file-list="true"
90
+          accept=".xlsx,.xls"
91
+          drag
92
+          ref="upload">
93
+          <i class="el-icon-upload"></i>
94
+          <div class="el-upload__text">将 Excel 文件拖到此处,或<em>点击选择</em></div>
95
+          <div class="el-upload__tip" slot="tip">
96
+            支持.xlsx 和.xls 格式文件
97
+          </div>
98
+        </el-upload>
99
+      </div>
100
+      
101
+      <span slot="footer" class="dialog-footer">
102
+        <el-button @click="importDialogVisible = false">取消</el-button>
103
+        <el-button type="primary" @click="submitImport" :loading="importLoading" :disabled="!importFile">
104
+          确认导入
105
+        </el-button>
106
+      </span>
107
+    </el-dialog>
108
+    
109
+    <!-- 字段映射对话框 -->
110
+    <el-dialog title="字段映射" :visible.sync="mappingDialogVisible" width="500px">
111
+      <div class="mapping-tips">
112
+        <el-alert
113
+          title="字段映射说明"
114
+          type="info"
115
+          :closable="false"
116
+          show-icon>
117
+          请将Excel中的列映射到对应的坐标字段。
118
+        </el-alert>
119
+      </div>
120
+      
121
+      <div class="mapping-section">
122
+        <div class="mapping-row">
123
+          <label class="mapping-label">点号字段</label>
124
+          <el-select v-model="fieldMapping.pointNumber" class="mapping-select" size="small">
125
+            <el-option label="请选择" value=""></el-option>
126
+            <el-option v-for="col in excelColumns" :key="col" :label="col" :value="col"></el-option>
127
+          </el-select>
128
+        </div>
129
+        <div class="mapping-row">
130
+          <label class="mapping-label">经度字段</label>
131
+          <el-select v-model="fieldMapping.longitude" class="mapping-select" size="small">
132
+            <el-option label="请选择" value=""></el-option>
133
+            <el-option v-for="col in excelColumns" :key="col" :label="col" :value="col"></el-option>
134
+          </el-select>
135
+        </div>
136
+        <div class="mapping-row">
137
+          <label class="mapping-label">纬度字段</label>
138
+          <el-select v-model="fieldMapping.latitude" class="mapping-select" size="small">
139
+            <el-option label="请选择" value=""></el-option>
140
+            <el-option v-for="col in excelColumns" :key="col" :label="col" :value="col"></el-option>
141
+          </el-select>
142
+        </div>
143
+      </div>
144
+      
145
+      <span slot="footer" class="dialog-footer">
146
+        <el-button @click="cancelMapping">取消</el-button>
147
+        <el-button type="primary" @click="confirmMapping" :disabled="!validateMapping">
148
+          确认映射
149
+        </el-button>
150
+      </span>
151
+    </el-dialog>
152
+  </div>
153
+</template>
154
+
155
+<script>
156
+import { calculate, importExcel, exportExcel, downloadTemplate } from '@/api/calculate/degreetoDMS'
157
+
158
+export default {
159
+  name: 'DegreeToDMSTool',
160
+  data() {
161
+    return {
162
+      importedData: [],        // 导入的原始数据
163
+      calculationResults: [],  // 计算结果
164
+      importDialogVisible: false,
165
+      importLoading: false,
166
+      importFile: null,
167
+      calculating: false,
168
+      
169
+      // 字段映射相关
170
+      mappingDialogVisible: false,
171
+      excelColumns: [],
172
+      fieldMapping: {
173
+        pointNumber: '',
174
+        longitude: '',
175
+        latitude: ''
176
+      },
177
+      rawImportedData: []
178
+    }
179
+  },
180
+  methods: {
181
+    // 打开导入对话框 
182
+    handleImport() {
183
+      this.importDialogVisible = true
184
+    },
185
+    
186
+    // 文件选择变化
187
+    handleFileChange(file) {
188
+      this.importFile = file.raw
189
+    },
190
+    
191
+    // 提交导入
192
+    async submitImport() {
193
+      if (!this.importFile) {
194
+        this.$message.warning('请选择文件')
195
+        return
196
+      }
197
+      
198
+      this.importLoading = true
199
+      
200
+      const formData = new FormData()
201
+      formData.append('file', this.importFile)
202
+      
203
+      try {
204
+        const response = await importExcel(formData)
205
+        
206
+        if (response.code === 200) {
207
+          const result = response.data
208
+          
209
+          // 获取Excel列名
210
+          this.excelColumns = result.columns || this.extractColumns(result.data)
211
+          
212
+          // 保存原始数据
213
+          this.rawImportedData = result.data || []
214
+          
215
+          // 清空之前的计算结果
216
+          this.calculationResults = []
217
+          
218
+          // 自动匹配字段
219
+          this.autoMatchFields()
220
+          
221
+          // 显示字段映射对话框
222
+          this.mappingDialogVisible = true
223
+          
224
+          this.$message.success(`${response.msg}`)
225
+          this.importDialogVisible = false
226
+          this.importLoading = false
227
+        } else {
228
+          this.$message.error(response.msg)
229
+          this.importLoading = false
230
+        }
231
+      } catch (error) {
232
+        this.$message.error('导入失败')
233
+        this.importLoading = false
234
+      }
235
+    },
236
+    
237
+    // 提取列名
238
+    extractColumns(data) {
239
+      if (!data || data.length === 0) return []
240
+      return Object.keys(data[0])
241
+    },
242
+    
243
+    // 自动匹配字段
244
+    autoMatchFields() {
245
+      this.resetFieldMapping()
246
+      
247
+      const keywordMap = {
248
+        pointNumber: ['点号', '待求点', '点', '编号', '序号', 'ID'],
249
+        longitude: ['经度', 'lon', 'Longitude', 'L', '东经', '西经'],
250
+        latitude: ['纬度', 'lat', 'Latitude', 'B', '北纬', '南纬'],
251
+      }
252
+      
253
+      this.excelColumns.forEach(col => {
254
+        const lowerCol = col.toLowerCase()
255
+        Object.keys(keywordMap).forEach(field => {
256
+          if (!this.fieldMapping[field]) {
257
+            keywordMap[field].forEach(keyword => {
258
+              if (col.includes(keyword) || lowerCol.includes(keyword.toLowerCase())) {
259
+                this.fieldMapping[field] = col
260
+              }
261
+            })
262
+          }
263
+        })
264
+      })
265
+    },
266
+    
267
+    // 确认字段映射
268
+    confirmMapping() {
269
+      if (!this.validateMapping) {
270
+        this.$message.warning('请完成所有必填字段的映射')
271
+        return
272
+      }
273
+      
274
+      // 根据映射转换数据
275
+      this.importedData = this.rawImportedData.map(item => ({
276
+        pointNumber: item[this.fieldMapping.pointNumber],
277
+        longitude: item[this.fieldMapping.longitude],
278
+        latitude: item[this.fieldMapping.latitude],
279
+      }))
280
+      
281
+      this.mappingDialogVisible = false
282
+      this.resetFieldMapping()
283
+    },
284
+    
285
+    // 取消字段映射
286
+    cancelMapping() {
287
+      this.mappingDialogVisible = false
288
+      this.resetFieldMapping()
289
+      this.rawImportedData = []
290
+    },
291
+    
292
+    // 重置字段映射
293
+    resetFieldMapping() {
294
+      this.fieldMapping = {
295
+        pointNumber: '',
296
+        longitude: '',
297
+        latitude: '',
298
+      }
299
+    },
300
+    
301
+    // 下载模板
302
+    handleDownloadTemplate() {
303
+      this.download('/calculate/degreeToDMS/template', {}, '度转度分秒模板.xlsx')
304
+    },
305
+    
306
+    // 执行计算
307
+    async handleCalculate() {
308
+      if (!this.importedData || this.importedData.length === 0) {
309
+        this.$message.warning('没有数据可计算')
310
+        return
311
+      }
312
+      
313
+      this.calculating = true
314
+      
315
+      // 构建请求参数
316
+      const requestData = this.importedData.map(item => ({
317
+        pointNumber: item.pointNumber,
318
+        longitude: parseFloat(item.longitude),
319
+        latitude: parseFloat(item.latitude),
320
+      }))
321
+      
322
+      const response = await calculate(requestData)
323
+      
324
+      if (response.code === 200) {
325
+        const results = response.data || []
326
+        
327
+        // 更新计算结果
328
+        this.calculationResults = results.map(item => ({
329
+          pointNumber: item.pointNumber,
330
+          longitude: item.longitude,
331
+          latitude: item.latitude,
332
+          longitudeDMS: item.longitudeDMS,
333
+          latitudeDMS: item.latitudeDMS
334
+        }))
335
+        
336
+        const successCount = results.filter(r => r.longitudeDMS !== undefined && r.longitudeDMS !== -1).length
337
+        const failCount = results.length - successCount
338
+        
339
+        this.$message.success(`计算完成,成功 ${successCount} 条${failCount > 0 ? `,失败 ${failCount} 条` : ''}`)
340
+      } else {
341
+        this.$message.error(response.msg)
342
+      }
343
+      
344
+      this.calculating = false
345
+    },
346
+    
347
+    // 导出结果
348
+    async handleExport() {
349
+      if (this.calculationResults.length === 0) {
350
+        this.$message.warning('没有结果可导出')
351
+        return
352
+      }
353
+      
354
+      const response = await exportExcel(this.calculationResults)
355
+      
356
+      const blob = new Blob([response], { 
357
+        type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
358
+      })
359
+      
360
+      const url = window.URL.createObjectURL(blob)
361
+      const link = document.createElement('a')
362
+      link.href = url
363
+      link.download = `角度度分秒转换结果_${new Date().getTime()}.xlsx`
364
+      link.click()
365
+      window.URL.revokeObjectURL(url)
366
+    },
367
+    
368
+    // 清空全部
369
+    clearAll() {
370
+      if ((!this.importedData || this.importedData.length === 0) && this.calculationResults.length === 0) {
371
+        return
372
+      }
373
+      
374
+      this.$confirm('确定清空所有数据吗?此操作不可恢复!', '警告', {
375
+        confirmButtonText: '确定',
376
+        cancelButtonText: '取消',
377
+        type: 'warning'
378
+      }).then(() => {
379
+        this.importedData = []
380
+        this.calculationResults = []
381
+        this.$message.success('已清空所有数据')
382
+      }).catch(() => {})
383
+    },
384
+    
385
+    // 重置导入状态
386
+    resetImport() {
387
+      this.importFile = null
388
+      if (this.$refs.upload) {
389
+        this.$refs.upload.clearFiles()
390
+      }
391
+    },
392
+    
393
+    // 格式化数字显示
394
+    formatNumber(value, decimals = 3) {
395
+      if (value === null || value === undefined) return '--'
396
+      return Number(value).toFixed(decimals)
397
+    }
398
+  },
399
+  
400
+  computed: {
401
+    // 验证字段映射
402
+    validateMapping() {
403
+      if (!this.fieldMapping) {
404
+        return false
405
+      }
406
+      return this.fieldMapping.pointNumber && 
407
+             this.fieldMapping.longitude && 
408
+             this.fieldMapping.latitude
409
+    },
410
+    
411
+    // 显示数据:优先显示计算结果,否则显示导入数据
412
+    displayData() {
413
+      if (this.calculationResults.length > 0) {
414
+        return this.calculationResults.map(item => ({
415
+          pointNumber: item.pointNumber,
416
+          longitude: item.longitude,
417
+          latitude: item.latitude,
418
+          longitudeDMS: item.longitudeDMS,
419
+          latitudeDMS: item.latitudeDMS,
420
+        }))
421
+      } else if (this.importedData.length > 0) {
422
+        return this.importedData.map(item => ({
423
+          pointNumber: item.pointNumber,
424
+          longitude: item.longitude,
425
+          latitude: item.latitude,
426
+          longitudeDMS: undefined,
427
+          latitudeDMS: undefined,
428
+        }))
429
+      }
430
+      return []
431
+    }
432
+  }
433
+}
434
+</script>
435
+
436
+<style scoped>
437
+.degree-to-dms-tool {
438
+  width: 100%;
439
+}
440
+
441
+.tool-header {
442
+  margin-bottom: 20px;
443
+}
444
+
445
+.card-header-buttons {
446
+  display: flex;
447
+  gap: 10px;
448
+  flex-wrap: wrap;
449
+}
450
+
451
+.result-section {
452
+  margin-top: 10px;
453
+}
454
+
455
+.section-header {
456
+  padding: 12px 16px;
457
+  background: #f5f7fa;
458
+  border-radius: 8px 8px 0 0;
459
+  border: 1px solid #e4e7ed;
460
+  border-bottom: none;
461
+  display: flex;
462
+  justify-content: space-between;
463
+  align-items: center;
464
+}
465
+
466
+.section-title {
467
+  font-size: 14px;
468
+  font-weight: bold;
469
+  color: #303133;
470
+}
471
+
472
+.section-title i {
473
+  margin-right: 8px;
474
+  color: #11a983;
475
+}
476
+
477
+.section-info {
478
+  font-size: 12px;
479
+  color: #909399;
480
+}
481
+
482
+.result-value {
483
+  color: #409eff;
484
+  font-family: monospace;
485
+  font-weight: 500;
486
+}
487
+
488
+.empty-state {
489
+  text-align: center;
490
+  padding: 60px 20px;
491
+  background: #fff;
492
+  border-radius: 8px;
493
+  border: 1px solid #e4e7ed;
494
+  color: #909399;
495
+}
496
+
497
+.empty-state i {
498
+  font-size: 64px;
499
+  margin-bottom: 16px;
500
+}
501
+
502
+.empty-tip {
503
+  font-size: 12px;
504
+  margin-top: 12px;
505
+  color: #c0c4cc;
506
+}
507
+
508
+.import-tips {
509
+  margin-bottom: 20px;
510
+}
511
+
512
+.template-info {
513
+  margin-top: 12px;
514
+}
515
+
516
+.template-info ul {
517
+  margin: 8px 0;
518
+  padding-left: 20px;
519
+}
520
+
521
+.template-info li {
522
+  margin: 4px 0;
523
+  font-size: 13px;
524
+}
525
+
526
+.template-download-wrapper {
527
+  margin-top: 12px;
528
+  display: flex;
529
+  align-items: center;
530
+  justify-content: flex-end;
531
+  gap: 10px;
532
+  padding-top: 12px;
533
+  border-top: 1px dashed #e4e7ed;
534
+}
535
+
536
+.upload-area {
537
+  margin-top: 10px;
538
+}
539
+
540
+.preview-info {
541
+  margin-bottom: 15px;
542
+  padding: 10px;
543
+  background: #f5f7fa;
544
+  border-radius: 4px;
545
+  font-size: 14px;
546
+  color: #606266;
547
+}
548
+
549
+.dialog-footer {
550
+  width: 100%;
551
+  display: flex;
552
+  justify-content: flex-end;
553
+  gap: 10px;
554
+}
555
+
556
+/* 字段映射对话框样式 */
557
+.mapping-tips {
558
+  margin-bottom: 20px;
559
+}
560
+
561
+.mapping-section {
562
+  background: #f9fafb;
563
+  border-radius: 8px;
564
+  padding: 16px;
565
+}
566
+
567
+.mapping-row {
568
+  display: flex;
569
+  align-items: center;
570
+  margin-bottom: 12px;
571
+}
572
+
573
+.mapping-row:last-child {
574
+  margin-bottom: 0;
575
+}
576
+
577
+.mapping-label {
578
+  width: 120px;
579
+  font-weight: 500;
580
+  color: #606266;
581
+}
582
+
583
+.mapping-select {
584
+  flex: 1;
585
+  min-width: 200px;
586
+}
587
+
588
+.empty-cell {
589
+  color: #C0C4CC;
590
+  font-style: italic;
591
+}
592
+</style>

+ 564
- 0
oa-ui/src/views/calculate/tools/degreetoradian.vue Datei anzeigen

@@ -0,0 +1,564 @@
1
+<!-- 角度转弧度工具组件 -->
2
+<template>
3
+  <div class="degree-to-radian-tool">
4
+    <!-- 操作按钮区 -->
5
+    <div class="tool-header">
6
+      <div class="card-header-buttons">
7
+        <el-button type="primary" @click="handleImport" size="small" icon="el-icon-upload">
8
+          导入 Excel
9
+        </el-button>
10
+        <el-button 
11
+          type="warning" 
12
+          @click="handleCalculate" 
13
+          size="small" 
14
+          :disabled="!importedData || importedData.length === 0 || calculating"
15
+          :loading="calculating"
16
+          icon="el-icon-caret-right">
17
+          {{ calculating ? '计算中...' : '执行计算' }}
18
+        </el-button>
19
+        <el-button 
20
+          type="danger" 
21
+          @click="clearAll" 
22
+          size="small" 
23
+          :disabled="(!importedData || importedData.length === 0) && calculationResults.length === 0"
24
+          plain
25
+          icon="el-icon-delete">
26
+          清空全部
27
+        </el-button>
28
+      </div>
29
+    </div>
30
+
31
+    <!-- 数据展示区域 -->
32
+    <div v-if="displayData.length > 0" class="result-section">
33
+      <div class="section-header">
34
+        <span class="section-title">
35
+          <i class="el-icon-tickets"></i>
36
+          {{ calculationResults.length > 0 ? '计算结果' : '导入数据' }}
37
+        </span>
38
+        <span class="section-info">
39
+          共 {{ displayData.length }} 条数据
40
+        </span>
41
+        <el-button v-if="calculationResults.length > 0" type="text" size="small" @click="handleExport" icon="el-icon-download">
42
+          导出结果
43
+        </el-button>
44
+      </div>
45
+      
46
+      <el-table :data="displayData" style="width: 100%" border stripe>
47
+        <el-table-column prop="pointNumber" label="待求点号" width="120" align="center"></el-table-column>
48
+        <el-table-column prop="longitude" label="经度(°)" width="180" align="center">
49
+          <template slot-scope="scope">
50
+            <span>{{ formatNumber(scope.row.longitude, 9) }}</span>
51
+          </template>
52
+        </el-table-column>
53
+        <el-table-column prop="latitude" label="纬度(°)" width="180" align="center">
54
+          <template slot-scope="scope">
55
+            <span>{{ formatNumber(scope.row.latitude, 9) }}</span>
56
+          </template>
57
+        </el-table-column>
58
+        <el-table-column prop="longitudeRadian" label="经度(弧度)" width="200" align="center">
59
+          <template slot-scope="scope">
60
+            <span v-if="scope.row.longitudeRadian !== undefined" class="result-value">{{ formatNumber(scope.row.longitudeRadian, 9) }}</span>
61
+            <span v-else class="empty-cell">-</span>
62
+          </template>
63
+        </el-table-column>
64
+        <el-table-column prop="latitudeRadian" label="纬度(弧度)" width="200" align="center">
65
+          <template slot-scope="scope">
66
+            <span v-if="scope.row.latitudeRadian !== undefined" class="result-value">{{ formatNumber(scope.row.latitudeRadian, 9) }}</span>
67
+            <span v-else class="empty-cell">-</span>
68
+          </template>
69
+        </el-table-column>
70
+      </el-table>
71
+    </div>
72
+    
73
+    <!-- 空状态 -->
74
+    <div v-else class="empty-state">
75
+      <i class="el-icon-document"></i>
76
+      <p>暂无数据</p>
77
+      <p class="empty-tip">请先导入 Excel 数据,然后点击"执行计算"</p>
78
+    </div>
79
+    
80
+    <!-- 导入 Excel 对话框 -->
81
+    <el-dialog title="导入 Excel" :visible.sync="importDialogVisible" width="550px" @close="resetImport">
82
+      <div style="text-align: center;">
83
+        <el-upload
84
+          class="upload-area"
85
+          :action="''"
86
+          :auto-upload="false"
87
+          :on-change="handleFileChange"
88
+          :show-file-list="true"
89
+          accept=".xlsx,.xls"
90
+          drag
91
+          ref="upload">
92
+          <i class="el-icon-upload"></i>
93
+          <div class="el-upload__text">将 Excel 文件拖到此处,或<em>点击选择</em></div>
94
+          <div class="el-upload__tip" slot="tip">
95
+            支持.xlsx 和.xls 格式文件
96
+          </div>
97
+        </el-upload>
98
+      </div>
99
+      
100
+      <span slot="footer" class="dialog-footer">
101
+        <el-button @click="importDialogVisible = false">取消</el-button>
102
+        <el-button type="primary" @click="submitImport" :loading="importLoading" :disabled="!importFile">
103
+          确认导入
104
+        </el-button>
105
+      </span>
106
+    </el-dialog>
107
+    
108
+    <!-- 字段映射对话框 -->
109
+    <el-dialog title="字段映射" :visible.sync="mappingDialogVisible" width="500px">
110
+      <div class="mapping-tips">
111
+        <el-alert
112
+          title="字段映射说明"
113
+          type="info"
114
+          :closable="false"
115
+          show-icon>
116
+          请将Excel中的列映射到对应的坐标字段。
117
+        </el-alert>
118
+      </div>
119
+      
120
+      <div class="mapping-section">
121
+        <div class="mapping-row">
122
+          <label class="mapping-label">点号字段</label>
123
+          <el-select v-model="fieldMapping.pointNumber" class="mapping-select" size="small">
124
+            <el-option label="请选择" value=""></el-option>
125
+            <el-option v-for="col in excelColumns" :key="col" :label="col" :value="col"></el-option>
126
+          </el-select>
127
+        </div>
128
+        <div class="mapping-row">
129
+          <label class="mapping-label">经度(度)字段</label>
130
+          <el-select v-model="fieldMapping.longitude" class="mapping-select" size="small">
131
+            <el-option label="请选择" value=""></el-option>
132
+            <el-option v-for="col in excelColumns" :key="col" :label="col" :value="col"></el-option>
133
+          </el-select>
134
+        </div>
135
+        <div class="mapping-row">
136
+          <label class="mapping-label">纬度(度)字段</label>
137
+          <el-select v-model="fieldMapping.latitude" class="mapping-select" size="small">
138
+            <el-option label="请选择" value=""></el-option>
139
+            <el-option v-for="col in excelColumns" :key="col" :label="col" :value="col"></el-option>
140
+          </el-select>
141
+        </div>
142
+      </div>
143
+      
144
+      <span slot="footer" class="dialog-footer">
145
+        <el-button @click="cancelMapping">取消</el-button>
146
+        <el-button type="primary" @click="confirmMapping" :disabled="!validateMapping">
147
+          确认映射
148
+        </el-button>
149
+      </span>
150
+    </el-dialog>
151
+  </div>
152
+</template>
153
+
154
+<script>
155
+import { calculate, importExcel, exportExcel } from '@/api/calculate/degreetoradian'
156
+
157
+export default {
158
+  name: 'DegreeToRadianTool',
159
+  data() {
160
+    return {
161
+      importedData: [],        // 导入的原始数据
162
+      calculationResults: [],  // 计算结果
163
+      importDialogVisible: false,
164
+      importLoading: false,
165
+      importFile: null,
166
+      calculating: false,
167
+      // 字段映射相关
168
+      mappingDialogVisible: false,
169
+      excelColumns: [],
170
+      fieldMapping: {
171
+        pointNumber: '',
172
+        longitude: '',
173
+        latitude: ''
174
+      },
175
+      rawImportedData: [],
176
+    }
177
+  },
178
+  computed: {
179
+    validateMapping() {
180
+      return this.fieldMapping.pointNumber && this.fieldMapping.longitude && this.fieldMapping.latitude
181
+    },
182
+    displayData() {
183
+      if (this.calculationResults.length > 0) {
184
+        return this.calculationResults.map(item => ({
185
+          pointNumber: item.pointNumber,
186
+          longitude: item.longitude,
187
+          latitude: item.latitude,
188
+          longitudeRadian: item.longitudeRadian,
189
+          latitudeRadian: item.latitudeRadian
190
+        }))
191
+      } else if (this.importedData.length > 0) {
192
+        return this.importedData.map(item => ({
193
+          pointNumber: item.pointNumber,
194
+          longitude: item.longitude,
195
+          latitude: item.latitude,
196
+          longitudeRadian: undefined,
197
+          latitudeRadian: undefined
198
+        }))
199
+      }
200
+      return []
201
+    }
202
+  },
203
+  methods: {
204
+    // 打开导入对话框 
205
+    handleImport() {
206
+      this.importDialogVisible = true
207
+    },
208
+    
209
+    // 文件选择变化
210
+    handleFileChange(file) {
211
+      this.importFile = file.raw
212
+    },
213
+    
214
+    // 提交导入
215
+    async submitImport() {
216
+      if (!this.importFile) {
217
+        this.$message.warning('请选择文件')
218
+        return
219
+      }
220
+      
221
+      this.importLoading = true
222
+      
223
+      const formData = new FormData()
224
+      formData.append('file', this.importFile)
225
+      
226
+      const response = await importExcel(formData)
227
+      
228
+      if (response.code === 200) {
229
+        const result = response.data
230
+        this.rawImportedData = result.data || []
231
+        this.excelColumns = result.columns || []
232
+        
233
+        // 自动匹配字段
234
+        this.autoMatchFields()
235
+        
236
+        this.importDialogVisible = false
237
+        this.mappingDialogVisible = true
238
+      } else {
239
+        this.$message.error(response.msg)
240
+      }
241
+      this.importLoading = false
242
+    },
243
+    
244
+    // 提取列名
245
+    extractColumns(rows) {
246
+      if (!rows || rows.length === 0) return []
247
+      return Object.keys(rows[0])
248
+    },
249
+    
250
+    // 自动匹配字段
251
+    autoMatchFields() {
252
+      const mappings = {
253
+        pointNumber: ['点号', '编号', '名称', 'ID', '序号'],
254
+        longitude: ['经度', '东经', '西经'],
255
+        latitude: ['纬度', '北纬', '南纬']
256
+      }
257
+      
258
+      Object.keys(mappings).forEach(key => {
259
+        const keywords = mappings[key]
260
+        const matchedColumn = this.excelColumns.find(col => 
261
+          keywords.some(kw => col.includes(kw))
262
+        )
263
+        if (matchedColumn) {
264
+          this.fieldMapping[key] = matchedColumn
265
+        }
266
+      })
267
+    },
268
+    
269
+    // 确认字段映射
270
+    confirmMapping() {
271
+      if (!this.validateMapping) {
272
+        this.$message.warning('请至少映射点号、经度、纬度字段')
273
+        return
274
+      }
275
+      
276
+      // 根据映射关系转换数据
277
+      this.importedData = this.rawImportedData.map(item => ({
278
+        pointNumber: item[this.fieldMapping.pointNumber],
279
+        longitude: item[this.fieldMapping.longitude],
280
+        latitude: item[this.fieldMapping.latitude]
281
+      }))
282
+      
283
+      // 清空之前的计算结果
284
+      this.calculationResults = []
285
+      
286
+      this.mappingDialogVisible = false
287
+      this.$message.success('数据导入成功')
288
+    },
289
+    
290
+    // 取消字段映射
291
+    cancelMapping() {
292
+      this.mappingDialogVisible = false
293
+      this.rawImportedData = []
294
+      this.excelColumns = []
295
+      this.resetFieldMapping()
296
+    },
297
+    
298
+    // 重置字段映射
299
+    resetFieldMapping() {
300
+      this.fieldMapping = {
301
+        pointNumber: '',
302
+        longitude: '',
303
+        latitude: ''
304
+      }
305
+    },
306
+    
307
+    // 参数变化时清空计算结果
308
+    onParamsChange() {
309
+      if (this.calculationResults.length > 0) {
310
+        this.calculationResults = []
311
+        this.$message.info('参数已修改,请重新执行计算')
312
+      }
313
+    },
314
+    
315
+    // 执行计算
316
+    async handleCalculate() {
317
+      if (!this.importedData || this.importedData.length === 0) {
318
+        this.$message.warning('没有数据可计算')
319
+        return
320
+      }
321
+      
322
+      this.calculating = true
323
+      
324
+      try {
325
+        // 构建请求参数
326
+        const requestData = this.importedData.map(item => ({
327
+          pointNumber: item.pointNumber,
328
+          longitude: parseFloat(item.longitude),
329
+          latitude: parseFloat(item.latitude),
330
+        }))
331
+        
332
+        const response = await calculate(requestData)
333
+        
334
+        if (response.code === 200) {
335
+          const results = response.data || []
336
+          
337
+          // 更新计算结果
338
+          this.calculationResults = results.map(item => ({
339
+            pointNumber: item.pointNumber,
340
+            longitude: item.longitude,
341
+            latitude: item.latitude,
342
+            longitudeRadian: item.longitudeRadian,
343
+            latitudeRadian: item.latitudeRadian
344
+          }))
345
+          
346
+          const successCount = results.filter(r => r.longitudeRadian !== undefined && r.longitudeRadian !== -1).length
347
+          const failCount = results.length - successCount
348
+          
349
+          this.$message.success(`计算完成,成功 ${successCount} 条${failCount > 0 ? `,失败 ${failCount} 条` : ''}`)
350
+        } else {
351
+          this.$message.error(response.msg)
352
+        }
353
+      } catch (error) {
354
+        this.$message.error('计算失败:' + (error.message || error))
355
+      } finally {
356
+        this.calculating = false
357
+      }
358
+    },
359
+
360
+    // 导出结果
361
+    async handleExport() {
362
+      if (this.calculationResults.length === 0) {
363
+        this.$message.warning('没有结果可导出')
364
+        return
365
+      }
366
+      
367
+      const response = await exportExcel(this.calculationResults)
368
+      
369
+      const blob = new Blob([response], { 
370
+        type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
371
+      })
372
+      
373
+      const url = window.URL.createObjectURL(blob)
374
+      const link = document.createElement('a')
375
+      link.href = url
376
+      link.download = `度转弧度结果_${new Date().getTime()}.xlsx`
377
+      link.click()
378
+      window.URL.revokeObjectURL(url)
379
+    },
380
+    
381
+    // 清空全部
382
+    clearAll() {
383
+      if ((!this.importedData || this.importedData.length === 0) && this.calculationResults.length === 0) {
384
+        return
385
+      }
386
+      
387
+      this.$confirm('确定清空所有数据吗?此操作不可恢复!', '警告', {
388
+        confirmButtonText: '确定',
389
+        cancelButtonText: '取消',
390
+        type: 'warning'
391
+      }).then(() => {
392
+        this.importedData = []
393
+        this.calculationResults = []
394
+        this.$message.success('已清空所有数据')
395
+      }).catch(() => {})
396
+    },
397
+    
398
+    // 重置导入状态
399
+    resetImport() {
400
+      this.importFile = null
401
+      if (this.$refs.upload) {
402
+        this.$refs.upload.clearFiles()
403
+      }
404
+    },
405
+    
406
+    // 格式化数字显示
407
+    formatNumber(value, decimals = 3) {
408
+      if (value === null || value === undefined) return '--'
409
+      return Number(value).toFixed(decimals)
410
+    }
411
+  }
412
+}
413
+</script>
414
+
415
+<style scoped>
416
+.degree-to-radian-tool {
417
+  width: 100%;
418
+}
419
+
420
+.tool-header {
421
+  margin-bottom: 20px;
422
+}
423
+
424
+.card-header-buttons {
425
+  display: flex;
426
+  gap: 10px;
427
+  flex-wrap: wrap;
428
+}
429
+
430
+.params-area {
431
+  margin-bottom: 20px;
432
+  padding: 16px;
433
+  background: #f5f7fa;
434
+  border-radius: 8px;
435
+  border: 1px solid #e4e7ed;
436
+}
437
+
438
+.params-row {
439
+  display: flex;
440
+  gap: 24px;
441
+  flex-wrap: wrap;
442
+}
443
+
444
+.params-group {
445
+  display: flex;
446
+  align-items: center;
447
+  gap: 8px;
448
+}
449
+
450
+.param-label {
451
+  font-size: 14px;
452
+  color: #606266;
453
+  font-weight: 500;
454
+}
455
+
456
+.result-section {
457
+  margin-top: 10px;
458
+}
459
+
460
+.section-header {
461
+  padding: 12px 16px;
462
+  background: #f5f7fa;
463
+  border-radius: 8px 8px 0 0;
464
+  border: 1px solid #e4e7ed;
465
+  border-bottom: none;
466
+  display: flex;
467
+  justify-content: space-between;
468
+  align-items: center;
469
+}
470
+
471
+.section-title {
472
+  font-size: 14px;
473
+  font-weight: bold;
474
+  color: #303133;
475
+}
476
+
477
+.section-title i {
478
+  margin-right: 8px;
479
+  color: #11a983;
480
+}
481
+
482
+.section-info {
483
+  font-size: 12px;
484
+  color: #909399;
485
+}
486
+
487
+.result-value {
488
+  color: #409eff;
489
+  font-family: monospace;
490
+  font-weight: 500;
491
+}
492
+
493
+.empty-cell {
494
+  color: #c0c4cc;
495
+}
496
+
497
+.empty-state {
498
+  text-align: center;
499
+  padding: 60px 20px;
500
+  background: #fff;
501
+  border-radius: 8px;
502
+  border: 1px solid #e4e7ed;
503
+  color: #909399;
504
+}
505
+
506
+.empty-state i {
507
+  font-size: 64px;
508
+  margin-bottom: 16px;
509
+}
510
+
511
+.empty-tip {
512
+  font-size: 12px;
513
+  margin-top: 12px;
514
+  color: #c0c4cc;
515
+}
516
+
517
+.upload-area {
518
+  margin-top: 10px;
519
+}
520
+
521
+.mapping-content {
522
+  max-height: 400px;
523
+  overflow-y: auto;
524
+}
525
+
526
+.dialog-footer {
527
+  width: 100%;
528
+  display: flex;
529
+  justify-content: flex-end;
530
+  gap: 10px;
531
+}
532
+
533
+/* 字段映射对话框样式 */
534
+.mapping-tips {
535
+  margin-bottom: 20px;
536
+}
537
+
538
+.mapping-section {
539
+  background: #f9fafb;
540
+  border-radius: 8px;
541
+  padding: 16px;
542
+}
543
+
544
+.mapping-row {
545
+  display: flex;
546
+  align-items: center;
547
+  margin-bottom: 12px;
548
+}
549
+
550
+.mapping-row:last-child {
551
+  margin-bottom: 0;
552
+}
553
+
554
+.mapping-label {
555
+  width: 140px;
556
+  font-weight: 500;
557
+  color: #606266;
558
+}
559
+
560
+.mapping-select {
561
+  flex: 1;
562
+  min-width: 200px;
563
+}
564
+</style>

+ 351
- 167
oa-ui/src/views/calculate/tools/gaussbandchange.vue Datei anzeigen

@@ -8,14 +8,6 @@
8 8
           导入Excel
9 9
         </el-button>
10 10
 
11
-        <el-button 
12
-          type="success" 
13
-          @click="handlePreview" 
14
-          size="small" 
15
-          :disabled="!importedData || importedData.length === 0"
16
-          icon="el-icon-view">
17
-          预览数据
18
-        </el-button>
19 11
         <el-button 
20 12
           type="warning" 
21 13
           @click="handleCalculate" 
@@ -37,47 +29,117 @@
37 29
       </div>
38 30
     </div>
39 31
     
40
-    <!-- 投影面高程设置 -->
41
-    <div class="height-setting">
42
-      <div class="setting-label">
43
-        <span>投影面高程</span>
32
+    <!-- 参数设置区域 -->
33
+    <div class="params-area">
34
+      <div class="params-row">
35
+        <div class="params-group">
36
+          <label class="param-label">投影面高程</label>
37
+          <el-input 
38
+            v-model="projectionHeight" 
39
+            size="small" 
40
+            style="width: 120px;">
41
+          </el-input>
42
+          <span class="unit">米 (m)</span>
43
+        </div>
44
+        
45
+        <div class="params-group">
46
+          <label class="param-label">坐标系</label>
47
+          <el-select v-model="commonParams.coordinateSystem" size="small" style="width: 140px;" @change="onParamsChange">
48
+            <el-option label="WGS84" value="WGS84"></el-option>
49
+            <el-option label="CGCS2000" value="CGCS2000"></el-option>
50
+            <el-option label="Beijing54" value="Beijing54"></el-option>
51
+            <el-option label="Xian80" value="Xian80"></el-option>
52
+          </el-select>
53
+        </div>
54
+        
55
+        <div class="params-group">
56
+          <label class="param-label">经度位置</label>
57
+          <el-select v-model="commonParams.longitudePosition" size="small" style="width: 80px;" @change="onParamsChange">
58
+            <el-option label="E" value="E"></el-option>
59
+            <el-option label="W" value="W"></el-option>
60
+          </el-select>
61
+        </div>
62
+        
63
+        <div class="params-group">
64
+          <label class="param-label">纬度位置</label>
65
+          <el-select v-model="commonParams.latitudePosition" size="small" style="width: 80px;" @change="onParamsChange">
66
+            <el-option label="N" value="N"></el-option>
67
+            <el-option label="S" value="S"></el-option>
68
+          </el-select>
69
+        </div>
44 70
       </div>
45
-      <div class="setting-content">
46
-        <el-input 
47
-          v-model="projectionHeight" 
48
-          size="small" 
49
-          style="width: 120px; margin-right: 10px;">
50
-        </el-input>
51
-        <span class="unit">米 (m)</span>
71
+
72
+      <div class="params-row" style="margin-top: 15px;">
73
+        <div class="params-group">
74
+          <label class="param-label">带号</label>
75
+          <el-input v-model.number="commonParams.band" size="small" style="width: 80px;" placeholder="带号" @change="onParamsChange"></el-input>
76
+        </div>
77
+        
78
+        <div class="params-group">
79
+          <label class="param-label">带宽</label>
80
+          <el-select v-model="commonParams.bandwidth" size="small" style="width: 80px;" @change="onParamsChange">
81
+            <el-option label="3" :value="3"></el-option>
82
+            <el-option label="6" :value="6"></el-option>
83
+          </el-select>
84
+        </div>
85
+        
86
+        <div class="params-group">
87
+          <label class="param-label">新带号</label>
88
+          <el-input v-model.number="commonParams.newBand" size="small" style="width: 80px;" placeholder="新带号" @change="onParamsChange"></el-input>
89
+        </div>
52 90
         
91
+        <div class="params-group">
92
+          <label class="param-label">新带宽</label>
93
+          <el-select v-model="commonParams.newBandwidth" size="small" style="width: 80px;" @change="onParamsChange">
94
+            <el-option label="3" :value="3"></el-option>
95
+            <el-option label="6" :value="6"></el-option>
96
+          </el-select>
97
+        </div>
53 98
       </div>
54 99
     </div>
55 100
 
56
-    <!-- 计算结果展示区 - 只显示计算结果,不包含原始数据 -->
57
-    <div v-if="calculationResults.length > 0" class="result-section">
101
+    <!-- 数据展示区域 - 合并显示导入数据和计算结果 -->
102
+    <div v-if="displayData.length > 0" class="result-section">
58 103
       <div class="section-header">
59 104
         <span class="section-title">
60 105
           <i class="el-icon-tickets"></i>
61
-          计算结果
106
+          {{ calculationResults.length > 0 ? '计算结果' : '导入数据' }}
62 107
         </span>
63 108
         <span class="section-info">
64
-          共 {{ calculationResults.length }} 条计算结果
109
+          共 {{ displayData.length }} 条数据
110
+          <span v-if="calculationResults.length > 0" style="margin-left: 10px;">
111
+            | 坐标系:{{ commonParams.coordinateSystem }}
112
+            | 带号:{{ commonParams.band }}
113
+            | 新带号:{{ commonParams.newBand }}
114
+          </span>
65 115
         </span>
66
-        <el-button type="text" size="small" @click="handleExport" icon="el-icon-download">
116
+        <el-button v-if="calculationResults.length > 0" type="text" size="small" @click="handleExport" icon="el-icon-download">
67 117
           导出结果
68 118
         </el-button>
69 119
       </div>
70 120
       
71
-      <el-table :data="calculationResults" style="width: 100%" border stripe>
121
+      <el-table :data="displayData" style="width: 100%" border stripe>
72 122
         <el-table-column prop="pointNumber" label="待求点号" width="120" align="center"></el-table-column>
73
-        <el-table-column prop="newGaussX" label="高斯坐标x(m)" width="180" align="center">
123
+        <el-table-column prop="gaussX" label="原高斯X(m)" width="150" align="center">
74 124
           <template slot-scope="scope">
75
-            <span class="result-value">{{ formatNumber(scope.row.newGaussX,4) }}</span>
125
+            <span>{{ formatNumber(scope.row.gaussX,4) }}</span>
76 126
           </template>
77 127
         </el-table-column>
78
-        <el-table-column prop="newGaussY" label="高斯坐标y(m)" width="180" align="center">
128
+        <el-table-column prop="gaussY" label="原高斯Y(m)" width="150" align="center">
79 129
           <template slot-scope="scope">
80
-            <span class="result-value">{{ formatNumber(scope.row.newGaussY,4) }}</span>
130
+            <span>{{ formatNumber(scope.row.gaussY,4) }}</span>
131
+          </template>
132
+        </el-table-column>
133
+        <el-table-column prop="newGaussX" label="新高斯X(m)" width="150" align="center">
134
+          <template slot-scope="scope">
135
+            <span v-if="scope.row.newGaussX" class="result-value">{{ formatNumber(scope.row.newGaussX,4) }}</span>
136
+            <span v-else class="empty-cell">-</span>
137
+          </template>
138
+        </el-table-column>
139
+        <el-table-column prop="newGaussY" label="新高斯Y(m)" width="150" align="center">
140
+          <template slot-scope="scope">
141
+            <span v-if="scope.row.newGaussY" class="result-value">{{ formatNumber(scope.row.newGaussY,4) }}</span>
142
+            <span v-else class="empty-cell">-</span>
81 143
           </template>
82 144
         </el-table-column>
83 145
       </el-table>
@@ -86,40 +148,12 @@
86 148
     <!-- 空状态 -->
87 149
     <div v-else class="empty-state">
88 150
       <i class="el-icon-document"></i>
89
-      <p>暂无计算结果</p>
90
-      <p class="empty-tip">请先导入Excel数据,然后点击"执行计算"</p>
151
+      <p>暂无数据</p>
152
+      <p class="empty-tip">请先导入Excel数据</p>
91 153
     </div>
92 154
     
93 155
     <!-- 导入Excel对话框 -->
94 156
     <el-dialog title="导入Excel" :visible.sync="importDialogVisible" width="550px" @close="resetImport">
95
-      <div class="import-tips">
96
-        <el-alert
97
-          title="Excel模板格式说明"
98
-          type="info"
99
-          :closable="false"
100
-          show-icon>
101
-          <div class="template-info">
102
-            <p>请确保Excel文件包含以下列(第一行为表头):</p>
103
-            <ul>
104
-              <li><strong>待求点</strong> - 点号</li>
105
-              <li><strong>坐标系</strong> - WGS84/CGCS2000/Beijing54/Xian80</li>
106
-              <li><strong>高斯坐标X</strong> - (m)</li>
107
-              <li><strong>高斯坐标Y</strong> - (m)</li>
108
-              <li><strong>带号</strong> - 带号数值</li>
109
-              <li><strong>带宽</strong> - 3或6</li>
110
-              <li><strong>新带号</strong> - 新带号数值</li>
111
-              <li><strong>新带宽</strong> - 3或6</li>
112
-              <li><strong>经度位置</strong> - E/W</li>
113
-              <li><strong>纬度位置</strong> - N/S</li>
114
-            </ul>
115
-          </div>
116
-        </el-alert>
117
-         <div class="template-download-wrapper">
118
-            <el-button type="primary" plain size="small" @click="handleDownloadTemplate" icon="el-icon-download">
119
-              下载Excel模板
120
-            </el-button>
121
-          </div>
122
-      </div>
123 157
       <div style="text-align: center;">
124 158
         <el-upload
125 159
           class="upload-area"
@@ -146,64 +180,47 @@
146 180
       </span>
147 181
     </el-dialog>
148 182
     
149
-    <!-- 预览数据对话框 - 显示导入的原始数据 -->
150
-    <el-dialog title="导入数据预览" :visible.sync="previewDialogVisible" width="80%">
151
-      <div class="preview-info">
152
-        <span>共 {{ importedData.length }} 条数据</span>
153
-        <span style="margin-left: 20px;">状态:{{ importedData.filter(item => item.calculated).length }} 条已计算,{{ importedData.filter(item => !item.calculated).length }} 条未计算</span>
183
+    <!-- 字段映射对话框 -->
184
+    <el-dialog title="字段映射" :visible.sync="mappingDialogVisible" width="500px">
185
+      <div class="mapping-tips">
186
+        <el-alert
187
+          title="字段映射说明"
188
+          type="info"
189
+          :closable="false"
190
+          show-icon>
191
+          请将Excel中的列映射到对应的坐标字段。
192
+        </el-alert>
154 193
       </div>
155
-      <el-table :data="importedData" border stripe max-height="500">
156
-        <el-table-column prop="pointNumber" label="待求点号" width="120" fixed="left" align="center"></el-table-column>
157
-        <el-table-column prop="coordinateSystem" label="坐标系" width="110" align="center">
158
-          <template slot-scope="scope">
159
-            <el-tag size="small" :type="getCoordinateType(scope.row.coordinateSystem)">
160
-              {{ scope.row.coordinateSystem }}
161
-            </el-tag>
162
-          </template>
163
-        </el-table-column>
164
-        <el-table-column prop="longitude" label="高斯X坐标(m)" width="130" align="center">
165
-          <template slot-scope="scope">
166
-            {{ formatNumber(scope.row.gaussX,4) }}
167
-          </template>
168
-        </el-table-column>
169
-         <el-table-column prop="longitudePosition" label="高斯Y坐标(m)" width="130" align="center">
170
-          <template slot-scope="scope">
171
-            {{ formatNumber(scope.row.gaussY,4) }}
172
-          </template>
173
-        </el-table-column>
174
-        <el-table-column prop="band" label="带号" width="130" align="center">
175
-          <template slot-scope="scope">
176
-            {{ scope.row.band}}
177
-                   </template>
178
-        </el-table-column>
179
-        <el-table-column prop="bandWidth" label="带宽" width="130" align="center">
180
-          <template slot-scope="scope">
181
-            {{ scope.row.bandWidth}}
182
-                   </template>
183
-        </el-table-column>
184
-        <el-table-column prop="newBand" label="新带号" width="130" align="center">
185
-          <template slot-scope="scope">
186
-            {{ scope.row.newBand}}
187
-                   </template>
188
-        </el-table-column>
189
-        <el-table-column prop="newBandWidth" label="新带宽" width="130" align="center">
190
-          <template slot-scope="scope">
191
-            {{ scope.row.newBandWidth}}
192
-                   </template>
193
-        </el-table-column>
194
-        <el-table-column prop="longitudePosition" label="经度位置" width="130" align="center">
195
-          <template slot-scope="scope">
196
-            {{ scope.row.longitudePosition}}
197
-                   </template>
198
-        </el-table-column>
199
-        <el-table-column prop="latitudePosition" label="纬度位置" width="130" align="center">
200
-          <template slot-scope="scope">
201
-            {{ scope.row.latitudePosition}}
202
-                   </template>
203
-        </el-table-column>
204
-      </el-table>
194
+      
195
+      <div class="mapping-section">
196
+        <div class="mapping-row">
197
+          <label class="mapping-label">点号字段</label>
198
+          <el-select v-model="fieldMapping.pointNumber" class="mapping-select" size="small">
199
+            <el-option label="请选择" value=""></el-option>
200
+            <el-option v-for="col in excelColumns" :key="col" :label="col" :value="col"></el-option>
201
+          </el-select>
202
+        </div>
203
+        <div class="mapping-row">
204
+          <label class="mapping-label">高斯X字段</label>
205
+          <el-select v-model="fieldMapping.gaussX" class="mapping-select" size="small">
206
+            <el-option label="请选择" value=""></el-option>
207
+            <el-option v-for="col in excelColumns" :key="col" :label="col" :value="col"></el-option>
208
+          </el-select>
209
+        </div>
210
+        <div class="mapping-row">
211
+          <label class="mapping-label">高斯Y字段</label>
212
+          <el-select v-model="fieldMapping.gaussY" class="mapping-select" size="small">
213
+            <el-option label="请选择" value=""></el-option>
214
+            <el-option v-for="col in excelColumns" :key="col" :label="col" :value="col"></el-option>
215
+          </el-select>
216
+        </div>
217
+      </div>
218
+      
205 219
       <span slot="footer" class="dialog-footer">
206
-        <el-button type="primary" @click="previewDialogVisible = false">关闭</el-button>
220
+        <el-button @click="cancelMapping">取消</el-button>
221
+        <el-button type="primary" @click="confirmMapping" :disabled="!validateMapping">
222
+          确认映射
223
+        </el-button>
207 224
       </span>
208 225
     </el-dialog>
209 226
   </div>
@@ -222,8 +239,27 @@ export default {
222 239
       importLoading: false,
223 240
       importFile: null,
224 241
       calculating: false,
225
-      previewDialogVisible: false,
226
-      projectionHeight: 0
242
+      projectionHeight: 0,
243
+      
244
+      // 公共参数(从Excel导入的数据中提取)
245
+      commonParams: {
246
+        coordinateSystem: 'CGCS2000',
247
+        longitudePosition: 'E',
248
+        latitudePosition: 'N',
249
+        band: 0,
250
+        bandwidth: 3,
251
+        newBand: 0,
252
+        newBandwidth: 3
253
+      },
254
+      
255
+      // 字段映射相关
256
+      mappingDialogVisible: false,
257
+      excelColumns: [],
258
+      fieldMapping: {
259
+        pointNumber: '',
260
+        gaussX: '',
261
+        gaussY: ''
262
+      }
227 263
     }
228 264
   },
229 265
   watch: {
@@ -266,42 +302,110 @@ export default {
266 302
       const formData = new FormData()
267 303
       formData.append('file', this.importFile)
268 304
       
305
+      try {
269 306
         const response = await importExcel(formData)
270 307
         
271 308
         if (response.code === 200) {
272 309
           const result = response.data
273
-          // 将后端返回的数据转换为前端格式
274
-          this.importedData = (result.data || []).map(item => ({
275
-            pointNumber: item.pointNumber,
276
-            coordinateSystem: item.coordinateSystem,
277
-            gaussX: item.gaussX,
278
-            gaussY: item.gaussY,
279
-            band: item.band,
280
-            bandWidth: item.bandWidth,
281
-            newBand: item.newBand,
282
-            newBandWidth: item.newBandWidth,
283
-            longitudePosition: item.longitudePosition,
284
-            latitudePosition: item.latitudePosition
285
-          }))
310
+          
311
+          // 获取Excel列名
312
+          this.excelColumns = result.columns || this.extractColumns(result.data)
313
+          
314
+          // 保存原始数据
315
+          this.rawImportedData = result.data || []
286 316
           
287 317
           // 清空之前的计算结果
288 318
           this.calculationResults = []
289 319
           
320
+          // 自动匹配字段
321
+          this.autoMatchFields()
322
+          
323
+          // 显示字段映射对话框
324
+          this.mappingDialogVisible = true
325
+          
290 326
           this.$message.success(`${response.msg}`)
291 327
           this.importDialogVisible = false
292 328
           this.importLoading = false
293 329
         } else {
294 330
           this.$message.error(response.msg)
331
+          this.importLoading = false
295 332
         }
333
+      } catch (error) {
334
+        this.$message.error('导入失败')
335
+        this.importLoading = false
336
+      }
296 337
     },
297 338
     
298
-    // 预览数据 - 显示导入的原始数据
299
-    handlePreview() {
300
-      if (!this.importedData || this.importedData.length === 0) {
301
-        this.$message.warning('没有可预览的数据')
339
+    // 提取列名
340
+    extractColumns(data) {
341
+      if (!data || data.length === 0) return []
342
+      return Object.keys(data[0])
343
+    },
344
+    
345
+    // 自动匹配字段
346
+    autoMatchFields() {
347
+      this.resetFieldMapping()
348
+      
349
+      const keywordMap = {
350
+        pointNumber: ['点号', '待求点', '点', '编号', '序号', 'ID'],
351
+        gaussX: ['x', 'X', '高斯x', '高斯X', 'x坐标', 'X坐标', '东', 'E', 'east'],
352
+        gaussY: ['y', 'Y', '高斯y', '高斯Y', 'y坐标', 'Y坐标', '北', 'N', 'north']
353
+      }
354
+      
355
+      this.excelColumns.forEach(col => {
356
+        const lowerCol = col.toLowerCase()
357
+        Object.keys(keywordMap).forEach(field => {
358
+          if (!this.fieldMapping[field]) {
359
+            keywordMap[field].forEach(keyword => {
360
+              if (col.includes(keyword) || lowerCol.includes(keyword.toLowerCase())) {
361
+                this.fieldMapping[field] = col
362
+              }
363
+            })
364
+          }
365
+        })
366
+      })
367
+    },
368
+    
369
+    // 确认字段映射
370
+    confirmMapping() {
371
+      if (!this.validateMapping) {
372
+        this.$message.warning('请完成所有必填字段的映射')
302 373
         return
303 374
       }
304
-      this.previewDialogVisible = true
375
+      
376
+      // 根据映射转换数据
377
+      this.importedData = this.rawImportedData.map(item => ({
378
+        pointNumber: item[this.fieldMapping.pointNumber],
379
+        gaussX: item[this.fieldMapping.gaussX],
380
+        gaussY: item[this.fieldMapping.gaussY]
381
+      }))
382
+      
383
+      this.mappingDialogVisible = false
384
+      this.resetFieldMapping()
385
+    },
386
+    
387
+    // 取消字段映射
388
+    cancelMapping() {
389
+      this.mappingDialogVisible = false
390
+      this.resetFieldMapping()
391
+      this.rawImportedData = []
392
+    },
393
+    
394
+    // 重置字段映射
395
+    resetFieldMapping() {
396
+      this.fieldMapping = {
397
+        pointNumber: '',
398
+        gaussX: '',
399
+        gaussY: ''
400
+      }
401
+    },
402
+    
403
+    // 参数变化时清空计算结果
404
+    onParamsChange() {
405
+      if (this.calculationResults.length > 0) {
406
+        this.calculationResults = []
407
+        this.$message.info('参数已更新,请重新执行计算')
408
+      }
305 409
     },
306 410
     
307 411
     // 下载模板
@@ -318,22 +422,19 @@ export default {
318 422
       }
319 423
       
320 424
       this.calculating = true
321
-      const results = []
322
-      let successCount = 0
323
-      let failCount = 0
324 425
       
325 426
       // 构建请求参数
326 427
       const requestData = this.importedData.map(item => ({
327 428
         pointNumber: item.pointNumber,
328
-        coordinateSystem: item.coordinateSystem,
429
+        coordinateSystem: this.commonParams.coordinateSystem,
329 430
         gaussX: item.gaussX,
330 431
         gaussY: item.gaussY,
331
-        band: item.band,
332
-        bandwidth: item.bandWidth,
333
-        newBand: item.newBand,
334
-        newBandwidth: item.newBandWidth,
335
-        longitudePosition: item.longitudePosition,
336
-        latitudePosition: item.latitudePosition,
432
+        band: this.commonParams.band,
433
+        bandwidth: this.commonParams.bandwidth,
434
+        newBand: this.commonParams.newBand,
435
+        newBandwidth: this.commonParams.newBandwidth,
436
+        longitudePosition: this.commonParams.longitudePosition,
437
+        latitudePosition: this.commonParams.latitudePosition,
337 438
         projectionHeight: this.projectionHeight
338 439
       }))
339 440
       
@@ -350,31 +451,23 @@ export default {
350 451
           gaussY: item.gaussY,
351 452
           band: item.band,
352 453
           bandwidth: item.bandwidth,
454
+          newGaussX: item.newGaussX,
455
+          newGaussY: item.newGaussY,
353 456
           newBand: item.newBand,
354 457
           newBandwidth: item.newBandwidth,
355 458
           longitudePosition: item.longitudePosition,
356 459
           latitudePosition: item.latitudePosition,
357
-          newGaussX: item.newGaussX,
358
-          newGaussY: item.newGaussY,
359
-          projectionHeight: this.projectionHeight
460
+          projectionHeight: item.projectionHeight
360 461
         }))
361 462
         
362
-        // 更新导入数据的计算状态
363
-        this.importedData.forEach(importItem => {
364
-          const found = results.find(r => r.pointNumber === importItem.pointNumber)
365
-          if (found && found.gaussX !== -1) {
366
-            importItem.calculated = true
367
-          }
368
-        })
369
-        
370
-        const successCount = results.filter(r => r.gaussX !== -1).length
463
+        const successCount = results.filter(r => r.newGaussX !== undefined && r.newGaussX !== -1).length
371 464
         const failCount = results.length - successCount
372 465
         
373 466
         this.$message.success(`计算完成,成功 ${successCount} 条${failCount > 0 ? `,失败 ${failCount} 条` : ''}`)
374
-        this.calculating = false
375
-        } else {
376
-          this.$message.error(response.msg)
377
-        }
467
+      } else {
468
+        this.$message.error(response.msg)
469
+      }
470
+      this.calculating = false
378 471
     },
379 472
     
380 473
     
@@ -440,12 +533,46 @@ export default {
440 533
     }
441 534
     return types[system] || 'info'
442 535
   }
536
+  },
537
+  
538
+  computed: {
539
+    // 验证字段映射
540
+    validateMapping() {
541
+      if (!this.fieldMapping) {
542
+        return false
543
+      }
544
+      return this.fieldMapping.pointNumber && 
545
+             this.fieldMapping.gaussX && 
546
+             this.fieldMapping.gaussY
547
+    },
548
+    
549
+    // 显示数据:优先显示计算结果,否则显示导入数据
550
+    displayData() {
551
+      if (this.calculationResults.length > 0) {
552
+        return this.calculationResults.map(item => ({
553
+          pointNumber: item.pointNumber,
554
+          gaussX: item.gaussX,
555
+          gaussY: item.gaussY,
556
+          newGaussX: item.newGaussX,
557
+          newGaussY: item.newGaussY
558
+        }))
559
+      } else if (this.importedData.length > 0) {
560
+        return this.importedData.map(item => ({
561
+          pointNumber: item.pointNumber,
562
+          gaussX: item.gaussX,
563
+          gaussY: item.gaussY,
564
+          newGaussX: undefined,
565
+          newGaussY: undefined
566
+        }))
567
+      }
568
+      return []
569
+    }
443 570
   }
444 571
 }
445 572
 </script>
446 573
 
447 574
 <style scoped>
448
-.gauss-positive-tool {
575
+.gauss-band-change-tool {
449 576
   width: 100%;
450 577
 }
451 578
 
@@ -459,17 +586,37 @@ export default {
459 586
   flex-wrap: wrap;
460 587
 }
461 588
 
462
-/* 投影面高程设置样式 */
463
-.height-setting {
589
+/* 参数设置区域样式 */
590
+.params-area {
464 591
   background: #f5f7fa;
465 592
   border-radius: 8px;
466
-  padding: 12px 20px;
593
+  padding: 16px 20px;
467 594
   margin-bottom: 20px;
468 595
   border: 1px solid #e4e7ed;
596
+}
597
+
598
+.params-row {
469 599
   display: flex;
470
-  align-items: center;
471
-  gap: 20px;
472 600
   flex-wrap: wrap;
601
+  gap: 24px;
602
+  align-items: center;
603
+}
604
+
605
+.params-group {
606
+  display: flex;
607
+  align-items: center;
608
+  gap: 8px;
609
+}
610
+
611
+.param-label {
612
+  font-weight: 500;
613
+  color: #606266;
614
+  min-width: 40px;
615
+}
616
+
617
+.unit {
618
+  color: #909399;
619
+  font-size: 13px;
473 620
 }
474 621
 
475 622
 .result-section {
@@ -576,4 +723,41 @@ export default {
576 723
   padding-top: 12px;
577 724
   border-top: 1px dashed #e4e7ed;
578 725
 }
726
+
727
+/* 字段映射对话框样式 */
728
+.mapping-tips {
729
+  margin-bottom: 20px;
730
+}
731
+
732
+.mapping-section {
733
+  background: #f9fafb;
734
+  border-radius: 8px;
735
+  padding: 16px;
736
+}
737
+
738
+.mapping-row {
739
+  display: flex;
740
+  align-items: center;
741
+  margin-bottom: 12px;
742
+}
743
+
744
+.mapping-row:last-child {
745
+  margin-bottom: 0;
746
+}
747
+
748
+.mapping-label {
749
+  width: 80px;
750
+  font-weight: 500;
751
+  color: #606266;
752
+}
753
+
754
+.mapping-select {
755
+  flex: 1;
756
+  min-width: 200px;
757
+}
758
+
759
+.empty-cell {
760
+  color: #C0C4CC;
761
+  font-style: italic;
762
+}
579 763
 </style>

+ 335
- 169
oa-ui/src/views/calculate/tools/gaussnegative.vue Datei anzeigen

@@ -8,14 +8,6 @@
8 8
           导入Excel
9 9
         </el-button>
10 10
 
11
-        <el-button 
12
-          type="success" 
13
-          @click="handlePreview" 
14
-          size="small" 
15
-          :disabled="!importedData || importedData.length === 0"
16
-          icon="el-icon-view">
17
-          预览数据
18
-        </el-button>
19 11
         <el-button 
20 12
           type="warning" 
21 13
           @click="handleCalculate" 
@@ -37,57 +29,97 @@
37 29
       </div>
38 30
     </div>
39 31
     
40
-    <!-- 投影面高程设置 -->
41
-    <div class="height-setting">
42
-      <div class="setting-label">
43
-        <span>投影面高程</span>
44
-      </div>
45
-      <div class="setting-content">
46
-        <el-input 
47
-          v-model="projectionHeight" 
48
-          size="small" 
49
-          style="width: 120px; margin-right: 10px;">
50
-        </el-input>
51
-        <span class="unit">米 (m)</span>
32
+    <!-- 参数设置区域 -->
33
+    <div class="params-area">
34
+      <div class="params-row">
35
+        <div class="params-group">
36
+          <label class="param-label">投影面高程</label>
37
+          <el-input 
38
+            v-model="projectionHeight" 
39
+            size="small" 
40
+            style="width: 120px;">
41
+          </el-input>
42
+          <span class="unit">米 (m)</span>
43
+        </div>
44
+        
45
+        <div class="params-group">
46
+          <label class="param-label">坐标系</label>
47
+          <el-select v-model="commonParams.coordinateSystem" size="small" style="width: 140px;" @change="onParamsChange">
48
+            <el-option label="WGS84" value="WGS84"></el-option>
49
+            <el-option label="CGCS2000" value="CGCS2000"></el-option>
50
+            <el-option label="Beijing54" value="Beijing54"></el-option>
51
+            <el-option label="Xian80" value="Xian80"></el-option>
52
+          </el-select>
53
+        </div>
54
+        
55
+        <div class="params-group">
56
+          <label class="param-label">经度位置</label>
57
+          <el-select v-model="commonParams.longitudePosition" size="small" style="width: 80px;" @change="onParamsChange">
58
+            <el-option label="E" value="E"></el-option>
59
+            <el-option label="W" value="W"></el-option>
60
+          </el-select>
61
+        </div>
62
+        
63
+        <div class="params-group">
64
+          <label class="param-label">纬度位置</label>
65
+          <el-select v-model="commonParams.latitudePosition" size="small" style="width: 80px;" @change="onParamsChange">
66
+            <el-option label="N" value="N"></el-option>
67
+            <el-option label="S" value="S"></el-option>
68
+          </el-select>
69
+        </div>
70
+        
71
+        <div class="params-group">
72
+          <label class="param-label">带号</label>
73
+          <el-input v-model.number="commonParams.band" size="small" style="width: 80px;" placeholder="带号" @change="onParamsChange"></el-input>
74
+        </div>
52 75
         
76
+        <div class="params-group">
77
+          <label class="param-label">带宽</label>
78
+          <el-select v-model="commonParams.bandwidth" size="small" style="width: 80px;" @change="onParamsChange">
79
+            <el-option label="3" :value="3"></el-option>
80
+            <el-option label="6" :value="6"></el-option>
81
+          </el-select>
82
+        </div>
53 83
       </div>
54 84
     </div>
55 85
 
56
-    <!-- 计算结果展示区 - 只显示计算结果,不包含原始数据 -->
57
-    <div v-if="calculationResults.length > 0" class="result-section">
86
+    <!-- 数据展示区 -->
87
+    <div v-if="displayData.length > 0" class="result-section">
58 88
       <div class="section-header">
59 89
         <span class="section-title">
60 90
           <i class="el-icon-tickets"></i>
61
-          计算结果
91
+          {{ calculationResults.length > 0 ? '计算结果' : '导入数据' }}
62 92
         </span>
63 93
         <span class="section-info">
64
-          共 {{ calculationResults.length }} 条计算结果
94
+          共 {{ displayData.length }} 条数据 | 坐标系:{{ commonParams.coordinateSystem }} | 带号:{{ commonParams.band }} | 带宽:{{ commonParams.bandwidth }}
65 95
         </span>
66
-        <el-button type="text" size="small" @click="handleExport" icon="el-icon-download">
96
+        <el-button v-if="calculationResults.length > 0" type="text" size="small" @click="handleExport" icon="el-icon-download">
67 97
           导出结果
68 98
         </el-button>
69 99
       </div>
70 100
       
71
-      <el-table :data="calculationResults" style="width: 100%" border stripe>
101
+      <el-table :data="displayData" style="width: 100%" border stripe>
72 102
         <el-table-column prop="pointNumber" label="待求点号" width="120" align="center"></el-table-column>
73
-        <el-table-column prop="band" label="带号" width="180" align="center">
103
+        <el-table-column prop="gaussX" label="高斯X坐标(m)" width="180" align="center">
74 104
           <template slot-scope="scope">
75
-            <span class="result-value">{{ formatNumber(scope.row.band,0) }}</span>
105
+            {{ formatNumber(scope.row.gaussX, 4) }}
76 106
           </template>
77 107
         </el-table-column>
78
-         <el-table-column prop="bandwidth" label="带宽" width="180" align="center">
108
+        <el-table-column prop="gaussY" label="高斯Y坐标(m)" width="180" align="center">
79 109
           <template slot-scope="scope">
80
-            <span class="result-value">{{ formatNumber(scope.row.bandwidth,0) }}</span>
110
+            {{ formatNumber(scope.row.gaussY, 4) }}
81 111
           </template>
82 112
         </el-table-column>
83
-        <el-table-column prop="gaussX" label="大地经度(° ′ ″)" width="180" align="center">
113
+        <el-table-column prop="longitude" label="大地经度" width="150" align="center">
84 114
           <template slot-scope="scope">
85
-            <span class="result-value">{{ formatNumber(scope.row.longitude,9) }}</span>
115
+            <span v-if="scope.row.longitude !== undefined" class="result-value">{{ formatNumber(scope.row.longitude, 9) }}</span>
116
+            <span v-else class="empty-cell">-</span>
86 117
           </template>
87 118
         </el-table-column>
88
-        <el-table-column prop="gaussY" label="大地纬度(° ′ ″)" width="180" align="center">
119
+        <el-table-column prop="latitude" label="大地纬度" width="150" align="center">
89 120
           <template slot-scope="scope">
90
-            <span class="result-value">{{ formatNumber(scope.row.latitude,9) }}</span>
121
+            <span v-if="scope.row.latitude !== undefined" class="result-value">{{ formatNumber(scope.row.latitude, 9) }}</span>
122
+            <span v-else class="empty-cell">-</span>
91 123
           </template>
92 124
         </el-table-column>
93 125
       </el-table>
@@ -96,36 +128,12 @@
96 128
     <!-- 空状态 -->
97 129
     <div v-else class="empty-state">
98 130
       <i class="el-icon-document"></i>
99
-      <p>暂无计算结果</p>
131
+      <p>暂无数据</p>
100 132
       <p class="empty-tip">请先导入Excel数据,然后点击"执行计算"</p>
101 133
     </div>
102 134
     
103 135
     <!-- 导入Excel对话框 -->
104 136
     <el-dialog title="导入Excel" :visible.sync="importDialogVisible" width="550px" @close="resetImport">
105
-      <div class="import-tips">
106
-        <el-alert
107
-          title="Excel模板格式说明"
108
-          type="info"
109
-          :closable="false"
110
-          show-icon>
111
-          <div class="template-info">
112
-            <p>请确保Excel文件包含以下列(第一行为表头):</p>
113
-            <ul>
114
-              <li><strong>待求点</strong> - 点号</li>
115
-              <li><strong>坐标系</strong> - WGS84/CGCS2000/Beijing54/Xian80</li>
116
-              <li><strong>高斯坐标X</strong> - (m)</li>
117
-              <li><strong>高斯坐标Y</strong> - (m)</li>
118
-              <li><strong>经度位置</strong> - E/W</li>
119
-              <li><strong>纬度位置</strong> - N/S</li>
120
-            </ul>
121
-          </div>
122
-        </el-alert>
123
-         <div class="template-download-wrapper">
124
-            <el-button type="primary" plain size="small" @click="handleDownloadTemplate" icon="el-icon-download">
125
-              下载Excel模板
126
-            </el-button>
127
-          </div>
128
-      </div>
129 137
       <div style="text-align: center;">
130 138
         <el-upload
131 139
           class="upload-area"
@@ -152,44 +160,45 @@
152 160
       </span>
153 161
     </el-dialog>
154 162
     
155
-    <!-- 预览数据对话框 - 显示导入的原始数据 -->
156
-    <el-dialog title="导入数据预览" :visible.sync="previewDialogVisible" width="80%">
157
-      <div class="preview-info">
158
-        <span>共 {{ importedData.length }} 条数据</span>
159
-        <span style="margin-left: 20px;">状态:{{ importedData.filter(item => item.calculated).length }} 条已计算,{{ importedData.filter(item => !item.calculated).length }} 条未计算</span>
163
+    <!-- 字段映射对话框 -->
164
+    <el-dialog title="字段映射" :visible.sync="fieldMappingVisible" width="500px" @close="cancelMapping">
165
+      <div class="mapping-tips">
166
+        <el-alert
167
+          title="字段映射说明"
168
+          type="info"
169
+          :closable="false"
170
+          show-icon>
171
+          <p>请将Excel中的列映射到对应的坐标字段。</p>
172
+        </el-alert>
160 173
       </div>
161
-      <el-table :data="importedData" border stripe max-height="500">
162
-        <el-table-column prop="pointNumber" label="待求点号" width="120" fixed="left" align="center"></el-table-column>
163
-        <el-table-column prop="coordinateSystem" label="坐标系" width="110" align="center">
164
-          <template slot-scope="scope">
165
-            <el-tag size="small" :type="getCoordinateType(scope.row.coordinateSystem)">
166
-              {{ scope.row.coordinateSystem }}
167
-            </el-tag>
168
-          </template>
169
-        </el-table-column>
170
-        <el-table-column prop="longitude" label="高斯X坐标(m)" width="130" align="center">
171
-          <template slot-scope="scope">
172
-            {{ scope.row.gaussX }}
173
-          </template>
174
-        </el-table-column>
175
-         <el-table-column prop="longitudePosition" label="高斯Y坐标(m)" width="130" align="center">
176
-          <template slot-scope="scope">
177
-            {{ scope.row.gaussY }}
178
-          </template>
179
-        </el-table-column>
180
-        <el-table-column prop="longitudePosition" label="经度位置" width="130" align="center">
181
-          <template slot-scope="scope">
182
-            {{ scope.row.longitudePosition}}
183
-                   </template>
184
-        </el-table-column>
185
-        <el-table-column prop="latitudePosition" label="纬度位置" width="130" align="center">
186
-          <template slot-scope="scope">
187
-            {{ scope.row.latitudePosition}}
188
-                   </template>
189
-        </el-table-column>
190
-      </el-table>
174
+      
175
+      <!-- 字段映射区域 -->
176
+      <div class="mapping-section">
177
+        <div class="mapping-row">
178
+          <label class="mapping-label">点号字段:</label>
179
+          <el-select v-model="fieldMapping.pointNumber" class="mapping-select" placeholder="请选择点号列">
180
+            <el-option v-for="col in excelColumns" :key="col" :label="col" :value="col"></el-option>
181
+          </el-select>
182
+        </div>
183
+        <div class="mapping-row">
184
+          <label class="mapping-label">高斯X字段:</label>
185
+          <el-select v-model="fieldMapping.gaussX" class="mapping-select" placeholder="请选择高斯X列">
186
+            <el-option v-for="col in excelColumns" :key="col" :label="col" :value="col"></el-option>
187
+          </el-select>
188
+        </div>
189
+        <div class="mapping-row">
190
+          <label class="mapping-label">高斯Y字段:</label>
191
+          <el-select v-model="fieldMapping.gaussY" class="mapping-select" placeholder="请选择高斯Y列">
192
+            <el-option v-for="col in excelColumns" :key="col" :label="col" :value="col"></el-option>
193
+          </el-select>
194
+        </div>
195
+      </div>
196
+      
191 197
       <span slot="footer" class="dialog-footer">
192
-        <el-button type="primary" @click="previewDialogVisible = false">关闭</el-button>
198
+        <el-button @click="cancelMapping">取消</el-button>
199
+        <el-button type="primary" @click="confirmMapping" :disabled="!validateMapping">
200
+          确认映射
201
+        </el-button>
193 202
       </span>
194 203
     </el-dialog>
195 204
   </div>
@@ -208,8 +217,27 @@ export default {
208 217
       importLoading: false,
209 218
       importFile: null,
210 219
       calculating: false,
211
-      previewDialogVisible: false,
212
-      projectionHeight: 0
220
+      projectionHeight: 0,
221
+      heightTimer: null,
222
+      
223
+      // 字段映射相关
224
+      fieldMappingVisible: false,
225
+      fieldMapping: {
226
+        pointNumber: '',
227
+        gaussX: '',
228
+        gaussY: ''
229
+      },
230
+      rawImportedData: [],
231
+      excelColumns: [],
232
+      
233
+      // 公共参数
234
+      commonParams: {
235
+        coordinateSystem: 'CGCS2000',
236
+        longitudePosition: 'E',
237
+        latitudePosition: 'N',
238
+        band: 0,
239
+        bandwidth: 3
240
+      }
213 241
     }
214 242
   },
215 243
   watch: {
@@ -252,39 +280,97 @@ export default {
252 280
       const formData = new FormData()
253 281
       formData.append('file', this.importFile)
254 282
       
255
-        const response = await importExcel(formData)
283
+      const response = await importExcel(formData)
284
+      
285
+      if (response.code === 200) {
286
+        const result = response.data
256 287
         
257
-        if (response.code === 200) {
258
-          const result = response.data
259
-          
260
-          // 将后端返回的数据转换为前端格式
261
-          this.importedData = (result.data || []).map(item => ({
262
-            pointNumber: item.pointNumber,
263
-            coordinateSystem: item.coordinateSystem,
264
-            gaussX: item.gaussX,
265
-            gaussY: item.gaussY,
266
-            longitudePosition: item.longitudePosition,
267
-            latitudePosition: item.latitudePosition
268
-          }))
269
-          
270
-          // 清空之前的计算结果
271
-          this.calculationResults = []
272
-          
273
-          this.$message.success(`${response.msg}`)
274
-          this.importDialogVisible = false
275
-          this.importLoading = false
276
-        } else {
277
-          this.$message.error(response.msg)
288
+        // 保存原始数据和列名
289
+        this.rawImportedData = result.data || []
290
+        this.excelColumns = result.columns || []
291
+        
292
+        // 尝试自动匹配字段
293
+        this.autoMatchFields()
294
+        
295
+        // 清空之前的计算结果
296
+        this.calculationResults = []
297
+        
298
+        this.importDialogVisible = false
299
+        this.importLoading = false
300
+        this.fieldMappingVisible = true
301
+      } else {
302
+        this.$message.error(response.msg)
303
+        this.importLoading = false
304
+      }
305
+    },
306
+    
307
+    // 自动匹配字段
308
+    autoMatchFields() {
309
+      const matchKeywords = {
310
+        pointNumber: ['点号', '待求点', '点名', '名称', 'pointNumber', 'ID'],
311
+        gaussX: ['高斯坐标x', 'X坐标', 'X', 'gaussX', 'x'],
312
+        gaussY: ['高斯坐标y', 'Y坐标', 'Y', 'gaussY', 'y']
313
+      }
314
+      
315
+      Object.keys(matchKeywords).forEach(key => {
316
+        for (const keyword of matchKeywords[key]) {
317
+          const matchedCol = this.excelColumns.find(col => 
318
+            col.includes(keyword) || col.toLowerCase().includes(keyword.toLowerCase())
319
+          )
320
+          if (matchedCol) {
321
+            this.fieldMapping[key] = matchedCol
322
+            break
323
+          }
278 324
         }
325
+      })
279 326
     },
280 327
     
281
-    // 预览数据 - 显示导入的原始数据
282
-    handlePreview() {
283
-      if (!this.importedData || this.importedData.length === 0) {
284
-        this.$message.warning('没有可预览的数据')
328
+    // 确认映射
329
+    confirmMapping() {
330
+      if (!this.validateMapping) {
331
+        this.$message.warning('请完成所有字段映射')
285 332
         return
286 333
       }
287
-      this.previewDialogVisible = true
334
+      
335
+      // 根据映射关系转换数据
336
+      this.importedData = this.rawImportedData.map(item => ({
337
+        pointNumber: item[this.fieldMapping.pointNumber],
338
+        gaussX: item[this.fieldMapping.gaussX],
339
+        gaussY: item[this.fieldMapping.gaussY]
340
+      }))
341
+      
342
+      this.fieldMappingVisible = false
343
+      this.$message.success('数据导入成功')
344
+    },
345
+    
346
+    // 取消映射
347
+    cancelMapping() {
348
+      this.fieldMappingVisible = false
349
+      this.resetFieldMapping()
350
+    },
351
+    
352
+    // 重置字段映射
353
+    resetFieldMapping() {
354
+      this.fieldMapping = {
355
+        pointNumber: '',
356
+        gaussX: '',
357
+        gaussY: ''
358
+      }
359
+      this.commonParams = {
360
+        coordinateSystem: 'CGCS2000',
361
+        longitudePosition: 'E',
362
+        latitudePosition: 'N',
363
+        band: 3,
364
+        bandwidth: 3
365
+      }
366
+    },
367
+    
368
+    // 参数变化时清除计算结果,提示用户重新计算
369
+    onParamsChange() {
370
+      if (this.calculationResults.length > 0) {
371
+        this.calculationResults = []
372
+        this.$message.info('参数已变更,请重新执行计算')
373
+      }
288 374
     },
289 375
     
290 376
     // 下载模板
@@ -308,11 +394,13 @@ export default {
308 394
       // 构建请求参数
309 395
       const requestData = this.importedData.map(item => ({
310 396
         pointNumber: item.pointNumber,
311
-        coordinateSystem: item.coordinateSystem,
397
+        coordinateSystem: this.commonParams.coordinateSystem,
312 398
         gaussX: item.gaussX,
313 399
         gaussY: item.gaussY,
314
-        longitudePosition: item.longitudePosition,
315
-        latitudePosition: item.latitudePosition,
400
+        longitudePosition: this.commonParams.longitudePosition,
401
+        latitudePosition: this.commonParams.latitudePosition,
402
+        band: this.commonParams.band,
403
+        bandwidth: this.commonParams.bandwidth,
316 404
         projectionHeight: this.projectionHeight
317 405
       }))
318 406
       
@@ -325,12 +413,12 @@ export default {
325 413
         this.calculationResults = results.map(item => ({
326 414
           pointNumber: item.pointNumber,
327 415
           coordinateSystem: item.coordinateSystem,
328
-          longitude: parseFloat(item.longitude),
416
+          longitude: item.longitude,
329 417
           longitudePosition: item.longitudePosition,
330
-          latitude: parseFloat(item.latitude),
418
+          latitude: item.latitude,
331 419
           latitudePosition: item.latitudePosition,
332
-          band: parseInt(item.band),
333
-          bandwidth: parseInt(item.bandwidth),
420
+          band: item.band,
421
+          bandwidth: item.bandwidth,
334 422
           projectionHeight: this.projectionHeight,
335 423
           gaussX: item.gaussX,
336 424
           gaussY: item.gaussY
@@ -343,17 +431,22 @@ export default {
343 431
             importItem.calculated = true
344 432
           }
345 433
         })
346
-        
347
-        const successCount = results.filter(r => r.gaussX !== -1).length
434
+         const successCount = results.filter(r => r.longitude !== -1).length
348 435
         const failCount = results.length - successCount
349 436
         
350 437
         this.$message.success(`计算完成,成功 ${successCount} 条${failCount > 0 ? `,失败 ${failCount} 条` : ''}`)
351 438
         this.calculating = false
352
-        } else {
353
-          this.$message.error(response.msg)
354
-        }
439
+      } else {
440
+        this.$message.error(response.msg)
441
+        this.calculating = false
442
+      }
443
+    },
444
+
445
+    // 格式化数字显示
446
+    formatNumber(value, decimals = 3) {
447
+      if (value === null || value === undefined) return '--'
448
+      return Number(value).toFixed(decimals)
355 449
     },
356
-    
357 450
     
358 451
     // 导出结果
359 452
     async handleExport() {
@@ -362,8 +455,6 @@ export default {
362 455
         return
363 456
       }
364 457
 
365
-      
366
-
367 458
       const response = await exportExcel(this.calculationResults)
368 459
       
369 460
       const blob = new Blob([response], { 
@@ -376,10 +467,10 @@ export default {
376 467
       link.download = `高斯反算结果_${new Date().getTime()}.xlsx`
377 468
       link.click()
378 469
       window.URL.revokeObjectURL(url)
379
-  },
470
+    },
380 471
     
381
-  // 清空全部
382
-  clearAll() {
472
+    // 清空全部
473
+    clearAll() {
383 474
     if ((!this.importedData || this.importedData.length === 0) && this.calculationResults.length === 0) {
384 475
       return
385 476
     }
@@ -395,36 +486,54 @@ export default {
395 486
     }).catch(() => {})
396 487
   },
397 488
   
398
-  // 重置导入状态
399
-  resetImport() {
400
-    this.importFile = null
401
-    if (this.$refs.upload) {
402
-      this.$refs.upload.clearFiles()
489
+    // 重置导入状态
490
+    resetImport() {
491
+      this.importFile = null
492
+      if (this.$refs.upload) {
493
+        this.$refs.upload.clearFiles()
494
+      }
403 495
     }
404 496
   },
405 497
   
406
-  // 格式化数字显示
407
-  formatNumber(value, decimals = 3) {
408
-    if (value === null || value === undefined) return '--'
409
-    return Number(value).toFixed(decimals)
410
-  },
411
-  
412
-  // 获取坐标系标签类型
413
-  getCoordinateType(system) {
414
-    const types = {
415
-      'WGS84': 'primary',
416
-      'CGCS2000': 'success',
417
-      'Beijing54': 'warning',
418
-      'Xian80': 'info'
498
+  computed: {
499
+    // 验证字段映射(只验证坐标字段)
500
+    validateMapping() {
501
+      if (!this.fieldMapping) {
502
+        return false
503
+      }
504
+      return this.fieldMapping.pointNumber && 
505
+             this.fieldMapping.gaussX && 
506
+             this.fieldMapping.gaussY
507
+    },
508
+    
509
+    // 显示数据:优先显示计算结果,否则显示导入数据
510
+    displayData() {
511
+      if (this.calculationResults.length > 0) {
512
+        return this.calculationResults.map(item => ({
513
+          pointNumber: item.pointNumber,
514
+          gaussX: item.gaussX,
515
+          gaussY: item.gaussY,
516
+          longitude: item.longitude,
517
+          latitude: item.latitude
518
+        }))
519
+      } else if (this.importedData.length > 0) {
520
+        return this.importedData.map(item => ({
521
+          pointNumber: item.pointNumber,
522
+          gaussX: item.gaussX,
523
+          gaussY: item.gaussY,
524
+          longitude: undefined,
525
+          latitude: undefined
526
+        }))
527
+      }
528
+      return []
419 529
     }
420
-    return types[system] || 'info'
421
-  }
422
-  }
530
+  },
531
+    
423 532
 }
424 533
 </script>
425 534
 
426 535
 <style scoped>
427
-.gauss-positive-tool {
536
+.gauss-negative-tool {
428 537
   width: 100%;
429 538
 }
430 539
 
@@ -438,17 +547,37 @@ export default {
438 547
   flex-wrap: wrap;
439 548
 }
440 549
 
441
-/* 投影面高程设置样式 */
442
-.height-setting {
550
+/* 参数设置区域样式 */
551
+.params-area {
443 552
   background: #f5f7fa;
444 553
   border-radius: 8px;
445
-  padding: 12px 20px;
554
+  padding: 16px 20px;
446 555
   margin-bottom: 20px;
447 556
   border: 1px solid #e4e7ed;
557
+}
558
+
559
+.params-row {
448 560
   display: flex;
449
-  align-items: center;
450
-  gap: 20px;
451 561
   flex-wrap: wrap;
562
+  gap: 24px;
563
+  align-items: center;
564
+}
565
+
566
+.params-group {
567
+  display: flex;
568
+  align-items: center;
569
+  gap: 8px;
570
+}
571
+
572
+.param-label {
573
+  font-weight: 500;
574
+  color: #606266;
575
+  min-width: 40px;
576
+}
577
+
578
+.unit {
579
+  color: #909399;
580
+  font-size: 13px;
452 581
 }
453 582
 
454 583
 .result-section {
@@ -555,4 +684,41 @@ export default {
555 684
   padding-top: 12px;
556 685
   border-top: 1px dashed #e4e7ed;
557 686
 }
687
+
688
+/* 字段映射对话框样式 */
689
+.mapping-tips {
690
+  margin-bottom: 20px;
691
+}
692
+
693
+.mapping-section {
694
+  background: #f9fafb;
695
+  border-radius: 8px;
696
+  padding: 16px;
697
+}
698
+
699
+.mapping-row {
700
+  display: flex;
701
+  align-items: center;
702
+  margin-bottom: 12px;
703
+}
704
+
705
+.mapping-row:last-child {
706
+  margin-bottom: 0;
707
+}
708
+
709
+.mapping-label {
710
+  width: 80px;
711
+  font-weight: 500;
712
+  color: #606266;
713
+}
714
+
715
+.mapping-select {
716
+  flex: 1;
717
+  min-width: 200px;
718
+}
719
+
720
+.empty-cell {
721
+  color: #C0C4CC;
722
+  font-style: italic;
723
+}
558 724
 </style>

+ 370
- 109
oa-ui/src/views/calculate/tools/gausspositive.vue Datei anzeigen

@@ -8,14 +8,6 @@
8 8
           导入Excel
9 9
         </el-button>
10 10
 
11
-        <el-button 
12
-          type="success" 
13
-          @click="handlePreview" 
14
-          size="small" 
15
-          :disabled="!importedData || importedData.length === 0"
16
-          icon="el-icon-view">
17
-          预览数据
18
-        </el-button>
19 11
         <el-button 
20 12
           type="warning" 
21 13
           @click="handleCalculate" 
@@ -37,52 +29,103 @@
37 29
       </div>
38 30
     </div>
39 31
     
40
-    <!-- 投影面高程设置 -->
41
-    <div class="height-setting">
42
-      <div class="setting-label">
43
-        <span>投影面高程</span>
44
-      </div>
45
-      <div class="setting-content">
46
-        <el-input 
47
-          v-model="projectionHeight" 
48
-          size="small" 
49
-          style="width: 120px; margin-right: 10px;">
50
-        </el-input>
51
-        <span class="unit">米 (m)</span>
32
+    <!-- 参数设置区域 -->
33
+    <div class="params-area">
34
+      <div class="params-row">
35
+        <div class="params-group">
36
+          <label class="param-label">投影面高程</label>
37
+          <el-input 
38
+            v-model="projectionHeight" 
39
+            size="small" 
40
+            style="width: 120px;">
41
+          </el-input>
42
+          <span class="unit">米 (m)</span>
43
+        </div>
44
+        
45
+        <div class="params-group">
46
+          <label class="param-label">坐标系</label>
47
+          <el-select v-model="commonParams.coordinateSystem" size="small" style="width: 140px;">
48
+            <el-option label="WGS84" value="WGS84"></el-option>
49
+            <el-option label="CGCS2000" value="CGCS2000"></el-option>
50
+            <el-option label="Beijing54" value="Beijing54"></el-option>
51
+            <el-option label="Xian80" value="Xian80"></el-option>
52
+          </el-select>
53
+        </div>
54
+        
55
+        <div class="params-group">
56
+          <label class="param-label">经度位置</label>
57
+          <el-select v-model="commonParams.longitudePosition" size="small" style="width: 80px;">
58
+            <el-option label="E" value="E"></el-option>
59
+            <el-option label="W" value="W"></el-option>
60
+          </el-select>
61
+        </div>
62
+        
63
+        <div class="params-group">
64
+          <label class="param-label">纬度位置</label>
65
+          <el-select v-model="commonParams.latitudePosition" size="small" style="width: 80px;">
66
+            <el-option label="N" value="N"></el-option>
67
+            <el-option label="S" value="S"></el-option>
68
+          </el-select>
69
+        </div>
52 70
         
71
+        <div class="params-group">
72
+          <label class="param-label">带号</label>
73
+          <el-input v-model.number="commonParams.band" size="small" style="width: 80px;" placeholder="带号"></el-input>
74
+        </div>
75
+        
76
+        <div class="params-group">
77
+          <label class="param-label">带宽</label>
78
+          <el-select v-model="commonParams.bandwidth" size="small" style="width: 80px;">
79
+            <el-option label="3" :value="3"></el-option>
80
+            <el-option label="6" :value="6"></el-option>
81
+          </el-select>
82
+        </div>
53 83
       </div>
54 84
     </div>
55 85
 
56
-    <!-- 计算结果展示区 - 只显示计算结果,不包含原始数据 -->
57
-    <div v-if="calculationResults.length > 0" class="result-section">
86
+    <!-- 数据展示区 -->
87
+    <div v-if="displayData.length > 0" class="result-section">
58 88
       <div class="section-header">
59 89
         <span class="section-title">
60 90
           <i class="el-icon-tickets"></i>
61
-          计算结果
91
+          {{ calculationResults.length > 0 ? '计算结果' : '导入数据' }}
62 92
         </span>
63 93
         <span class="section-info">
64
-          共 {{ calculationResults.length }} 条计算结果
94
+          共 {{ displayData.length }} 条数据 | 坐标系:{{ commonParams.coordinateSystem }} | 带号:{{ commonParams.band }} | 带宽:{{ commonParams.bandwidth }}
65 95
         </span>
66
-        <el-button type="text" size="small" @click="handleExport" icon="el-icon-download">
96
+        <el-button v-if="calculationResults.length > 0" type="text" size="small" @click="handleExport" icon="el-icon-download">
67 97
           导出结果
68 98
         </el-button>
69 99
       </div>
70 100
       
71
-      <el-table :data="calculationResults" style="width: 100%" border stripe>
101
+      <el-table :data="displayData" style="width: 100%" border stripe>
72 102
         <el-table-column prop="pointNumber" label="待求点号" width="120" align="center"></el-table-column>
103
+        <el-table-column prop="longitude" label="大地经度(° ′ ″)" width="150" align="center">
104
+          <template slot-scope="scope">
105
+            {{ formatNumber(scope.row.longitude, 9) }}
106
+          </template>
107
+        </el-table-column>
108
+        <el-table-column prop="latitude" label="大地纬度(° ′ ″)" width="150" align="center">
109
+          <template slot-scope="scope">
110
+            {{ formatNumber(scope.row.latitude, 9) }}
111
+          </template>
112
+        </el-table-column>
73 113
         <el-table-column prop="gaussX" label="高斯X坐标(m)" width="180" align="center">
74 114
           <template slot-scope="scope">
75
-            <span class="result-value">{{ formatNumber(scope.row.gaussX,4) }}</span>
115
+            <span v-if="scope.row.gaussX !== undefined" class="result-value">{{ formatNumber(scope.row.gaussX,4) }}</span>
116
+            <span v-else class="empty-cell">-</span>
76 117
           </template>
77 118
         </el-table-column>
78 119
         <el-table-column prop="gaussY" label="高斯Y坐标(m)" width="180" align="center">
79 120
           <template slot-scope="scope">
80
-            <span class="result-value">{{ formatNumber(scope.row.gaussY,4) }}</span>
121
+            <span v-if="scope.row.gaussY !== undefined" class="result-value">{{ formatNumber(scope.row.gaussY,4) }}</span>
122
+            <span v-else class="empty-cell">-</span>
81 123
           </template>
82 124
         </el-table-column>
83
-        <el-table-column prop="meridianConvergence" label="子午线收敛角γ(°)" width="200" align="center">
125
+        <el-table-column prop="meridianConvergence" label="子午线收敛角γ(° ′ ″)" width="160" align="center">
84 126
           <template slot-scope="scope">
85
-            <span class="result-value">{{ formatNumber(scope.row.meridianConvergence, 7) }}</span>
127
+            <span v-if="scope.row.meridianConvergence !== undefined" class="result-value">{{ formatNumber(scope.row.meridianConvergence, 7) }}</span>
128
+            <span v-else class="empty-cell">-</span>
86 129
           </template>
87 130
         </el-table-column>
88 131
       </el-table>
@@ -91,38 +134,12 @@
91 134
     <!-- 空状态 -->
92 135
     <div v-else class="empty-state">
93 136
       <i class="el-icon-document"></i>
94
-      <p>暂无计算结果</p>
137
+      <p>暂无数据</p>
95 138
       <p class="empty-tip">请先导入Excel数据,然后点击"执行计算"</p>
96 139
     </div>
97 140
     
98 141
     <!-- 导入Excel对话框 -->
99 142
     <el-dialog title="导入Excel" :visible.sync="importDialogVisible" width="550px" @close="resetImport">
100
-      <div class="import-tips">
101
-        <el-alert
102
-          title="Excel模板格式说明"
103
-          type="info"
104
-          :closable="false"
105
-          show-icon>
106
-          <div class="template-info">
107
-            <p>请确保Excel文件包含以下列(第一行为表头):</p>
108
-            <ul>
109
-              <li><strong>待求点</strong> - 点号</li>
110
-              <li><strong>坐标系</strong> - WGS84/CGCS2000/Beijing54/Xian80</li>
111
-              <li><strong>大地经度</strong> - 经度数值(° ′ ″)</li>
112
-              <li><strong>经度位置</strong> - E/W</li>
113
-              <li><strong>大地纬度</strong> - 纬度数值(° ′ ″)</li>
114
-              <li><strong>纬度位置</strong> - N/S</li>
115
-              <li><strong>带号</strong> - 带号数值</li>
116
-              <li><strong>带宽</strong> - 3或6</li>
117
-            </ul>
118
-          </div>
119
-        </el-alert>
120
-         <div class="template-download-wrapper">
121
-            <el-button type="primary" plain size="small" @click="handleDownloadTemplate" icon="el-icon-download">
122
-              下载Excel模板
123
-            </el-button>
124
-          </div>
125
-      </div>
126 143
       <div style="text-align: center;">
127 144
         <el-upload
128 145
           class="upload-area"
@@ -149,6 +166,48 @@
149 166
       </span>
150 167
     </el-dialog>
151 168
     
169
+    <!-- 字段映射对话框 -->
170
+    <el-dialog title="字段映射" :visible.sync="fieldMappingVisible" width="500px" @close="cancelMapping">
171
+      <div class="mapping-tips">
172
+        <el-alert
173
+          title="字段映射说明"
174
+          type="info"
175
+          :closable="false"
176
+          show-icon>
177
+          <p>请将Excel中的列映射到对应的坐标字段。</p>
178
+        </el-alert>
179
+      </div>
180
+      
181
+      <!-- 字段映射区域 -->
182
+      <div class="mapping-section">
183
+        <div class="mapping-row">
184
+          <label class="mapping-label">点号字段:</label>
185
+          <el-select v-model="fieldMapping.pointNumber" class="mapping-select" placeholder="请选择点号列">
186
+            <el-option v-for="col in excelColumns" :key="col" :label="col" :value="col"></el-option>
187
+          </el-select>
188
+        </div>
189
+        <div class="mapping-row">
190
+          <label class="mapping-label">经度字段:</label>
191
+          <el-select v-model="fieldMapping.longitude" class="mapping-select" placeholder="请选择经度列">
192
+            <el-option v-for="col in excelColumns" :key="col" :label="col" :value="col"></el-option>
193
+          </el-select>
194
+        </div>
195
+        <div class="mapping-row">
196
+          <label class="mapping-label">纬度字段:</label>
197
+          <el-select v-model="fieldMapping.latitude" class="mapping-select" placeholder="请选择纬度列">
198
+            <el-option v-for="col in excelColumns" :key="col" :label="col" :value="col"></el-option>
199
+          </el-select>
200
+        </div>
201
+      </div>
202
+      
203
+      <span slot="footer" class="dialog-footer">
204
+        <el-button @click="cancelMapping">取消</el-button>
205
+        <el-button type="primary" @click="confirmMapping" :disabled="!validateMapping">
206
+          确认映射
207
+        </el-button>
208
+      </span>
209
+    </el-dialog>
210
+    
152 211
     <!-- 预览数据对话框 - 显示导入的原始数据 -->
153 212
     <el-dialog title="导入数据预览" :visible.sync="previewDialogVisible" width="80%">
154 213
       <div class="preview-info">
@@ -211,9 +270,27 @@ export default {
211 270
       importLoading: false,
212 271
       importFile: null,
213 272
       calculating: false,
214
-      previewDialogVisible: false,
215 273
       projectionHeight: 0,
216 274
       heightTimer: null,
275
+      //  预览对话框显示控制
276
+      previewDialogVisible: false, 
277
+      
278
+      // 字段映射相关
279
+      fieldMappingVisible: false,
280
+      rawImportedData: [],     // 原始导入数据(未映射)
281
+      excelColumns: [],        // Excel列名
282
+      fieldMapping: {
283
+        pointNumber: '',
284
+        longitude: '',
285
+        latitude: ''
286
+      },
287
+      commonParams: {
288
+        coordinateSystem: 'CGCS2000',
289
+        longitudePosition: 'E',
290
+        latitudePosition: 'N',
291
+        band: 0,
292
+        bandwidth: 3
293
+      }
217 294
     }
218 295
   },
219 296
   watch: {
@@ -233,6 +310,75 @@ export default {
233 310
       }
234 311
     }
235 312
   },
313
+  computed: {
314
+    // 验证字段映射(只验证坐标字段)
315
+    validateMapping() {
316
+      if (!this.fieldMapping) {
317
+        return false
318
+      }
319
+      return this.fieldMapping.pointNumber && 
320
+             this.fieldMapping.longitude && 
321
+             this.fieldMapping.latitude
322
+    },
323
+    
324
+    // 显示数据:优先显示计算结果,否则显示导入数据
325
+    displayData() {
326
+      if (this.calculationResults.length > 0) {
327
+        // 只返回需要显示的字段
328
+        return this.calculationResults.map(item => ({
329
+          pointNumber: item.pointNumber,
330
+          longitude: item.longitude,
331
+          latitude: item.latitude,
332
+          gaussX: item.gaussX,
333
+          gaussY: item.gaussY,
334
+          meridianConvergence: item.meridianConvergence
335
+        }))
336
+      } else if (this.importedData.length > 0) {
337
+        // 将导入数据转换为统一格式,只包含映射字段
338
+        return this.importedData.map(item => ({
339
+          pointNumber: item.pointNumber,
340
+          longitude: item.longitude,
341
+          latitude: item.latitude,
342
+          gaussX: undefined,
343
+          gaussY: undefined,
344
+          meridianConvergence: undefined
345
+        }))
346
+      }
347
+      return []
348
+    }
349
+  },
350
+  computed: {
351
+    // 验证字段映射(只验证坐标字段)
352
+    validateMapping() {
353
+      if (!this.fieldMapping) {
354
+        return false
355
+      }
356
+      return this.fieldMapping.pointNumber && 
357
+             this.fieldMapping.longitude && 
358
+             this.fieldMapping.latitude
359
+    },
360
+    
361
+    // 显示数据:优先显示计算结果,否则显示导入数据
362
+    displayData() {
363
+      if (this.calculationResults.length > 0) {
364
+        return this.calculationResults
365
+      } else if (this.importedData.length > 0) {
366
+        // 将导入数据转换为统一格式,保留公共参数信息
367
+        return this.importedData.map(item => ({
368
+          pointNumber: item.pointNumber,
369
+          coordinateSystem: this.commonParams.coordinateSystem,
370
+          band: this.commonParams.band,
371
+          bandwidth: this.commonParams.bandwidth,
372
+          longitude: item.longitude,
373
+          latitude: item.latitude,
374
+          gaussX: undefined,
375
+          gaussY: undefined,
376
+          meridianConvergence: undefined
377
+        }))
378
+      }
379
+      return []
380
+    }
381
+  },
236 382
   methods: {
237 383
     // 打开导入对话框 
238 384
     handleImport() {
@@ -256,41 +402,104 @@ export default {
256 402
       const formData = new FormData()
257 403
       formData.append('file', this.importFile)
258 404
       
259
-        const response = await importExcel(formData)
405
+      const response = await importExcel(formData)
406
+      
407
+      if (response.code === 200) {
408
+        const result = response.data
260 409
         
261
-        if (response.code === 200) {
262
-          const result = response.data
263
-          
264
-          // 将后端返回的数据转换为前端格式
265
-          this.importedData = (result.data || []).map(item => ({
266
-            pointNumber: item.pointNumber,
267
-            coordinateSystem: item.coordinateSystem,
268
-            longitude: item.longitude,
269
-            longitudePosition: item.longitudePosition,
270
-            latitude: item.latitude,
271
-            latitudePosition: item.latitudePosition,
272
-            band: item.band,
273
-            bandwidth: item.bandwidth
274
-          }))
275
-          
276
-          // 清空之前的计算结果
277
-          this.calculationResults = []
278
-          
279
-          this.$message.success(`${response.msg}`)
280
-          this.importDialogVisible = false
281
-          this.importLoading = false
282
-        } else {
283
-          this.$message.error(response.msg)
410
+        // 保存原始数据和列名
411
+        this.rawImportedData = result.data || []
412
+        this.excelColumns = result.columns || []
413
+        
414
+        // 尝试自动匹配字段
415
+        this.autoMatchFields()
416
+        
417
+        // 清空之前的计算结果
418
+        this.calculationResults = []
419
+        
420
+        this.importDialogVisible = false
421
+        this.importLoading = false
422
+        this.fieldMappingVisible = true
423
+      } else {
424
+        this.$message.error(response.msg)
425
+        this.importLoading = false
426
+      }
427
+    },
428
+    
429
+    // 自动匹配字段
430
+    autoMatchFields() {
431
+      const matchKeywords = {
432
+        pointNumber: ['点号', '待求点', '点名', '名称', 'pointNumber', 'ID'],
433
+        longitude: ['经度', '大地经度', 'lon', 'longitude', 'X'],
434
+        latitude: ['纬度', '大地纬度', 'lat', 'latitude', 'Y']
435
+      }
436
+      
437
+      Object.keys(matchKeywords).forEach(key => {
438
+        for (const keyword of matchKeywords[key]) {
439
+          const matchedCol = this.excelColumns.find(col => 
440
+            col.includes(keyword) || col.toLowerCase().includes(keyword.toLowerCase())
441
+          )
442
+          if (matchedCol) {
443
+            this.fieldMapping[key] = matchedCol
444
+            break
445
+          }
284 446
         }
447
+      })
285 448
     },
286 449
     
287
-    // 预览数据 - 显示导入的原始数据
288
-    handlePreview() {
289
-      if (!this.importedData || this.importedData.length === 0) {
290
-        this.$message.warning('没有可预览的数据')
450
+    // 确认映射
451
+    confirmMapping() {
452
+      if (!this.validateMapping) {
453
+        this.$message.warning('请完成所有字段映射和参数设置')
291 454
         return
292 455
       }
293
-      this.previewDialogVisible = true
456
+      
457
+      // 根据映射关系转换数据
458
+      this.importedData = this.rawImportedData.map(item => ({
459
+        pointNumber: item[this.fieldMapping.pointNumber],
460
+        longitude: item[this.fieldMapping.longitude],
461
+        latitude: item[this.fieldMapping.latitude],
462
+        coordinateSystem: this.commonParams.coordinateSystem,
463
+        longitudePosition: this.commonParams.longitudePosition,
464
+        latitudePosition: this.commonParams.latitudePosition,
465
+        band: this.commonParams.band,
466
+        bandwidth: this.commonParams.bandwidth
467
+      }))
468
+      
469
+      this.fieldMappingVisible = false
470
+      this.$message.success('数据导入成功')
471
+    },
472
+    
473
+    // 取消映射
474
+    cancelMapping() {
475
+      this.fieldMappingVisible = false
476
+      this.rawImportedData = []
477
+      this.excelColumns = []
478
+      this.resetFieldMapping()
479
+    },
480
+    
481
+    // 重置字段映射
482
+    resetFieldMapping() {
483
+      this.fieldMapping = {
484
+        pointNumber: '',
485
+        longitude: '',
486
+        latitude: ''
487
+      }
488
+      this.commonParams = {
489
+        coordinateSystem: 'CGCS2000',
490
+        longitudePosition: 'E',
491
+        latitudePosition: 'N',
492
+        band: 3,
493
+        bandwidth: 3
494
+      }
495
+    },
496
+    
497
+    // 参数变化时清除计算结果,提示用户重新计算
498
+    onParamsChange() {
499
+      if (this.calculationResults.length > 0) {
500
+        this.calculationResults = []
501
+        this.$message.info('参数已变更,请重新执行计算')
502
+      }
294 503
     },
295 504
     
296 505
     // 下载模板
@@ -306,22 +515,21 @@ export default {
306 515
         return
307 516
       }
308 517
       
309
-      this.calculating = true
310
-      const results = []
311
-      let successCount = 0
312
-      let failCount = 0
518
+      // 清除之前的计算结果,确保显示最新数据
519
+      this.calculationResults = []
313 520
       
314
-      // 构建请求参数
521
+      this.calculating = true
522
+      // 构建请求参数(使用主界面设置的公共参数)
315 523
       const requestData = this.importedData.map(item => ({
316 524
         pointNumber: item.pointNumber,
317
-        coordinateSystem: item.coordinateSystem,
318
-        longitude: parseFloat(item.longitude),
319
-        longitudePosition: item.longitudePosition,
320
-        latitude: parseFloat(item.latitude),
321
-        latitudePosition: item.latitudePosition,
322
-        band: parseInt(item.band),
323
-        bandwidth: parseInt(item.bandwidth),
324
-        projectionHeight: this.projectionHeight
525
+        coordinateSystem: this.commonParams.coordinateSystem,
526
+        longitude: item.longitude,
527
+        longitudePosition: this.commonParams.longitudePosition,
528
+        latitude: item.latitude,
529
+        latitudePosition: this.commonParams.latitudePosition,
530
+        band: this.commonParams.band,
531
+        bandwidth: this.commonParams.bandwidth,
532
+        projectionHeight: this.commonParams.projectionHeight
325 533
       }))
326 534
       
327 535
       const response = await calculate(requestData)
@@ -333,12 +541,12 @@ export default {
333 541
         this.calculationResults = results.map(item => ({
334 542
           pointNumber: item.pointNumber,
335 543
           coordinateSystem: item.coordinateSystem,
336
-          longitude: parseFloat(item.longitude),
544
+          longitude: item.longitude,
337 545
           longitudePosition: item.longitudePosition,
338
-          latitude: parseFloat(item.latitude),
546
+          latitude: item.latitude,
339 547
           latitudePosition: item.latitudePosition,
340
-          band: parseInt(item.band),
341
-          bandwidth: parseInt(item.bandwidth),
548
+          band: item.band,
549
+          bandwidth: item.bandwidth,
342 550
           projectionHeight: this.projectionHeight,
343 551
           gaussX: item.gaussX,
344 552
           gaussY: item.gaussY,
@@ -360,6 +568,7 @@ export default {
360 568
         this.calculating = false
361 569
         } else {
362 570
           this.$message.error(response.msg)
571
+          this.calculating = false
363 572
         }
364 573
     },
365 574
     
@@ -445,17 +654,37 @@ export default {
445 654
   flex-wrap: wrap;
446 655
 }
447 656
 
448
-/* 投影面高程设置样式 */
449
-.height-setting {
657
+/* 参数设置区域样式 */
658
+.params-area {
450 659
   background: #f5f7fa;
451 660
   border-radius: 8px;
452
-  padding: 12px 20px;
661
+  padding: 16px 20px;
453 662
   margin-bottom: 20px;
454 663
   border: 1px solid #e4e7ed;
664
+}
665
+
666
+.params-row {
455 667
   display: flex;
456
-  align-items: center;
457
-  gap: 20px;
458 668
   flex-wrap: wrap;
669
+  gap: 24px;
670
+  align-items: center;
671
+}
672
+
673
+.params-group {
674
+  display: flex;
675
+  align-items: center;
676
+  gap: 8px;
677
+}
678
+
679
+.param-label {
680
+  font-weight: 500;
681
+  color: #606266;
682
+  min-width: 40px;
683
+}
684
+
685
+.unit {
686
+  color: #909399;
687
+  font-size: 13px;
459 688
 }
460 689
 
461 690
 .result-section {
@@ -562,4 +791,36 @@ export default {
562 791
   padding-top: 12px;
563 792
   border-top: 1px dashed #e4e7ed;
564 793
 }
794
+
795
+/* 字段映射对话框样式 */
796
+.mapping-tips {
797
+  margin-bottom: 20px;
798
+}
799
+
800
+.mapping-section {
801
+  background: #f9fafb;
802
+  border-radius: 8px;
803
+  padding: 16px;
804
+}
805
+
806
+.mapping-row {
807
+  display: flex;
808
+  align-items: center;
809
+  margin-bottom: 12px;
810
+}
811
+
812
+.mapping-row:last-child {
813
+  margin-bottom: 0;
814
+}
815
+
816
+.mapping-label {
817
+  width: 80px;
818
+  font-weight: 500;
819
+  color: #606266;
820
+}
821
+
822
+.mapping-select {
823
+  flex: 1;
824
+  min-width: 200px;
825
+}
565 826
 </style>

+ 382
- 140
oa-ui/src/views/calculate/tools/geodetictospatial.vue Datei anzeigen

@@ -8,14 +8,6 @@
8 8
           导入 Excel
9 9
         </el-button>
10 10
 
11
-        <el-button 
12
-          type="success" 
13
-          @click="handlePreview" 
14
-          size="small" 
15
-          :disabled="!importedData || importedData.length === 0"
16
-          icon="el-icon-view">
17
-          预览数据
18
-        </el-button>
19 11
         <el-button 
20 12
           type="warning" 
21 13
           @click="handleCalculate" 
@@ -36,37 +28,101 @@
36 28
         </el-button>
37 29
       </div>
38 30
     </div>
31
+    
32
+    <!-- 参数设置区域 -->
33
+    <div class="params-area">
34
+      <div class="params-row">
35
+        <div class="params-group">
36
+          <label class="param-label">投影面高程</label>
37
+          <el-input 
38
+            v-model="commonParams.projectionHeight" 
39
+            size="small" 
40
+            style="width: 120px;"
41
+            @change="onParamsChange">
42
+          </el-input>
43
+          <span class="unit">米 (m)</span>
44
+        </div>
39 45
 
40
-    <!-- 计算结果展示区 -->
41
-    <div v-if="calculationResults.length > 0" class="result-section">
46
+        <div class="params-group">
47
+          <label class="param-label">坐标系</label>
48
+          <el-select v-model="commonParams.coordinateSystem" size="small" style="width: 140px;" @change="onParamsChange">
49
+            <el-option label="WGS84" value="WGS84"></el-option>
50
+            <el-option label="CGCS2000" value="CGCS2000"></el-option>
51
+            <el-option label="Beijing54" value="Beijing54"></el-option>
52
+            <el-option label="Xian80" value="Xian80"></el-option>
53
+          </el-select>
54
+        </div>
55
+        
56
+        
57
+        <div class="params-group">
58
+          <label class="param-label">经度位置</label>
59
+          <el-select v-model="commonParams.longitudePosition" size="small" style="width: 80px;" @change="onParamsChange">
60
+            <el-option label="E" value="E"></el-option>
61
+            <el-option label="W" value="W"></el-option>
62
+          </el-select>
63
+        </div>
64
+        
65
+        <div class="params-group">
66
+          <label class="param-label">纬度位置</label>
67
+          <el-select v-model="commonParams.latitudePosition" size="small" style="width: 80px;" @change="onParamsChange">
68
+            <el-option label="N" value="N"></el-option>
69
+            <el-option label="S" value="S"></el-option>
70
+          </el-select>
71
+        </div>
72
+      </div>
73
+    </div>
74
+
75
+    <!-- 数据展示区域 - 合并显示导入数据和计算结果 -->
76
+    <div v-if="displayData.length > 0" class="result-section">
42 77
       <div class="section-header">
43 78
         <span class="section-title">
44 79
           <i class="el-icon-tickets"></i>
45
-          计算结果
80
+          {{ calculationResults.length > 0 ? '计算结果' : '导入数据' }}
46 81
         </span>
47 82
         <span class="section-info">
48
-          共 {{ calculationResults.length }} 条计算结果
83
+          共 {{ displayData.length }} 条数据
84
+          <span v-if="calculationResults.length > 0" style="margin-left: 10px;">
85
+            | 坐标系:{{ commonParams.coordinateSystem }} | 投影面高程:{{ commonParams.projectionHeight }}m
86
+          </span>
49 87
         </span>
50
-        <el-button type="text" size="small" @click="handleExport" icon="el-icon-download">
88
+        <el-button v-if="calculationResults.length > 0" type="text" size="small" @click="handleExport" icon="el-icon-download">
51 89
           导出结果
52 90
         </el-button>
53 91
       </div>
54 92
       
55
-      <el-table :data="calculationResults" style="width: 100%" border stripe>
93
+      <el-table :data="displayData" style="width: 100%" border stripe>
56 94
         <el-table-column prop="pointNumber" label="待求点号" width="120" align="center"></el-table-column>
57
-        <el-table-column prop="spatialX" label="空间直角坐标 X(m)" width="200" align="center">
95
+        <el-table-column prop="longitude" label="大地经度(° ′ ″)" width="150" align="center">
58 96
           <template slot-scope="scope">
59
-            <span class="result-value">{{ formatNumber(scope.row.spatialX, 4) }}</span>
97
+            <span>{{ formatNumber(scope.row.longitude, 10) }}</span>
60 98
           </template>
61 99
         </el-table-column>
62
-        <el-table-column prop="spatialY" label="空间直角坐标 Y(m)" width="200" align="center">
100
+        <el-table-column prop="latitude" label="大地纬度(° ′ ″)" width="150" align="center">
63 101
           <template slot-scope="scope">
64
-            <span class="result-value">{{ formatNumber(scope.row.spatialY, 4) }}</span>
102
+            <span>{{ formatNumber(scope.row.latitude, 10) }}</span>
65 103
           </template>
66 104
         </el-table-column>
67
-        <el-table-column prop="spatialZ" label="空间直角坐标 Z(m)" width="200" align="center">
105
+        <el-table-column prop="height" label="大地高(m)" width="120" align="center">
68 106
           <template slot-scope="scope">
69
-            <span class="result-value">{{ formatNumber(scope.row.spatialZ, 4) }}</span>
107
+            <span>{{ formatNumber(scope.row.height, 4) }}</span>
108
+          </template>
109
+        </el-table-column>
110
+        <el-table-column prop="spatialX" label="空间直角坐标 X(m)" width="150" align="center">
111
+          <template slot-scope="scope">
112
+            <span v-if="scope.row.spatialX" class="result-value">{{ formatNumber(scope.row.spatialX, 4) }}</span>
113
+            <span v-else class="empty-cell">-</span>
114
+          </template>
115
+        </el-table-column>
116
+        <el-table-column prop="spatialY" label="空间直角坐标 Y(m)" width="150" align="center">
117
+          <template slot-scope="scope">
118
+            <span v-if="scope.row.spatialY" class="result-value">{{ formatNumber(scope.row.spatialY, 4) }}</span>
119
+            <span v-else class="empty-cell">-</span>
120
+          </template>
121
+        </el-table-column>
122
+        <el-table-column prop="spatialZ" label="空间直角坐标 Z(m)" width="150" align="center">
123
+          <template slot-scope="scope">
124
+            <span v-if="scope.row.spatialZ" class="result-value">{{ formatNumber(scope.row.spatialZ, 4) }}</span>
125
+            <span v-else class="empty-cell">-</span>
70 126
           </template>
71 127
         </el-table-column>
72 128
       </el-table>
@@ -75,37 +131,12 @@
75 131
     <!-- 空状态 -->
76 132
     <div v-else class="empty-state">
77 133
       <i class="el-icon-document"></i>
78
-      <p>暂无计算结果</p>
79
-      <p class="empty-tip">请先导入 Excel 数据,然后点击"执行计算"</p>
134
+      <p>暂无数据</p>
135
+      <p class="empty-tip">请先导入 Excel 数据</p>
80 136
     </div>
81 137
     
82 138
     <!-- 导入 Excel 对话框 -->
83 139
     <el-dialog title="导入 Excel" :visible.sync="importDialogVisible" width="550px" @close="resetImport">
84
-      <div class="import-tips">
85
-        <el-alert
86
-          title="Excel 模板格式说明"
87
-          type="info"
88
-          :closable="false"
89
-          show-icon>
90
-          <div class="template-info">
91
-            <p>请确保 Excel 文件包含以下列(第一行为表头):</p>
92
-            <ul>
93
-              <li><strong>待求点</strong> - 点号</li>
94
-              <li><strong>坐标系</strong> - WGS84/CGCS2000/Beijing54/Xian80</li>
95
-              <li><strong>大地经度</strong> - 经度数值 (°)</li>
96
-              <li><strong>经度位置</strong> - E/W</li>
97
-              <li><strong>大地纬度</strong> - 纬度数值 (°)</li>
98
-              <li><strong>纬度位置</strong> - N/S</li>
99
-              <li><strong>大地高</strong> - 高程数值 (m)</li>
100
-            </ul>
101
-          </div>
102
-        </el-alert>
103
-         <div class="template-download-wrapper">
104
-            <el-button type="primary" plain size="small" @click="handleDownloadTemplate" icon="el-icon-download">
105
-              下载 Excel 模板
106
-            </el-button>
107
-          </div>
108
-      </div>
109 140
       <div style="text-align: center;">
110 141
         <el-upload
111 142
           class="upload-area"
@@ -132,49 +163,54 @@
132 163
       </span>
133 164
     </el-dialog>
134 165
     
135
-    <!-- 预览数据对话框 -->
136
-    <el-dialog title="导入数据预览" :visible.sync="previewDialogVisible" width="80%">
137
-      <div class="preview-info">
138
-        <span>共 {{ importedData.length }} 条数据</span>
139
-        <span style="margin-left: 20px;">状态:{{ importedData.filter(item => item.calculated).length }} 条已计算,{{ importedData.filter(item => !item.calculated).length }} 条未计算</span>
166
+    <!-- 字段映射对话框 -->
167
+    <el-dialog title="字段映射" :visible.sync="mappingDialogVisible" width="500px">
168
+      <div class="mapping-tips">
169
+        <el-alert
170
+          title="字段映射说明"
171
+          type="info"
172
+          :closable="false"
173
+          show-icon>
174
+          请将Excel中的列映射到对应的坐标字段。
175
+        </el-alert>
140 176
       </div>
141
-      <el-table :data="importedData" border stripe max-height="500">
142
-        <el-table-column prop="pointNumber" label="待求点号" width="120" fixed="left" align="center"></el-table-column>
143
-        <el-table-column prop="coordinateSystem" label="坐标系" width="110" align="center">
144
-          <template slot-scope="scope">
145
-            <el-tag size="small" :type="getCoordinateType(scope.row.coordinateSystem)">
146
-              {{ scope.row.coordinateSystem }}
147
-            </el-tag>
148
-          </template>
149
-        </el-table-column>
150
-        <el-table-column prop="longitude" label="大地经度" width="130" align="center">
151
-          <template slot-scope="scope">
152
-            {{ scope.row.longitude }}
153
-          </template>
154
-        </el-table-column>
155
-         <el-table-column prop="longitudePosition" label="经度位置" width="100" align="center">
156
-          <template slot-scope="scope">
157
-            {{ scope.row.longitudePosition}}
158
-          </template>
159
-        </el-table-column>
160
-        <el-table-column prop="latitude" label="大地纬度" width="130" align="center">
161
-          <template slot-scope="scope">
162
-            {{ scope.row.latitude }}
163
-          </template>
164
-        </el-table-column>
165
-         <el-table-column prop="latitudePosition" label="纬度位置" width="100" align="center">
166
-          <template slot-scope="scope">
167
-            {{ scope.row.latitudePosition}}
168
-          </template>
169
-        </el-table-column>
170
-        <el-table-column prop="height" label="大地高 (m)" width="100" align="center">
171
-          <template slot-scope="scope">
172
-            {{ formatNumber(scope.row.height, 4) }}
173
-          </template>
174
-        </el-table-column>
175
-      </el-table>
177
+      
178
+      <div class="mapping-section">
179
+        <div class="mapping-row">
180
+          <label class="mapping-label">点号字段</label>
181
+          <el-select v-model="fieldMapping.pointNumber" class="mapping-select" size="small">
182
+            <el-option label="请选择" value=""></el-option>
183
+            <el-option v-for="col in excelColumns" :key="col" :label="col" :value="col"></el-option>
184
+          </el-select>
185
+        </div>
186
+        <div class="mapping-row">
187
+          <label class="mapping-label">大地经度字段</label>
188
+          <el-select v-model="fieldMapping.longitude" class="mapping-select" size="small">
189
+            <el-option label="请选择" value=""></el-option>
190
+            <el-option v-for="col in excelColumns" :key="col" :label="col" :value="col"></el-option>
191
+          </el-select>
192
+        </div>
193
+        <div class="mapping-row">
194
+          <label class="mapping-label">大地纬度字段</label>
195
+          <el-select v-model="fieldMapping.latitude" class="mapping-select" size="small">
196
+            <el-option label="请选择" value=""></el-option>
197
+            <el-option v-for="col in excelColumns" :key="col" :label="col" :value="col"></el-option>
198
+          </el-select>
199
+        </div>
200
+        <div class="mapping-row">
201
+          <label class="mapping-label">大地高字段</label>
202
+          <el-select v-model="fieldMapping.height" class="mapping-select" size="small">
203
+            <el-option label="请选择" value=""></el-option>
204
+            <el-option v-for="col in excelColumns" :key="col" :label="col" :value="col"></el-option>
205
+          </el-select>
206
+        </div>
207
+      </div>
208
+      
176 209
       <span slot="footer" class="dialog-footer">
177
-        <el-button type="primary" @click="previewDialogVisible = false">关闭</el-button>
210
+        <el-button @click="cancelMapping">取消</el-button>
211
+        <el-button type="primary" @click="confirmMapping" :disabled="!validateMapping">
212
+          确认映射
213
+        </el-button>
178 214
       </span>
179 215
     </el-dialog>
180 216
   </div>
@@ -193,7 +229,25 @@ export default {
193 229
       importLoading: false,
194 230
       importFile: null,
195 231
       calculating: false,
196
-      previewDialogVisible: false,
232
+      
233
+      // 公共参数
234
+      commonParams: {
235
+        coordinateSystem: 'WGS84',
236
+        projectionHeight: 0,
237
+        longitudePosition: 'E',
238
+        latitudePosition: 'N'
239
+      },
240
+      
241
+      // 字段映射相关
242
+      mappingDialogVisible: false,
243
+      excelColumns: [],
244
+      fieldMapping: {
245
+        pointNumber: '',
246
+        longitude: '',
247
+        latitude: '',
248
+        height: ''
249
+      },
250
+      rawImportedData: []
197 251
     }
198 252
   },
199 253
   methods: {
@@ -219,40 +273,113 @@ export default {
219 273
       const formData = new FormData()
220 274
       formData.append('file', this.importFile)
221 275
       
222
-      const response = await importExcel(formData)
223
-      
224
-      if (response.code === 200) {
225
-        const result = response.data
276
+      try {
277
+        const response = await importExcel(formData)
226 278
         
227
-        // 将后端返回的数据转换为前端格式
228
-        this.importedData = (result.data || []).map(item => ({
229
-          pointNumber: item.pointNumber,
230
-          coordinateSystem: item.coordinateSystem,
231
-          longitude: item.longitude,
232
-          longitudePosition: item.longitudePosition,
233
-          latitude: item.latitude,
234
-          latitudePosition: item.latitudePosition,
235
-          height: item.height
236
-        }))
237
-        
238
-        // 清空之前的计算结果
239
-        this.calculationResults = []
240
-        
241
-        this.$message.success(`${response.msg}`)
242
-        this.importDialogVisible = false
279
+        if (response.code === 200) {
280
+          const result = response.data
281
+          
282
+          // 获取Excel列名
283
+          this.excelColumns = result.columns || this.extractColumns(result.data)
284
+          
285
+          // 保存原始数据
286
+          this.rawImportedData = result.data || []
287
+          
288
+          // 清空之前的计算结果
289
+          this.calculationResults = []
290
+          
291
+          // 自动匹配字段
292
+          this.autoMatchFields()
293
+          
294
+          // 显示字段映射对话框
295
+          this.mappingDialogVisible = true
296
+          
297
+          this.$message.success(`${response.msg}`)
298
+          this.importDialogVisible = false
299
+          this.importLoading = false
300
+        } else {
301
+          this.$message.error(response.msg)
302
+          this.importLoading = false
303
+        }
304
+      } catch (error) {
305
+        this.$message.error('导入失败')
243 306
         this.importLoading = false
244
-      } else {
245
-        this.$message.error(response.msg)
246 307
       }
247 308
     },
248 309
     
249
-    // 预览数据 - 显示导入的原始数据
250
-    handlePreview() {
251
-      if (!this.importedData || this.importedData.length === 0) {
252
-        this.$message.warning('没有可预览的数据')
310
+    // 提取列名
311
+    extractColumns(data) {
312
+      if (!data || data.length === 0) return []
313
+      return Object.keys(data[0])
314
+    },
315
+    
316
+    // 自动匹配字段
317
+    autoMatchFields() {
318
+      this.resetFieldMapping()
319
+      
320
+      const keywordMap = {
321
+        pointNumber: ['点号', '待求点', '点', '编号', '序号', 'ID'],
322
+        longitude: ['大地经度', '经度', 'longitude', 'L', 'B', '经'],
323
+        latitude: ['大地纬度', '纬度', 'latitude', 'B', '纬'],
324
+        height: ['大地高', '高程', 'height', 'H', '高']
325
+      }
326
+      
327
+      this.excelColumns.forEach(col => {
328
+        const lowerCol = col.toLowerCase()
329
+        Object.keys(keywordMap).forEach(field => {
330
+          if (!this.fieldMapping[field]) {
331
+            keywordMap[field].forEach(keyword => {
332
+              if (col.includes(keyword) || lowerCol.includes(keyword.toLowerCase())) {
333
+                this.fieldMapping[field] = col
334
+              }
335
+            })
336
+          }
337
+        })
338
+      })
339
+    },
340
+    
341
+    // 确认字段映射
342
+    confirmMapping() {
343
+      if (!this.validateMapping) {
344
+        this.$message.warning('请完成所有必填字段的映射')
253 345
         return
254 346
       }
255
-      this.previewDialogVisible = true
347
+      
348
+      // 根据映射转换数据
349
+      this.importedData = this.rawImportedData.map(item => ({
350
+        pointNumber: item[this.fieldMapping.pointNumber],
351
+        longitude: item[this.fieldMapping.longitude],
352
+        latitude: item[this.fieldMapping.latitude],
353
+        height: item[this.fieldMapping.height]
354
+      }))
355
+      
356
+      this.mappingDialogVisible = false
357
+      this.resetFieldMapping()
358
+    },
359
+    
360
+    // 取消字段映射
361
+    cancelMapping() {
362
+      this.mappingDialogVisible = false
363
+      this.resetFieldMapping()
364
+      this.rawImportedData = []
365
+    },
366
+    
367
+    // 重置字段映射
368
+    resetFieldMapping() {
369
+      this.fieldMapping = {
370
+        pointNumber: '',
371
+        longitude: '',
372
+        latitude: '',
373
+        height: ''
374
+      }
375
+    },
376
+    
377
+    // 参数变化时清空计算结果
378
+    onParamsChange() {
379
+      if (this.calculationResults.length > 0) {
380
+        this.calculationResults = []
381
+        this.$message.info('参数已更新,请重新执行计算')
382
+      }
256 383
     },
257 384
     
258 385
     // 下载模板
@@ -272,11 +399,12 @@ export default {
272 399
       // 构建请求参数
273 400
       const requestData = this.importedData.map(item => ({
274 401
         pointNumber: item.pointNumber,
275
-        coordinateSystem: item.coordinateSystem,
402
+        coordinateSystem: this.commonParams.coordinateSystem,
403
+        projectionHeight: parseFloat(this.commonParams.projectionHeight) || 0,
276 404
         longitude: parseFloat(item.longitude),
277
-        longitudePosition: item.longitudePosition,
405
+        longitudePosition: this.commonParams.longitudePosition,
278 406
         latitude: parseFloat(item.latitude),
279
-        latitudePosition: item.latitudePosition,
407
+        latitudePosition: this.commonParams.latitudePosition,
280 408
         height: parseFloat(item.height) || 0
281 409
       }))
282 410
       
@@ -290,31 +418,26 @@ export default {
290 418
           pointNumber: item.pointNumber,
291 419
           coordinateSystem: item.coordinateSystem,
292 420
           longitude: item.longitude,
293
-          longitudePosition: item.longitudePosition,
294 421
           latitude: item.latitude,
295
-          latitudePosition: item.latitudePosition,
296 422
           height: item.height,
423
+          band: item.band,
424
+          longitudePosition: item.longitudePosition,
425
+          latitudePosition: item.latitudePosition,
297 426
           spatialX: item.spatialX,
298 427
           spatialY: item.spatialY,
299
-          spatialZ: item.spatialZ
428
+          spatialZ: item.spatialZ,
429
+          projectionHeight: item.projectionHeight,
300 430
         }))
301 431
         
302
-        // 更新导入数据的计算状态
303
-        this.importedData.forEach(importItem => {
304
-          const found = results.find(r => r.pointNumber === importItem.pointNumber)
305
-          if (found && found.spatialX !== -1) {
306
-            importItem.calculated = true
307
-          }
308
-        })
309
-        
310
-        const successCount = results.filter(r => r.spatialX !== -1).length
432
+        const successCount = results.filter(r => r.spatialX !== undefined && r.spatialX !== -1).length
311 433
         const failCount = results.length - successCount
312 434
         
313 435
         this.$message.success(`计算完成,成功 ${successCount} 条${failCount > 0 ? `,失败 ${failCount} 条` : ''}`)
314
-        this.calculating = false
315 436
       } else {
316 437
         this.$message.error(response.msg)
317 438
       }
439
+      
440
+      this.calculating = false
318 441
     },
319 442
     
320 443
     // 导出结果
@@ -370,17 +493,58 @@ export default {
370 493
     },
371 494
     
372 495
     // 获取坐标系标签类型
373
-    getCoordinateType(system) {
374
-      const types = {
375
-        'WGS84': 'primary',
376
-        'CGCS2000': 'success',
377
-        'Beijing54': 'warning',
378
-        'Xian80': 'info'
379
-      }
380
-      return types[system] || 'info'
496
+  getCoordinateType(system) {
497
+    const types = {
498
+      'WGS84': 'primary',
499
+      'CGCS2000': 'success',
500
+      'Beijing54': 'warning',
501
+      'Xian80': 'info'
381 502
     }
503
+    return types[system] || 'info'
382 504
   }
505
+},
506
+
507
+computed: {
508
+  // 验证字段映射
509
+  validateMapping() {
510
+    if (!this.fieldMapping) {
511
+      return false
512
+    }
513
+    return this.fieldMapping.pointNumber && 
514
+           this.fieldMapping.longitude && 
515
+           this.fieldMapping.latitude && 
516
+           this.fieldMapping.height
517
+  },
518
+  
519
+  // 显示数据:优先显示计算结果,否则显示导入数据
520
+  displayData() {
521
+    if (this.calculationResults.length > 0) {
522
+      return this.calculationResults.map(item => ({
523
+        pointNumber: item.pointNumber,
524
+        longitude: item.longitude,
525
+        latitude: item.latitude,
526
+        height: item.height,
527
+        spatialX: item.spatialX,
528
+        spatialY: item.spatialY,
529
+        spatialZ: item.spatialZ
530
+      }))
531
+    } else if (this.importedData.length > 0) {
532
+      return this.importedData.map(item => ({
533
+        pointNumber: item.pointNumber,
534
+        longitude: item.longitude,
535
+        latitude: item.latitude,
536
+        height: item.height,
537
+        spatialX: undefined,
538
+        spatialY: undefined,
539
+        spatialZ: undefined
540
+      }))
541
+    }
542
+    return []
543
+  }
544
+}
383 545
 }
546
+
547
+
384 548
 </script>
385 549
 
386 550
 <style scoped>
@@ -398,6 +562,43 @@ export default {
398 562
   flex-wrap: wrap;
399 563
 }
400 564
 
565
+/* 参数设置区域样式 */
566
+.params-area {
567
+  background: #f5f7fa;
568
+  border-radius: 8px;
569
+  padding: 16px 20px;
570
+  margin-bottom: 20px;
571
+  border: 1px solid #e4e7ed;
572
+}
573
+
574
+.params-row {
575
+  display: flex;
576
+  flex-wrap: wrap;
577
+  gap: 24px;
578
+  align-items: center;
579
+}
580
+
581
+.params-row + .params-row {
582
+  margin-top: 12px;
583
+}
584
+
585
+.params-group {
586
+  display: flex;
587
+  align-items: center;
588
+  gap: 8px;
589
+}
590
+
591
+.param-label {
592
+  font-weight: 500;
593
+  color: #606266;
594
+  min-width: 40px;
595
+}
596
+
597
+.unit {
598
+  color: #909399;
599
+  font-size: 13px;
600
+}
601
+
401 602
 .result-section {
402 603
   margin-top: 10px;
403 604
 }
@@ -435,6 +636,10 @@ export default {
435 636
   font-weight: 500;
436 637
 }
437 638
 
639
+.empty-cell {
640
+  color: #c0c4cc;
641
+}
642
+
438 643
 .empty-state {
439 644
   text-align: center;
440 645
   padding: 60px 20px;
@@ -502,4 +707,41 @@ export default {
502 707
   padding-top: 12px;
503 708
   border-top: 1px dashed #e4e7ed;
504 709
 }
710
+
711
+/* 字段映射对话框样式 */
712
+.mapping-tips {
713
+  margin-bottom: 20px;
714
+}
715
+
716
+.mapping-section {
717
+  background: #f9fafb;
718
+  border-radius: 8px;
719
+  padding: 16px;
720
+}
721
+
722
+.mapping-row {
723
+  display: flex;
724
+  align-items: center;
725
+  margin-bottom: 12px;
726
+}
727
+
728
+.mapping-row:last-child {
729
+  margin-bottom: 0;
730
+}
731
+
732
+.mapping-label {
733
+  width: 120px;
734
+  font-weight: 500;
735
+  color: #606266;
736
+}
737
+
738
+.mapping-select {
739
+  flex: 1;
740
+  min-width: 200px;
741
+}
742
+
743
+.empty-cell {
744
+  color: #C0C4CC;
745
+  font-style: italic;
746
+}
505 747
 </style>

+ 353
- 119
oa-ui/src/views/calculate/tools/spatialtogeodetic.vue Datei anzeigen

@@ -8,14 +8,6 @@
8 8
           导入 Excel
9 9
         </el-button>
10 10
 
11
-        <el-button 
12
-          type="success" 
13
-          @click="handlePreview" 
14
-          size="small" 
15
-          :disabled="!importedData || importedData.length === 0"
16
-          icon="el-icon-view">
17
-          预览数据
18
-        </el-button>
19 11
         <el-button 
20 12
           type="warning" 
21 13
           @click="handleCalculate" 
@@ -36,47 +28,96 @@
36 28
         </el-button>
37 29
       </div>
38 30
     </div>
31
+    
32
+    <!-- 参数设置区域 -->
33
+    <div class="params-area">
34
+      <div class="params-row">
35
+        <div class="params-group">
36
+          <label class="param-label">投影面高程</label>
37
+          <el-input 
38
+            v-model="commonParams.projectionHeight" 
39
+            size="small" 
40
+            style="width: 120px;"
41
+            @change="onParamsChange">
42
+          </el-input>
43
+          <span class="unit">米 (m)</span>
44
+        </div>
45
+
46
+        <div class="params-group">
47
+          <label class="param-label">坐标系</label>
48
+          <el-select v-model="commonParams.coordinateSystem" size="small" style="width: 140px;" @change="onParamsChange">
49
+            <el-option label="WGS84" value="WGS84"></el-option>
50
+            <el-option label="CGCS2000" value="CGCS2000"></el-option>
51
+            <el-option label="Beijing54" value="Beijing54"></el-option>
52
+            <el-option label="Xian80" value="Xian80"></el-option>
53
+          </el-select>
54
+        </div>
55
+      </div>
56
+    </div>
39 57
 
40
-    <!-- 计算结果展示区 -->
41
-    <div v-if="calculationResults.length > 0" class="result-section">
58
+    <!-- 数据展示区域 - 合并显示导入数据和计算结果 -->
59
+    <div v-if="displayData.length > 0" class="result-section">
42 60
       <div class="section-header">
43 61
         <span class="section-title">
44 62
           <i class="el-icon-tickets"></i>
45
-          计算结果
63
+          {{ calculationResults.length > 0 ? '计算结果' : '导入数据' }}
46 64
         </span>
47 65
         <span class="section-info">
48
-          共 {{ calculationResults.length }} 条计算结果
66
+          共 {{ displayData.length }} 条数据
67
+          <span v-if="calculationResults.length > 0" style="margin-left: 10px;">
68
+            | 坐标系:{{ commonParams.coordinateSystem }} | 投影面高程:{{ commonParams.projectionHeight }}m
69
+          </span>
49 70
         </span>
50
-        <el-button type="text" size="small" @click="handleExport" icon="el-icon-download">
71
+        <el-button v-if="calculationResults.length > 0" type="text" size="small" @click="handleExport" icon="el-icon-download">
51 72
           导出结果
52 73
         </el-button>
53 74
       </div>
54 75
       
55
-      <el-table :data="calculationResults" style="width: 100%" border stripe>
76
+      <el-table :data="displayData" style="width: 100%" border stripe>
56 77
         <el-table-column prop="pointNumber" label="待求点号" width="120" align="center"></el-table-column>
57
-        <el-table-column prop="longitude" label="大地经度(°)" width="180" align="center">
78
+        <el-table-column prop="spatialX" label="空间直角坐标 X(m)" width="150" align="center">
79
+          <template slot-scope="scope">
80
+            <span>{{ formatNumber(scope.row.spatialX, 4) }}</span>
81
+          </template>
82
+        </el-table-column>
83
+        <el-table-column prop="spatialY" label="空间直角坐标 Y(m)" width="150" align="center">
58 84
           <template slot-scope="scope">
59
-            <span class="result-value">{{ formatNumber(scope.row.longitude, 9) }}</span>
85
+            <span>{{ formatNumber(scope.row.spatialY, 4) }}</span>
60 86
           </template>
61 87
         </el-table-column>
62
-        <el-table-column prop="longitudePosition" label="经度位置" width="100" align="center">
88
+        <el-table-column prop="spatialZ" label="空间直角坐标 Z(m)" width="150" align="center">
63 89
           <template slot-scope="scope">
64
-            <el-tag size="small" type="primary">{{ scope.row.longitudePosition }}</el-tag>
90
+            <span>{{ formatNumber(scope.row.spatialZ, 4) }}</span>
65 91
           </template>
66 92
         </el-table-column>
67
-        <el-table-column prop="latitude" label="大地纬度(°)" width="180" align="center">
93
+        <el-table-column prop="longitude" label="大地经度(° ′ ″)" width="150" align="center">
68 94
           <template slot-scope="scope">
69
-            <span class="result-value">{{ formatNumber(scope.row.latitude, 9) }}</span>
95
+            <span v-if="scope.row.longitude" class="result-value">{{ formatNumber(scope.row.longitude, 10) }}</span>
96
+            <span v-else class="empty-cell">-</span>
70 97
           </template>
71 98
         </el-table-column>
72
-        <el-table-column prop="latitudePosition" label="纬度位置" width="100" align="center">
99
+        <el-table-column prop="longitudePosition" label="经度位置" width="150" align="center">
73 100
           <template slot-scope="scope">
74
-            <el-tag size="small" type="success">{{ scope.row.latitudePosition }}</el-tag>
101
+            <span v-if="scope.row.longitudePosition" class="result-value">{{ scope.row.longitudePosition }}</span>
102
+            <span v-else class="empty-cell">-</span>
103
+          </template>
104
+        </el-table-column>
105
+        <el-table-column prop="latitude" label="大地纬度(° ′ ″)" width="150" align="center">
106
+          <template slot-scope="scope">
107
+            <span v-if="scope.row.latitude" class="result-value">{{ formatNumber(scope.row.latitude, 10) }}</span>
108
+            <span v-else class="empty-cell">-</span>
109
+          </template>
110
+        </el-table-column>
111
+        <el-table-column prop="latitudePosition" label="纬度位置" width="150" align="center">
112
+          <template slot-scope="scope">
113
+            <span v-if="scope.row.latitudePosition" class="result-value">{{ scope.row.latitudePosition }}</span>
114
+            <span v-else class="empty-cell">-</span>
75 115
           </template>
76 116
         </el-table-column>
77 117
         <el-table-column prop="height" label="大地高(m)" width="120" align="center">
78 118
           <template slot-scope="scope">
79
-            <span class="result-value">{{ formatNumber(scope.row.height, 4) }}</span>
119
+            <span v-if="scope.row.height" class="result-value">{{ formatNumber(scope.row.height, 4) }}</span>
120
+            <span v-else class="empty-cell">-</span>
80 121
           </template>
81 122
         </el-table-column>
82 123
       </el-table>
@@ -85,35 +126,12 @@
85 126
     <!-- 空状态 -->
86 127
     <div v-else class="empty-state">
87 128
       <i class="el-icon-document"></i>
88
-      <p>暂无计算结果</p>
89
-      <p class="empty-tip">请先导入 Excel 数据,然后点击"执行计算"</p>
129
+      <p>暂无数据</p>
130
+      <p class="empty-tip">请先导入 Excel 数据</p>
90 131
     </div>
91 132
     
92 133
     <!-- 导入 Excel 对话框 -->
93 134
     <el-dialog title="导入 Excel" :visible.sync="importDialogVisible" width="550px" @close="resetImport">
94
-      <div class="import-tips">
95
-        <el-alert
96
-          title="Excel 模板格式说明"
97
-          type="info"
98
-          :closable="false"
99
-          show-icon>
100
-          <div class="template-info">
101
-            <p>请确保 Excel 文件包含以下列(第一行为表头):</p>
102
-            <ul>
103
-              <li><strong>待求点</strong> - 点号</li>
104
-              <li><strong>坐标系</strong> - WGS84/CGCS2000/Beijing54/Xian80</li>
105
-              <li><strong>空间直角坐标X</strong> - X坐标值 (m)</li>
106
-              <li><strong>空间直角坐标Y</strong> - Y坐标值 (m)</li>
107
-              <li><strong>空间直角坐标Z</strong> - Z坐标值 (m)</li>
108
-            </ul>
109
-          </div>
110
-        </el-alert>
111
-         <div class="template-download-wrapper">
112
-            <el-button type="primary" plain size="small" @click="handleDownloadTemplate" icon="el-icon-download">
113
-              下载 Excel 模板
114
-            </el-button>
115
-          </div>
116
-      </div>
117 135
       <div style="text-align: center;">
118 136
         <el-upload
119 137
           class="upload-area"
@@ -140,39 +158,54 @@
140 158
       </span>
141 159
     </el-dialog>
142 160
     
143
-    <!-- 预览数据对话框 -->
144
-    <el-dialog title="导入数据预览" :visible.sync="previewDialogVisible" width="80%">
145
-      <div class="preview-info">
146
-        <span>共 {{ importedData.length }} 条数据</span>
147
-        <span style="margin-left: 20px;">状态:{{ importedData.filter(item => item.calculated).length }} 条已计算,{{ importedData.filter(item => !item.calculated).length }} 条未计算</span>
161
+    <!-- 字段映射对话框 -->
162
+    <el-dialog title="字段映射" :visible.sync="mappingDialogVisible" width="500px">
163
+      <div class="mapping-tips">
164
+        <el-alert
165
+          title="字段映射说明"
166
+          type="info"
167
+          :closable="false"
168
+          show-icon>
169
+          请将Excel中的列映射到对应的坐标字段。
170
+        </el-alert>
148 171
       </div>
149
-      <el-table :data="importedData" border stripe max-height="500">
150
-        <el-table-column prop="pointNumber" label="待求点号" width="120" fixed="left" align="center"></el-table-column>
151
-        <el-table-column prop="coordinateSystem" label="坐标系" width="110" align="center">
152
-          <template slot-scope="scope">
153
-            <el-tag size="small" :type="getCoordinateType(scope.row.coordinateSystem)">
154
-              {{ scope.row.coordinateSystem }}
155
-            </el-tag>
156
-          </template>
157
-        </el-table-column>
158
-        <el-table-column prop="spatialX" label="空间X坐标" width="150" align="center">
159
-          <template slot-scope="scope">
160
-            {{ formatNumber(scope.row.spatialX, 4) }}
161
-          </template>
162
-        </el-table-column>
163
-        <el-table-column prop="spatialY" label="空间Y坐标" width="150" align="center">
164
-          <template slot-scope="scope">
165
-            {{ formatNumber(scope.row.spatialY, 4) }}
166
-          </template>
167
-        </el-table-column>
168
-        <el-table-column prop="spatialZ" label="空间Z坐标" width="150" align="center">
169
-          <template slot-scope="scope">
170
-            {{ formatNumber(scope.row.spatialZ, 4) }}
171
-          </template>
172
-        </el-table-column>
173
-      </el-table>
172
+      
173
+      <div class="mapping-section">
174
+        <div class="mapping-row">
175
+          <label class="mapping-label">点号字段</label>
176
+          <el-select v-model="fieldMapping.pointNumber" class="mapping-select" size="small">
177
+            <el-option label="请选择" value=""></el-option>
178
+            <el-option v-for="col in excelColumns" :key="col" :label="col" :value="col"></el-option>
179
+          </el-select>
180
+        </div>
181
+        <div class="mapping-row">
182
+          <label class="mapping-label">空间X坐标字段</label>
183
+          <el-select v-model="fieldMapping.spatialX" class="mapping-select" size="small">
184
+            <el-option label="请选择" value=""></el-option>
185
+            <el-option v-for="col in excelColumns" :key="col" :label="col" :value="col"></el-option>
186
+          </el-select>
187
+        </div>
188
+        <div class="mapping-row">
189
+          <label class="mapping-label">空间Y坐标字段</label>
190
+          <el-select v-model="fieldMapping.spatialY" class="mapping-select" size="small">
191
+            <el-option label="请选择" value=""></el-option>
192
+            <el-option v-for="col in excelColumns" :key="col" :label="col" :value="col"></el-option>
193
+          </el-select>
194
+        </div>
195
+        <div class="mapping-row">
196
+          <label class="mapping-label">空间Z坐标字段</label>
197
+          <el-select v-model="fieldMapping.spatialZ" class="mapping-select" size="small">
198
+            <el-option label="请选择" value=""></el-option>
199
+            <el-option v-for="col in excelColumns" :key="col" :label="col" :value="col"></el-option>
200
+          </el-select>
201
+        </div>
202
+      </div>
203
+      
174 204
       <span slot="footer" class="dialog-footer">
175
-        <el-button type="primary" @click="previewDialogVisible = false">关闭</el-button>
205
+        <el-button @click="cancelMapping">取消</el-button>
206
+        <el-button type="primary" @click="confirmMapping" :disabled="!validateMapping">
207
+          确认映射
208
+        </el-button>
176 209
       </span>
177 210
     </el-dialog>
178 211
   </div>
@@ -191,7 +224,23 @@ export default {
191 224
       importLoading: false,
192 225
       importFile: null,
193 226
       calculating: false,
194
-      previewDialogVisible: false,
227
+      
228
+      // 公共参数
229
+      commonParams: {
230
+        projectionHeight: 0,
231
+        coordinateSystem: 'WGS84'
232
+      },
233
+      
234
+      // 字段映射相关
235
+      mappingDialogVisible: false,
236
+      excelColumns: [],
237
+      fieldMapping: {
238
+        pointNumber: '',
239
+        spatialX: '',
240
+        spatialY: '',
241
+        spatialZ: ''
242
+      },
243
+      rawImportedData: []
195 244
     }
196 245
   },
197 246
   methods: {
@@ -217,38 +266,113 @@ export default {
217 266
       const formData = new FormData()
218 267
       formData.append('file', this.importFile)
219 268
       
220
-      const response = await importExcel(formData)
221
-      
222
-      if (response.code === 200) {
223
-        const result = response.data
269
+      try {
270
+        const response = await importExcel(formData)
224 271
         
225
-        // 将后端返回的数据转换为前端格式
226
-        this.importedData = (result.data || []).map(item => ({
227
-          pointNumber: item.pointNumber,
228
-          coordinateSystem: item.coordinateSystem,
229
-          spatialX: item.spatialX,
230
-          spatialY: item.spatialY,
231
-          spatialZ: item.spatialZ
232
-        }))
233
-        
234
-        // 清空之前的计算结果
235
-        this.calculationResults = []
236
-        
237
-        this.$message.success(`${response.msg}`)
238
-        this.importDialogVisible = false
272
+        if (response.code === 200) {
273
+          const result = response.data
274
+          
275
+          // 获取Excel列名
276
+          this.excelColumns = result.columns || this.extractColumns(result.data)
277
+          
278
+          // 保存原始数据
279
+          this.rawImportedData = result.data || []
280
+          
281
+          // 清空之前的计算结果
282
+          this.calculationResults = []
283
+          
284
+          // 自动匹配字段
285
+          this.autoMatchFields()
286
+          
287
+          // 显示字段映射对话框
288
+          this.mappingDialogVisible = true
289
+          
290
+          this.$message.success(`${response.msg}`)
291
+          this.importDialogVisible = false
292
+          this.importLoading = false
293
+        } else {
294
+          this.$message.error(response.msg)
295
+          this.importLoading = false
296
+        }
297
+      } catch (error) {
298
+        this.$message.error('导入失败')
239 299
         this.importLoading = false
240
-      } else {
241
-        this.$message.error(response.msg)
242 300
       }
243 301
     },
244 302
     
245
-    // 预览数据 - 显示导入的原始数据
246
-    handlePreview() {
247
-      if (!this.importedData || this.importedData.length === 0) {
248
-        this.$message.warning('没有可预览的数据')
303
+    // 提取列名
304
+    extractColumns(data) {
305
+      if (!data || data.length === 0) return []
306
+      return Object.keys(data[0])
307
+    },
308
+    
309
+    // 自动匹配字段
310
+    autoMatchFields() {
311
+      this.resetFieldMapping()
312
+      
313
+      const keywordMap = {
314
+        pointNumber: ['点号', '待求点', '点', '编号', '序号', 'ID'],
315
+        spatialX: ['空间直角坐标X', '空间X', 'X坐标', 'X', 'x'],
316
+        spatialY: ['空间直角坐标Y', '空间Y', 'Y坐标', 'Y', 'y'],
317
+        spatialZ: ['空间直角坐标Z', '空间Z', 'Z坐标', 'Z', 'z']
318
+      }
319
+      
320
+      this.excelColumns.forEach(col => {
321
+        const lowerCol = col.toLowerCase()
322
+        Object.keys(keywordMap).forEach(field => {
323
+          if (!this.fieldMapping[field]) {
324
+            keywordMap[field].forEach(keyword => {
325
+              if (col.includes(keyword) || lowerCol.includes(keyword.toLowerCase())) {
326
+                this.fieldMapping[field] = col
327
+              }
328
+            })
329
+          }
330
+        })
331
+      })
332
+    },
333
+    
334
+    // 确认字段映射
335
+    confirmMapping() {
336
+      if (!this.validateMapping) {
337
+        this.$message.warning('请完成所有必填字段的映射')
249 338
         return
250 339
       }
251
-      this.previewDialogVisible = true
340
+      
341
+      // 根据映射转换数据
342
+      this.importedData = this.rawImportedData.map(item => ({
343
+        pointNumber: item[this.fieldMapping.pointNumber],
344
+        spatialX: item[this.fieldMapping.spatialX],
345
+        spatialY: item[this.fieldMapping.spatialY],
346
+        spatialZ: item[this.fieldMapping.spatialZ]
347
+      }))
348
+      
349
+      this.mappingDialogVisible = false
350
+      this.resetFieldMapping()
351
+    },
352
+    
353
+    // 取消字段映射
354
+    cancelMapping() {
355
+      this.mappingDialogVisible = false
356
+      this.resetFieldMapping()
357
+      this.rawImportedData = []
358
+    },
359
+    
360
+    // 重置字段映射
361
+    resetFieldMapping() {
362
+      this.fieldMapping = {
363
+        pointNumber: '',
364
+        spatialX: '',
365
+        spatialY: '',
366
+        spatialZ: ''
367
+      }
368
+    },
369
+    
370
+    // 参数变化时清空计算结果
371
+    onParamsChange() {
372
+      if (this.calculationResults.length > 0) {
373
+        this.calculationResults = []
374
+        this.$message.info('参数已更新,请重新执行计算')
375
+      }
252 376
     },
253 377
     
254 378
     // 下载模板
@@ -268,7 +392,8 @@ export default {
268 392
       // 构建请求参数
269 393
       const requestData = this.importedData.map(item => ({
270 394
         pointNumber: item.pointNumber,
271
-        coordinateSystem: item.coordinateSystem,
395
+        projectionHeight: parseFloat(this.commonParams.projectionHeight) || 0,
396
+        coordinateSystem: this.commonParams.coordinateSystem,
272 397
         spatialX: parseFloat(item.spatialX),
273 398
         spatialY: parseFloat(item.spatialY),
274 399
         spatialZ: parseFloat(item.spatialZ)
@@ -282,33 +407,27 @@ export default {
282 407
         // 更新计算结果
283 408
         this.calculationResults = results.map(item => ({
284 409
           pointNumber: item.pointNumber,
285
-          coordinateSystem: item.coordinateSystem,
286 410
           spatialX: item.spatialX,
287 411
           spatialY: item.spatialY,
288 412
           spatialZ: item.spatialZ,
289 413
           longitude: item.longitude,
290
-          longitudePosition: item.longitudePosition,
291 414
           latitude: item.latitude,
415
+          height: item.height,
416
+          coordinateSystem: item.coordinateSystem,
417
+          longitudePosition: item.longitudePosition,
292 418
           latitudePosition: item.latitudePosition,
293
-          height: item.height
419
+          projectionHeight: item.projectionHeight,
294 420
         }))
295 421
         
296
-        // 更新导入数据的计算状态
297
-        this.importedData.forEach(importItem => {
298
-          const found = results.find(r => r.pointNumber === importItem.pointNumber)
299
-          if (found && found.longitude !== -1) {
300
-            importItem.calculated = true
301
-          }
302
-        })
303
-        
304
-        const successCount = results.filter(r => r.longitude !== -1).length
422
+        const successCount = results.filter(r => r.longitude !== undefined && r.longitude !== -1).length
305 423
         const failCount = results.length - successCount
306 424
         
307 425
         this.$message.success(`计算完成,成功 ${successCount} 条${failCount > 0 ? `,失败 ${failCount} 条` : ''}`)
308
-        this.calculating = false
309 426
       } else {
310 427
         this.$message.error(response.msg)
311 428
       }
429
+      
430
+      this.calculating = false
312 431
     },
313 432
     
314 433
     // 导出结果
@@ -373,6 +492,47 @@ export default {
373 492
       }
374 493
       return types[system] || 'info'
375 494
     }
495
+  },
496
+  
497
+  computed: {
498
+    // 验证字段映射
499
+    validateMapping() {
500
+      if (!this.fieldMapping) {
501
+        return false
502
+      }
503
+      return this.fieldMapping.pointNumber && 
504
+             this.fieldMapping.spatialX && 
505
+             this.fieldMapping.spatialY && 
506
+             this.fieldMapping.spatialZ
507
+    },
508
+    
509
+    // 显示数据:优先显示计算结果,否则显示导入数据
510
+    displayData() {
511
+      if (this.calculationResults.length > 0) {
512
+        return this.calculationResults.map(item => ({
513
+          pointNumber: item.pointNumber,
514
+          spatialX: item.spatialX,
515
+          spatialY: item.spatialY,
516
+          spatialZ: item.spatialZ,
517
+          longitude: item.longitude,
518
+          latitude: item.latitude,
519
+          height: item.height,
520
+          longitudePosition: item.longitudePosition,
521
+          latitudePosition: item.latitudePosition,
522
+        }))
523
+      } else if (this.importedData.length > 0) {
524
+        return this.importedData.map(item => ({
525
+          pointNumber: item.pointNumber,
526
+          spatialX: item.spatialX,
527
+          spatialY: item.spatialY,
528
+          spatialZ: item.spatialZ,
529
+          longitude: undefined,
530
+          latitude: undefined,
531
+          height: undefined
532
+        }))
533
+      }
534
+      return []
535
+    }
376 536
   }
377 537
 }
378 538
 </script>
@@ -496,4 +656,78 @@ export default {
496 656
   padding-top: 12px;
497 657
   border-top: 1px dashed #e4e7ed;
498 658
 }
659
+
660
+/* 参数设置区域样式 */
661
+.params-area {
662
+  background: #f5f7fa;
663
+  border-radius: 8px;
664
+  padding: 16px 20px;
665
+  margin-bottom: 20px;
666
+  border: 1px solid #e4e7ed;
667
+}
668
+
669
+.params-row {
670
+  display: flex;
671
+  flex-wrap: wrap;
672
+  gap: 24px;
673
+  align-items: center;
674
+}
675
+
676
+.params-row + .params-row {
677
+  margin-top: 12px;
678
+}
679
+
680
+.params-group {
681
+  display: flex;
682
+  align-items: center;
683
+  gap: 8px;
684
+}
685
+
686
+.param-label {
687
+  font-weight: 500;
688
+  color: #606266;
689
+  min-width: 40px;
690
+}
691
+
692
+.unit {
693
+  color: #909399;
694
+  font-size: 13px;
695
+}
696
+
697
+.empty-cell {
698
+  color: #C0C4CC;
699
+  font-style: italic;
700
+}
701
+
702
+/* 字段映射对话框样式 */
703
+.mapping-tips {
704
+  margin-bottom: 20px;
705
+}
706
+
707
+.mapping-section {
708
+  background: #f9fafb;
709
+  border-radius: 8px;
710
+  padding: 16px;
711
+}
712
+
713
+.mapping-row {
714
+  display: flex;
715
+  align-items: center;
716
+  margin-bottom: 12px;
717
+}
718
+
719
+.mapping-row:last-child {
720
+  margin-bottom: 0;
721
+}
722
+
723
+.mapping-label {
724
+  width: 120px;
725
+  font-weight: 500;
726
+  color: #606266;
727
+}
728
+
729
+.mapping-select {
730
+  flex: 1;
731
+  min-width: 200px;
732
+}
499 733
 </style>

+ 342
- 154
oa-ui/src/views/calculate/tools/utmbandchange.vue Datei anzeigen

@@ -8,14 +8,6 @@
8 8
           导入Excel
9 9
         </el-button>
10 10
 
11
-        <el-button 
12
-          type="success" 
13
-          @click="handlePreview" 
14
-          size="small" 
15
-          :disabled="!importedData || importedData.length === 0"
16
-          icon="el-icon-view">
17
-          预览数据
18
-        </el-button>
19 11
         <el-button 
20 12
           type="warning" 
21 13
           @click="handleCalculate" 
@@ -37,47 +29,99 @@
37 29
       </div>
38 30
     </div>
39 31
     
40
-    <!-- 投影面高程设置 -->
41
-    <div class="height-setting">
42
-      <div class="setting-label">
43
-        <span>投影面高程</span>
44
-      </div>
45
-      <div class="setting-content">
46
-        <el-input 
47
-          v-model="projectionHeight" 
48
-          size="small" 
49
-          style="width: 120px; margin-right: 10px;">
50
-        </el-input>
51
-        <span class="unit">米 (m)</span>
32
+    <!-- 参数设置区域 -->
33
+    <div class="params-area">
34
+      <div class="params-row">
35
+        <div class="params-group">
36
+          <label class="param-label">投影面高程</label>
37
+          <el-input 
38
+            v-model="projectionHeight" 
39
+            size="small" 
40
+            style="width: 120px;">
41
+          </el-input>
42
+          <span class="unit">米 (m)</span>
43
+        </div>
52 44
         
45
+        <div class="params-group">
46
+          <label class="param-label">坐标系</label>
47
+          <el-select v-model="commonParams.coordinateSystem" size="small" style="width: 140px;" @change="onParamsChange">
48
+            <el-option label="WGS84" value="WGS84"></el-option>
49
+            <el-option label="CGCS2000" value="CGCS2000"></el-option>
50
+            <el-option label="Beijing54" value="Beijing54"></el-option>
51
+            <el-option label="Xian80" value="Xian80"></el-option>
52
+          </el-select>
53
+        </div>
54
+        
55
+        <div class="params-group">
56
+          <label class="param-label">经度位置</label>
57
+          <el-select v-model="commonParams.longitudePosition" size="small" style="width: 80px;" @change="onParamsChange">
58
+            <el-option label="E" value="E"></el-option>
59
+            <el-option label="W" value="W"></el-option>
60
+          </el-select>
61
+        </div>
62
+        
63
+        <div class="params-group">
64
+          <label class="param-label">纬度位置</label>
65
+          <el-select v-model="commonParams.latitudePosition" size="small" style="width: 80px;" @change="onParamsChange">
66
+            <el-option label="N" value="N"></el-option>
67
+            <el-option label="S" value="S"></el-option>
68
+          </el-select>
69
+        </div>
70
+        
71
+        <div class="params-group">
72
+          <label class="param-label">原带号</label>
73
+          <el-input v-model.number="commonParams.band" size="small" style="width: 80px;" placeholder="带号" @change="onParamsChange"></el-input>
74
+        </div>
75
+        
76
+        <div class="params-group">
77
+          <label class="param-label">新带号</label>
78
+          <el-input v-model.number="commonParams.newBand" size="small" style="width: 80px;" placeholder="新带号" @change="onParamsChange"></el-input>
79
+        </div>
53 80
       </div>
54 81
     </div>
55 82
 
56
-    <!-- 计算结果展示区 - 只显示计算结果,不包含原始数据 -->
57
-    <div v-if="calculationResults.length > 0" class="result-section">
83
+    <!-- 数据展示区域 - 合并显示导入数据和计算结果 -->
84
+    <div v-if="displayData.length > 0" class="result-section">
58 85
       <div class="section-header">
59 86
         <span class="section-title">
60 87
           <i class="el-icon-tickets"></i>
61
-          计算结果
88
+          {{ calculationResults.length > 0 ? '计算结果' : '导入数据' }}
62 89
         </span>
63 90
         <span class="section-info">
64
-          共 {{ calculationResults.length }} 条计算结果
91
+          共 {{ displayData.length }} 条数据
92
+          <span v-if="calculationResults.length > 0" style="margin-left: 10px;">
93
+            | 坐标系:{{ commonParams.coordinateSystem }}
94
+            | 原带号:{{ commonParams.band }}
95
+            | 新带号:{{ commonParams.newBand }}
96
+          </span>
65 97
         </span>
66
-        <el-button type="text" size="small" @click="handleExport" icon="el-icon-download">
98
+        <el-button v-if="calculationResults.length > 0" type="text" size="small" @click="handleExport" icon="el-icon-download">
67 99
           导出结果
68 100
         </el-button>
69 101
       </div>
70 102
       
71
-      <el-table :data="calculationResults" style="width: 100%" border stripe>
103
+      <el-table :data="displayData" style="width: 100%" border stripe>
72 104
         <el-table-column prop="pointNumber" label="待求点号" width="120" align="center"></el-table-column>
73
-        <el-table-column prop="newUtmX" label="UTM坐标x(m)" width="180" align="center">
105
+        <el-table-column prop="utmX" label="原UTM坐标X(m)" width="150" align="center">
74 106
           <template slot-scope="scope">
75
-            <span class="result-value">{{ formatNumber(scope.row.newUtmX,4) }}</span>
107
+            <span>{{ formatNumber(scope.row.utmX, 4) }}</span>
76 108
           </template>
77 109
         </el-table-column>
78
-        <el-table-column prop="newUtmY" label="UTM坐标y(m)" width="180" align="center">
110
+        <el-table-column prop="utmY" label="原UTM坐标Y(m)" width="150" align="center">
79 111
           <template slot-scope="scope">
80
-            <span class="result-value">{{ formatNumber(scope.row.newUtmY,4) }}</span>
112
+            <span>{{ formatNumber(scope.row.utmY, 4) }}</span>
113
+          </template>
114
+        </el-table-column>
115
+        <el-table-column prop="newUtmX" label="新UTM坐标X(m)" width="150" align="center">
116
+          <template slot-scope="scope">
117
+            <span v-if="scope.row.newUtmX" class="result-value">{{ formatNumber(scope.row.newUtmX, 4) }}</span>
118
+            <span v-else class="empty-cell">-</span>
119
+          </template>
120
+        </el-table-column>
121
+        <el-table-column prop="newUtmY" label="新UTM坐标Y(m)" width="150" align="center">
122
+          <template slot-scope="scope">
123
+            <span v-if="scope.row.newUtmY" class="result-value">{{ formatNumber(scope.row.newUtmY, 4) }}</span>
124
+            <span v-else class="empty-cell">-</span>
81 125
           </template>
82 126
         </el-table-column>
83 127
       </el-table>
@@ -86,38 +130,12 @@
86 130
     <!-- 空状态 -->
87 131
     <div v-else class="empty-state">
88 132
       <i class="el-icon-document"></i>
89
-      <p>暂无计算结果</p>
90
-      <p class="empty-tip">请先导入Excel数据,然后点击"执行计算"</p>
133
+      <p>暂无数据</p>
134
+      <p class="empty-tip">请先导入Excel数据</p>
91 135
     </div>
92 136
     
93 137
     <!-- 导入Excel对话框 -->
94 138
     <el-dialog title="导入Excel" :visible.sync="importDialogVisible" width="550px" @close="resetImport">
95
-      <div class="import-tips">
96
-        <el-alert
97
-          title="Excel模板格式说明"
98
-          type="info"
99
-          :closable="false"
100
-          show-icon>
101
-          <div class="template-info">
102
-            <p>请确保Excel文件包含以下列(第一行为表头):</p>
103
-            <ul>
104
-              <li><strong>待求点</strong> - 点号</li>
105
-              <li><strong>坐标系</strong> - WGS84/CGCS2000/Beijing54/Xian80</li>
106
-              <li><strong>UTM坐标X</strong> - (m)</li>
107
-              <li><strong>UTM坐标Y</strong> - (m)</li>
108
-              <li><strong>带号</strong> - 带号数值</li>
109
-              <li><strong>新带号</strong> - 新带号数值</li>
110
-              <li><strong>经度位置</strong> - E/W</li>
111
-              <li><strong>纬度位置</strong> - N/S</li>
112
-            </ul>
113
-          </div>
114
-        </el-alert>
115
-         <div class="template-download-wrapper">
116
-            <el-button type="primary" plain size="small" @click="handleDownloadTemplate" icon="el-icon-download">
117
-              下载Excel模板
118
-            </el-button>
119
-          </div>
120
-      </div>
121 139
       <div style="text-align: center;">
122 140
         <el-upload
123 141
           class="upload-area"
@@ -144,54 +162,47 @@
144 162
       </span>
145 163
     </el-dialog>
146 164
     
147
-    <!-- 预览数据对话框 - 显示导入的原始数据 -->
148
-    <el-dialog title="导入数据预览" :visible.sync="previewDialogVisible" width="80%">
149
-      <div class="preview-info">
150
-        <span>共 {{ importedData.length }} 条数据</span>
151
-        <span style="margin-left: 20px;">状态:{{ importedData.filter(item => item.calculated).length }} 条已计算,{{ importedData.filter(item => !item.calculated).length }} 条未计算</span>
165
+    <!-- 字段映射对话框 -->
166
+    <el-dialog title="字段映射" :visible.sync="mappingDialogVisible" width="500px">
167
+      <div class="mapping-tips">
168
+        <el-alert
169
+          title="字段映射说明"
170
+          type="info"
171
+          :closable="false"
172
+          show-icon>
173
+          请将Excel中的列映射到对应的坐标字段。
174
+        </el-alert>
152 175
       </div>
153
-      <el-table :data="importedData" border stripe max-height="500">
154
-        <el-table-column prop="pointNumber" label="待求点号" width="120" fixed="left" align="center"></el-table-column>
155
-        <el-table-column prop="coordinateSystem" label="坐标系" width="110" align="center">
156
-          <template slot-scope="scope">
157
-            <el-tag size="small" :type="getCoordinateType(scope.row.coordinateSystem)">
158
-              {{ scope.row.coordinateSystem }}
159
-            </el-tag>
160
-          </template>
161
-        </el-table-column>
162
-        <el-table-column prop="utmX" label="UTM X坐标(m)" width="130" align="center">
163
-          <template slot-scope="scope">
164
-            {{ formatNumber(scope.row.utmX,4) }}
165
-          </template>
166
-        </el-table-column>
167
-         <el-table-column prop="utmY" label="UTM Y坐标(m)" width="130" align="center">
168
-          <template slot-scope="scope">
169
-            {{ formatNumber(scope.row.utmY,4) }}
170
-          </template>
171
-        </el-table-column>
172
-        <el-table-column prop="band" label="带号" width="130" align="center">
173
-          <template slot-scope="scope">
174
-            {{ scope.row.band}}
175
-                   </template>
176
-        </el-table-column>
177
-        <el-table-column prop="newBand" label="新带号" width="130" align="center">
178
-          <template slot-scope="scope">
179
-            {{ scope.row.newBand}}
180
-                   </template>
181
-        </el-table-column>
182
-        <el-table-column prop="longitudePosition" label="经度位置" width="130" align="center">
183
-          <template slot-scope="scope">
184
-            {{ scope.row.longitudePosition}}
185
-                   </template>
186
-        </el-table-column>
187
-        <el-table-column prop="latitudePosition" label="纬度位置" width="130" align="center">
188
-          <template slot-scope="scope">
189
-            {{ scope.row.latitudePosition}}
190
-                   </template>
191
-        </el-table-column>
192
-      </el-table>
176
+      
177
+      <div class="mapping-section">
178
+        <div class="mapping-row">
179
+          <label class="mapping-label">点号字段</label>
180
+          <el-select v-model="fieldMapping.pointNumber" class="mapping-select" size="small">
181
+            <el-option label="请选择" value=""></el-option>
182
+            <el-option v-for="col in excelColumns" :key="col" :label="col" :value="col"></el-option>
183
+          </el-select>
184
+        </div>
185
+        <div class="mapping-row">
186
+          <label class="mapping-label">UTM坐标X字段</label>
187
+          <el-select v-model="fieldMapping.utmX" class="mapping-select" size="small">
188
+            <el-option label="请选择" value=""></el-option>
189
+            <el-option v-for="col in excelColumns" :key="col" :label="col" :value="col"></el-option>
190
+          </el-select>
191
+        </div>
192
+        <div class="mapping-row">
193
+          <label class="mapping-label">UTM坐标Y字段</label>
194
+          <el-select v-model="fieldMapping.utmY" class="mapping-select" size="small">
195
+            <el-option label="请选择" value=""></el-option>
196
+            <el-option v-for="col in excelColumns" :key="col" :label="col" :value="col"></el-option>
197
+          </el-select>
198
+        </div>
199
+      </div>
200
+      
193 201
       <span slot="footer" class="dialog-footer">
194
-        <el-button type="primary" @click="previewDialogVisible = false">关闭</el-button>
202
+        <el-button @click="cancelMapping">取消</el-button>
203
+        <el-button type="primary" @click="confirmMapping" :disabled="!validateMapping">
204
+          确认映射
205
+        </el-button>
195 206
       </span>
196 207
     </el-dialog>
197 208
   </div>
@@ -201,7 +212,7 @@
201 212
 import { calculate, importExcel,exportExcel,downloadTemplate } from '@/api/calculate/utmbandchange'
202 213
 
203 214
 export default {
204
-  name: 'UTMBandChangeTool',    
215
+  name: 'UTMBandChangeTool',
205 216
   data() {
206 217
     return {
207 218
       importedData: [],        // 导入的原始数据
@@ -210,8 +221,27 @@ export default {
210 221
       importLoading: false,
211 222
       importFile: null,
212 223
       calculating: false,
213
-      previewDialogVisible: false,
214
-      projectionHeight: 0
224
+      projectionHeight: 0,
225
+      heightTimer: null,
226
+      
227
+      // 公共参数
228
+      commonParams: {
229
+        coordinateSystem: 'WGS84',
230
+        longitudePosition: 'E',
231
+        latitudePosition: 'N',
232
+        band: 0,
233
+        newBand: 0
234
+      },
235
+      
236
+      // 字段映射相关
237
+      mappingDialogVisible: false,
238
+      excelColumns: [],
239
+      fieldMapping: {
240
+        pointNumber: '',
241
+        utmX: '',
242
+        utmY: ''
243
+      },
244
+      rawImportedData: []
215 245
     }
216 246
   },
217 247
   watch: {
@@ -254,40 +284,110 @@ export default {
254 284
       const formData = new FormData()
255 285
       formData.append('file', this.importFile)
256 286
       
287
+      try {
257 288
         const response = await importExcel(formData)
258 289
         
259 290
         if (response.code === 200) {
260 291
           const result = response.data
261
-          // 将后端返回的数据转换为前端格式
262
-          this.importedData = (result.data || []).map(item => ({
263
-            pointNumber: item.pointNumber,
264
-            coordinateSystem: item.coordinateSystem,
265
-            utmX: item.utmX,
266
-            utmY: item.utmY,
267
-            band: item.band,
268
-            newBand: item.newBand,
269
-            longitudePosition: item.longitudePosition,
270
-            latitudePosition: item.latitudePosition
271
-          }))
292
+          
293
+          // 获取Excel列名
294
+          this.excelColumns = result.columns || this.extractColumns(result.data)
295
+          
296
+          // 保存原始数据
297
+          this.rawImportedData = result.data || []
272 298
           
273 299
           // 清空之前的计算结果
274 300
           this.calculationResults = []
275 301
           
302
+          // 自动匹配字段
303
+          this.autoMatchFields()
304
+          
305
+          // 显示字段映射对话框
306
+          this.mappingDialogVisible = true
307
+          
276 308
           this.$message.success(`${response.msg}`)
277 309
           this.importDialogVisible = false
278 310
           this.importLoading = false
279 311
         } else {
280 312
           this.$message.error(response.msg)
313
+          this.importLoading = false
281 314
         }
315
+      } catch (error) {
316
+        this.$message.error('导入失败')
317
+        this.importLoading = false
318
+      }
282 319
     },
283 320
     
284
-    // 预览数据 - 显示导入的原始数据
285
-    handlePreview() {
286
-      if (!this.importedData || this.importedData.length === 0) {
287
-        this.$message.warning('没有可预览的数据')
321
+    // 提取列名
322
+    extractColumns(data) {
323
+      if (!data || data.length === 0) return []
324
+      return Object.keys(data[0])
325
+    },
326
+    
327
+    // 自动匹配字段
328
+    autoMatchFields() {
329
+      this.resetFieldMapping()
330
+      
331
+      const keywordMap = {
332
+        pointNumber: ['点号', '待求点', '点', '编号', '序号', 'ID'],
333
+        utmX: ['UTM坐标X', 'utmX', 'X坐标', 'x', 'X', '东坐标'],
334
+        utmY: ['UTM坐标Y', 'utmY', 'Y坐标', 'y', 'Y', '北坐标']
335
+      }
336
+      
337
+      this.excelColumns.forEach(col => {
338
+        const lowerCol = col.toLowerCase()
339
+        Object.keys(keywordMap).forEach(field => {
340
+          if (!this.fieldMapping[field]) {
341
+            keywordMap[field].forEach(keyword => {
342
+              if (col.includes(keyword) || lowerCol.includes(keyword.toLowerCase())) {
343
+                this.fieldMapping[field] = col
344
+              }
345
+            })
346
+          }
347
+        })
348
+      })
349
+    },
350
+    
351
+    // 确认字段映射
352
+    confirmMapping() {
353
+      if (!this.validateMapping) {
354
+        this.$message.warning('请完成所有必填字段的映射')
288 355
         return
289 356
       }
290
-      this.previewDialogVisible = true
357
+      
358
+      // 根据映射转换数据
359
+      this.importedData = this.rawImportedData.map(item => ({
360
+        pointNumber: item[this.fieldMapping.pointNumber],
361
+        utmX: item[this.fieldMapping.utmX],
362
+        utmY: item[this.fieldMapping.utmY]
363
+      }))
364
+      
365
+      this.mappingDialogVisible = false
366
+      this.resetFieldMapping()
367
+    },
368
+    
369
+    // 取消字段映射
370
+    cancelMapping() {
371
+      this.mappingDialogVisible = false
372
+      this.resetFieldMapping()
373
+      this.rawImportedData = []
374
+    },
375
+    
376
+    // 重置字段映射
377
+    resetFieldMapping() {
378
+      this.fieldMapping = {
379
+        pointNumber: '',
380
+        utmX: '',
381
+        utmY: ''
382
+      }
383
+    },
384
+    
385
+    // 参数变化时清空计算结果
386
+    onParamsChange() {
387
+      if (this.calculationResults.length > 0) {
388
+        this.calculationResults = []
389
+        this.$message.info('参数已更新,请重新执行计算')
390
+      }
291 391
     },
292 392
     
293 393
     // 下载模板
@@ -304,20 +404,17 @@ export default {
304 404
       }
305 405
       
306 406
       this.calculating = true
307
-      const results = []
308
-      let successCount = 0
309
-      let failCount = 0
310 407
       
311 408
       // 构建请求参数
312 409
       const requestData = this.importedData.map(item => ({
313 410
         pointNumber: item.pointNumber,
314
-        coordinateSystem: item.coordinateSystem,
315
-        utmX: item.utmX,
316
-        utmY: item.utmY,
317
-        band: item.band,
318
-        newBand: item.newBand,
319
-        longitudePosition: item.longitudePosition,
320
-        latitudePosition: item.latitudePosition,
411
+        coordinateSystem: this.commonParams.coordinateSystem,
412
+        utmX: parseFloat(item.utmX),
413
+        utmY: parseFloat(item.utmY),
414
+        band: this.commonParams.band,
415
+        newBand: this.commonParams.newBand,
416
+        longitudePosition: this.commonParams.longitudePosition,
417
+        latitudePosition: this.commonParams.latitudePosition,
321 418
         projectionHeight: this.projectionHeight
322 419
       }))
323 420
       
@@ -332,31 +429,24 @@ export default {
332 429
           coordinateSystem: item.coordinateSystem,
333 430
           utmX: item.utmX,
334 431
           utmY: item.utmY,
432
+          newUtmX: item.newUtmX,
433
+          newUtmY: item.newUtmY,
335 434
           band: item.band,
336 435
           newBand: item.newBand,
337 436
           longitudePosition: item.longitudePosition,
338 437
           latitudePosition: item.latitudePosition,
339
-          newUtmX: item.newUtmX,
340
-          newUtmY: item.newUtmY,
341
-          projectionHeight: this.projectionHeight
438
+          projectionHeight: item.projectionHeight,
342 439
         }))
343 440
         
344
-        // 更新导入数据的计算状态
345
-        this.importedData.forEach(importItem => {
346
-          const found = results.find(r => r.pointNumber === importItem.pointNumber)
347
-          if (found && found.utmX !== -1) {
348
-            importItem.calculated = true
349
-          }
350
-        })
351
-        
352
-        const successCount = results.filter(r => r.utmX !== -1).length
441
+        const successCount = results.filter(r => r.newUtmX !== undefined && r.newUtmX !== -1).length
353 442
         const failCount = results.length - successCount
354 443
         
355 444
         this.$message.success(`计算完成,成功 ${successCount} 条${failCount > 0 ? `,失败 ${failCount} 条` : ''}`)
356
-        this.calculating = false
357
-        } else {
358
-          this.$message.error(response.msg)
359
-        }
445
+      } else {
446
+        this.$message.error(response.msg)
447
+      }
448
+      
449
+      this.calculating = false
360 450
     },
361 451
     
362 452
     
@@ -422,6 +512,40 @@ export default {
422 512
     }
423 513
     return types[system] || 'info'
424 514
   }
515
+  },
516
+  
517
+  computed: {
518
+    // 验证字段映射
519
+    validateMapping() {
520
+      if (!this.fieldMapping) {
521
+        return false
522
+      }
523
+      return this.fieldMapping.pointNumber && 
524
+             this.fieldMapping.utmX && 
525
+             this.fieldMapping.utmY
526
+    },
527
+    
528
+    // 显示数据:优先显示计算结果,否则显示导入数据
529
+    displayData() {
530
+      if (this.calculationResults.length > 0) {
531
+        return this.calculationResults.map(item => ({
532
+          pointNumber: item.pointNumber,
533
+          utmX: item.utmX,
534
+          utmY: item.utmY,
535
+          newUtmX: item.newUtmX,
536
+          newUtmY: item.newUtmY
537
+        }))
538
+      } else if (this.importedData.length > 0) {
539
+        return this.importedData.map(item => ({
540
+          pointNumber: item.pointNumber,
541
+          utmX: item.utmX,
542
+          utmY: item.utmY,
543
+          newUtmX: undefined,
544
+          newUtmY: undefined
545
+        }))
546
+      }
547
+      return []
548
+    }
425 549
   }
426 550
 }
427 551
 </script>
@@ -441,17 +565,41 @@ export default {
441 565
   flex-wrap: wrap;
442 566
 }
443 567
 
444
-/* 投影面高程设置样式 */
445
-.height-setting {
568
+/* 参数设置区域样式 */
569
+.params-area {
446 570
   background: #f5f7fa;
447 571
   border-radius: 8px;
448
-  padding: 12px 20px;
572
+  padding: 16px 20px;
449 573
   margin-bottom: 20px;
450 574
   border: 1px solid #e4e7ed;
575
+}
576
+
577
+.params-row {
451 578
   display: flex;
452
-  align-items: center;
453
-  gap: 20px;
454 579
   flex-wrap: wrap;
580
+  gap: 24px;
581
+  align-items: center;
582
+}
583
+
584
+.params-row + .params-row {
585
+  margin-top: 12px;
586
+}
587
+
588
+.params-group {
589
+  display: flex;
590
+  align-items: center;
591
+  gap: 8px;
592
+}
593
+
594
+.param-label {
595
+  font-weight: 500;
596
+  color: #606266;
597
+  min-width: 40px;
598
+}
599
+
600
+.unit {
601
+  color: #909399;
602
+  font-size: 13px;
455 603
 }
456 604
 
457 605
 .result-section {
@@ -491,6 +639,10 @@ export default {
491 639
   font-weight: 500;
492 640
 }
493 641
 
642
+.empty-cell {
643
+  color: #c0c4cc;
644
+}
645
+
494 646
 .empty-state {
495 647
   text-align: center;
496 648
   padding: 60px 20px;
@@ -558,4 +710,40 @@ export default {
558 710
   padding-top: 12px;
559 711
   border-top: 1px dashed #e4e7ed;
560 712
 }
713
+/* 字段映射对话框样式 */
714
+.mapping-tips {
715
+  margin-bottom: 20px;
716
+}
717
+
718
+.mapping-section {
719
+  background: #f9fafb;
720
+  border-radius: 8px;
721
+  padding: 16px;
722
+}
723
+
724
+.mapping-row {
725
+  display: flex;
726
+  align-items: center;
727
+  margin-bottom: 12px;
728
+}
729
+
730
+.mapping-row:last-child {
731
+  margin-bottom: 0;
732
+}
733
+
734
+.mapping-label {
735
+  width: 120px;
736
+  font-weight: 500;
737
+  color: #606266;
738
+}
739
+
740
+.mapping-select {
741
+  flex: 1;
742
+  min-width: 200px;
743
+}
744
+
745
+.empty-cell {
746
+  color: #C0C4CC;
747
+  font-style: italic;
748
+}
561 749
 </style>

+ 323
- 138
oa-ui/src/views/calculate/tools/utmnegative.vue Datei anzeigen

@@ -8,14 +8,6 @@
8 8
           导入Excel
9 9
         </el-button>
10 10
 
11
-        <el-button 
12
-          type="success" 
13
-          @click="handlePreview" 
14
-          size="small" 
15
-          :disabled="!importedData || importedData.length === 0"
16
-          icon="el-icon-view">
17
-          预览数据
18
-        </el-button>
19 11
         <el-button 
20 12
           type="warning" 
21 13
           @click="handleCalculate" 
@@ -37,47 +29,93 @@
37 29
       </div>
38 30
     </div>
39 31
     
40
-    <!-- 投影面高程设置 -->
41
-    <div class="height-setting">
42
-      <div class="setting-label">
43
-        <span>投影面高程</span>
44
-      </div>
45
-      <div class="setting-content">
46
-        <el-input 
47
-          v-model="projectionHeight" 
48
-          size="small" 
49
-          style="width: 120px; margin-right: 10px;">
50
-        </el-input>
51
-        <span class="unit">米 (m)</span>
32
+    <!-- 参数设置区域 -->
33
+    <div class="params-area">
34
+      <div class="params-row">
35
+        <div class="params-group">
36
+          <label class="param-label">投影面高程</label>
37
+          <el-input 
38
+            v-model="projectionHeight" 
39
+            size="small" 
40
+            style="width: 120px;">
41
+          </el-input>
42
+          <span class="unit">米 (m)</span>
43
+        </div>
52 44
         
45
+        <div class="params-group">
46
+          <label class="param-label">坐标系</label>
47
+          <el-select v-model="commonParams.coordinateSystem" size="small" style="width: 140px;" @change="onParamsChange">
48
+            <el-option label="WGS84" value="WGS84"></el-option>
49
+            <el-option label="CGCS2000" value="CGCS2000"></el-option>
50
+            <el-option label="Beijing54" value="Beijing54"></el-option>
51
+            <el-option label="Xian80" value="Xian80"></el-option>
52
+          </el-select>
53
+        </div>
54
+        
55
+        <div class="params-group">
56
+          <label class="param-label">经度位置</label>
57
+          <el-select v-model="commonParams.longitudePosition" size="small" style="width: 80px;" @change="onParamsChange">
58
+            <el-option label="E" value="E"></el-option>
59
+            <el-option label="W" value="W"></el-option>
60
+          </el-select>
61
+        </div>
62
+        
63
+        <div class="params-group">
64
+          <label class="param-label">纬度位置</label>
65
+          <el-select v-model="commonParams.latitudePosition" size="small" style="width: 80px;" @change="onParamsChange">
66
+            <el-option label="N" value="N"></el-option>
67
+            <el-option label="S" value="S"></el-option>
68
+          </el-select>
69
+        </div>
70
+        
71
+        <div class="params-group">
72
+          <label class="param-label">带号</label>
73
+          <el-input v-model.number="commonParams.band" size="small" style="width: 80px;" placeholder="带号" @change="onParamsChange"></el-input>
74
+        </div>
53 75
       </div>
54 76
     </div>
55 77
 
56
-    <!-- 计算结果展示区 - 只显示计算结果,不包含原始数据 -->
57
-    <div v-if="calculationResults.length > 0" class="result-section">
78
+    <!-- 数据展示区域 - 合并显示导入数据和计算结果 -->
79
+    <div v-if="displayData.length > 0" class="result-section">
58 80
       <div class="section-header">
59 81
         <span class="section-title">
60 82
           <i class="el-icon-tickets"></i>
61
-          计算结果
83
+          {{ calculationResults.length > 0 ? '计算结果' : '导入数据' }}
62 84
         </span>
63 85
         <span class="section-info">
64
-          共 {{ calculationResults.length }} 条计算结果
86
+          共 {{ displayData.length }} 条数据
87
+          <span v-if="calculationResults.length > 0" style="margin-left: 10px;">
88
+            | 坐标系:{{ commonParams.coordinateSystem }}
89
+            | 带号:{{ commonParams.band }}
90
+          </span>
65 91
         </span>
66
-        <el-button type="text" size="small" @click="handleExport" icon="el-icon-download">
92
+        <el-button v-if="calculationResults.length > 0" type="text" size="small" @click="handleExport" icon="el-icon-download">
67 93
           导出结果
68 94
         </el-button>
69 95
       </div>
70 96
       
71
-      <el-table :data="calculationResults" style="width: 100%" border stripe>
97
+      <el-table :data="displayData" style="width: 100%" border stripe>
72 98
         <el-table-column prop="pointNumber" label="待求点号" width="120" align="center"></el-table-column>
73
-        <el-table-column prop="longitude" label="大地经度(°)" width="160" align="center">
99
+        <el-table-column prop="utmX" label="UTM坐标X(m)" width="150" align="center">
74 100
           <template slot-scope="scope">
75
-            <span class="result-value">{{ formatNumber(scope.row.longitude,10) }}</span>
101
+            <span>{{ formatNumber(scope.row.utmX, 4) }}</span>
76 102
           </template>
77 103
         </el-table-column>
78
-        <el-table-column prop="latitude" label="大地纬度(°)" width="160" align="center">
104
+        <el-table-column prop="utmY" label="UTM坐标Y(m)" width="150" align="center">
79 105
           <template slot-scope="scope">
80
-            <span class="result-value">{{ formatNumber(scope.row.latitude,10) }}</span>
106
+            <span>{{ formatNumber(scope.row.utmY, 4) }}</span>
107
+          </template>
108
+        </el-table-column>
109
+        <el-table-column prop="longitude" label="大地经度(° ′ ″)" width="160" align="center">
110
+          <template slot-scope="scope">
111
+            <span v-if="scope.row.longitude" class="result-value">{{ formatNumber(scope.row.longitude, 10) }}</span>
112
+            <span v-else class="empty-cell">-</span>
113
+          </template>
114
+        </el-table-column>
115
+        <el-table-column prop="latitude" label="大地纬度(° ′ ″)" width="160" align="center">
116
+          <template slot-scope="scope">
117
+            <span v-if="scope.row.latitude" class="result-value">{{ formatNumber(scope.row.latitude, 10) }}</span>
118
+            <span v-else class="empty-cell">-</span>
81 119
           </template>
82 120
         </el-table-column>
83 121
       </el-table>
@@ -86,37 +124,12 @@
86 124
     <!-- 空状态 -->
87 125
     <div v-else class="empty-state">
88 126
       <i class="el-icon-document"></i>
89
-      <p>暂无计算结果</p>
90
-      <p class="empty-tip">请先导入Excel数据,然后点击"执行计算"</p>
127
+      <p>暂无数据</p>
128
+      <p class="empty-tip">请先导入Excel数据</p>
91 129
     </div>
92 130
     
93 131
     <!-- 导入Excel对话框 -->
94 132
     <el-dialog title="导入Excel" :visible.sync="importDialogVisible" width="550px" @close="resetImport">
95
-      <div class="import-tips">
96
-        <el-alert
97
-          title="Excel模板格式说明"
98
-          type="info"
99
-          :closable="false"
100
-          show-icon>
101
-          <div class="template-info">
102
-            <p>请确保Excel文件包含以下列(第一行为表头):</p>
103
-            <ul>
104
-              <li><strong>待求点</strong> - 点号</li>
105
-              <li><strong>坐标系</strong> - WGS84/CGCS2000/Beijing54/Xian80</li>
106
-              <li><strong>UTM坐标x</strong> - X坐标值(米)</li>
107
-              <li><strong>UTM坐标y</strong> - Y坐标值(米)</li>
108
-              <li><strong>带号</strong> - 带号数值(UTM使用6度带)</li>
109
-              <li><strong>经度位置</strong> - E/W(东/西)</li>
110
-              <li><strong>纬度位置</strong> - N/S(北/南)</li>
111
-            </ul>
112
-          </div>
113
-        </el-alert>
114
-         <div class="template-download-wrapper">
115
-            <el-button type="primary" plain size="small" @click="handleDownloadTemplate" icon="el-icon-download">
116
-              下载Excel模板
117
-            </el-button>
118
-          </div>
119
-      </div>
120 133
       <div style="text-align: center;">
121 134
         <el-upload
122 135
           class="upload-area"
@@ -142,45 +155,48 @@
142 155
         </el-button>
143 156
       </span>
144 157
     </el-dialog>
145
-    <!-- 预览数据对话框 - 显示导入的原始数据 -->
146
-    <el-dialog title="导入数据预览"  :visible.sync="previewDialogVisible" width="80%">
147
-      <div class="preview-info">
148
-        <span>共 {{ importedData.length }} 条数据</span>
149
-        <span style="margin-left: 20px;">状态:{{ importedData.filter(item => item.calculated).length }} 条已计算,{{ importedData.filter(item => !item.calculated).length }} 条未计算</span>
158
+    
159
+    <!-- 字段映射对话框 -->
160
+    <el-dialog title="字段映射" :visible.sync="mappingDialogVisible" width="500px">
161
+      <div class="mapping-tips">
162
+        <el-alert
163
+          title="字段映射说明"
164
+          type="info"
165
+          :closable="false"
166
+          show-icon>
167
+          请将Excel中的列映射到对应的坐标字段。
168
+        </el-alert>
150 169
       </div>
151
-      <el-table :data="importedData" border stripe max-height="500">
152
-        <el-table-column prop="pointNumber" label="待求点号" width="120" fixed="left" align="center"></el-table-column>
153
-        <el-table-column prop="coordinateSystem" label="坐标系" width="110" align="center">
154
-          <template slot-scope="scope">
155
-            <el-tag size="small" :type="getCoordinateType(scope.row.coordinateSystem)">
156
-              {{ scope.row.coordinateSystem }}
157
-            </el-tag>
158
-          </template>
159
-        </el-table-column>
160
-        <el-table-column prop="utmX" label="UTM坐标X(m)" width="150" align="center">
161
-          <template slot-scope="scope">
162
-            {{ formatNumber(scope.row.utmX,4) }}
163
-          </template>
164
-        </el-table-column>
165
-        <el-table-column prop="utmY" label="UTM坐标Y(m)" width="150" align="center">
166
-          <template slot-scope="scope">
167
-            {{ formatNumber(scope.row.utmY,4) }}
168
-          </template>
169
-        </el-table-column>
170
-        <el-table-column prop="band" label="带号" width="80" align="center"></el-table-column>
171
-        <el-table-column prop="longitudePosition" label="经度位置" width="100" align="center">
172
-          <template slot-scope="scope">
173
-            {{scope.row.longitudePosition}}
174
-          </template>
175
-        </el-table-column>
176
-        <el-table-column prop="latitudePosition" label="纬度位置" width="100" align="center">
177
-          <template slot-scope="scope">
178
-            {{ scope.row.latitudePosition }}
179
-          </template>
180
-        </el-table-column>
181
-      </el-table>
170
+      
171
+      <div class="mapping-section">
172
+        <div class="mapping-row">
173
+          <label class="mapping-label">点号字段</label>
174
+          <el-select v-model="fieldMapping.pointNumber" class="mapping-select" size="small">
175
+            <el-option label="请选择" value=""></el-option>
176
+            <el-option v-for="col in excelColumns" :key="col" :label="col" :value="col"></el-option>
177
+          </el-select>
178
+        </div>
179
+        <div class="mapping-row">
180
+          <label class="mapping-label">UTM坐标X字段</label>
181
+          <el-select v-model="fieldMapping.utmX" class="mapping-select" size="small">
182
+            <el-option label="请选择" value=""></el-option>
183
+            <el-option v-for="col in excelColumns" :key="col" :label="col" :value="col"></el-option>
184
+          </el-select>
185
+        </div>
186
+        <div class="mapping-row">
187
+          <label class="mapping-label">UTM坐标Y字段</label>
188
+          <el-select v-model="fieldMapping.utmY" class="mapping-select" size="small">
189
+            <el-option label="请选择" value=""></el-option>
190
+            <el-option v-for="col in excelColumns" :key="col" :label="col" :value="col"></el-option>
191
+          </el-select>
192
+        </div>
193
+      </div>
194
+      
182 195
       <span slot="footer" class="dialog-footer">
183
-        <el-button type="primary" @click="previewDialogVisible = false">关闭</el-button>
196
+        <el-button @click="cancelMapping">取消</el-button>
197
+        <el-button type="primary" @click="confirmMapping" :disabled="!validateMapping">
198
+          确认映射
199
+        </el-button>
184 200
       </span>
185 201
     </el-dialog>
186 202
  </div>
@@ -199,9 +215,26 @@ export default {
199 215
       importLoading: false,
200 216
       importFile: null,
201 217
       calculating: false,
202
-      previewDialogVisible: false,
203 218
       projectionHeight: 0,
204 219
       heightTimer: null,
220
+      
221
+      // 公共参数
222
+      commonParams: {
223
+        coordinateSystem: 'WGS84',
224
+        longitudePosition: 'E',
225
+        latitudePosition: 'N',
226
+        band: 0
227
+      },
228
+      
229
+      // 字段映射相关
230
+      mappingDialogVisible: false,
231
+      excelColumns: [],
232
+      fieldMapping: {
233
+        pointNumber: '',
234
+        utmX: '',
235
+        utmY: ''
236
+      },
237
+      rawImportedData: []
205 238
     }
206 239
   },
207 240
   watch: {
@@ -244,40 +277,110 @@ export default {
244 277
       const formData = new FormData()
245 278
       formData.append('file', this.importFile)
246 279
       
280
+      try {
247 281
         const response = await importExcel(formData)
248 282
         
249 283
         if (response.code === 200) {
250 284
           const result = response.data
251 285
           
252
-          // 将后端返回的数据转换为前端格式
253
-          this.importedData = (result.data || []).map(item => ({
254
-            pointNumber: item.pointNumber,
255
-            coordinateSystem: item.coordinateSystem,
256
-            utmX: item.utmX,
257
-            utmY: item.utmY,
258
-            band: item.band,
259
-            longitudePosition: item.longitudePosition,
260
-            latitudePosition: item.latitudePosition
261
-          }))
286
+          // 获取Excel列名
287
+          this.excelColumns = result.columns || this.extractColumns(result.data)
288
+          
289
+          // 保存原始数据
290
+          this.rawImportedData = result.data || []
262 291
           
263 292
           // 清空之前的计算结果
264 293
           this.calculationResults = []
265 294
           
295
+          // 自动匹配字段
296
+          this.autoMatchFields()
297
+          
298
+          // 显示字段映射对话框
299
+          this.mappingDialogVisible = true
300
+          
266 301
           this.$message.success(`${response.msg}`)
267 302
           this.importDialogVisible = false
268 303
           this.importLoading = false
269 304
         } else {
270 305
           this.$message.error(response.msg)
306
+          this.importLoading = false
271 307
         }
308
+      } catch (error) {
309
+        this.$message.error('导入失败')
310
+        this.importLoading = false
311
+      }
272 312
     },
273 313
     
274
-    // 预览数据 - 显示导入的原始数据
275
-    handlePreview() {
276
-      if (!this.importedData || this.importedData.length === 0) {
277
-        this.$message.warning('没有可预览的数据')
314
+    // 提取列名
315
+    extractColumns(data) {
316
+      if (!data || data.length === 0) return []
317
+      return Object.keys(data[0])
318
+    },
319
+    
320
+    // 自动匹配字段
321
+    autoMatchFields() {
322
+      this.resetFieldMapping()
323
+      
324
+      const keywordMap = {
325
+        pointNumber: ['点号', '待求点', '点', '编号', '序号', 'ID'],
326
+        utmX: ['UTM坐标X', 'utmX', 'X坐标', 'x', 'X', '东坐标'],
327
+        utmY: ['UTM坐标Y', 'utmY', 'Y坐标', 'y', 'Y', '北坐标']
328
+      }
329
+      
330
+      this.excelColumns.forEach(col => {
331
+        const lowerCol = col.toLowerCase()
332
+        Object.keys(keywordMap).forEach(field => {
333
+          if (!this.fieldMapping[field]) {
334
+            keywordMap[field].forEach(keyword => {
335
+              if (col.includes(keyword) || lowerCol.includes(keyword.toLowerCase())) {
336
+                this.fieldMapping[field] = col
337
+              }
338
+            })
339
+          }
340
+        })
341
+      })
342
+    },
343
+    
344
+    // 确认字段映射
345
+    confirmMapping() {
346
+      if (!this.validateMapping) {
347
+        this.$message.warning('请完成所有必填字段的映射')
278 348
         return
279 349
       }
280
-      this.previewDialogVisible = true
350
+      
351
+      // 根据映射转换数据
352
+      this.importedData = this.rawImportedData.map(item => ({
353
+        pointNumber: item[this.fieldMapping.pointNumber],
354
+        utmX: item[this.fieldMapping.utmX],
355
+        utmY: item[this.fieldMapping.utmY]
356
+      }))
357
+      
358
+      this.mappingDialogVisible = false
359
+      this.resetFieldMapping()
360
+    },
361
+    
362
+    // 取消字段映射
363
+    cancelMapping() {
364
+      this.mappingDialogVisible = false
365
+      this.resetFieldMapping()
366
+      this.rawImportedData = []
367
+    },
368
+    
369
+    // 重置字段映射
370
+    resetFieldMapping() {
371
+      this.fieldMapping = {
372
+        pointNumber: '',
373
+        utmX: '',
374
+        utmY: ''
375
+      }
376
+    },
377
+    
378
+    // 参数变化时清空计算结果
379
+    onParamsChange() {
380
+      if (this.calculationResults.length > 0) {
381
+        this.calculationResults = []
382
+        this.$message.info('参数已更新,请重新执行计算')
383
+      }
281 384
     },
282 385
     
283 386
     // 下载模板
@@ -294,19 +397,16 @@ export default {
294 397
       }
295 398
       
296 399
       this.calculating = true
297
-      const results = []
298
-      let successCount = 0
299
-      let failCount = 0
300 400
       
301 401
       // 构建请求参数
302 402
       const requestData = this.importedData.map(item => ({
303 403
         pointNumber: item.pointNumber,
304
-        coordinateSystem: item.coordinateSystem,
404
+        coordinateSystem: this.commonParams.coordinateSystem,
305 405
         utmX: parseFloat(item.utmX),
306 406
         utmY: parseFloat(item.utmY),
307
-        band: parseInt(item.band),
308
-        longitudePosition: item.longitudePosition,
309
-        latitudePosition: item.latitudePosition,
407
+        band: this.commonParams.band,
408
+        longitudePosition: this.commonParams.longitudePosition,
409
+        latitudePosition: this.commonParams.latitudePosition,
310 410
         projectionHeight: this.projectionHeight
311 411
       }))
312 412
       
@@ -321,29 +421,23 @@ export default {
321 421
           coordinateSystem: item.coordinateSystem,
322 422
           utmX: item.utmX,
323 423
           utmY: item.utmY,
324
-          band: parseInt(item.band),
325 424
           longitude: item.longitude,
326
-          longitudePosition: item.longitudePosition,
327 425
           latitude: item.latitude,
328
-          latitudePosition: item.latitudePosition
426
+          band: item.band,
427
+          longitudePosition: item.longitudePosition,
428
+          latitudePosition: item.latitudePosition,
429
+          projectionHeight: item.projectionHeight,
329 430
         }))
330 431
         
331
-        // 更新导入数据的计算状态
332
-        this.importedData.forEach(importItem => {
333
-          const found = results.find(r => r.pointNumber === importItem.pointNumber)
334
-          if (found && found.longitude !== -1) {
335
-            importItem.calculated = true
336
-          }
337
-        })
338
-        
339
-        const successCount = results.filter(r => r.longitude !== -1).length
432
+        const successCount = results.filter(r => r.longitude !== undefined && r.longitude !== -1).length
340 433
         const failCount = results.length - successCount
341 434
         
342 435
         this.$message.success(`计算完成,成功 ${successCount} 条${failCount > 0 ? `,失败 ${failCount} 条` : ''}`)
343
-        this.calculating = false
344
-        } else {
345
-          this.$message.error(response.msg)
346
-        }
436
+      } else {
437
+        this.$message.error(response.msg)
438
+      }
439
+      
440
+      this.calculating = false
347 441
     },
348 442
     
349 443
     
@@ -409,6 +503,40 @@ export default {
409 503
     }
410 504
     return types[system] || 'info'
411 505
   }
506
+  },
507
+  
508
+  computed: {
509
+    // 验证字段映射
510
+    validateMapping() {
511
+      if (!this.fieldMapping) {
512
+        return false
513
+      }
514
+      return this.fieldMapping.pointNumber && 
515
+             this.fieldMapping.utmX && 
516
+             this.fieldMapping.utmY
517
+    },
518
+    
519
+    // 显示数据:优先显示计算结果,否则显示导入数据
520
+    displayData() {
521
+      if (this.calculationResults.length > 0) {
522
+        return this.calculationResults.map(item => ({
523
+          pointNumber: item.pointNumber,
524
+          utmX: item.utmX,
525
+          utmY: item.utmY,
526
+          longitude: item.longitude,
527
+          latitude: item.latitude
528
+        }))
529
+      } else if (this.importedData.length > 0) {
530
+        return this.importedData.map(item => ({
531
+          pointNumber: item.pointNumber,
532
+          utmX: item.utmX,
533
+          utmY: item.utmY,
534
+          longitude: undefined,
535
+          latitude: undefined
536
+        }))
537
+      }
538
+      return []
539
+    }
412 540
   }
413 541
 }
414 542
 </script>
@@ -428,17 +556,37 @@ export default {
428 556
   flex-wrap: wrap;
429 557
 }
430 558
 
431
-/* 投影面高程设置样式 */
432
-.height-setting {
559
+/* 参数设置区域样式 */
560
+.params-area {
433 561
   background: #f5f7fa;
434 562
   border-radius: 8px;
435
-  padding: 12px 20px;
563
+  padding: 16px 20px;
436 564
   margin-bottom: 20px;
437 565
   border: 1px solid #e4e7ed;
566
+}
567
+
568
+.params-row {
438 569
   display: flex;
439
-  align-items: center;
440
-  gap: 20px;
441 570
   flex-wrap: wrap;
571
+  gap: 24px;
572
+  align-items: center;
573
+}
574
+
575
+.params-group {
576
+  display: flex;
577
+  align-items: center;
578
+  gap: 8px;
579
+}
580
+
581
+.param-label {
582
+  font-weight: 500;
583
+  color: #606266;
584
+  min-width: 40px;
585
+}
586
+
587
+.unit {
588
+  color: #909399;
589
+  font-size: 13px;
442 590
 }
443 591
 
444 592
 .result-section {
@@ -545,4 +693,41 @@ export default {
545 693
   padding-top: 12px;
546 694
   border-top: 1px dashed #e4e7ed;
547 695
 }
696
+
697
+/* 字段映射对话框样式 */
698
+.mapping-tips {
699
+  margin-bottom: 20px;
700
+}
701
+
702
+.mapping-section {
703
+  background: #f9fafb;
704
+  border-radius: 8px;
705
+  padding: 16px;
706
+}
707
+
708
+.mapping-row {
709
+  display: flex;
710
+  align-items: center;
711
+  margin-bottom: 12px;
712
+}
713
+
714
+.mapping-row:last-child {
715
+  margin-bottom: 0;
716
+}
717
+
718
+.mapping-label {
719
+  width: 120px;
720
+  font-weight: 500;
721
+  color: #606266;
722
+}
723
+
724
+.mapping-select {
725
+  flex: 1;
726
+  min-width: 200px;
727
+}
728
+
729
+.empty-cell {
730
+  color: #C0C4CC;
731
+  font-style: italic;
732
+}
548 733
 </style>

+ 366
- 179
oa-ui/src/views/calculate/tools/utmpositive.vue Datei anzeigen

@@ -8,14 +8,6 @@
8 8
           导入Excel
9 9
         </el-button>
10 10
 
11
-        <el-button 
12
-          type="success" 
13
-          @click="handlePreview" 
14
-          size="small" 
15
-          :disabled="!importedData || importedData.length === 0"
16
-          icon="el-icon-view">
17
-          预览数据
18
-        </el-button>
19 11
         <el-button 
20 12
           type="warning" 
21 13
           @click="handleCalculate" 
@@ -37,52 +29,99 @@
37 29
       </div>
38 30
     </div>
39 31
     
40
-    <!-- 投影面高程设置 -->
41
-    <div class="height-setting">
42
-      <div class="setting-label">
43
-        <span>投影面高程</span>
44
-      </div>
45
-      <div class="setting-content">
46
-        <el-input 
47
-          v-model="projectionHeight" 
48
-          size="small" 
49
-          style="width: 120px; margin-right: 10px;">
50
-        </el-input>
51
-        <span class="unit">米 (m)</span>
32
+    <!-- 参数设置区域 -->
33
+    <div class="params-area">
34
+      <div class="params-row">
35
+        <div class="params-group">
36
+          <label class="param-label">投影面高程</label>
37
+          <el-input 
38
+            v-model="projectionHeight" 
39
+            size="small" 
40
+            style="width: 120px;">
41
+          </el-input>
42
+          <span class="unit">米 (m)</span>
43
+        </div>
52 44
         
45
+        <div class="params-group">
46
+          <label class="param-label">坐标系</label>
47
+          <el-select v-model="commonParams.coordinateSystem" size="small" style="width: 140px;" @change="onParamsChange">
48
+            <el-option label="WGS84" value="WGS84"></el-option>
49
+            <el-option label="CGCS2000" value="CGCS2000"></el-option>
50
+            <el-option label="Beijing54" value="Beijing54"></el-option>
51
+            <el-option label="Xian80" value="Xian80"></el-option>
52
+          </el-select>
53
+        </div>
54
+        
55
+        <div class="params-group">
56
+          <label class="param-label">经度位置</label>
57
+          <el-select v-model="commonParams.longitudePosition" size="small" style="width: 80px;" @change="onParamsChange">
58
+            <el-option label="E" value="E"></el-option>
59
+            <el-option label="W" value="W"></el-option>
60
+          </el-select>
61
+        </div>
62
+        
63
+        <div class="params-group">
64
+          <label class="param-label">纬度位置</label>
65
+          <el-select v-model="commonParams.latitudePosition" size="small" style="width: 80px;" @change="onParamsChange">
66
+            <el-option label="N" value="N"></el-option>
67
+            <el-option label="S" value="S"></el-option>
68
+          </el-select>
69
+        </div>
70
+        
71
+        <div class="params-group">
72
+          <label class="param-label">带号</label>
73
+          <el-input v-model.number="commonParams.band" size="small" style="width: 80px;" placeholder="带号" @change="onParamsChange"></el-input>
74
+        </div>
53 75
       </div>
54 76
     </div>
55 77
 
56
-    <!-- 计算结果展示区 - 只显示计算结果,不包含原始数据 -->
57
-    <div v-if="calculationResults.length > 0" class="result-section">
78
+    <!-- 数据展示区域 - 合并显示导入数据和计算结果 -->
79
+    <div v-if="displayData.length > 0" class="result-section">
58 80
       <div class="section-header">
59 81
         <span class="section-title">
60 82
           <i class="el-icon-tickets"></i>
61
-          计算结果
83
+          {{ calculationResults.length > 0 ? '计算结果' : '导入数据' }}
62 84
         </span>
63 85
         <span class="section-info">
64
-          共 {{ calculationResults.length }} 条计算结果
86
+          共 {{ displayData.length }} 条数据
87
+          <span v-if="calculationResults.length > 0" style="margin-left: 10px;">
88
+            | 坐标系:{{ commonParams.coordinateSystem }}
89
+            | 带号:{{ commonParams.band }}
90
+          </span>
65 91
         </span>
66
-        <el-button type="text" size="small" @click="handleExport" icon="el-icon-download">
92
+        <el-button v-if="calculationResults.length > 0" type="text" size="small" @click="handleExport" icon="el-icon-download">
67 93
           导出结果
68 94
         </el-button>
69 95
       </div>
70 96
       
71
-      <el-table :data="calculationResults" style="width: 100%" border stripe>
97
+      <el-table :data="displayData" style="width: 100%" border stripe>
72 98
         <el-table-column prop="pointNumber" label="待求点号" width="120" align="center"></el-table-column>
73
-        <el-table-column prop="utmX" label="UTM 坐标X(m)" width="180" align="center">
99
+        <el-table-column prop="longitude" label="大地经度(° ′ ″)" width="150" align="center">
100
+          <template slot-scope="scope">
101
+            <span>{{ formatNumber(scope.row.longitude, 6) }}</span>
102
+          </template>
103
+        </el-table-column>
104
+        <el-table-column prop="latitude" label="大地纬度(° ′ ″)" width="150" align="center">
74 105
           <template slot-scope="scope">
75
-            <span class="result-value">{{ formatNumber(scope.row.utmX,4) }}</span>
106
+            <span>{{ formatNumber(scope.row.latitude, 6) }}</span>
76 107
           </template>
77 108
         </el-table-column>
78
-        <el-table-column prop="utmY" label="UTM 坐标Y(m)" width="180" align="center">
109
+        <el-table-column prop="utmX" label="UTM 坐标X(m)" width="150" align="center">
79 110
           <template slot-scope="scope">
80
-            <span class="result-value">{{ formatNumber(scope.row.utmY,4) }}</span>
111
+            <span v-if="scope.row.utmX" class="result-value">{{ formatNumber(scope.row.utmX,4) }}</span>
112
+            <span v-else class="empty-cell">-</span>
81 113
           </template>
82 114
         </el-table-column>
83
-        <el-table-column prop="meridianConvergence" label="子午线收敛角γ(°)" width="200" align="center">
115
+        <el-table-column prop="utmY" label="UTM 坐标Y(m)" width="150" align="center">
84 116
           <template slot-scope="scope">
85
-            <span class="result-value">{{ formatNumber(scope.row.meridianConvergence, 7) }}</span>
117
+            <span v-if="scope.row.utmY" class="result-value">{{ formatNumber(scope.row.utmY,4) }}</span>
118
+            <span v-else class="empty-cell">-</span>
119
+          </template>
120
+        </el-table-column>
121
+        <el-table-column prop="meridianConvergence" label="子午线收敛角γ(°)" width="180" align="center">
122
+          <template slot-scope="scope">
123
+            <span v-if="scope.row.meridianConvergence" class="result-value">{{ formatNumber(scope.row.meridianConvergence, 7) }}</span>
124
+            <span v-else class="empty-cell">-</span>
86 125
           </template>
87 126
         </el-table-column>
88 127
       </el-table>
@@ -91,37 +130,12 @@
91 130
     <!-- 空状态 -->
92 131
     <div v-else class="empty-state">
93 132
       <i class="el-icon-document"></i>
94
-      <p>暂无计算结果</p>
95
-      <p class="empty-tip">请先导入Excel数据,然后点击"执行计算"</p>
133
+      <p>暂无数据</p>
134
+      <p class="empty-tip">请先导入Excel数据</p>
96 135
     </div>
97 136
     
98 137
     <!-- 导入Excel对话框 -->
99 138
     <el-dialog title="导入Excel" :visible.sync="importDialogVisible" width="550px" @close="resetImport">
100
-      <div class="import-tips">
101
-        <el-alert
102
-          title="Excel模板格式说明"
103
-          type="info"
104
-          :closable="false"
105
-          show-icon>
106
-          <div class="template-info">
107
-            <p>请确保Excel文件包含以下列(第一行为表头):</p>
108
-            <ul>
109
-              <li><strong>待求点</strong> - 点号</li>
110
-              <li><strong>坐标系</strong> - WGS84/CGCS2000/Beijing54/Xian80</li>
111
-              <li><strong>大地经度</strong> - 经度数值(° ′ ″)</li>
112
-              <li><strong>经度位置</strong> - E/W</li>
113
-              <li><strong>大地纬度</strong> - 纬度数值(° ′ ″)</li>
114
-              <li><strong>纬度位置</strong> - N/S</li>
115
-              <li><strong>带号</strong> - 带号数值(UTM使用6度带)</li>
116
-            </ul>
117
-          </div>
118
-        </el-alert>
119
-         <div class="template-download-wrapper">
120
-            <el-button type="primary" plain size="small" @click="handleDownloadTemplate" icon="el-icon-download">
121
-              下载Excel模板
122
-            </el-button>
123
-          </div>
124
-      </div>
125 139
       <div style="text-align: center;">
126 140
         <el-upload
127 141
           class="upload-area"
@@ -147,45 +161,48 @@
147 161
         </el-button>
148 162
       </span>
149 163
     </el-dialog>
150
-    <!-- 预览数据对话框 - 显示导入的原始数据 -->
151
-    <el-dialog title="导入数据预览"  :visible.sync="previewDialogVisible" width="80%">
152
-      <div class="preview-info">
153
-        <span>共 {{ importedData.length }} 条数据</span>
154
-        <span style="margin-left: 20px;">状态:{{ importedData.filter(item => item.calculated).length }} 条已计算,{{ importedData.filter(item => !item.calculated).length }} 条未计算</span>
164
+    
165
+    <!-- 字段映射对话框 -->
166
+    <el-dialog title="字段映射" :visible.sync="mappingDialogVisible" width="500px">
167
+      <div class="mapping-tips">
168
+        <el-alert
169
+          title="字段映射说明"
170
+          type="info"
171
+          :closable="false"
172
+          show-icon>
173
+          请将Excel中的列映射到对应的坐标字段。
174
+        </el-alert>
155 175
       </div>
156
-      <el-table :data="importedData" border stripe max-height="500">
157
-        <el-table-column prop="pointNumber" label="待求点号" width="120" fixed="left" align="center"></el-table-column>
158
-        <el-table-column prop="coordinateSystem" label="坐标系" width="110" align="center">
159
-          <template slot-scope="scope">
160
-            <el-tag size="small" :type="getCoordinateType(scope.row.coordinateSystem)">
161
-              {{ scope.row.coordinateSystem }}
162
-            </el-tag>
163
-          </template>
164
-        </el-table-column>
165
-        <el-table-column prop="longitude" label="大地经度" width="130" align="center">
166
-          <template slot-scope="scope">
167
-            {{ scope.row.longitude }}
168
-          </template>
169
-        </el-table-column>
170
-         <el-table-column prop="longitudePosition" label="经度位置" width="130" align="center">
171
-          <template slot-scope="scope">
172
-            {{ scope.row.longitudePosition}}
173
-          </template>
174
-        </el-table-column>
175
-        <el-table-column prop="latitude" label="大地纬度" width="130" align="center">
176
-          <template slot-scope="scope">
177
-            {{ scope.row.latitude }}
178
-          </template>
179
-        </el-table-column>
180
-         <el-table-column prop="latitudePosition" label="纬度位置" width="130" align="center">
181
-          <template slot-scope="scope">
182
-            {{ scope.row.latitudePosition}}
183
-                   </template>
184
-        </el-table-column>
185
-        <el-table-column prop="band" label="带号" width="80" align="center"></el-table-column>
186
-      </el-table>
176
+      
177
+      <div class="mapping-section">
178
+        <div class="mapping-row">
179
+          <label class="mapping-label">点号字段</label>
180
+          <el-select v-model="fieldMapping.pointNumber" class="mapping-select" size="small">
181
+            <el-option label="请选择" value=""></el-option>
182
+            <el-option v-for="col in excelColumns" :key="col" :label="col" :value="col"></el-option>
183
+          </el-select>
184
+        </div>
185
+        <div class="mapping-row">
186
+          <label class="mapping-label">经度字段</label>
187
+          <el-select v-model="fieldMapping.longitude" class="mapping-select" size="small">
188
+            <el-option label="请选择" value=""></el-option>
189
+            <el-option v-for="col in excelColumns" :key="col" :label="col" :value="col"></el-option>
190
+          </el-select>
191
+        </div>
192
+        <div class="mapping-row">
193
+          <label class="mapping-label">纬度字段</label>
194
+          <el-select v-model="fieldMapping.latitude" class="mapping-select" size="small">
195
+            <el-option label="请选择" value=""></el-option>
196
+            <el-option v-for="col in excelColumns" :key="col" :label="col" :value="col"></el-option>
197
+          </el-select>
198
+        </div>
199
+      </div>
200
+      
187 201
       <span slot="footer" class="dialog-footer">
188
-        <el-button type="primary" @click="previewDialogVisible = false">关闭</el-button>
202
+        <el-button @click="cancelMapping">取消</el-button>
203
+        <el-button type="primary" @click="confirmMapping" :disabled="!validateMapping">
204
+          确认映射
205
+        </el-button>
189 206
       </span>
190 207
     </el-dialog>
191 208
  </div>
@@ -204,9 +221,26 @@ export default {
204 221
       importLoading: false,
205 222
       importFile: null,
206 223
       calculating: false,
207
-      previewDialogVisible: false,
208 224
       projectionHeight: 0,
209 225
       heightTimer: null,
226
+      
227
+      // 公共参数
228
+      commonParams: {
229
+        coordinateSystem: 'WGS84',
230
+        longitudePosition: 'E',
231
+        latitudePosition: 'N',
232
+        band: 0
233
+      },
234
+      
235
+      // 字段映射相关
236
+      mappingDialogVisible: false,
237
+      excelColumns: [],
238
+      fieldMapping: {
239
+        pointNumber: '',
240
+        longitude: '',
241
+        latitude: ''
242
+      },
243
+      rawImportedData: []
210 244
     }
211 245
   },
212 246
   watch: {
@@ -249,40 +283,110 @@ export default {
249 283
       const formData = new FormData()
250 284
       formData.append('file', this.importFile)
251 285
       
286
+      try {
252 287
         const response = await importExcel(formData)
253 288
         
254 289
         if (response.code === 200) {
255 290
           const result = response.data
291
+
292
+          // 获取Excel列名
293
+          this.excelColumns = result.columns || this.extractColumns(result.data)
256 294
           
257
-          // 将后端返回的数据转换为前端格式
258
-          this.importedData = (result.data || []).map(item => ({
259
-            pointNumber: item.pointNumber,
260
-            coordinateSystem: item.coordinateSystem,
261
-            longitude: item.longitude,
262
-            longitudePosition: item.longitudePosition,
263
-            latitude: item.latitude,
264
-            latitudePosition: item.latitudePosition,
265
-            band: item.band
266
-          }))
295
+          // 保存原始数据
296
+          this.rawImportedData = result.data || []
267 297
           
268 298
           // 清空之前的计算结果
269 299
           this.calculationResults = []
270 300
           
301
+          // 自动匹配字段
302
+          this.autoMatchFields()
303
+          
304
+          // 显示字段映射对话框
305
+          this.mappingDialogVisible = true
306
+          
271 307
           this.$message.success(`${response.msg}`)
272 308
           this.importDialogVisible = false
273 309
           this.importLoading = false
274 310
         } else {
275 311
           this.$message.error(response.msg)
312
+          this.importLoading = false
276 313
         }
314
+      } catch (error) {
315
+        this.$message.error('导入失败')
316
+        this.importLoading = false
317
+      }
277 318
     },
278 319
     
279
-    // 预览数据 - 显示导入的原始数据
280
-    handlePreview() {
281
-      if (!this.importedData || this.importedData.length === 0) {
282
-        this.$message.warning('没有可预览的数据')
320
+    // 提取列名
321
+    extractColumns(data) {
322
+      if (!data || data.length === 0) return []
323
+      return Object.keys(data[0])
324
+    },
325
+    
326
+    // 自动匹配字段
327
+    autoMatchFields() {
328
+      this.resetFieldMapping()
329
+      
330
+      const keywordMap = {
331
+        pointNumber: ['点号', '待求点', '点', '编号', '序号', 'ID'],
332
+        longitude: ['大地经度', 'lon', 'Longitude', 'L', '东经', '西经'],
333
+        latitude: ['大地纬度', 'lat', 'Latitude', 'B', '北纬', '南纬']
334
+      }
335
+      
336
+      this.excelColumns.forEach(col => {
337
+        const lowerCol = col.toLowerCase()
338
+        Object.keys(keywordMap).forEach(field => {
339
+          if (!this.fieldMapping[field]) {
340
+            keywordMap[field].forEach(keyword => {
341
+              if (col.includes(keyword) || lowerCol.includes(keyword.toLowerCase())) {
342
+                this.fieldMapping[field] = col
343
+              }
344
+            })
345
+          }
346
+        })
347
+      })
348
+    },
349
+    
350
+    // 确认字段映射
351
+    confirmMapping() {
352
+      if (!this.validateMapping) {
353
+        this.$message.warning('请完成所有必填字段的映射')
283 354
         return
284 355
       }
285
-      this.previewDialogVisible = true
356
+      
357
+      // 根据映射转换数据
358
+      this.importedData = this.rawImportedData.map(item => ({
359
+        pointNumber: item[this.fieldMapping.pointNumber],
360
+        longitude: item[this.fieldMapping.longitude],
361
+        latitude: item[this.fieldMapping.latitude]
362
+      }))
363
+      
364
+      this.mappingDialogVisible = false
365
+      this.resetFieldMapping()
366
+    },
367
+    
368
+    // 取消字段映射
369
+    cancelMapping() {
370
+      this.mappingDialogVisible = false
371
+      this.resetFieldMapping()
372
+      this.rawImportedData = []
373
+    },
374
+    
375
+    // 重置字段映射
376
+    resetFieldMapping() {
377
+      this.fieldMapping = {
378
+        pointNumber: '',
379
+        longitude: '',
380
+        latitude: ''
381
+      }
382
+    },
383
+    
384
+    // 参数变化时清空计算结果
385
+    onParamsChange() {
386
+      if (this.calculationResults.length > 0) {
387
+        this.calculationResults = []
388
+        this.$message.info('参数已更新,请重新执行计算')
389
+      }
286 390
     },
287 391
     
288 392
     // 下载模板
@@ -299,19 +403,16 @@ export default {
299 403
       }
300 404
       
301 405
       this.calculating = true
302
-      const results = []
303
-      let successCount = 0
304
-      let failCount = 0
305 406
       
306 407
       // 构建请求参数
307 408
       const requestData = this.importedData.map(item => ({
308 409
         pointNumber: item.pointNumber,
309
-        coordinateSystem: item.coordinateSystem,
410
+        coordinateSystem: this.commonParams.coordinateSystem,
310 411
         longitude: parseFloat(item.longitude),
311
-        longitudePosition: item.longitudePosition,
412
+        longitudePosition: this.commonParams.longitudePosition,
312 413
         latitude: parseFloat(item.latitude),
313
-        latitudePosition: item.latitudePosition,
314
-        band: parseInt(item.band),
414
+        latitudePosition: this.commonParams.latitudePosition,
415
+        band: this.commonParams.band,
315 416
         projectionHeight: this.projectionHeight
316 417
       }))
317 418
       
@@ -325,32 +426,25 @@ export default {
325 426
           pointNumber: item.pointNumber,
326 427
           coordinateSystem: item.coordinateSystem,
327 428
           longitude: parseFloat(item.longitude),
328
-          longitudePosition: item.longitudePosition,
329 429
           latitude: parseFloat(item.latitude),
330
-          latitudePosition: item.latitudePosition,
331
-          band: parseInt(item.band),
332
-          projectionHeight: this.projectionHeight,
333 430
           utmX: item.utmX,
334 431
           utmY: item.utmY,
335
-          meridianConvergence: item.meridianConvergence
432
+          meridianConvergence: item.meridianConvergence,
433
+          band: item.band,
434
+          longitudePosition: item.longitudePosition,
435
+          latitudePosition: item.latitudePosition,
436
+          projectionHeight: item.projectionHeight,
336 437
         }))
337 438
         
338
-        // 更新导入数据的计算状态
339
-        this.importedData.forEach(importItem => {
340
-          const found = results.find(r => r.pointNumber === importItem.pointNumber)
341
-          if (found && found.utmX !== -1) {
342
-            importItem.calculated = true
343
-          }
344
-        })
345
-        
346
-        const successCount = results.filter(r => r.utmX !== -1).length
439
+        const successCount = results.filter(r => r.utmX !== undefined && r.utmX !== -1).length
347 440
         const failCount = results.length - successCount
348 441
         
349 442
         this.$message.success(`计算完成,成功 ${successCount} 条${failCount > 0 ? `,失败 ${failCount} 条` : ''}`)
350
-        this.calculating = false
351
-        } else {
352
-          this.$message.error(response.msg)
353
-        }
443
+      } else {
444
+        this.$message.error(response.msg)
445
+      }
446
+      
447
+      this.calculating = false
354 448
     },
355 449
     
356 450
     
@@ -375,47 +469,83 @@ export default {
375 469
         window.URL.revokeObjectURL(url)
376 470
   },
377 471
     
378
-  // 清空全部
379
-  clearAll() {
380
-    if ((!this.importedData || this.importedData.length === 0) && this.calculationResults.length === 0) {
381
-      return
382
-    }
472
+    // 清空全部
473
+    clearAll() {
474
+      if ((!this.importedData || this.importedData.length === 0) && this.calculationResults.length === 0) {
475
+        return
476
+      }
477
+      
478
+      this.$confirm('确定清空所有数据吗?此操作不可恢复!', '警告', {
479
+        confirmButtonText: '确定',
480
+        cancelButtonText: '取消',
481
+        type: 'warning'
482
+      }).then(() => {
483
+        this.importedData = []
484
+        this.calculationResults = []
485
+        this.$message.success('已清空所有数据')
486
+      }).catch(() => {})
487
+    },
383 488
     
384
-    this.$confirm('确定清空所有数据吗?此操作不可恢复!', '警告', {
385
-      confirmButtonText: '确定',
386
-      cancelButtonText: '取消',
387
-      type: 'warning'
388
-    }).then(() => {
389
-      this.importedData = []
390
-      this.calculationResults = []
391
-      this.$message.success('已清空所有数据')
392
-    }).catch(() => {})
393
-  },
394
-  
395
-  // 重置导入状态
396
-  resetImport() {
397
-    this.importFile = null
398
-    if (this.$refs.upload) {
399
-      this.$refs.upload.clearFiles()
400
-    }
401
-  },
402
-  
403
-  // 格式化数字显示
404
-  formatNumber(value, decimals = 3) {
405
-    if (value === null || value === undefined) return '--'
406
-    return Number(value).toFixed(decimals)
489
+    // 重置导入状态
490
+    resetImport() {
491
+      this.importFile = null
492
+      if (this.$refs.upload) {
493
+        this.$refs.upload.clearFiles()
494
+      }
495
+    },
496
+    
497
+    // 格式化数字显示
498
+    formatNumber(value, decimals = 3) {
499
+      if (value === null || value === undefined) return '--'
500
+      return Number(value).toFixed(decimals)
501
+    },
502
+    
503
+    // 获取坐标系标签类型
504
+    getCoordinateType(system) {
505
+      const types = {
506
+        'WGS84': 'primary',
507
+        'CGCS2000': 'success',
508
+        'Beijing54': 'warning',
509
+        'Xian80': 'info'
510
+      }
511
+      return types[system] || 'info'
512
+    },
407 513
   },
408 514
   
409
-  // 获取坐标系标签类型
410
-  getCoordinateType(system) {
411
-    const types = {
412
-      'WGS84': 'primary',
413
-      'CGCS2000': 'success',
414
-      'Beijing54': 'warning',
415
-      'Xian80': 'info'
515
+  computed: {
516
+    // 验证字段映射
517
+    validateMapping() {
518
+      if (!this.fieldMapping) {
519
+        return false
520
+      }
521
+      return this.fieldMapping.pointNumber && 
522
+             this.fieldMapping.longitude && 
523
+             this.fieldMapping.latitude
524
+    },
525
+    
526
+    // 显示数据:优先显示计算结果,否则显示导入数据
527
+    displayData() {
528
+      if (this.calculationResults.length > 0) {
529
+        return this.calculationResults.map(item => ({
530
+          pointNumber: item.pointNumber,
531
+          longitude: item.longitude,
532
+          latitude: item.latitude,
533
+          utmX: item.utmX,
534
+          utmY: item.utmY,
535
+          meridianConvergence: item.meridianConvergence
536
+        }))
537
+      } else if (this.importedData.length > 0) {
538
+        return this.importedData.map(item => ({
539
+          pointNumber: item.pointNumber,
540
+          longitude: item.longitude,
541
+          latitude: item.latitude,
542
+          utmX: undefined,
543
+          utmY: undefined,
544
+          meridianConvergence: undefined
545
+        }))
546
+      }
547
+      return []
416 548
     }
417
-    return types[system] || 'info'
418
-  }
419 549
   }
420 550
 }
421 551
 </script>
@@ -435,17 +565,37 @@ export default {
435 565
   flex-wrap: wrap;
436 566
 }
437 567
 
438
-/* 投影面高程设置样式 */
439
-.height-setting {
568
+/* 参数设置区域样式 */
569
+.params-area {
440 570
   background: #f5f7fa;
441 571
   border-radius: 8px;
442
-  padding: 12px 20px;
572
+  padding: 16px 20px;
443 573
   margin-bottom: 20px;
444 574
   border: 1px solid #e4e7ed;
575
+}
576
+
577
+.params-row {
445 578
   display: flex;
446
-  align-items: center;
447
-  gap: 20px;
448 579
   flex-wrap: wrap;
580
+  gap: 24px;
581
+  align-items: center;
582
+}
583
+
584
+.params-group {
585
+  display: flex;
586
+  align-items: center;
587
+  gap: 8px;
588
+}
589
+
590
+.param-label {
591
+  font-weight: 500;
592
+  color: #606266;
593
+  min-width: 40px;
594
+}
595
+
596
+.unit {
597
+  color: #909399;
598
+  font-size: 13px;
449 599
 }
450 600
 
451 601
 .result-section {
@@ -552,4 +702,41 @@ export default {
552 702
   padding-top: 12px;
553 703
   border-top: 1px dashed #e4e7ed;
554 704
 }
705
+
706
+/* 字段映射对话框样式 */
707
+.mapping-tips {
708
+  margin-bottom: 20px;
709
+}
710
+
711
+.mapping-section {
712
+  background: #f9fafb;
713
+  border-radius: 8px;
714
+  padding: 16px;
715
+}
716
+
717
+.mapping-row {
718
+  display: flex;
719
+  align-items: center;
720
+  margin-bottom: 12px;
721
+}
722
+
723
+.mapping-row:last-child {
724
+  margin-bottom: 0;
725
+}
726
+
727
+.mapping-label {
728
+  width: 80px;
729
+  font-weight: 500;
730
+  color: #606266;
731
+}
732
+
733
+.mapping-select {
734
+  flex: 1;
735
+  min-width: 200px;
736
+}
737
+
738
+.empty-cell {
739
+  color: #C0C4CC;
740
+  font-style: italic;
741
+}
555 742
 </style>

Laden…
Abbrechen
Speichern