Sfoglia il codice sorgente

新增计算工具

qyx 16 ore fa
parent
commit
705a156902

+ 8
- 0
oa-back/pom.xml Vedi File

@@ -178,6 +178,13 @@
178 178
                 <version>${cmc.version}</version>
179 179
             </dependency>
180 180
 
181
+            <!-- 高斯正算-->
182
+            <dependency>
183
+                <groupId>com.ruoyi</groupId>
184
+                <artifactId>ruoyi-calculate</artifactId>
185
+                <version>${cmc.version}</version>
186
+            </dependency>
187
+
181 188
             <dependency>
182 189
                 <groupId>com.google.protobuf</groupId>
183 190
                 <artifactId>protobuf-java</artifactId>
@@ -224,6 +231,7 @@
224 231
         <module>ruoyi-generator</module>
225 232
         <module>ruoyi-common</module>
226 233
         <module>ruoyi-flowable</module>
234
+		<module>ruoyi-calculate</module>
227 235
     </modules>
228 236
     <packaging>pom</packaging>
229 237
 

+ 6
- 0
oa-back/ruoyi-admin/pom.xml Vedi File

@@ -74,6 +74,12 @@
74 74
             <artifactId>ruoyi-llm</artifactId>
75 75
         </dependency>
76 76
 
77
+        <!-- 高斯正算-->
78
+        <dependency>
79
+            <groupId>com.ruoyi</groupId>
80
+            <artifactId>ruoyi-calculate</artifactId>
81
+        </dependency>
82
+
77 83
     </dependencies>
78 84
 
79 85
     <build>

+ 28
- 0
oa-back/ruoyi-calculate/pom.xml Vedi File

@@ -0,0 +1,28 @@
1
+<?xml version="1.0" encoding="UTF-8"?>
2
+<project xmlns="http://maven.apache.org/POM/4.0.0"
3
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
5
+    <modelVersion>4.0.0</modelVersion>
6
+    <parent>
7
+          <groupId>com.ruoyi</groupId>
8
+          <artifactId>ruoyi</artifactId>
9
+          <version>3.8.7</version>
10
+    </parent>
11
+
12
+    <artifactId>ruoyi-calculate</artifactId>
13
+    <properties>
14
+        <maven.compiler.source>17</maven.compiler.source>
15
+        <maven.compiler.target>17</maven.compiler.target>
16
+    </properties>
17
+    <dependencies>
18
+        <!-- 核心依赖 -->
19
+        <dependency>
20
+            <groupId>com.ruoyi</groupId>
21
+            <artifactId>ruoyi-common</artifactId>
22
+        </dependency>
23
+        <dependency>
24
+            <groupId>com.ruoyi</groupId>
25
+            <artifactId>ruoyi-framework</artifactId>
26
+        </dependency>
27
+    </dependencies>
28
+</project>

+ 132
- 0
oa-back/ruoyi-calculate/src/main/java/com/ruoyi/web/calculate/controller/CmcGaussPositiveController.java Vedi File

