Selaa lähdekoodia

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

qyx 2 päivää sitten
vanhempi
commit
3d747a6741
59 muutettua tiedostoa jossa 7494 lisäystä ja 2549 poistoa
  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 Näytä tiedosto

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 Näytä tiedosto

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 Näytä tiedosto

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 Näytä tiedosto

19
 import com.ruoyi.common.utils.poi.ExcelUtil;
19
 import com.ruoyi.common.utils.poi.ExcelUtil;
20
 import com.ruoyi.web.calculate.domain.CmcGaussBandChange;
20
 import com.ruoyi.web.calculate.domain.CmcGaussBandChange;
21
 import com.ruoyi.web.calculate.service.ICmcGaussBandChangeService;
21
 import com.ruoyi.web.calculate.service.ICmcGaussBandChangeService;
22
-import com.ruoyi.web.calculate.vo.CmcGaussBandChangeTemplate;
23
 
22
 
24
 @RestController
23
 @RestController
25
 @RequestMapping("/calculate/gaussBandChange")
24
 @RequestMapping("/calculate/gaussBandChange")
96
         return AjaxResult.success(result);
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 Näytä tiedosto

19
 import com.ruoyi.common.utils.poi.ExcelUtil;
19
 import com.ruoyi.common.utils.poi.ExcelUtil;
20
 import com.ruoyi.web.calculate.domain.CmcGaussNegative;
20
 import com.ruoyi.web.calculate.domain.CmcGaussNegative;
21
 import com.ruoyi.web.calculate.service.ICmcGaussNegativeService;
21
 import com.ruoyi.web.calculate.service.ICmcGaussNegativeService;
22
-import com.ruoyi.web.calculate.vo.CmcGaussNegativeTemplate;
23
 
22
 
24
 
23
 
25
 @RestController
24
 @RestController
98
         List<CmcGaussNegative> result = cmcGaussNegativeService.calculateGaussNegative(dataList);
97
         List<CmcGaussNegative> result = cmcGaussNegativeService.calculateGaussNegative(dataList);
99
         return AjaxResult.success(result);
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 Näytä tiedosto

22
 import com.ruoyi.common.enums.BusinessType;
22
 import com.ruoyi.common.enums.BusinessType;
23
 import com.ruoyi.web.calculate.domain.CmcGaussPositive;
23
 import com.ruoyi.web.calculate.domain.CmcGaussPositive;
24
 import com.ruoyi.web.calculate.service.ICmcGaussPositiveService;
24
 import com.ruoyi.web.calculate.service.ICmcGaussPositiveService;
25
-import com.ruoyi.web.calculate.vo.CmcGaussPositiveTemplate;
26
 import com.ruoyi.common.utils.poi.ExcelUtil;
25
 import com.ruoyi.common.utils.poi.ExcelUtil;
27
 import com.ruoyi.common.core.page.TableDataInfo;
26
 import com.ruoyi.common.core.page.TableDataInfo;
28
 
27
 
108
         List<CmcGaussPositive> result = cmcGaussPositiveService.calculateGaussPositive(dataList);
107
         List<CmcGaussPositive> result = cmcGaussPositiveService.calculateGaussPositive(dataList);
109
         return success(result);
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 Näytä tiedosto

22
 import com.ruoyi.common.enums.BusinessType;
22
 import com.ruoyi.common.enums.BusinessType;
23
 import com.ruoyi.web.calculate.domain.CmcGeodeticToSpatial;
23
 import com.ruoyi.web.calculate.domain.CmcGeodeticToSpatial;
24
 import com.ruoyi.web.calculate.service.ICmcGeodeticToSpatialService;
24
 import com.ruoyi.web.calculate.service.ICmcGeodeticToSpatialService;
25
-import com.ruoyi.web.calculate.vo.CmcGeodeticToSpatialTemplate;
26
 import com.ruoyi.common.utils.poi.ExcelUtil;
25
 import com.ruoyi.common.utils.poi.ExcelUtil;
27
 import com.ruoyi.common.core.page.TableDataInfo;
26
 import com.ruoyi.common.core.page.TableDataInfo;
28
 
27
 
107
         List<CmcGeodeticToSpatial> result = cmcGeodeticToSpatialService.calculateGeodeticToSpatial(dataList);
106
         List<CmcGeodeticToSpatial> result = cmcGeodeticToSpatialService.calculateGeodeticToSpatial(dataList);
108
         return success(result);
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 Näytä tiedosto

22
 import com.ruoyi.common.enums.BusinessType;
22
 import com.ruoyi.common.enums.BusinessType;
23
 import com.ruoyi.web.calculate.domain.CmcSpatialToGeodetic;
23
 import com.ruoyi.web.calculate.domain.CmcSpatialToGeodetic;
24
 import com.ruoyi.web.calculate.service.ICmcSpatialToGeodeticService;
24
 import com.ruoyi.web.calculate.service.ICmcSpatialToGeodeticService;
25
-import com.ruoyi.web.calculate.vo.CmcSpatialToGeodeticTemplate;
26
 import com.ruoyi.common.utils.poi.ExcelUtil;
25
 import com.ruoyi.common.utils.poi.ExcelUtil;
27
 import com.ruoyi.common.core.page.TableDataInfo;
26
 import com.ruoyi.common.core.page.TableDataInfo;
28
 
27
 
107
         List<CmcSpatialToGeodetic> result = cmcSpatialToGeodeticService.calculateSpatialToGeodetic(dataList);
106
         List<CmcSpatialToGeodetic> result = cmcSpatialToGeodeticService.calculateSpatialToGeodetic(dataList);
108
         return success(result);
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 Näytä tiedosto

19
 import com.ruoyi.common.utils.poi.ExcelUtil;
19
 import com.ruoyi.common.utils.poi.ExcelUtil;
20
 import com.ruoyi.web.calculate.domain.CmcUTMBandChange;
20
 import com.ruoyi.web.calculate.domain.CmcUTMBandChange;
21
 import com.ruoyi.web.calculate.service.ICmcUTMBandChangeService;
21
 import com.ruoyi.web.calculate.service.ICmcUTMBandChangeService;
22
-import com.ruoyi.web.calculate.vo.CmcUTMBandChangeTemplate;
23
 
22
 
24
 @RestController
23
 @RestController
25
 @RequestMapping("/calculate/utmBandChange")
24
 @RequestMapping("/calculate/utmBandChange")
93
         List<CmcUTMBandChange> result = cmcUTMBandChangeService.calculateUTMBandChange(dataList);
92
         List<CmcUTMBandChange> result = cmcUTMBandChangeService.calculateUTMBandChange(dataList);
94
         return AjaxResult.success(result);
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 Näytä tiedosto

19
 import com.ruoyi.common.enums.BusinessType;
19
 import com.ruoyi.common.enums.BusinessType;
20
 import com.ruoyi.web.calculate.domain.CmcUTMNegative;
20
 import com.ruoyi.web.calculate.domain.CmcUTMNegative;
21
 import com.ruoyi.web.calculate.service.ICmcUTMNegativeService;
21
 import com.ruoyi.web.calculate.service.ICmcUTMNegativeService;
22
-import com.ruoyi.web.calculate.vo.CmcUTMNegativeTemplate;
23
 import com.ruoyi.common.utils.poi.ExcelUtil;
22
 import com.ruoyi.common.utils.poi.ExcelUtil;
24
 
23
 