@@ -0,0 +1,132 @@
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.PutMapping;
11
+import org.springframework.web.bind.annotation.DeleteMapping;
12
+import org.springframework.web.bind.annotation.PathVariable;
13
+import org.springframework.web.bind.annotation.RequestBody;
14
+import org.springframework.web.bind.annotation.RequestMapping;
15
+import org.springframework.web.bind.annotation.RequestParam;
16
+import org.springframework.web.bind.annotation.RestController;
17
+import org.springframework.web.multipart.MultipartFile;
18
+
19
+import com.ruoyi.common.annotation.Log;
20
+import com.ruoyi.common.core.controller.BaseController;
21
+import com.ruoyi.common.core.domain.AjaxResult;
22
+import com.ruoyi.common.enums.BusinessType;
23
+import com.ruoyi.web.calculate.domain.CmcGaussPositive;
24
+import com.ruoyi.web.calculate.service.ICmcGaussPositiveService;
25
+import com.ruoyi.web.calculate.vo.CmcGaussPositiveTemplate;
26
+import com.ruoyi.common.utils.poi.ExcelUtil;
27
+import com.ruoyi.common.core.page.TableDataInfo;
28
+
29
+/**
30
+ * 高斯正算Controller
31
+ * 
32
+ * @author ruoyi
33
+ * @date 2026-04-21
34
+ */
35
+@RestController
36
+@RequestMapping("/calculate/gaussPositive")
37
+public class CmcGaussPositiveController extends BaseController
38
+{
39
+    @Autowired
40
+    private ICmcGaussPositiveService cmcGaussPositiveService;
41
+
42
+     /**
43
+     * 导入Excel数据
44
+     */
45
+    @PostMapping("/import")
46
+    public AjaxResult importExcel(@RequestParam("file") MultipartFile file)
47
+    {
48
+        try {
49
+            // 检查文件是否为空
50
+            if (file.isEmpty()) {
51
+                return AjaxResult.error("请选择要导入的文件!");
52
+            }
53
+
54
+            // 检查文件格式
55
+            String fileName = file.getOriginalFilename();
56
+            if (fileName == null || (!fileName.endsWith(".xls") && !fileName.endsWith(".xlsx"))) {
57
+                return AjaxResult.error("请上传Excel文件(.xls或.xlsx格式)!");
58
+            }
59
+
60
+            // 调用Service层解析Excel并返回数据
61
+            return cmcGaussPositiveService.importExcelData(file);
62
+
63
+        } catch (Exception e) {
64
+            e.printStackTrace();
65
+            return AjaxResult.error("导入失败:" + e.getMessage());
66
+        }
67
+    }
68
+
69
+    /**
70
+     * 导出高斯正算列表
71
+     */
72
+    @Log(title = "高斯正算", businessType = BusinessType.EXPORT)
73
+    @PostMapping("/export")
74
+    public void export(HttpServletResponse response, @RequestBody List<CmcGaussPositive> dataList)
75
+    {
76
+        if (dataList == null || dataList.isEmpty()) {
77
+            throw new RuntimeException("没有需要导出的数据");
78
+        }
79
+        
80
+        List<CmcGaussPositive> exportList = new ArrayList<>();
81
+        for (CmcGaussPositive item : dataList) {
82
+            CmcGaussPositive exportItem = new CmcGaussPositive();
83
+            exportItem.setPointNumber(item.getPointNumber());
84
+            exportItem.setCoordinateSystem(item.getCoordinateSystem());
85
+            exportItem.setLongitude(item.getLongitude());
86
+            exportItem.setLongitudePosition(item.getLongitudePosition());
87
+            exportItem.setLatitude(item.getLatitude());
88
+            exportItem.setLatitudePosition(item.getLatitudePosition());
89
+            exportItem.setZone(item.getZone());
90
+            exportItem.setBandwidth(item.getBandwidth());
91
+            exportItem.setProjectionHeight(item.getProjectionHeight());
92
+            exportItem.setGaussX(item.getGaussX());
93
+            exportItem.setGaussY(item.getGaussY());
94
+            exportItem.setMeridianConvergence(item.getMeridianConvergence());
95
+            exportList.add(exportItem);
96
+        }
97
+        
98
+        ExcelUtil<CmcGaussPositive> util = new ExcelUtil<CmcGaussPositive>(CmcGaussPositive.class);
99
+        util.exportExcel(response, exportList, "高斯正算结果");
100
+    }
101
+
102
+    /**
103
+     * 执行高斯正算(仅计算,不保存)
104
+     */
105
+    @PostMapping("/calculate")
106
+    public AjaxResult calculate(@RequestBody List<CmcGaussPositive> dataList)
107
+    {
108
+        List<CmcGaussPositive> result = cmcGaussPositiveService.calculateGaussPositive(dataList);
109
+        return success(result);
110
+    }
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.setZone(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
+}

+ 252
- 0
oa-back/ruoyi-calculate/src/main/java/com/ruoyi/web/calculate/domain/CmcGaussPositive.java Vedi File

@@ -0,0 +1,252 @@
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_gauss_positive
14
+ * 
15
+ * @author ruoyi
16
+ * @date 2026-04-21
17
+ */
18
+public class CmcGaussPositive 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 = "坐标系")
31
+    private String coordinateSystem;
32
+
33
+    /** 大地经度 */
34
+    @Excel(name = "大地经度" , cellType = ColumnType.NUMERIC)
35
+    private Double longitude;
36
+
37
+    /** 经度位置 */
38
+    @Excel(name = "经度位置")
39
+    private String longitudePosition;
40
+
41
+    /** 大地纬度 */
42
+    @Excel(name = "大地纬度", cellType = ColumnType.NUMERIC)
43
+    private Double latitude;
44
+
45
+    /** 纬度位置 */
46
+    @Excel(name = "纬度位置")
47
+    private String latitudePosition;
48
+
49
+    /** 带号 */
50
+    @Excel(name = "带号", cellType = ColumnType.NUMERIC)
51
+    private Integer zone;
52
+
53
+    /** 带宽 */
54
+    @Excel(name = "带宽", cellType = ColumnType.NUMERIC)
55
+    private Integer bandwidth;
56
+
57
+    /** 带宽 */
58
+    @Excel(name = "投影面高程", cellType = ColumnType.NUMERIC)
59
+    private Integer projectionHeight;
60
+
61
+    /** 高斯坐标x */
62
+    @Excel(name = "高斯坐标x", cellType = ColumnType.NUMERIC)
63
+    private Double gaussX;
64
+
65
+    /** 高斯坐标y */
66
+    @Excel(name = "高斯坐标y", cellType = ColumnType.NUMERIC)
67
+    private Double gaussY;
68
+
69
+    /** 子午线收敛角γ */
70
+    @Excel(name = "子午线收敛角γ", cellType = ColumnType.NUMERIC)
71
+    private Double meridianConvergence;
72
+
73
+    public void setId(Long id) 
74
+    {
75
+        this.id = id;
76
+    }
77
+
78
+    public Long getId() 
79
+    {
80
+        return id;
81
+    }
82
+    public void setPointNumber(String pointNumber) 
83
+    {
84
+        this.pointNumber = pointNumber;
85
+    }
86
+
87
+    public String getPointNumber() 
88
+    {
89
+        return pointNumber;
90
+    }
91
+    public void setCoordinateSystem(String coordinateSystem) 
92
+    {
93
+        this.coordinateSystem = coordinateSystem;
94
+    }
95
+
96
+    public String getCoordinateSystem() 
97
+    {
98
+        return coordinateSystem;
99
+    }
100
+     public void setLongitude(Double longitude) 
101
+    {
102
+        // 大地经度保留10位小数
103
+        if (longitude != null) {
104
+            BigDecimal bd = new BigDecimal(longitude);
105
+            this.longitude = bd.setScale(10, RoundingMode.HALF_UP).doubleValue();
106
+        } else {
107
+            this.longitude = null;
108
+        }
109
+    }
110
+
111
+    public Double getLongitude() 
112
+    {
113
+        return longitude;
114
+    }
115
+    
116
+    public void setLongitudePosition(String longitudePosition) 
117
+    {
118
+        this.longitudePosition = longitudePosition;
119
+    }
120
+
121
+    public String getLongitudePosition() 
122
+    {
123
+        return longitudePosition;
124
+    }
125
+    
126
+    public void setLatitude(Double latitude) 
127
+    {
128
+        // 大地纬度保留10位小数
129
+        if (latitude != null) {
130
+            BigDecimal bd = new BigDecimal(latitude);
131
+            this.latitude = bd.setScale(10, RoundingMode.HALF_UP).doubleValue();
132
+        } else {
133
+            this.latitude = null;
134
+        }
135
+    }
136
+
137
+    public Double getLatitude() 
138
+    {
139
+        return latitude;
140
+    }
141
+    
142
+    public void setLatitudePosition(String latitudePosition) 
143
+    {
144
+        this.latitudePosition = latitudePosition;
145
+    }
146
+
147
+    public String getLatitudePosition() 
148
+    {
149
+        return latitudePosition;
150
+    }
151
+    
152
+    public void setZone(Integer zone) 
153
+    {
154
+        this.zone = zone;
155
+    }
156
+
157
+    public Integer getZone() 
158
+    {
159
+        return zone;
160
+    }
161
+    
162
+    public void setBandwidth(Integer bandwidth) 
163
+    {
164
+        this.bandwidth = bandwidth;
165
+    }
166
+
167
+    public Integer getBandwidth() 
168
+    {
169
+        return bandwidth;
170
+    }
171
+    
172
+    public void setProjectionHeight(Integer projectionHeight) 
173
+    {
174
+        this.projectionHeight = projectionHeight;
175
+    }
176
+
177
+    public Integer getProjectionHeight() 
178
+    {
179
+        return projectionHeight;
180
+    }
181
+    
182
+    public void setGaussX(Double gaussX) 
183
+    {
184
+        // 高斯X坐标保留4位小数
185
+        if (gaussX != null) {
186
+            BigDecimal bd = new BigDecimal(gaussX);
187
+            this.gaussX = bd.setScale(4, RoundingMode.HALF_UP).doubleValue();
188
+        } else {
189
+            this.gaussX = null;
190
+        }
191
+    }
192
+
193
+    public Double getGaussX() 
194
+    {
195
+        return gaussX;
196
+    }
197
+    
198
+    public void setGaussY(Double gaussY) 
199
+    {
200
+        // 高斯Y坐标保留4位小数
201
+        if (gaussY != null) {
202
+            BigDecimal bd = new BigDecimal(gaussY);
203
+            this.gaussY = bd.setScale(4, RoundingMode.HALF_UP).doubleValue();
204
+        } else {
205
+            this.gaussY = null;
206
+        }
207
+    }
208
+
209
+    public Double getGaussY() 
210
+    {
211
+        return gaussY;
212
+    }
213
+    
214
+    public void setMeridianConvergence(Double meridianConvergence) 
215
+    {
216
+        // 子午线收敛角保留7位小数
217
+        if (meridianConvergence != null) {
218
+            BigDecimal bd = new BigDecimal(meridianConvergence);
219
+            this.meridianConvergence = bd.setScale(7, RoundingMode.HALF_UP).doubleValue();
220
+        } else {
221
+            this.meridianConvergence = null;
222
+        }
223
+    }
224
+
225
+    public Double getMeridianConvergence() 
226
+    {
227
+        return meridianConvergence;
228
+    }
229
+
230
+
231
+    @Override
232
+    public String toString() {
233
+        return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)
234
+            .append("id", getId())
235
+            .append("pointNumber", getPointNumber())
236
+            .append("coordinateSystem", getCoordinateSystem())
237
+            .append("longitude", getLongitude())
238
+            .append("longitudePosition", getLongitudePosition())
239
+            .append("latitude", getLatitude())
240
+            .append("latitudePosition", getLatitudePosition())
241
+            .append("zone", getZone())
242
+            .append("bandwidth", getBandwidth())
243
+            .append("gaussX", getGaussX())
244
+            .append("gaussY", getGaussY())
245
+            .append("meridianConvergence", getMeridianConvergence())
246
+            .append("createTime", getCreateTime())
247
+            .append("createBy", getCreateBy())
248
+            .append("updateTime", getUpdateTime())
249
+            .append("updateBy", getUpdateBy())
250
+            .toString();
251
+    }
252
+}

+ 36
- 0
oa-back/ruoyi-calculate/src/main/java/com/ruoyi/web/calculate/service/ICmcGaussPositiveService.java Vedi File

@@ -0,0 +1,36 @@
1
+package com.ruoyi.web.calculate.service;
2
+
3
+import java.util.List;
4
+
5
+import javax.servlet.http.HttpServletResponse;
6
+
7
+import org.springframework.web.multipart.MultipartFile;
8
+
9
+import com.ruoyi.common.core.domain.AjaxResult;
10
+import com.ruoyi.web.calculate.domain.CmcGaussPositive;
11
+
12
+/**
13
+ * 高斯正算服务接口
14
+ * 
15
+ * @author ruoyi
16
+ * @date 2026-04-21
17
+ */
18
+public interface ICmcGaussPositiveService 
19
+{
20
+     /**
21
+     * 导入Excel数据
22
+     * 
23
+     * @param file Excel文件
24
+     * @return 导入结果
25
+     */
26
+     public AjaxResult importExcelData(MultipartFile file);
27
+    
28
+    /**
29
+     * 执行高斯正算
30
+     * 
31
+     * @param cmcGaussPositive 高斯正算参数
32
+     * @return 计算结果
33
+     */
34
+    public List<CmcGaussPositive> calculateGaussPositive(List<CmcGaussPositive> dataList);
35
+
36
+}

+ 349
- 0
oa-back/ruoyi-calculate/src/main/java/com/ruoyi/web/calculate/service/impl/CmcGaussPositiveServiceImpl.java Vedi File