25
 /**
24
 /**
102
         List<CmcUTMNegative> result = cmcUTMNagativeService.calculateUTMNagative(dataList);
101
         List<CmcUTMNegative> result = cmcUTMNagativeService.calculateUTMNagative(dataList);
103
         return success(result);
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 Näytä tiedosto

22
 import com.ruoyi.common.enums.BusinessType;
22
 import com.ruoyi.common.enums.BusinessType;
23
 import com.ruoyi.web.calculate.domain.CmcUTMPositive;
23
 import com.ruoyi.web.calculate.domain.CmcUTMPositive;
24
 import com.ruoyi.web.calculate.service.ICmcUTMPositiveService;
24
 import com.ruoyi.web.calculate.service.ICmcUTMPositiveService;
25
-import com.ruoyi.web.calculate.vo.CmcUTMPositiveTemplate;
26
 import com.ruoyi.common.utils.poi.ExcelUtil;
25
 import com.ruoyi.common.utils.poi.ExcelUtil;
27
 import com.ruoyi.common.core.page.TableDataInfo;
26
 import com.ruoyi.common.core.page.TableDataInfo;
28
 
27
 
107
         List<CmcUTMPositive> result = cmcUTMPositiveService.calculateUTMPositive(dataList);
106
         List<CmcUTMPositive> result = cmcUTMPositiveService.calculateUTMPositive(dataList);
108
         return success(result);
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 Näytä tiedosto

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 Näytä tiedosto

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 Näytä tiedosto

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 Näytä tiedosto

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 Näytä tiedosto

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 Näytä tiedosto

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 Näytä tiedosto

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 Näytä tiedosto

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 Näytä tiedosto

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 Näytä tiedosto

16
 
16
 
17
 import com.ruoyi.common.core.domain.AjaxResult;
17
 import com.ruoyi.common.core.domain.AjaxResult;
18
 import com.ruoyi.common.utils.poi.ExcelUtil;
18
 import com.ruoyi.common.utils.poi.ExcelUtil;
19
-// import com.ruoyi.web.calculate.mapper.CmcGaussPositiveMapper;
20
 import com.ruoyi.web.calculate.domain.CmcGaussBandChange;
19
 import com.ruoyi.web.calculate.domain.CmcGaussBandChange;
21
 import com.ruoyi.web.calculate.service.ICmcGaussBandChangeService;
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
     @Transactional(rollbackFor = Exception.class)
59
     @Transactional(rollbackFor = Exception.class)
54
     public AjaxResult importExcelData(MultipartFile file) {
60
     public AjaxResult importExcelData(MultipartFile file) {
55
         try {
61
         try {
62
+            // 获取Excel表头信息
63
+            List<String> excelColumns = getExcelColumns(file);
64
+
56
             // 使用若依的ExcelUtil工具类
65
             // 使用若依的ExcelUtil工具类
57
             ExcelUtil<CmcGaussBandChange> util = new ExcelUtil<CmcGaussBandChange>(CmcGaussBandChange.class);
66
             ExcelUtil<CmcGaussBandChange> util = new ExcelUtil<CmcGaussBandChange>(CmcGaussBandChange.class);
58
             List<CmcGaussBandChange> dataList = util.importExcel(file.getInputStream());
67
             List<CmcGaussBandChange> dataList = util.importExcel(file.getInputStream());
62
                 return AjaxResult.error("Excel文件中没有数据!");
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
             Map<String, Object> result = new HashMap<>();
78
             Map<String, Object> result = new HashMap<>();
99
             result.put("total", dataList.size());
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
             return AjaxResult.success(message, result);
85
             return AjaxResult.success(message, result);
114
 
86
 
115
         } catch (Exception e) {
87
         } catch (Exception e) {
117
             return AjaxResult.error("导入失败:" + e.getMessage());
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 Näytä tiedosto

16
 
16
 
17
 import com.ruoyi.common.core.domain.AjaxResult;
17
 import com.ruoyi.common.core.domain.AjaxResult;
18
 import com.ruoyi.common.utils.poi.ExcelUtil;
18
 import com.ruoyi.common.utils.poi.ExcelUtil;
19
-// import com.ruoyi.web.calculate.mapper.CmcGaussPositiveMapper;
20
 import com.ruoyi.web.calculate.domain.CmcGaussNegative;
19
 import com.ruoyi.web.calculate.domain.CmcGaussNegative;
21
 import com.ruoyi.web.calculate.service.ICmcGaussNegativeService;
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
     @Transactional(rollbackFor = Exception.class)
59
     @Transactional(rollbackFor = Exception.class)
54
     public AjaxResult importExcelData(MultipartFile file) {
60
     public AjaxResult importExcelData(MultipartFile file) {
55
         try {
61
         try {
62
+            // 获取Excel表头信息
63
+            List<String> excelColumns = getExcelColumns(file);
64
+
56
             // 使用若依的ExcelUtil工具类
65
             // 使用若依的ExcelUtil工具类
57
             ExcelUtil<CmcGaussNegative> util = new ExcelUtil<CmcGaussNegative>(CmcGaussNegative.class);
66
             ExcelUtil<CmcGaussNegative> util = new ExcelUtil<CmcGaussNegative>(CmcGaussNegative.class);
58
             List<CmcGaussNegative> dataList = util.importExcel(file.getInputStream());
67
             List<CmcGaussNegative> dataList = util.importExcel(file.getInputStream());
62
                 return AjaxResult.error("Excel文件中没有数据!");
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
             Map<String, Object> result = new HashMap<>();
78
             Map<String, Object> result = new HashMap<>();
95
             result.put("total", dataList.size());
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
             return AjaxResult.success(message, result);
85
             return AjaxResult.success(message, result);
110
 
86
 
111
         } catch (Exception e) {
87
         } catch (Exception e) {
113
             return AjaxResult.error("导入失败:" + e.getMessage());
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
             String latitudePosition = cmcGaussNegative.getLatitudePosition(); // 纬度位置(北/南)
244
             String latitudePosition = cmcGaussNegative.getLatitudePosition(); // 纬度位置(北/南)
152
             double projectionHeight = cmcGaussNegative.getProjectionHeight(); // 投影面高程
245
             double projectionHeight = cmcGaussNegative.getProjectionHeight(); // 投影面高程
153
             String yStr = String.valueOf(Math.abs((long) gaussY));
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
             double x = latitudePosition.equals("S") ? 10000000 - gaussX : gaussX;
250
             double x = latitudePosition.equals("S") ? 10000000 - gaussX : gaussX;
158
             double y = gaussY > 1.0E+7 ? gaussY - (int) Math.floor(gaussY / 1.0E+6) * 1.0E+6 - 500000 : gaussY - 500000;
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 Näytä tiedosto

7
 import java.util.List;
7
 import java.util.List;
8
 import java.util.Map;
8
 import java.util.Map;
9
 
9
 
10
-import javax.servlet.http.HttpServletResponse;
11
-
12
 import org.springframework.beans.factory.annotation.Autowired;
10
 import org.springframework.beans.factory.annotation.Autowired;
13
 import org.springframework.stereotype.Service;
11
 import org.springframework.stereotype.Service;
14
 import org.springframework.transaction.annotation.Transactional;
12
 import org.springframework.transaction.annotation.Transactional;
19
 // import com.ruoyi.web.calculate.mapper.CmcGaussPositiveMapper;
17
 // import com.ruoyi.web.calculate.mapper.CmcGaussPositiveMapper;
20
 import com.ruoyi.web.calculate.domain.CmcGaussPositive;
18
 import com.ruoyi.web.calculate.domain.CmcGaussPositive;
21
 import com.ruoyi.web.calculate.service.ICmcGaussPositiveService;
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
     public AjaxResult importExcelData(MultipartFile file)
61
     public AjaxResult importExcelData(MultipartFile file)
57
     {
62
     {
58
         try {
63
         try {
64
+            // 获取Excel表头信息
65
+            List<String> excelColumns = getExcelColumns(file);
66
+
59
             // 使用若依的ExcelUtil工具类
67
             // 使用若依的ExcelUtil工具类
60
             ExcelUtil<CmcGaussPositive> util = new ExcelUtil<CmcGaussPositive>(CmcGaussPositive.class);
68
             ExcelUtil<CmcGaussPositive> util = new ExcelUtil<CmcGaussPositive>(CmcGaussPositive.class);
61
             List<CmcGaussPositive> dataList = util.importExcel(file.getInputStream());
69
             List<CmcGaussPositive> dataList = util.importExcel(file.getInputStream());
65
                 return AjaxResult.error("Excel文件中没有数据!");
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
             Map<String, Object> result = new HashMap<>();
80
             Map<String, Object> result = new HashMap<>();
100
             result.put("total", dataList.size());
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
             return AjaxResult.success(message, result);
87
             return AjaxResult.success(message, result);
115
 
88
 
116
         } catch (Exception e) {
89
         } catch (Exception e) {
118
             return AjaxResult.error("导入失败:" + e.getMessage());
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 Näytä tiedosto

18
 import com.ruoyi.common.utils.poi.ExcelUtil;
18
 import com.ruoyi.common.utils.poi.ExcelUtil;
19
 import com.ruoyi.web.calculate.domain.CmcGeodeticToSpatial;
19
 import com.ruoyi.web.calculate.domain.CmcGeodeticToSpatial;
20
 import com.ruoyi.web.calculate.service.ICmcGeodeticToSpatialService;
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
     @Transactional(rollbackFor = Exception.class)
59
     @Transactional(rollbackFor = Exception.class)
53
     public AjaxResult importExcelData(MultipartFile file) {
60
     public AjaxResult importExcelData(MultipartFile file) {
54
         try {
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
                 return AjaxResult.error("Excel文件中没有数据!");
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
             Map<String, Object> result = new HashMap<>();
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
             return AjaxResult.success(message, result);
81
             return AjaxResult.success(message, result);
109
 
82
 
110
         } catch (Exception e) {
83
         } catch (Exception e) {
112
             return AjaxResult.error("导入失败:" + e.getMessage());
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 Näytä tiedosto

18
 import com.ruoyi.common.utils.poi.ExcelUtil;
18
 import com.ruoyi.common.utils.poi.ExcelUtil;
19
 import com.ruoyi.web.calculate.domain.CmcSpatialToGeodetic;
19
 import com.ruoyi.web.calculate.domain.CmcSpatialToGeodetic;
20
 import com.ruoyi.web.calculate.service.ICmcSpatialToGeodeticService;
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
     @Transactional(rollbackFor = Exception.class)
59
     @Transactional(rollbackFor = Exception.class)
53
     public AjaxResult importExcelData(MultipartFile file) {
60
     public AjaxResult importExcelData(MultipartFile file) {
54
         try {
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
                 return AjaxResult.error("Excel文件中没有数据!");
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
             Map<String, Object> result = new HashMap<>();
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
             return AjaxResult.success(message, result);
81
             return AjaxResult.success(message, result);
107
 
82
 
108
         } catch (Exception e) {
83
         } catch (Exception e) {
110
             return AjaxResult.error("导入失败:" + e.getMessage());
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
                            Math.floor(((B * 60 - Math.floor(B * 60)) * 60 + Math.pow(10, -11)) * Math.pow(10, 6))
311
                            Math.floor(((B * 60 - Math.floor(B * 60)) * 60 + Math.pow(10, -11)) * Math.pow(10, 6))
178
                         / Math.pow(10, 10);
312
                         / Math.pow(10, 10);
179
         String longitudePosition = spatialY < 0 ? "W" : "E";
313
         String longitudePosition = spatialY < 0 ? "W" : "E";
180
-        String latitudePosition = spatialX < 0 ? "S" : "N";
314
+        String latitudePosition = spatialZ < 0 ? "S" : "N";
181
         double height = Math.sqrt(Math.pow(spatialX, 2) + Math.pow(spatialY, 2) + Math.pow(spatialZ + t, 2)) - N;
315
         double height = Math.sqrt(Math.pow(spatialX, 2) + Math.pow(spatialY, 2) + Math.pow(spatialZ + t, 2)) - N;
182
         cmcSpatialToGeodetic.setLongitude(longitude);
316
         cmcSpatialToGeodetic.setLongitude(longitude);
183
         cmcSpatialToGeodetic.setLatitude(latitude);
317
         cmcSpatialToGeodetic.setLatitude(latitude);

+ 177
- 42
oa-back/ruoyi-calculate/src/main/java/com/ruoyi/web/calculate/service/impl/CmcUTMBandChangeServicelmpl.java Näytä tiedosto

18
 import com.ruoyi.common.utils.poi.ExcelUtil;
18
 import com.ruoyi.common.utils.poi.ExcelUtil;
19
 import com.ruoyi.web.calculate.domain.CmcUTMBandChange;
19
 import com.ruoyi.web.calculate.domain.CmcUTMBandChange;
20
 import com.ruoyi.web.calculate.service.ICmcUTMBandChangeService;
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
  * UTM换带服务实现
30
  * UTM换带服务实现
50
      */