@@ -0,0 +1,349 @@
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.mapper.CmcGaussPositiveMapper;
20
+import com.ruoyi.web.calculate.domain.CmcGaussPositive;
21
+import com.ruoyi.web.calculate.service.ICmcGaussPositiveService;
22
+
23
+/**
24
+ * 高斯正算服务实现
25
+ * 
26
+ * @author ruoyi
27
+ * @date 2026-04-21
28
+ */
29
+@Service
30
+public class CmcGaussPositiveServiceImpl implements ICmcGaussPositiveService 
31
+{
32
+    @Autowired
33
+    // private CmcGaussPositiveMapper cmcGaussPositiveMapper;
34
+
35
+    private static final double A_WGS84 = 6378137.0;
36
+    private static final double F_WGS84 = 1 / 298.257223563;
37
+
38
+    private static final double A_CGCS2000 = 6378137.0;
39
+    private static final double F_CGCS2000 = 1 / 298.257222101;
40
+
41
+    private static final double A_BJ54 = 6378245.0;
42
+    private static final double F_BJ54 = 1 / 298.3;
43
+
44
+    private static final double A_XA80 = 6378140.0;
45
+    private static final double F_XA80 = 1 / 298.257;
46
+
47
+    private static double A;
48
+    private static double F;
49
+    private static double E2;
50
+
51
+
52
+    /**
53
+     * 导入Excel数据
54
+     */
55
+    @Override
56
+    @Transactional(rollbackFor = Exception.class)
57
+    public AjaxResult importExcelData(MultipartFile file)
58
+    {
59
+        try {
60
+            // 使用若依的ExcelUtil工具类
61
+            ExcelUtil<CmcGaussPositive> util = new ExcelUtil<CmcGaussPositive>(CmcGaussPositive.class);
62
+            List<CmcGaussPositive> dataList = util.importExcel(file.getInputStream());
63
+
64
+            // 检查是否有数据
65
+            if (dataList == null || dataList.isEmpty()) {
66
+                return AjaxResult.error("Excel文件中没有数据!");
67
+            }
68
+
69
+            // 验证数据并转换为前端需要的格式
70
+            List<Map<String, Object>> validData = new ArrayList<>();
71
+            int successCount = 0;
72
+            StringBuilder errorMsg = new StringBuilder();
73
+
74
+            for (int i = 0; i < dataList.size(); i++) {
75
+                CmcGaussPositive item = dataList.get(i);
76
+
77
+                try {
78
+                    // 转换为前端需要的Map格式
79
+                    Map<String, Object> rowMap = new HashMap<>();
80
+                    rowMap.put("pointNumber", item.getPointNumber());
81
+                    rowMap.put("coordinateSystem", item.getCoordinateSystem());
82
+                    rowMap.put("longitude", item.getLongitude());
83
+                    rowMap.put("longitudePosition", item.getLongitudePosition());
84
+                    rowMap.put("latitude", item.getLatitude());
85
+                    rowMap.put("latitudePosition", item.getLatitudePosition());
86
+                    rowMap.put("zone", item.getZone());
87
+                    rowMap.put("bandwidth", item.getBandwidth());
88
+                    rowMap.put("projectionHeight", item.getProjectionHeight());
89
+                    rowMap.put("rowNum", i + 2); // Excel中的行号
90
+
91
+                    validData.add(rowMap);
92
+                    successCount++;
93
+
94
+                } catch (Exception e) {
95
+                    errorMsg.append(String.format("第%d行数据验证失败:%s;", i + 2, e.getMessage()));
96
+                }
97
+            }
98
+
99
+            // 构建返回结果
100
+            Map<String, Object> result = new HashMap<>();
101
+            result.put("total", dataList.size());
102
+            result.put("validCount", successCount);
103
+            result.put("data", validData);
104
+
105
+            if (successCount == 0) {
106
+                return AjaxResult.error("没有有效数据!" + errorMsg.toString());
107
+            }
108
+
109
+            String message = String.format("导入成功!共解析%d条数据,有效数据%d条", dataList.size(), successCount);
110
+            if (errorMsg.length() > 0) {
111
+                message += ";部分数据验证失败:" + errorMsg.toString();
112
+                return AjaxResult.success(message, result);
113
+            }
114
+
115
+            return AjaxResult.success(message, result);
116
+
117
+        } catch (Exception e) {
118
+            e.printStackTrace();
119
+            return AjaxResult.error("导入失败:" + e.getMessage());
120
+        }
121
+    }
122
+
123
+    /**
124
+     * 执行高斯正算
125
+     */
126
+    @Override
127
+    public List<CmcGaussPositive> calculateGaussPositive(List<CmcGaussPositive> dataList)
128
+    {
129
+        List<CmcGaussPositive> resultList = new ArrayList<>();
130
+        for (CmcGaussPositive item : dataList) {
131
+            try {
132
+                CmcGaussPositive result = calculateGaussPositiveSingle(item);
133
+                resultList.add(result);
134
+            } catch (Exception e) {
135
+                e.printStackTrace();
136
+                // 计算失败时,返回带有错误信息的对象
137
+                CmcGaussPositive errorResult = new CmcGaussPositive();
138
+                errorResult.setPointNumber(item.getPointNumber());
139
+                errorResult.setGaussX(-1.0);
140
+                errorResult.setGaussY(-1.0);
141
+                errorResult.setMeridianConvergence(-1.0);
142
+                resultList.add(errorResult);
143
+            }
144
+        }
145
+
146
+        return resultList;
147
+    }
148
+    
149
+    /**
150
+     * 单条高斯正算
151
+     */
152
+    private CmcGaussPositive calculateGaussPositiveSingle(CmcGaussPositive cmcGaussPositive)
153
+    {
154
+        try {
155
+            initEllipsoidParams(cmcGaussPositive.getCoordinateSystem());
156
+
157
+            double longitude = cmcGaussPositive.getLongitude(); // 经度(度)
158
+            String longitudePosition = cmcGaussPositive.getLongitudePosition(); // 经度位置(东/西)
159
+            String latitudePosition = cmcGaussPositive.getLatitudePosition(); // 纬度位置(北/南)
160
+            double latitude = cmcGaussPositive.getLatitude(); // 纬度(度)
161
+            Integer zone = cmcGaussPositive.getZone(); // 带号
162
+            int bandwidth = cmcGaussPositive.getBandwidth(); // 带宽
163
+            int projectionHeight = cmcGaussPositive.getProjectionHeight(); // 投影面高程
164
+
165
+            // 计算中央经线
166
+            double centralMeridian;
167
+            if (bandwidth == 6) {
168
+                centralMeridian = zone * 6 - 3;
169
+            } else { // 3度带
170
+                centralMeridian = zone * 3;
171
+            }
172
+            // 转换为弧度
173
+            double B = convertDecimalDegrees(latitude) * Math.PI / 180;
174
+            double L = convertDecimalDegrees(longitude);
175
+            // double L0 = Math.toRadians(centralMeridian);
176
+           
177
+            // 计算经差
178
+            double l = (centralMeridian - L) * Math.PI / 180;
179
+            // 处理西经情况
180
+            if ("东".equals(longitudePosition) || "E".equalsIgnoreCase(longitudePosition)) {
181
+                l = (L - centralMeridian) * Math.PI / 180;
182
+            }
183
+            double m = Math.cos(B) * l;
184
+            double ep2 = 1 / Math.pow(1 - F, 2) - 1;
185
+            double eta2 = ep2 * Math.cos(B) * Math.cos(B);
186
+
187
+            // 计算卯酉圈曲率半径
188
+            double N = (A + projectionHeight) / ((1 - F) * Math.sqrt(1 + eta2));
189
+            // 计算子午线弧长
190
+            double X = calculateMeridianArc(B, projectionHeight);
191
+
192
+            // 计算Y坐标的系数
193
+            double t = Math.tan(B);
194
+            double t2 = t * t;
195
+            double t4 = t2 * t2;
196
+            double m2 = m * m;
197
+            double m4 = m2 * m2;
198
+
199
+            // 计算X坐标
200
+            double x = X + N * Math.cos(B) * Math.cos(B) * t * l * l / 2 +
201
+                    N * Math.pow(Math.cos(B), 4) * t * (5 - t * t + 9 * eta2 + 4 * eta2 * eta2) * Math.pow(l, 4) / 24 +
202
+                    N * Math.pow(Math.cos(B), 6) * t * (61 - 58 * t2 + t4 + 270 * eta2 - 330 * eta2 * t2)
203
+                            * Math.pow(l, 6) / 720;
204
+            // l 
205
+            double term1 = m;
206
+            //  l³ 
207
+            double term3 = (1 - t2 + eta2) * m2 * m / 6;
208
+            //  l⁵ 
209
+            double term5 = (5 - 18 * t2 + t4 + 14 * eta2 - 58 * eta2 * t2) * m4 * m / 120;
210
+            //计算y坐标
211
+            double y = N * (term1 + term3 + term5);
212
+
213
+            // 计算子午线收敛角
214
+            double meridianConvergence = t * (m + (1 + 3 * eta2 + 2 * eta2 * eta2) * m2 * m / 3 + (2 - t2) * m4 * m * 12 / 180);
215
+            meridianConvergence = Math.toDegrees(meridianConvergence);
216
+            meridianConvergence = decimalToDMS(meridianConvergence);
217
+            if ("南".equals(latitudePosition) || "S".equalsIgnoreCase(latitudePosition)) {
218
+                x = 10000000 - x;
219
+            }
220
+            // 加入东偏
221
+            y = 500000 + y;
222
+            System.out.println("x = " + x + ", y = " + y+", meridianConvergence = "+meridianConvergence);
223
+            // 设置计算结果
224
+            cmcGaussPositive.setGaussX(x);
225
+            cmcGaussPositive.setGaussY(y);
226
+            cmcGaussPositive.setMeridianConvergence(meridianConvergence);
227
+        } catch (Exception e) {
228
+            e.printStackTrace();
229
+        }
230
+        return cmcGaussPositive;
231
+    }
232
+    
233
+    //选择椭球体参数
234
+    private void initEllipsoidParams(String coordinateSystem) {
235
+        if ("CGCS2000".equalsIgnoreCase(coordinateSystem) || "国家2000".equalsIgnoreCase(coordinateSystem)) {
236
+            A = A_CGCS2000;
237
+            F = F_CGCS2000;
238
+        } else if ("1954".equalsIgnoreCase(coordinateSystem) || "北京54".equalsIgnoreCase(coordinateSystem) || "Beijing54".equalsIgnoreCase(coordinateSystem)) {
239
+            A = A_BJ54;
240
+            F = F_BJ54;
241
+        } else if ("1980".equalsIgnoreCase(coordinateSystem) || "西安80".equalsIgnoreCase(coordinateSystem) || "Xian80".equalsIgnoreCase(coordinateSystem)) {
242
+            A = A_XA80;
243
+            F = F_XA80;
244
+        } else {
245
+            A = A_WGS84;
246
+            F = F_WGS84;
247
+        }
248
+        BigDecimal one = BigDecimal.ONE;
249
+        BigDecimal divisor = new BigDecimal("298.257222101");
250
+        BigDecimal db_F = one.divide(divisor, 20, RoundingMode.HALF_UP);
251
+
252
+        // 计算 2 * F
253
+        BigDecimal two = new BigDecimal("2");
254
+        BigDecimal twoF = two.multiply(db_F);
255
+
256
+        // 计算 F * F
257
+        BigDecimal F_squared = db_F.multiply(db_F);
258
+
259
+        // 计算 e2 = 2*F - F*F
260
+        BigDecimal e2 = twoF.subtract(F_squared);
261
+        E2 = e2.doubleValue();
262
+        F = db_F.doubleValue();
263
+    }
264
+
265
+    /**
266
+     * 计算子午线弧长
267
+     */
268
+    private double calculateMeridianArc(double B, double projectionHeight) {
269
+        double e2 = E2;
270
+        double e4 = E2 * E2;
271
+        double e6 = e4 * E2;
272
+        double e8 = e6 * E2;
273
+        double e10 = e8 * E2;
274
+        double d = A + projectionHeight;
275
+        // 计算子午线弧长系数
276
+        double a0 = (d - d * e2) * (1
277
+                + 3.0 / 4.0 * e2
278
+                + 45.0 / 64.0 * e4
279
+                + 175.0 / 256.0 * e6
280
+                + 11025.0 / 16384.0 * e8
281
+                + 43659.0 / 65536.0 * e10);
282
+
283
+        double a2 = (d - d * e2) * (3.0 / 4.0 * e2
284
+                + 45.0 / 64.0 * e4
285
+                + 175.0 / 256.0 * e6
286
+                + 11025.0 / 16384.0 * e8
287
+                + 43659.0 / 65536.0 * e10);
288
+
289
+        double a4 = (d - d * e2) * (15.0 / 32.0 * e4
290
+                + 175.0 / 384.0 * e6
291
+                + 3675.0 / 8192.0 * e8
292
+                + 14553.0 / 32768.0 * e10);
293
+
294
+        double a6 = (d - d * e2) * (35.0 / 96.0 * e6
295
+                + 735.0 / 2048.0 * e8
296
+                + 14553.0 / 40960.0 * e10);
297
+
298
+        double a8 = (d - d * e2) * (315.0 / 1024.0 * e8
299
+                + 6237.0 / 20480.0 * e10);
300
+
301
+        double a10 = (d - d * e2) * (693.0/2560.0)* e10;
302
+
303
+        // 计算子午线弧长
304
+        double sinB = Math.sin(B);
305
+        double cosB = Math.cos(B);
306
+
307
+        double X = a0 * B - (a2 * sinB + a4 * Math.pow(sinB, 3) + a6 * Math.pow(sinB, 5) + a8 * Math.pow(sinB, 7)
308
+                + a10 * Math.pow(sinB, 9)) * cosB;
309
+        return X;
310
+    }
311
+    
312
+    /**
313
+     * 将十进制度数转换为特定格式(
314
+     * @param decimalDegrees 十进制度数
315
+     * @return 转换后的值
316
+     */
317
+    public static double convertDecimalDegrees(double decimalDegrees) {
318
+        // 度的整数部分
319
+        double degrees = Math.floor(decimalDegrees);
320
+
321
+        // 分的计算
322
+        double minutes = Math.floor(100 * (decimalDegrees - degrees)) / 60;
323
+
324
+        // 秒的计算
325
+        double seconds = (decimalDegrees * 100 - Math.floor(decimalDegrees * 100)) / 36;
326
+
327
+        return degrees + minutes + seconds;
328
+    }
329
+    
330
+    /**
331
+     * 将十进制度数转换为度分秒格式数值(DD.MMSSssss)
332
+     * 
333
+     * @param decimalDegrees 十进制度数(如 103.373428195)
334
+     */
335
+    public static double decimalToDMS(double decimalDegrees) {
336
+        // 1. 提取度
337
+        double degrees = Math.floor(decimalDegrees );
338
+
339
+        // 2. 提取分
340
+        double minutesRaw = 60 * (decimalDegrees - degrees);
341
+        double minutes = Math.floor(minutesRaw) / 100;
342
+
343
+        // 3. 提取秒
344
+        double secondsRaw = 60 * (60 * decimalDegrees - Math.floor(60 * decimalDegrees ));
345
+        double seconds = secondsRaw / 10000;
346
+
347
+        return degrees + minutes + seconds;
348
+    }
349
+}

+ 123
- 0
oa-back/ruoyi-calculate/src/main/java/com/ruoyi/web/calculate/vo/CmcGaussPositiveTemplate.java Vedi File

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

+ 42
- 0
oa-ui/src/api/calculate/gausspositive.js Vedi File

@@ -0,0 +1,42 @@
1
+import request from '@/utils/request'
2
+
3
+
4
+// 执行高斯正算
5
+export function calculate(data) {
6
+    return request({
7
+        url: `/calculate/gaussPositive/calculate`,
8
+        method: 'post',
9
+        data: data
10
+    })
11
+}
12
+
13
+// 导入Excel
14
+export function importExcel(data) {
15
+    return request({
16
+        url: `/calculate/gaussPositive/import`,
17
+        method: 'post',
18
+        data: data,
19
+        headers: {
20
+            'Content-Type': 'multipart/form-data'
21
+        }
22
+    })
23
+}
24
+
25
+// 导出高斯正算
26
+export function exportExcel(data) {
27
+    return request({
28
+        url: `/calculate/gaussPositive/export`,
29
+        method: 'post',
30
+        data: data,
31
+        responseType: 'blob'
32
+    })
33
+}
34
+
35
+// 下载模板
36
+export function downloadTemplate() {
37
+    return request({
38
+        url: '/calculate/gaussPositive/template',
39
+        method: 'get',
40
+        responseType: 'blob'
41
+    })
42
+}

+ 223
- 0
oa-ui/src/views/calculate/index.vue Vedi File