57
      */
51
     @Override
58
     @Override
52
     @Transactional(rollbackFor = Exception.class)
59
     @Transactional(rollbackFor = Exception.class)
53
-    public AjaxResult importExcelData(MultipartFile file) {
60
+    public AjaxResult importExcelData(MultipartFile file)
61
+    {
54
         try {
62
         try {
63
+            // 获取Excel表头信息
64
+            List<String> excelColumns = getExcelColumns(file);
65
+
55
             // 使用若依的ExcelUtil工具类
66
             // 使用若依的ExcelUtil工具类
56
             ExcelUtil<CmcUTMBandChange> util = new ExcelUtil<CmcUTMBandChange>(CmcUTMBandChange.class);
67
             ExcelUtil<CmcUTMBandChange> util = new ExcelUtil<CmcUTMBandChange>(CmcUTMBandChange.class);
57
             List<CmcUTMBandChange> dataList = util.importExcel(file.getInputStream());
68
             List<CmcUTMBandChange> dataList = util.importExcel(file.getInputStream());
61
                 return AjaxResult.error("Excel文件中没有数据!");
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
             Map<String, Object> result = new HashMap<>();
79
             Map<String, Object> result = new HashMap<>();
96
             result.put("total", dataList.size());
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
             return AjaxResult.success(message, result);
86
             return AjaxResult.success(message, result);
111
 
87
 
112
         } catch (Exception e) {
88
         } catch (Exception e) {
114
             return AjaxResult.error("导入失败:" + e.getMessage());
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
      * 执行UTM换带计算
254
      * 执行UTM换带计算

+ 175
- 40
oa-back/ruoyi-calculate/src/main/java/com/ruoyi/web/calculate/service/impl/CmcUTMNegativeServiceImpl.java Näytä tiedosto

16
 import com.ruoyi.common.utils.poi.ExcelUtil;
16
 import com.ruoyi.common.utils.poi.ExcelUtil;
17
 import com.ruoyi.web.calculate.domain.CmcUTMNegative;
17
 import com.ruoyi.web.calculate.domain.CmcUTMNegative;
18
 import com.ruoyi.web.calculate.service.ICmcUTMNegativeService;
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
  * UTM反算服务实现
28
  * UTM反算服务实现
54
     public AjaxResult importExcelData(MultipartFile file)
61
     public AjaxResult importExcelData(MultipartFile file)
55
     {
62
     {
56
         try {
63
         try {
64
+            // 获取Excel表头信息
65
+            List<String> excelColumns = getExcelColumns(file);
66
+
57
             // 使用若依的ExcelUtil工具类
67
             // 使用若依的ExcelUtil工具类
58
             ExcelUtil<CmcUTMNegative> util = new ExcelUtil<CmcUTMNegative>(CmcUTMNegative.class);
68
             ExcelUtil<CmcUTMNegative> util = new ExcelUtil<CmcUTMNegative>(CmcUTMNegative.class);
59
             List<CmcUTMNegative> dataList = util.importExcel(file.getInputStream());
69
             List<CmcUTMNegative> dataList = util.importExcel(file.getInputStream());
63
                 return AjaxResult.error("Excel文件中没有数据!");
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
             Map<String, Object> result = new HashMap<>();
80
             Map<String, Object> result = new HashMap<>();
97
             result.put("total", dataList.size());
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
             return AjaxResult.success(message, result);
87
             return AjaxResult.success(message, result);
112
 
88
 
113
         } catch (Exception e) {
89
         } catch (Exception e) {
115
             return AjaxResult.error("导入失败:" + e.getMessage());
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
      * 执行UTM反算
255
      * 执行UTM反算

+ 133
- 40
oa-back/ruoyi-calculate/src/main/java/com/ruoyi/web/calculate/service/impl/CmcUTMPositiveServiceImpl.java Näytä tiedosto

18
 import com.ruoyi.common.utils.poi.ExcelUtil;
18
 import com.ruoyi.common.utils.poi.ExcelUtil;
19
 import com.ruoyi.web.calculate.domain.CmcUTMPositive;
19
 import com.ruoyi.web.calculate.domain.CmcUTMPositive;
20
 import com.ruoyi.web.calculate.service.ICmcUTMPositiveService;
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
  * UTM正算服务实现
30
  * UTM正算服务实现
55
     public AjaxResult importExcelData(MultipartFile file)
62
     public AjaxResult importExcelData(MultipartFile file)
56
     {
63
     {
57
         try {
64
         try {
65
+            // 获取Excel表头信息
66
+            List<String> excelColumns = getExcelColumns(file);
67
+
58
             // 使用若依的ExcelUtil工具类
68
             // 使用若依的ExcelUtil工具类
59
             ExcelUtil<CmcUTMPositive> util = new ExcelUtil<CmcUTMPositive>(CmcUTMPositive.class);
69
             ExcelUtil<CmcUTMPositive> util = new ExcelUtil<CmcUTMPositive>(CmcUTMPositive.class);
60
             List<CmcUTMPositive> dataList = util.importExcel(file.getInputStream());
70
             List<CmcUTMPositive> dataList = util.importExcel(file.getInputStream());
64
                 return AjaxResult.error("Excel文件中没有数据!");
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
             Map<String, Object> result = new HashMap<>();
81
             Map<String, Object> result = new HashMap<>();
98
             result.put("total", dataList.size());
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
             return AjaxResult.success(message, result);
88
             return AjaxResult.success(message, result);
113
 
89
 
114
         } catch (Exception e) {
90
         } catch (Exception e) {
116
             return AjaxResult.error("导入失败:" + e.getMessage());
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
      * 执行UTM正算
214
      * 执行UTM正算

+ 0
- 125
oa-back/ruoyi-calculate/src/main/java/com/ruoyi/web/calculate/vo/CmcGaussBandChangeTemplate.java Näytä tiedosto

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 Näytä tiedosto

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 Näytä tiedosto

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 Näytä tiedosto

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 Näytä tiedosto

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 Näytä tiedosto

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 Näytä tiedosto

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 Näytä tiedosto

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 Näytä tiedosto

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 Näytä tiedosto

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 Näytä tiedosto

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 Näytä tiedosto

31
         responseType: 'blob'
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 Näytä tiedosto

31
         responseType: 'blob'
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 Näytä tiedosto

31
         responseType: 'blob'
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 Näytä tiedosto

29
         data: data,
29
         data: data,
30
         responseType: 'blob'
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 Näytä tiedosto

29
         data: data,
29
         data: data,
30
         responseType: 'blob'
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 Näytä tiedosto

30
         data: data,
30
         data: data,
31
         responseType: 'blob'
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 Näytä tiedosto

31
         responseType: 'blob'
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 Näytä tiedosto

30
         data: data,
30
         data: data,
31
         responseType: 'blob'
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 Näytä tiedosto

53
 import UTMBandChangeTool from './tools/utmbandchange.vue'
53
 import UTMBandChangeTool from './tools/utmbandchange.vue'
54
 import GeodeticToSpatialTool from './tools/geodetictospatial.vue'
54
 import GeodeticToSpatialTool from './tools/geodetictospatial.vue'
55
 import SpatialToGeodeticTool from './tools/spatialtogeodetic.vue'
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
 export default {
60
 export default {
58
   name: 'CalculationTools',
61
   name: 'CalculationTools',
65
     UTMBandChangeTool,
68
     UTMBandChangeTool,
66
     GeodeticToSpatialTool,
69
     GeodeticToSpatialTool,
67
     SpatialToGeodeticTool,
70
     SpatialToGeodeticTool,
71
+    DegreeToDSTool, 
72
+    DMStodegreeTool,
73
+    DegreeToRadianTool,
68
   },
74
   },
69
   data() {
75
   data() {
70
     return {
76
     return {
128
               id: 'spatial-to-geodetic',
134
               id: 'spatial-to-geodetic',
129
               name: '空间坐标转大地坐标工具',
135
               name: '空间坐标转大地坐标工具',
130
               component: 'SpatialToGeodeticTool'
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 Näytä tiedosto

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 Näytä tiedosto

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 Näytä tiedosto

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 Näytä tiedosto

8
           导入Excel
8
           导入Excel
9
         </el-button>
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
         <el-button 
11
         <el-button 
20
           type="warning" 
12
           type="warning" 
21
           @click="handleCalculate" 
13
           @click="handleCalculate" 
37
       </div>
29
       </div>
38
     </div>
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
       </div>
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
       </div>
98
       </div>
54
     </div>
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
       <div class="section-header">
103
       <div class="section-header">
59
         <span class="section-title">
104
         <span class="section-title">
60
           <i class="el-icon-tickets"></i>
105
           <i class="el-icon-tickets"></i>
61
-          计算结果
106
+          {{ calculationResults.length > 0 ? '计算结果' : '导入数据' }}
62
         </span>
107
         </span>
63
         <span class="section-info">
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
         </span>
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
         </el-button>
118
         </el-button>
69
       </div>
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
         <el-table-column prop="pointNumber" label="待求点号" width="120" align="center"></el-table-column>
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
           <template slot-scope="scope">
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
           </template>
126
           </template>
77
         </el-table-column>
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
           <template slot-scope="scope">
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
           </template>
143
           </template>
82
         </el-table-column>
144
         </el-table-column>
83
       </el-table>
145
       </el-table>
86
     <!-- 空状态 -->
148
     <!-- 空状态 -->
87
     <div v-else class="empty-state">
149
     <div v-else class="empty-state">
88
       <i class="el-icon-document"></i>
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
     </div>
153
     </div>
92
     
154
     
93
     <!-- 导入Excel对话框 -->
155
     <!-- 导入Excel对话框 -->
94
     <el-dialog title="导入Excel" :visible.sync="importDialogVisible" width="550px" @close="resetImport">
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
       <div style="text-align: center;">
157
       <div style="text-align: center;">
124
         <el-upload
158
         <el-upload
125
           class="upload-area"
159
           class="upload-area"
146
       </span>
180
       </span>
147
     </el-dialog>
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
       </div>
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
       <span slot="footer" class="dialog-footer">
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
       </span>
224
       </span>
208
     </el-dialog>
225
     </el-dialog>
209
   </div>
226
   </div>
222
       importLoading: false,
239
       importLoading: false,
223
       importFile: null,
240
       importFile: null,
224
       calculating: false,
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
   watch: {
265
   watch: {
266
       const formData = new FormData()
302
       const formData = new FormData()
267
       formData.append('file', this.importFile)
303
       formData.append('file', this.importFile)
268
       
304
       
305
+      try {
269
         const response = await importExcel(formData)
306
         const response = await importExcel(formData)
270
         
307
         
271
         if (response.code === 200) {
308
         if (response.code === 200) {
272
           const result = response.data
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
           this.calculationResults = []
318
           this.calculationResults = []
289
           
319
           
320
+          // 自动匹配字段
321
+          this.autoMatchFields()
322
+          
323
+          // 显示字段映射对话框
324
+          this.mappingDialogVisible = true
325
+          
290
           this.$message.success(`${response.msg}`)
326
           this.$message.success(`${response.msg}`)
291
           this.importDialogVisible = false
327
           this.importDialogVisible = false
292
           this.importLoading = false
328
           this.importLoading = false
293
         } else {
329
         } else {
294
           this.$message.error(response.msg)
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
         return
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
       }
422
       }
319
       
423
       
320
       this.calculating = true
424
       this.calculating = true
321
-      const results = []
322
-      let successCount = 0
323
-      let failCount = 0
324
       
425
       
325
       // 构建请求参数
426
       // 构建请求参数
326
       const requestData = this.importedData.map(item => ({
427
       const requestData = this.importedData.map(item => ({
327
         pointNumber: item.pointNumber,
428
         pointNumber: item.pointNumber,
328
-        coordinateSystem: item.coordinateSystem,
429
+        coordinateSystem: this.commonParams.coordinateSystem,
329
         gaussX: item.gaussX,
430
         gaussX: item.gaussX,
330
         gaussY: item.gaussY,
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
         projectionHeight: this.projectionHeight
438
         projectionHeight: this.projectionHeight
338
       }))
439
       }))
339
       
440
       
350
           gaussY: item.gaussY,
451
           gaussY: item.gaussY,
351
           band: item.band,
452
           band: item.band,
352
           bandwidth: item.bandwidth,
453
           bandwidth: item.bandwidth,
454
+          newGaussX: item.newGaussX,
455
+          newGaussY: item.newGaussY,
353
           newBand: item.newBand,
456
           newBand: item.newBand,
354
           newBandwidth: item.newBandwidth,
457
           newBandwidth: item.newBandwidth,
355
           longitudePosition: item.longitudePosition,
458
           longitudePosition: item.longitudePosition,
356
           latitudePosition: item.latitudePosition,
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
         const failCount = results.length - successCount
464
         const failCount = results.length - successCount
372
         
465
         
373
         this.$message.success(`计算完成,成功 ${successCount} 条${failCount > 0 ? `,失败 ${failCount} 条` : ''}`)
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
     }
533
     }
441
     return types[system] || 'info'
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
 </script>
572
 </script>
446
 
573
 
447
 <style scoped>
574
 <style scoped>
448
-.gauss-positive-tool {
575
+.gauss-band-change-tool {
449
   width: 100%;
576
   width: 100%;
450
 }
577
 }
451
 
578
 
459
   flex-wrap: wrap;
586
   flex-wrap: wrap;
460
 }
587
 }
461
 
588
 
462
-/* 投影面高程设置样式 */
463
-.height-setting {
589
+/* 参数设置区域样式 */
590
+.params-area {
464
   background: #f5f7fa;
591
   background: #f5f7fa;
465
   border-radius: 8px;
592
   border-radius: 8px;
466
-  padding: 12px 20px;
593
+  padding: 16px 20px;
467
   margin-bottom: 20px;
594
   margin-bottom: 20px;
468
   border: 1px solid #e4e7ed;
595
   border: 1px solid #e4e7ed;
596
+}
597
+
598
+.params-row {
469
   display: flex;
599
   display: flex;
470
-  align-items: center;
471
-  gap: 20px;
472
   flex-wrap: wrap;
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
 .result-section {
622
 .result-section {
576
   padding-top: 12px;
723
   padding-top: 12px;
577
   border-top: 1px dashed #e4e7ed;
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
 </style>
763
 </style>

+ 335
- 169
oa-ui/src/views/calculate/tools/gaussnegative.vue Näytä tiedosto

8
           导入Excel
8
           导入Excel
9
         </el-button>
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
         <el-button 
11
         <el-button 
20
           type="warning" 
12
           type="warning" 
21
           @click="handleCalculate" 
13
           @click="handleCalculate" 
37
       </div>
29
       </div>
38
     </div>
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
       </div>
83
       </div>
54
     </div>
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
       <div class="section-header">
88
       <div class="section-header">
59
         <span class="section-title">
89
         <span class="section-title">
60
           <i class="el-icon-tickets"></i>
90
           <i class="el-icon-tickets"></i>
61
-          计算结果
91
+          {{ calculationResults.length > 0 ? '计算结果' : '导入数据' }}
62
         </span>
92
         </span>
63
         <span class="section-info">
93
         <span class="section-info">
64
-          共 {{ calculationResults.length }} 条计算结果
94
+          共 {{ displayData.length }} 条数据 | 坐标系:{{ commonParams.coordinateSystem }} | 带号:{{ commonParams.band }} | 带宽:{{ commonParams.bandwidth }}
65
         </span>
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
         </el-button>
98
         </el-button>
69
       </div>
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
         <el-table-column prop="pointNumber" label="待求点号" width="120" align="center"></el-table-column>
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
           <template slot-scope="scope">
104
           <template slot-scope="scope">
75
-            <span class="result-value">{{ formatNumber(scope.row.band,0) }}</span>
105
+            {{ formatNumber(scope.row.gaussX, 4) }}
76
           </template>
106
           </template>
77
         </el-table-column>
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
           <template slot-scope="scope">
109
           <template slot-scope="scope">
80
-            <span class="result-value">{{ formatNumber(scope.row.bandwidth,0) }}</span>
110
+            {{ formatNumber(scope.row.gaussY, 4) }}
81
           </template>
111
           </template>
82
         </el-table-column>
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
           <template slot-scope="scope">
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
           </template>
117
           </template>
87
         </el-table-column>
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
           <template slot-scope="scope">
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
           </template>
123
           </template>
92
         </el-table-column>
124
         </el-table-column>
93
       </el-table>
125
       </el-table>
96
     <!-- 空状态 -->
128
     <!-- 空状态 -->
97
     <div v-else class="empty-state">
129
     <div v-else class="empty-state">
98
       <i class="el-icon-document"></i>
130
       <i class="el-icon-document"></i>
99
-      <p>暂无计算结果</p>
131
+      <p>暂无数据</p>
100
       <p class="empty-tip">请先导入Excel数据,然后点击"执行计算"</p>
132
       <p class="empty-tip">请先导入Excel数据,然后点击"执行计算"</p>
101
     </div>
133
     </div>
102
     
134
     
103
     <!-- 导入Excel对话框 -->
135
     <!-- 导入Excel对话框 -->
104
     <el-dialog title="导入Excel" :visible.sync="importDialogVisible" width="550px" @close="resetImport">
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
       <div style="text-align: center;">
137
       <div style="text-align: center;">
130
         <el-upload
138
         <el-upload
131
           class="upload-area"
139
           class="upload-area"
152
       </span>
160
       </span>
153
     </el-dialog>
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
       </div>
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
       <span slot="footer" class="dialog-footer">
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
       </span>
202
       </span>
194
     </el-dialog>
203
     </el-dialog>
195
   </div>
204
   </div>
208
       importLoading: false,
217
       importLoading: false,
209
       importFile: null,
218
       importFile: null,
210
       calculating: false,
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
   watch: {
243
   watch: {
252
       const formData = new FormData()
280
       const formData = new FormData()
253
       formData.append('file', this.importFile)
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
         return
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
       // 构建请求参数
394
       // 构建请求参数
309
       const requestData = this.importedData.map(item => ({
395
       const requestData = this.importedData.map(item => ({
310
         pointNumber: item.pointNumber,
396
         pointNumber: item.pointNumber,
311
-        coordinateSystem: item.coordinateSystem,
397
+        coordinateSystem: this.commonParams.coordinateSystem,
312
         gaussX: item.gaussX,
398
         gaussX: item.gaussX,
313
         gaussY: item.gaussY,
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
         projectionHeight: this.projectionHeight
404
         projectionHeight: this.projectionHeight
317
       }))
405
       }))
318
       
406
       
325
         this.calculationResults = results.map(item => ({
413
         this.calculationResults = results.map(item => ({
326
           pointNumber: item.pointNumber,
414
           pointNumber: item.pointNumber,
327
           coordinateSystem: item.coordinateSystem,
415
           coordinateSystem: item.coordinateSystem,
328
-          longitude: parseFloat(item.longitude),
416
+          longitude: item.longitude,
329
           longitudePosition: item.longitudePosition,
417
           longitudePosition: item.longitudePosition,
330
-          latitude: parseFloat(item.latitude),
418
+          latitude: item.latitude,
331
           latitudePosition: item.latitudePosition,
419
           latitudePosition: item.latitudePosition,
332
-          band: parseInt(item.band),
333
-          bandwidth: parseInt(item.bandwidth),
420
+          band: item.band,
421
+          bandwidth: item.bandwidth,
334
           projectionHeight: this.projectionHeight,
422
           projectionHeight: this.projectionHeight,
335
           gaussX: item.gaussX,
423
           gaussX: item.gaussX,
336
           gaussY: item.gaussY
424
           gaussY: item.gaussY
343
             importItem.calculated = true
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
         const failCount = results.length - successCount
435
         const failCount = results.length - successCount
349
         
436
         
350
         this.$message.success(`计算完成,成功 ${successCount} 条${failCount > 0 ? `,失败 ${failCount} 条` : ''}`)
437
         this.$message.success(`计算完成,成功 ${successCount} 条${failCount > 0 ? `,失败 ${failCount} 条` : ''}`)
351
         this.calculating = false
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
     async handleExport() {
452
     async handleExport() {
362
         return
455
         return
363
       }
456
       }
364
 
457
 
365
-      
366
-
367
       const response = await exportExcel(this.calculationResults)
458
       const response = await exportExcel(this.calculationResults)
368
       
459
       
369
       const blob = new Blob([response], { 
460
       const blob = new Blob([response], { 
376
       link.download = `高斯反算结果_${new Date().getTime()}.xlsx`
467
       link.download = `高斯反算结果_${new Date().getTime()}.xlsx`
377
       link.click()
468
       link.click()
378
       window.URL.revokeObjectURL(url)
469
       window.URL.revokeObjectURL(url)
379
-  },
470
+    },
380
     
471
     
381
-  // 清空全部
382
-  clearAll() {
472
+    // 清空全部
473
+    clearAll() {
383
     if ((!this.importedData || this.importedData.length === 0) && this.calculationResults.length === 0) {
474
     if ((!this.importedData || this.importedData.length === 0) && this.calculationResults.length === 0) {
384
       return
475
       return
385
     }
476
     }
395
     }).catch(() => {})
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
 </script>
533
 </script>
425
 
534
 
426
 <style scoped>
535
 <style scoped>
427
-.gauss-positive-tool {
536
+.gauss-negative-tool {
428
   width: 100%;
537
   width: 100%;
429
 }
538
 }
430
 
539
 
438
   flex-wrap: wrap;
547
   flex-wrap: wrap;
439
 }
548
 }
440
 
549
 
441
-/* 投影面高程设置样式 */
442
-.height-setting {
550
+/* 参数设置区域样式 */
551
+.params-area {
443
   background: #f5f7fa;
552
   background: #f5f7fa;
444
   border-radius: 8px;
553
   border-radius: 8px;
445
-  padding: 12px 20px;
554
+  padding: 16px 20px;
446
   margin-bottom: 20px;
555
   margin-bottom: 20px;
447
   border: 1px solid #e4e7ed;
556
   border: 1px solid #e4e7ed;
557
+}
558
+
559
+.params-row {
448
   display: flex;
560
   display: flex;
449
-  align-items: center;
450
-  gap: 20px;
451
   flex-wrap: wrap;
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
 .result-section {
583
 .result-section {
555
   padding-top: 12px;
684
   padding-top: 12px;
556
   border-top: 1px dashed #e4e7ed;
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
 </style>
724
 </style>

+ 370
- 109
oa-ui/src/views/calculate/tools/gausspositive.vue Näytä tiedosto

8
           导入Excel
8
           导入Excel
9
         </el-button>
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
         <el-button 
11
         <el-button 
20
           type="warning" 
12
           type="warning" 
21
           @click="handleCalculate" 
13
           @click="handleCalculate" 
37
       </div>
29
       </div>
38
     </div>
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
       </div>
83
       </div>
54
     </div>
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
       <div class="section-header">
88
       <div class="section-header">
59
         <span class="section-title">
89
         <span class="section-title">
60
           <i class="el-icon-tickets"></i>
90
           <i class="el-icon-tickets"></i>
61
-          计算结果
91
+          {{ calculationResults.length > 0 ? '计算结果' : '导入数据' }}
62
         </span>
92
         </span>
63
         <span class="section-info">
93
         <span class="section-info">
64
-          共 {{ calculationResults.length }} 条计算结果
94
+          共 {{ displayData.length }} 条数据 | 坐标系:{{ commonParams.coordinateSystem }} | 带号:{{ commonParams.band }} | 带宽:{{ commonParams.bandwidth }}
65
         </span>
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
         </el-button>
98
         </el-button>
69
       </div>
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
         <el-table-column prop="pointNumber" label="待求点号" width="120" align="center"></el-table-column>
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
         <el-table-column prop="gaussX" label="高斯X坐标(m)" width="180" align="center">
113
         <el-table-column prop="gaussX" label="高斯X坐标(m)" width="180" align="center">
74
           <template slot-scope="scope">
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
           </template>
117
           </template>
77
         </el-table-column>
118
         </el-table-column>
78
         <el-table-column prop="gaussY" label="高斯Y坐标(m)" width="180" align="center">
119
         <el-table-column prop="gaussY" label="高斯Y坐标(m)" width="180" align="center">
79
           <template slot-scope="scope">
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
           </template>
123
           </template>
82
         </el-table-column>
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
           <template slot-scope="scope">
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
           </template>
129
           </template>
87
         </el-table-column>
130
         </el-table-column>
88
       </el-table>
131
       </el-table>
91
     <!-- 空状态 -->
134
     <!-- 空状态 -->
92
     <div v-else class="empty-state">
135
     <div v-else class="empty-state">
93
       <i class="el-icon-document"></i>
136
       <i class="el-icon-document"></i>
94
-      <p>暂无计算结果</p>
137
+      <p>暂无数据</p>
95
       <p class="empty-tip">请先导入Excel数据,然后点击"执行计算"</p>
138
       <p class="empty-tip">请先导入Excel数据,然后点击"执行计算"</p>
96
     </div>
139
     </div>
97
     
140
     
98
     <!-- 导入Excel对话框 -->
141
     <!-- 导入Excel对话框 -->
99
     <el-dialog title="导入Excel" :visible.sync="importDialogVisible" width="550px" @close="resetImport">
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
       <div style="text-align: center;">
143
       <div style="text-align: center;">
127
         <el-upload
144
         <el-upload
128
           class="upload-area"
145
           class="upload-area"
149
       </span>
166
       </span>
150
     </el-dialog>
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
     <el-dialog title="导入数据预览" :visible.sync="previewDialogVisible" width="80%">
212
     <el-dialog title="导入数据预览" :visible.sync="previewDialogVisible" width="80%">
154
       <div class="preview-info">
213
       <div class="preview-info">
211
       importLoading: false,
270
       importLoading: false,
212
       importFile: null,
271
       importFile: null,
213
       calculating: false,
272
       calculating: false,
214
-      previewDialogVisible: false,
215
       projectionHeight: 0,
273
       projectionHeight: 0,
216
       heightTimer: null,
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
   watch: {
296
   watch: {
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
   methods: {
382
   methods: {
237
     // 打开导入对话框 
383
     // 打开导入对话框 
238
     handleImport() {
384
     handleImport() {
256
       const formData = new FormData()
402
       const formData = new FormData()
257
       formData.append('file', this.importFile)
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
         return
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
         return
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
       const requestData = this.importedData.map(item => ({
523
       const requestData = this.importedData.map(item => ({
316
         pointNumber: item.pointNumber,
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
       const response = await calculate(requestData)
535
       const response = await calculate(requestData)
333
         this.calculationResults = results.map(item => ({
541
         this.calculationResults = results.map(item => ({
334
           pointNumber: item.pointNumber,
542
           pointNumber: item.pointNumber,
335
           coordinateSystem: item.coordinateSystem,
543
           coordinateSystem: item.coordinateSystem,
336
-          longitude: parseFloat(item.longitude),
544
+          longitude: item.longitude,
337
           longitudePosition: item.longitudePosition,
545
           longitudePosition: item.longitudePosition,
338
-          latitude: parseFloat(item.latitude),
546
+          latitude: item.latitude,
339
           latitudePosition: item.latitudePosition,
547
           latitudePosition: item.latitudePosition,
340
-          band: parseInt(item.band),
341
-          bandwidth: parseInt(item.bandwidth),
548
+          band: item.band,
549
+          bandwidth: item.bandwidth,
342
           projectionHeight: this.projectionHeight,
550
           projectionHeight: this.projectionHeight,
343
           gaussX: item.gaussX,
551
           gaussX: item.gaussX,
344
           gaussY: item.gaussY,
552
           gaussY: item.gaussY,
360
         this.calculating = false
568
         this.calculating = false
361
         } else {
569
         } else {
362
           this.$message.error(response.msg)
570
           this.$message.error(response.msg)
571
+          this.calculating = false
363
         }
572
         }
364
     },
573
     },
365
     
574
     
445
   flex-wrap: wrap;
654
   flex-wrap: wrap;
446
 }
655
 }
447
 
656
 
448
-/* 投影面高程设置样式 */
449
-.height-setting {
657
+/* 参数设置区域样式 */
658
+.params-area {
450
   background: #f5f7fa;
659
   background: #f5f7fa;
451
   border-radius: 8px;
660
   border-radius: 8px;
452
-  padding: 12px 20px;
661
+  padding: 16px 20px;
453
   margin-bottom: 20px;
662
   margin-bottom: 20px;
454
   border: 1px solid #e4e7ed;
663
   border: 1px solid #e4e7ed;
664
+}
665
+
666
+.params-row {
455
   display: flex;
667
   display: flex;
456
-  align-items: center;
457
-  gap: 20px;
458
   flex-wrap: wrap;
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
 .result-section {
690
 .result-section {
562
   padding-top: 12px;
791
   padding-top: 12px;
563
   border-top: 1px dashed #e4e7ed;
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
 </style>
826
 </style>

+ 382
- 140
oa-ui/src/views/calculate/tools/geodetictospatial.vue Näytä tiedosto

8
           导入 Excel
8
           导入 Excel
9
         </el-button>
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
         <el-button 
11
         <el-button 
20
           type="warning" 
12
           type="warning" 
21
           @click="handleCalculate" 
13
           @click="handleCalculate" 
36
         </el-button>
28
         </el-button>
37
       </div>
29
       </div>
38
     </div>
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
       <div class="section-header">
77
       <div class="section-header">
43
         <span class="section-title">
78
         <span class="section-title">
44
           <i class="el-icon-tickets"></i>
79
           <i class="el-icon-tickets"></i>
45
-          计算结果
80
+          {{ calculationResults.length > 0 ? '计算结果' : '导入数据' }}
46
         </span>
81
         </span>
47
         <span class="section-info">
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
         </span>
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
         </el-button>
90
         </el-button>
53
       </div>
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
         <el-table-column prop="pointNumber" label="待求点号" width="120" align="center"></el-table-column>
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
           <template slot-scope="scope">
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
           </template>
98
           </template>
61
         </el-table-column>
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
           <template slot-scope="scope">
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
           </template>
103
           </template>
66
         </el-table-column>
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
           <template slot-scope="scope">
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
           </template>
126
           </template>
71
         </el-table-column>
127
         </el-table-column>
72
       </el-table>
128
       </el-table>
75
     <!-- 空状态 -->
131
     <!-- 空状态 -->
76
     <div v-else class="empty-state">
132
     <div v-else class="empty-state">
77
       <i class="el-icon-document"></i>
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
     </div>
136
     </div>
81
     
137
     
82
     <!-- 导入 Excel 对话框 -->
138
     <!-- 导入 Excel 对话框 -->
83
     <el-dialog title="导入 Excel" :visible.sync="importDialogVisible" width="550px" @close="resetImport">
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
       <div style="text-align: center;">
140
       <div style="text-align: center;">
110
         <el-upload
141
         <el-upload
111
           class="upload-area"
142
           class="upload-area"
132
       </span>
163
       </span>
133
     </el-dialog>
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
       </div>
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
       <span slot="footer" class="dialog-footer">
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
       </span>
214
       </span>
179
     </el-dialog>
215
     </el-dialog>
180
   </div>
216
   </div>
193
       importLoading: false,
229
       importLoading: false,
194
       importFile: null,
230
       importFile: null,
195
       calculating: false,
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
   methods: {
253
   methods: {
219
       const formData = new FormData()
273
       const formData = new FormData()
220
       formData.append('file', this.importFile)
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
         this.importLoading = false
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
         return
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
       // 构建请求参数
399
       // 构建请求参数
273
       const requestData = this.importedData.map(item => ({
400
       const requestData = this.importedData.map(item => ({
274
         pointNumber: item.pointNumber,
401
         pointNumber: item.pointNumber,
275
-        coordinateSystem: item.coordinateSystem,
402
+        coordinateSystem: this.commonParams.coordinateSystem,
403
+        projectionHeight: parseFloat(this.commonParams.projectionHeight) || 0,
276
         longitude: parseFloat(item.longitude),
404
         longitude: parseFloat(item.longitude),
277
-        longitudePosition: item.longitudePosition,
405
+        longitudePosition: this.commonParams.longitudePosition,
278
         latitude: parseFloat(item.latitude),
406
         latitude: parseFloat(item.latitude),
279
-        latitudePosition: item.latitudePosition,
407
+        latitudePosition: this.commonParams.latitudePosition,
280
         height: parseFloat(item.height) || 0
408
         height: parseFloat(item.height) || 0
281
       }))
409
       }))
282
       
410
       
290
           pointNumber: item.pointNumber,
418
           pointNumber: item.pointNumber,
291
           coordinateSystem: item.coordinateSystem,
419
           coordinateSystem: item.coordinateSystem,
292
           longitude: item.longitude,
420
           longitude: item.longitude,
293
-          longitudePosition: item.longitudePosition,
294
           latitude: item.latitude,
421
           latitude: item.latitude,
295
-          latitudePosition: item.latitudePosition,
296
           height: item.height,
422
           height: item.height,
423
+          band: item.band,
424
+          longitudePosition: item.longitudePosition,
425
+          latitudePosition: item.latitudePosition,
297
           spatialX: item.spatialX,
426
           spatialX: item.spatialX,
298
           spatialY: item.spatialY,
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
         const failCount = results.length - successCount
433
         const failCount = results.length - successCount
312
         
434
         
313
         this.$message.success(`计算完成,成功 ${successCount} 条${failCount > 0 ? `,失败 ${failCount} 条` : ''}`)
435
         this.$message.success(`计算完成,成功 ${successCount} 条${failCount > 0 ? `,失败 ${failCount} 条` : ''}`)
314
-        this.calculating = false
315
       } else {
436
       } else {
316
         this.$message.error(response.msg)
437
         this.$message.error(response.msg)
317
       }
438
       }
439
+      
440
+      this.calculating = false
318
     },
441
     },
319
     
442
     
320
     // 导出结果
443
     // 导出结果
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
 </script>
548
 </script>
385
 
549
 
386
 <style scoped>
550
 <style scoped>
398
   flex-wrap: wrap;
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
 .result-section {
602
 .result-section {
402
   margin-top: 10px;
603
   margin-top: 10px;
403
 }
604
 }
435
   font-weight: 500;
636
   font-weight: 500;
436
 }
637
 }
437
 
638
 
639
+.empty-cell {
640
+  color: #c0c4cc;
641
+}
642
+
438
 .empty-state {
643
 .empty-state {
439
   text-align: center;
644
   text-align: center;
440
   padding: 60px 20px;
645
   padding: 60px 20px;
502
   padding-top: 12px;
707
   padding-top: 12px;
503
   border-top: 1px dashed #e4e7ed;
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
 </style>
747
 </style>

+ 353
- 119
oa-ui/src/views/calculate/tools/spatialtogeodetic.vue Näytä tiedosto

8
           导入 Excel
8
           导入 Excel
9
         </el-button>
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
         <el-button 
11
         <el-button 
20
           type="warning" 
12
           type="warning" 
21
           @click="handleCalculate" 
13
           @click="handleCalculate" 
36
         </el-button>
28
         </el-button>
37
       </div>
29
       </div>
38
     </div>
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
       <div class="section-header">
60
       <div class="section-header">
43
         <span class="section-title">
61
         <span class="section-title">
44
           <i class="el-icon-tickets"></i>
62
           <i class="el-icon-tickets"></i>
45
-          计算结果
63
+          {{ calculationResults.length > 0 ? '计算结果' : '导入数据' }}
46
         </span>
64
         </span>
47
         <span class="section-info">
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
         </span>
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
         </el-button>
73
         </el-button>
53
       </div>
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
         <el-table-column prop="pointNumber" label="待求点号" width="120" align="center"></el-table-column>
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
           <template slot-scope="scope">
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
           </template>
86
           </template>
61
         </el-table-column>
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
           <template slot-scope="scope">
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
           </template>
91
           </template>
66
         </el-table-column>
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
           <template slot-scope="scope">
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
           </template>
97
           </template>
71
         </el-table-column>
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
           <template slot-scope="scope">
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
           </template>
115
           </template>
76
         </el-table-column>
116
         </el-table-column>
77
         <el-table-column prop="height" label="大地高(m)" width="120" align="center">
117
         <el-table-column prop="height" label="大地高(m)" width="120" align="center">
78
           <template slot-scope="scope">
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
           </template>
121
           </template>
81
         </el-table-column>
122
         </el-table-column>
82
       </el-table>
123
       </el-table>
85
     <!-- 空状态 -->
126
     <!-- 空状态 -->
86
     <div v-else class="empty-state">
127
     <div v-else class="empty-state">
87
       <i class="el-icon-document"></i>
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
     </div>
131
     </div>
91
     
132
     
92
     <!-- 导入 Excel 对话框 -->
133
     <!-- 导入 Excel 对话框 -->
93
     <el-dialog title="导入 Excel" :visible.sync="importDialogVisible" width="550px" @close="resetImport">
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
       <div style="text-align: center;">
135
       <div style="text-align: center;">
118
         <el-upload
136
         <el-upload
119
           class="upload-area"
137
           class="upload-area"
140
       </span>
158
       </span>
141
     </el-dialog>
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
       </div>
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
       <span slot="footer" class="dialog-footer">
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
       </span>
209
       </span>
177
     </el-dialog>
210
     </el-dialog>
178
   </div>
211
   </div>
191
       importLoading: false,
224
       importLoading: false,
192
       importFile: null,
225
       importFile: null,
193
       calculating: false,
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
   methods: {
246
   methods: {
217
       const formData = new FormData()
266
       const formData = new FormData()
218
       formData.append('file', this.importFile)
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
         this.importLoading = false
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
         return
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
       // 构建请求参数
392
       // 构建请求参数
269
       const requestData = this.importedData.map(item => ({
393
       const requestData = this.importedData.map(item => ({
270
         pointNumber: item.pointNumber,
394
         pointNumber: item.pointNumber,
271
-        coordinateSystem: item.coordinateSystem,
395
+        projectionHeight: parseFloat(this.commonParams.projectionHeight) || 0,
396
+        coordinateSystem: this.commonParams.coordinateSystem,
272
         spatialX: parseFloat(item.spatialX),
397
         spatialX: parseFloat(item.spatialX),
273
         spatialY: parseFloat(item.spatialY),
398
         spatialY: parseFloat(item.spatialY),
274
         spatialZ: parseFloat(item.spatialZ)
399
         spatialZ: parseFloat(item.spatialZ)
282
         // 更新计算结果
407
         // 更新计算结果
283
         this.calculationResults = results.map(item => ({
408
         this.calculationResults = results.map(item => ({
284
           pointNumber: item.pointNumber,
409
           pointNumber: item.pointNumber,
285
-          coordinateSystem: item.coordinateSystem,
286
           spatialX: item.spatialX,
410
           spatialX: item.spatialX,
287
           spatialY: item.spatialY,
411
           spatialY: item.spatialY,
288
           spatialZ: item.spatialZ,
412
           spatialZ: item.spatialZ,
289
           longitude: item.longitude,
413
           longitude: item.longitude,
290
-          longitudePosition: item.longitudePosition,
291
           latitude: item.latitude,
414
           latitude: item.latitude,
415
+          height: item.height,
416
+          coordinateSystem: item.coordinateSystem,
417
+          longitudePosition: item.longitudePosition,
292
           latitudePosition: item.latitudePosition,
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
         const failCount = results.length - successCount
423
         const failCount = results.length - successCount
306
         
424
         
307
         this.$message.success(`计算完成,成功 ${successCount} 条${failCount > 0 ? `,失败 ${failCount} 条` : ''}`)
425
         this.$message.success(`计算完成,成功 ${successCount} 条${failCount > 0 ? `,失败 ${failCount} 条` : ''}`)
308
-        this.calculating = false
309
       } else {
426
       } else {
310
         this.$message.error(response.msg)
427
         this.$message.error(response.msg)
311
       }
428
       }
429
+      
430
+      this.calculating = false
312
     },
431
     },
313
     
432
     
314
     // 导出结果
433
     // 导出结果
373
       }
492
       }
374
       return types[system] || 'info'
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
 </script>
538
 </script>
496
   padding-top: 12px;
656
   padding-top: 12px;
497
   border-top: 1px dashed #e4e7ed;
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
 </style>
733
 </style>

+ 342
- 154
oa-ui/src/views/calculate/tools/utmbandchange.vue Näytä tiedosto

8
           导入Excel
8
           导入Excel
9
         </el-button>
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
         <el-button 
11
         <el-button 
20
           type="warning" 
12
           type="warning" 
21
           @click="handleCalculate" 
13
           @click="handleCalculate" 
37
       </div>
29
       </div>
38
     </div>
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
       </div>
80
       </div>
54
     </div>
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
       <div class="section-header">
85
       <div class="section-header">
59
         <span class="section-title">
86
         <span class="section-title">
60
           <i class="el-icon-tickets"></i>
87
           <i class="el-icon-tickets"></i>
61
-          计算结果
88
+          {{ calculationResults.length > 0 ? '计算结果' : '导入数据' }}
62
         </span>
89
         </span>
63
         <span class="section-info">
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
         </span>
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
         </el-button>
100
         </el-button>
69
       </div>
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
         <el-table-column prop="pointNumber" label="待求点号" width="120" align="center"></el-table-column>
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
           <template slot-scope="scope">
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
           </template>
108
           </template>
77
         </el-table-column>
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
           <template slot-scope="scope">
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
           </template>
125
           </template>
82
         </el-table-column>
126
         </el-table-column>
83
       </el-table>
127
       </el-table>
86
     <!-- 空状态 -->
130
     <!-- 空状态 -->
87
     <div v-else class="empty-state">
131
     <div v-else class="empty-state">
88
       <i class="el-icon-document"></i>
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
     </div>
135
     </div>
92
     
136
     
93
     <!-- 导入Excel对话框 -->
137
     <!-- 导入Excel对话框 -->
94
     <el-dialog title="导入Excel" :visible.sync="importDialogVisible" width="550px" @close="resetImport">
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
       <div style="text-align: center;">
139
       <div style="text-align: center;">
122
         <el-upload
140
         <el-upload
123
           class="upload-area"
141
           class="upload-area"
144
       </span>
162
       </span>
145
     </el-dialog>
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
       </div>
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
       <span slot="footer" class="dialog-footer">
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
       </span>
206
       </span>
196
     </el-dialog>
207
     </el-dialog>
197
   </div>
208
   </div>
201
 import { calculate, importExcel,exportExcel,downloadTemplate } from '@/api/calculate/utmbandchange'
212
 import { calculate, importExcel,exportExcel,downloadTemplate } from '@/api/calculate/utmbandchange'
202
 
213
 
203
 export default {
214
 export default {
204
-  name: 'UTMBandChangeTool',    
215
+  name: 'UTMBandChangeTool',
205
   data() {
216
   data() {
206
     return {
217
     return {
207
       importedData: [],        // 导入的原始数据
218
       importedData: [],        // 导入的原始数据
210
       importLoading: false,
221
       importLoading: false,
211
       importFile: null,
222
       importFile: null,
212
       calculating: false,
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
   watch: {
247
   watch: {
254
       const formData = new FormData()
284
       const formData = new FormData()
255
       formData.append('file', this.importFile)
285
       formData.append('file', this.importFile)
256
       
286
       
287
+      try {
257
         const response = await importExcel(formData)
288
         const response = await importExcel(formData)
258
         
289
         
259
         if (response.code === 200) {
290
         if (response.code === 200) {
260
           const result = response.data
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
           this.calculationResults = []
300
           this.calculationResults = []
275
           
301
           
302
+          // 自动匹配字段
303
+          this.autoMatchFields()
304
+          
305
+          // 显示字段映射对话框
306
+          this.mappingDialogVisible = true
307
+          
276
           this.$message.success(`${response.msg}`)
308
           this.$message.success(`${response.msg}`)
277
           this.importDialogVisible = false
309
           this.importDialogVisible = false
278
           this.importLoading = false
310
           this.importLoading = false
279
         } else {
311
         } else {
280
           this.$message.error(response.msg)
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
         return
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
       }
404
       }
305
       
405
       
306
       this.calculating = true
406
       this.calculating = true
307
-      const results = []
308
-      let successCount = 0
309
-      let failCount = 0
310
       
407
       
311
       // 构建请求参数
408
       // 构建请求参数
312
       const requestData = this.importedData.map(item => ({
409
       const requestData = this.importedData.map(item => ({
313
         pointNumber: item.pointNumber,
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
         projectionHeight: this.projectionHeight
418
         projectionHeight: this.projectionHeight
322
       }))
419
       }))
323
       
420
       
332
           coordinateSystem: item.coordinateSystem,
429
           coordinateSystem: item.coordinateSystem,
333
           utmX: item.utmX,
430
           utmX: item.utmX,
334
           utmY: item.utmY,
431
           utmY: item.utmY,
432
+          newUtmX: item.newUtmX,
433
+          newUtmY: item.newUtmY,
335
           band: item.band,
434
           band: item.band,
336
           newBand: item.newBand,
435
           newBand: item.newBand,
337
           longitudePosition: item.longitudePosition,
436
           longitudePosition: item.longitudePosition,
338
           latitudePosition: item.latitudePosition,
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
         const failCount = results.length - successCount
442
         const failCount = results.length - successCount
354
         
443
         
355
         this.$message.success(`计算完成,成功 ${successCount} 条${failCount > 0 ? `,失败 ${failCount} 条` : ''}`)
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
     }
512
     }
423
     return types[system] || 'info'
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
 </script>
551
 </script>
441
   flex-wrap: wrap;
565
   flex-wrap: wrap;
442
 }
566
 }
443
 
567
 
444
-/* 投影面高程设置样式 */
445
-.height-setting {
568
+/* 参数设置区域样式 */
569
+.params-area {
446
   background: #f5f7fa;
570
   background: #f5f7fa;
447
   border-radius: 8px;
571
   border-radius: 8px;
448
-  padding: 12px 20px;
572
+  padding: 16px 20px;
449
   margin-bottom: 20px;
573
   margin-bottom: 20px;
450
   border: 1px solid #e4e7ed;
574
   border: 1px solid #e4e7ed;
575
+}
576
+
577
+.params-row {
451
   display: flex;
578
   display: flex;
452
-  align-items: center;
453
-  gap: 20px;
454
   flex-wrap: wrap;
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
 .result-section {
605
 .result-section {
491
   font-weight: 500;
639
   font-weight: 500;
492
 }
640
 }
493
 
641
 
642
+.empty-cell {
643
+  color: #c0c4cc;
644
+}
645
+
494
 .empty-state {
646
 .empty-state {
495
   text-align: center;
647
   text-align: center;
496
   padding: 60px 20px;
648
   padding: 60px 20px;
558
   padding-top: 12px;
710
   padding-top: 12px;
559
   border-top: 1px dashed #e4e7ed;
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
 </style>
749
 </style>

+ 323
- 138
oa-ui/src/views/calculate/tools/utmnegative.vue Näytä tiedosto

8
           导入Excel
8
           导入Excel
9
         </el-button>
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
         <el-button 
11
         <el-button 
20
           type="warning" 
12
           type="warning" 
21
           @click="handleCalculate" 
13
           @click="handleCalculate" 
37
       </div>
29
       </div>
38
     </div>
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
       </div>
75
       </div>
54
     </div>
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
       <div class="section-header">
80
       <div class="section-header">
59
         <span class="section-title">
81
         <span class="section-title">
60
           <i class="el-icon-tickets"></i>
82
           <i class="el-icon-tickets"></i>
61
-          计算结果
83
+          {{ calculationResults.length > 0 ? '计算结果' : '导入数据' }}
62
         </span>
84
         </span>
63
         <span class="section-info">
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
         </span>
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
         </el-button>
94
         </el-button>
69
       </div>
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
         <el-table-column prop="pointNumber" label="待求点号" width="120" align="center"></el-table-column>
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
           <template slot-scope="scope">
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
           </template>
102
           </template>
77
         </el-table-column>
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
           <template slot-scope="scope">
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
           </template>
119
           </template>
82
         </el-table-column>
120
         </el-table-column>
83
       </el-table>
121
       </el-table>
86
     <!-- 空状态 -->
124
     <!-- 空状态 -->
87
     <div v-else class="empty-state">
125
     <div v-else class="empty-state">
88
       <i class="el-icon-document"></i>
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
     </div>
129
     </div>
92
     
130
     
93
     <!-- 导入Excel对话框 -->
131
     <!-- 导入Excel对话框 -->
94
     <el-dialog title="导入Excel" :visible.sync="importDialogVisible" width="550px" @close="resetImport">
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
       <div style="text-align: center;">
133
       <div style="text-align: center;">
121
         <el-upload
134
         <el-upload
122
           class="upload-area"
135
           class="upload-area"
142
         </el-button>
155
         </el-button>
143
       </span>
156
       </span>
144
     </el-dialog>
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
       </div>
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
       <span slot="footer" class="dialog-footer">
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
       </span>
200
       </span>
185
     </el-dialog>
201
     </el-dialog>
186
  </div>
202
  </div>
199
       importLoading: false,
215
       importLoading: false,
200
       importFile: null,
216
       importFile: null,
201
       calculating: false,
217
       calculating: false,
202
-      previewDialogVisible: false,
203
       projectionHeight: 0,
218
       projectionHeight: 0,
204
       heightTimer: null,
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
   watch: {
240
   watch: {
244
       const formData = new FormData()
277
       const formData = new FormData()
245
       formData.append('file', this.importFile)
278
       formData.append('file', this.importFile)
246
       
279
       
280
+      try {
247
         const response = await importExcel(formData)
281
         const response = await importExcel(formData)
248
         
282
         
249
         if (response.code === 200) {
283
         if (response.code === 200) {
250
           const result = response.data
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
           this.calculationResults = []
293
           this.calculationResults = []
265
           
294
           
295
+          // 自动匹配字段
296
+          this.autoMatchFields()
297
+          
298
+          // 显示字段映射对话框
299
+          this.mappingDialogVisible = true
300
+          
266
           this.$message.success(`${response.msg}`)
301
           this.$message.success(`${response.msg}`)
267
           this.importDialogVisible = false
302
           this.importDialogVisible = false
268
           this.importLoading = false
303
           this.importLoading = false
269
         } else {
304
         } else {
270
           this.$message.error(response.msg)
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
         return
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
       }
397
       }
295
       
398
       
296
       this.calculating = true
399
       this.calculating = true
297
-      const results = []
298
-      let successCount = 0
299
-      let failCount = 0
300
       
400
       
301
       // 构建请求参数
401
       // 构建请求参数
302
       const requestData = this.importedData.map(item => ({
402
       const requestData = this.importedData.map(item => ({
303
         pointNumber: item.pointNumber,
403
         pointNumber: item.pointNumber,
304
-        coordinateSystem: item.coordinateSystem,
404
+        coordinateSystem: this.commonParams.coordinateSystem,
305
         utmX: parseFloat(item.utmX),
405
         utmX: parseFloat(item.utmX),
306
         utmY: parseFloat(item.utmY),
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
         projectionHeight: this.projectionHeight
410
         projectionHeight: this.projectionHeight
311
       }))
411
       }))
312
       
412
       
321
           coordinateSystem: item.coordinateSystem,
421
           coordinateSystem: item.coordinateSystem,
322
           utmX: item.utmX,
422
           utmX: item.utmX,
323
           utmY: item.utmY,
423
           utmY: item.utmY,
324
-          band: parseInt(item.band),
325
           longitude: item.longitude,
424
           longitude: item.longitude,
326
-          longitudePosition: item.longitudePosition,
327
           latitude: item.latitude,
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
         const failCount = results.length - successCount
433
         const failCount = results.length - successCount
341
         
434
         
342
         this.$message.success(`计算完成,成功 ${successCount} 条${failCount > 0 ? `,失败 ${failCount} 条` : ''}`)
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
     }
503
     }
410
     return types[system] || 'info'
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
 </script>
542
 </script>
428
   flex-wrap: wrap;
556
   flex-wrap: wrap;
429
 }
557
 }
430
 
558
 
431
-/* 投影面高程设置样式 */
432
-.height-setting {
559
+/* 参数设置区域样式 */
560
+.params-area {
433
   background: #f5f7fa;
561
   background: #f5f7fa;
434
   border-radius: 8px;
562
   border-radius: 8px;
435
-  padding: 12px 20px;
563
+  padding: 16px 20px;
436
   margin-bottom: 20px;
564
   margin-bottom: 20px;
437
   border: 1px solid #e4e7ed;
565
   border: 1px solid #e4e7ed;
566
+}
567
+
568
+.params-row {
438
   display: flex;
569
   display: flex;
439
-  align-items: center;
440
-  gap: 20px;
441
   flex-wrap: wrap;
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
 .result-section {
592
 .result-section {
545
   padding-top: 12px;
693
   padding-top: 12px;
546
   border-top: 1px dashed #e4e7ed;
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
 </style>
733
 </style>

+ 366
- 179
oa-ui/src/views/calculate/tools/utmpositive.vue Näytä tiedosto

8
           导入Excel
8
           导入Excel
9
         </el-button>
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
         <el-button 
11
         <el-button 
20
           type="warning" 
12
           type="warning" 
21
           @click="handleCalculate" 
13
           @click="handleCalculate" 
37
       </div>
29
       </div>
38
     </div>
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
       </div>
75
       </div>
54
     </div>
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
       <div class="section-header">
80
       <div class="section-header">
59
         <span class="section-title">
81
         <span class="section-title">
60
           <i class="el-icon-tickets"></i>
82
           <i class="el-icon-tickets"></i>
61
-          计算结果
83
+          {{ calculationResults.length > 0 ? '计算结果' : '导入数据' }}
62
         </span>
84
         </span>
63
         <span class="section-info">
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
         </span>
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
         </el-button>
94
         </el-button>
69
       </div>
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
         <el-table-column prop="pointNumber" label="待求点号" width="120" align="center"></el-table-column>
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
           <template slot-scope="scope">
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
           </template>
107
           </template>
77
         </el-table-column>
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
           <template slot-scope="scope">
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
           </template>
113
           </template>
82
         </el-table-column>
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
           <template slot-scope="scope">
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
           </template>
125
           </template>
87
         </el-table-column>
126
         </el-table-column>
88
       </el-table>
127
       </el-table>
91
     <!-- 空状态 -->
130
     <!-- 空状态 -->
92
     <div v-else class="empty-state">
131
     <div v-else class="empty-state">
93
       <i class="el-icon-document"></i>
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
     </div>
135
     </div>
97
     
136
     
98
     <!-- 导入Excel对话框 -->
137
     <!-- 导入Excel对话框 -->
99
     <el-dialog title="导入Excel" :visible.sync="importDialogVisible" width="550px" @close="resetImport">
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
       <div style="text-align: center;">
139
       <div style="text-align: center;">
126
         <el-upload
140
         <el-upload
127
           class="upload-area"
141
           class="upload-area"
147
         </el-button>
161
         </el-button>
148
       </span>
162
       </span>
149
     </el-dialog>
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
       </div>
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
       <span slot="footer" class="dialog-footer">
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
       </span>
206
       </span>
190
     </el-dialog>
207
     </el-dialog>
191
  </div>
208
  </div>
204
       importLoading: false,
221
       importLoading: false,
205
       importFile: null,
222
       importFile: null,
206
       calculating: false,
223
       calculating: false,
207
-      previewDialogVisible: false,
208
       projectionHeight: 0,
224
       projectionHeight: 0,
209
       heightTimer: null,
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
   watch: {
246
   watch: {
249
       const formData = new FormData()
283
       const formData = new FormData()
250
       formData.append('file', this.importFile)
284
       formData.append('file', this.importFile)
251
       
285
       
286
+      try {
252
         const response = await importExcel(formData)
287
         const response = await importExcel(formData)
253
         
288
         
254
         if (response.code === 200) {
289
         if (response.code === 200) {
255
           const result = response.data
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
           this.calculationResults = []
299
           this.calculationResults = []
270
           
300
           
301
+          // 自动匹配字段
302
+          this.autoMatchFields()
303
+          
304
+          // 显示字段映射对话框
305
+          this.mappingDialogVisible = true
306
+          
271
           this.$message.success(`${response.msg}`)
307
           this.$message.success(`${response.msg}`)
272
           this.importDialogVisible = false
308
           this.importDialogVisible = false
273
           this.importLoading = false
309
           this.importLoading = false
274
         } else {
310
         } else {
275
           this.$message.error(response.msg)
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
         return
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
       }
403
       }
300
       
404
       
301
       this.calculating = true
405
       this.calculating = true
302
-      const results = []
303
-      let successCount = 0
304
-      let failCount = 0
305
       
406
       
306
       // 构建请求参数
407
       // 构建请求参数
307
       const requestData = this.importedData.map(item => ({
408
       const requestData = this.importedData.map(item => ({
308
         pointNumber: item.pointNumber,
409
         pointNumber: item.pointNumber,
309
-        coordinateSystem: item.coordinateSystem,
410
+        coordinateSystem: this.commonParams.coordinateSystem,
310
         longitude: parseFloat(item.longitude),
411
         longitude: parseFloat(item.longitude),
311
-        longitudePosition: item.longitudePosition,
412
+        longitudePosition: this.commonParams.longitudePosition,
312
         latitude: parseFloat(item.latitude),
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
         projectionHeight: this.projectionHeight
416
         projectionHeight: this.projectionHeight
316
       }))
417
       }))
317
       
418
       
325
           pointNumber: item.pointNumber,
426
           pointNumber: item.pointNumber,
326
           coordinateSystem: item.coordinateSystem,
427
           coordinateSystem: item.coordinateSystem,
327
           longitude: parseFloat(item.longitude),
428
           longitude: parseFloat(item.longitude),
328
-          longitudePosition: item.longitudePosition,
329
           latitude: parseFloat(item.latitude),
429
           latitude: parseFloat(item.latitude),
330
-          latitudePosition: item.latitudePosition,
331
-          band: parseInt(item.band),
332
-          projectionHeight: this.projectionHeight,
333
           utmX: item.utmX,
430
           utmX: item.utmX,
334
           utmY: item.utmY,
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
         const failCount = results.length - successCount
440
         const failCount = results.length - successCount
348
         
441
         
349
         this.$message.success(`计算完成,成功 ${successCount} 条${failCount > 0 ? `,失败 ${failCount} 条` : ''}`)
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
         window.URL.revokeObjectURL(url)
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
 </script>
551
 </script>
435
   flex-wrap: wrap;
565
   flex-wrap: wrap;
436
 }
566
 }
437
 
567
 
438
-/* 投影面高程设置样式 */
439
-.height-setting {
568
+/* 参数设置区域样式 */
569
+.params-area {
440
   background: #f5f7fa;
570
   background: #f5f7fa;
441
   border-radius: 8px;
571
   border-radius: 8px;
442
-  padding: 12px 20px;
572
+  padding: 16px 20px;
443
   margin-bottom: 20px;
573
   margin-bottom: 20px;
444
   border: 1px solid #e4e7ed;
574
   border: 1px solid #e4e7ed;
575
+}
576
+
577
+.params-row {
445
   display: flex;
578
   display: flex;
446
-  align-items: center;
447
-  gap: 20px;
448
   flex-wrap: wrap;
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
 .result-section {
601
 .result-section {
552
   padding-top: 12px;
702
   padding-top: 12px;
553
   border-top: 1px dashed #e4e7ed;
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
 </style>
742
 </style>

Loading…
Peruuta
Tallenna