@@ -0,0 +1,223 @@
1
+<template>
2
+  <div class="gauss-calculate">
3
+    <!-- 工具选择区域 -->
4
+    <div class="tool-selector-area">
5
+      <div class="tool-selector-label">
6
+        <i class="el-icon-s-tools"></i>
7
+        <span class="label-text">选择工具</span>
8
+        <span class="label-sub">请从下方选择需要使用的计算工具</span>
9
+      </div>
10
+      <el-cascader
11
+        v-model="selectedToolPath"
12
+        :options="toolOptions"
13
+        :props="cascaderProps"
14
+        placeholder="请选择要使用的工具"
15
+        @change="handleToolChange"
16
+        clearable
17
+        class="tool-cascader"
18
+      ></el-cascader>
19
+    </div>
20
+    
21
+    <!-- 动态组件:根据选择的工具显示对应的工具页面 -->
22
+    <div class="tool-content">
23
+      <transition name="fade" mode="out-in">
24
+        <component 
25
+          :is="currentToolComponent" 
26
+          :key="currentToolKey"
27
+          ref="currentTool"
28
+          @tool-event="handleToolEvent"
29
+        />
30
+      </transition>
31
+    </div>
32
+  </div>
33
+</template>
34
+
35
+<script>
36
+// 导入各个工具组件
37
+import GaussPositiveTool from './tools/gausspositive.vue'
38
+
39
+export default {
40
+  name: 'CalculationTools',
41
+  components: {
42
+    GaussPositiveTool,
43
+  },
44
+  data() {
45
+    return {
46
+      // 级联选择器选中的值(路径数组)
47
+      selectedToolPath: [],
48
+      // 当前激活的工具组件名称
49
+      currentToolComponent: 'GaussPositiveTool',
50
+      // 当前工具的key(用于强制刷新组件)
51
+      currentToolKey: 0,
52
+      // 级联选择器的配置
53
+      cascaderProps: {
54
+        value: 'id',
55
+        label: 'name',
56
+        children: 'children',
57
+        expandTrigger: 'hover',
58
+      },
59
+      // 工具选项配置(分类 + 工具)
60
+      toolOptions: [
61
+        {
62
+          id: 'gauss-category',
63
+          name: '各种坐标系高斯正算',
64
+          children: [
65
+            {
66
+              id: 'gauss-positive',
67
+              name: '高斯正算工具',
68
+              component: 'GaussPositiveTool'
69
+            }
70
+          ]
71
+        },
72
+        {
73
+          id: 'other-category',
74
+          name: '其他计算工具',
75
+          children: [
76
+            // 预留其他工具位置
77
+          ]
78
+        }
79
+      ]
80
+    }
81
+  },
82
+  mounted() {
83
+    // 首次进入,默认选中第一个工具
84
+    this.setDefaultTool()
85
+  },
86
+  methods: {
87
+    // 设置默认工具(第一个分类下的第一个工具)
88
+    setDefaultTool() {
89
+      if (this.toolOptions.length > 0 && this.toolOptions[0].children.length > 0) {
90
+        const firstCategory = this.toolOptions[0]
91
+        const firstTool = firstCategory.children[0]
92
+        this.selectedToolPath = [firstCategory.id, firstTool.id]
93
+        this.currentToolComponent = firstTool.component
94
+        // 强制刷新组件
95
+        this.currentToolKey++
96
+      }
97
+    },
98
+    
99
+    // 级联选择器值变化时的处理
100
+    handleToolChange(value) {
101
+      if (!value || value.length === 0) {
102
+        // 如果清空了选择,恢复上一个工具
103
+        if (this.selectedToolPath && this.selectedToolPath.length > 0) {
104
+          this.$nextTick(() => {
105
+            this.selectedToolPath = [...this.prevSelectedPath]
106
+          })
107
+        }
108
+        return
109
+      }
110
+      
111
+      // 保存当前路径
112
+      this.prevSelectedPath = [...value]
113
+      
114
+      // 根据路径找到对应的工具组件
115
+      const categoryId = value[0]
116
+      const toolId = value[1]
117
+      
118
+      const category = this.toolOptions.find(opt => opt.id === categoryId)
119
+      if (category && category.children) {
120
+        const tool = category.children.find(child => child.id === toolId)
121
+        if (tool && tool.component) {
122
+          this.currentToolComponent = tool.component
123
+          // 强制刷新组件
124
+          this.currentToolKey++
125
+        }
126
+      }
127
+    },
128
+    
129
+    // 处理工具组件触发的事件
130
+    handleToolEvent(eventData) {
131
+      console.log('工具事件:', eventData)
132
+      if (eventData && eventData.message) {
133
+        this.$message({
134
+          message: eventData.message,
135
+          type: eventData.type || 'info',
136
+          duration: 2000
137
+        })
138
+      }
139
+    }
140
+  }
141
+}
142
+</script>
143
+
144
+<style scoped>
145
+.gauss-calculate {
146
+  padding: 20px;
147
+  background-color: #f5f7fa;
148
+  min-height: 100vh;
149
+}
150
+
151
+/* 工具选择区域样式 */
152
+.tool-selector-area {
153
+  background: linear-gradient(135deg, #11a983 0%, rgba(196, 211, 207, 0.85) 100%);
154
+  border-radius: 12px;
155
+  padding: 24px 30px;
156
+  margin-bottom: 24px;
157
+  box-shadow: 0 4px 15px rgba(0, 0, 0, 0.1);
158
+}
159
+
160
+.tool-selector-label {
161
+  display: flex;
162
+  align-items: baseline;
163
+  flex-wrap: wrap;
164
+  gap: 12px;
165
+  margin-bottom: 16px;
166
+}
167
+
168
+.tool-selector-label i {
169
+  font-size: 28px;
170
+  color: #fff;
171
+}
172
+
173
+.label-text {
174
+  font-size: 24px;
175
+  font-weight: bold;
176
+  color: #fff;
177
+  letter-spacing: 2px;
178
+}
179
+
180
+.label-sub {
181
+  font-size: 14px;
182
+  color: rgba(255, 255, 255, 0.85);
183
+  margin-left: 8px;
184
+}
185
+
186
+.tool-cascader {
187
+  width: 100%;
188
+  max-width: 500px;
189
+}
190
+
191
+.tool-cascader /deep/ .el-input__inner {
192
+  height: 44px;
193
+  line-height: 44px;
194
+  font-size: 15px;
195
+  border-radius: 8px;
196
+  border: none;
197
+  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
198
+}
199
+
200
+.tool-cascader /deep/ .el-input__inner:focus {
201
+  box-shadow: 0 0 0 2px rgba(255, 255, 255, 0.5);
202
+}
203
+
204
+/* 工具内容区域 */
205
+.tool-content {
206
+  background-color: #ffffff;
207
+  border-radius: 12px;
208
+  padding: 24px;
209
+  box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.05);
210
+  min-height: 500px;
211
+}
212
+
213
+/* 过渡动画 */
214
+.fade-enter-active,
215
+.fade-leave-active {
216
+  transition: opacity 0.3s ease;
217
+}
218
+
219
+.fade-enter,
220
+.fade-leave-to {
221
+  opacity: 0;
222
+}
223
+</style>

+ 555
- 0
oa-ui/src/views/calculate/tools/gausspositive.vue Vedi File

@@ -0,0 +1,555 @@
1
+<!-- 高斯正算工具组件 -->
2
+<template>
3
+  <div class="gauss-positive-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="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 
20
+          type="warning" 
21
+          @click="handleCalculate" 
22
+          size="small" 
23
+          :disabled="!importedData || importedData.length === 0 || calculating"
24
+          :loading="calculating"
25
+          icon="el-icon-caret-right">
26
+          {{ calculating ? '计算中...' : '执行计算' }}
27
+        </el-button>
28
+        <el-button 
29
+          type="danger" 
30
+          @click="clearAll" 
31
+          size="small" 
32
+          :disabled="(!importedData || importedData.length === 0) && calculationResults.length === 0"
33
+          plain
34
+          icon="el-icon-delete">
35
+          清空全部
36
+        </el-button>
37
+      </div>
38
+    </div>
39
+    
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>
52
+        
53
+      </div>
54
+    </div>
55
+
56
+    <!-- 计算结果展示区 - 只显示计算结果,不包含原始数据 -->
57
+    <div v-if="calculationResults.length > 0" class="result-section">
58
+      <div class="section-header">
59
+        <span class="section-title">
60
+          <i class="el-icon-tickets"></i>
61
+          计算结果
62
+        </span>
63
+        <span class="section-info">
64
+          共 {{ calculationResults.length }} 条计算结果
65
+        </span>
66
+        <el-button type="text" size="small" @click="handleExport" icon="el-icon-download">
67
+          导出结果
68
+        </el-button>
69
+      </div>
70
+      
71
+      <el-table :data="calculationResults" style="width: 100%" border stripe>
72
+        <el-table-column prop="pointNumber" label="待求点号" width="120" align="center"></el-table-column>
73
+        <el-table-column prop="gaussX" label="高斯X坐标(m)" width="180" align="center">
74
+          <template slot-scope="scope">
75
+            <span class="result-value">{{ formatNumber(scope.row.gaussX,4) }}</span>
76
+          </template>
77
+        </el-table-column>
78
+        <el-table-column prop="gaussY" label="高斯Y坐标(m)" width="180" align="center">
79
+          <template slot-scope="scope">
80
+            <span class="result-value">{{ formatNumber(scope.row.gaussY,4) }}</span>
81
+          </template>
82
+        </el-table-column>
83
+        <el-table-column prop="meridianConvergence" label="子午线收敛角γ(°)" width="200" align="center">
84
+          <template slot-scope="scope">
85
+            <span class="result-value">{{ formatNumber(scope.row.meridianConvergence, 7) }}</span>
86
+          </template>
87
+        </el-table-column>
88
+      </el-table>
89
+    </div>
90
+    
91
+    <!-- 空状态 -->
92
+    <div v-else class="empty-state">
93
+      <i class="el-icon-document"></i>
94
+      <p>暂无计算结果</p>
95
+      <p class="empty-tip">请先导入Excel数据,然后点击"执行计算"</p>
96
+    </div>
97
+    
98
+    <!-- 导入Excel对话框 -->
99
+    <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;">
127
+        <el-upload
128
+          class="upload-area"
129
+          :action="''"
130
+          :auto-upload="false"
131
+          :on-change="handleFileChange"
132
+          :show-file-list="true"
133
+          accept=".xlsx,.xls"
134
+          drag
135
+          ref="upload">
136
+          <i class="el-icon-upload"></i>
137
+          <div class="el-upload__text">将Excel文件拖到此处,或<em>点击选择</em></div>
138
+          <div class="el-upload__tip" slot="tip">
139
+            支持.xlsx和.xls格式文件
140
+          </div>
141
+        </el-upload>
142
+      </div>
143
+      
144
+      <span slot="footer" class="dialog-footer">
145
+        <el-button @click="importDialogVisible = false">取消</el-button>
146
+        <el-button type="primary" @click="submitImport" :loading="importLoading" :disabled="!importFile">
147
+          确认导入
148
+        </el-button>
149
+      </span>
150
+    </el-dialog>
151
+    
152
+    <!-- 预览数据对话框 - 显示导入的原始数据 -->
153
+    <el-dialog title="导入数据预览" :visible.sync="previewDialogVisible" width="80%">
154
+      <div class="preview-info">
155
+        <span>共 {{ importedData.length }} 条数据</span>
156
+        <span style="margin-left: 20px;">状态:{{ importedData.filter(item => item.calculated).length }} 条已计算,{{ importedData.filter(item => !item.calculated).length }} 条未计算</span>
157
+      </div>
158
+      <el-table :data="importedData" border stripe max-height="500">
159
+        <el-table-column prop="pointNumber" label="待求点号" width="120" fixed="left"></el-table-column>
160
+        <el-table-column prop="coordinateSystem" label="坐标系" width="110">
161
+          <template slot-scope="scope">
162
+            <el-tag size="small" :type="getCoordinateType(scope.row.coordinateSystem)">
163
+              {{ scope.row.coordinateSystem }}
164
+            </el-tag>
165
+          </template>
166
+        </el-table-column>
167
+        <el-table-column prop="longitude" label="大地经度" width="130" align="center">
168
+          <template slot-scope="scope">
169
+            {{ scope.row.longitude }}
170
+          </template>
171
+        </el-table-column>
172
+         <el-table-column prop="longitudePosition" label="经度位置" width="130" align="center">
173
+          <template slot-scope="scope">
174
+            {{ scope.row.longitudePosition}}
175
+          </template>
176
+        </el-table-column>
177
+        <el-table-column prop="latitude" label="大地纬度" width="130" align="center">
178
+          <template slot-scope="scope">
179
+            {{ scope.row.latitude }}
180
+          </template>
181
+        </el-table-column>
182
+         <el-table-column prop="latitudePosition" label="纬度位置" width="130" align="center">
183
+          <template slot-scope="scope">
184
+            {{ scope.row.latitudePosition}}
185
+                   </template>
186
+        </el-table-column>
187
+        <el-table-column prop="zone" label="带号" width="80" align="center"></el-table-column>
188
+        <el-table-column prop="bandwidth" label="带宽" width="80" align="center">
189
+          <template slot-scope="scope">
190
+            {{ scope.row.bandwidth }}
191
+          </template>
192
+        </el-table-column>
193
+      </el-table>
194
+      <span slot="footer" class="dialog-footer">
195
+        <el-button type="primary" @click="previewDialogVisible = false">关闭</el-button>
196
+      </span>
197
+    </el-dialog>
198
+  </div>
199
+</template>
200
+
201
+<script>
202
+import { calculate, importExcel,exportExcel,downloadTemplate } from '@/api/calculate/gausspositive'
203
+
204
+export default {
205
+  name: 'GaussPositiveTool',
206
+  data() {
207
+    return {
208
+      importedData: [],        // 导入的原始数据
209
+      calculationResults: [],  // 计算结果
210
+      importDialogVisible: false,
211
+      importLoading: false,
212
+      importFile: null,
213
+      calculating: false,
214
+      previewDialogVisible: false,
215
+      projectionHeight: 0
216
+    }
217
+  },
218
+  watch: {
219
+    // 当投影面高程改变时,重新计算(如果已有数据)
220
+    projectionHeight(newVal, oldVal) {
221
+      if (this.importedData.length > 0 && this.calculationResults.length > 0) {
222
+        this.handleCalculate()
223
+      }
224
+    }
225
+  },
226
+  methods: {
227
+    // 打开导入对话框
228
+    handleImport() {
229
+      this.importDialogVisible = true
230
+    },
231
+    
232
+    // 文件选择变化
233
+    handleFileChange(file) {
234
+      this.importFile = file.raw
235
+    },
236
+    
237
+    // 提交导入
238
+    async submitImport() {
239
+      if (!this.importFile) {
240
+        this.$message.warning('请选择文件')
241
+        return
242
+      }
243
+      
244
+      this.importLoading = true
245
+      
246
+      const formData = new FormData()
247
+      formData.append('file', this.importFile)
248
+      
249
+        const response = await importExcel(formData)
250
+        
251
+        if (response.code === 200) {
252
+          const result = response.data
253
+          
254
+          // 将后端返回的数据转换为前端格式
255
+          this.importedData = (result.data || []).map(item => ({
256
+            pointNumber: item.pointNumber,
257
+            coordinateSystem: item.coordinateSystem,
258
+            longitude: item.longitude,
259
+            longitudePosition: item.longitudePosition,
260
+            latitude: item.latitude,
261
+            latitudePosition: item.latitudePosition,
262
+            zone: item.zone,
263
+            bandwidth: item.bandwidth
264
+          }))
265
+          
266
+          // 清空之前的计算结果
267
+          this.calculationResults = []
268
+          
269
+          this.$message.success(`${response.msg}`)
270
+          this.importDialogVisible = false
271
+          this.importLoading = false
272
+        } else {
273
+          this.$message.error(response.msg)
274
+        }
275
+    },
276
+    
277
+    // 预览数据 - 显示导入的原始数据
278
+    handlePreview() {
279
+      if (!this.importedData || this.importedData.length === 0) {
280
+        this.$message.warning('没有可预览的数据')
281
+        return
282
+      }
283
+      this.previewDialogVisible = true
284
+    },
285
+    
286
+    // 下载模板
287
+    handleDownloadTemplate() {
288
+       this.download('/calculate/gaussPositive/template', {}, '高斯正算模板.xlsx')
289
+    },
290
+    
291
+    
292
+    // 执行计算
293
+    async handleCalculate() {
294
+      if (!this.importedData || this.importedData.length === 0) {
295
+        this.$message.warning('没有数据可计算')
296
+        return
297
+      }
298
+      
299
+      this.calculating = true
300
+      const results = []
301
+      let successCount = 0
302
+      let failCount = 0
303
+      
304
+      // 构建请求参数
305
+      const requestData = this.importedData.map(item => ({
306
+        pointNumber: item.pointNumber,
307
+        coordinateSystem: item.coordinateSystem,
308
+        longitude: parseFloat(item.longitude),
309
+        longitudePosition: item.longitudePosition,
310
+        latitude: parseFloat(item.latitude),
311
+        latitudePosition: item.latitudePosition,
312
+        zone: parseInt(item.zone),
313
+        bandwidth: parseInt(item.bandwidth),
314
+        projectionHeight: this.projectionHeight
315
+      }))
316
+      
317
+      const response = await calculate(requestData)
318
+      
319
+      if (response.code === 200) {
320
+        const results = response.data || []
321
+        
322
+        // 更新计算结果
323
+        this.calculationResults = results.map(item => ({
324
+          pointNumber: item.pointNumber,
325
+          coordinateSystem: item.coordinateSystem,
326
+          longitude: parseFloat(item.longitude),
327
+          longitudePosition: item.longitudePosition,
328
+          latitude: parseFloat(item.latitude),
329
+          latitudePosition: item.latitudePosition,
330
+          zone: parseInt(item.zone),
331
+          bandwidth: parseInt(item.bandwidth),
332
+          projectionHeight: this.projectionHeight,
333
+          gaussX: item.gaussX,
334
+          gaussY: item.gaussY,
335
+          meridianConvergence: item.meridianConvergence
336
+        }))
337
+        
338
+        // 更新导入数据的计算状态
339
+        this.importedData.forEach(importItem => {
340
+          const found = results.find(r => r.pointNumber === importItem.pointNumber)
341
+          if (found && found.gaussX !== -1) {
342
+            importItem.calculated = true
343
+          }
344
+        })
345
+        
346
+        const successCount = results.filter(r => r.gaussX !== -1).length
347
+        const failCount = results.length - successCount
348
+        
349
+        this.$message.success(`计算完成,成功 ${successCount} 条${failCount > 0 ? `,失败 ${failCount} 条` : ''}`)
350
+        this.calculating = false
351
+        } else {
352
+          this.$message.error(response.msg)
353
+        }
354
+    },
355
+    
356
+    
357
+    // 导出结果
358
+    async handleExport() {
359
+      if (this.calculationResults.length === 0) {
360
+        this.$message.warning('没有结果可导出')
361
+        return
362
+      }
363
+      
364
+        const response = await exportExcel(this.calculationResults)
365
+        
366
+        const blob = new Blob([response], { 
367
+          type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
368
+        })
369
+        
370
+        const url = window.URL.createObjectURL(blob)
371
+        const link = document.createElement('a')
372
+        link.href = url
373
+        link.download = `高斯正算结果_${new Date().getTime()}.xlsx`
374
+        link.click()
375
+        window.URL.revokeObjectURL(url)
376
+  },
377
+    
378
+  // 清空全部
379
+  clearAll() {
380
+    if ((!this.importedData || this.importedData.length === 0) && this.calculationResults.length === 0) {
381
+      return
382
+    }
383
+    
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)
407
+  },
408
+  
409
+  // 获取坐标系标签类型
410
+  getCoordinateType(system) {
411
+    const types = {
412
+      'WGS84': 'primary',
413
+      'CGCS2000': 'success',
414
+      'Beijing54': 'warning',
415
+      'Xian80': 'info'
416
+    }
417
+    return types[system] || 'info'
418
+  }
419
+  }
420
+}
421
+</script>
422
+
423
+<style scoped>
424
+.gauss-positive-tool {
425
+  width: 100%;
426
+}
427
+
428
+.tool-header {
429
+  margin-bottom: 20px;
430
+}
431
+
432
+.card-header-buttons {
433
+  display: flex;
434
+  gap: 10px;
435
+  flex-wrap: wrap;
436
+}
437
+
438
+/* 投影面高程设置样式 */
439
+.height-setting {
440
+  background: #f5f7fa;
441
+  border-radius: 8px;
442
+  padding: 12px 20px;
443
+  margin-bottom: 20px;
444
+  border: 1px solid #e4e7ed;
445
+  display: flex;
446
+  align-items: center;
447
+  gap: 20px;
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
+.upload-area {
527
+  margin-top: 10px;
528
+}
529
+
530
+.preview-info {
531
+  margin-bottom: 15px;
532
+  padding: 10px;
533
+  background: #f5f7fa;
534
+  border-radius: 4px;
535
+  font-size: 14px;
536
+  color: #606266;
537
+}
538
+
539
+.dialog-footer {
540
+  width: 100%;
541
+  display: flex;
542
+  justify-content: flex-end;
543
+  gap: 10px;
544
+}
545
+
546
+.template-download-wrapper {
547
+  margin-top: 12px;
548
+  display: flex;
549
+  align-items: center;
550
+  justify-content: flex-end;
551
+  gap: 10px;
552
+  padding-top: 12px;
553
+  border-top: 1px dashed #e4e7ed;
554
+}
555
+</style>

Loading…
Annulla
Salva